Commit a3ae310f authored by Ivan Semenov's avatar Ivan Semenov Committed by intellij-monorepo-bot
Browse files

[gitlab] sync changes tree with diff

GitOrigin-RevId: 208dbf5eac0cbc2227cbe190d977101fe4d8e1d9
parent a5c1082a
Showing with 85 additions and 18 deletions
+85 -18
......@@ -11,9 +11,16 @@ class GitLabMergeRequestDiffBridge {
private val _changes = MutableStateFlow<ListSelection<Change>>(ListSelection.empty())
val changes: StateFlow<ListSelection<Change>> = _changes.asStateFlow()
private val _selectedChange = MutableStateFlow<Change?>(null)
val selectedChange: StateFlow<Change?> = _selectedChange.asStateFlow()
fun setChanges(changes: ListSelection<Change>) {
_changes.value = changes
}
fun changeSelected(change: Change?) {
_selectedChange.value = change
}
}
......
......@@ -20,6 +20,7 @@ import com.intellij.util.childScope
import git4idea.changes.GitParsedChangesBundle
import git4idea.changes.getDiffComputer
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import org.jetbrains.plugins.gitlab.api.GitLabProjectConnection
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestChanges
......@@ -56,6 +57,21 @@ fun createMergeRequestDiffRequestProcessor(project: Project,
processor.chain = it
}
}
uiCs.launch(start = CoroutineStart.UNDISPATCHED) {
callbackFlow {
val listener = MutableDiffRequestChainProcessor.SelectionListener { producer ->
trySend((producer as? ChangeDiffRequestProducer)?.change)
}
processor.selectionEventDispatcher.addListener(listener)
awaitClose {
processor.selectionEventDispatcher.removeListener(listener)
}
}.collect {
diffBridge.changeSelected(it)
}
}
return processor
}
......
......@@ -18,10 +18,10 @@ import com.intellij.ui.Side
import com.intellij.ui.components.panels.Wrapper
import com.intellij.util.EditSourceOnDoubleClickHandler
import com.intellij.util.Processor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapLatest
import org.jetbrains.plugins.gitlab.mergerequest.ui.details.model.GitLabMergeRequestChangesViewModel
import org.jetbrains.plugins.gitlab.util.GitLabBundle
import javax.swing.JComponent
......@@ -30,20 +30,29 @@ import javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
internal class GitLabMergeRequestDetailsChangesComponentFactory(private val project: Project) {
@OptIn(ExperimentalCoroutinesApi::class)
fun create(cs: CoroutineScope, vm: GitLabMergeRequestChangesViewModel): JComponent {
val wrapper = Wrapper(LoadingLabel())
cs.launch(start = CoroutineStart.UNDISPATCHED) {
val changesModel = SingleValueModel<Collection<Change>>(emptyList())
vm.changesResult.collectLatest { res ->
res.onFailure {
wrapper.setContent(SimpleHtmlPane(it.localizedMessage))
wrapper.repaint()
}.onSuccess {
changesModel.value = it
if (wrapper.targetComponent !is ChangesTree) {
wrapper.setContent(createChangesTree(vm, changesModel))
vm.changesResult.mapLatest {
it.map {
changesModel.apply { value = it }
}
}.distinctUntilChanged { old, new ->
old.getOrNull() === new.getOrNull()
}.collectLatest { res ->
coroutineScope {
res.onFailure {
wrapper.setContent(SimpleHtmlPane(it.localizedMessage))
wrapper.repaint()
}.onSuccess {
if (wrapper.targetComponent !is ChangesTree) {
wrapper.setContent(createChangesTree(vm, it))
wrapper.repaint()
}
}
awaitCancellation()
}
}
}
......@@ -54,9 +63,16 @@ internal class GitLabMergeRequestDetailsChangesComponentFactory(private val proj
}
}
private fun createChangesTree(vm: GitLabMergeRequestChangesViewModel, changesModel: SingleValueModel<Collection<Change>>): JComponent =
private fun CoroutineScope.createChangesTree(vm: GitLabMergeRequestChangesViewModel,
changesModel: SingleValueModel<Collection<Change>>): JComponent =
CodeReviewChangesTreeFactory(project, changesModel)
.create(GitLabBundle.message("merge.request.details.changes.empty")).also { tree ->
launch(start = CoroutineStart.UNDISPATCHED) {
vm.changeSelectionRequests.collect {
tree.setSelectedChanges(listOf(it))
}
}
tree.addTreeSelectionListener {
// focus transfer happens after selection change :(
invokeLater {
......
......@@ -16,7 +16,11 @@ import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestChanges
internal interface GitLabMergeRequestChangesViewModel : CodeReviewChangesViewModel<GitLabCommitDTO> {
val changesResult: Flow<Result<Collection<Change>>>
val userChangesSelection: StateFlow<ListSelection<Change>>
val changeSelectionRequests: Flow<Change>
fun selectChange(change: Change)
fun updateChangesSelectedByUser(changes: ListSelection<Change>)
......@@ -44,6 +48,15 @@ internal class GitLabMergeRequestChangesViewModelImpl(
private val _userChangesSelection = MutableStateFlow<ListSelection<Change>>(ListSelection.empty())
override val userChangesSelection: StateFlow<ListSelection<Change>> = _userChangesSelection.asStateFlow()
private val _changeSelectionRequests = MutableSharedFlow<Change>()
override val changeSelectionRequests: Flow<Change> = _changeSelectionRequests.asSharedFlow()
override fun selectChange(change: Change) {
cs.launch {
_changeSelectionRequests.emit(change)
}
}
private val _showDiffRequests = MutableSharedFlow<Unit>()
val showDiffRequests = _showDiffRequests.asSharedFlow()
......@@ -70,17 +83,20 @@ internal class GitLabMergeRequestChangesViewModelImpl(
val newList = new.list
if (oldList.size != newList.size) return false
return oldList.equalsVia(newList) { o1, o2 ->
return oldList.isEqual(newList)
}
private fun Collection<Change>.isEqual(other: Collection<Change>): Boolean =
equalsVia(other) { o1, o2 ->
o1 == o2 &&
o1.beforeRevision == o2.beforeRevision &&
o1.afterRevision == o2.afterRevision
}
}
private fun <E> List<E>.equalsVia(other: List<E>, isEqual: (E, E) -> Boolean): Boolean {
private fun <E> Collection<E>.equalsVia(other: Collection<E>, isEqual: (E, E) -> Boolean): Boolean {
if (other === this) return true
val i1 = listIterator()
val i2 = other.listIterator()
val i1 = iterator()
val i2 = other.iterator()
while (i1.hasNext() && i2.hasNext()) {
val e1 = i1.next()
......
......@@ -85,8 +85,8 @@ internal class GitLabReviewTabComponentFactory(
}
}
val diffBridge = project.service<GitLabMergeRequestDiffBridgeRepository>().get(conn, reviewId)
cs.launch(Dispatchers.EDT, start = CoroutineStart.UNDISPATCHED) {
val diffBridge = project.service<GitLabMergeRequestDiffBridgeRepository>().get(conn, reviewId)
detailsVmFlow.flatMapLatest {
it.changesVm.userChangesSelection
}.collectLatest {
......@@ -94,6 +94,18 @@ internal class GitLabReviewTabComponentFactory(
}
}
cs.launch(Dispatchers.EDT, start = CoroutineStart.UNDISPATCHED) {
detailsVmFlow.collectLatest { detailsVm ->
diffBridge.changes.collectLatest { changes ->
if (!changes.isExplicitSelection) {
diffBridge.selectedChange.filterNotNull().collect {
detailsVm.changesVm.selectChange(it)
}
}
}
}
}
cs.launch(Dispatchers.EDT, start = CoroutineStart.UNDISPATCHED) {
detailsVmFlow.flatMapLatest {
it.changesVm.showDiffRequests
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment