From c6470837eb3d4e9963808951b11cf0e3f94fb152 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 10 Nov 2013 18:01:59 +0400 Subject: [PATCH 1/6] [log] Extract the popup text field used for user completion Will be used for 'jump to commit' --- .../PopupWithTextFieldWithAutoCompletion.java | 78 +++++++++++++++++++ .../ui/filter/UserFilterPopupComponent.java | 54 +------------ 2 files changed, 81 insertions(+), 51 deletions(-) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java new file mode 100644 index 000000000000..f37b7b173b63 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2013 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.openapi.editor.ex.EditorEx; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; +import com.intellij.ui.TextFieldWithAutoCompletion; +import com.intellij.ui.components.JBTextField; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.KeyEvent; +import java.util.Collection; + +public class PopupWithTextFieldWithAutoCompletion extends TextFieldWithAutoCompletion { + + @Nullable private JBPopup myPopup; + + public PopupWithTextFieldWithAutoCompletion(@NotNull Project project, @NotNull Collection variants) { + super(project, new StringsCompletionProvider(variants, null), false, null); + + // this hack is needed, because the preferred size is requested before createEditor(), + // and EditorTextField#getPreferredSize() hardcodes unacceptable (100, 20) + setPreferredSize(new JBTextField(20).getPreferredSize()); + } + + public JBPopup createPopup() { + myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(this, this) + .setCancelOnClickOutside(true) + .setCancelOnWindowDeactivation(true) + .setCancelKeyEnabled(true) + .setRequestFocus(true) + .createPopup(); + return myPopup; + } + + @Override + protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + if (myPopup != null) { + myPopup.closeOk(e); + } + return true; + } + else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + if (myPopup != null) { + myPopup.cancel(e); + } + return true; + } + return false; + } + + @Override + protected EditorEx createEditor() { + // spell check is not needed + EditorEx editor = super.createEditor(); + SpellCheckingEditorCustomization.getInstance(false).customize(editor); + return editor; + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java index fbc42de6692f..6a5ee0a1da34 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java @@ -18,16 +18,10 @@ package com.intellij.vcs.log.ui.filter; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupAdapter; -import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.LightweightWindowEvent; -import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; -import com.intellij.ui.TextFieldWithAutoCompletion; -import com.intellij.ui.components.JBTextField; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsLogFilter; @@ -35,10 +29,9 @@ import com.intellij.vcs.log.VcsUser; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VcsLogUiProperties; import com.intellij.vcs.log.data.VcsLogUserFilter; +import com.intellij.vcs.log.ui.PopupWithTextFieldWithAutoCompletion; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import java.awt.event.KeyEvent; import java.util.Collection; import java.util.List; @@ -104,15 +97,8 @@ class UserFilterPopupComponent extends FilterPopupComponent { } }); - final UserTextField textField = new UserTextField(e.getProject(), users); - - JBPopup popup = JBPopupFactory.getInstance().createComponentPopupBuilder(textField, textField) - .setCancelOnClickOutside(true) - .setCancelOnWindowDeactivation(true) - .setCancelKeyEnabled(true) - .setRequestFocus(true) - .createPopup(); - textField.myPopup = popup; + final PopupWithTextFieldWithAutoCompletion textField = new PopupWithTextFieldWithAutoCompletion(e.getProject(), users); + JBPopup popup = textField.createPopup(); popup.addListener(new JBPopupAdapter() { @Override @@ -127,39 +113,5 @@ class UserFilterPopupComponent extends FilterPopupComponent { popup.showUnderneathOf(UserFilterPopupComponent.this); } - private class UserTextField extends TextFieldWithAutoCompletion { - - private JBPopup myPopup; - - public UserTextField(Project project, Collection users) { - super(project, new StringsCompletionProvider(users, null), false, null); - - // this hack is needed, because the preferred size is requested before createEditor(), - // and EditorTextField#getPreferredSize() hardcodes unacceptable (100, 20) - setPreferredSize(new JBTextField(20).getPreferredSize()); - } - - @Override - protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - myPopup.closeOk(e); - return true; - } - else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - myPopup.cancel(e); - return true; - } - return false; - } - - @Override - protected EditorEx createEditor() { - // spell check is not needed - EditorEx editor = super.createEditor(); - SpellCheckingEditorCustomization.getInstance(false).customize(editor); - return editor; - } - } } - } From 796438517887860785f471faf7d83282200156f7 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 10 Nov 2013 18:55:57 +0400 Subject: [PATCH 2/6] [log] "Go To Commit" action * Provide all references to the text field completion. * Delegate all work to the VcsLog interface. * If reference was specified, jump to its hash. * Otherwise try to jump to the hash: delegate to VcsLogUi and further to DataPack to get the Node of Hash starting from the given string. * If full log is not loaded and we haven't found anything, request to load full log and try again. As opposed to jumpToHash with the given Hash, we can't assume that the given String represents a valid Hash which exists in this repository => we must check if full log is already showing to prevent infinite request. --- .../api/src/com/intellij/vcs/log/VcsLog.java | 18 +++++ .../vcs-log/impl/src/META-INF/vcs-log.xml | 3 + .../com/intellij/vcs/log/data/DataPack.java | 12 ++++ .../com/intellij/vcs/log/data/RefsModel.java | 6 ++ .../vcs/log/data/VcsLogDataHolder.java | 4 ++ .../com/intellij/vcs/log/impl/VcsLogImpl.java | 31 +++++++++ .../intellij/vcs/log/impl/VcsLogManager.java | 13 +++- .../intellij/vcs/log/ui/GoToRefAction.java | 67 +++++++++++++++++++ .../src/com/intellij/vcs/log/ui/VcsLogUI.java | 19 ++++++ .../intellij/vcs/log/ui/frame/MainFrame.java | 9 ++- 10 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java index a90fad17baae..31e116c9dfff 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java @@ -18,6 +18,7 @@ package com.intellij.vcs.log; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.awt.*; import java.util.Collection; import java.util.List; @@ -54,4 +55,21 @@ public interface VcsLog { @Nullable Collection getContainingBranches(@NotNull Hash commitHash); + /** + * Returns all {@link VcsRef commit references} available in the log. + */ + @NotNull + Collection getAllReferences(); + + /** + * Selects the commit node defined by the given reference (commit hash, branch or tag). + */ + void jumpToReference(String reference); + + /** + * Returns the VCS log toolbar component. + */ + @NotNull + Component getToolbar(); + } 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 6f85187e620c..3c0749b0b2c6 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -18,11 +18,14 @@ + + diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java index c40e62ff2b0a..78da9967064d 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java @@ -135,6 +135,18 @@ public class DataPack { return null; } + @Nullable + public Node getNodeByPartOfHash(@NotNull String hash) { + Graph graph = getGraphModel().getGraph(); + for (int i = 0; i < graph.getNodeRows().size(); i++) { + Node node = graph.getCommitNodeInRow(i); + if (node != null && node.getCommitHash().asString().startsWith(hash.toLowerCase())) { + return node; + } + } + return null; + } + @Nullable public Node getCommonParent(Node a, Node b) { List commitDiff = getCommitsDownToCommon(a, b); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java index cb82767f747c..c1b2dc745e51 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java @@ -58,4 +58,10 @@ public class RefsModel { public Collection getBranches() { return myBranches; } + + @NotNull + public Collection getAllRefs() { + return new ArrayList(myRefsToHashes.values()); + } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java index 52debbe9a0ef..939121dc4313 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java @@ -305,6 +305,10 @@ public class VcsLogDataHolder implements Disposable { }, "Building full log..."); } + public boolean isFullLogShowing() { + return myFullLogShowing; + } + /** * Queries the VCS for the number of recent unordered commits, orders them and connects to the existing log structure. * This is done after refresh, when the whole log skeleton has been loaded. diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java index 3489b5e5a2a7..50da09cce0c1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java @@ -15,17 +15,20 @@ */ package com.intellij.vcs.log.impl; +import com.intellij.openapi.util.Condition; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsLog; +import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.ui.VcsLogUI; import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.awt.*; import java.util.Collection; import java.util.List; @@ -84,4 +87,32 @@ public class VcsLogImpl implements VcsLog { return null; } + @NotNull + @Override + public Collection getAllReferences() { + return myDataHolder.getDataPack().getRefsModel().getAllRefs(); + } + + @Override + public void jumpToReference(final String reference) { + Collection references = getAllReferences(); + VcsRef ref = ContainerUtil.find(references, new Condition() { + @Override + public boolean value(VcsRef ref) { + return ref.getName().startsWith(reference); + } + }); + if (ref != null) { + myUi.jumpToCommit(ref.getCommitHash()); + } + else { + myUi.jumpToCommitByPartOfHash(reference); + } + } + + @NotNull + @Override + public Component getToolbar() { + return myUi.getToolbar(); + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java index 983425b873a6..525a7748c4ac 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java @@ -1,6 +1,9 @@ package com.intellij.vcs.log.impl; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.actionSystem.DataSink; +import com.intellij.openapi.actionSystem.TypeSafeDataProvider; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; @@ -124,7 +127,7 @@ public class VcsLogManager implements Disposable { public void dispose() { } - private static class VcsLogContainer extends JPanel { + private class VcsLogContainer extends JPanel implements TypeSafeDataProvider { private final JBLoadingPanel myLoadingPanel; @@ -139,6 +142,14 @@ public class VcsLogManager implements Disposable { myLoadingPanel.add(mainComponent); myLoadingPanel.stopLoading(); } + + @Override + public void calcData(DataKey key, DataSink sink) { + if (myUi != null) { + myUi.getMainFrame().calcData(key, sink); + } + } + } private static class PostponeableLogRefresher implements VcsLogRefresher, Disposable { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java new file mode 100644 index 000000000000..4514217a25d9 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2013 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.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupListener; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.VcsLog; +import com.intellij.vcs.log.VcsLogDataKeys; +import com.intellij.vcs.log.VcsRef; + +import java.util.Collection; + +public class GoToRefAction extends DumbAwareAction { + + @Override + public void actionPerformed(AnActionEvent e) { + Project project = e.getProject(); + final VcsLog log = e.getData(VcsLogDataKeys.VSC_LOG); + if (project == null || log == null) { + return; + } + + Collection refs = ContainerUtil.map(log.getAllReferences(), new Function() { + @Override + public String fun(VcsRef ref) { + return ref.getName(); + } + }); + final PopupWithTextFieldWithAutoCompletion textField = new PopupWithTextFieldWithAutoCompletion(project, refs); + JBPopup popup = textField.createPopup(); + popup.addListener(new JBPopupListener.Adapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + if (event.isOk()) { + log.jumpToReference(textField.getText()); + } + } + }); + popup.showUnderneathOf(log.getToolbar()); + } + + @Override + public void update(AnActionEvent e) { + VcsLog log = e.getData(VcsLogDataKeys.VSC_LOG); + getTemplatePresentation().setEnabledAndVisible(e.getProject() != null && log != null); + } + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java index 81ddba796957..7706c2b5d630 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.table.TableModel; +import java.awt.*; import java.util.Collection; /** @@ -205,6 +206,21 @@ public class VcsLogUI { } } + public void jumpToCommitByPartOfHash(final String hash) { + Node node = myLogDataHolder.getDataPack().getNodeByPartOfHash(hash); + if (node != null) { + jumpToRow(node.getRowIndex()); + } + else if (!myLogDataHolder.isFullLogShowing()) { + myLogDataHolder.showFullLog(new Runnable() { + @Override + public void run() { + jumpToCommitByPartOfHash(hash); + } + }); + } + } + @NotNull public VcsLogColorManager getColorManager() { return myColorManager; @@ -255,4 +271,7 @@ public class VcsLogUI { myMainFrame.setBranchesPanelVisible(visible); } + public Component getToolbar() { + return myMainFrame.getToolbar(); + } } 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 e63a1dab3766..14ad6b3a5fa0 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 @@ -49,6 +49,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { @NotNull private final BranchesPanel myBranchesPanel; @NotNull private final DetailsPanel myDetailsPanel; @NotNull private final Splitter myDetailsSplitter; + private final JComponent myToolbar; public MainFrame(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUI vcsLogUI, @NotNull Project project, @NotNull VcsLogSettings settings, @NotNull VcsLogUiProperties uiProperties, @NotNull VcsLog log) { @@ -78,14 +79,14 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { updateWhenDetailsAreLoaded(selectionChangeListener); // layout - JComponent toolbar = createActionsToolbar(); + myToolbar = createActionsToolbar(); myDetailsSplitter = new Splitter(true, 0.7f); myDetailsSplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myGraphTable)); setupDetailsSplitter(myUiProperties.isShowDetails()); JComponent toolbars = new JPanel(new BorderLayout()); - toolbars.add(toolbar, BorderLayout.NORTH); + toolbars.add(myToolbar, BorderLayout.NORTH); toolbars.add(myBranchesPanel, BorderLayout.CENTER); JComponent toolbarsAndTable = new JPanel(new BorderLayout()); toolbarsAndTable.add(toolbars, BorderLayout.NORTH); @@ -234,6 +235,10 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { } } + public Component getToolbar() { + return myToolbar; + } + private class CommitSelectionListener implements ListSelectionListener { private final ChangesBrowser myChangesBrowser; From 73f7ab54bca4c2e048ab155af0d8e58fb7ce49f0 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 10 Nov 2013 19:37:32 +0400 Subject: [PATCH 3/6] [log] IDEA-115980 Don't show Git actions if there are no Git roots --- .../api/src/com/intellij/vcs/log/VcsLog.java | 6 ++++++ .../intellij/vcs/log/data/VcsLogDataHolder.java | 5 +++++ .../com/intellij/vcs/log/impl/VcsLogImpl.java | 12 ++++++++---- plugins/git4idea/src/git4idea/GitUtil.java | 16 ++++++++++++++++ .../actions/GitLogSingleCommitAction.java | 2 +- .../git4idea/cherrypick/GitCherryPickAction.java | 9 ++++++++- 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java index 31e116c9dfff..511854deaddc 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java @@ -72,4 +72,10 @@ public interface VcsLog { @NotNull Component getToolbar(); + /** + * Returns {@link VcsLogProvider VcsLogProviders} which are active in this log, i.e. which VCS roots are shown in the log. + */ + @NotNull + Collection getLogProviders(); + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java index 939121dc4313..685490bfde19 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java @@ -491,6 +491,11 @@ public class VcsLogDataHolder implements Disposable { return myLogProviders.keySet(); } + @NotNull + public Collection getLogProviders() { + return myLogProviders.values(); + } + private static class RecentCommitsInfo { List firstBlockCommits; Collection newRefs; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java index 50da09cce0c1..96fffeb5c212 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java @@ -18,10 +18,7 @@ package com.intellij.vcs.log.impl; import com.intellij.openapi.util.Condition; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.ContainerUtil; -import com.intellij.vcs.log.Hash; -import com.intellij.vcs.log.VcsFullCommitDetails; -import com.intellij.vcs.log.VcsLog; -import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.*; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.ui.VcsLogUI; import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel; @@ -115,4 +112,11 @@ public class VcsLogImpl implements VcsLog { public Component getToolbar() { return myUi.getToolbar(); } + + @NotNull + @Override + public Collection getLogProviders() { + return myDataHolder.getLogProviders(); + } + } diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index 42c8f1030d10..b507e4a6a161 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -23,6 +23,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -37,7 +38,10 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; +import com.intellij.vcs.log.VcsLog; +import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcsUtil.VcsFileUtil; import com.intellij.vcsUtil.VcsUtil; import git4idea.branch.GitBranchUtil; @@ -951,4 +955,16 @@ public class GitUtil { repositoryManager.getRepositories())); } + /** + * Checks if there are Git roots in the VCS log. + */ + public static boolean logHasGitRoot(@NotNull VcsLog log) { + return ContainerUtil.find(log.getLogProviders(), new Condition() { + @Override + public boolean value(VcsLogProvider logProvider) { + return logProvider.getSupportedVcs().equals(GitVcs.getKey()); + } + }) != null; + } + } diff --git a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java index 364b1f58b15e..0ec2ab63e877 100644 --- a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java @@ -83,7 +83,7 @@ public abstract class GitLogSingleCommitAction extends DumbAwareAction { } boolean isValid() { - return project != null && log != null; + return project != null && log != null && GitUtil.logHasGitRoot(log); } } diff --git a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java index 41bb56105703..7d24caf30152 100644 --- a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java +++ b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java @@ -29,6 +29,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; import git4idea.GitLocalBranch; import git4idea.GitPlatformFacade; +import git4idea.GitUtil; import git4idea.GitVcs; import git4idea.commands.Git; import git4idea.config.GitVcsSettings; @@ -131,7 +132,13 @@ public class GitCherryPickAction extends DumbAwareAction { @Override public void update(AnActionEvent e) { super.update(e); - e.getPresentation().setEnabled(enabled(e)); + final VcsLog log = getVcsLog(e); + if (log != null && !GitUtil.logHasGitRoot(log)) { + e.getPresentation().setEnabledAndVisible(false); + } + else { + e.getPresentation().setEnabled(enabled(e)); + } } private boolean enabled(AnActionEvent e) { From 29a4f604ff41ebabc6b34cd079db6533c750d8dc Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 10 Nov 2013 21:21:59 +0100 Subject: [PATCH 4/6] add accessOrder parameter to our LinkedHashMap --- .../com/intellij/util/containers/SLRUMap.java | 16 ++-------------- .../util/containers/hash/LinkedHashMap.java | 19 +++++++++++++------ .../src/com/intellij/util/io/PagePool.java | 13 ++----------- .../intellij/util/io/PagedFileStorage.java | 7 +------ .../containers/hash/LinkedHashMapTest.java | 16 ++-------------- .../tasks/youtrack/YouTrackIntellisense.java | 7 +------ 6 files changed, 21 insertions(+), 57 deletions(-) diff --git a/platform/util/src/com/intellij/util/containers/SLRUMap.java b/platform/util/src/com/intellij/util/containers/SLRUMap.java index e4df6bca95f2..3d66f95c5df5 100644 --- a/platform/util/src/com/intellij/util/containers/SLRUMap.java +++ b/platform/util/src/com/intellij/util/containers/SLRUMap.java @@ -48,7 +48,7 @@ public class SLRUMap { myProtectedQueueSize = protectedQueueSize * FACTOR; myProbationalQueueSize = probationalQueueSize * FACTOR; - myProtectedQueue = new LinkedHashMap(10, 0.6f, hashingStrategy) { + myProtectedQueue = new LinkedHashMap(10, 0.6f, hashingStrategy, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest, K key, V value) { if (size() > myProtectedQueueSize) { @@ -58,15 +58,9 @@ public class SLRUMap { return false; } - - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } - }; - myProbationalQueue = new LinkedHashMap(10, 0.6f, hashingStrategy) { + myProbationalQueue = new LinkedHashMap(10, 0.6f, hashingStrategy, true) { @Override protected boolean removeEldestEntry(final Map.Entry eldest, K key, V value) { if (size() > myProbationalQueueSize) { @@ -75,12 +69,6 @@ public class SLRUMap { } return false; } - - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } - }; } diff --git a/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java b/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java index e61076a67d36..9cc29915ba28 100644 --- a/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java +++ b/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java @@ -29,7 +29,7 @@ public class LinkedHashMap extends AbstractMap implements Map private int size; private final float loadFactor; private final EqualityPolicy hashingStrategy; - + private final boolean accessOrder; public LinkedHashMap() { this(0); @@ -38,19 +38,30 @@ public class LinkedHashMap extends AbstractMap implements Map public LinkedHashMap(int capacity) { this(capacity, HashUtil.DEFAULT_LOAD_FACTOR); } + public LinkedHashMap(int capacity, boolean accessOrder) { + this(capacity, HashUtil.DEFAULT_LOAD_FACTOR, accessOrder); + } public LinkedHashMap(int capacity, float loadFactor) { this(capacity, loadFactor, (EqualityPolicy)EqualityPolicy.CANONICAL); } + public LinkedHashMap(int capacity, float loadFactor, boolean accessOrder) { + this(capacity, loadFactor, (EqualityPolicy)EqualityPolicy.CANONICAL, accessOrder); + } + public LinkedHashMap(EqualityPolicy hashingStrategy) { this(0, HashUtil.DEFAULT_LOAD_FACTOR, hashingStrategy); } public LinkedHashMap(int capacity, float loadFactor, EqualityPolicy hashingStrategy) { + this(capacity, loadFactor, hashingStrategy, false); + } + public LinkedHashMap(int capacity, float loadFactor, EqualityPolicy hashingStrategy, boolean accessOrder) { this.loadFactor = loadFactor; this.hashingStrategy = hashingStrategy; clear(capacity); + this.accessOrder = accessOrder; } @Override @@ -200,12 +211,8 @@ public class LinkedHashMap extends AbstractMap implements Map size = 0; } - protected boolean shouldMoveEntryToTopWhenReading() { - return false; - } - private void moveToTop(final Entry e) { - if (!shouldMoveEntryToTopWhenReading()) { + if (!accessOrder) { return; } diff --git a/platform/util/src/com/intellij/util/io/PagePool.java b/platform/util/src/com/intellij/util/io/PagePool.java index bfc825274e0f..acb3a088b52f 100644 --- a/platform/util/src/com/intellij/util/io/PagePool.java +++ b/platform/util/src/com/intellij/util/io/PagePool.java @@ -45,7 +45,7 @@ public class PagePool { private PoolPageKey lastFinalizedKey = null; public PagePool(final int protectedPagesLimit, final int probationalPagesLimit) { - myProbationalQueue = new LinkedHashMap(probationalPagesLimit * 2, 0.6f) { + myProbationalQueue = new LinkedHashMap(probationalPagesLimit * 2, 0.6f, true) { @Override protected boolean removeEldestEntry(final Map.Entry eldest) { if (size() > probationalPagesLimit) { @@ -54,14 +54,9 @@ public class PagePool { } return false; } - - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } }; - myProtectedQueue = new LinkedHashMap(protectedPagesLimit, 0.6f) { + myProtectedQueue = new LinkedHashMap(protectedPagesLimit, 0.6f, true) { @Override protected boolean removeEldestEntry(final Map.Entry eldest) { if (size() > protectedPagesLimit) { @@ -70,10 +65,6 @@ public class PagePool { } return false; } - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } }; } diff --git a/platform/util/src/com/intellij/util/io/PagedFileStorage.java b/platform/util/src/com/intellij/util/io/PagedFileStorage.java index 845cc31d054f..6c4c9a3ed2bf 100644 --- a/platform/util/src/com/intellij/util/io/PagedFileStorage.java +++ b/platform/util/src/com/intellij/util/io/PagedFileStorage.java @@ -484,17 +484,12 @@ public class PagedFileStorage implements Forceable { myDefaultStorageLockContext = new StorageLockContext(this, checkThreadAccess); mySizeLimit = UPPER_LIMIT; - mySegments = new LinkedHashMap(10, 0.75f) { + mySegments = new LinkedHashMap(10, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return mySize > mySizeLimit; } - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } - @Nullable @Override public ByteBufferWrapper remove(Object key) { diff --git a/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java b/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java index 5e109c9df0cd..6d5db54769d0 100644 --- a/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java +++ b/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java @@ -135,17 +135,11 @@ public class LinkedHashMapTest { @Test public void lru2() { - final LinkedHashMap tested = new LinkedHashMap() { + final LinkedHashMap tested = new LinkedHashMap(0, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 1000; } - - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } - }; for (int i = 0; i < 1000; ++i) { tested.put(i, Integer.toString(i)); @@ -161,17 +155,11 @@ public class LinkedHashMapTest { @Test public void lru3() { - final LinkedHashMap tested = new LinkedHashMap() { + final LinkedHashMap tested = new LinkedHashMap(0, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 1000; } - - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } - }; for (int i = 0; i < 1000; ++i) { tested.put(i, Integer.toString(i)); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackIntellisense.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackIntellisense.java index 27b9cf8231ad..609a8d787658 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackIntellisense.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackIntellisense.java @@ -56,15 +56,10 @@ public class YouTrackIntellisense { private final int myMaxSize; private SizeLimitedCache(int max) { - super((int)(max / 0.75) + 1); + super((int)(max / 0.75) + 1, true); myMaxSize = max; } - @Override - protected boolean shouldMoveEntryToTopWhenReading() { - return true; - } - @Override protected boolean removeEldestEntry(Map.Entry eldest, K key, V value) { return size() > myMaxSize; From 34b357caf08d2aeeec433bffd69267e74243c858 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 10 Nov 2013 21:56:40 +0100 Subject: [PATCH 5/6] more console folding by default --- plugins/devkit/resources/META-INF/plugin.xml | 3 +++ resources/src/META-INF/IdeaPlugin.xml | 12 ++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index e0a0ae70f4de..5a2b0f0ec244 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -96,6 +96,9 @@ + + + diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 57ae4c1beca4..ca66e772922e 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -983,16 +983,14 @@ - - - + - + + - @@ -1004,12 +1002,10 @@ - - - + From 9ea940ab07cbeb75157e824d397cb2a238799e81 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 11 Nov 2013 00:56:53 +0400 Subject: [PATCH 6/6] [git] Fix refresh notification after changing non-active branch hashes We send notification after Git repository update only if there were some changes. However, because of perverted GitBranch#equals() contract we didn't capture the change if some non-active branch was moved (its Hash changes), for example, in IDEA-116111 (after push). Until all deprecated GitBranch object creations are eliminated from the code, we can't change its equals => use custom hashing strategy for correct comparison. --- .../src/git4idea/repo/GitRepoInfo.java | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/repo/GitRepoInfo.java b/plugins/git4idea/src/git4idea/repo/GitRepoInfo.java index cbeb0c4268b1..33dc6a771f21 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepoInfo.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepoInfo.java @@ -16,8 +16,11 @@ package git4idea.repo; import com.intellij.dvcs.repo.Repository; +import git4idea.GitBranch; import git4idea.GitLocalBranch; import git4idea.GitRemoteBranch; +import gnu.trove.THashSet; +import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -97,8 +100,8 @@ public class GitRepoInfo { if (myCurrentBranch != null ? !myCurrentBranch.equals(info.myCurrentBranch) : info.myCurrentBranch != null) return false; if (!myRemotes.equals(info.myRemotes)) return false; if (!myBranchTrackInfos.equals(info.myBranchTrackInfos)) return false; - if (!myLocalBranches.equals(info.myLocalBranches)) return false; - if (!myRemoteBranches.equals(info.myRemoteBranches)) return false; + if (!areEqual(myLocalBranches, info.myLocalBranches)) return false; + if (!areEqual(myRemoteBranches, info.myRemoteBranches)) return false; return true; } @@ -120,4 +123,32 @@ public class GitRepoInfo { return String.format("GitRepoInfo{current=%s, remotes=%s, localBranches=%s, remoteBranches=%s, trackInfos=%s}", myCurrentBranch, myRemotes, myLocalBranches, myRemoteBranches, myBranchTrackInfos); } + + private static boolean areEqual(Collection c1, Collection c2) { + // GitBranch has perverted equals contract (see the comment there) + // until GitBranch is created only from a single place with correctly defined Hash, we can't change its equals + THashSet set1 = new THashSet(c1, new BranchesComparingStrategy()); + THashSet set2 = new THashSet(c2, new BranchesComparingStrategy()); + return set1.equals(set2); + } + + private static class BranchesComparingStrategy implements TObjectHashingStrategy { + + @Override + public int computeHashCode(@NotNull GitBranch branch) { + return 31 * branch.getName().hashCode() + branch.getHash().hashCode(); + } + + @Override + public boolean equals(@NotNull GitBranch b1, @NotNull GitBranch b2) { + if (b1 == b2) { + return true; + } + if (b1.getClass() != b2.getClass()) { + return false; + } + return b1.getName().equals(b2.getName()) && b1.getHash().equals(b2.getHash()); + } + } + }