From 77919bbb497b2c5964e64818805642c32de7fcc4 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 9 Feb 2012 15:32:19 +0100 Subject: [PATCH 01/18] SVN refresh test can now run with lesser delay --- .../idea/svn/SvnChangesCorrectlyRefreshedTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java b/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java index d49a3e711301..fbed7a8a9a7a 100644 --- a/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java +++ b/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java @@ -89,14 +89,14 @@ public class SvnChangesCorrectlyRefreshedTest extends SvnTestCase { //ChangeListManagerImpl.DEBUG = true; final SubTree subTree = new SubTree(myWorkingCopyDir); checkin(); - sleep(1000); + sleep(100); - editFileInCommand(myProject, subTree.myS1File, "new"); + editFileInCommand(myProject, subTree.myS1File, "new content"); final CharSequence text1 = LoadTextUtil.loadText(subTree.myS1File); - Assert.assertEquals("new", text1.toString()); + Assert.assertEquals("new content", text1.toString()); - sleep(1000); + sleep(100); LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(subTree.myS1File.getPath())); VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); clManager.ensureUpToDate(false); From 02b57f0ec2c3adf6864278f2a2fc279219122579 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 9 Feb 2012 22:30:08 +0100 Subject: [PATCH 02/18] .ico support --- .../impl/ImageFileTypeManagerImpl.java | 10 ++++++---- .../src/org/intellij/images/vfs/IfsUtil.java | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/images/src/org/intellij/images/fileTypes/impl/ImageFileTypeManagerImpl.java b/images/src/org/intellij/images/fileTypes/impl/ImageFileTypeManagerImpl.java index 761f578bb50f..d1af0a440dd6 100644 --- a/images/src/org/intellij/images/fileTypes/impl/ImageFileTypeManagerImpl.java +++ b/images/src/org/intellij/images/fileTypes/impl/ImageFileTypeManagerImpl.java @@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile; import gnu.trove.THashSet; import org.intellij.images.ImagesBundle; import org.intellij.images.fileTypes.ImageFileTypeManager; +import org.intellij.images.vfs.IfsUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -76,15 +77,16 @@ final class ImageFileTypeManagerImpl extends ImageFileTypeManager implements App } public void createFileTypes(final @NotNull FileTypeConsumer consumer) { - final String[] readerFormatNames = ImageIO.getReaderFormatNames(); final Set processed = new THashSet(); + final String[] readerFormatNames = ImageIO.getReaderFormatNames(); for (String format : readerFormatNames) { - final String s = format.toLowerCase(); - if (processed.contains(s)) continue; - processed.add(s); + final String ext = format.toLowerCase(); + processed.add(ext); } + processed.add(IfsUtil.ICO_FORMAT.toLowerCase()); + consumer.consume(imageFileType, StringUtil.join(processed, FileTypeConsumer.EXTENSION_DELIMITER)); } } diff --git a/images/src/org/intellij/images/vfs/IfsUtil.java b/images/src/org/intellij/images/vfs/IfsUtil.java index 8e67e3f6de9d..bb2f1541f299 100644 --- a/images/src/org/intellij/images/vfs/IfsUtil.java +++ b/images/src/org/intellij/images/vfs/IfsUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -27,6 +27,9 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.reference.SoftReference; import com.intellij.util.LogicalRoot; import com.intellij.util.LogicalRootsManager; +import org.apache.sanselan.ImageReadException; +import org.apache.sanselan.common.byteSources.ByteSourceArray; +import org.apache.sanselan.formats.ico.IcoImageParser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,9 +49,12 @@ import java.util.Iterator; * @author Alexey Efimov */ public final class IfsUtil { + public static final String ICO_FORMAT = "ico"; + private static final Key TIMESTAMP_KEY = Key.create("Image.timeStamp"); private static final Key FORMAT_KEY = Key.create("Image.format"); private static final Key> BUFFERED_IMAGE_REF_KEY = Key.create("Image.bufferedImage"); + private static final IcoImageParser ICO_IMAGE_PARSER = new IcoImageParser(); /** * Load image data for file and put user data attributes into file. @@ -63,6 +69,17 @@ public final class IfsUtil { if (loadedTimeStamp == null || loadedTimeStamp.longValue() != file.getTimeStamp() || imageRef == null || imageRef.get() == null) { try { final byte[] content = file.contentsToByteArray(); + + if (ICO_FORMAT.equalsIgnoreCase(file.getExtension())) { + try { + final BufferedImage image = ICO_IMAGE_PARSER.getBufferedImage(new ByteSourceArray(content), null); + file.putUserData(FORMAT_KEY, ICO_FORMAT); + file.putUserData(BUFFERED_IMAGE_REF_KEY, new SoftReference(image)); + return true; + } + catch (ImageReadException ignore) { } + } + InputStream inputStream = new ByteArrayInputStream(content, 0, content.length); ImageInputStream imageInputStream = ImageIO.createImageInputStream(inputStream); try { From 35b4fdc301afb1076ba6a85fc34d27d8c7ff155b Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Thu, 9 Feb 2012 23:28:50 +0400 Subject: [PATCH 03/18] IDEA-24310 easy-to-use 'find usages' and 'rename' for value android resources. --- plugins/android/src/META-INF/plugin.xml | 3 + .../android/AndroidRenameHandler.java | 98 ++++++++++++++ .../android/AndroidUsagesTargetProvider.java | 72 ++++++++++ .../testData/findUsages/fu1_values.xml | 5 + .../testData/findUsages/fu2_values.xml | 5 + .../testData/findUsages/fu3_values.xml | 5 + .../testData/findUsages/fu4_values.xml | 5 + .../testData/findUsages/fu5_values.xml | 5 + .../testData/findUsages/fu6_values.xml | 5 + .../testData/findUsages/fu7_values.xml | 5 + .../testData/findUsages/fu8_values.xml | 5 + .../testData/findUsages/fu9_values.xml | 8 ++ .../testData/rename/layoutStrUsage.xml | 4 + .../testData/rename/layoutStrUsage_after.xml | 4 + plugins/android/testData/rename/strings1.xml | 3 + plugins/android/testData/rename/strings2.xml | 3 + plugins/android/testData/rename/strings3.xml | 3 + plugins/android/testData/rename/strings4.xml | 3 + .../android/AndroidFindUsagesTest.java | 123 ++++++++++++++---- .../jetbrains/android/AndroidRenameTest.java | 55 +++++++- 20 files changed, 394 insertions(+), 25 deletions(-) create mode 100644 plugins/android/src/org/jetbrains/android/AndroidRenameHandler.java create mode 100644 plugins/android/src/org/jetbrains/android/AndroidUsagesTargetProvider.java create mode 100644 plugins/android/testData/findUsages/fu1_values.xml create mode 100644 plugins/android/testData/findUsages/fu2_values.xml create mode 100644 plugins/android/testData/findUsages/fu3_values.xml create mode 100644 plugins/android/testData/findUsages/fu4_values.xml create mode 100644 plugins/android/testData/findUsages/fu5_values.xml create mode 100644 plugins/android/testData/findUsages/fu6_values.xml create mode 100644 plugins/android/testData/findUsages/fu7_values.xml create mode 100644 plugins/android/testData/findUsages/fu8_values.xml create mode 100644 plugins/android/testData/findUsages/fu9_values.xml create mode 100644 plugins/android/testData/rename/layoutStrUsage.xml create mode 100644 plugins/android/testData/rename/layoutStrUsage_after.xml create mode 100644 plugins/android/testData/rename/strings1.xml create mode 100644 plugins/android/testData/rename/strings2.xml create mode 100644 plugins/android/testData/rename/strings3.xml create mode 100644 plugins/android/testData/rename/strings4.xml diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 67a616094d46..245e7a2f1ad4 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -190,6 +190,9 @@ + + + diff --git a/plugins/android/src/org/jetbrains/android/AndroidRenameHandler.java b/plugins/android/src/org/jetbrains/android/AndroidRenameHandler.java new file mode 100644 index 000000000000..2924d9cd3a3b --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/AndroidRenameHandler.java @@ -0,0 +1,98 @@ +package org.jetbrains.android; + +import com.intellij.ide.TitledHandler; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlTag; +import com.intellij.refactoring.rename.PsiElementRenameHandler; +import com.intellij.refactoring.rename.RenameDialog; +import com.intellij.refactoring.rename.RenameHandler; +import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper; +import org.jetbrains.annotations.NotNull; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidRenameHandler implements RenameHandler, TitledHandler { + @Override + public boolean isAvailableOnDataContext(DataContext dataContext) { + final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext); + if (editor == null) { + return false; + } + + final PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext); + if (file == null) { + return false; + } + + return AndroidUsagesTargetProvider.findValueResourceTagInContext(editor, file) != null; + } + + @Override + public boolean isRenaming(DataContext dataContext) { + return isAvailableOnDataContext(dataContext); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { + if (file == null || editor == null) { + return; + } + + final XmlTag tag = AndroidUsagesTargetProvider.findValueResourceTagInContext(editor, file); + if (tag == null) { + return; + } + + final XmlAttribute nameAttribute = tag.getAttribute("name"); + if (nameAttribute == null) { + return; + } + + final XmlAttributeValue attributeValue = nameAttribute.getValueElement(); + if (attributeValue == null) { + return; + } + final RenameDialog dialog = new RenameDialog(project, new ValueResourceElementWrapper(attributeValue), null, editor); + + if (ApplicationManager.getApplication().isUnitTestMode()) { + final String name = PsiElementRenameHandler.DEFAULT_NAME.getData(dataContext); + //noinspection TestOnlyProblems + dialog.performRename(name); + dialog.close(DialogWrapper.OK_EXIT_CODE); + } + else { + dialog.show(); + } + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext); + if (editor == null) { + return; + } + + final PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext); + if (file == null) { + return; + } + + invoke(project, editor, file, dataContext); + } + + @Override + public String getActionTitle() { + return "Rename Android value resource"; + } +} diff --git a/plugins/android/src/org/jetbrains/android/AndroidUsagesTargetProvider.java b/plugins/android/src/org/jetbrains/android/AndroidUsagesTargetProvider.java new file mode 100644 index 000000000000..da7bad1164e4 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/AndroidUsagesTargetProvider.java @@ -0,0 +1,72 @@ +package org.jetbrains.android; + +import com.android.resources.ResourceFolderType; +import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.usages.UsageTarget; +import com.intellij.usages.UsageTargetProvider; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.resourceManagers.ResourceManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidUsagesTargetProvider implements UsageTargetProvider { + @Override + public UsageTarget[] getTargets(Editor editor, PsiFile file) { + if (editor == null || file == null) { + return UsageTarget.EMPTY_ARRAY; + } + + final XmlTag tag = findValueResourceTagInContext(editor, file); + return tag != null + ? new UsageTarget[]{new PsiElement2UsageTargetAdapter(tag)} + : UsageTarget.EMPTY_ARRAY; + } + + @Override + public UsageTarget[] getTargets(PsiElement psiElement) { + return UsageTarget.EMPTY_ARRAY; + } + + @Nullable + static XmlTag findValueResourceTagInContext(@NotNull Editor editor, @NotNull PsiFile file) { + if (!(file instanceof XmlFile)) { + return null; + } + + final AndroidFacet facet = AndroidFacet.getInstance(file); + if (facet == null) { + return null; + } + + if (!ResourceManager.isInResourceSubdirectory(file, ResourceFolderType.VALUES.getName())) { + return null; + } + + final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + if (element == null) { + return null; + } + + final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); + if (tag == null) { + return null; + } + + final String name = tag.getAttributeValue("name"); + if (name == null || name.length() == 0) { + return null; + } + + final XmlTag parentTag = tag.getParentTag(); + return parentTag != null && parentTag.getParentTag() == null ? tag : null; + } +} diff --git a/plugins/android/testData/findUsages/fu1_values.xml b/plugins/android/testData/findUsages/fu1_values.xml new file mode 100644 index 000000000000..7030fd307cf3 --- /dev/null +++ b/plugins/android/testData/findUsages/fu1_values.xml @@ -0,0 +1,5 @@ + + hello + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu2_values.xml b/plugins/android/testData/findUsages/fu2_values.xml new file mode 100644 index 000000000000..ae503ae09b39 --- /dev/null +++ b/plugins/android/testData/findUsages/fu2_values.xml @@ -0,0 +1,5 @@ + + ng name="hello">hello + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu3_values.xml b/plugins/android/testData/findUsages/fu3_values.xml new file mode 100644 index 000000000000..0f71c487817f --- /dev/null +++ b/plugins/android/testData/findUsages/fu3_values.xml @@ -0,0 +1,5 @@ + + name="hello">hello + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu4_values.xml b/plugins/android/testData/findUsages/fu4_values.xml new file mode 100644 index 000000000000..68bcff440083 --- /dev/null +++ b/plugins/android/testData/findUsages/fu4_values.xml @@ -0,0 +1,5 @@ + + hello + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu5_values.xml b/plugins/android/testData/findUsages/fu5_values.xml new file mode 100644 index 000000000000..88982c39bb12 --- /dev/null +++ b/plugins/android/testData/findUsages/fu5_values.xml @@ -0,0 +1,5 @@ + + >hello + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu6_values.xml b/plugins/android/testData/findUsages/fu6_values.xml new file mode 100644 index 000000000000..3319786f7050 --- /dev/null +++ b/plugins/android/testData/findUsages/fu6_values.xml @@ -0,0 +1,5 @@ + + hello + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu7_values.xml b/plugins/android/testData/findUsages/fu7_values.xml new file mode 100644 index 000000000000..cac0b17f269a --- /dev/null +++ b/plugins/android/testData/findUsages/fu7_values.xml @@ -0,0 +1,5 @@ + + hellotring> + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu8_values.xml b/plugins/android/testData/findUsages/fu8_values.xml new file mode 100644 index 000000000000..b3349b91a06f --- /dev/null +++ b/plugins/android/testData/findUsages/fu8_values.xml @@ -0,0 +1,5 @@ + + hello</string> + + + \ No newline at end of file diff --git a/plugins/android/testData/findUsages/fu9_values.xml b/plugins/android/testData/findUsages/fu9_values.xml new file mode 100644 index 000000000000..2c8a4248c85b --- /dev/null +++ b/plugins/android/testData/findUsages/fu9_values.xml @@ -0,0 +1,8 @@ + + hello + + + str1 + str2 + + \ No newline at end of file diff --git a/plugins/android/testData/rename/layoutStrUsage.xml b/plugins/android/testData/rename/layoutStrUsage.xml new file mode 100644 index 000000000000..72f3d5e274a2 --- /dev/null +++ b/plugins/android/testData/rename/layoutStrUsage.xml @@ -0,0 +1,4 @@ + + + + diff --git a/plugins/android/testData/rename/layoutStrUsage_after.xml b/plugins/android/testData/rename/layoutStrUsage_after.xml new file mode 100644 index 000000000000..a2db8f608af7 --- /dev/null +++ b/plugins/android/testData/rename/layoutStrUsage_after.xml @@ -0,0 +1,4 @@ + + + + diff --git a/plugins/android/testData/rename/strings1.xml b/plugins/android/testData/rename/strings1.xml new file mode 100644 index 000000000000..3a956ed1c142 --- /dev/null +++ b/plugins/android/testData/rename/strings1.xml @@ -0,0 +1,3 @@ + + HellO! + \ No newline at end of file diff --git a/plugins/android/testData/rename/strings2.xml b/plugins/android/testData/rename/strings2.xml new file mode 100644 index 000000000000..a516f0490b97 --- /dev/null +++ b/plugins/android/testData/rename/strings2.xml @@ -0,0 +1,3 @@ + + HellO! + \ No newline at end of file diff --git a/plugins/android/testData/rename/strings3.xml b/plugins/android/testData/rename/strings3.xml new file mode 100644 index 000000000000..9a555511326d --- /dev/null +++ b/plugins/android/testData/rename/strings3.xml @@ -0,0 +1,3 @@ + + HellO!ng> + \ No newline at end of file diff --git a/plugins/android/testData/rename/strings4.xml b/plugins/android/testData/rename/strings4.xml new file mode 100644 index 000000000000..a2cc809bcb54 --- /dev/null +++ b/plugins/android/testData/rename/strings4.xml @@ -0,0 +1,3 @@ + + tring name="str">HellO! + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/AndroidFindUsagesTest.java b/plugins/android/testSrc/org/jetbrains/android/AndroidFindUsagesTest.java index bdc34d014777..6dd6f4b58898 100644 --- a/plugins/android/testSrc/org/jetbrains/android/AndroidFindUsagesTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/AndroidFindUsagesTest.java @@ -17,21 +17,23 @@ package org.jetbrains.android; import com.intellij.codeInsight.TargetElementUtilBase; +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; import com.intellij.usageView.UsageInfo; +import com.intellij.usages.PsiElementUsageTarget; +import com.intellij.usages.UsageTarget; +import com.intellij.usages.UsageTargetUtil; +import org.jetbrains.annotations.NonNls; +import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.ArrayList; /** - * Created by IntelliJ IDEA. - * User: Eugene.Kudelevsky - * Date: Aug 5, 2009 - * Time: 4:48:01 PM - * To change this template use File | Settings | File Templates. + * @author Eugene.Kudelevsky */ public class AndroidFindUsagesTest extends AndroidTestCase { private static final String BASE_PATH = "/findUsages/"; @@ -39,13 +41,12 @@ public class AndroidFindUsagesTest extends AndroidTestCase { @Override public void setUp() throws Exception { super.setUp(); - myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "picture3.gif", "res/drawable/picture3.gif"); myFixture.copyFileToProject(BASE_PATH + "R.java", "gen/p1/p2/R.java"); } - public List findCodeUsages(String path) throws Throwable { - Collection usages = findElementAtCaret(path, myFixture, BASE_PATH); + public List findCodeUsages(String path, String pathInProject) throws Throwable { + Collection usages = findUsages(path, myFixture, pathInProject); List result = new ArrayList(); for (UsageInfo usage : usages) { if (!usage.isNonCodeUsage) { @@ -56,70 +57,146 @@ public class AndroidFindUsagesTest extends AndroidTestCase { } public void testFileResource() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); myFixture.copyFileToProject(BASE_PATH + "styles.xml", "res/values/styles.xml"); - Collection references = findCodeUsages("fu1_layout.xml"); + Collection references = findCodeUsages("fu1_layout.xml", "res/layout/fu1_layout.xml"); assertEquals(3, references.size()); } public void testValueResource() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); - Collection references = findCodeUsages("fu2_layout.xml"); + Collection references = findCodeUsages("fu2_layout.xml", "res/layout/fu2_layout.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource1() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu1_values.xml", "res/values/fu1_values.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource2() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu2_values.xml", "res/values/fu2_values.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource3() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu2_values.xml", "res/values/fu3_values.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource4() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu2_values.xml", "res/values/fu4_values.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource5() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu2_values.xml", "res/values/fu5_values.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource6() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu2_values.xml", "res/values/fu6_values.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource7() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu2_values.xml", "res/values/fu7_values.xml"); + assertEquals(2, references.size()); + } + + public void testValueResource8() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); + myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); + Collection references = findCodeUsages("fu2_values.xml", "res/values/f8_values.xml"); assertEquals(2, references.size()); } public void testValueItemResource() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); - Collection references = findCodeUsages("fu5_layout.xml"); + Collection references = findCodeUsages("fu5_layout.xml", "res/layout/fu5_layout.xml"); assertEquals(2, references.size()); } public void testFileResourceField() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); - Collection references = findCodeUsages("Fu3.java"); + Collection references = findCodeUsages("Fu3.java", "src/p1/p2/Fu3.java"); assertEquals(2, references.size()); } public void testValueResourceField() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); - Collection references = findCodeUsages("Fu4.java"); + Collection references = findCodeUsages("Fu4.java", "src/p1/p2/Fu4.java"); assertEquals(2, references.size()); } public void testValueItemResourceField() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); - Collection references = findCodeUsages("Fu6.java"); + Collection references = findCodeUsages("Fu6.java", "src/p1/p2/Fu6.java"); assertEquals(2, references.size()); } public void testIdResource() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); - Collection references = findCodeUsages("fu7_layout.xml"); + Collection references = findCodeUsages("fu7_layout.xml", "res/layout/fu7_layout.xml"); assertEquals(2, references.size()); } public void testIdResourceField() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml"); - Collection references = findCodeUsages("Fu8.java"); + Collection references = findCodeUsages("Fu8.java", "src/p1/p2/Fu8.java"); assertEquals(2, references.size()); } public void testIdResourceDeclaration() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); - Collection references = findCodeUsages("fu9_layout.xml"); + Collection references = findCodeUsages("fu9_layout.xml", "res/layout/fu9_layout.xml"); assertEquals(2, references.size()); } public void testStringArray() throws Throwable { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java"); - Collection references = findCodeUsages("stringArray.xml"); + Collection references = findCodeUsages("stringArray.xml", "res/layout/stringArray.xml"); assertEquals(2, references.size()); } - private static Collection findElementAtCaret(String fileName, JavaCodeInsightTestFixture fixture, String basePath) throws Throwable { - String newFilePath = "res/layout/" + fileName; - VirtualFile file = fixture.copyFileToProject(basePath + fileName, newFilePath); - return findUsages(file, fixture); + private static Collection findUsages(String fileName, final JavaCodeInsightTestFixture fixture, String newFilePath) + throws Throwable { + VirtualFile file = fixture.copyFileToProject(BASE_PATH + fileName, newFilePath); + fixture.configureFromExistingVirtualFile(file); + + final UsageTarget[] targets = UsageTargetUtil.findUsageTargets(new DataProvider() { + @Override + public Object getData(@NonNls String dataId) { + return ((EditorEx)fixture.getEditor()).getDataContext().getData(dataId); + } + }); + + assert targets != null && targets.length > 0 && targets[0] instanceof PsiElementUsageTarget; + return fixture.findUsages(((PsiElementUsageTarget)targets[0]).getElement()); } public static Collection findUsages(VirtualFile file, JavaCodeInsightTestFixture fixture) throws Exception { diff --git a/plugins/android/testSrc/org/jetbrains/android/AndroidRenameTest.java b/plugins/android/testSrc/org/jetbrains/android/AndroidRenameTest.java index 2221080aa23b..6e022c678f55 100644 --- a/plugins/android/testSrc/org/jetbrains/android/AndroidRenameTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/AndroidRenameTest.java @@ -18,8 +18,10 @@ package org.jetbrains.android; import com.android.sdklib.SdkConstants; import com.intellij.codeInsight.TargetElementUtilBase; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -28,8 +30,10 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.PackageWrapper; import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesProcessor; import com.intellij.refactoring.move.moveClassesOrPackages.SingleSourceRootMoveDestination; -import com.intellij.refactoring.rename.RenameProcessor; -import com.intellij.refactoring.rename.RenamePsiElementProcessor; +import com.intellij.refactoring.rename.*; +import org.jetbrains.annotations.NonNls; + +import java.io.IOException; /** * Created by IntelliJ IDEA. @@ -132,6 +136,53 @@ public class AndroidRenameTest extends AndroidTestCase { myFixture.checkResultByFile("res/values/strings.xml", BASE_PATH + "strings_after.xml", true); } + public void testValueResource1() throws Throwable { + doTestStringRename("strings1.xml"); + } + + public void testValueResource2() throws Throwable { + doTestStringRename("strings2.xml"); + } + + public void testValueResource3() throws Throwable { + doTestStringRename("strings3.xml"); + } + + public void testValueResource4() throws Throwable { + doTestStringRename("strings4.xml"); + } + + private void doTestStringRename(String fileName) throws IOException { + createManifest(); + VirtualFile file = myFixture.copyFileToProject(BASE_PATH + fileName, "res/values/strings.xml"); + myFixture.configureFromExistingVirtualFile(file); + + myFixture.copyFileToProject(BASE_PATH + "layoutStrUsage.xml", "res/layout/layoutStrUsage.xml"); + myFixture.copyFileToProject(BASE_PATH + "R2.java", R_JAVA_PATH); + + findHandlerAndDoRename(); + + myFixture.checkResultByFile(BASE_PATH + "strings_after.xml"); + myFixture.checkResultByFile(R_JAVA_PATH, BASE_PATH + "R_value_after.java", true); + myFixture.checkResultByFile("res/layout/layoutStrUsage.xml", BASE_PATH + "layoutStrUsage_after.xml", true); + } + + private void findHandlerAndDoRename() throws IOException { + final DataContext editorContext = ((EditorEx)myFixture.getEditor()).getDataContext(); + final DataContext context = new DataContext() { + @Override + public Object getData(@NonNls String dataId) { + return PsiElementRenameHandler.DEFAULT_NAME.getName().equals(dataId) + ? "str1" + : editorContext.getData(dataId); + } + }; + final RenameHandler renameHandler = RenameHandlerRegistry.getInstance().getRenameHandler(context); + assertNotNull(renameHandler); + + renameHandler.invoke(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), context); + } + public void testJavaReferenceToFileResource() throws Throwable { createManifest(); VirtualFile file = myFixture.copyFileToProject(BASE_PATH + "R3.java", R_JAVA_PATH); From f6289371ef41f9067542d37b8dd81f41f0cc5ed3 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Thu, 9 Feb 2012 23:30:31 +0400 Subject: [PATCH 04/18] title for rename handler (can be available in group with AndroidRenameHandler) --- .../com/intellij/xml/refactoring/XmlTagRenameHandler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/xml/impl/src/com/intellij/xml/refactoring/XmlTagRenameHandler.java b/xml/impl/src/com/intellij/xml/refactoring/XmlTagRenameHandler.java index 8d4ed2a0bda8..e2531e7956fc 100644 --- a/xml/impl/src/com/intellij/xml/refactoring/XmlTagRenameHandler.java +++ b/xml/impl/src/com/intellij/xml/refactoring/XmlTagRenameHandler.java @@ -23,6 +23,7 @@ package com.intellij.xml.refactoring; import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.ide.TitledHandler; import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.LangDataKeys; @@ -45,7 +46,7 @@ import com.intellij.xml.impl.schema.AnyXmlElementDescriptor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class XmlTagRenameHandler implements RenameHandler { +public class XmlTagRenameHandler implements RenameHandler, TitledHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.xml.refactoring.XmlTagRenameHandler"); @@ -74,6 +75,11 @@ public class XmlTagRenameHandler implements RenameHandler { return isAvailableOnDataContext(dataContext); } + @Override + public String getActionTitle() { + return "Rename XML tag"; + } + private static boolean isInplaceRenameAvailable(final Editor editor) { return editor.getSettings().isVariableInplaceRenameEnabled(); } From b3a9e1d728d8d12e122b12602c8286d19a106bcb Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Fri, 10 Feb 2012 14:47:57 +0400 Subject: [PATCH 05/18] fix tests: provide path for community output directory --- plugins/android/src/META-INF/plugin.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 245e7a2f1ad4..879eb049c278 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -198,6 +198,7 @@ + From bd679f42d710f33a308d4124c00f65daa3e18bea Mon Sep 17 00:00:00 2001 From: anna Date: Fri, 10 Feb 2012 11:53:27 +0100 Subject: [PATCH 06/18] revert: waiting for a fix --- .../src/com/intellij/execution/impl/ConsoleViewImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index 24102411122a..e9c60bb92039 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -500,7 +500,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private ModalityState getStateForUpdate() { - return null;//myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(this); + return myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(this); } private void requestFlushImmediately() { From 5ce639ae27ffa2f842fcb8ecc184148a8811d46f Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 9 Feb 2012 17:02:32 +0400 Subject: [PATCH 07/18] GitSmartOperationDialog: "checkout" -> operation title --- .../branch/GitSmartOperationDialog.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java index 0d8a187664e4..9eb9b8c6193b 100644 --- a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java +++ b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -17,7 +17,6 @@ package git4idea.branch; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; import com.intellij.ui.IdeBorderFactory; @@ -31,6 +30,8 @@ import java.awt.event.ActionEvent; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import static com.intellij.openapi.util.text.StringUtil.capitalize; + /** * The dialog that is shown when the error * "Your local changes to the following files would be overwritten by merge/checkout" @@ -46,7 +47,7 @@ class GitSmartOperationDialog extends DialogWrapper { private final Project myProject; private final List myChanges; - @NotNull private final String myCapitalizedOperationTitle; + @NotNull private final String myOperationTitle; private final boolean myForceButton; /** @@ -71,10 +72,10 @@ class GitSmartOperationDialog extends DialogWrapper { super(project); myProject = project; myChanges = changes; - myCapitalizedOperationTitle = StringUtil.capitalize(operationTitle); + myOperationTitle = operationTitle; myForceButton = forceButton; - setOKButtonText("Smart " + myCapitalizedOperationTitle); - setCancelButtonText("Don't " + myCapitalizedOperationTitle); + setOKButtonText("Smart " + capitalize(myOperationTitle)); + setCancelButtonText("Don't " + capitalize(myOperationTitle)); getCancelAction().putValue(FOCUSED_ACTION, Boolean.TRUE); init(); } @@ -82,15 +83,15 @@ class GitSmartOperationDialog extends DialogWrapper { @Override protected Action[] createLeftSideActions() { if (myForceButton) { - return new Action[] {new ForceCheckoutAction(myCapitalizedOperationTitle) }; + return new Action[] {new ForceCheckoutAction(myOperationTitle) }; } return new Action[0]; } @Override protected JComponent createNorthPanel() { - JBLabel description = new JBLabel("Your local changes to the following files would be overwritten by checkout.
" + - "IDEA can stash the changes, checkout and unstash them after that."); + JBLabel description = new JBLabel("Your local changes to the following files would be overwritten by " + myOperationTitle + + ".
IDEA can stash the changes, " + myOperationTitle + " and unstash them after that."); description.setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 10, 0)); return description; } @@ -112,7 +113,7 @@ class GitSmartOperationDialog extends DialogWrapper { private class ForceCheckoutAction extends AbstractAction { ForceCheckoutAction(@NotNull String operationTitle) { - super("Force " + operationTitle); + super("Force " + capitalize(operationTitle)); } @Override From d8c483d3077c04be2e32203bd966f06207926278 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 9 Feb 2012 17:03:15 +0400 Subject: [PATCH 08/18] GitStashChangesSaver: show only one conflict resolving dialog for several conflicting roots: collect conflicts and then display them at once. --- .../git4idea/stash/GitStashChangesSaver.java | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java index 9ce2fe859bde..24265cf34d37 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java @@ -77,9 +77,22 @@ public class GitStashChangesSaver extends GitChangesSaver { } public void load() throws VcsException { + Collection conflictedRoots = new ArrayList(); for (VirtualFile root : myStashedRoots) { - loadRoot(root); + boolean conflict = loadRoot(root); + if (conflict) { + conflictedRoots.add(root); + } } + + boolean conflictsResolved = new UnstashConflictResolver(myProject, myStashedRoots, myParams).merge(); + if (conflictsResolved) { + LOG.info("load: all conflicts resolved, dropping stash in " + myStashedRoots); + for (VirtualFile root : conflictedRoots) { + GitStashUtils.dropStash(myProject, root); + } + } + // we'll refresh more but this way we needn't compute what files under roots etc LocalFileSystem.getInstance().refreshIoFiles(myChangeManager.getAffectedPaths()); } @@ -122,7 +135,11 @@ public class GitStashChangesSaver extends GitChangesSaver { } } - private void loadRoot(final VirtualFile root) throws VcsException { + /** + * Returns true if the root was loaded with conflict. + * False is returned in all other cases: in the case of success and in case of some other error. + */ + private boolean loadRoot(final VirtualFile root) throws VcsException { LOG.info("loadRoot " + root); myProgressIndicator.setText(GitHandlerUtil.formatOperationName("Unstashing changes to", root)); final GitLineHandler handler = new GitLineHandler(myProject, root, GitCommand.STASH); @@ -159,26 +176,21 @@ public class GitStashChangesSaver extends GitChangesSaver { if (failure.get()) { if (conflict.get()) { - boolean conflictsResolved = new UnstashConflictResolver(myProject, root, myStashedRoots, myParams).merge(); - if (conflictsResolved) { - LOG.info("loadRoot " + root + " conflicts resolved, dropping stash"); - GitStashUtils.dropStash(myProject, root); - } + return true; } else { LOG.info("unstash failed " + handler.errors()); GitUIUtil.notifyImportantError(myProject, "Couldn't unstash", "
" + GitUIUtil.stringifyErrors(handler.errors())); } } + return false; } private static class UnstashConflictResolver extends GitConflictResolver { - private final VirtualFile myRoot; private final Set myStashedRoots; - public UnstashConflictResolver(@NotNull Project project, @NotNull VirtualFile root, @NotNull Set stashedRoots, @Nullable Params params) { - super(project, Collections.singleton(root), makeParamsOrUse(params)); - myRoot = root; + public UnstashConflictResolver(@NotNull Project project, @NotNull Set stashedRoots, @Nullable Params params) { + super(project, stashedRoots, makeParamsOrUse(params)); myStashedRoots = stashedRoots; } @@ -206,7 +218,7 @@ public class GitStashChangesSaver extends GitChangesSaver { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { if (event.getDescription().equals("saver")) { // we don't use #showSavedChanges to specify unmerged root first - GitUnstashDialog.showUnstashDialog(myProject, new ArrayList(myStashedRoots), myRoot, + GitUnstashDialog.showUnstashDialog(myProject, new ArrayList(myStashedRoots), myStashedRoots.iterator().next(), new HashSet()); } else if (event.getDescription().equals("resolve")) { mergeNoProceed(); From 36823438097561d0c8b8520482f3fae17901b026 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 9 Feb 2012 17:17:33 +0400 Subject: [PATCH 09/18] Hide balloon with a link, only when the link is clicked, not on any event. --- .../src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java index d96b8b17f2b8..c7ac33742fe9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java @@ -1873,7 +1873,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements @Override public void hyperlinkUpdate(HyperlinkEvent e) { - if (myBalloon != null) { + if (myBalloon != null && e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { myBalloon.hide(); } if (myListener != null) { From 28155b5e1314165ffd93632ef3417b6c871a961d Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 9 Feb 2012 17:21:30 +0400 Subject: [PATCH 10/18] Propose to delete branch after merge. In the successful notification about merged branch propose to delete this branch. Clicking the link invokes new GitBranchOperationsProcessor.delete. --- .../git4idea/branch/GitBranchOperation.java | 2 +- .../branch/GitBranchOperationsProcessor.java | 2 +- .../git4idea/branch/GitMergeOperation.java | 23 +++++++++++++++++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java index d9676498e5e1..456f6922f096 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java @@ -159,7 +159,7 @@ abstract class GitBranchOperation { NotificationManager.getInstance(myProject).notify(GitVcs.NOTIFICATION_GROUP_ID, "", message, NotificationType.INFORMATION); } - protected void notifySuccess() { + protected final void notifySuccess() { notifySuccess(getSuccessMessage()); } diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java index e066aa825424..4f68d4dcd8aa 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java @@ -247,7 +247,7 @@ public final class GitBranchOperationsProcessor { for (GitRepository repository : myRepositories) { revisions.put(repository, repository.getCurrentRevision()); } - new GitMergeOperation(myProject, myRepositories, branchName, getCurrentBranch(), revisions, indicator).execute(); + new GitMergeOperation(myProject, myRepositories, branchName, getCurrentBranch(), mySelectedRepository, revisions, indicator).execute(); } /** diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java index fce48e4c0643..05e0069dc8db 100644 --- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java @@ -15,6 +15,8 @@ */ package git4idea.branch; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; @@ -35,6 +37,7 @@ import git4idea.repo.GitRepository; import git4idea.util.GitPreservingProcess; import org.jetbrains.annotations.NotNull; +import javax.swing.event.HyperlinkEvent; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -52,6 +55,7 @@ class GitMergeOperation extends GitBranchOperation { @NotNull private final ChangeListManager myChangeListManager; @NotNull private final String myBranchToMerge; @NotNull private final String myCurrentBranch; + @NotNull private final GitRepository myCurrentRepository; @NotNull private final Map myCurrentRevisionsBeforeMerge; // true in value if unstash will be needed after resolving conflicts. @@ -59,13 +63,13 @@ class GitMergeOperation extends GitBranchOperation { private GitPreservingProcess myPreservingProcess; protected GitMergeOperation(@NotNull Project project, @NotNull Collection repositories, - @NotNull String branchToMerge, - @NotNull String currentBranch, + @NotNull String branchToMerge, @NotNull String currentBranch, @NotNull GitRepository currentRepository, @NotNull Map currentRevisionsBeforeMerge, @NotNull ProgressIndicator indicator) { super(project, repositories, indicator); myBranchToMerge = branchToMerge; myCurrentBranch = currentBranch; + myCurrentRepository = currentRepository; myCurrentRevisionsBeforeMerge = currentRevisionsBeforeMerge; myChangeListManager = ChangeListManager.getInstance(myProject); } @@ -133,6 +137,21 @@ class GitMergeOperation extends GitBranchOperation { restoreLocalChanges(); } + @Override + protected void notifySuccess(@NotNull String message) { + String description = message + "
Delete " + myBranchToMerge + ""; + NotificationManager.getInstance(myProject).notify(GitVcs.NOTIFICATION_GROUP_ID, "", description, NotificationType.INFORMATION, + new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED && event.getDescription().equalsIgnoreCase("delete")) { + new GitBranchOperationsProcessor(myProject, new ArrayList(getRepositories()), myCurrentRepository). + deleteBranch(myBranchToMerge); + } + } + }); + } + private boolean resolveConflicts() { if (!myConflictedRepositories.isEmpty()) { GitMerger merger = new GitMerger(myProject); From ca652fcca7ac1d16bd032f8d4bbc1451a285c264 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 9 Feb 2012 19:23:52 +0400 Subject: [PATCH 11/18] Fix messages in tests --- plugins/git4idea/src/git4idea/branch/GitMergeOperation.java | 2 +- .../tests/git4idea/branch/GitBranchOperationsTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java index 05e0069dc8db..dde3d9a2abac 100644 --- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java @@ -130,7 +130,7 @@ class GitMergeOperation extends GitBranchOperation { notifySuccess(); } else { - notifySuccess("Already up-to-date."); + notifySuccess("Already up-to-date"); } } diff --git a/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java b/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java index 958213bbd41f..69dc52cb57c4 100644 --- a/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java +++ b/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java @@ -492,7 +492,7 @@ public class GitBranchOperationsTest extends AbstractVcsTestCase { public void merge_simple_without_problems() throws Exception { prepareBranchWithCommit("feature", myUltimate, myCommunity, myContrib); doMerge("feature"); - assertNotify(NotificationType.INFORMATION, "Merged feature to master"); + assertNotify(NotificationType.INFORMATION, "Merged feature to master
Delete feature"); assertFile(myUltimate, "unmerged_branch_file", "content"); assertFile(myCommunity, "unmerged_branch_file", "content"); @@ -514,7 +514,7 @@ public class GitBranchOperationsTest extends AbstractVcsTestCase { doMerge("master2"); - assertNotify(NotificationType.INFORMATION, "Already up-to-date."); + assertNotify(NotificationType.INFORMATION, "Already up-to-date
Delete master2"); } @Test @@ -525,7 +525,7 @@ public class GitBranchOperationsTest extends AbstractVcsTestCase { doMerge("master2"); - assertNotify(NotificationType.INFORMATION, "Merged master2 to master"); + assertNotify(NotificationType.INFORMATION, "Merged master2 to master
Delete master2"); assertFile(myCommunity, "unmerged_branch_file", "content"); assertNull(myUltimate.getRoot().findChild("unmerged_branch_file")); } From 2292dd7069812cc094e6e6403dbf05a529272343 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 9 Feb 2012 19:25:16 +0400 Subject: [PATCH 12/18] Don't refresh automatically on unstash. Add the capability to refresh() method and call it from GitUpdateProcess. --- .../src/git4idea/stash/GitChangesSaver.java | 5 +++++ .../git4idea/stash/GitShelveChangesSaver.java | 12 ++++++++++-- .../git4idea/stash/GitStashChangesSaver.java | 17 +++++++++++------ .../src/git4idea/update/GitUpdateProcess.java | 1 + 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java index 589e847d342b..7b1bb953cf38 100644 --- a/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitChangesSaver.java @@ -51,6 +51,11 @@ public abstract class GitChangesSaver { protected GitConflictResolver.Params myParams; + /** + * Refreshes files changed during save or load. + */ + public abstract void refresh(); + /** * Returns an instance of the proper GitChangesSaver depending on the chosen save changes policy. * @return {@link GitStashChangesSaver}, {@link GitShelveChangesSaver} or {@link GitDumbChangesSaver} diff --git a/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java index ca5aa577ce80..55d2829e6d87 100644 --- a/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java @@ -89,11 +89,19 @@ public class GitShelveChangesSaver extends GitChangesSaver { return myShelvedChangeList != null; } - @Override public String getSaverName() { + @Override + public String getSaverName() { return "shelf"; } - @Override protected void showSavedChanges() { + @Override + protected void showSavedChanges() { myShelveViewManager.activateView(myShelvedChangeList); } + + @Override + public void refresh() { + // refreshed inside shelve manager + } + } diff --git a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java index 24265cf34d37..c81bf8dbc64b 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java @@ -35,8 +35,8 @@ import git4idea.commands.*; import git4idea.config.GitVcsSettings; import git4idea.convert.GitFileSeparatorConverter; import git4idea.merge.GitConflictResolver; -import git4idea.util.GitUIUtil; import git4idea.ui.GitUnstashDialog; +import git4idea.util.GitUIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -92,9 +92,6 @@ public class GitStashChangesSaver extends GitChangesSaver { GitStashUtils.dropStash(myProject, root); } } - - // we'll refresh more but this way we needn't compute what files under roots etc - LocalFileSystem.getInstance().refreshIoFiles(myChangeManager.getAffectedPaths()); } @Override @@ -102,14 +99,22 @@ public class GitStashChangesSaver extends GitChangesSaver { return !myStashedRoots.isEmpty(); } - @Override public String getSaverName() { + @Override + public String getSaverName() { return "stash"; } - @Override protected void showSavedChanges() { + @Override + protected void showSavedChanges() { GitUnstashDialog.showUnstashDialog(myProject, new ArrayList(myStashedRoots), myStashedRoots.iterator().next(), new HashSet()); } + @Override + public void refresh() { + // we'll refresh more but this way we needn't compute what files under roots etc + LocalFileSystem.getInstance().refreshIoFiles(myChangeManager.getAffectedPaths()); + } + private void stash(Collection roots) throws VcsException { for (VirtualFile root : roots) { final String message = GitHandlerUtil.formatOperationName("Stashing changes from", root); diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java b/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java index 6f4c81c8aeb6..9e0209a15d0a 100644 --- a/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java +++ b/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java @@ -215,6 +215,7 @@ public class GitUpdateProcess { final ContinuationFinalTasksInserter finalTasksInserter = new ContinuationFinalTasksInserter(context); finalTasksInserter.allNextAreFinal(); mySaver.restoreLocalChanges(context); + mySaver.refresh(); finalTasksInserter.removeFinalPropertyAdder(); } From 12b55a7321b012fa3de93293c625898456f19576 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 9 Feb 2012 19:33:51 +0400 Subject: [PATCH 13/18] Add mnemonic to "Force checkout/merge" --- .../git4idea/src/git4idea/branch/GitSmartOperationDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java index 9eb9b8c6193b..f03b940f4bf8 100644 --- a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java +++ b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java @@ -113,7 +113,7 @@ class GitSmartOperationDialog extends DialogWrapper { private class ForceCheckoutAction extends AbstractAction { ForceCheckoutAction(@NotNull String operationTitle) { - super("Force " + capitalize(operationTitle)); + super("&Force " + capitalize(operationTitle)); } @Override From 2406fba76ac378874a7e63535314d3c6f969f1d2 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 10 Feb 2012 14:08:19 +0400 Subject: [PATCH 14/18] better name for GitBranchOperationsProcessor#getCurrentBranchOrRev --- .../branch/GitBranchOperationsProcessor.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java index 4f68d4dcd8aa..887983904b2c 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java @@ -81,7 +81,7 @@ public final class GitBranchOperationsProcessor { } @NotNull - private String getCurrentBranch() { + private String getCurrentBranchOrRev() { if (myRepositories.size() > 1) { GitMultiRootBranchConfig multiRootBranchConfig = new GitMultiRootBranchConfig(myRepositories); String currentBranch = multiRootBranchConfig.getCurrentBranch(); @@ -121,7 +121,7 @@ public final class GitBranchOperationsProcessor { } private void doCheckoutNewBranch(@NotNull final String name, @NotNull ProgressIndicator indicator) { - new GitCheckoutNewBranchOperation(myProject, myRepositories, name, getCurrentBranch(), indicator).execute(); + new GitCheckoutNewBranchOperation(myProject, myRepositories, name, getCurrentBranchOrRev(), indicator).execute(); } /** @@ -162,7 +162,7 @@ public final class GitBranchOperationsProcessor { } private void doCheckout(@NotNull ProgressIndicator indicator, @NotNull String reference, @Nullable String newBranch) { - new GitCheckoutOperation(myProject, myRepositories, reference, newBranch, getCurrentBranch(), indicator).execute(); + new GitCheckoutOperation(myProject, myRepositories, reference, newBranch, getCurrentBranchOrRev(), indicator).execute(); } public void deleteBranch(final String branchName) { @@ -174,7 +174,7 @@ public final class GitBranchOperationsProcessor { } private void doDelete(final String branchName, ProgressIndicator indicator) { - new GitDeleteBranchOperation(myProject, myRepositories, branchName, getCurrentBranch(), indicator).execute(); + new GitDeleteBranchOperation(myProject, myRepositories, branchName, getCurrentBranchOrRev(), indicator).execute(); } /** @@ -197,7 +197,7 @@ public final class GitBranchOperationsProcessor { LOG.error("The task to get compare info didn't finish. Repositories: \n" + myRepositories + "\nbranch name: " + branchName); return; } - displayCompareDialog(branchName, getCurrentBranch(), myCompareInfo); + displayCompareDialog(branchName, getCurrentBranchOrRev(), myCompareInfo); } }.runInBackground(); } @@ -247,7 +247,7 @@ public final class GitBranchOperationsProcessor { for (GitRepository repository : myRepositories) { revisions.put(repository, repository.getCurrentRevision()); } - new GitMergeOperation(myProject, myRepositories, branchName, getCurrentBranch(), mySelectedRepository, revisions, indicator).execute(); + new GitMergeOperation(myProject, myRepositories, branchName, getCurrentBranchOrRev(), mySelectedRepository, revisions, indicator).execute(); } /** From 0d42ddfea256f7f3fad228cdb0dc0a3605e3ef10 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 10 Feb 2012 14:28:08 +0400 Subject: [PATCH 15/18] PopupFactoryImpl: use the preselected action condition in sub-popups. --- .../intellij/ui/popup/PopupFactoryImpl.java | 87 +++++++++++++------ 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java index 2057539a4037..1e6e8f9940ae 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java @@ -208,20 +208,8 @@ public class PopupFactoryImpl extends JBPopupFactory { builder.buildGroup(actionGroup); final List items = builder.getItems(); - int defaultOptionIndex = 0; - if (preselectActionCondition != null) { - for (int i = 0; i < items.size(); i++) { - final AnAction action = items.get(i).getAction(); - if (preselectActionCondition.value(action)) { - defaultOptionIndex = i; - break; - } - } - } - return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items), - defaultOptionIndex, - false, showDisabledActions); + preselectActionCondition, false, showDisabledActions); } @Override @@ -300,14 +288,42 @@ public class PopupFactoryImpl extends JBPopupFactory { return createActionsStep(actionGroup, dataContext, showNumbers, showDisabledActions, title, component, honorActionMnemonics, 0, false); } - private static ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext, boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions, - String title, Component component, boolean honorActionMnemonics, int defaultOptionIndex, - final boolean autoSelectionEnabled) { - final ActionStepBuilder builder = new ActionStepBuilder(dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics); - builder.buildGroup(actionGroup); - final List items = builder.getItems(); + private static ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext, + boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions, + String title, Component component, boolean honorActionMnemonics, + final int defaultOptionIndex, final boolean autoSelectionEnabled) { + final List items = makeActionItemsFromActionGroup(actionGroup, dataContext, showNumbers, useAlphaAsNumbers, + showDisabledActions, honorActionMnemonics); + return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items), + new Condition() { + @Override + public boolean value(AnAction action) { + return defaultOptionIndex >= 0 && + defaultOptionIndex < items.size() && + items.get(defaultOptionIndex).getAction().equals(action); + } + }, autoSelectionEnabled, showDisabledActions); + } - return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items), defaultOptionIndex, + private static List makeActionItemsFromActionGroup(ActionGroup actionGroup, + DataContext dataContext, + boolean showNumbers, + boolean useAlphaAsNumbers, + boolean showDisabledActions, + boolean honorActionMnemonics) { + final ActionStepBuilder builder = new ActionStepBuilder(dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, + honorActionMnemonics); + builder.buildGroup(actionGroup); + return builder.getItems(); + } + + private static ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext, + boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions, + String title, Component component, boolean honorActionMnemonics, + Condition preselectActionCondition, boolean autoSelectionEnabled) { + final List items = makeActionItemsFromActionGroup(actionGroup, dataContext, showNumbers, useAlphaAsNumbers, + showDisabledActions, honorActionMnemonics); + return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items), preselectActionCondition, autoSelectionEnabled, showDisabledActions); } @@ -487,21 +503,35 @@ public class PopupFactoryImpl extends JBPopupFactory { private final boolean myAutoSelectionEnabled; private final boolean myShowDisabledActions; private Runnable myFinalRunnable; + @Nullable private final Condition myPreselectActionCondition; - private ActionPopupStep(@NotNull final List items, - final String title, - Component context, - boolean enableMnemonics, - final int defaultOptionIndex, final boolean autoSelection, boolean showDisabledActions) { + private ActionPopupStep(@NotNull final List items, final String title, Component context, boolean enableMnemonics, + @Nullable Condition preselectActionCondition, final boolean autoSelection, boolean showDisabledActions) { myItems = items; myTitle = title; myContext = context; myEnableMnemonics = enableMnemonics; - myDefaultOptionIndex = defaultOptionIndex; + myDefaultOptionIndex = getDefaultOptionIndexFromSelectCondition(preselectActionCondition, items); + myPreselectActionCondition = preselectActionCondition; myAutoSelectionEnabled = autoSelection; myShowDisabledActions = showDisabledActions; } + private static int getDefaultOptionIndexFromSelectCondition(@Nullable Condition preselectActionCondition, + @NotNull List items) { + int defaultOptionIndex = 0; + if (preselectActionCondition != null) { + for (int i = 0; i < items.size(); i++) { + final AnAction action = items.get(i).getAction(); + if (preselectActionCondition.value(action)) { + defaultOptionIndex = i; + break; + } + } + } + return defaultOptionIndex; + } + @NotNull public List getValues() { return myItems; @@ -552,13 +582,14 @@ public class PopupFactoryImpl extends JBPopupFactory { final DataContext dataContext = myContext != null ? mgr.getDataContext(myContext) : mgr.getDataContext(); if (action instanceof ActionGroup && (!finalChoice || !((ActionGroup)action).canBePerformed(dataContext))) { - return JBPopupFactory.getInstance().createActionsStep((ActionGroup)action, dataContext, myEnableMnemonics, myShowDisabledActions, null, myContext, false); + return createActionsStep((ActionGroup)action, dataContext, myEnableMnemonics, true, myShowDisabledActions, null, myContext, false, + myPreselectActionCondition, false); } else { myFinalRunnable = new Runnable() { public void run() { action.actionPerformed( - new AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, (Presentation)action.getTemplatePresentation().clone(), + new AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, action.getTemplatePresentation().clone(), ActionManager.getInstance(), 0)); } }; From a83c894c7d23a33aad872db560fe4437cd5f01e5 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 10 Feb 2012 14:31:29 +0400 Subject: [PATCH 16/18] Don't return null from getInstance(): better to handle disposed project, where it is needed. --- plugins/git4idea/src/git4idea/config/GitVcsSettings.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index b05c7370d1f8..d5d7ef1f20e8 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java @@ -80,11 +80,7 @@ public class GitVcsSettings implements PersistentStateComponent Date: Fri, 10 Feb 2012 14:34:38 +0400 Subject: [PATCH 17/18] GitBranchPopup: preselect the recent branch. Store recent branch information for common branches and for each repository (preselect such in sub-popups and if branches diverge or not synched). --- .../git4idea/branch/GitBranchOperation.java | 21 ++++++++++++++- .../branch/GitCheckoutNewBranchOperation.java | 6 ++--- .../git4idea/branch/GitCheckoutOperation.java | 5 ++-- .../branch/GitDeleteBranchOperation.java | 4 +-- .../git4idea/branch/GitMergeOperation.java | 2 +- .../src/git4idea/config/GitVcsSettings.java | 22 ++++++++++++++++ .../git4idea/ui/branch/GitBranchPopup.java | 26 ++++++++++++++++++- .../ui/branch/GitBranchPopupActions.java | 10 +++++++ 8 files changed, 86 insertions(+), 10 deletions(-) diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java index 456f6922f096..99a5a01bf2e7 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java @@ -34,6 +34,7 @@ import com.intellij.util.Function; import com.intellij.util.ui.UIUtil; import git4idea.*; import git4idea.commands.GitMessageWithFilesDetector; +import git4idea.config.GitVcsSettings; import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; import git4idea.util.UntrackedFilesNotifier; @@ -59,18 +60,22 @@ abstract class GitBranchOperation { @NotNull protected final Project myProject; @NotNull private final Collection myRepositories; + @NotNull private final String myCurrentBranchOrRev; @NotNull private final ProgressIndicator myIndicator; + private final GitVcsSettings mySettings; @NotNull private final Collection mySuccessfulRepositories; @NotNull private final Collection myRemainingRepositories; protected GitBranchOperation(@NotNull Project project, @NotNull Collection repositories, - @NotNull ProgressIndicator indicator) { + @NotNull String currentBranchOrRev, @NotNull ProgressIndicator indicator) { myProject = project; myRepositories = repositories; + myCurrentBranchOrRev = currentBranchOrRev; myIndicator = indicator; mySuccessfulRepositories = new ArrayList(); myRemainingRepositories = new ArrayList(myRepositories); + mySettings = GitVcsSettings.getInstance(myProject); } protected abstract void execute(); @@ -242,6 +247,20 @@ abstract class GitBranchOperation { return pluralize("repository", getSuccessfulRepositories().size()); } + /** + * Updates the recently visited branch in the settings. + * This is to be performed after successful checkout operation. + */ + protected void updateRecentBranch() { + if (getRepositories().size() == 1) { + GitRepository repository = myRepositories.iterator().next(); + mySettings.setRecentBranchOfRepository(repository.getRoot().getPath(), myCurrentBranchOrRev); + } + else { + mySettings.setRecentCommonBranch(myCurrentBranchOrRev); + } + } + private void showUnmergedFilesDialogWithRollback() { final AtomicBoolean ok = new AtomicBoolean(); UIUtil.invokeAndWaitIfNeeded(new Runnable() { diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java index 27f135bac9a3..553277e329f4 100644 --- a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java @@ -44,9 +44,8 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation { @NotNull private final String myPreviousBranch; GitCheckoutNewBranchOperation(@NotNull Project project, @NotNull Collection repositories, - @NotNull String newBranchName, @NotNull String previousBranch, - @NotNull ProgressIndicator indicator) { - super(project, repositories, indicator); + @NotNull String newBranchName, @NotNull String previousBranch, @NotNull ProgressIndicator indicator) { + super(project, repositories, previousBranch, indicator); myNewBranchName = newBranchName; myProject = project; myPreviousBranch = previousBranch; @@ -77,6 +76,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation { if (!fatalErrorHappened) { notifySuccess(); + updateRecentBranch(); } } diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java index 6585ce74fd73..e3530b19e040 100644 --- a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java @@ -59,7 +59,7 @@ class GitCheckoutOperation extends GitBranchOperation { GitCheckoutOperation(@NotNull Project project, @NotNull Collection repositories, @NotNull String startPointReference, @Nullable String newBranch, @NotNull String previousBranch, @NotNull ProgressIndicator indicator) { - super(project, repositories, indicator); + super(project, repositories, previousBranch, indicator); myStartPointReference = startPointReference; myNewBranch = newBranch; myPreviousBranch = previousBranch; @@ -104,10 +104,11 @@ class GitCheckoutOperation extends GitBranchOperation { if (!fatalErrorHappened) { notifySuccess(); + updateRecentBranch(); } } - private boolean smartCheckoutOrNotify(@NotNull GitRepository repository, + private boolean smartCheckoutOrNotify(@NotNull GitRepository repository, @NotNull GitMessageWithFilesDetector localChangesOverwrittenByCheckout) { Pair, List> conflictingRepositoriesAndAffectedChanges = getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByCheckout, myPreviousBranch, myStartPointReference); diff --git a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java index 0c4c89fa3f69..b21d46ffc41b 100644 --- a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java @@ -53,8 +53,8 @@ class GitDeleteBranchOperation extends GitBranchOperation { private final String myCurrentBranch; GitDeleteBranchOperation(@NotNull Project project, @NotNull Collection repositories, - @NotNull String branchName, @NotNull String currentBranch, @NotNull ProgressIndicator indicator) { - super(project, repositories, indicator); + @NotNull String branchName, @NotNull String currentBranch, @NotNull ProgressIndicator indicator) { + super(project, repositories, currentBranch, indicator); myBranchName = branchName; myCurrentBranch = currentBranch; } diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java index dde3d9a2abac..5009f1ca5400 100644 --- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java @@ -66,7 +66,7 @@ class GitMergeOperation extends GitBranchOperation { @NotNull String branchToMerge, @NotNull String currentBranch, @NotNull GitRepository currentRepository, @NotNull Map currentRevisionsBeforeMerge, @NotNull ProgressIndicator indicator) { - super(project, repositories, indicator); + super(project, repositories, currentBranch, indicator); myBranchToMerge = branchToMerge; myCurrentBranch = currentBranch; myCurrentRepository = currentRepository; diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index d5d7ef1f20e8..61ccb83bc04a 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java @@ -26,7 +26,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Git VCS settings @@ -70,6 +72,8 @@ public class GitVcsSettings implements PersistentStateComponent RECENT_BRANCH_BY_REPOSITORY = new HashMap(); + public String RECENT_COMMON_BRANCH = null; } public GitVcsSettings(GitVcsApplicationSettings appSettings) { @@ -168,4 +172,22 @@ public class GitVcsSettings implements PersistentStateComponent getRecentBranchesByRepository() { + return myState.RECENT_BRANCH_BY_REPOSITORY; + } + + public void setRecentBranchOfRepository(@NotNull String repositoryPath, @NotNull String branch) { + myState.RECENT_BRANCH_BY_REPOSITORY.put(repositoryPath, branch); + } + + @Nullable + public String getRecentCommonBranch() { + return myState.RECENT_COMMON_BRANCH; + } + + public void setRecentCommonBranch(@NotNull String branch) { + myState.RECENT_COMMON_BRANCH = branch; + } + } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index 715e459f40ac..395488cd219d 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -27,6 +27,7 @@ import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.IconLoader; import com.intellij.ui.popup.PopupFactoryImpl; import com.intellij.ui.popup.WizardPopup; @@ -88,10 +89,33 @@ class GitBranchPopup { String title = createPopupTitle(currentRepository); + Condition preselectActionCondition = new Condition() { + @Override + public boolean value(AnAction action) { + if (action instanceof GitBranchPopupActions.LocalBranchActions) { + GitBranchPopupActions.LocalBranchActions branchAction = (GitBranchPopupActions.LocalBranchActions)action; + String branchName = branchAction.getBranchName(); + + String recentBranch; + List repositories = branchAction.getRepositories(); + if (repositories.size() == 1) { + recentBranch = myVcsSettings.getRecentBranchesByRepository().get(repositories.iterator().next().getRoot().getPath()); + } + else { + recentBranch = myVcsSettings.getRecentCommonBranch(); + } + + if (recentBranch != null && recentBranch.equals(branchName)) { + return true; + } + } + return false; + } + }; myPopup = new PopupFactoryImpl.ActionGroupPopup( title, createActions(), SimpleDataContext.getProjectContext(project), - false, false, false, true, null, -1, null, null) { + false, false, false, true, null, -1, preselectActionCondition, null) { @Override protected WizardPopup createPopup(WizardPopup parent, PopupStep step, Object parentValue) { WizardPopup popup = super.createPopup(parent, step, parentValue); diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java index ba4d118967ea..fa6e23495187 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java @@ -180,6 +180,16 @@ class GitBranchPopupActions { } } + @NotNull + List getRepositories() { + return myRepositories; + } + + @NotNull + public String getBranchName() { + return myBranchName; + } + @NotNull @Override public AnAction[] getChildren(@Nullable AnActionEvent e) { From e07375b2904266ebc289e26db5eebe91ba92aa72 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 10 Feb 2012 14:43:44 +0400 Subject: [PATCH 18/18] Add icon to Git -> Branches action --- plugins/git4idea/src/META-INF/plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index 266e2f06b86d..732ef0e79d7f 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -37,7 +37,7 @@ - +