diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java index c6717ad747d4..5d4427290e1a 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java @@ -16,7 +16,11 @@ package com.intellij.vcs.log.graph.utils; +import com.intellij.openapi.util.Pair; import com.intellij.util.containers.IntStack; +import com.intellij.util.containers.Stack; +import com.intellij.vcs.log.graph.api.LiteLinearGraph; +import com.intellij.vcs.log.graph.utils.impl.BitSetFlags; import org.jetbrains.annotations.NotNull; public class DfsUtil { @@ -27,6 +31,66 @@ public class DfsUtil { int fun(int currentNode); } + public interface NodeVisitor { + void enterNode(int node); + + void exitNode(int node); + } + + /* + * Depth-first walk for a graph. For each node, walks both into upward and downward siblings. + * Tries to preserve direction of travel: when a node is entered from up-sibling, goes to the down-siblings first. + * Then goes to the other up-siblings. + * And when a node is entered from down-sibling, goes to the up-siblings first. + * Then goes to the other down-siblings. + * When a node is entered the first time, enterNode is called. + * When a node is passes in the same direction, exitNode is called. + * Nothing is called when a all the siblings of the node are visited. + */ + public static void walk(@NotNull LiteLinearGraph graph, int start, @NotNull NodeVisitor visitor) { + BitSetFlags visited = new BitSetFlags(graph.nodesCount(), false); + BitSetFlags visitedInSameDirection = new BitSetFlags(graph.nodesCount(), false); + + Stack> stack = new Stack<>(); + stack.push(new Pair<>(start, true)); // commit + direction of travel + + while (!stack.empty()) { + int currentNode = stack.peek().first; + boolean down = stack.peek().second; + if (!visited.get(currentNode)) { + visited.set(currentNode, true); + visitor.enterNode(currentNode); + } + + boolean found = false; + for (int nextNode : graph.getNodes(currentNode, down ? LiteLinearGraph.NodeFilter.DOWN : LiteLinearGraph.NodeFilter.UP)) { + if (!visited.get(nextNode)) { + stack.push(new Pair<>(nextNode, down)); + found = true; + break; + } + } + + if (!found) { + if (!visitedInSameDirection.get(currentNode)) { + visitedInSameDirection.set(currentNode, true); + visitor.exitNode(currentNode); + } + for (int nextNode : graph.getNodes(currentNode, down ? LiteLinearGraph.NodeFilter.UP : LiteLinearGraph.NodeFilter.DOWN)) { + if (!visited.get(nextNode)) { + stack.push(new Pair<>(nextNode, !down)); + found = true; + break; + } + } + } + + if (!found) { + stack.pop(); + } + } + } + public static void walk(int startRowIndex, @NotNull NextNode nextNodeFun) { IntStack stack = new IntStack(); stack.push(startRowIndex); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/history/FileHistoryFilterer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/history/FileHistoryFilterer.java index 1e408dc54893..3c1a3100e3af 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/history/FileHistoryFilterer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/history/FileHistoryFilterer.java @@ -15,7 +15,6 @@ */ package com.intellij.vcs.log.ui.history; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vfs.VirtualFile; @@ -32,13 +31,12 @@ import com.intellij.vcs.log.data.index.IndexDataGetter; import com.intellij.vcs.log.graph.PermanentGraph; import com.intellij.vcs.log.graph.VisibleGraph; import com.intellij.vcs.log.graph.api.LinearGraph; -import com.intellij.vcs.log.graph.api.LiteLinearGraph; import com.intellij.vcs.log.graph.impl.facade.PermanentGraphImpl; import com.intellij.vcs.log.graph.impl.facade.ReachableNodes; import com.intellij.vcs.log.graph.impl.facade.VisibleGraphImpl; import com.intellij.vcs.log.graph.impl.permanent.PermanentCommitsInfoImpl; +import com.intellij.vcs.log.graph.utils.DfsUtil; import com.intellij.vcs.log.graph.utils.LinearGraphUtils; -import com.intellij.vcs.log.graph.utils.impl.BitSetFlags; import com.intellij.vcs.log.visible.VcsLogFilterer; import com.intellij.vcs.log.visible.VisiblePack; import com.intellij.vcsUtil.VcsUtil; @@ -136,57 +134,7 @@ class FileHistoryFilterer extends VcsLogFilterer { return 0; } - private static void walk(@NotNull LiteLinearGraph graph, int start, @NotNull NodeVisitor visitor) { - BitSetFlags visited = new BitSetFlags(graph.nodesCount(), false); - BitSetFlags visitedInSameDirection = new BitSetFlags(graph.nodesCount(), false); - - Stack> stack = new Stack<>(); - stack.push(new Pair<>(start, true)); // commit + direction of travel - - while (!stack.empty()) { - int currentNode = stack.peek().first; - boolean down = stack.peek().second; - if (!visited.get(currentNode)) { - visited.set(currentNode, true); - visitor.enterNode(currentNode); - } - - boolean found = false; - for (int nextNode : graph.getNodes(currentNode, down ? LiteLinearGraph.NodeFilter.DOWN : LiteLinearGraph.NodeFilter.UP)) { - if (!visited.get(nextNode)) { - stack.push(new Pair<>(nextNode, down)); - found = true; - break; - } - } - - if (!found) { - if (!visitedInSameDirection.get(currentNode)) { - visitedInSameDirection.set(currentNode, true); - visitor.exitNode(currentNode); - } - for (int nextNode : graph.getNodes(currentNode, down ? LiteLinearGraph.NodeFilter.UP : LiteLinearGraph.NodeFilter.DOWN)) { - if (!visited.get(nextNode)) { - stack.push(new Pair<>(nextNode, !down)); - found = true; - break; - } - } - } - - if (!found) { - stack.pop(); - } - } - } - - public interface NodeVisitor { - void enterNode(int node); - - void exitNode(int node); - } - - private static class FileHistoryRefiner implements NodeVisitor { + private static class FileHistoryRefiner implements DfsUtil.NodeVisitor { @NotNull private final VisibleGraph myVisibleGraph; @NotNull private final IndexDataGetter.FileNamesData myNamesData; @NotNull private final Stack myPaths; @@ -206,7 +154,7 @@ class FileHistoryFilterer extends VcsLogFilterer { public boolean refine(@NotNull LinearGraph graph, int row, @NotNull FilePath startPath) { myPaths.push(startPath); - walk(LinearGraphUtils.asLiteLinearGraph(graph), row, this); + DfsUtil.walk(LinearGraphUtils.asLiteLinearGraph(graph), row, this); return myWasChanged; }