[file-history] when revision filter in revision history is not set, jump to specified or nearest commit

This commit is contained in:
Julia Beliaeva
2018-08-14 16:42:05 +03:00
parent d4470eaaba
commit ec86ba9281
5 changed files with 93 additions and 48 deletions
@@ -2,7 +2,6 @@
package com.intellij.vcs.log.history
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Ref
import com.intellij.openapi.vcs.FilePath
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.Stack
@@ -51,7 +50,7 @@ internal class FileHistoryBuilder(private val startCommit: Int?,
val visibleLinearGraph = controller.compiledGraph
val row = startCommit?.let {
findAncestorRowAffectingFile(startCommit, startPath, visibleLinearGraph, permanentGraphInfo, fileNamesData)
findAncestorRowAffectingFile(startCommit, visibleLinearGraph, permanentGraphInfo)
} ?: 0
if (row >= 0) {
val refiner = FileHistoryRefiner(visibleLinearGraph, permanentGraphInfo, fileNamesData)
@@ -66,6 +65,14 @@ internal class FileHistoryBuilder(private val startCommit: Int?,
return fileNamesData.buildPathsMap()
}
private fun findAncestorRowAffectingFile(commitId: Int,
visibleLinearGraph: LinearGraph,
permanentGraphInfo: PermanentGraphInfo<Int>): Int {
return findVisibleAncestorRow(commitId, visibleLinearGraph, permanentGraphInfo) { nodeId ->
fileNamesData.affects(permanentGraphInfo.permanentCommitsInfo.getCommitId(nodeId), startPath)
} ?: -1
}
companion object {
private val LOG = Logger.getInstance(FileHistoryBuilder::class.java)
}
@@ -110,33 +117,6 @@ private fun hideTrivialMerge(collapsedGraph: CollapsedGraph, graph: LiteLinearGr
}
}
internal fun findAncestorRowAffectingFile(commitId: Int,
filePath: FilePath,
visibleLinearGraph: LinearGraph,
permanentGraphInfo: PermanentGraphInfo<Int>,
fileNamesData: FileNamesData): Int {
val resultNodeId = Ref<Int>()
val commitsInfo = permanentGraphInfo.permanentCommitsInfo
val reachableNodes = ReachableNodes(LinearGraphUtils.asLiteLinearGraph(permanentGraphInfo.linearGraph))
reachableNodes.walk(setOf(commitsInfo.getNodeId(commitId)), true) { currentNode ->
val id = commitsInfo.getCommitId(currentNode)
if (fileNamesData.affects(id, filePath)) {
resultNodeId.set(currentNode)
false // stop walk, we have found it
}
else {
true // continue walk
}
}
if (!resultNodeId.isNull) {
return visibleLinearGraph.getNodeIndex(resultNodeId.get())!!
}
return -1
}
internal class FileHistoryRefiner(private val visibleLinearGraph: LinearGraph,
permanentGraphInfo: PermanentGraphInfo<Int>,
private val namesData: FileNamesData) : DfsUtil.NodeVisitor {
@@ -33,12 +33,8 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsLogFilterCollection;
import com.intellij.vcs.log.VcsLogFilterUi;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.VcsLogData;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.*;
import com.intellij.vcs.log.data.index.IndexDataGetter;
import com.intellij.vcs.log.impl.CommonUiProperties;
import com.intellij.vcs.log.impl.VcsLogContentUtil;
@@ -72,6 +68,7 @@ public class FileHistoryUi extends AbstractVcsLogUi {
@NotNull private final FileHistoryFilterUi myFilterUi;
@NotNull private final FilePath myPath;
@Nullable private final Hash myRevision;
@NotNull private final VirtualFile myRoot;
@NotNull private final FileHistoryPanel myFileHistoryPanel;
@NotNull private final IndexDataGetter myIndexDataGetter;
@NotNull private final MyPropertiesChangeListener myPropertiesChangeListener;
@@ -88,6 +85,7 @@ public class FileHistoryUi extends AbstractVcsLogUi {
myIndexDataGetter = ObjectUtils.assertNotNull(logData.getIndex().getDataGetter());
myRevision = revision;
myRoot = root;
myFilterUi = new FileHistoryFilterUi(path, revision, root, uiProperties);
myPath = path;
myFileHistoryPanel = new FileHistoryPanel(this, logData, myVisiblePack, path);
@@ -250,14 +248,20 @@ public class FileHistoryUi extends AbstractVcsLogUi {
}
}
@NotNull
public FilePath getPath() {
return myPath;
public void jumpToNearestCommit(@NotNull Hash hash) {
jumpTo(hash, (model, h) -> {
if (!myLogData.getStorage().containsCommit(new CommitId(h, myRoot))) return GraphTableModel.COMMIT_NOT_FOUND;
int commitIndex = myLogData.getCommitIndex(h, myRoot);
Integer rowIndex = myVisiblePack.getVisibleGraph().getVisibleRowIndex(commitIndex);
if (rowIndex == null) {
rowIndex = ReachableNodesUtilKt.findVisibleAncestorRow(commitIndex, myVisiblePack);
}
return rowIndex == null ? GraphTableModel.COMMIT_DOES_NOT_MATCH : rowIndex;
}, SettableFuture.create());
}
@Nullable
public Hash getRevision() {
return myRevision;
public boolean matches(@NotNull FilePath targetPath, @Nullable Hash targetRevision) {
return myPath.equals(targetPath) && Objects.equals(myRevision, targetRevision);
}
@NotNull
@@ -0,0 +1,47 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.vcs.log.history
import com.intellij.openapi.util.Ref
import com.intellij.vcs.log.data.DataPack
import com.intellij.vcs.log.graph.api.LinearGraph
import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo
import com.intellij.vcs.log.graph.impl.facade.ReachableNodes
import com.intellij.vcs.log.graph.impl.facade.VisibleGraphImpl
import com.intellij.vcs.log.graph.utils.LinearGraphUtils
import com.intellij.vcs.log.visible.VisiblePack
fun findMatchingAncestorNodeId(commitId: Int, permanentGraphInfo: PermanentGraphInfo<Int>, condition: (Int) -> Boolean): Int? {
val resultNodeId = Ref<Int>()
val startNodeId = permanentGraphInfo.permanentCommitsInfo.getNodeId(commitId)
val reachableNodes = ReachableNodes(LinearGraphUtils.asLiteLinearGraph(permanentGraphInfo.linearGraph))
reachableNodes.walk(setOf(startNodeId), true) { currentNodeId ->
if (condition(currentNodeId)) {
resultNodeId.set(currentNodeId)
false // stop walk, we have found it
}
else {
true // continue walk
}
}
return resultNodeId.get()
}
fun findVisibleAncestorRow(commitId: Int, visiblePack: VisiblePack): Int? {
val dataPack = visiblePack.dataPack
val visibleGraph = visiblePack.visibleGraph
if (dataPack is DataPack && dataPack.permanentGraph is PermanentGraphInfo<*> && visibleGraph is VisibleGraphImpl) {
return findVisibleAncestorRow(commitId, visibleGraph.linearGraph, dataPack.permanentGraph as PermanentGraphInfo<Int>) { _ -> true }
}
return null
}
fun findVisibleAncestorRow(commitId: Int,
visibleLinearGraph: LinearGraph,
permanentGraphInfo: PermanentGraphInfo<Int>,
condition: (Int) -> Boolean): Int? {
val nodeId = findMatchingAncestorNodeId(commitId, permanentGraphInfo) { nodeId ->
condition(nodeId) && visibleLinearGraph.getNodeIndex(nodeId) != null
} ?: return null
return visibleLinearGraph.getNodeIndex(nodeId)
}
@@ -22,6 +22,7 @@ import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsLogFileHistoryProvider;
import com.intellij.vcs.log.VcsLogFilterCollection;
import com.intellij.vcs.log.data.VcsLogData;
import com.intellij.vcs.log.impl.HashImpl;
import com.intellij.vcs.log.impl.VcsLogContentUtil;
@@ -30,8 +31,6 @@ import com.intellij.vcs.log.impl.VcsProjectLog;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class VcsLogFileHistoryProviderImpl implements VcsLogFileHistoryProvider {
@NotNull
public static final String TAB_NAME = "History";
@@ -52,12 +51,18 @@ public class VcsLogFileHistoryProviderImpl implements VcsLogFileHistoryProvider
@Override
public void showFileHistory(@NotNull Project project, @NotNull FilePath path, @Nullable String revisionNumber) {
Hash hash = (revisionNumber != null) ? HashImpl.build(revisionNumber) : null;
if (!VcsLogContentUtil.findAndSelectContent(project, FileHistoryUi.class,
ui -> ui.getPath().equals(path) && Objects.equals(ui.getRevision(), hash))) {
FileHistoryUi fileHistoryUi = VcsLogContentUtil.findAndSelect(project, FileHistoryUi.class,
ui -> ui.matches(path, hash));
if (fileHistoryUi == null) {
VcsLogManager logManager = VcsProjectLog.getInstance(project).getLogManager();
assert logManager != null;
String suffix = hash != null ? " (" + hash.toShortString() + ")" : "";
VcsLogContentUtil.openLogTab(project, logManager, TAB_NAME, path.getName() + suffix, new FileHistoryUiFactory(path, hash), true);
fileHistoryUi = VcsLogContentUtil.openLogTab(project, logManager, TAB_NAME, path.getName() + suffix,
new FileHistoryUiFactory(path, hash), true);
}
if (hash != null && fileHistoryUi.getFilterUi().getFilters().get(VcsLogFilterCollection.REVISION_FILTER) == null) {
fileHistoryUi.jumpToNearestCommit(hash);
}
}
}
@@ -65,6 +65,13 @@ public class VcsLogContentUtil {
public static <U extends AbstractVcsLogUi> boolean findAndSelectContent(@NotNull Project project,
@NotNull Class<U> clazz,
@NotNull Condition<U> condition) {
return findAndSelect(project, clazz, condition) != null;
}
@Nullable
public static <U extends AbstractVcsLogUi> U findAndSelect(@NotNull Project project,
@NotNull Class<U> clazz,
@NotNull Condition<U> condition) {
ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.VCS);
ContentManager manager = toolWindow.getContentManager();
@@ -76,10 +83,12 @@ public class VcsLogContentUtil {
}
return false;
});
if (component == null) return false;
if (component == null) return null;
if (!toolWindow.isVisible()) toolWindow.activate(null);
return ContentUtilEx.selectContent(manager, component, true);
if (!ContentUtilEx.selectContent(manager, component, true)) return null;
//noinspection unchecked
return (U)getLogUi(component);
}
public static boolean selectLogUi(@NotNull Project project, @NotNull VcsLogUi ui) {