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 { 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() { 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) { 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)); } }; diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 67a616094d46..879eb049c278 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -190,11 +190,15 @@ + + + + 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); 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 @@ - + diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java index d9676498e5e1..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(); @@ -159,7 +164,7 @@ abstract class GitBranchOperation { NotificationManager.getInstance(myProject).notify(GitVcs.NOTIFICATION_GROUP_ID, "", message, NotificationType.INFORMATION); } - protected void notifySuccess() { + protected final void notifySuccess() { notifySuccess(getSuccessMessage()); } @@ -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/GitBranchOperationsProcessor.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java index e066aa825424..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(), revisions, indicator).execute(); + new GitMergeOperation(myProject, myRepositories, branchName, getCurrentBranchOrRev(), mySelectedRepository, revisions, indicator).execute(); } /** 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 fce48e4c0643..5009f1ca5400 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); + super(project, repositories, currentBranch, indicator); myBranchToMerge = branchToMerge; myCurrentBranch = currentBranch; + myCurrentRepository = currentRepository; myCurrentRevisionsBeforeMerge = currentRevisionsBeforeMerge; myChangeListManager = ChangeListManager.getInstance(myProject); } @@ -126,13 +130,28 @@ class GitMergeOperation extends GitBranchOperation { notifySuccess(); } else { - notifySuccess("Already up-to-date."); + notifySuccess("Already up-to-date"); } } 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); diff --git a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java index 0d8a187664e4..f03b940f4bf8 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 diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index b05c7370d1f8..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) { @@ -80,11 +84,7 @@ 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/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 9ce2fe859bde..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; @@ -77,11 +77,21 @@ 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()); } @Override @@ -89,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); @@ -122,7 +140,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 +181,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 +223,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(); 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) { 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(); } 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")); } 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); 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(); }