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/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..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 @@ -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,27 @@ 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(); + + /** + * 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/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..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 @@ -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. @@ -487,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 3489b5e5a2a7..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 @@ -15,17 +15,17 @@ */ 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.*; 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 +84,39 @@ 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(); + } + + @NotNull + @Override + public Collection getLogProviders() { + return myDataHolder.getLogProviders(); + } + } 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/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/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/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; - } - } } - } 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; 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/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) { 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()); + } + } + } 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; 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 @@ - - - +