[vcs-log] highlighter for current branch

* create branch checker with precalculated branch content
* place "Highlight ..." action under gear
* make highlighters services
This commit is contained in:
Julia Beliaeva
2015-06-09 17:13:06 +03:00
parent ce6ce361e3
commit 069125da9c
15 changed files with 288 additions and 3 deletions
+1
View File
@@ -38,6 +38,7 @@
<extensions defaultExtensionNs="com.intellij">
<projectService serviceImplementation="com.intellij.dvcs.push.PushSettings"/>
<logHighlighterCreator implementation="com.intellij.dvcs.ui.CurrentBranchHighlighter$Creator"/>
</extensions>
</idea-plugin>
@@ -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<Hash> 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);
}
}
}
@@ -47,4 +47,7 @@ public interface VcsLogUi {
boolean isHighlightMyCommits();
void setHighlightMyCommits(boolean state);
void setHighlightCurrentBranch(boolean state);
boolean isHighlightCurrentBranch();
}
@@ -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<CommitId> {
@NotNull
Set<CommitId> getContainingBranches(@NotNull CommitId commit);
@NotNull
Condition<CommitId> 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)"),
@@ -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<Integer> result) {
if (myBranchNodeIndexes.contains(nodeIndex)) result.add(nodeIndex);
}
public void walkBranch(int branchHead, @NotNull final Consumer<Integer> 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;
}
});
}
}
@@ -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<CommitId> implements PermanentGraph<CommitId>, P
return myPermanentCommitsInfo.convertToCommitIdSet(myBranchesGetter.getBranchNodeIndexes(commitIndex));
}
@NotNull
@Override
public Condition<CommitId> getBranchChecker(@NotNull final CommitId head) {
if (head instanceof Integer) {
final TIntHashSet branchNodes = new TIntHashSet();
myBranchesGetter.walkBranch(myPermanentCommitsInfo.getNodeId(head), new Consumer<Integer>() {
@Override
public void consume(Integer node) {
branchNodes.add((Integer)myPermanentCommitsInfo.getCommitId(node));
}
});
return new Condition<CommitId>() {
@Override
public boolean value(CommitId commitId) {
return branchNodes.contains((Integer)commitId);
}
};
}
else {
final TIntHashSet branchNodes = new TIntHashSet();
myBranchesGetter.walkBranch(myPermanentCommitsInfo.getNodeId(head), new Consumer<Integer>() {
@Override
public void consume(Integer node) {
branchNodes.add(node);
}
});
return new Condition<CommitId>() {
@Override
public boolean value(CommitId commitId) {
return branchNodes.contains(myPermanentCommitsInfo.getNodeId(commitId));
}
};
}
}
@NotNull
public PermanentCommitsInfoIml<CommitId> getPermanentCommitsInfo() {
return myPermanentCommitsInfo;
@@ -2,6 +2,7 @@
<extensionPoints>
<extensionPoint name="logProvider" interface="com.intellij.vcs.log.VcsLogProvider" area="IDEA_PROJECT"/>
<extensionPoint name="logHighlighterCreator" interface="com.intellij.vcs.log.ui.VcsLogHighlighterCreator" area="IDEA_PROJECT"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
@@ -14,6 +15,8 @@
<projectService serviceInterface="com.intellij.vcs.log.VcsUserRegistry" serviceImplementation="com.intellij.vcs.log.data.VcsUserRegistryImpl"/>
<statistics.usagesCollector implementation="com.intellij.vcs.log.statistics.VcsLogRepoSizeCollector" />
<logHighlighterCreator implementation="com.intellij.vcs.log.ui.MyCommitsHighlighter$Creator"/>
</extensions>
<actions>
@@ -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<Integer> myGraph;
@NotNull private volatile Map<Pair<VirtualFile, String>, 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<Pair<VirtualFile, String>, 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<String> requestContainingBranches(@NotNull VirtualFile root, @NotNull Hash hash) {
List<String> refs = myCache.get(hash);
List<String> refs = getContainingBranchesIfAvailable(hash);
if (refs == null) {
myTaskExecutor.queue(new Task(root, hash, myCache, myGraph, myRefs));
}
return refs;
}
@Nullable
public List<String> getContainingBranchesIfAvailable(@NotNull Hash hash) {
return myCache.get(hash);
}
@NotNull
public Condition<Hash> 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<VcsRef>() {
@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<Hash, List<String>> createCache() {
return new SLRUMap<Hash, List<String>>(1000, 1000);
@@ -179,4 +211,22 @@ public class ContainingBranchesGetter implements VcsLogListener {
}
}
private class BranchChecker implements Condition<Hash> {
@NotNull private final Condition<Integer> myChecker;
private volatile boolean isDisposed = false;
public BranchChecker(@NotNull Condition<Integer> checker) {
myChecker = checker;
}
@Override
public boolean value(Hash hash) {
if (isDisposed) return false;
return myChecker.value(myDataHolder.getCommitIndex(hash));
}
public void dispose() {
isDisposed = true;
}
}
}
@@ -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<Integer> {
return Collections.emptySet();
}
@NotNull
@Override
public Condition<Integer> getBranchChecker(@NotNull Integer currentBranchHead) {
return Conditions.alwaysFalse();
}
}
@@ -48,6 +48,7 @@ public class VcsLogUiProperties implements PersistentStateComponent<VcsLogUiProp
public int BEK_SORT_TYPE = 0;
public boolean SHOW_ROOT_NAMES = false;
public boolean HIGHLIGHT_MY_COMMITS = true;
public boolean HIGHLIGHT_CURRENT_BRANCH = false;
public Deque<UserGroup> RECENTLY_FILTERED_USER_GROUPS = new ArrayDeque<UserGroup>();
public Deque<UserGroup> RECENTLY_FILTERED_BRANCH_GROUPS = new ArrayDeque<UserGroup>();
}
@@ -147,6 +148,14 @@ public class VcsLogUiProperties implements PersistentStateComponent<VcsLogUiProp
myState.HIGHLIGHT_MY_COMMITS = isHighlightMyCommits;
}
public boolean isHighlightCurrentBranch() {
return myState.HIGHLIGHT_CURRENT_BRANCH;
}
public void setHighlightCurrentBranch(boolean isHighlightCurrentBranch) {
myState.HIGHLIGHT_CURRENT_BRANCH = isHighlightCurrentBranch;
}
public static class UserGroup {
public List<String> users = new ArrayList<String>();
@@ -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);
}
}
}
@@ -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);
}
@@ -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<VcsLogHighlighterCreator> 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();
@@ -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);
}
}
}
}
@@ -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
}
});
}