diff --git a/platform/dvcs-impl/src/META-INF/dvcs.xml b/platform/dvcs-impl/src/META-INF/dvcs.xml index c045e58a9e3a..1541db0caaea 100644 --- a/platform/dvcs-impl/src/META-INF/dvcs.xml +++ b/platform/dvcs-impl/src/META-INF/dvcs.xml @@ -38,6 +38,7 @@ + diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/CurrentBranchHighlighter.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/CurrentBranchHighlighter.java new file mode 100644 index 000000000000..3849f6761a07 --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/CurrentBranchHighlighter.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.dvcs.ui; + +import com.intellij.dvcs.repo.Repository; +import com.intellij.dvcs.repo.VcsRepositoryManager; +import com.intellij.openapi.util.Condition; +import com.intellij.ui.JBColor; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsCommitStyleFactory; +import com.intellij.vcs.log.VcsLogHighlighter; +import com.intellij.vcs.log.VcsShortCommitDetails; +import com.intellij.vcs.log.data.LoadingDetails; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.data.VcsLogUiProperties; +import com.intellij.vcs.log.ui.VcsLogHighlighterCreator; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +public class CurrentBranchHighlighter implements VcsLogHighlighter { + private static final JBColor CURRENT_BRANCH_BG = new JBColor(new Color(228, 250, 255), new Color(63, 71, 73)); + @NotNull private final VcsLogUiProperties myUiProperties; + @NotNull private final VcsLogDataHolder myDataHolder; + @NotNull private final VcsRepositoryManager myRepositoryManager; + + public CurrentBranchHighlighter(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties) { + myDataHolder = logDataHolder; + myUiProperties = uiProperties; + myRepositoryManager = myDataHolder.getProject().getComponent(VcsRepositoryManager.class); + } + + @NotNull + @Override + public VcsCommitStyle getStyle(int commitIndex, boolean isSelected) { + if (isSelected || !myUiProperties.isHighlightCurrentBranch()) return VcsCommitStyle.DEFAULT; + VcsShortCommitDetails details = myDataHolder.getMiniDetailsGetter().getCommitDataIfAvailable(commitIndex); + if (details != null && !(details instanceof LoadingDetails)) { + Repository repo = myRepositoryManager.getRepositoryForRoot(details.getRoot()); + if (repo != null) { + String currentBranch = repo.getCurrentBranchName(); + if (currentBranch == null) { + if (repo.getCurrentRevision() != null) currentBranch = "HEAD"; // does this work for hg? + } + if (currentBranch != null) { + Condition checker = myDataHolder.getContainingBranchesGetter().getBranchChecker(currentBranch, details.getRoot()); + if (checker.value(details.getId())) { + return VcsCommitStyleFactory.background(CURRENT_BRANCH_BG); + } + } + } + } + return VcsCommitStyle.DEFAULT; + } + + public static class Creator implements VcsLogHighlighterCreator { + @NotNull + @Override + public VcsLogHighlighter createHighlighter(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties) { + return new CurrentBranchHighlighter(logDataHolder, uiProperties); + } + } +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java index 436b5800edd4..e09eef093a36 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java @@ -47,4 +47,7 @@ public interface VcsLogUi { boolean isHighlightMyCommits(); void setHighlightMyCommits(boolean state); + + void setHighlightCurrentBranch(boolean state); + boolean isHighlightCurrentBranch(); } diff --git a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/PermanentGraph.java b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/PermanentGraph.java index 9b1088e8ee2f..c9f90386fc20 100644 --- a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/PermanentGraph.java +++ b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/PermanentGraph.java @@ -15,6 +15,7 @@ */ package com.intellij.vcs.log.graph; +import com.intellij.openapi.util.Condition; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,6 +45,9 @@ public interface PermanentGraph { @NotNull Set getContainingBranches(@NotNull CommitId commit); + @NotNull + Condition getBranchChecker(@NotNull CommitId currentBranchHead); + enum SortType { Normal("Off", "Sort commits topologically and by date"), Bek("Standard", "In case of merge show incoming commits first (directly below merge commit)"), diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ContainingBranchesGetter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ContainingBranchesGetter.java index 3a9a25fdf7b6..5f5a1f0d78f4 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ContainingBranchesGetter.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ContainingBranchesGetter.java @@ -16,6 +16,7 @@ package com.intellij.vcs.log.graph.impl.facade; +import com.intellij.util.Consumer; import com.intellij.vcs.log.graph.api.LiteLinearGraph; import com.intellij.vcs.log.graph.utils.DfsUtil; import com.intellij.vcs.log.graph.utils.Flags; @@ -67,4 +68,25 @@ public class ContainingBranchesGetter { private void checkAndAdd(int nodeIndex, Set result) { if (myBranchNodeIndexes.contains(nodeIndex)) result.add(nodeIndex); } + + public void walkBranch(int branchHead, @NotNull final Consumer consumer) { + myTempFlags.setAll(false); + myTempFlags.set(branchHead, true); + consumer.consume(branchHead); + + myDfsUtil.nodeDfsIterator(branchHead, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + for (int downNode : myGraph.getNodes(currentNode, LiteLinearGraph.NodeFilter.DOWN)) { + if (!myTempFlags.get(downNode)) { + myTempFlags.set(downNode, true); + consumer.consume(downNode); + return downNode; + } + } + + return NODE_NOT_FOUND; + } + }); + } } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java index 91d9842bc346..ac12288d9df8 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java @@ -17,6 +17,8 @@ package com.intellij.vcs.log.graph.impl.facade; +import com.intellij.openapi.util.Condition; +import com.intellij.util.Consumer; import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.graph.*; @@ -28,6 +30,7 @@ import com.intellij.vcs.log.graph.impl.facade.bek.BekSorter; import com.intellij.vcs.log.graph.impl.permanent.*; import com.intellij.vcs.log.graph.linearBek.LinearBekController; import com.intellij.vcs.log.graph.utils.LinearGraphUtils; +import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -163,6 +166,41 @@ public class PermanentGraphImpl implements PermanentGraph, P return myPermanentCommitsInfo.convertToCommitIdSet(myBranchesGetter.getBranchNodeIndexes(commitIndex)); } + @NotNull + @Override + public Condition getBranchChecker(@NotNull final CommitId head) { + if (head instanceof Integer) { + final TIntHashSet branchNodes = new TIntHashSet(); + myBranchesGetter.walkBranch(myPermanentCommitsInfo.getNodeId(head), new Consumer() { + @Override + public void consume(Integer node) { + branchNodes.add((Integer)myPermanentCommitsInfo.getCommitId(node)); + } + }); + return new Condition() { + @Override + public boolean value(CommitId commitId) { + return branchNodes.contains((Integer)commitId); + } + }; + } + else { + final TIntHashSet branchNodes = new TIntHashSet(); + myBranchesGetter.walkBranch(myPermanentCommitsInfo.getNodeId(head), new Consumer() { + @Override + public void consume(Integer node) { + branchNodes.add(node); + } + }); + return new Condition() { + @Override + public boolean value(CommitId commitId) { + return branchNodes.contains(myPermanentCommitsInfo.getNodeId(commitId)); + } + }; + } + } + @NotNull public PermanentCommitsInfoIml getPermanentCommitsInfo() { return myPermanentCommitsInfo; diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index 28f337df10ee..c676ec5ad5e1 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -2,6 +2,7 @@ + @@ -14,6 +15,8 @@ + + diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/ContainingBranchesGetter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/ContainingBranchesGetter.java index 1aa3e3fcdb02..b04e4a4e9955 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/ContainingBranchesGetter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/ContainingBranchesGetter.java @@ -18,6 +18,9 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ThrowableConsumer; @@ -47,6 +50,7 @@ public class ContainingBranchesGetter implements VcsLogListener { private int myCurrentBranchesChecksum; @Nullable private VcsLogRefs myRefs; @Nullable private PermanentGraph myGraph; + @NotNull private volatile Map, BranchChecker> myCheckers = ContainerUtil.newHashMap(); ContainingBranchesGetter(@NotNull VcsLogDataHolder dataHolder, @NotNull Disposable parentDisposable) { myDataHolder = dataHolder; @@ -84,6 +88,11 @@ public class ContainingBranchesGetter implements VcsLogListener { private void clearCache() { myCache = createCache(); myTaskExecutor.clear(); + Map, BranchChecker> checkers = myCheckers; + myCheckers = ContainerUtil.newHashMap(); + for (BranchChecker c : checkers.values()) { + c.dispose(); + } // re-request containing branches information for the commit user (possibly) currently stays on ApplicationManager.getApplication().invokeLater(new Runnable() { @Override @@ -114,13 +123,36 @@ public class ContainingBranchesGetter implements VcsLogListener { */ @Nullable public List requestContainingBranches(@NotNull VirtualFile root, @NotNull Hash hash) { - List refs = myCache.get(hash); + List refs = getContainingBranchesIfAvailable(hash); if (refs == null) { myTaskExecutor.queue(new Task(root, hash, myCache, myGraph, myRefs)); } return refs; } + @Nullable + public List getContainingBranchesIfAvailable(@NotNull Hash hash) { + return myCache.get(hash); + } + + @NotNull + public Condition getBranchChecker(@NotNull final String branchName, @NotNull final VirtualFile root) { + if (myRefs == null || myGraph == null) return Conditions.alwaysFalse(); + VcsRef branchRef = ContainerUtil.find(myRefs.getBranches(), new Condition() { + @Override + public boolean value(VcsRef vcsRef) { + return vcsRef.getRoot().equals(root) && vcsRef.getName().equals(branchName); + } + }); + if (branchRef == null) return Conditions.alwaysFalse(); + BranchChecker checker = myCheckers.get(Pair.create(root, branchName)); + if (checker == null) { + checker = new BranchChecker(myGraph.getBranchChecker(myDataHolder.getCommitIndex(branchRef.getCommitHash()))); + myCheckers.put(Pair.create(root, branchName), checker); + } + return checker; + } + @NotNull private static SLRUMap> createCache() { return new SLRUMap>(1000, 1000); @@ -179,4 +211,22 @@ public class ContainingBranchesGetter implements VcsLogListener { } } + private class BranchChecker implements Condition { + @NotNull private final Condition myChecker; + private volatile boolean isDisposed = false; + + public BranchChecker(@NotNull Condition checker) { + myChecker = checker; + } + + @Override + public boolean value(Hash hash) { + if (isDisposed) return false; + return myChecker.value(myDataHolder.getCommitIndex(hash)); + } + + public void dispose() { + isDisposed = true; + } + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyPermanentGraph.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyPermanentGraph.java index cfdf3212307a..6a3a83fc8acd 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyPermanentGraph.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyPermanentGraph.java @@ -15,6 +15,8 @@ */ package com.intellij.vcs.log.data; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.vcs.log.graph.GraphCommit; import com.intellij.vcs.log.graph.PermanentGraph; import com.intellij.vcs.log.graph.VisibleGraph; @@ -59,4 +61,10 @@ public class EmptyPermanentGraph implements PermanentGraph { return Collections.emptySet(); } + @NotNull + @Override + public Condition getBranchChecker(@NotNull Integer currentBranchHead) { + return Conditions.alwaysFalse(); + } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java index e37a25a378bf..3d6daf5fb349 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java @@ -48,6 +48,7 @@ public class VcsLogUiProperties implements PersistentStateComponent RECENTLY_FILTERED_USER_GROUPS = new ArrayDeque(); public Deque RECENTLY_FILTERED_BRANCH_GROUPS = new ArrayDeque(); } @@ -147,6 +148,14 @@ public class VcsLogUiProperties implements PersistentStateComponent users = new ArrayList(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/MyCommitsHighlighter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/MyCommitsHighlighter.java index 0a22bee55eaa..bdbdc8fd0104 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/MyCommitsHighlighter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/MyCommitsHighlighter.java @@ -54,4 +54,12 @@ public class MyCommitsHighlighter implements VcsLogHighlighter { } return VcsCommitStyle.DEFAULT; } + + public static class Creator implements VcsLogHighlighterCreator { + @NotNull + @Override + public VcsLogHighlighter createHighlighter(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties) { + return new MyCommitsHighlighter(logDataHolder, uiProperties); + } + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogHighlighterCreator.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogHighlighterCreator.java new file mode 100644 index 000000000000..be7cfd3a4395 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogHighlighterCreator.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui; + +import com.intellij.vcs.log.VcsLogHighlighter; +import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.data.VcsLogUiProperties; +import org.jetbrains.annotations.NotNull; + +public interface VcsLogHighlighterCreator { + @NotNull + VcsLogHighlighter createHighlighter(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties); +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java index 8dfc100f1937..6c2b39ec48ca 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java @@ -4,6 +4,8 @@ import com.google.common.util.concurrent.SettableFuture; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -33,6 +35,8 @@ import java.util.Collection; import java.util.concurrent.Future; public class VcsLogUiImpl implements VcsLogUi, Disposable { + public static final ExtensionPointName LOG_HIGHLIGHTER_CREATOR_EP = + ExtensionPointName.create("com.intellij.logHighlighterCreator"); public static final String POPUP_ACTION_GROUP = "Vcs.Log.ContextMenu"; public static final String TOOLBAR_ACTION_GROUP = "Vcs.Log.Toolbar"; @@ -69,7 +73,10 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { myLog = new VcsLogImpl(logDataHolder, this); myVisiblePack = VisiblePack.EMPTY; myMainFrame = new MainFrame(logDataHolder, this, project, settings, uiProperties, myLog, myVisiblePack); - addHighlighter(new MyCommitsHighlighter(myLogDataHolder, myUiProperties)); + + for (VcsLogHighlighterCreator creator : Extensions.getExtensions(LOG_HIGHLIGHTER_CREATOR_EP, myProject)) { + addHighlighter(creator.createHighlighter(myLogDataHolder, myUiProperties)); + } } public void setVisiblePack(@NotNull VisiblePack pack) { @@ -164,6 +171,17 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { repaintUI(); } + @Override + public void setHighlightCurrentBranch(boolean state) { + myUiProperties.setHighlightCurrentBranch(state); + repaintUI(); + } + + @Override + public boolean isHighlightCurrentBranch() { + return myUiProperties.isHighlightCurrentBranch(); + } + @Override public boolean areGraphActionsEnabled() { return myMainFrame.areGraphActionsEnabled(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java index c852a6975e29..e2efba70486a 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java @@ -71,7 +71,8 @@ public class VcsLogQuickSettingsActions extends DumbAwareAction { @NotNull @Override public AnAction[] getChildren(@Nullable AnActionEvent e) { - return new AnAction[]{new ShowBranchesPanelAction(), new ShowRootsColumnAction(), new HighlightMyCommitsAction()}; + return new AnAction[]{new ShowBranchesPanelAction(), new ShowRootsColumnAction(), new HighlightMyCommitsAction(), + new HighlightCurrentBranchAction()}; } private class ShowBranchesPanelAction extends ToggleAction implements DumbAware { @@ -131,5 +132,22 @@ public class VcsLogQuickSettingsActions extends DumbAwareAction { myUi.setHighlightMyCommits(state); } } + + private class HighlightCurrentBranchAction extends ToggleAction implements DumbAware { + + public HighlightCurrentBranchAction() { + super("Highlight Current Branch"); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myUi.isHighlightCurrentBranch(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myUi.setHighlightCurrentBranch(state); + } + } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java index 2d21d26793b0..a0f54b72aba4 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java @@ -156,6 +156,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { @Override public void run() { myDetailsPanel.valueChanged(null); + myGraphTable.repaint(); // we may need to repaint highlighters } }); }