From 709cabb3b524dac6e261fea23f4e97cddbed3013 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 28 Sep 2020 18:37:29 +0300 Subject: [PATCH] git-index: support navigation between files in diff GitOrigin-RevId: 49bb4d7607a7b812387ee736232cc59cf1b826cc --- .../index/actions/GitStageDiffAction.kt | 22 ++++++----- .../src/git4idea/index/ui/GitStageTree.kt | 39 +++++++++++++++++-- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/plugins/git4idea/src/git4idea/index/actions/GitStageDiffAction.kt b/plugins/git4idea/src/git4idea/index/actions/GitStageDiffAction.kt index 5024c14b16cb..a61a8f3c57de 100644 --- a/plugins/git4idea/src/git4idea/index/actions/GitStageDiffAction.kt +++ b/plugins/git4idea/src/git4idea/index/actions/GitStageDiffAction.kt @@ -11,31 +11,35 @@ import com.intellij.util.containers.isEmpty import git4idea.index.createThreeSidesDiffRequestProducer import git4idea.index.createTwoSidesDiffRequestProducer import git4idea.index.ui.GIT_FILE_STATUS_NODES_STREAM -import git4idea.index.ui.GIT_STAGE_TRACKER -import kotlin.streams.toList +import git4idea.index.ui.GIT_STAGE_TREE class GitStageDiffAction : AnActionExtensionProvider { - override fun isActive(e: AnActionEvent): Boolean = e.getData(GIT_STAGE_TRACKER) != null + override fun isActive(e: AnActionEvent): Boolean = e.getData(GIT_STAGE_TREE) != null override fun update(e: AnActionEvent) { - e.presentation.isEnabled = e.project != null && !e.getData(GIT_FILE_STATUS_NODES_STREAM).isEmpty() + e.presentation.isEnabled = e.project != null && + !e.getData(GIT_FILE_STATUS_NODES_STREAM).isEmpty() e.presentation.isVisible = e.presentation.isEnabled || e.isFromActionToolbar } override fun actionPerformed(e: AnActionEvent) { - val producers = e.getRequiredData(GIT_FILE_STATUS_NODES_STREAM).map { createTwoSidesDiffRequestProducer(e.project!!, it) }.toList() - DiffManager.getInstance().showDiff(e.project, ChangeDiffRequestChain(producers, 0), DiffDialogHints.DEFAULT) + val producers = e.getRequiredData(GIT_STAGE_TREE).statusNodesListSelection(true) + .map { createTwoSidesDiffRequestProducer(e.project!!, it) } + DiffManager.getInstance().showDiff(e.project, ChangeDiffRequestChain(producers.list, producers.selectedIndex), DiffDialogHints.DEFAULT) } } class GitStageThreeSideDiffAction : DumbAwareAction() { override fun update(e: AnActionEvent) { - e.presentation.isEnabled = e.project != null && !e.getData(GIT_FILE_STATUS_NODES_STREAM).isEmpty() + e.presentation.isEnabled = e.project != null && + e.getData(GIT_STAGE_TREE) != null && + !e.getData(GIT_FILE_STATUS_NODES_STREAM).isEmpty() e.presentation.isVisible = e.presentation.isEnabled || e.isFromActionToolbar } override fun actionPerformed(e: AnActionEvent) { - val producers = e.getRequiredData(GIT_FILE_STATUS_NODES_STREAM).map { createThreeSidesDiffRequestProducer(e.project!!, it) }.toList() - DiffManager.getInstance().showDiff(e.project, ChangeDiffRequestChain(producers, 0), DiffDialogHints.DEFAULT) + val producers = e.getRequiredData(GIT_STAGE_TREE).statusNodesListSelection(false) + .map { createThreeSidesDiffRequestProducer(e.project!!, it) } + DiffManager.getInstance().showDiff(e.project, ChangeDiffRequestChain(producers.list, producers.selectedIndex), DiffDialogHints.DEFAULT) } } \ No newline at end of file diff --git a/plugins/git4idea/src/git4idea/index/ui/GitStageTree.kt b/plugins/git4idea/src/git4idea/index/ui/GitStageTree.kt index 98fa1d55b1cd..0ee6114125ce 100644 --- a/plugins/git4idea/src/git4idea/index/ui/GitStageTree.kt +++ b/plugins/git4idea/src/git4idea/index/ui/GitStageTree.kt @@ -6,6 +6,7 @@ import com.intellij.ide.dnd.DnDDragStartBean import com.intellij.ide.dnd.DnDEvent import com.intellij.ide.util.treeView.TreeState import com.intellij.openapi.Disposable +import com.intellij.openapi.ListSelection import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.actionSystem.DataKey import com.intellij.openapi.actionSystem.PlatformDataKeys @@ -27,6 +28,7 @@ import com.intellij.ui.ClickListener import com.intellij.ui.SimpleTextAttributes import com.intellij.ui.components.JBLabel import com.intellij.util.FontUtil +import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.isEmpty import git4idea.conflicts.getConflictType import git4idea.i18n.GitBundle @@ -50,6 +52,7 @@ import javax.swing.tree.TreePath import kotlin.streams.toList val GIT_FILE_STATUS_NODES_STREAM = DataKey.create>("GitFileStatusNodesStream") +val GIT_STAGE_TREE = DataKey.create("GitStageTree") abstract class GitStageTree(project: Project, parentDisposable: Disposable) : ChangesTree(project, false, true) { private var hoverNode: ChangesBrowserNode<*>? = null @@ -113,6 +116,7 @@ abstract class GitStageTree(project: Project, parentDisposable: Disposable) : Ch override fun getData(dataId: String): Any? { return when { + GIT_STAGE_TREE.`is`(dataId) -> this GIT_FILE_STATUS_NODES_STREAM.`is`(dataId) -> selectedStatusNodes() VcsDataKeys.FILE_PATH_STREAM.`is`(dataId) -> selectedStatusNodes().map { it.filePath } VcsDataKeys.VIRTUAL_FILE_STREAM.`is`(dataId) -> selectedStatusNodes().map { it.filePath.virtualFile }.filter { it != null } @@ -126,11 +130,40 @@ abstract class GitStageTree(project: Project, parentDisposable: Disposable) : Ch } fun selectedStatusNodes(): Stream { - return VcsTreeModelData.selected(this).userObjectsStream() - .filter { it is GitFileStatusNode } - .map { it as GitFileStatusNode } + return VcsTreeModelData.selected(this).userObjectsStream(GitFileStatusNode::class.java) } + fun statusNodesListSelection(preferLimitedContext: Boolean): ListSelection { + val entries = VcsTreeModelData.selected(this).userObjects(GitFileStatusNode::class.java) + if (entries.size > 1) { + return ListSelection.createAt(entries, 0) + } + + val selected = entries.singleOrNull() + val selectedKind = selected?.kind + + val allEntriesData: VcsTreeModelData = when { + preferLimitedContext && (selectedKind == NodeKind.UNSTAGED || selectedKind == NodeKind.UNTRACKED) -> { + VcsTreeModelData.allUnderTag(this, NodeKind.UNSTAGED) + } + preferLimitedContext && selectedKind == NodeKind.STAGED -> { + VcsTreeModelData.allUnderTag(this, NodeKind.STAGED) + } + else -> { + VcsTreeModelData.all(this) + } + } + + val allEntries = allEntriesData.userObjects(GitFileStatusNode::class.java) + return if (allEntries.size <= entries.size) { + ListSelection.createAt(entries, 0) + } + else { + ListSelection.create(allEntries, selected) + } + } + + private inner class MyTreeModelBuilder internal constructor(project: Project, grouping: ChangesGroupingPolicyFactory) : TreeModelBuilder(project, grouping) { private val parentNodes: MutableMap = mutableMapOf()