diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index 2c34d769898c..0867214460f6 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -770,7 +770,7 @@ def layoutJps(String home, String targetDir, String buildNumber, Closure additio include(name: "jdom.jar") include(name: "jna.jar") include(name: "trove4j.jar") - include(name: "asm4-all.jar") + include(name: "asm-all.jar") include(name: "nanoxml-*.jar") include(name: "protobuf-*.jar") include(name: "cli-parser-*.jar") diff --git a/build/scripts/utils.gant b/build/scripts/utils.gant index ed09424fa0c4..44fbe4f56935 100644 --- a/build/scripts/utils.gant +++ b/build/scripts/utils.gant @@ -226,6 +226,7 @@ binding.setVariable("wireBuildDate", { String buildNumber, String appInfoFile -> binding.setVariable("commonJvmArgs", { return [ "-ea", + "-server", "-Didea.home.path=$home", "-Xbootclasspath/p:${projectBuilder.moduleOutput(findModule("boot"))}", "-XX:+HeapDumpOnOutOfMemoryError", diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java index 8904331bfaba..48efa9ac87dc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java @@ -80,7 +80,8 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler { //noinspection unchecked final List targets = new OrderedSet(); - final Range lines = new Range(doc.getLineNumber(element.getTextOffset()), doc.getLineNumber(element.getTextOffset() + element.getTextLength())); + TextRange textRange = element.getTextRange(); + final Range lines = new Range(doc.getLineNumber(textRange.getStartOffset()), doc.getLineNumber(textRange.getEndOffset())); final PsiElementVisitor methodCollector = new JavaRecursiveElementVisitor() { final Stack myContextStack = new Stack(); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java index 4104000a0b27..e37576bae8f4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java @@ -20,7 +20,6 @@ import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; import com.intellij.debugger.impl.EditorTextProvider; import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor; -import com.intellij.debugger.ui.tree.render.DescriptorLabelListener; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; @@ -60,6 +59,11 @@ public class JavaDebuggerEvaluator extends XDebuggerEvaluator { @NotNull final XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition) { myDebugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(myDebugProcess.getDebuggerContext()) { + @Override + public Priority getPriority() { + return Priority.NORMAL; + } + @Override public void threadAction() { WatchItemDescriptor descriptor = new WatchItemDescriptor(myDebugProcess.getProject(), TextWithImportsImpl.fromXExpression( diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/NodeRendererSettings.java b/java/debugger/impl/src/com/intellij/debugger/settings/NodeRendererSettings.java index a43ca7e9eae3..e1d80414f0e6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/NodeRendererSettings.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/NodeRendererSettings.java @@ -33,6 +33,7 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.CommonClassNames; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiElementFactory; @@ -546,7 +547,12 @@ public class NodeRendererSettings implements PersistentStateComponent { } static String constructLabelText(final String keylabel, final String valueLabel) { - return keylabel + " -> " + valueLabel; + StringBuilder sb = new StringBuilder(); + sb.append('\"').append(keylabel).append("\" -> "); + if (!StringUtil.isEmpty(valueLabel)) { + sb.append('\"').append(valueLabel).append('\"'); + } + return sb.toString(); } private static String getDescriptorLabel(final ValueDescriptorImpl keyDescriptor) { diff --git a/java/idea-ui/src/com/intellij/ide/actions/ShowStructureSettingsAction.java b/java/idea-ui/src/com/intellij/ide/actions/ShowStructureSettingsAction.java index 9b803f5d8d6e..36cd7430b827 100644 --- a/java/idea-ui/src/com/intellij/ide/actions/ShowStructureSettingsAction.java +++ b/java/idea-ui/src/com/intellij/ide/actions/ShowStructureSettingsAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -18,13 +18,19 @@ package com.intellij.ide.actions; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.options.ShowSettingsUtil; +import com.intellij.openapi.options.ex.SingleConfigurableEditor; import com.intellij.openapi.options.newEditor.OptionsEditorDialog; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable; +import com.intellij.openapi.util.registry.Registry; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; public class ShowStructureSettingsAction extends AnAction implements DumbAware { public void actionPerformed(AnActionEvent e) { @@ -33,6 +39,23 @@ public class ShowStructureSettingsAction extends AnAction implements DumbAware { project = ProjectManager.getInstance().getDefaultProject(); } - ShowSettingsUtil.getInstance().editConfigurable(project, OptionsEditorDialog.DIMENSION_KEY, ProjectStructureConfigurable.getInstance(project)); + if (Registry.is("ide.new.project.settings")) { + new SingleConfigurableEditor(project, ProjectStructureConfigurable.getInstance(project), OptionsEditorDialog.DIMENSION_KEY) { + @Nullable + @Override + protected Border createContentPaneBorder() { + return new EmptyBorder(0,0,0,0); + } + + @Nullable + @Override + protected JComponent createSouthPanel() { + return null; + } + }.show(); + } else { + ShowSettingsUtil + .getInstance().editConfigurable(project, OptionsEditorDialog.DIMENSION_KEY, ProjectStructureConfigurable.getInstance(project)); + } } } \ No newline at end of file diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/NewProjectWizard.java b/java/idea-ui/src/com/intellij/ide/projectWizard/NewProjectWizard.java index e7e172c0c117..fbe8636ee360 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/NewProjectWizard.java +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/NewProjectWizard.java @@ -21,6 +21,7 @@ import com.intellij.ide.util.newProjectWizard.StepSequence; import com.intellij.ide.util.projectWizard.ModuleWizardStep; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ui.configuration.ModulesProvider; +import com.intellij.openapi.util.Disposer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,6 +47,7 @@ public class NewProjectWizard extends AbstractProjectWizard { protected void init(@NotNull ModulesProvider modulesProvider) { myWizardContext.setNewWizard(true); ProjectTypeStep projectTypeStep = new ProjectTypeStep(myWizardContext, this, modulesProvider); + Disposer.register(getDisposable(), projectTypeStep); mySequence.addCommonStep(projectTypeStep); ChooseTemplateStep chooseTemplateStep = new ChooseTemplateStep(myWizardContext, projectTypeStep); mySequence.addCommonStep(chooseTemplateStep); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java index 14c5fba1ded9..10df2939f6e7 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -37,6 +37,7 @@ import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.impl.libraries.LibraryTableBase; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.navigation.History; import com.intellij.ui.navigation.Place; import com.intellij.util.EventDispatcher; @@ -238,9 +239,10 @@ public abstract class ModuleEditor implements Place.Navigator, Disposable { createEditors(getModule()); - JPanel northPanel = new JPanel(new GridBagLayout()); - - myGenericSettingsPanel.add(northPanel, BorderLayout.NORTH); + if (!Registry.is("ide.new.project.settings")) { + JPanel northPanel = new JPanel(new GridBagLayout()); + myGenericSettingsPanel.add(northPanel, BorderLayout.NORTH); + } final JComponent component = createCenterPanel(); myGenericSettingsPanel.add(component, BorderLayout.CENTER); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java index af34e4092d32..d007c3650642 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -38,11 +38,13 @@ import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureC import com.intellij.openapi.roots.ui.configuration.projectRoot.*; import com.intellij.openapi.ui.DetailsComponent; import com.intellij.openapi.ui.MasterDetailsComponent; -import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; import com.intellij.packaging.artifacts.Artifact; +import com.intellij.ui.Gray; +import com.intellij.ui.JBSplitter; import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.navigation.BackAction; import com.intellij.ui.navigation.ForwardAction; @@ -67,7 +69,7 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se public static final DataKey KEY = DataKey.create("ProjectStructureConfiguration"); protected final UIState myUiState = new UIState(); - private Splitter mySplitter; + private JBSplitter mySplitter; private JComponent myToolbarComponent; @NonNls public static final String CATEGORY = "category"; private JComponent myToFocus; @@ -173,8 +175,16 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se public JComponent createComponent() { myComponent = new MyPanel(); - mySplitter = new Splitter(false, .15f); + mySplitter = new JBSplitter(false, .15f); + mySplitter.setSplitterProportionKey("ProjectStructure.TopLevelElements"); mySplitter.setHonorComponentsMinimumSize(true); + if (Registry.is("ide.new.project.settings")) { + mySplitter.setDividerWidth(1); + mySplitter.setShowDividerIcon(false); + mySplitter.getDivider().setBackground(Gray._153.withAlpha(128)); + mySplitter.setShowDividerControls(false); + mySplitter.setOrientation(mySplitter.getOrientation()); + } initSidePanel(); @@ -192,7 +202,11 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarGroup, true); toolbar.setTargetComponent(myComponent); myToolbarComponent = toolbar.getComponent(); - left.add(myToolbarComponent, BorderLayout.NORTH); + if (Registry.is("ide.new.project.settings")) { + left.setBackground(new Color(0xD2D6DD)); + } else { + left.add(myToolbarComponent, BorderLayout.NORTH); + } left.add(mySidePanel, BorderLayout.CENTER); mySplitter.setFirstComponent(left); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/SidePanel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/SidePanel.java index 193ce66a8f36..a2d1e4e0489d 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/SidePanel.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/SidePanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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,14 +17,17 @@ package com.intellij.openapi.roots.ui.configuration; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.ui.popup.ListItemDescriptor; +import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBList; import com.intellij.ui.navigation.History; import com.intellij.ui.navigation.Place; import com.intellij.ui.popup.list.GroupedItemsListRenderer; +import com.intellij.util.ui.EmptyIcon; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; @@ -52,7 +55,10 @@ public class SidePanel extends JPanel { myModel = new DefaultListModel(); myList = new JBList(myModel); - + if (Registry.is("ide.new.project.settings")) { + myList.setBackground(new Color(0xD2D6DD)); + myList.setBorder(new EmptyBorder(5, 0, 0, 0)); + } final ListItemDescriptor descriptor = new ListItemDescriptor() { @Override public String getTextFor(final Object value) { @@ -66,7 +72,7 @@ public class SidePanel extends JPanel { @Override public Icon getIconFor(final Object value) { - return null; + return Registry.is("ide.new.project.settings") ? EmptyIcon.create(16, 20) : null; //return myPlace2Presentation.get(value).getIcon(); } @@ -78,14 +84,22 @@ public class SidePanel extends JPanel { @Override public String getCaptionAboveOf(final Object value) { - return myIndex2Separator.get(myPlaces.indexOf(value)); + String text = myIndex2Separator.get(myPlaces.indexOf(value)); + return text != null && Registry.is("ide.new.project.settings") ? text.toUpperCase() : text; } }; - myList.setCellRenderer(new GroupedItemsListRenderer(descriptor)); + myList.setCellRenderer(new GroupedItemsListRenderer(descriptor) { + { + mySeparatorComponent.setCaptionCentered(false); + } + @Override + protected Color getBackground() { + return Registry.is("ide.new.project.settings") ? new Color(0xD2D6DD) : super.getBackground(); + } + }); - - add(ScrollPaneFactory.createScrollPane(myList), BorderLayout.CENTER); + add(ScrollPaneFactory.createScrollPane(myList, Registry.is("ide.new.project.settings")), BorderLayout.CENTER); myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); myList.addListSelectionListener(new ListSelectionListener() { diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java index bf4f67c0c824..b88f92474fcf 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -38,6 +38,8 @@ import com.intellij.openapi.ui.NamedConfigurable; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.registry.Registry; import com.intellij.packaging.artifacts.Artifact; import com.intellij.ui.TreeSpeedSearch; import com.intellij.ui.awt.RelativePoint; @@ -300,7 +302,9 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i result.addAll(copyActions); result.add(Separator.getInstance()); - result.add(new MyFindUsagesAction(myTree)); + if (fromPopup || !(SystemInfo.isMac && Registry.is("ide.new.project.settings"))) { + result.add(new MyFindUsagesAction(myTree)); + } return result; diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java index 44d7820feb88..1c313c1077ee 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -53,7 +53,9 @@ import com.intellij.openapi.ui.*; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.NullableComputable; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -149,7 +151,9 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple final ArrayList result = super.createActions(fromPopup); result.add(Separator.getInstance()); result.add(new MyGroupAction()); - addCollapseExpandActions(result); + if (fromPopup || !(SystemInfo.isMac && Registry.is("ide.new.project.settings"))) { + addCollapseExpandActions(result); + } return result; } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index 47cf86819c0f..7310227a80f0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -53,6 +53,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.Consumer; +import com.intellij.util.ObjectUtils; import com.intellij.util.PairConsumer; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; @@ -340,6 +341,7 @@ public class JavaCompletionContributor extends CompletionContributor { } final Object[] variants = reference.getVariants(); + //noinspection ConstantConditions if (variants == null) { LOG.error("Reference=" + reference); } @@ -365,6 +367,7 @@ public class JavaCompletionContributor extends CompletionContributor { } else { + //noinspection deprecation LookupElement element = LookupItemUtil.objectToLookupItem(completion); usedWords.add(element.getLookupString()); result.addElement(element); @@ -434,7 +437,7 @@ public class JavaCompletionContributor extends CompletionContributor { private static void completeAnnotationAttributeName(CompletionResultSet result, PsiElement insertedElement, CompletionParameters parameters) { PsiNameValuePair pair = PsiTreeUtil.getParentOfType(insertedElement, PsiNameValuePair.class); - PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)pair.getParent(); + PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)ObjectUtils.assertNotNull(pair).getParent(); PsiAnnotation anno = (PsiAnnotation)parameterList.getParent(); boolean showClasses = psiElement().afterLeaf("(").accepts(insertedElement); PsiClass annoClass = null; @@ -507,7 +510,7 @@ public class JavaCompletionContributor extends CompletionContributor { if (psiElement().withParent(psiReferenceExpression().withFirstChild(psiReferenceExpression().referencing(psiClass()))).accepts(position)) { if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.GLOBAL_MEMBER_NAME)) { final String shortcut = getActionShortcut(IdeActions.ACTION_CODE_COMPLETION); - if (shortcut != null) { + if (StringUtil.isNotEmpty(shortcut)) { return "Pressing " + shortcut + " twice without a class qualifier would show all accessible static methods"; } } @@ -517,7 +520,7 @@ public class JavaCompletionContributor extends CompletionContributor { if (parameters.getCompletionType() != CompletionType.SMART && shouldSuggestSmartCompletion(parameters.getPosition())) { if (CompletionUtil.shouldShowFeature(parameters, CodeCompletionFeatures.EDITING_COMPLETION_SMARTTYPE_GENERAL)) { final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION); - if (shortcut != null) { + if (StringUtil.isNotEmpty(shortcut)) { return CompletionBundle.message("completion.smart.hint", shortcut); } } @@ -528,7 +531,7 @@ public class JavaCompletionContributor extends CompletionContributor { if (psiTypes.length > 0) { if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_TOAR)) { final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION); - if (shortcut != null) { + if (StringUtil.isNotEmpty(shortcut)) { for (final PsiType psiType : psiTypes) { final PsiType type = PsiUtil.extractIterableTypeParameter(psiType, false); if (type != null) { @@ -539,7 +542,7 @@ public class JavaCompletionContributor extends CompletionContributor { } if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_ASLIST)) { final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION); - if (shortcut != null) { + if (StringUtil.isNotEmpty(shortcut)) { for (final PsiType psiType : psiTypes) { if (psiType instanceof PsiArrayType) { final PsiType componentType = ((PsiArrayType)psiType).getComponentType(); @@ -553,7 +556,7 @@ public class JavaCompletionContributor extends CompletionContributor { if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_CHAIN)) { final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION); - if (shortcut != null) { + if (StringUtil.isNotEmpty(shortcut)) { return CompletionBundle.message("completion.smart.chain.hint", shortcut); } } @@ -639,9 +642,12 @@ public class JavaCompletionContributor extends CompletionContributor { final PsiFile file = context.getFile(); if (file instanceof PsiJavaFile) { - JavaCompletionUtil.initOffsets(file, context.getOffsetMap()); + if (context.getInvocationCount() > 0) { + autoImport(file, context.getStartOffset() - 1, context.getEditor()); + PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getEditor().getDocument()); + } - autoImport(file, context.getStartOffset() - 1, context.getEditor()); + JavaCompletionUtil.initOffsets(file, context.getOffsetMap()); if (context.getCompletionType() == CompletionType.BASIC) { if (semicolonNeeded(context.getEditor(), file, context.getStartOffset())) { diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java index 49befb363437..a6d734b92d8e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java @@ -103,10 +103,11 @@ public class PsiTypeLookupItem extends LookupItem { myImportFixer.handleInsert(context, this); PsiElement position = context.getFile().findElementAt(context.getStartOffset()); - assert position != null; - int genericsStart = context.getTailOffset(); - context.getDocument().insertString(genericsStart, JavaCompletionUtil.escapeXmlIfNeeded(context, calcGenerics(position, context))); - JavaCompletionUtil.shortenReference(context.getFile(), genericsStart - 1); + if (position != null) { + int genericsStart = context.getTailOffset(); + context.getDocument().insertString(genericsStart, JavaCompletionUtil.escapeXmlIfNeeded(context, calcGenerics(position, context))); + JavaCompletionUtil.shortenReference(context.getFile(), genericsStart - 1); + } int tail = context.getTailOffset(); String braces = StringUtil.repeat("[]", getBracketsCount()); diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementParseInfo.java b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementParseInfo.java index 9664bc223f8f..f042bb316c40 100644 --- a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementParseInfo.java +++ b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementParseInfo.java @@ -229,4 +229,9 @@ public class JavaArrangementParseInfo { public List getFieldDependencyRoots() { return new FieldDependenciesManager(myFieldDependencies, myFields).getRoots(); } + + @NotNull + public Collection getFields() { + return myFields.values(); + } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java index 6ec40043433e..9e23eadc3073 100644 --- a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java +++ b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java @@ -27,6 +27,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.arrangement.engine.ArrangementEngine; import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule; import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher; import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher; @@ -34,8 +35,6 @@ import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import com.intellij.psi.codeStyle.arrangement.std.ArrangementColorsAware; -import com.intellij.psi.codeStyle.arrangement.std.ArrangementStandardSettingsAware; import com.intellij.psi.codeStyle.arrangement.std.*; import com.intellij.util.containers.ContainerUtilRt; import org.jetbrains.annotations.NotNull; @@ -46,10 +45,10 @@ import java.util.*; import java.util.List; import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.EntryType.*; +import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.General.*; import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.Grouping.*; import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.Modifier.*; import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.Order.*; -import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.General.*; /** * @author Denis Zhdanov @@ -250,19 +249,34 @@ public class JavaRearranger implements Rearranger, setupOverriddenMethods(parseInfo); } } - setupFieldInitializationDependencies(parseInfo.getFieldDependencyRoots()); + List fieldDependencyRoots = parseInfo.getFieldDependencyRoots(); + if (!fieldDependencyRoots.isEmpty()) { + setupFieldInitializationDependencies(fieldDependencyRoots, settings, parseInfo); + } return parseInfo.getEntries(); } - public void setupFieldInitializationDependencies(@NotNull List fieldDependencyRoots) { + public void setupFieldInitializationDependencies(@NotNull List fieldDependencyRoots, + @NotNull ArrangementSettings settings, + @NotNull JavaArrangementParseInfo parseInfo) + { + Collection fields = parseInfo.getFields(); + List arrangedFields = ArrangementEngine.arrange(fields, settings.getSections(), settings.getRulesSortedByPriority(), null); + for (ArrangementEntryDependencyInfo root : fieldDependencyRoots) { JavaElementArrangementEntry anchorField = root.getAnchorEntry(); + final int anchorEntryIndex = arrangedFields.indexOf(anchorField); + for (ArrangementEntryDependencyInfo fieldInInitializerInfo : root.getDependentEntriesInfos()) { - anchorField.addDependency(fieldInInitializerInfo.getAnchorEntry()); + JavaElementArrangementEntry fieldInInitializer = fieldInInitializerInfo.getAnchorEntry(); + if (arrangedFields.indexOf(fieldInInitializer) > anchorEntryIndex) { + anchorField.addDependency(fieldInInitializer); + } } } } + @Override public int getBlankLines(@NotNull CodeStyleSettings settings, @Nullable JavaElementArrangementEntry parent, diff --git a/java/java-impl/src/com/intellij/spellchecker/LiteralExpressionTokenizer.java b/java/java-impl/src/com/intellij/spellchecker/LiteralExpressionTokenizer.java index 77246ec92763..2eade6903cd2 100644 --- a/java/java-impl/src/com/intellij/spellchecker/LiteralExpressionTokenizer.java +++ b/java/java-impl/src/com/intellij/spellchecker/LiteralExpressionTokenizer.java @@ -17,8 +17,10 @@ package com.intellij.spellchecker; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.PsiLiteralExpression; import com.intellij.psi.PsiModifierListOwner; +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.spellchecker.inspections.PlainTextSplitter; @@ -30,8 +32,6 @@ import org.jetbrains.annotations.NotNull; import java.util.Collections; /** - * Created by IntelliJ IDEA. - * * @author shkate@jetbrains.com */ public class LiteralExpressionTokenizer extends Tokenizer { @@ -42,6 +42,8 @@ public class LiteralExpressionTokenizer extends Tokenizer return; // not a string literal } + if (InjectedLanguageUtil.hasInjections((PsiLanguageInjectionHost)element)) return; + final PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class); if (listOwner != null && AnnotationUtil.isAnnotated(listOwner, Collections.singleton(AnnotationUtil.NON_NLS), false, false)) { return; diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java index e165ba718467..b88cc90e4b74 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java @@ -48,6 +48,7 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.Function; import com.intellij.util.ObjectUtils; import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -703,7 +704,7 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem else if (element instanceof PsiComment) { return settings.isCollapseEndOfLineComments(); } - else if (element instanceof PsiLiteralExpression + else if (isLiteralExpression(element) && element.getParent() instanceof PsiExpressionList && (element.getParent().getParent() instanceof PsiCallExpression || element.getParent().getParent() instanceof PsiAnonymousClass)) { @@ -775,7 +776,7 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem for (int i = 0; i < callArguments.length; i++) { PsiExpression callArgument = callArguments[i]; - if (callArgument instanceof PsiLiteralExpression) { + if (isLiteralExpression(callArgument)) { if (!isResolved) { PsiMethod method = expression.resolveMethod(); isResolved = true; @@ -799,6 +800,20 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem } } + @Contract("null -> false") + private static boolean isLiteralExpression(@Nullable PsiElement callArgument) { + if (callArgument instanceof PsiLiteralExpression) + return true; + + if (callArgument instanceof PsiPrefixExpression) { + PsiPrefixExpression expr = (PsiPrefixExpression)callArgument; + IElementType tokenType = expr.getOperationTokenType(); + return JavaTokenType.MINUS.equals(tokenType) && expr.getOperand() instanceof PsiLiteralExpression; + } + + return false; + } + private boolean addClosureFolding(final PsiClass aClass, final Document document, final List foldElements, @NotNull Set processedComments, final boolean quick) { if (!JavaCodeFoldingSettings.getInstance().isCollapseLambdas()) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA126109.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA126109.java new file mode 100644 index 000000000000..0fd7c685535a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA126109.java @@ -0,0 +1,26 @@ +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class Test { + void foo(final Stream stream) { + final Map> mapByFirstLetter = stream.collect(Collectors.groupingBy(p -> "" + p.name.charAt(0))); + + final String vV = mapByFirstLetter.values().stream().map(lp -> lp.stream().map(p -> p.name) + .collect(Collectors.joining("/","<",">"))) .collect(Collectors.joining(" : ")); + + final String vV2 = mapByFirstLetter.values().stream() + .map(lp -> lp.stream().map(Person::getName).collect(Collectors.joining("/","<",">"))) + .collect(Collectors.joining(" : ")); + System.out.println("mapByFirstLetter2 : "+ vV2); + } + + public static class Person { + private String name; + public Person(String name) { + this.name = name; + } + public String getName() {return name;} + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java index f4ff8e530a91..df8c50339f35 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java @@ -234,6 +234,10 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testIDEA126109() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy index c68fb5a7cc43..78629192782f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy @@ -698,6 +698,29 @@ public class CharSymbol { assert regions[2].placeholderText == "fast: false" } + public void "test inline negative numbers (IDEA-126753)"() { + def text = """ +public class CharSymbol { + + public void main() { + Object obj = new Object(); + count(-1, obj); + } + + public void count(int test, Object obj) { + Object tmp = obj; + boolean isFast = false; + } +} +""" + configure text + def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } + assert regions.size() == 3 + + checkRangeOffsetByPositionInText(regions[1], text, "-1") + assert regions[1].placeholderText == "test: -1" + } + public void "test inline constructor literal arguments names"() { def text = """ public class Test { diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy index 302f1b331530..d11a34f6e376 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy @@ -328,9 +328,61 @@ class Second extends First { ) } + void "test IDEA-123875"() { + doTest( + initial: '''\ +public class RearrangeFail { + public static final byte[] ENTITIES_END = "".getBytes(); + private final Element entitiesEndElement = new Element(ENTITIES_END); - - + public static final byte[] ENTITIES_START = "".getBytes(); + private final Element entitiesStartElement = new Element(ENTITIES_START); } +''', + expected: '''\ +public class RearrangeFail { + + public static final byte[] ENTITIES_END = "".getBytes(); + public static final byte[] ENTITIES_START = "".getBytes(); + private final Element entitiesEndElement = new Element(ENTITIES_END); + private final Element entitiesStartElement = new Element(ENTITIES_START); + +} +''', + rules: [ + rule(PUBLIC, STATIC, FINAL), + rule(PRIVATE), + ] + ) + } + + void "test IDEA-125099"() { + doTest( + initial: '''\ +public class test { + + private int a = 2; + + public static final String TEST = "1"; + public static final String SHOULD_BE_IN_BETWEEN = "2"; + public static final String USERS_ROLE_ID_COLUMN = TEST; +} +''', + expected: '''\ +public class test { + + public static final String TEST = "1"; + public static final String SHOULD_BE_IN_BETWEEN = "2"; + public static final String USERS_ROLE_ID_COLUMN = TEST; + private int a = 2; +} +''', + rules: [ + rule(PUBLIC, STATIC, FINAL), + rule(PRIVATE) + ] + ) + } +} diff --git a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java index d26cef614566..5232dc439b90 100644 --- a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java +++ b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java @@ -25,7 +25,8 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class SimpleModificationTracker implements ModificationTracker { static { // field made public to workaround bug in JDK7 when AtomicIntegerFieldUpdater can't be created for private field, even from within its own class - assert Patches.HACK_USED_WHICH_IS_FIXED_IN_JDK8; + // fixed in JDK8 + assert Patches.JDK_BUG_ID_7103570; } public volatile int myCounter; diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileContentChangeEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileContentChangeEvent.java index cc1614c16ecd..3c9dbb6c6bdd 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileContentChangeEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileContentChangeEvent.java @@ -60,6 +60,7 @@ public class VFileContentChangeEvent extends VFileEvent { return "VfsEvent[update: " + myFile.getUrl() + "]"; } + @NotNull @Override public String getPath() { return myFile.getPath(); diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCopyEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCopyEvent.java index 50ac2c99cc51..5ea519067db8 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCopyEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCopyEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -29,7 +29,7 @@ public class VFileCopyEvent extends VFileEvent { private final VirtualFile myNewParent; private final String myNewChildName; - public VFileCopyEvent(final Object requestor, final VirtualFile file, final VirtualFile newParent, final String newChildName) { + public VFileCopyEvent(final Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile newParent, @NotNull String newChildName) { super(requestor, false); myFile = file; myNewParent = newParent; @@ -37,23 +37,28 @@ public class VFileCopyEvent extends VFileEvent { } @Override + @NotNull public VirtualFile getFile() { return myFile; } + @NotNull public VirtualFile getNewParent() { return myNewParent; } + @NotNull public String getNewChildName() { return myNewChildName; } + @Override @NonNls public String toString() { return "VfsEvent[copy " + myFile +" to " + myNewParent + " as " + myNewChildName +"]"; } + @NotNull @Override public String getPath() { return myNewParent.getPath() + "/" + myNewChildName; @@ -70,6 +75,7 @@ public class VFileCopyEvent extends VFileEvent { return myFile.isValid() && myNewParent.findChild(myNewChildName) == null; } + @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -83,9 +89,9 @@ public class VFileCopyEvent extends VFileEvent { return true; } + @Override public int hashCode() { - int result; - result = myFile.hashCode(); + int result = myFile.hashCode(); result = 31 * result + myNewParent.hashCode(); result = 31 * result + myNewChildName.hashCode(); return result; diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java index e3a9b9040e32..c730b9b9fe6b 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -24,9 +24,9 @@ import org.jetbrains.annotations.NotNull; * @author max */ public class VFileCreateEvent extends VFileEvent { - private final VirtualFile myParent; + @NotNull private final VirtualFile myParent; private final boolean myDirectory; - private final String myChildName; + @NotNull private final String myChildName; private final boolean myReCreation; private VirtualFile myCreatedFile; @@ -51,6 +51,7 @@ public class VFileCreateEvent extends VFileEvent { myReCreation = isReCreation; } + @NotNull public String getChildName() { return myChildName; } @@ -59,6 +60,7 @@ public class VFileCreateEvent extends VFileEvent { return myDirectory; } + @NotNull public VirtualFile getParent() { return myParent; } @@ -74,6 +76,7 @@ public class VFileCreateEvent extends VFileEvent { myChildName + " in " + myParent.getUrl() + "]"; } + @NotNull @Override public String getPath() { return myParent.getPath() + "/" + myChildName; diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileDeleteEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileDeleteEvent.java index 10b03e082575..f1ed38a12e9b 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileDeleteEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileDeleteEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -40,11 +40,13 @@ public class VFileDeleteEvent extends VFileEvent { return myFile; } + @Override @NonNls public String toString() { return "VfsEvent[deleted: " + myFile.getUrl() + "]"; } + @NotNull @Override public String getPath() { return myFile.getPath(); @@ -61,6 +63,7 @@ public class VFileDeleteEvent extends VFileEvent { return myFile.isValid(); } + @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -70,6 +73,7 @@ public class VFileDeleteEvent extends VFileEvent { return myFile.equals(event.myFile); } + @Override public int hashCode() { return myFile.hashCode(); } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java index 3e931a588d7d..22eb1f3a3fb8 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -41,6 +41,7 @@ public abstract class VFileEvent { return myRequestor; } + @NotNull public abstract String getPath(); /** @@ -58,6 +59,8 @@ public abstract class VFileEvent { public abstract boolean isValid(); + @Override public abstract int hashCode(); + @Override public abstract boolean equals(Object o); } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileMoveEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileMoveEvent.java index 42ff9d42d8a0..dd145f88bc69 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileMoveEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileMoveEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -30,7 +30,7 @@ public class VFileMoveEvent extends VFileEvent { private final VirtualFile myOldParent; private final VirtualFile myNewParent; - public VFileMoveEvent(final Object requestor, @NotNull final VirtualFile file, final VirtualFile newParent) { + public VFileMoveEvent(final Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile newParent) { super(requestor, false); myFile = file; myNewParent = newParent; @@ -43,6 +43,7 @@ public class VFileMoveEvent extends VFileEvent { return myFile; } + @NotNull public VirtualFile getNewParent() { return myNewParent; } @@ -51,11 +52,13 @@ public class VFileMoveEvent extends VFileEvent { return myOldParent; } + @Override @NonNls public String toString() { return "VfsEvent[move " + myFile.getName() +" from " + myOldParent + " to " + myNewParent + "]"; } + @NotNull @Override public String getPath() { return myFile.getPath(); @@ -72,6 +75,7 @@ public class VFileMoveEvent extends VFileEvent { return myFile.isValid() && Comparing.equal(myFile.getParent(), myOldParent) && myOldParent.isValid(); } + @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -85,9 +89,9 @@ public class VFileMoveEvent extends VFileEvent { return true; } + @Override public int hashCode() { - int result; - result = myFile.hashCode(); + int result = myFile.hashCode(); result = 31 * result + myOldParent.hashCode(); result = 31 * result + myNewParent.hashCode(); return result; diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java index 360ce833a28b..90d10a76d3a8 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFilePropertyChangeEvent.java @@ -91,6 +91,7 @@ public class VFilePropertyChangeEvent extends VFileEvent { return myPropertyName; } + @NotNull @Override public String getPath() { return myFile.getPath(); @@ -131,6 +132,7 @@ public class VFilePropertyChangeEvent extends VFileEvent { return result; } + @Override @NotNull @NonNls public String toString() { diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiFileFactoryImpl.java b/platform/core-impl/src/com/intellij/psi/impl/PsiFileFactoryImpl.java index a24a942bcfea..1933626770dc 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiFileFactoryImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiFileFactoryImpl.java @@ -114,6 +114,9 @@ public class PsiFileFactoryImpl extends PsiFileFactory { final PsiFile psiFile = viewProvider.getPsi(language); if (psiFile != null) { if (markAsCopy) { + if (psiFile.getNode() == null) { + throw new AssertionError("No node for file " + psiFile + "; language=" + language); + } markGenerated(psiFile); } return psiFile; diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionInitializationContext.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionInitializationContext.java index 7cf2c7d7db11..624d60fd8688 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionInitializationContext.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionInitializationContext.java @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilBase; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -40,13 +41,15 @@ public class CompletionInitializationContext { private final Editor myEditor; private final PsiFile myFile; private final CompletionType myCompletionType; + private final int myInvocationCount; private final OffsetMap myOffsetMap; private String myDummyIdentifier = DUMMY_IDENTIFIER; - public CompletionInitializationContext(final Editor editor, final PsiFile file, final CompletionType completionType) { + public CompletionInitializationContext(final Editor editor, final PsiFile file, final CompletionType completionType, int invocationCount) { myEditor = editor; myFile = file; myCompletionType = completionType; + myInvocationCount = invocationCount; myOffsetMap = new OffsetMap(editor.getDocument()); myOffsetMap.addOffset(START_OFFSET, calcStartOffset(editor)); @@ -79,7 +82,7 @@ public class CompletionInitializationContext { @NotNull public Language getPositionLanguage() { - return PsiUtilBase.getLanguageInEditor(getEditor(), getProject()); + return ObjectUtils.assertNotNull(PsiUtilBase.getLanguageInEditor(getEditor(), getProject())); } public String getDummyIdentifier() { @@ -127,9 +130,12 @@ public class CompletionInitializationContext { return getIdentifierEndOffset(); } + public int getInvocationCount() { + return myInvocationCount; + } + /** * Mark the offset up to which the text will be deleted if a completion variant is selected using Replace character (Tab) - * @param idEnd */ public void setReplacementOffset(int idEnd) { myOffsetMap.addOffset(IDENTIFIER_END_OFFSET, idEnd); diff --git a/platform/lang-api/src/com/intellij/execution/configuration/EnvironmentVariablesComponent.java b/platform/lang-api/src/com/intellij/execution/configuration/EnvironmentVariablesComponent.java index f022608750f4..21c7deabff9f 100644 --- a/platform/lang-api/src/com/intellij/execution/configuration/EnvironmentVariablesComponent.java +++ b/platform/lang-api/src/com/intellij/execution/configuration/EnvironmentVariablesComponent.java @@ -43,30 +43,30 @@ public class EnvironmentVariablesComponent extends LabeledComponent envs) { - myEnvsTextField.setEnvs(envs); + myEnvVars.setEnvs(envs); } @NotNull public Map getEnvs() { - return myEnvsTextField.getEnvs(); + return myEnvVars.getEnvs(); } public boolean isPassParentEnvs() { - return myEnvsTextField.isPassParentEnvs(); + return myEnvVars.isPassParentEnvs(); } public void setPassParentEnvs(final boolean passParentEnvs) { - myEnvsTextField.setPassParentEnvs(passParentEnvs); + myEnvVars.setPassParentEnvs(passParentEnvs); } public static void readExternal(Element element, Map envs) { @@ -93,12 +93,10 @@ public class EnvironmentVariablesComponent extends LabeledComponent envs, final String val) { if (val != null) { final String[] envVars = val.split(";"); - if (envVars != null) { - for (String envVar : envVars) { - final int idx = envVar.indexOf('='); - if (idx > -1) { - envs.put(envVar.substring(0, idx), idx < envVar.length() - 1 ? envVar.substring(idx + 1) : ""); - } + for (String envVar : envVars) { + final int idx = envVar.indexOf('='); + if (idx > -1) { + envs.put(envVar.substring(0, idx), idx < envVar.length() - 1 ? envVar.substring(idx + 1) : ""); } } } @@ -134,11 +132,11 @@ public class EnvironmentVariablesComponent extends LabeledComponent myEnvs = new THashMap(); private boolean myPassParentEnvs; private final List myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - public EnvironmentVariablesTextField() { - myEnvsTextField = new TextFieldWithBrowseButton(); - myEnvsTextField.setEditable(false); - myEnvsTextField.addActionListener(new ActionListener() { + public EnvironmentVariablesTextFieldWithBrowseButton() { + super(); + setEditable(false); + addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { new MyEnvironmentVariablesDialog().show(); @@ -52,11 +51,6 @@ public class EnvironmentVariablesTextField { }); } - @NotNull - public TextFieldWithBrowseButton getComponent() { - return myEnvsTextField; - } - @NotNull public Map getEnvs() { return myEnvs; @@ -66,7 +60,7 @@ public class EnvironmentVariablesTextField { myEnvs.clear(); myEnvs.putAll(envs); String envsStr = stringifyEnvs(myEnvs); - myEnvsTextField.setText(envsStr); + setText(envsStr); } @NotNull @@ -115,7 +109,7 @@ public class EnvironmentVariablesTextField { private final JPanel myWholePanel = new JPanel(new BorderLayout()); protected MyEnvironmentVariablesDialog() { - super(myEnvsTextField, true); + super(EnvironmentVariablesTextFieldWithBrowseButton.this, true); myEnvVariablesTable = new EnvVariablesTable(); List envVariables = ContainerUtil.newArrayList(); for (Map.Entry entry : myEnvs.entrySet()) { diff --git a/platform/lang-api/src/com/intellij/find/FindManager.java b/platform/lang-api/src/com/intellij/find/FindManager.java index 102872655dca..56339d116e14 100644 --- a/platform/lang-api/src/com/intellij/find/FindManager.java +++ b/platform/lang-api/src/com/intellij/find/FindManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -22,6 +22,7 @@ import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; +import com.intellij.psi.search.SearchScope; import com.intellij.util.messages.Topic; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; @@ -217,6 +218,7 @@ public abstract class FindManager { * @param element the element to find the usages for. */ public abstract void findUsages(@NotNull PsiElement element); + public abstract void findUsagesInScope(@NotNull PsiElement element, @NotNull SearchScope searchScope); /** * Shows the Find Usages dialog (if {@code showDialog} is true} and performs the Find Usages operation for the diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 0685c53c2ad7..31a79bab98bb 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -135,19 +135,18 @@ public class CodeCompletionHandlerBase { } */ - int newTime = phase.newCompletionStarted(time, repeated); + final int newTime = phase.newCompletionStarted(time, repeated); if (invokedExplicitly) { time = newTime; } + final int invocationCount = time; if (CompletionServiceImpl.isPhase(CompletionPhase.InsertedSingleItem.class)) { CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); } CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass(), CompletionPhase.CommittingDocuments.class); - if (time > 1) { - if (myCompletionType == CompletionType.BASIC) { - FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.SECOND_BASIC_COMPLETION); - } + if (invocationCount > 1 && myCompletionType == CompletionType.BASIC) { + FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.SECOND_BASIC_COMPLETION); } final CompletionInitializationContext[] initializationContext = {null}; @@ -168,7 +167,7 @@ public class CodeCompletionHandlerBase { psiFile.putUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING, Boolean.TRUE); CompletionAssertions.assertCommitSuccessful(editor, psiFile); - initializationContext[0] = runContributorsBeforeCompletion(editor, psiFile); + initializationContext[0] = runContributorsBeforeCompletion(editor, psiFile, invocationCount); } }; ApplicationManager.getApplication().runWriteAction(runnable); @@ -184,12 +183,12 @@ public class CodeCompletionHandlerBase { CommandProcessor.getInstance().executeCommand(project, initCmd, null, null); } - insertDummyIdentifier(initializationContext[0], hasModifiers, time); + insertDummyIdentifier(initializationContext[0], hasModifiers, invocationCount); } - private CompletionInitializationContext runContributorsBeforeCompletion(Editor editor, PsiFile psiFile) { + private CompletionInitializationContext runContributorsBeforeCompletion(Editor editor, PsiFile psiFile, int invocationCount) { final Ref current = Ref.create(null); - CompletionInitializationContext context = new CompletionInitializationContext(editor, psiFile, myCompletionType) { + CompletionInitializationContext context = new CompletionInitializationContext(editor, psiFile, myCompletionType, invocationCount) { CompletionContributor dummyIdentifierChanger; @Override @@ -759,10 +758,12 @@ public class CodeCompletionHandlerBase { } if (context.getCompletionChar() == Lookup.COMPLETE_STATEMENT_SELECT_CHAR) { final Language language = PsiUtilBase.getLanguageInEditor(editor, project); - final List processors = SmartEnterProcessors.INSTANCE.forKey(language); - if (processors.size() > 0) { - for (SmartEnterProcessor processor : processors) { - processor.process(project, editor, indicator.getParameters().getOriginalFile()); + if (language != null) { + final List processors = SmartEnterProcessors.INSTANCE.forKey(language); + if (processors.size() > 0) { + for (SmartEnterProcessor processor : processors) { + processor.process(project, editor, indicator.getParameters().getOriginalFile()); + } } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java index 8c79df55ee7a..75a4fd410959 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java @@ -21,6 +21,7 @@ import com.intellij.codeHighlighting.Pass; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings; import com.intellij.codeInsight.hint.TooltipController; +import com.intellij.ide.IdeTooltipManager; import com.intellij.ide.PowerSaveMode; import com.intellij.ide.todo.TodoConfiguration; import com.intellij.openapi.Disposable; @@ -599,6 +600,7 @@ public class DaemonListeners implements Disposable { if (editor.offsetToLogicalPosition(offset).column != logical.column) return; // we are in virtual space HighlightInfo info = myDaemonCodeAnalyzer.findHighlightByOffset(editor.getDocument(), offset, false); if (info == null || info.getDescription() == null) return; + if (IdeTooltipManager.getInstance().hasCurrent()) return; DaemonTooltipUtil.showInfoTooltip(info, editor, offset); shown = true; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupOffsets.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupOffsets.java index 28d3aff18665..fe53f1796dbd 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupOffsets.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupOffsets.java @@ -51,10 +51,7 @@ public class LookupOffsets { private void updateLookupStart(int minPrefixLength) { int offset = getPivotOffset(); int start = offset - minPrefixLength - myAdditionalPrefix.length() + myRemovedPrefix; - if (start < 0) { - LOG.error("Invalid start offset: o=" + offset + ", mpl=" + minPrefixLength + ", ap=" + myAdditionalPrefix + ", rp=" + myRemovedPrefix); - return; - } + start = Math.max(Math.min(start, myEditor.getDocument().getTextLength()), 0); if (myLookupStartMarker != null) { if (myLookupStartMarker.isValid() && myLookupStartMarker.getStartOffset() == start && myLookupStartMarker.getEndOffset() == start) { return; diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java index 00c717faf9df..495a13614005 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java @@ -162,16 +162,9 @@ public class ConsoleExecuteAction extends DumbAwareAction { protected void beforeExecution(@NotNull LanguageConsoleImpl console) { } - protected boolean shouldExecute(@NotNull LanguageConsoleImpl console) { + protected void runExecuteAction(@NotNull LanguageConsoleImpl console, @Nullable LanguageConsoleView consoleView) { //noinspection deprecation beforeExecution(console); - return true; - } - - final void runExecuteAction(@NotNull LanguageConsoleImpl console, @Nullable LanguageConsoleView consoleView) { - if (!shouldExecute(console)) { - return; - } String text = console.prepareExecuteAction(myAddToHistory, myPreserveMarkup, true); ((UndoManagerImpl)UndoManager.getInstance(console.getProject())).invalidateActionsFor(DocumentReferenceManager.getInstance().create(console.getCurrentEditor().getDocument())); diff --git a/platform/lang-impl/src/com/intellij/facet/impl/FacetModificationTrackingServiceImpl.java b/platform/lang-impl/src/com/intellij/facet/impl/FacetModificationTrackingServiceImpl.java index b5ae84e3d931..deb5cc516e6c 100644 --- a/platform/lang-impl/src/com/intellij/facet/impl/FacetModificationTrackingServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/facet/impl/FacetModificationTrackingServiceImpl.java @@ -22,6 +22,7 @@ import com.intellij.facet.FacetManagerAdapter; import com.intellij.facet.FacetModificationTrackingService; import com.intellij.openapi.Disposable; import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.ModificationTrackerListener; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SimpleModificationTracker; @@ -35,8 +36,8 @@ import java.util.Map; * @author nik */ public class FacetModificationTrackingServiceImpl extends FacetModificationTrackingService { - private final Map>> myModificationsTrackers = - new THashMap>>(); + private final Map>> myModificationsTrackers = + new THashMap>>(); public FacetModificationTrackingServiceImpl(final Module module) { module.getMessageBus().connect().subscribe(FacetManager.FACETS_TOPIC, new FacetModificationTrackingListener()); @@ -44,14 +45,14 @@ public class FacetModificationTrackingServiceImpl extends FacetModificationTrack @Override @NotNull - public FacetModificationTracker getFacetModificationTracker(@NotNull final Facet facet) { + public ModificationTracker getFacetModificationTracker(@NotNull final Facet facet) { return getFacetInfo(facet).first; } - private Pair> getFacetInfo(final Facet facet) { - Pair> pair = myModificationsTrackers.get(facet); + private Pair> getFacetInfo(final Facet facet) { + Pair> pair = myModificationsTrackers.get(facet); if (pair == null) { - pair = Pair.create(new FacetModificationTracker(), EventDispatcher.create(ModificationTrackerListener.class)); + pair = Pair.create(new SimpleModificationTracker(), EventDispatcher.create(ModificationTrackerListener.class)); myModificationsTrackers.put(facet, pair); } return pair; @@ -59,7 +60,7 @@ public class FacetModificationTrackingServiceImpl extends FacetModificationTrack @Override public void incFacetModificationTracker(@NotNull final Facet facet) { - final Pair> pair = getFacetInfo(facet); + final Pair> pair = getFacetInfo(facet); pair.first.incModificationCount(); pair.second.getMulticaster().modificationCountChanged(facet); } @@ -74,13 +75,10 @@ public class FacetModificationTrackingServiceImpl extends FacetModificationTrack getFacetInfo(facet).second.removeListener(listener); } - private static class FacetModificationTracker extends SimpleModificationTracker { - } - private class FacetModificationTrackingListener extends FacetManagerAdapter { @Override public void facetConfigurationChanged(@NotNull final Facet facet) { - final Pair> pair = myModificationsTrackers.get(facet); + final Pair> pair = myModificationsTrackers.get(facet); if (pair != null) { pair.first.incModificationCount(); pair.second.getMulticaster().modificationCountChanged(facet); diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java index f84fed5a05d9..84776f4bf7ff 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java @@ -37,6 +37,7 @@ import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.PopupChooserBuilder; @@ -50,7 +51,6 @@ import com.intellij.pom.Navigatable; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.ProjectScope; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.search.SearchScope; import com.intellij.ui.*; @@ -84,14 +84,17 @@ import java.awt.event.ActionListener; import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class ShowUsagesAction extends AnAction implements PopupAction { private final boolean showSettingsDialogBefore; private static final int USAGES_PAGE_SIZE = 100; - static final NullUsage MORE_USAGES_SEPARATOR = NullUsage.INSTANCE; + static final Usage MORE_USAGES_SEPARATOR = NullUsage.INSTANCE; private static final UsageNode MORE_USAGES_SEPARATOR_NODE = UsageViewImpl.NULL_NODE; + static final Usage USAGES_OUTSIDE_SCOPE_SEPARATOR = new UsageAdapter(); + private static final UsageNode USAGES_OUTSIDE_SCOPE_NODE = new UsageNode(USAGES_OUTSIDE_SCOPE_SEPARATOR, new UsageViewTreeModelBuilder(new UsageViewPresentation(), UsageTarget.EMPTY_ARRAY)); private static final Comparator USAGE_NODE_COMPARATOR = new Comparator() { @Override @@ -100,8 +103,9 @@ public class ShowUsagesAction extends AnAction implements PopupAction { if (c2 instanceof StringNode) return -1; Usage o1 = c1.getUsage(); Usage o2 = c2.getUsage(); - if (o1 == MORE_USAGES_SEPARATOR) return 1; - if (o2 == MORE_USAGES_SEPARATOR) return -1; + int weight1 = o1 == USAGES_OUTSIDE_SCOPE_SEPARATOR ? 2 : o1 == MORE_USAGES_SEPARATOR ? 1 : 0; + int weight2 = o2 == USAGES_OUTSIDE_SCOPE_SEPARATOR ? 2 : o2 == MORE_USAGES_SEPARATOR ? 1 : 0; + if (weight1 != weight2) return weight1 - weight2; VirtualFile v1 = UsageListCellRenderer.getVirtualFile(o1); VirtualFile v2 = UsageListCellRenderer.getVirtualFile(o2); @@ -193,26 +197,18 @@ public class ShowUsagesAction extends AnAction implements PopupAction { private void startFindUsages(@NotNull PsiElement element, @NotNull RelativePoint popupPosition, Editor editor, int maxUsages) { Project project = element.getProject(); FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager(); - FindUsagesHandler handler = findUsagesManager.getNewFindUsagesHandler(element, false); + FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(element, false); if (handler == null) return; if (showSettingsDialogBefore) { showDialogAndFindUsages(handler, popupPosition, editor, maxUsages); return; } - showElementUsages(handler, editor, popupPosition, maxUsages, getDefaultOptions(handler)); + showElementUsages(editor, popupPosition, handler, maxUsages, handler.getFindUsagesOptions(DataManager.getInstance().getDataContext())); } - @NotNull - private static FindUsagesOptions getDefaultOptions(@NotNull FindUsagesHandler handler) { - FindUsagesOptions options = handler.getFindUsagesOptions(DataManager.getInstance().getDataContext()); - // by default, scope in FindUsagesOptions is copied from the FindSettings, but we need a default one - options.searchScope = GlobalSearchScope.projectScope(handler.getProject()); - return options; - } - - private void showElementUsages(@NotNull final FindUsagesHandler handler, - final Editor editor, + private void showElementUsages(final Editor editor, @NotNull final RelativePoint popupPosition, + @NotNull final FindUsagesHandler handler, final int maxUsages, @NotNull final FindUsagesOptions options) { ApplicationManager.getApplication().assertIsDispatchThread(); @@ -236,30 +232,26 @@ public class ShowUsagesAction extends AnAction implements PopupAction { usageViewSettings.loadState(savedGlobalSettings); } }); + final AtomicInteger outOfScopeUsages = new AtomicInteger(); final List usages = new ArrayList(); final Set visibleNodes = new LinkedHashSet(); final MyTable table = new MyTable(); final AsyncProcessIcon processIcon = new AsyncProcessIcon("xxx"); - boolean hadMoreSeparator = visibleNodes.remove(MORE_USAGES_SEPARATOR_NODE); - if (hadMoreSeparator) { - usages.add(MORE_USAGES_SEPARATOR); - visibleNodes.add(MORE_USAGES_SEPARATOR_NODE); - } addUsageNodes(usageView.getRoot(), usageView, new ArrayList()); TableScrollingUtil.installActions(table); final List data = collectData(usages, visibleNodes, usageView, presentation); - setTableModel(table, usageView, data); + setTableModel(table, usageView, data, outOfScopeUsages, options.searchScope); SpeedSearchBase speedSearch = new MySpeedSearch(table); speedSearch.setComparator(new SpeedSearchComparator(false)); final JBPopup popup = createUsagePopup(usages, visibleNodes, handler, editor, popupPosition, - maxUsages, usageView, options, table, presentation, processIcon, hadMoreSeparator); + maxUsages, usageView, options, table, presentation, processIcon); Disposer.register(popup, usageView); @@ -293,7 +285,8 @@ public class ShowUsagesAction extends AnAction implements PopupAction { copy = new ArrayList(usages); } - rebuildPopup(usageView, copy, nodes, table, popup, presentation, popupPosition, !processIcon.isDisposed()); + rebuildPopup(usageView, copy, nodes, table, popup, presentation, popupPosition, !processIcon.isDisposed(), outOfScopeUsages, + options.searchScope); } }); @@ -305,11 +298,17 @@ public class ShowUsagesAction extends AnAction implements PopupAction { } }); - + final UsageTarget[] myUsageTarget = {new PsiElement2UsageTargetAdapter(handler.getPsiElement())}; Processor collect = new Processor() { - private final UsageTarget[] myUsageTarget = {new PsiElement2UsageTargetAdapter(handler.getPsiElement())}; @Override public boolean process(@NotNull final Usage usage) { + if (!UsageViewManagerImpl.isInScope(usage, options.searchScope)) { + if (outOfScopeUsages.getAndIncrement() == 0) { + visibleNodes.add(USAGES_OUTSIDE_SCOPE_NODE); + usages.add(USAGES_OUTSIDE_SCOPE_SEPARATOR); + } + return true; + } synchronized (usages) { if (visibleNodes.size() >= maxUsages) return false; if(UsageViewManager.isSelfUsage(usage, myUsageTarget)) return true; @@ -352,8 +351,8 @@ public class ShowUsagesAction extends AnAction implements PopupAction { synchronized (usages) { if (visibleNodes.isEmpty()) { if (usages.isEmpty()) { - String text = UsageViewBundle.message("no.usages.found.in", searchScopePresentableName(options, project)); - showHint(text, editor, popupPosition, handler, maxUsages, options); + String text = UsageViewBundle.message("no.usages.found.in", searchScopePresentableName(options)); + hint(editor, text, handler, popupPosition, maxUsages, options, false); popup.cancel(); } else { @@ -364,8 +363,13 @@ public class ShowUsagesAction extends AnAction implements PopupAction { if (usages.size() == 1) { //the only usage Usage usage = visibleNodes.iterator().next().getUsage(); - String message = UsageViewBundle.message("show.usages.only.usage", searchScopePresentableName(options, project)); - navigateAndHint(usage, message, handler, popupPosition, maxUsages, options); + if (usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) { + hint(editor, UsageViewManagerImpl.outOfScopeMessage(outOfScopeUsages.get(), options.searchScope), handler, popupPosition, maxUsages, options, true); + } + else { + String message = UsageViewBundle.message("show.usages.only.usage", searchScopePresentableName(options)); + navigateAndHint(usage, message, handler, popupPosition, maxUsages, options); + } popup.cancel(); } else { @@ -373,7 +377,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { // usage view can filter usages down to one Usage visibleUsage = visibleNodes.iterator().next().getUsage(); if (areAllUsagesInOneLine(visibleUsage, usages)) { - String hint = UsageViewBundle.message("all.usages.are.in.this.line", usages.size(), searchScopePresentableName(options, project)); + String hint = UsageViewBundle.message("all.usages.are.in.this.line", usages.size(), searchScopePresentableName(options)); navigateAndHint(visibleUsage, hint, handler, popupPosition, maxUsages, options); popup.cancel(); } @@ -451,21 +455,6 @@ public class ShowUsagesAction extends AnAction implements PopupAction { } } - private void showHint(@NotNull String text, - @Nullable final Editor editor, - @NotNull final RelativePoint popupPosition, - @NotNull FindUsagesHandler handler, - int maxUsages, - @NotNull FindUsagesOptions options) { - JComponent label = createHintComponent(text, handler, popupPosition, editor, HIDE_HINTS_ACTION, maxUsages, options); - if (editor == null || editor.isDisposed() || !editor.getComponent().isShowing()) { - HintManager.getInstance().showHint(label, popupPosition, HintManager.HIDE_BY_ANY_KEY | - HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0); - } - else { - HintManager.getInstance().showInformationHint(editor, label); - } - } @NotNull private JComponent createHintComponent(@NotNull String text, @@ -474,8 +463,12 @@ public class ShowUsagesAction extends AnAction implements PopupAction { final Editor editor, @NotNull final Runnable cancelAction, final int maxUsages, - @NotNull final FindUsagesOptions options) { + @NotNull final FindUsagesOptions options, + boolean isWarning) { JComponent label = HintUtil.createInformationLabel(suggestSecondInvocation(options, handler, text + " ")); + if (isWarning) { + label.setBackground(MessageType.WARNING.getPopupBackground()); + } InplaceButton button = createSettingsButton(handler, popupPosition, editor, maxUsages, cancelAction); JPanel panel = new JPanel(new BorderLayout()) { @@ -540,19 +533,13 @@ public class ShowUsagesAction extends AnAction implements PopupAction { if (dialog.isOK()) { dialog.calcFindUsagesOptions(); FindUsagesOptions options = handler.getFindUsagesOptions(DataManager.getInstance().getDataContext()); - showElementUsages(handler, editor, popupPosition, maxUsages, options); + showElementUsages(editor, popupPosition, handler, maxUsages, options); } } - private static String searchScopePresentableName(@NotNull FindUsagesOptions options, @NotNull Project project) { - return notNullizeScope(options, project).getDisplayName(); - } - @NotNull - private static SearchScope notNullizeScope(@NotNull FindUsagesOptions options, @NotNull Project project) { - SearchScope scope = options.searchScope; - if (scope == null) return ProjectScope.getAllScope(project); - return scope; + private static String searchScopePresentableName(@NotNull FindUsagesOptions options) { + return options.searchScope.getDisplayName(); } @NotNull @@ -566,8 +553,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { @NotNull final FindUsagesOptions options, @NotNull final JTable table, @NotNull final UsageViewPresentation presentation, - @NotNull final AsyncProcessIcon processIcon, - boolean hadMoreSeparator) { + @NotNull final AsyncProcessIcon processIcon) { table.setRowHeight(PlatformIcons.CLASS_ICON.getIconHeight()+2); table.setShowGrid(false); table.setShowVerticalLines(false); @@ -579,14 +565,15 @@ public class ShowUsagesAction extends AnAction implements PopupAction { PopupChooserBuilder builder = new PopupChooserBuilder(table); final String title = presentation.getTabText(); if (title != null) { - String result = getFullTitle(usages, title, hadMoreSeparator, visibleNodes.size() - 1, true); + String result = getFullTitle(usages, title, false, visibleNodes.size() - 1, true); builder.setTitle(result); builder.setAdText(getSecondInvocationTitle(options, handler)); } builder.setMovable(true).setResizable(true); final AtomicReference selectedUsage = new AtomicReference(); - final AtomicBoolean moreUsages = new AtomicBoolean(); + final AtomicBoolean moreUsagesSelected = new AtomicBoolean(); + final AtomicBoolean outsideScopeUsagesSelected = new AtomicBoolean(); table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { @@ -596,12 +583,17 @@ public class ShowUsagesAction extends AnAction implements PopupAction { Object value = table.getValueAt(i, 0); if (value instanceof UsageNode) { Usage usage = ((UsageNode)value).getUsage(); - if (usage == MORE_USAGES_SEPARATOR) { - moreUsages.set(true); + outsideScopeUsagesSelected.set(false); + moreUsagesSelected.set(false); + if (usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) { + outsideScopeUsagesSelected.set(true); + selectedUsage.set(null); + } + else if (usage == MORE_USAGES_SEPARATOR) { + moreUsagesSelected.set(true); selectedUsage.set(null); } else { - moreUsages.set(false); selectedUsage.set(usage instanceof UsageInfo2UsageAdapter ? ((UsageInfo2UsageAdapter)usage).getUsageInfo().copy() : usage); } break; @@ -613,10 +605,15 @@ public class ShowUsagesAction extends AnAction implements PopupAction { builder.setItemChoosenCallback(new Runnable() { @Override public void run() { - if (moreUsages.get()) { + if (moreUsagesSelected.get()) { appendMoreUsages(editor, popupPosition, handler, maxUsages, options); return; } + if (outsideScopeUsagesSelected.get()) { + options.searchScope = GlobalSearchScope.projectScope(handler.getProject()); + showElementUsages(editor, popupPosition, handler, maxUsages, options); + return; + } Object usage = selectedUsage.get(); if (usage instanceof UsageInfo) { UsageViewUtil.navigateTo((UsageInfo)usage, true); @@ -684,7 +681,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { JComponent content = popup[0].getContent(); myWidth = (int)(toolBar.getPreferredSize().getWidth() - + new JLabel(getFullTitle(usages, title, hadMoreSeparator, visibleNodes.size() - 1, true)).getPreferredSize().getWidth() + + new JLabel(getFullTitle(usages, title, false, visibleNodes.size() - 1, true)).getPreferredSize().getWidth() + settingsButton.getPreferredSize().getWidth()); myWidth = -1; for (AnAction action : toolbar.getChildren(null)) { @@ -770,7 +767,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { private static String getSecondInvocationTitle(@NotNull FindUsagesOptions options, @NotNull FindUsagesHandler handler) { if (getShowUsagesShortcut() != null) { GlobalSearchScope maximalScope = FindUsagesManager.getMaximalScope(handler); - if (!notNullizeScope(options, handler.getProject()).equals(maximalScope)) { + if (!options.searchScope.equals(maximalScope)) { return "Press " + KeymapUtil.getShortcutText(getShowUsagesShortcut()) + " again to search in " + maximalScope.getDisplayName(); } } @@ -784,7 +781,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { int maxUsages) { FindUsagesOptions cloned = options.clone(); cloned.searchScope = FindUsagesManager.getMaximalScope(handler); - showElementUsages(handler, editor, popupPosition, maxUsages, cloned); + showElementUsages(editor, popupPosition, handler, maxUsages, cloned); } @Nullable @@ -827,7 +824,9 @@ public class ShowUsagesAction extends AnAction implements PopupAction { @NotNull private static MyModel setTableModel(@NotNull JTable table, @NotNull UsageViewImpl usageView, - @NotNull final List data) { + @NotNull final List data, + @NotNull AtomicInteger outOfScopeUsages, + @NotNull SearchScope searchScope) { ApplicationManager.getApplication().assertIsDispatchThread(); final int columnCount = calcColumnCount(data); MyModel model = table.getModel() instanceof MyModel ? (MyModel)table.getModel() : null; @@ -835,7 +834,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { model = new MyModel(data, columnCount); table.setModel(model); - ShowUsagesTableCellRenderer renderer = new ShowUsagesTableCellRenderer(usageView); + ShowUsagesTableCellRenderer renderer = new ShowUsagesTableCellRenderer(usageView, outOfScopeUsages, searchScope); for (int i=0;i data = collectData(usages, nodes, usageView, presentation); - MyModel tableModel = setTableModel(table, usageView, data); + MyModel tableModel = setTableModel(table, usageView, data, outOfScopeUsages, searchScope); List existingData = tableModel.getItems(); int row = table.getSelectedRow(); @@ -1021,8 +1026,9 @@ public class ShowUsagesAction extends AnAction implements PopupAction { private void appendMoreUsages(Editor editor, @NotNull RelativePoint popupPosition, @NotNull FindUsagesHandler handler, - int maxUsages, final FindUsagesOptions options) { - showElementUsages(handler, editor, popupPosition, maxUsages+USAGES_PAGE_SIZE, options); + int maxUsages, + @NotNull FindUsagesOptions options) { + showElementUsages(editor, popupPosition, handler, maxUsages+USAGES_PAGE_SIZE, options); } private static void addUsageNodes(@NotNull GroupNode root, @NotNull final UsageViewImpl usageView, @NotNull List outNodes) { @@ -1054,25 +1060,51 @@ public class ShowUsagesAction extends AnAction implements PopupAction { if (hint == null) return; final Editor newEditor = getEditorFor(usage); if (newEditor == null) return; + hint(newEditor, hint, handler, popupPosition, maxUsages, options, false); + } + + private void showHint(@Nullable final Editor editor, + @NotNull String hint, + @NotNull FindUsagesHandler handler, + @NotNull final RelativePoint popupPosition, + int maxUsages, + @NotNull FindUsagesOptions options, + boolean isWarning) { + JComponent label = createHintComponent(hint, handler, popupPosition, editor, HIDE_HINTS_ACTION, maxUsages, options, isWarning); + if (editor == null || editor.isDisposed() || !editor.getComponent().isShowing()) { + HintManager.getInstance().showHint(label, popupPosition, HintManager.HIDE_BY_ANY_KEY | + HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0); + } + else { + HintManager.getInstance().showInformationHint(editor, label); + } + } + + private void hint(@Nullable final Editor editor, + @NotNull final String hint, + @NotNull final FindUsagesHandler handler, + @NotNull final RelativePoint popupPosition, + final int maxUsages, + @NotNull final FindUsagesOptions options, + final boolean isWarning) { final Project project = handler.getProject(); //opening editor is performing in invokeLater IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() { @Override public void run() { - newEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { // after new editor created, some editor resizing events are still bubbling. To prevent hiding hint, invokeLater this IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() { @Override public void run() { - if (newEditor.getComponent().isShowing()) { - showHint(hint, newEditor, popupPosition, handler, maxUsages, options); - } + showHint(editor, hint, handler, popupPosition, maxUsages, options, isWarning); } }); } - }); + }; + if (editor == null) runnable.run(); else editor.getScrollingModel().runActionOnScrollingFinished(runnable); } }); } @@ -1119,9 +1151,9 @@ public class ShowUsagesAction extends AnAction implements PopupAction { } static class StringNode extends UsageNode { - private final Object myString; + @NotNull private final Object myString; - public StringNode(Object string) { + public StringNode(@NotNull Object string) { super(NullUsage.INSTANCE, new UsageViewTreeModelBuilder(new UsageViewPresentation(), UsageTarget.EMPTY_ARRAY)); myString = string; } @@ -1159,7 +1191,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction { UsageNode node = (UsageNode)element; if (node instanceof StringNode) return ""; Usage usage = node.getUsage(); - if (usage == MORE_USAGES_SEPARATOR) return ""; + if (usage == MORE_USAGES_SEPARATOR || usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) return ""; GroupNode group = (GroupNode)node.getParent(); return usage.getPresentation().getPlainText() + group; } diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTableCellRenderer.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTableCellRenderer.java index 7603bab35b7b..d9848fdec4b0 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTableCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTableCellRenderer.java @@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; +import com.intellij.psi.search.SearchScope; import com.intellij.ui.FileColorManager; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; @@ -30,24 +31,30 @@ import com.intellij.usages.UsagePresentation; import com.intellij.usages.impl.GroupNode; import com.intellij.usages.impl.UsageNode; import com.intellij.usages.impl.UsageViewImpl; +import com.intellij.usages.impl.UsageViewManagerImpl; import com.intellij.usages.rules.UsageInFile; import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.UIUtil; -import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumnModel; import java.awt.*; +import java.util.concurrent.atomic.AtomicInteger; /** * @author cdr */ class ShowUsagesTableCellRenderer implements TableCellRenderer { private final UsageViewImpl myUsageView; + @NotNull private final AtomicInteger myOutOfScopeUsages; + @NotNull private final SearchScope mySearchScope; - ShowUsagesTableCellRenderer(@NotNull UsageViewImpl usageView) { + ShowUsagesTableCellRenderer(@NotNull UsageViewImpl usageView, @NotNull AtomicInteger outOfScopeUsages, @NotNull SearchScope searchScope) { myUsageView = usageView; + myOutOfScopeUsages = outOfScopeUsages; + mySearchScope = searchScope; } @Override @@ -60,32 +67,37 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer { Color fileBgColor = getBackgroundColor(isSelected, usage); final Color bg = UIUtil.getListSelectionBackground(); final Color fg = UIUtil.getListSelectionForeground(); - panel.setBackground(isSelected ? bg : fileBgColor == null ? list.getBackground() : fileBgColor); - panel.setForeground(isSelected ? fg : list.getForeground()); - - if (usage == null || usageNode instanceof ShowUsagesAction.StringNode) { - panel.setLayout(new BorderLayout()); - if (column == 0) { - panel.add(new JLabel(XmlStringUtil.wrapInHtml("" + value + ""), SwingConstants.CENTER)); - } - return panel; - } - + Color panelBackground = isSelected ? bg : fileBgColor == null ? list.getBackground() : fileBgColor; + Color panelForeground = isSelected ? fg : list.getForeground(); + panel.setBackground(panelBackground); + panel.setForeground(panelForeground); SimpleColoredComponent textChunks = new SimpleColoredComponent(); - textChunks.setIpad(new Insets(0,0,0,0)); + textChunks.setIpad(new Insets(0, 0, 0, 0)); textChunks.setBorder(null); + if (usage == null || usageNode instanceof ShowUsagesAction.StringNode) { + textChunks.append(value.toString(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + return textComponentSpanningWholeRow(textChunks, panelBackground, panelForeground, column, list, row); + } + if (usage == ShowUsagesAction.MORE_USAGES_SEPARATOR) { + textChunks.append("...<"); + textChunks.append("more usages", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + textChunks.append(">..."); + return textComponentSpanningWholeRow(textChunks, panelBackground, panelForeground, column, list, row); + } + else if (usage == ShowUsagesAction.USAGES_OUTSIDE_SCOPE_SEPARATOR) { + textChunks.append("...<"); + textChunks.append(UsageViewManagerImpl.outOfScopeMessage(myOutOfScopeUsages.get(), mySearchScope), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + textChunks.append(">..."); + return textComponentSpanningWholeRow(textChunks, panelBackground, panelForeground, column, list, row); + } + if (column == 0) { GroupNode parent = (GroupNode)usageNode.getParent(); appendGroupText(parent, panel, fileBgColor); - if (usage == ShowUsagesAction.MORE_USAGES_SEPARATOR) { - textChunks.append("...<"); - textChunks.append("more usages", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); - textChunks.append(">..."); - } } - else if (usage != ShowUsagesAction.MORE_USAGES_SEPARATOR) { + else if (usage != ShowUsagesAction.MORE_USAGES_SEPARATOR && usage != ShowUsagesAction.USAGES_OUTSIDE_SCOPE_SEPARATOR) { UsagePresentation presentation = usage.getPresentation(); TextChunk[] text = presentation.getText(); @@ -116,6 +128,59 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer { return panel; } + @NotNull + private static Component textComponentSpanningWholeRow(@NotNull SimpleColoredComponent chunks, + Color panelBackground, + Color panelForeground, + final int column, + @NotNull final JTable table, int row) { + final SimpleColoredComponent component = new SimpleColoredComponent() { + @Override + protected void doPaint(Graphics2D g) { + int offset = 0; + int i = 0; + final TableColumnModel columnModel = table.getColumnModel(); + while (i < column) { + offset += columnModel.getColumn(i).getWidth(); + i++; + } + g.translate(-offset, 0); + + //if (column == columnModel.getColumnCount()-1) { + //} + setSize(getWidth()+offset, getHeight()); // should increase the column width so that selection background will be visible even after offset translation + + super.doPaint(g); + + g.translate(+offset, 0); + } + + @NotNull + @Override + public Dimension getPreferredSize() { + //return super.getPreferredSize(); + return column == table.getColumnModel().getColumnCount()-1 ? super.getPreferredSize() : new Dimension(0,0); + // it should span the whole row, so we can't return any specific value here, + // because otherwise it would be used in the "max width" calculation in com.intellij.find.actions.ShowUsagesAction.calcMaxWidth + } + }; + + component.setIpad(new Insets(0,0,0,0)); + component.setBorder(null); + component.setBackground(panelBackground); + component.setForeground(panelForeground); + + for (SimpleColoredComponent.ColoredIterator iterator = chunks.iterator(); iterator.hasNext(); ) { + iterator.next(); + String fragment = iterator.getFragment(); + SimpleTextAttributes attributes = iterator.getTextAttributes(); + attributes = attributes.derive(attributes.getStyle(), panelForeground, panelBackground, attributes.getWaveColor()); + component.append(fragment, attributes); + } + + return component; + } + private static SimpleTextAttributes deriveAttributesWithColor(SimpleTextAttributes attributes, Color fileBgColor) { if (fileBgColor != null) { attributes = attributes.derive(-1,null, fileBgColor,null); diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java index 6e6765594e22..d3978780e535 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java @@ -205,8 +205,8 @@ public class FindUsagesManager implements JDOMExternalizable { return null; } - public void findUsages(@NotNull PsiElement psiElement, final PsiFile scopeFile, final FileEditor editor, boolean showDialog) { - FindUsagesHandler handler = getNewFindUsagesHandler(psiElement, false); + public void findUsages(@NotNull PsiElement psiElement, final PsiFile scopeFile, final FileEditor editor, boolean showDialog, @Nullable("null means default (stored in options)") SearchScope searchScope) { + FindUsagesHandler handler = getFindUsagesHandler(psiElement, false); if (handler == null) return; boolean singleFile = scopeFile != null; @@ -222,8 +222,8 @@ public class FindUsagesManager implements JDOMExternalizable { setOpenInNewTab(dialog.isShowInSeparateWindow()); FindUsagesOptions findUsagesOptions = dialog.calcFindUsagesOptions(); - if (!showDialog) { - findUsagesOptions.searchScope = GlobalSearchScope.projectScope(myProject); + if (searchScope != null) { + findUsagesOptions.searchScope = searchScope; } clearFindingNextUsageInFile(); @@ -235,7 +235,7 @@ public class FindUsagesManager implements JDOMExternalizable { @NotNull FindUsagesOptions findUsagesOptions, PsiFile scopeFile, FileEditor editor) { - FindUsagesHandler handler = getNewFindUsagesHandler(psiElement, false); + FindUsagesHandler handler = getFindUsagesHandler(psiElement, false); if (handler == null) return; startFindUsages(findUsagesOptions, handler, scopeFile, editor); } @@ -262,7 +262,7 @@ public class FindUsagesManager implements JDOMExternalizable { } } - public void showSettingsAndFindUsages(@NotNull NavigationItem[] targets) { + public static void showSettingsAndFindUsages(@NotNull NavigationItem[] targets) { if (targets.length == 0) return; NavigationItem target = targets[0]; if (!(target instanceof ConfigurableUsageTarget)) return; @@ -368,9 +368,10 @@ public class FindUsagesManager implements JDOMExternalizable { final Iterable elements = ContainerUtil.concat(primaryElements, secondaryElements); optionsClone.fastTrack = new SearchRequestCollector(new SearchSession()); - //if (optionsClone.searchScope instanceof GlobalSearchScope) { - // optionsClone.searchScope = optionsClone.searchScope.union(GlobalSearchScope.projectScope(project)); - //} + if (optionsClone.searchScope instanceof GlobalSearchScope) { + // we will search in project scope always but warn if some usage is out of scope + optionsClone.searchScope = optionsClone.searchScope.union(GlobalSearchScope.projectScope(project)); + } try { for (final PsiElement element : elements) { ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -437,14 +438,14 @@ public class FindUsagesManager implements JDOMExternalizable { throw new AssertionError(handler + " " + findUsagesOptions); } Iterable allElements = ContainerUtil.concat(primaryElements, secondaryElements); - final UsageTarget[] targets = convertToUsageTargets(allElements, findUsagesOptions); + final PsiElement2UsageTargetAdapter[] targets = convertToUsageTargets(allElements, findUsagesOptions); myAnotherManager.searchAndShowUsages(targets, new Factory() { @Override public UsageSearcher create() { return createUsageSearcher(primaryElements, secondaryElements, handler, findUsagesOptions, null); } }, !toSkipUsagePanelWhenOneUsage, true, createPresentation(primaryElements[0], findUsagesOptions, shouldOpenInNewTab()), null); - myHistory.add((ConfigurableUsageTarget)targets[0]); + myHistory.add(targets[0]); } private static void dropResolveCacheRegularly(ProgressIndicator indicator, @NotNull final Project project) { diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetAdapter.java b/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetAdapter.java index a05332d283c4..5e4c59dfd572 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetAdapter.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetAdapter.java @@ -40,7 +40,6 @@ import com.intellij.psi.meta.PsiMetaData; import com.intellij.psi.meta.PsiMetaOwner; import com.intellij.psi.meta.PsiPresentableMetaData; import com.intellij.psi.search.LocalSearchScope; -import com.intellij.psi.search.ProjectScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.ui.ComputableIcon; @@ -219,18 +218,16 @@ public class PsiElement2UsageTargetAdapter return psiElement == null ? UsageViewBundle.message("node.invalid") : FindBundle.message("recent.find.usages.action.popup", StringUtil.capitalize(UsageViewUtil.getType(psiElement)), DescriptiveNameUtil.getDescriptiveName(psiElement), - scopeString == null - ? ProjectScope.getAllScope(psiElement.getProject()).getDisplayName() - : scopeString + scopeString ); } @Override public void showSettings() { - FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(myPointer.getProject())).getFindUsagesManager(); PsiElement element = getElement(); if (element != null) { - findUsagesManager.findUsages(element, null, null, true); + FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(myPointer.getProject())).getFindUsagesManager(); + findUsagesManager.findUsages(element, null, null, true, null); } } diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetComposite.java b/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetComposite.java index a16050a08b07..a44208095e02 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetComposite.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/PsiElement2UsageTargetComposite.java @@ -43,7 +43,7 @@ public class PsiElement2UsageTargetComposite extends PsiElement2UsageTargetAdapt PsiElement element = getElement(); if (element == null) return; FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(element.getProject())).getFindUsagesManager(); - FindUsagesHandler handler = findUsagesManager.getNewFindUsagesHandler(element, false); + FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(element, false); boolean skipResultsWithOneUsage = FindSettings.getInstance().isSkipResultsWithOneUsage(); findUsagesManager.findUsages(myDescriptor.getPrimaryElements(), myDescriptor.getAdditionalElements(), handler, myOptions, skipResultsWithOneUsage); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index 6400db8c43db..e327ec79c227 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -59,6 +59,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.search.LexerEditorHighlighterLexer; +import com.intellij.psi.search.SearchScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.ui.LightweightHint; @@ -859,14 +860,19 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo findUsages(element, false); } + @Override + public void findUsagesInScope(@NotNull PsiElement element, @NotNull SearchScope searchScope) { + myFindUsagesManager.findUsages(element, null, null, false, searchScope); + } + @Override public void findUsages(@NotNull PsiElement element, boolean showDialog) { - myFindUsagesManager.findUsages(element, null, null, showDialog); + myFindUsagesManager.findUsages(element, null, null, showDialog, null); } @Override public void showSettingsAndFindUsages(@NotNull NavigationItem[] targets) { - myFindUsagesManager.showSettingsAndFindUsages(targets); + FindUsagesManager.showSettingsAndFindUsages(targets); } @Override @@ -882,7 +888,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo Document document = editor.getDocument(); PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document); - myFindUsagesManager.findUsages(element, psiFile, fileEditor, false); + myFindUsagesManager.findUsages(element, psiFile, fileEditor, false, null); } } diff --git a/platform/lang-impl/src/com/intellij/ide/structureView/impl/TemplateLanguageStructureViewBuilder.java b/platform/lang-impl/src/com/intellij/ide/structureView/impl/TemplateLanguageStructureViewBuilder.java index 83174587bf01..849b01b703b9 100644 --- a/platform/lang-impl/src/com/intellij/ide/structureView/impl/TemplateLanguageStructureViewBuilder.java +++ b/platform/lang-impl/src/com/intellij/ide/structureView/impl/TemplateLanguageStructureViewBuilder.java @@ -60,9 +60,7 @@ public abstract class TemplateLanguageStructureViewBuilder implements StructureV myVirtualFile = psiElement.getContainingFile().getVirtualFile(); myProject = psiElement.getProject(); - final TemplateLanguageFileViewProvider provider = getViewProvider(); - assert provider != null; - myTemplateDataLanguage = provider.getTemplateDataLanguage(); + myTemplateDataLanguage = getNotNullViewProvider().getTemplateDataLanguage(); } private void updateAfterPsiChange() { @@ -115,6 +113,15 @@ public abstract class TemplateLanguageStructureViewBuilder implements StructureV return provider instanceof TemplateLanguageFileViewProvider ? (TemplateLanguageFileViewProvider)provider : null; } + @NotNull + private TemplateLanguageFileViewProvider getNotNullViewProvider() { + final FileViewProvider provider = PsiManager.getInstance(myProject).findViewProvider(myVirtualFile); + if (provider == null || !(provider instanceof TemplateLanguageFileViewProvider)) { + throw new AssertionError("Not a template view provider for " + myVirtualFile + ": " + provider); + } + return (TemplateLanguageFileViewProvider)provider; + } + private void updateBaseLanguageView() { if (myBaseStructureViewDescriptor == null || !myProject.isOpen()) return; final StructureViewComponent view = (StructureViewComponent)myBaseStructureViewDescriptor.structureView; @@ -158,8 +165,7 @@ public abstract class TemplateLanguageStructureViewBuilder implements StructureV public StructureView createStructureView(FileEditor fileEditor, @NotNull Project project) { myFileEditor = fileEditor; List viewDescriptors = new ArrayList(); - final TemplateLanguageFileViewProvider provider = getViewProvider(); - assert provider != null; + final TemplateLanguageFileViewProvider provider = getNotNullViewProvider(); final StructureViewComposite.StructureViewDescriptor structureViewDescriptor = createMainView(fileEditor, provider.getPsi(provider.getBaseLanguage())); if (structureViewDescriptor != null) viewDescriptors.add(structureViewDescriptor); diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java index 0b077fa72fde..077c41e477ff 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java @@ -580,7 +580,7 @@ public class EditorWindowImpl extends UserDataHolderBase implements EditorWindow if (offsetInLine > end- lineStartOffset) offsetInLine = end - lineStartOffset; CharSequence text = myDocumentWindow.getCharsSequence(); - return EditorUtil.calcColumnNumber(this, text, lineStartOffset, lineStartOffset +offsetInLine, EditorUtil.getTabSize(myDelegate)); + return EditorUtil.calcColumnNumber(this, text, lineStartOffset, lineStartOffset +offsetInLine); } private int calcOffset(int col, int lineNumber, int lineStartOffset) { if (myDocumentWindow.getTextLength() == 0) return 0; diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java b/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java index fe34cc6c6aa6..7b200641b945 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java @@ -82,7 +82,7 @@ public class PsiElementRenameHandler implements RenameHandler { } } - public static void invoke(PsiElement element, Project project, PsiElement nameSuggestionContext, Editor editor) { + public static void invoke(PsiElement element, Project project, PsiElement nameSuggestionContext, @Nullable Editor editor) { if (element != null && !canRename(project, editor, element)) { return; } diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameDialog.java b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameDialog.java index 48460c912403..4020c8fc5cae 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameDialog.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameDialog.java @@ -26,7 +26,6 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -137,12 +136,12 @@ public class RenameDialog extends RefactoringDialog { return RenamePsiElementProcessor.forElement(myPsiElement).isToSearchInComments(myPsiElement); } - private String getFullName() { + protected String getFullName() { final String name = DescriptiveNameUtil.getDescriptiveName(myPsiElement); return (UsageViewUtil.getType(myPsiElement) + " " + name).trim(); } - private void createNewNameComponent() { + protected void createNewNameComponent() { String[] suggestedNames = getSuggestedNames(); myOldName = suggestedNames.length > 0 ? suggestedNames[0] : null; myNameSuggestionsField = new NameSuggestionsField(suggestedNames, myProject, FileTypes.PLAIN_TEXT, myEditor) { @@ -341,4 +340,12 @@ public class RenameDialog extends RefactoringDialog { final String newName = getNewName(); return RenameUtil.isValidName(myProject, myPsiElement, newName); } + + protected NameSuggestionsField getNameSuggestionsField() { + return myNameSuggestionsField; + } + + public JCheckBox getCbSearchInComments() { + return myCbSearchInComments; + } } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java b/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java index 602615a656e2..219a5524e011 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java @@ -74,9 +74,6 @@ public class ComponentWithBrowseButton extends JPanel i if (ApplicationManager.getApplication() != null) { // avoid crash at design time new MyDoClickAction(myBrowseButton).registerShortcut(myComponent); } - if (UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderDarcula()) { - setBorder(new EmptyBorder(0, 1, 0, 1)); - } } public final Comp getChildComponent() { diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DetailsComponent.java b/platform/platform-api/src/com/intellij/openapi/ui/DetailsComponent.java index 5eb15bd9d3cf..5bbd3d12da6a 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DetailsComponent.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DetailsComponent.java @@ -16,6 +16,7 @@ package com.intellij.openapi.ui; +import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.components.panels.Wrapper; import com.intellij.util.ArrayUtil; @@ -44,13 +45,13 @@ public class DetailsComponent { private final NonOpaquePanel myBanner; private String[] myBannerText; - private boolean myDetailsEnabled = true; + private boolean myDetailsEnabled = !Registry.is("ide.new.project.settings"); private String[] myPrefix; private String[] myText; private final Wrapper myContentGutter = new Wrapper(); - private boolean myPaintBorder = true; + private boolean myPaintBorder = !Registry.is("ide.new.project.settings"); public DetailsComponent() { myComponent = new JPanel(new BorderLayout()) { @@ -109,7 +110,9 @@ public class DetailsComponent { myBanner = new NonOpaquePanel(new BorderLayout()); myBannerLabel = new Banner(); - myBanner.add(myBannerLabel, BorderLayout.CENTER); + if (!Registry.is("ide.new.project.settings")) { + myBanner.add(myBannerLabel, BorderLayout.CENTER); + } myEmptyContentLabel = new JLabel("", SwingConstants.CENTER); @@ -167,7 +170,11 @@ public class DetailsComponent { myContent.setBorder(new EmptyBorder(UIUtil.PANEL_REGULAR_INSETS)); } else { - myContent.setBorder(null); + if (Registry.is("ide.new.project.settings")) { + myContent.setBorder(new EmptyBorder(16, 10, 16, 10)); + } else { + myContent.setBorder(null); + } } } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java index f9df4e325dee..75cb4de7af74 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java @@ -76,7 +76,7 @@ import java.util.Set; public abstract class DialogWrapper { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.ui.DialogWrapper"); - public static enum IdeModalityType { + public enum IdeModalityType { IDE, PROJECT, MODELESS; @@ -839,7 +839,7 @@ public abstract class DialogWrapper { /** * Dispose the wrapped and releases all resources allocated be the wrapper to help - * more effecient garbage collection. You should never invoke this method twice or + * more efficient garbage collection. You should never invoke this method twice or * invoke any method of the wrapper after invocation of dispose. * * @throws IllegalStateException if the dialog is disposed not on the event dispatch thread diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java index 0003e717b35c..35e81b032ba5 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -29,6 +29,7 @@ import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; import com.intellij.ui.navigation.History; @@ -46,6 +47,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.border.EmptyBorder; import javax.swing.tree.*; import java.awt.*; import java.util.*; @@ -61,7 +63,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom protected static final Icon COPY_ICON = PlatformIcons.COPY_ICON; protected NamedConfigurable myCurrentConfigurable; - private final Splitter mySplitter = new Splitter(false, .2f); + private final JBSplitter mySplitter; @NonNls public static final String TREE_OBJECT = "treeObject"; @NonNls public static final String TREE_NAME = "treeName"; @@ -79,7 +81,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom public void queryPlace(@NotNull final Place place) { } }); - private JScrollPane myMaster; + private JComponent myMaster; public void setHistory(final History history) { myHistory = history; @@ -128,6 +130,18 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom protected MasterDetailsComponent(MasterDetailsState state) { myState = state; + + mySplitter = new JBSplitter(false, .2f); + mySplitter.setSplitterProportionKey("ProjectStructure.SecondLevelElements"); + mySplitter.setHonorComponentsMinimumSize(true); + if (Registry.is("ide.new.project.settings")) { + mySplitter.setDividerWidth(1); + mySplitter.setShowDividerIcon(false); + mySplitter.getDivider().setBackground(Gray._153.withAlpha(128)); + mySplitter.setShowDividerControls(false); + mySplitter.setOrientation(mySplitter.getOrientation()); + } + installAutoScroll(); reInitWholePanelIfNeeded(); } @@ -163,8 +177,18 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom } }; - left.add(myNorthPanel, BorderLayout.NORTH); - myMaster = ScrollPaneFactory.createScrollPane(myTree); + if (Registry.is("ide.new.project.settings")) { + ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTree); + DefaultActionGroup group = createToolbarActionGroup(); + if (group != null) { + decorator.setActionGroup(group); + } + //left.add(myNorthPanel, BorderLayout.NORTH); + myMaster = decorator.setPanelBorder(new EmptyBorder(0, 0, 0, 0)).createPanel(); + } else { + left.add(myNorthPanel, BorderLayout.NORTH); + myMaster = ScrollPaneFactory.createScrollPane(myTree); + } left.add(myMaster, BorderLayout.CENTER); mySplitter.setFirstComponent(left); @@ -245,7 +269,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom return myHistory == null || !myHistory.isNavigatingNow(); } - private void initToolbar() { + protected DefaultActionGroup createToolbarActionGroup() { final ArrayList actions = createActions(false); if (actions != null) { final DefaultActionGroup group = new DefaultActionGroup(); @@ -257,6 +281,14 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom group.add(action); } } + return group; + } + return null; + } + + private void initToolbar() { + DefaultActionGroup group = createToolbarActionGroup(); + if (group != null) { final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent(); myNorthPanel.add(component, BorderLayout.NORTH); } @@ -270,6 +302,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom return new Dimension(800, 600); } + @NotNull public JComponent createComponent() { reInitWholePanelIfNeeded(); diff --git a/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java b/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java index 2293d45bf329..3ddbf47028f4 100644 --- a/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java +++ b/platform/platform-api/src/com/intellij/ui/GroupedElementsRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -15,7 +15,9 @@ */ package com.intellij.ui; +import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.components.panels.OpaquePanel; +import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import javax.swing.*; @@ -24,16 +26,43 @@ import javax.swing.border.EmptyBorder; import javax.swing.tree.TreeCellRenderer; import java.awt.*; +import static javax.swing.SwingConstants.CENTER; +import static javax.swing.SwingConstants.LEFT; + public abstract class GroupedElementsRenderer { public static final Color POPUP_SEPARATOR_FOREGROUND = new JBColor(Color.gray.brighter(), Gray._43); public static final Color POPUP_SEPARATOR_TEXT_FOREGROUND = Color.gray; public static final Color SELECTED_FRAME_FOREGROUND = Color.black; - protected SeparatorWithText mySeparatorComponent = new SeparatorWithText(); + protected SeparatorWithText mySeparatorComponent = new SeparatorWithText() { + @Override + protected void paintComponent(Graphics g) { + if (Registry.is("ide.new.project.settings")) { + g.setColor(POPUP_SEPARATOR_FOREGROUND); + Rectangle viewR = new Rectangle(0, getVgap(), getWidth() - 1, getHeight() - getVgap() - 1); + Rectangle iconR = new Rectangle(); + Rectangle textR = new Rectangle(); + String s = SwingUtilities + .layoutCompoundLabel(g.getFontMetrics(), getCaption(), null, CENTER, + LEFT, + CENTER, + LEFT, + viewR, iconR, textR, 0); + GraphicsUtil.setupAAPainting(g); + g.setColor(Gray._255.withAlpha(80)); + g.drawString(s, textR.x + 10, textR.y + 1 + g.getFontMetrics().getAscent()); + g.setColor(new Color(0x5F6D7B)); + g.drawString(s, textR.x + 10, textR.y + g.getFontMetrics().getAscent()); + } else { + super.paintComponent(g); + } + } + }; + protected JComponent myComponent; protected MyComponent myRendererComponent; - protected ErrorLabel myTextLabel; + protected JLabel myTextLabel; public GroupedElementsRenderer() { @@ -58,14 +87,16 @@ public abstract class GroupedElementsRenderer { myTextLabel.setIcon(icon); myTextLabel.setDisabledIcon(disabledIcon); + if (myTextLabel instanceof EngravedLabel) { + ((EngravedLabel)myTextLabel).setShadowColor(isSelected ? UIUtil.getTreeSelectionBackground() : null); + } if (isSelected) { - myComponent.setBorder(getSelectedBorder()); + //myComponent.setBorder(getSelectedBorder()); setSelected(myComponent); setSelected(myTextLabel); - } - else { - myComponent.setBorder(getBorder()); + } else { + //myComponent.setBorder(getBorder()); setDeselected(myComponent); setDeselected(myTextLabel); } @@ -98,7 +129,7 @@ public abstract class GroupedElementsRenderer { protected final void setDeselected(JComponent aComponent) { aComponent.setBackground(getBackground()); - aComponent.setForeground(getForeground()); + aComponent.setForeground(Registry.is("ide.new.project.settings") ? Gray._60 : getForeground()); } protected abstract Color getSelectionBackground(); @@ -139,12 +170,12 @@ public abstract class GroupedElementsRenderer { } @Override - protected final Color getBackground() { + protected Color getBackground() { return UIUtil.getListBackground(); } @Override - protected final Color getForeground() { + protected Color getForeground() { return UIUtil.getListForeground(); } } diff --git a/platform/platform-api/src/com/intellij/ui/SeparatorWithText.java b/platform/platform-api/src/com/intellij/ui/SeparatorWithText.java index 059875a4acfc..2633ef31c9f3 100644 --- a/platform/platform-api/src/com/intellij/ui/SeparatorWithText.java +++ b/platform/platform-api/src/com/intellij/ui/SeparatorWithText.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -20,7 +20,8 @@ import com.intellij.util.ui.UIUtil; import javax.swing.*; import java.awt.*; -import static javax.swing.SwingConstants.*; +import static javax.swing.SwingConstants.CENTER; +import static javax.swing.SwingConstants.LEFT; public class SeparatorWithText extends JComponent { @@ -34,11 +35,11 @@ public class SeparatorWithText extends JComponent { setFont(getFont().deriveFont(Font.BOLD)); } - private static int getVgap() { + protected static int getVgap() { return UIUtil.isUnderNativeMacLookAndFeel() ? 1 : 3; } - private static int getHgap() { + protected static int getHgap() { return 3; } @@ -109,6 +110,10 @@ public class SeparatorWithText extends JComponent { } } + protected String getCaption() { + return myCaption; + } + public void setCaption(String captionAboveOf) { myCaption = captionAboveOf; } diff --git a/platform/platform-api/src/com/intellij/ui/ToolbarDecorator.java b/platform/platform-api/src/com/intellij/ui/ToolbarDecorator.java index 24cb28001fed..80e3d1eec7fb 100644 --- a/platform/platform-api/src/com/intellij/ui/ToolbarDecorator.java +++ b/platform/platform-api/src/com/intellij/ui/ToolbarDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -15,10 +15,7 @@ */ package com.intellij.ui; -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.actionSystem.ActionToolbar; -import com.intellij.openapi.actionSystem.ActionToolbarPosition; -import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; @@ -296,7 +293,9 @@ public abstract class ToolbarDecorator implements CommonActionsPanel.ListenerFac public ToolbarDecorator setActionGroup(@NotNull ActionGroup actionGroup) { AnAction[] actions = actionGroup.getChildren(null); for (AnAction action : actions) { - addExtraAction(AnActionButton.fromAction(action)); + if (!(action instanceof Separator)) { + addExtraAction(AnActionButton.fromAction(action)); + } } return this; } diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java index 0f62f53dab8c..169b5d57479b 100644 --- a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java @@ -753,6 +753,7 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith UIUtil.setLineStyleAngled(this); } + @NotNull public T[] getSelectedNodes(Class nodeType, @Nullable NodeFilter filter) { TreePath[] paths = getSelectionPaths(); if (paths == null) return (T[])Array.newInstance(nodeType, 0); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java index caadfc8ae744..216429557cea 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java @@ -488,8 +488,6 @@ public final class EditorUtil { } if (editor == null || useOptimization) { - int shift = 0; - Document document = editor == null ? null : editor.getDocument(); if (document != null && start < offset-1 && document.getLineNumber(start) != document.getLineNumber(offset-1)) { String editorInfo = editor instanceof EditorImpl ? ". Editor info: " + ((EditorImpl)editor).dumpState() : ""; @@ -504,6 +502,7 @@ public final class EditorUtil { LOG, "detected incorrect offset -> column number calculation", "start: " + start + ", given offset: " + offset+", given tab size: " + tabSize + ". "+documentInfo+ editorInfo); } + int shift = 0; if (hasTabs) { for (int i = start; i < offset; i++) { char c = text.charAt(i); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index 5910830e6385..5f248b6e5959 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -561,7 +561,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret { myEditor.updateCaretCursor(); requestRepaint(oldInfo); - if (locateBeforeSoftWrap && SoftWrapHelper.isCaretAfterSoftWrap(myEditor)) { + if (locateBeforeSoftWrap && SoftWrapHelper.isCaretAfterSoftWrap(this)) { int lineToUse = myVisibleCaret.line - 1; if (lineToUse >= 0) { final VisualPosition visualPosition = new VisualPosition(lineToUse, EditorUtil.getLastVisualLineColumnNumber(myEditor, lineToUse)); @@ -1451,6 +1451,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret { return marker != null && marker.isValid() && isVirtualSelectionEnabled() && myEndVirtualOffset > myStartVirtualOffset; } + public EditorImpl getEditor() { + return myEditor; + } + /** * Encapsulates information about target vertical range info - its 'y' coordinate and height in pixels. */ diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapHelper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapHelper.java index a3dd31e80a53..baf9477d1e9f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -15,9 +15,9 @@ */ package com.intellij.openapi.editor.impl.softwrap; -import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.SoftWrap; import com.intellij.openapi.editor.SoftWrapModel; +import com.intellij.openapi.editor.impl.CaretImpl; import com.intellij.openapi.editor.impl.EditorImpl; /** @@ -48,18 +48,18 @@ public class SoftWrapHelper { * @return true if caret offset of the given editor points to visual position that belongs to * visual line where soft wrap end is located */ - public static boolean isCaretAfterSoftWrap(EditorImpl editor) { - CaretModel caretModel = editor.getCaretModel(); - if (!caretModel.isUpToDate()) { + public static boolean isCaretAfterSoftWrap(CaretImpl caret) { + if (!caret.isUpToDate()) { return false; } + EditorImpl editor = caret.getEditor(); SoftWrapModel softWrapModel = editor.getSoftWrapModel(); - int offset = caretModel.getOffset(); + int offset = caret.getOffset(); SoftWrap softWrap = softWrapModel.getSoftWrap(offset); if (softWrap == null) { return false; } - return editor.offsetToVisualLine(offset) == caretModel.getVisualPosition().line; + return editor.offsetToVisualLine(offset) == caret.getVisualPosition().line; } } diff --git a/platform/platform-impl/src/com/intellij/ui/popup/list/GroupedItemsListRenderer.java b/platform/platform-impl/src/com/intellij/ui/popup/list/GroupedItemsListRenderer.java index 30c609029630..72f2e1dc3b37 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/list/GroupedItemsListRenderer.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/list/GroupedItemsListRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -16,8 +16,11 @@ package com.intellij.ui.popup.list; import com.intellij.openapi.ui.popup.ListItemDescriptor; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.EngravedLabel; import com.intellij.ui.ErrorLabel; +import com.intellij.ui.Gray; import com.intellij.ui.GroupedElementsRenderer; import com.intellij.ui.components.panels.OpaquePanel; @@ -55,15 +58,21 @@ public class GroupedItemsListRenderer extends GroupedElementsRenderer.List imple @Override protected JComponent createItemComponent() { - myTextLabel = new ErrorLabel(); - myTextLabel.setOpaque(true); - myTextLabel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + if (Registry.is("ide.new.project.settings")) { + myTextLabel = new EngravedLabel(); + myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD)); + myTextLabel.setForeground(Gray._240); + } else { + myTextLabel = new ErrorLabel(); + myTextLabel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + } + myTextLabel.setOpaque(true); return layoutComponent(myTextLabel); } protected final JComponent layoutComponent(JComponent middleItemComponent) { - JPanel result = new OpaquePanel(new BorderLayout(4, 4), Color.white); + JPanel result = new OpaquePanel(Registry.is("ide.new.project.settings") ? new BorderLayout(0, 0) : new BorderLayout(4, 4), Color.white); myNextStepLabel = new JLabel(); myNextStepLabel.setOpaque(true); diff --git a/platform/platform-resources-en/src/messages/VcsBundle.properties b/platform/platform-resources-en/src/messages/VcsBundle.properties index 594db771aa60..5bd7b463c794 100644 --- a/platform/platform-resources-en/src/messages/VcsBundle.properties +++ b/platform/platform-resources-en/src/messages/VcsBundle.properties @@ -138,6 +138,8 @@ button.configure=&Configure VCS... action.name.rollback=Rollback command.name.rollback.change=Rollback Change action.name.show.difference=Show Difference +tooltip.text.line.before.deleted=Line before {0} deleted +tooltip.text.lines.before.deleted={1} lines before {0} deleted tooltip.text.line.changed=Line {0} changed tooltip.text.lines.changed=Lines {0}-{1} changed dialog.title.diff.for.range=Diff for Range diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 2d803f5fe361..dad0fd91d83d 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -402,3 +402,5 @@ console.too.much.text.buffer.ratio=10 console.too.much.text.buffer.ratio.description=Used for disabling of console processing(console filters for highlights, foldings...),\n\ when there is too much of text to process.\n\ The ratio is used against the console cycle buffer size (idea.cycle.buffer.size/theRatio=maxTextLength). +ide.new.project.settings=false +ide.new.project.settings.description=Temporary key for new project settings dialog UI diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorMultiCaretTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorMultiCaretTest.java index 1c05f47b16ed..26be81d2f341 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorMultiCaretTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorMultiCaretTest.java @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.openapi.editor.impl.AbstractEditorTest; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManager; +import com.intellij.testFramework.EditorTestUtil; import com.intellij.testFramework.TestFileType; import java.awt.event.InputEvent; @@ -305,4 +306,18 @@ public class EditorMultiCaretTest extends AbstractEditorTest { "brown\n" + "fox"); } + + public void testCaretPositionsRecalculationOnDocumentChange() throws Exception { + init("\n" + + "word\n" + + "some long prefix word-suffix", TestFileType.TEXT); + EditorTestUtil.configureSoftWraps(myEditor, 17); // wrapping right before 'word-suffix' + + delete(); + + checkResultByText("\n" + + "\n" + + "some long prefix -suffix"); + verifySoftWrapPositions(19); + } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java index 1dcc6c8ef248..c19ba91e3346 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java @@ -282,7 +282,7 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project } } - fireErrors(errors); + onModuleLoadErrors(errors); showUnknownModuleTypeNotification(modulesWithUnknownTypes); @@ -321,7 +321,7 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project } } - private void fireErrors(final List errors) { + protected void onModuleLoadErrors(final List errors) { if (errors.isEmpty()) return; myModuleModel.myModulesCache = null; @@ -337,6 +337,10 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project } } + fireModuleLoadErrors(errors); + } + + protected void fireModuleLoadErrors(List errors) { if (ApplicationManager.getApplication().isHeadlessEnvironment()) { throw new RuntimeException(errors.get(0).getDescription()); } diff --git a/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/deployment/ServerRuntimeInstance.java b/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/deployment/ServerRuntimeInstance.java index 93dfe954431f..3f5e4ba42b0b 100644 --- a/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/deployment/ServerRuntimeInstance.java +++ b/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/deployment/ServerRuntimeInstance.java @@ -21,6 +21,9 @@ public abstract class ServerRuntimeInstance { return getDeploymentName(source); } + /** + * @deprecated use {@link #getDeploymentName(com.intellij.remoteServer.configuration.deployment.DeploymentSource, D configuration)} instead + */ @NotNull public String getDeploymentName(@NotNull DeploymentSource source) { return source.getPresentableName(); diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/HasUrl.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/HasUrl.java new file mode 100644 index 000000000000..56f51c5d6d51 --- /dev/null +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/HasUrl.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.debugger; + +import com.intellij.util.Url; +import org.jetbrains.annotations.NotNull; + + +public interface HasUrl { + @NotNull + Url getUrl(); +} diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/Script.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/Script.java index 354a51387a50..e44d308977d3 100755 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/Script.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/Script.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.sourcemap.SourceMap; -public interface Script extends UserDataHolderEx { +public interface Script extends UserDataHolderEx, HasUrl { void setSourceMap(SourceMap sourceMap); enum Type { diff --git a/platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/FieldProcessor.java b/platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/FieldProcessor.java index 92a3a6bf0c78..eabc8d8e4337 100644 --- a/platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/FieldProcessor.java +++ b/platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/FieldProcessor.java @@ -60,9 +60,23 @@ class FieldProcessor { private MethodHandler processFieldGetterMethod(Method m, String fieldName) { Type genericReturnType = m.getGenericReturnType(); - boolean nullable = m.getAnnotation(JsonNullable.class) != null || - (genericReturnType == String.class && ((m.getAnnotation(JsonField.class) != null && m.getAnnotation(JsonField.class).optional()) - || m.getAnnotation(JsonOptionalField.class) != null)); + boolean nullable; + if (m.getAnnotation(JsonNullable.class) != null) { + nullable = true; + } + else if (genericReturnType == String.class || genericReturnType == Enum.class) { + JsonField jsonField = m.getAnnotation(JsonField.class); + if (jsonField != null) { + nullable = jsonField.optional() && !jsonField.allowAnyPrimitiveValue() && !jsonField.allowAnyPrimitiveValueAndMap(); + } + else { + nullable = m.getAnnotation(JsonOptionalField.class) != null; + } + } + else { + nullable = false; + } + ValueReader fieldTypeParser = reader.getFieldTypeParser(genericReturnType, nullable, false, m); if (fieldTypeParser != InterfaceReader.VOID_PARSER) { fieldLoaders.add(new FieldLoader(fieldName, fieldTypeParser)); diff --git a/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java new file mode 100644 index 000000000000..8b065b30d167 --- /dev/null +++ b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java @@ -0,0 +1,478 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.usages.impl; + +import com.intellij.find.FindManager; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.colors.CodeInsightColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.util.ProgressWrapper; +import com.intellij.openapi.progress.util.TooManyUsagesStatus; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Factory; +import com.intellij.openapi.util.Segment; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchScope; +import com.intellij.ui.HyperlinkAdapter; +import com.intellij.usageView.UsageViewBundle; +import com.intellij.usages.*; +import com.intellij.util.Alarm; +import com.intellij.util.ArrayUtil; +import com.intellij.util.CommonProcessors; +import com.intellij.util.Processor; +import com.intellij.util.ui.RangeBlinker; +import com.intellij.xml.util.XmlStringUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import java.awt.event.ActionEvent; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +class SearchForUsagesRunnable implements Runnable { + @NonNls private static final String FIND_OPTIONS_HREF_TARGET = "FindOptions"; + @NonNls private static final String SEARCH_IN_PROJECT_HREF_TARGET = "SearchInProject"; + @NonNls private static final String LARGE_FILES_HREF_TARGET = "LargeFiles"; + private final AtomicInteger myUsageCountWithoutDefinition = new AtomicInteger(0); + private final AtomicReference myFirstUsage = new AtomicReference(); + @NotNull + private final Project myProject; + private final AtomicReference myUsageViewRef; + private final UsageViewPresentation myPresentation; + private final UsageTarget[] mySearchFor; + private final Factory mySearcherFactory; + private final FindUsagesProcessPresentation myProcessPresentation; + @NotNull private final SearchScope mySearchScope; + private final UsageViewManager.UsageViewStateListener myListener; + private final UsageViewManagerImpl myUsageViewManager; + private final AtomicInteger myOutOfScopeUsages = new AtomicInteger(); + + SearchForUsagesRunnable(@NotNull UsageViewManagerImpl usageViewManager, + @NotNull Project project, + @NotNull AtomicReference usageViewRef, + @NotNull UsageViewPresentation presentation, + @NotNull UsageTarget[] searchFor, + @NotNull Factory searcherFactory, + @NotNull FindUsagesProcessPresentation processPresentation, + @NotNull SearchScope scope, + @Nullable UsageViewManager.UsageViewStateListener listener) { + myProject = project; + myUsageViewRef = usageViewRef; + myPresentation = presentation; + mySearchFor = searchFor; + mySearcherFactory = searcherFactory; + myProcessPresentation = processPresentation; + mySearchScope = scope; + myListener = listener; + myUsageViewManager = usageViewManager; + } + + @NotNull + private static String createOptionsHtml(@NonNls UsageTarget[] searchFor) { + KeyboardShortcut shortcut = UsageViewImpl.getShowUsagesWithSettingsShortcut(searchFor); + String shortcutText = ""; + if (shortcut != null) { + shortcutText = " (" + KeymapUtil.getShortcutText(shortcut) + ")"; + } + return "Find Options..." + shortcutText; + } + + @NotNull + private static String createSearchInProjectHtml() { + return "Search in Project"; + } + + private static void notifyByFindBalloon(final HyperlinkListener listener, + @NotNull final MessageType info, + @NotNull FindUsagesProcessPresentation processPresentation, + @NotNull final Project project, + @NotNull final List lines) { + com.intellij.usageView.UsageViewManager.getInstance(project); // in case tool window not registered + + final Collection largeFiles = processPresentation.getLargeFiles(); + List resultLines = new ArrayList(lines); + HyperlinkListener resultListener = listener; + if (!largeFiles.isEmpty()) { + String shortMessage = "(" + + UsageViewBundle.message("large.files.were.ignored", largeFiles.size()) + ")"; + + resultLines.add(shortMessage); + resultListener = new HyperlinkAdapter(){ + @Override + protected void hyperlinkActivated(HyperlinkEvent e) { + if (e.getDescription().equals(LARGE_FILES_HREF_TARGET)) { + String detailedMessage = detailedLargeFilesMessage(largeFiles); + List strings = new ArrayList(lines); + strings.add(detailedMessage); + ToolWindowManager.getInstance(project).notifyByBalloon(ToolWindowId.FIND, info, wrapInHtml(strings), AllIcons.Actions.Find, listener); + } + else if (listener != null) { + listener.hyperlinkUpdate(e); + } + } + }; + } + + ToolWindowManager.getInstance(project).notifyByBalloon(ToolWindowId.FIND, info, wrapInHtml(resultLines), AllIcons.Actions.Find, resultListener); + } + + @NotNull + private static String wrapInHtml(@NotNull List strings) { + return XmlStringUtil.wrapInHtml(StringUtil.join(strings, "
")); + } + + @NotNull + private static String detailedLargeFilesMessage(@NotNull Collection largeFiles) { + String message = ""; + if (largeFiles.size() == 1) { + final VirtualFile vFile = largeFiles.iterator().next().getVirtualFile(); + message += "File " + presentableFileInfo(vFile) + " is "; + } + else { + message += "Files
"; + + int counter = 0; + for (PsiFile file : largeFiles) { + final VirtualFile vFile = file.getVirtualFile(); + message += presentableFileInfo(vFile) + "
"; + if (counter++ > 10) break; + } + + message += "are "; + } + + message += "too large and cannot be scanned"; + return message; + } + + @NotNull + private static String presentableFileInfo(@NotNull VirtualFile vFile) { + return getPresentablePath(vFile) + + " (" + + UsageViewManagerImpl.presentableSize(UsageViewManagerImpl.getFileLength(vFile)) + + ")"; + } + + @NotNull + private static String getPresentablePath(@NotNull final VirtualFile virtualFile) { + return "'" + ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + return virtualFile.getPresentableUrl(); + } + }) + "'"; + } + + @NotNull + private HyperlinkListener createGotToOptionsListener(@NotNull final UsageTarget[] targets) { + return new HyperlinkAdapter() { + @Override + protected void hyperlinkActivated(HyperlinkEvent e) { + if (e.getDescription().equals(FIND_OPTIONS_HREF_TARGET)) { + FindManager.getInstance(myProject).showSettingsAndFindUsages(targets); + } + } + }; + } + @NotNull + private HyperlinkListener createSearchInProjectListener() { + return new HyperlinkAdapter() { + @Override + protected void hyperlinkActivated(HyperlinkEvent e) { + if (e.getDescription().equals(SEARCH_IN_PROJECT_HREF_TARGET)) { + PsiElement psiElement = getPsiElement(mySearchFor); + if (psiElement != null) { + FindManager.getInstance(myProject).findUsagesInScope(psiElement, GlobalSearchScope.projectScope(myProject)); + } + } + } + }; + } + + private static PsiElement getPsiElement(UsageTarget[] searchFor) { + if (!(searchFor[0] instanceof PsiElementUsageTarget)) return null; + return ((PsiElementUsageTarget)searchFor[0]).getElement(); + } + + private static void flashUsageScriptaculously(@NotNull final Usage usage) { + if (!(usage instanceof UsageInfo2UsageAdapter)) { + return; + } + UsageInfo2UsageAdapter usageInfo = (UsageInfo2UsageAdapter)usage; + + Editor editor = usageInfo.openTextEditor(true); + if (editor == null) return; + TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.BLINKING_HIGHLIGHTS_ATTRIBUTES); + + RangeBlinker rangeBlinker = new RangeBlinker(editor, attributes, 6); + List segments = new ArrayList(); + CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(segments); + usageInfo.processRangeMarkers(processor); + rangeBlinker.resetMarkers(segments); + rangeBlinker.startBlinking(); + } + + private UsageViewImpl getUsageView(ProgressIndicator indicator) { + UsageViewImpl usageView = myUsageViewRef.get(); + if (usageView != null) return usageView; + int usageCount = myUsageCountWithoutDefinition.get(); + if (usageCount >= 2 || usageCount == 1 && myProcessPresentation.isShowPanelIfOnlyOneUsage()) { + usageView = new UsageViewImpl(myProject, myPresentation, mySearchFor, mySearcherFactory); + usageView.associateProgress(indicator); + if (myUsageViewRef.compareAndSet(null, usageView)) { + openView(usageView); + final Usage firstUsage = myFirstUsage.get(); + if (firstUsage != null) { + final UsageViewImpl finalUsageView = usageView; + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + finalUsageView.appendUsage(firstUsage); + } + }); + } + } + else { + Disposer.dispose(usageView); + } + return myUsageViewRef.get(); + } + return null; + } + + private void openView(@NotNull final UsageViewImpl usageView) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (myProject.isDisposed()) return; + myUsageViewManager.addContent(usageView, myPresentation); + if (myListener != null) { + myListener.usageViewCreated(usageView); + } + myUsageViewManager.showToolWindow(false); + } + }); + } + + @Override + public void run() { + AtomicBoolean findUsagesStartedShown = new AtomicBoolean(); + searchUsages(findUsagesStartedShown); + endSearchForUsages(findUsagesStartedShown); + } + + private void searchUsages(@NotNull final AtomicBoolean findStartedBalloonShown) { + ProgressIndicator indicator = ProgressWrapper.unwrap(ProgressManager.getInstance().getProgressIndicator()); + TooManyUsagesStatus.createFor(indicator); + Alarm findUsagesStartedBalloon = new Alarm(); + findUsagesStartedBalloon.addRequest(new Runnable() { + @Override + public void run() { + notifyByFindBalloon(null, MessageType.WARNING, myProcessPresentation, myProject, + Collections.singletonList(UsageViewManagerImpl.getProgressTitle(myPresentation))); + findStartedBalloonShown.set(true); + } + }, 300, ModalityState.NON_MODAL); + UsageSearcher usageSearcher = mySearcherFactory.create(); + + usageSearcher.generate(new Processor() { + @Override + public boolean process(final Usage usage) { + ProgressIndicator indicator = ProgressWrapper.unwrap(ProgressManager.getInstance().getProgressIndicator()); + if (indicator != null && indicator.isCanceled()) return false; + + if (!UsageViewManagerImpl.isInScope(usage, mySearchScope)) { + myOutOfScopeUsages.incrementAndGet(); + return true; + } + + boolean incrementCounter = !UsageViewManager.isSelfUsage(usage, mySearchFor); + + if (incrementCounter) { + final int usageCount = myUsageCountWithoutDefinition.incrementAndGet(); + if (usageCount == 1 && !myProcessPresentation.isShowPanelIfOnlyOneUsage()) { + myFirstUsage.compareAndSet(null, usage); + } + + final UsageViewImpl usageView = getUsageView(indicator); + + TooManyUsagesStatus tooManyUsagesStatus; + if (usageCount > UsageLimitUtil.USAGES_LIMIT && (tooManyUsagesStatus = TooManyUsagesStatus.getFrom(indicator)).switchTooManyUsagesStatus()) { + UsageViewManagerImpl.showTooManyUsagesWarning(myProject, tooManyUsagesStatus, indicator, myPresentation, usageCount, usageView); + } + + if (usageView != null) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + usageView.appendUsage(usage); + } + }); + } + } + return indicator == null || !indicator.isCanceled(); + } + }); + if (getUsageView(indicator) != null) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + myUsageViewManager.showToolWindow(true); + } + }, myProject.getDisposed()); + } + Disposer.dispose(findUsagesStartedBalloon); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (findStartedBalloonShown.get()) { + Balloon balloon = ToolWindowManager.getInstance(myProject).getToolWindowBalloon(ToolWindowId.FIND); + if (balloon != null) { + balloon.hide(); + } + } + } + }, myProject.getDisposed()); + } + + private void endSearchForUsages(@NotNull final AtomicBoolean findStartedBalloonShown) { + assert !ApplicationManager.getApplication().isDispatchThread() : Thread.currentThread(); + int usageCount = myUsageCountWithoutDefinition.get(); + if (usageCount == 0 && myProcessPresentation.isShowNotFoundMessage()) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + final List notFoundActions = myProcessPresentation.getNotFoundActions(); + final String message = UsageViewBundle.message("dialog.no.usages.found.in", + StringUtil.decapitalize(myPresentation.getUsagesString()), + myPresentation.getScopeText()); + + if (notFoundActions.isEmpty()) { + List lines = new ArrayList(); + lines.add(StringUtil.escapeXml(message)); + if (myOutOfScopeUsages.get() != 0) { + lines.add(UsageViewManagerImpl.outOfScopeMessage(myOutOfScopeUsages.get(), mySearchScope)); + } + if (myProcessPresentation.isShowFindOptionsPrompt()) { + lines.add(createOptionsHtml(mySearchFor)); + } + MessageType type = myOutOfScopeUsages.get() == 0 ? MessageType.INFO : MessageType.WARNING; + notifyByFindBalloon(createGotToOptionsListener(mySearchFor), + type, myProcessPresentation, myProject, lines); + findStartedBalloonShown.set(false); + } + else { + List titles = new ArrayList(notFoundActions.size() + 1); + titles.add(UsageViewBundle.message("dialog.button.ok")); + for (Action action : notFoundActions) { + Object value = action.getValue(FindUsagesProcessPresentation.NAME_WITH_MNEMONIC_KEY); + if (value == null) value = action.getValue(Action.NAME); + + titles.add((String)value); + } + + int option = Messages.showDialog(myProject, message, UsageViewBundle.message("dialog.title.information"), + ArrayUtil.toStringArray(titles), 0, Messages.getInformationIcon()); + + if (option > 0) { + notFoundActions.get(option - 1).actionPerformed(new ActionEvent(this, 0, titles.get(option))); + } + } + } + }, ModalityState.NON_MODAL, myProject.getDisposed()); + } + else if (usageCount == 1 && !myProcessPresentation.isShowPanelIfOnlyOneUsage()) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + Usage usage = myFirstUsage.get(); + if (usage.canNavigate()) { + usage.navigate(true); + flashUsageScriptaculously(usage); + } + List lines = new ArrayList(); + + lines.add("Only one usage found."); + if (myOutOfScopeUsages.get() != 0) { + lines.add(UsageViewManagerImpl.outOfScopeMessage(myOutOfScopeUsages.get(), mySearchScope)); + } + lines.add(createOptionsHtml(mySearchFor)); + MessageType type = myOutOfScopeUsages.get() == 0 ? MessageType.INFO : MessageType.WARNING; + notifyByFindBalloon(createGotToOptionsListener(mySearchFor), + type, myProcessPresentation, myProject, + lines); + } + }, ModalityState.NON_MODAL, myProject.getDisposed()); + } + else { + final UsageViewImpl usageView = myUsageViewRef.get(); + if (usageView != null) { + usageView.drainQueuedUsageNodes(); + usageView.setSearchInProgress(false); + } + + final List lines; + final HyperlinkListener hyperlinkListener; + if (myOutOfScopeUsages.get() == 0 || getPsiElement(mySearchFor)==null) { + lines = Collections.emptyList(); + hyperlinkListener = null; + } + else { + lines = Arrays.asList(UsageViewManagerImpl.outOfScopeMessage(myOutOfScopeUsages.get(), mySearchScope), createSearchInProjectHtml()); + hyperlinkListener = createSearchInProjectListener(); + } + + if (!myProcessPresentation.getLargeFiles().isEmpty() || myOutOfScopeUsages.get() != 0) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + MessageType type = myOutOfScopeUsages.get() == 0 ? MessageType.INFO : MessageType.WARNING; + notifyByFindBalloon(hyperlinkListener, type, myProcessPresentation, myProject, lines); + } + }, ModalityState.NON_MODAL, myProject.getDisposed()); + } + } + + if (myListener != null) { + myListener.findingUsagesFinished(myUsageViewRef.get()); + } + } +} diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java index 1667cc4a9858..6d7ce9c07a14 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java @@ -15,59 +15,37 @@ */ package com.intellij.usages.impl; -import com.intellij.find.FindManager; import com.intellij.find.SearchInBackgroundOption; -import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.actionSystem.DataSink; +import com.intellij.openapi.actionSystem.TypeSafeDataProvider; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.colors.CodeInsightColors; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; -import com.intellij.openapi.progress.util.ProgressWrapper; import com.intellij.openapi.progress.util.TooManyUsagesStatus; import com.intellij.openapi.project.DumbModeAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.MessageType; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Factory; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.psi.PsiFile; -import com.intellij.ui.HyperlinkAdapter; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.content.Content; import com.intellij.usageView.UsageViewBundle; import com.intellij.usages.*; -import com.intellij.util.Alarm; -import com.intellij.util.ArrayUtil; -import com.intellij.util.CommonProcessors; -import com.intellij.util.Processor; -import com.intellij.util.ui.RangeBlinker; +import com.intellij.usages.rules.PsiElementUsage; +import com.intellij.usages.rules.UsageInFile; import com.intellij.util.ui.UIUtil; -import com.intellij.xml.util.XmlStringUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkListener; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; /** @@ -76,8 +54,6 @@ import java.util.concurrent.atomic.AtomicReference; public class UsageViewManagerImpl extends UsageViewManager { private final Project myProject; private static final Key USAGE_VIEW_KEY = Key.create("USAGE_VIEW"); - @NonNls private static final String LARGE_FILES_HREF_TARGET = "LargeFiles"; - @NonNls private static final String FIND_OPTIONS_HREF_TARGET = "FindOptions"; public UsageViewManagerImpl(@NotNull Project project) { myProject = project; @@ -113,7 +89,7 @@ public class UsageViewManagerImpl extends UsageViewManager { return showUsages(searchedFor, foundUsages, presentation, null); } - private void addContent(@NotNull UsageViewImpl usageView, @NotNull UsageViewPresentation presentation) { + void addContent(@NotNull UsageViewImpl usageView, @NotNull UsageViewPresentation presentation) { Content content = com.intellij.usageView.UsageViewManager.getInstance(myProject).addContent( presentation.getTabText(), presentation.getTabName(), @@ -146,14 +122,26 @@ public class UsageViewManagerImpl extends UsageViewManager { @NotNull final UsageViewPresentation presentation, @NotNull final FindUsagesProcessPresentation processPresentation, @Nullable final UsageViewStateListener listener) { + final SearchScope searchScope = getSearchScope(searchFor); + return doSearchAndShow(searchFor, searcherFactory, presentation, processPresentation, listener, searchScope); + } + + UsageView doSearchAndShow(@NotNull final UsageTarget[] searchFor, + @NotNull final Factory searcherFactory, + @NotNull final UsageViewPresentation presentation, + @NotNull final FindUsagesProcessPresentation processPresentation, + @Nullable final UsageViewStateListener listener, + @NotNull final SearchScope searchScope) { final AtomicReference usageViewRef = new AtomicReference(); + Task.Backgroundable task = new Task.Backgroundable(myProject, getProgressTitle(presentation), true, new SearchInBackgroundOption()) { @Override public void run(@NotNull final ProgressIndicator indicator) { - new SearchForUsagesRunnable(UsageViewManagerImpl.this.myProject, usageViewRef, presentation, searchFor, searcherFactory, - processPresentation, listener).run(); + new SearchForUsagesRunnable(UsageViewManagerImpl.this, UsageViewManagerImpl.this.myProject, usageViewRef, presentation, searchFor, searcherFactory, + processPresentation, searchScope, listener).run(); } + @NotNull @Override public DumbModeAction getDumbModeAction() { return DumbModeAction.CANCEL; @@ -170,6 +158,22 @@ public class UsageViewManagerImpl extends UsageViewManager { return usageViewRef.get(); } + @NotNull + private SearchScope getSearchScope(@NotNull UsageTarget[] searchFor) { + UsageTarget target = searchFor[0]; + if (target instanceof TypeSafeDataProvider) { + final SearchScope[] scope = new SearchScope[1]; + ((TypeSafeDataProvider)target).calcData(UsageView.USAGE_SCOPE, new DataSink() { + @Override + public void put(DataKey key, T data) { + scope[0] = (SearchScope)data; + } + }); + return scope[0]; + } + return GlobalSearchScope.projectScope(myProject); + } + @Override public void searchAndShowUsages(@NotNull UsageTarget[] searchFor, @NotNull Factory searcherFactory, @@ -197,7 +201,7 @@ public class UsageViewManagerImpl extends UsageViewManager { return StringUtil.escapeXml(result); } - private void showToolWindow(boolean activateWindow) { + void showToolWindow(boolean activateWindow) { ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.FIND); toolWindow.show(null); if (activateWindow && !toolWindow.isActive()) { @@ -238,304 +242,6 @@ public class UsageViewManagerImpl extends UsageViewManager { }); } - private class SearchForUsagesRunnable implements Runnable { - private final AtomicInteger myUsageCountWithoutDefinition = new AtomicInteger(0); - private final AtomicReference myFirstUsage = new AtomicReference(); - @NotNull - private final Project myProject; - private final AtomicReference myUsageViewRef; - private final UsageViewPresentation myPresentation; - private final UsageTarget[] mySearchFor; - private final Factory mySearcherFactory; - private final FindUsagesProcessPresentation myProcessPresentation; - private final UsageViewStateListener myListener; - - private SearchForUsagesRunnable(@NotNull Project project, - @NotNull AtomicReference usageViewRef, - @NotNull UsageViewPresentation presentation, - @NotNull UsageTarget[] searchFor, - @NotNull Factory searcherFactory, - @NotNull FindUsagesProcessPresentation processPresentation, - @Nullable UsageViewStateListener listener) { - myProject = project; - myUsageViewRef = usageViewRef; - myPresentation = presentation; - mySearchFor = searchFor; - mySearcherFactory = searcherFactory; - myProcessPresentation = processPresentation; - myListener = listener; - } - - private UsageViewImpl getUsageView(ProgressIndicator indicator) { - UsageViewImpl usageView = myUsageViewRef.get(); - if (usageView != null) return usageView; - int usageCount = myUsageCountWithoutDefinition.get(); - if (usageCount >= 2 || usageCount == 1 && myProcessPresentation.isShowPanelIfOnlyOneUsage()) { - usageView = new UsageViewImpl(myProject, myPresentation, mySearchFor, mySearcherFactory); - usageView.associateProgress(indicator); - if (myUsageViewRef.compareAndSet(null, usageView)) { - openView(usageView); - final Usage firstUsage = myFirstUsage.get(); - if (firstUsage != null) { - final UsageViewImpl finalUsageView = usageView; - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - finalUsageView.appendUsage(firstUsage); - } - }); - } - } - else { - Disposer.dispose(usageView); - } - return myUsageViewRef.get(); - } - return null; - } - - private void openView(@NotNull final UsageViewImpl usageView) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (myProject.isDisposed()) return; - addContent(usageView, myPresentation); - if (myListener != null) { - myListener.usageViewCreated(usageView); - } - showToolWindow(false); - } - }); - } - - @Override - public void run() { - AtomicBoolean findUsagesStartedShown = new AtomicBoolean(); - searchUsages(findUsagesStartedShown); - endSearchForUsages(findUsagesStartedShown); - } - - private void searchUsages(@NotNull final AtomicBoolean findStartedBalloonShown) { - ProgressIndicator indicator = ProgressWrapper.unwrap(ProgressManager.getInstance().getProgressIndicator()); - TooManyUsagesStatus.createFor(indicator); - Alarm findUsagesStartedBalloon = new Alarm(); - findUsagesStartedBalloon.addRequest(new Runnable() { - @Override - public void run() { - notifyByFindBalloon(null, MessageType.WARNING, myProcessPresentation, UsageViewManagerImpl.this.myProject, - getProgressTitle(myPresentation)); - findStartedBalloonShown.set(true); - } - }, 300, ModalityState.NON_MODAL); - UsageSearcher usageSearcher = mySearcherFactory.create(); - - usageSearcher.generate(new Processor() { - @Override - public boolean process(final Usage usage) { - ProgressIndicator indicator = ProgressWrapper.unwrap(ProgressManager.getInstance().getProgressIndicator()); - if (indicator != null && indicator.isCanceled()) return false; - TooManyUsagesStatus tooManyUsagesStatus = TooManyUsagesStatus.getFrom(indicator); - boolean incrementCounter = !isSelfUsage(usage, mySearchFor); - - if (incrementCounter) { - final int usageCount = myUsageCountWithoutDefinition.incrementAndGet(); - if (usageCount == 1 && !myProcessPresentation.isShowPanelIfOnlyOneUsage()) { - myFirstUsage.compareAndSet(null, usage); - } - - final UsageViewImpl usageView = getUsageView(indicator); - - if (usageCount > UsageLimitUtil.USAGES_LIMIT && tooManyUsagesStatus.switchTooManyUsagesStatus()) { - showTooManyUsagesWarning(myProject, tooManyUsagesStatus, indicator, myPresentation, usageCount, usageView); - } - - if (usageView != null) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - usageView.appendUsage(usage); - } - }); - } - } - return indicator == null || !indicator.isCanceled(); - } - }); - if (getUsageView(indicator) != null) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - showToolWindow(true); - } - }, myProject.getDisposed()); - } - Disposer.dispose(findUsagesStartedBalloon); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (findStartedBalloonShown.get()) { - Balloon balloon = ToolWindowManager.getInstance(myProject).getToolWindowBalloon(ToolWindowId.FIND); - if (balloon != null) { - balloon.hide(); - } - } - } - }, myProject.getDisposed()); - } - - private void endSearchForUsages(@NotNull final AtomicBoolean findStartedBalloonShown) { - assert !ApplicationManager.getApplication().isDispatchThread() : Thread.currentThread(); - int usageCount = myUsageCountWithoutDefinition.get(); - if (usageCount == 0 && myProcessPresentation.isShowNotFoundMessage()) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - final List notFoundActions = myProcessPresentation.getNotFoundActions(); - final String message = UsageViewBundle.message("dialog.no.usages.found.in", - StringUtil.decapitalize(myPresentation.getUsagesString()), - myPresentation.getScopeText()); - - if (notFoundActions.isEmpty()) { - String[] lines = myProcessPresentation.isShowFindOptionsPrompt() ? new String[] {StringUtil.escapeXml(message), createOptionsHtml(mySearchFor)} : new String[]{StringUtil.escapeXml(message)}; - notifyByFindBalloon(createGotToOptionsListener(mySearchFor), - MessageType.INFO, myProcessPresentation, UsageViewManagerImpl.this.myProject, lines); - findStartedBalloonShown.set(false); - } - else { - List titles = new ArrayList(notFoundActions.size() + 1); - titles.add(UsageViewBundle.message("dialog.button.ok")); - for (Action action : notFoundActions) { - Object value = action.getValue(FindUsagesProcessPresentation.NAME_WITH_MNEMONIC_KEY); - if (value == null) value = action.getValue(Action.NAME); - - titles.add((String)value); - } - - int option = Messages.showDialog(myProject, message, UsageViewBundle.message("dialog.title.information"), - ArrayUtil.toStringArray(titles), 0, Messages.getInformationIcon()); - - if (option > 0) { - notFoundActions.get(option - 1).actionPerformed(new ActionEvent(this, 0, titles.get(option))); - } - } - } - }, ModalityState.NON_MODAL, myProject.getDisposed()); - } - else if (usageCount == 1 && !myProcessPresentation.isShowPanelIfOnlyOneUsage()) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - Usage usage = myFirstUsage.get(); - if (usage.canNavigate()) { - usage.navigate(true); - flashUsageScriptaculously(usage); - } - notifyByFindBalloon(createGotToOptionsListener(mySearchFor), - MessageType.INFO, myProcessPresentation, UsageViewManagerImpl.this.myProject,"Only one usage found.", createOptionsHtml( - mySearchFor)); - } - }, ModalityState.NON_MODAL, myProject.getDisposed()); - } - else { - final UsageViewImpl usageView = myUsageViewRef.get(); - if (usageView != null) { - usageView.drainQueuedUsageNodes(); - usageView.setSearchInProgress(false); - } - if (!myProcessPresentation.getLargeFiles().isEmpty()) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - notifyByFindBalloon(null, MessageType.INFO, myProcessPresentation, UsageViewManagerImpl.this.myProject); - } - }, ModalityState.NON_MODAL, myProject.getDisposed()); - } - } - - if (myListener != null) { - myListener.findingUsagesFinished(myUsageViewRef.get()); - } - } - } - - private static void notifyByFindBalloon(final HyperlinkListener listener, - @NotNull final MessageType info, - @NotNull FindUsagesProcessPresentation processPresentation, - @NotNull final Project project, - @NotNull String... sLines) { - com.intellij.usageView.UsageViewManager.getInstance(project); // in case tool window not registered - - final List lines = new ArrayList(Arrays.asList(sLines)); - final Collection largeFiles = processPresentation.getLargeFiles(); - List resultLines = new ArrayList(lines); - HyperlinkListener resultListener = listener; - if (!largeFiles.isEmpty()) { - String shortMessage = "(" - + UsageViewBundle.message("large.files.were.ignored", largeFiles.size()) + ")"; - - resultLines.add(shortMessage); - resultListener = new HyperlinkAdapter(){ - @Override - protected void hyperlinkActivated(HyperlinkEvent e) { - if (e.getDescription().equals(LARGE_FILES_HREF_TARGET)) { - String detailedMessage = detailedLargeFilesMessage(largeFiles); - List strings = new ArrayList(lines); - strings.add(detailedMessage); - ToolWindowManager.getInstance(project).notifyByBalloon(ToolWindowId.FIND, info, wrapInHtml(strings), AllIcons.Actions.Find, listener); - } - else if (listener != null) { - listener.hyperlinkUpdate(e); - } - } - }; - } - - ToolWindowManager.getInstance(project).notifyByBalloon(ToolWindowId.FIND, info, wrapInHtml(resultLines), AllIcons.Actions.Find, resultListener); - } - - @NotNull - private static String wrapInHtml(@NotNull List strings) { - return XmlStringUtil.wrapInHtml(StringUtil.join(strings, "
")); - } - - @NotNull - private static String detailedLargeFilesMessage(@NotNull Collection largeFiles) { - String message = ""; - if (largeFiles.size() == 1) { - final VirtualFile vFile = largeFiles.iterator().next().getVirtualFile(); - message += "File " + presentableFileInfo(vFile) + " is "; - } - else { - message += "Files
"; - - int counter = 0; - for (PsiFile file : largeFiles) { - final VirtualFile vFile = file.getVirtualFile(); - message += presentableFileInfo(vFile) + "
"; - if (counter++ > 10) break; - } - - message += "are "; - } - - message += "too large and cannot be scanned"; - return message; - } - - @NotNull - private static String presentableFileInfo(@NotNull VirtualFile vFile) { - return getPresentablePath(vFile) - + " (" - + presentableSize(getFileLength(vFile)) - + ")"; - } - - @NotNull - public static String presentableSize(long bytes) { - long megabytes = bytes / (1024 * 1024); - return UsageViewBundle.message("find.file.size.megabytes", Long.toString(megabytes)); - } - public static long getFileLength(@NotNull final VirtualFile virtualFile) { final long[] length = {-1L}; ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -550,52 +256,22 @@ public class UsageViewManagerImpl extends UsageViewManager { } @NotNull - private static String getPresentablePath(@NotNull final VirtualFile virtualFile) { - return "'" + ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return virtualFile.getPresentableUrl(); - } - }) + "'"; + public static String presentableSize(long bytes) { + long megabytes = bytes / (1024 * 1024); + return UsageViewBundle.message("find.file.size.megabytes", Long.toString(megabytes)); + } + + public static boolean isInScope(@NotNull Usage usage, @NotNull SearchScope searchScope) { + VirtualFile file = usage instanceof UsageInFile ? ((UsageInFile)usage).getFile() : usage instanceof PsiElementUsage ? PsiUtilCore + .getVirtualFile(((PsiElementUsage)usage).getElement()) : null; + return file != null && (searchScope instanceof LocalSearchScope + ? ((LocalSearchScope)searchScope).isInScope(file) : ((GlobalSearchScope)searchScope).contains(file)); } @NotNull - private HyperlinkListener createGotToOptionsListener(@NotNull final UsageTarget[] targets) { - return new HyperlinkAdapter() { - @Override - protected void hyperlinkActivated(HyperlinkEvent e) { - if (e.getDescription().equals(FIND_OPTIONS_HREF_TARGET)) { - FindManager.getInstance(myProject).showSettingsAndFindUsages(targets); - } - } - }; + public static String outOfScopeMessage(int nUsages, @NotNull SearchScope searchScope) { + return (nUsages == 1 ? "One usage is" : nUsages + " usages are") + + " out of scope '"+ searchScope.getDisplayName()+"'"; } - @NotNull - private static String createOptionsHtml(@NonNls UsageTarget[] searchFor) { - KeyboardShortcut shortcut = UsageViewImpl.getShowUsagesWithSettingsShortcut(searchFor); - String shortcutText = ""; - if (shortcut != null) { - shortcutText = " (" + KeymapUtil.getShortcutText(shortcut) + ")"; - } - return "Find Options..." + shortcutText; - } - - private static void flashUsageScriptaculously(@NotNull final Usage usage) { - if (!(usage instanceof UsageInfo2UsageAdapter)) { - return; - } - UsageInfo2UsageAdapter usageInfo = (UsageInfo2UsageAdapter)usage; - - Editor editor = usageInfo.openTextEditor(true); - if (editor == null) return; - TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.BLINKING_HIGHLIGHTS_ATTRIBUTES); - - RangeBlinker rangeBlinker = new RangeBlinker(editor, attributes, 6); - List segments = new ArrayList(); - CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(segments); - usageInfo.processRangeMarkers(processor); - rangeBlinker.resetMarkers(segments); - rangeBlinker.startBlinking(); - } } diff --git a/platform/util/src/com/intellij/Patches.java b/platform/util/src/com/intellij/Patches.java index ec9a3bee1650..edbf8e2b8496 100644 --- a/platform/util/src/com/intellij/Patches.java +++ b/platform/util/src/com/intellij/Patches.java @@ -147,8 +147,8 @@ public class Patches { public static final boolean USE_REFLECTION_TO_ACCESS_JDK7 = true; /** - * Marker field to find all usages of workarounds - * which will not be needed when migrated to JDK 8+ + * AtomicIntegerFieldUpdater does not work when SecurityManager is installed + * fixed in JDK8 */ - public static final boolean HACK_USED_WHICH_IS_FIXED_IN_JDK8 = true; + public static final boolean JDK_BUG_ID_7103570 = true; } diff --git a/platform/util/src/com/intellij/ui/EngravedLabel.java b/platform/util/src/com/intellij/ui/EngravedLabel.java index fdf098a52c4b..ec4fc8afef8c 100644 --- a/platform/util/src/com/intellij/ui/EngravedLabel.java +++ b/platform/util/src/com/intellij/ui/EngravedLabel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -28,6 +28,8 @@ import java.awt.*; * @author max */ public class EngravedLabel extends JLabel { + private Color myShadowColor = EngravedTextGraphics.SHADOW_COLOR; + public EngravedLabel(String text) { super(text); setOpaque(false); @@ -38,7 +40,18 @@ public class EngravedLabel extends JLabel { } @Override - protected void paintComponent(Graphics g) { - super.paintComponent(UIUtil.isUnderDarcula() ? g : new EngravedTextGraphics((Graphics2D)g)); + protected void paintComponent(Graphics graphics) { + if (!UIUtil.isUnderDarcula()) { + graphics = new EngravedTextGraphics((Graphics2D)graphics, 0, 1, getShadowColor()); + } + super.paintComponent(graphics); + } + + public Color getShadowColor() { + return myShadowColor == null ? EngravedTextGraphics.SHADOW_COLOR : myShadowColor; + } + + public void setShadowColor(Color shadowColor) { + myShadowColor = shadowColor; } } diff --git a/platform/util/src/com/intellij/ui/EngravedTextGraphics.java b/platform/util/src/com/intellij/ui/EngravedTextGraphics.java index f47072f423a8..916bde842c3b 100644 --- a/platform/util/src/com/intellij/ui/EngravedTextGraphics.java +++ b/platform/util/src/com/intellij/ui/EngravedTextGraphics.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -25,7 +25,7 @@ import java.awt.*; import java.text.AttributedCharacterIterator; public class EngravedTextGraphics extends Graphics2DDelegate { - private static final Color SHADOW_COLOR = Gray._250.withAlpha(140); + public static final Color SHADOW_COLOR = Gray._250.withAlpha(140); private static final boolean ALLOW_ENGRAVEMENT = SystemInfo.isMac; private Color myShadowColor; private int myXOffset; diff --git a/platform/util/src/com/intellij/util/lang/JarLoader.java b/platform/util/src/com/intellij/util/lang/JarLoader.java index a088275a7913..15556a8d0a52 100644 --- a/platform/util/src/com/intellij/util/lang/JarLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarLoader.java @@ -58,8 +58,17 @@ class JarLoader extends Loader { } void preloadClasses() { + ZipFile zipFile; + + try { + zipFile = acquireZipFile(); + } + catch (Exception e) { + LOG.debug("url: " + myURL, e); + return; + } + try { - ZipFile zipFile = acquireZipFile(); try { JarMemoryLoader loader = JarMemoryLoader.load(zipFile, getBaseURL()); if (loader != null) { diff --git a/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java b/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java index 31a4127080eb..7345bfaf7e3b 100644 --- a/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java +++ b/platform/util/testSrc/com/intellij/util/containers/ConcurrentMapsTest.java @@ -17,15 +17,18 @@ package com.intellij.util.containers; import com.intellij.openapi.util.text.StringUtil; import gnu.trove.TObjectHashingStrategy; -import junit.framework.TestCase; +import org.junit.Test; import java.lang.ref.SoftReference; import java.util.List; import java.util.Map; import java.util.Set; -public class ConcurrentMapsTest extends TestCase { - public static final TObjectHashingStrategy CUSTOM_STRATEGY = new TObjectHashingStrategy() { +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ConcurrentMapsTest { + private static final TObjectHashingStrategy CUSTOM_STRATEGY = new TObjectHashingStrategy() { @Override public int computeHashCode(String object) { return Character.toLowerCase(object.charAt(object.length() - 1)); @@ -37,8 +40,9 @@ public class ConcurrentMapsTest extends TestCase { } }; + @Test public void testKeysRemovedWhenIdentityStrategyIsUsed() { - ConcurrentWeakHashMap map = new ConcurrentWeakHashMap(TObjectHashingStrategy.IDENTITY); + @SuppressWarnings("unchecked") ConcurrentWeakHashMap map = new ConcurrentWeakHashMap(TObjectHashingStrategy.IDENTITY); map.put(new Object(), new Object()); tryGcSoftlyReachableObjects(); // sometimes weak references are not collected under linux, try to stress gc to force them @@ -50,6 +54,7 @@ public class ConcurrentMapsTest extends TestCase { assertEquals(1, map.underlyingMapSize()); } + @Test public void testRemoveFromSoftEntrySet() { ConcurrentSoftHashMap map = new ConcurrentSoftHashMap(); map.put(this, this); @@ -61,6 +66,7 @@ public class ConcurrentMapsTest extends TestCase { assertTrue(map.isEmpty()); } + @Test public void testRemoveFromWeakEntrySet() { ConcurrentWeakHashMap map = new ConcurrentWeakHashMap(); map.put(this, this); @@ -72,6 +78,7 @@ public class ConcurrentMapsTest extends TestCase { assertTrue(map.isEmpty()); } + @Test public void testTossedWeakKeysAreRemoved() { ConcurrentWeakHashMap map = new ConcurrentWeakHashMap(); map.put(new Object(), new Object()); @@ -87,13 +94,14 @@ public class ConcurrentMapsTest extends TestCase { } public static void tryGcSoftlyReachableObjects() { - SoftReference reference = new SoftReference(new Object()); + SoftReference reference = new SoftReference(new Object()); List list = ContainerUtil.newArrayList(); while (reference.get() != null) { list.add(new SoftReference(new byte[(int)Runtime.getRuntime().freeMemory() / 2])); } } + @Test public void testTossedSoftKeysAreRemoved() { ConcurrentSoftHashMap map = new ConcurrentSoftHashMap(); map.put(new Object(), new Object()); @@ -108,6 +116,7 @@ public class ConcurrentMapsTest extends TestCase { assertEquals(1, map.underlyingMapSize()); } + @Test public void testTossedWeakValueIsRemoved() { ConcurrentWeakValueHashMap map = new ConcurrentWeakValueHashMap(); map.put(new Object(), new Object()); @@ -121,6 +130,7 @@ public class ConcurrentMapsTest extends TestCase { map.put(this, this); assertEquals(1, map.underlyingMapSize()); } + @Test public void testTossedSoftValueIsRemoved() { ConcurrentSoftValueHashMap map = new ConcurrentSoftValueHashMap(); map.put(new Object(), new Object()); @@ -135,6 +145,7 @@ public class ConcurrentMapsTest extends TestCase { assertEquals(1, map.underlyingMapSize()); } + @Test public void testCustomStrategy() { SoftHashMap map = new SoftHashMap(CUSTOM_STRATEGY); @@ -145,6 +156,7 @@ public class ConcurrentMapsTest extends TestCase { assertTrue(map.isEmpty()); } + @Test public void testCustomStrategyForConcurrentSoft() { ConcurrentSoftHashMap map = new ConcurrentSoftHashMap(CUSTOM_STRATEGY); @@ -156,6 +168,7 @@ public class ConcurrentMapsTest extends TestCase { assertTrue(map.isEmpty()); } + @Test public void testCustomStrategyForConcurrentWeakSoft() { ConcurrentWeakKeySoftValueHashMap map = new ConcurrentWeakKeySoftValueHashMap(1,1,1,CUSTOM_STRATEGY); @@ -167,6 +180,7 @@ public class ConcurrentMapsTest extends TestCase { assertTrue(map.isEmpty()); } + @Test public void testTossedSoftKeyAndValue() { SoftKeySoftValueHashMap map = new SoftKeySoftValueHashMap(); map.put(new Object(), new Object()); @@ -179,6 +193,7 @@ public class ConcurrentMapsTest extends TestCase { assertTrue(map.isEmpty()); } + @Test public void testTossedWeakKeyAndValue() { WeakKeyWeakValueHashMap map = new WeakKeyWeakValueHashMap(); map.put(new Object(), new Object()); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java index d8b931009f43..8b9884502755 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java @@ -173,14 +173,12 @@ public class LineStatusTracker { @SuppressWarnings({"AutoBoxing"}) private RangeHighlighter createHighlighter(final Range range) { LOG.assertTrue(!myReleased, "Already released"); - int first = - range.getOffset1() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset1()); - int second = - range.getOffset2() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset2()); + TextRange textRange = getCurrentTextRange(range); final RangeHighlighter highlighter = DocumentMarkupModel.forDocument(myDocument, myProject, true) - .addRangeHighlighter(first, second, HighlighterLayer.FIRST - 1, null, HighlighterTargetArea.LINES_IN_RANGE); + .addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.FIRST - 1, null, + HighlighterTargetArea.LINES_IN_RANGE); final TextAttributes attr = LineStatusTrackerDrawing.getAttributesFor(range); highlighter.setErrorStripeMarkColor(attr.getErrorStripeColor()); highlighter.setThinErrorStripeMark(true); @@ -188,14 +186,21 @@ public class LineStatusTracker { highlighter.setGreedyToRight(true); highlighter.setLineMarkerRenderer(LineStatusTrackerDrawing.createRenderer(range, this)); highlighter.setEditorFilter(MarkupEditorFilterFactory.createIsNotDiffFilter()); - final int line1 = myDocument.getLineNumber(first); - final int line2 = myDocument.getLineNumber(second); + final String tooltip; - if (line1 == line2) { - tooltip = VcsBundle.message("tooltip.text.line.changed", line1); + if (range.getOffset1() == range.getOffset2()) { + if (range.getUOffset1() + 1 == range.getUOffset2()) { + tooltip = VcsBundle.message("tooltip.text.line.before.deleted", range.getOffset1() + 1); + } + else { + tooltip = VcsBundle.message("tooltip.text.lines.before.deleted", range.getOffset1() + 1, range.getUOffset2() - range.getUOffset1()); + } + } + else if (range.getOffset1() + 1 == range.getOffset2()) { + tooltip = VcsBundle.message("tooltip.text.line.changed", range.getOffset1() + 1); } else { - tooltip = VcsBundle.message("tooltip.text.lines.changed", line1, line2); + tooltip = VcsBundle.message("tooltip.text.lines.changed", range.getOffset1() + 1, range.getOffset2()); } highlighter.setErrorStripeTooltip(tooltip); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java index ef0b72008cf7..ce349fccc292 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java @@ -168,9 +168,9 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { } DefaultActionGroup leftToolbar = new DefaultActionGroup(); - final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance(); + final Executor debugExecutor = DefaultDebugExecutor.getDebugExecutorInstance(); if (runner != null && environment != null) { - RestartAction restartAction = new RestartAction(executor, runner, myRunContentDescriptor, environment); + RestartAction restartAction = new RestartAction(debugExecutor, runner, myRunContentDescriptor, environment); leftToolbar.add(restartAction); restartAction.registerShortcut(myUi.getComponent()); @@ -221,8 +221,8 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { leftToolbar.addSeparator(); leftToolbar.add(PinToolwindowTabAction.getPinAction()); - leftToolbar.add(new CloseAction(executor, myRunContentDescriptor, getProject())); - leftToolbar.add(new ContextHelpAction(executor.getHelpId())); + leftToolbar.add(new CloseAction(environment != null ? environment.getExecutor() : debugExecutor, myRunContentDescriptor, getProject())); + leftToolbar.add(new ContextHelpAction(debugExecutor.getHelpId())); DefaultActionGroup topToolbar = new DefaultActionGroup(); topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP)); diff --git a/plugins/coverage-common/lib/coverage-agent.jar b/plugins/coverage-common/lib/coverage-agent.jar index 0fbfdbefffeb..2f403fef94ba 100644 Binary files a/plugins/coverage-common/lib/coverage-agent.jar and b/plugins/coverage-common/lib/coverage-agent.jar differ diff --git a/plugins/coverage-common/lib/coverage-src.zip b/plugins/coverage-common/lib/coverage-src.zip index 437732eb20e2..c094a3a80b9d 100644 Binary files a/plugins/coverage-common/lib/coverage-src.zip and b/plugins/coverage-common/lib/coverage-src.zip differ diff --git a/plugins/coverage-common/lib/coverage-util.jar b/plugins/coverage-common/lib/coverage-util.jar index 0260a9abc169..dbfe4a3786ec 100644 Binary files a/plugins/coverage-common/lib/coverage-util.jar and b/plugins/coverage-common/lib/coverage-util.jar differ diff --git a/plugins/coverage-common/lib/instrumenter.jar b/plugins/coverage-common/lib/instrumenter.jar index 8552a4fe9390..54953f575420 100644 Binary files a/plugins/coverage-common/lib/instrumenter.jar and b/plugins/coverage-common/lib/instrumenter.jar differ diff --git a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java index baf835820ddb..f1ab1d476c6f 100644 --- a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java +++ b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java @@ -22,6 +22,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.committed.DecoratorManager; import com.intellij.openapi.vcs.changes.committed.VcsCommittedListsZipper; import com.intellij.openapi.vcs.changes.committed.VcsCommittedViewAuxiliary; @@ -197,7 +198,14 @@ public class GitCommittedChangeListProvider implements CommittedChangesProvider< final Collection changes = commit.getChanges(); if (changes.size() == 1) { - return Pair.create(commit, changes.iterator().next().getAfterRevision().getFile()); + Change change = changes.iterator().next(); + ContentRevision revision = change.getAfterRevision(); + if (revision == null) { + revision = change.getBeforeRevision(); + } + assert revision != null : "Revision can't be null in " + change; + FilePath filePathInRevision = revision.getFile(); + return Pair.create(commit, filePathInRevision); } for (Change change : changes) { if (change.getAfterRevision() != null && FileUtil.filesEqual(filePath.getIOFile(), change.getAfterRevision().getFile().getIOFile())) { diff --git a/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy b/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy index 982bcb7d7fec..5ccf63eaa7c2 100644 --- a/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy +++ b/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy @@ -205,7 +205,7 @@ class GitBranchWorkerTest extends GitPlatformTest { boolean notificationShown = false; checkoutOrMerge operation, "feature", [ - showUntrackedFilesNotification : { String s, Collection c -> notificationShown = true } + showUntrackedFilesNotification: { String s, VirtualFile root, Collection c -> notificationShown = true } ] assertTrue "Untracked files notification was not shown", notificationShown @@ -224,15 +224,15 @@ class GitBranchWorkerTest extends GitPlatformTest { def untracked = ["untracked.txt"] untrackedFileOverwrittenBy(myCommunity, "feature", untracked) - Collection untrackedFiles = null; + Collection untrackedPaths = null; checkoutOrMerge operation, "feature", [ - showUntrackedFilesDialogWithRollback : { String s, String p, Collection files -> untrackedFiles = files; false } + showUntrackedFilesDialogWithRollback: { + String s, String p, VirtualFile root, Collection files -> untrackedPaths = files; false + } ] - assertTrue "Untracked files dialog was not shown", untrackedFiles != null - assertEquals "Incorrect set of untracked files was shown in the dialog", - untracked, - untrackedFiles.collect { FileUtil.getRelativePath(myCommunity.root.path, it.path, '/'.toCharacter()) } + assertTrue "Untracked files dialog was not shown", untrackedPaths != null + assertEquals "Incorrect set of untracked files was shown in the dialog", untracked.asList(), untrackedPaths.asList() } public void "test checkout with local changes overwritten by checkout should show smart checkout dialog"() { @@ -252,7 +252,7 @@ class GitBranchWorkerTest extends GitPlatformTest { List changes = null; checkoutOrMerge(operation, "feature", [ - showSmartOperationDialog: { Project p, List cs, String op, boolean force -> + showSmartOperationDialog: { Project p, List cs, Collection paths, String op, boolean force -> changes = cs DialogWrapper.CANCEL_EXIT_CODE } @@ -349,7 +349,9 @@ class GitBranchWorkerTest extends GitPlatformTest { prepareLocalChangesOverwrittenBy(myUltimate) checkoutOrMerge(operation, "feature", [ - showSmartOperationDialog : { Project p, List cs, String op, boolean f -> GitSmartOperationDialog.CANCEL_EXIT_CODE }, + showSmartOperationDialog: { Project p, List cs, Collection paths, String op, boolean f + -> GitSmartOperationDialog.CANCEL_EXIT_CODE + }, ] as GitBranchUiHandler ) assertNull "Notification was unexpectedly shown:" + myVcsNotifier.lastNotification, myVcsNotifier.lastNotification @@ -372,8 +374,10 @@ class GitBranchWorkerTest extends GitPlatformTest { def rollbackMsg = null checkoutOrMerge(operation, "feature", [ - showSmartOperationDialog : { Project p, List cs, String op, boolean f -> GitSmartOperationDialog.CANCEL_EXIT_CODE }, - notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m ; false } + showSmartOperationDialog : { + Project p, List cs, Collection paths, String op, boolean f -> GitSmartOperationDialog.CANCEL_EXIT_CODE + }, + notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m; false } ] as GitBranchUiHandler ) assertNotNull "Rollback proposal was not shown", rollbackMsg @@ -384,7 +388,7 @@ class GitBranchWorkerTest extends GitPlatformTest { prepareLocalChangesOverwrittenBy(myUltimate) def uiHandler = [ - showSmartOperationDialog: { Project p, List cs, String op, boolean force -> + showSmartOperationDialog: { Project p, List cs, Collection paths, String op, boolean force -> GitSmartOperationDialog.FORCE_EXIT_CODE; }, ] as GitBranchUiHandler diff --git a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/inconsistentResourceBundle/InconsistentResourceBundleInspection.java b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/inconsistentResourceBundle/InconsistentResourceBundleInspection.java index 48f4b05adfb2..1e71e5d1ce50 100644 --- a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/inconsistentResourceBundle/InconsistentResourceBundleInspection.java +++ b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/inconsistentResourceBundle/InconsistentResourceBundleInspection.java @@ -144,7 +144,7 @@ public class InconsistentResourceBundleInspection extends GlobalSimpleInspection ResourceBundle resourceBundle = propertiesFile.getResourceBundle(); assert visitedBundles != null; if (!visitedBundles.add(resourceBundle)) return; - List files = resourceBundle.getPropertiesFiles(manager.getProject()); + List files = resourceBundle.getPropertiesFiles(); if (files.size() < 2) return; BidirectionalMap parents = new BidirectionalMap(); for (PropertiesFile f : files) { diff --git a/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties b/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties index 0864d1e31206..06e0c2e3ae03 100644 --- a/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties +++ b/plugins/properties/properties-psi-api/resources/messages/PropertiesBundle.properties @@ -4,7 +4,9 @@ properties.files.file.type.description=Properties files remove.property.intention.text=Remove property rename.bundle.enter.new.resource.bundle.base.name.prompt.text=Enter new resource bundle base name rename.bundle.enter.new.resource.bundle.key.name.prompt.text=Enter new resource bundle key name +rename.bundle.enter.new.resource.bundle.section.name.prompt.text=Enter new resource bundle section name rename.resource.bundle.dialog.title=Rename Resource Bundle +rename.resource.bundle.section.dialog.title=Rename Resource Bundle Section rename.resource.bundle.key.dialog.title=Rename Resource Bundle Key properties.files.inspection.group.display.name=Properties Files unused.property.inspection.display.name=Unused Property @@ -40,4 +42,12 @@ options.properties.attribute.descriptor.property.value=Property value options.properties.attribute.descriptor.key.value.separator=Key/value separator options.properties.attribute.descriptor.comment=Comment options.properties.attribute.descriptor.valid.string.escape=Valid string escape -options.properties.attribute.descriptor.invalid.string.escape=Invalid string escape \ No newline at end of file +options.properties.attribute.descriptor.invalid.string.escape=Invalid string escape + +new.property.dialog.title=New Property Key +new.property.dialog.name.prompt.text=Enter new property key name + +resource.bundle.renamer=Rename resource bundle properties files +resource.bundle.renamer.dialog.description=Rename resource bundle properties files with the following names to: +resource.bundle.renamer.entity.name=Resource bundle +resource.bundle.renamer.option=Rename bound &resource bundle \ No newline at end of file diff --git a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/EmptyResourceBundle.java b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/EmptyResourceBundle.java index 17858a2ca77d..a343ad34b477 100644 --- a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/EmptyResourceBundle.java +++ b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/EmptyResourceBundle.java @@ -18,6 +18,7 @@ package com.intellij.lang.properties; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDirectory; import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -31,15 +32,29 @@ public class EmptyResourceBundle { private static class Holder { private static final ResourceBundle NULL = new ResourceBundle() { @NotNull - public List getPropertiesFiles(final Project project) { + @Override + public List getPropertiesFiles() { return Collections.emptyList(); } @NotNull - public PropertiesFile getDefaultPropertiesFile(final Project project) { + @Override + public List getPropertiesFiles(final Project project) { + return getPropertiesFiles(); + } + + @NotNull + @Override + public PropertiesFile getDefaultPropertiesFile() { throw new IllegalStateException(); } + @NotNull + @Override + public PropertiesFile getDefaultPropertiesFile(final Project project) { + return getDefaultPropertiesFile(); + } + @NotNull public String getBaseName() { return ""; @@ -49,6 +64,12 @@ public class EmptyResourceBundle { public VirtualFile getBaseDirectory() { throw new IllegalStateException(); } + + @NotNull + @Override + public Project getProject() { + throw new IllegalStateException(); + } }; } public static ResourceBundle getInstance() { diff --git a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesUtil.java b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesUtil.java index abc281f7ea82..9ccd7569aba3 100644 --- a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesUtil.java +++ b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesUtil.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,8 +42,16 @@ public class PropertiesUtil { private final static Locale DEFAULT_LOCALE = new Locale("", "", ""); - public static boolean isPropertyComplete(final Project project, ResourceBundle resourceBundle, String propertyName) { - List propertiesFiles = resourceBundle.getPropertiesFiles(project); + /** + * @deprecated use PropertiesUtil.isPropertyComplete(ResourceBundle resourceBundle, String propertyName) + */ + @Deprecated + public static boolean isPropertyComplete(final Project project, final ResourceBundle resourceBundle, final String propertyName) { + return isPropertyComplete(resourceBundle, propertyName); + } + + public static boolean isPropertyComplete(final ResourceBundle resourceBundle, final String propertyName) { + List propertiesFiles = resourceBundle.getPropertiesFiles(); for (PropertiesFile propertiesFile : propertiesFiles) { if (propertiesFile.findPropertyByKey(propertyName) == null) return false; } @@ -50,8 +59,13 @@ public class PropertiesUtil { } @NotNull - public static String getBaseName(@NotNull VirtualFile virtualFile) { - String name = virtualFile.getName(); + public static String getBaseName(@NotNull PsiFile file) { + return getBaseName(file.getContainingFile().getVirtualFile()); + } + + @NotNull + public static String getBaseName(@NotNull VirtualFile file) { + final String name = file.getName(); final Matcher matcher = LOCALE_PATTERN.matcher(name); final String baseNameWithExtension; if (matcher.find()) { @@ -92,10 +106,10 @@ public class PropertiesUtil { } @Nullable - public static String getFullName(final PropertiesFile psiFile) { + public static String getFullName(final PropertiesFile propertiesFile) { return ApplicationManager.getApplication().runReadAction(new NullableComputable() { public String compute() { - PsiDirectory directory = psiFile.getParent(); + PsiDirectory directory = propertiesFile.getParent(); String packageQualifiedName = getPackageQualifiedName(directory); if (packageQualifiedName == null) { return null; @@ -104,9 +118,7 @@ public class PropertiesUtil { if (qName.length() > 0) { qName.append("."); } - final VirtualFile virtualFile = psiFile.getVirtualFile(); - assert virtualFile != null; - qName.append(getBaseName(virtualFile)); + qName.append(getBaseName(propertiesFile.getContainingFile())); return qName.toString(); } }); @@ -132,7 +144,7 @@ public class PropertiesUtil { @NotNull public static List findAllProperties(Project project, @NotNull ResourceBundle resourceBundle, String key) { List result = new SmartList(); - List propertiesFiles = resourceBundle.getPropertiesFiles(project); + List propertiesFiles = resourceBundle.getPropertiesFiles(); for (PropertiesFile propertiesFile : propertiesFiles) { result.addAll(propertiesFile.findPropertiesByKey(key)); } diff --git a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/ResourceBundle.java b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/ResourceBundle.java index d4a43a522b04..7ea56269949d 100644 --- a/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/ResourceBundle.java +++ b/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/ResourceBundle.java @@ -26,6 +26,7 @@ import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDirectory; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -33,9 +34,23 @@ import java.util.List; public abstract class ResourceBundle { public static final DataKey ARRAY_DATA_KEY = DataKey.create("resource.bundle.array"); + @NotNull + public abstract List getPropertiesFiles(); + + /** + * @deprecated, use getPropertiesFiles() instead this method + */ + @Deprecated @NotNull public abstract List getPropertiesFiles(final Project project); + @NotNull + public abstract PropertiesFile getDefaultPropertiesFile(); + + /** + * @deprecated, use getDefaultPropertiesFile() instead this method + */ + @Deprecated @NotNull public abstract PropertiesFile getDefaultPropertiesFile(final Project project); @@ -44,4 +59,7 @@ public abstract class ResourceBundle { @NotNull public abstract VirtualFile getBaseDirectory(); + + @NotNull + public abstract Project getProject(); } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/BundleNameEvaluator.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/BundleNameEvaluator.java index 43ffb513a4b2..ebe28b38b37e 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/BundleNameEvaluator.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/BundleNameEvaluator.java @@ -15,7 +15,6 @@ */ package com.intellij.lang.properties; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.Nullable; @@ -26,11 +25,6 @@ public interface BundleNameEvaluator { @Nullable public String evaluateBundleName(final PsiFile psiFile) { - final VirtualFile virtualFile = psiFile == null ? null : psiFile.getOriginalFile().getVirtualFile(); - if (virtualFile == null) { - return null; - } - final PsiDirectory directory = psiFile.getParent(); if (directory == null) { return null; @@ -43,7 +37,7 @@ public interface BundleNameEvaluator { if (qName.length() > 0) { qName.append("."); } - qName.append(PropertiesUtil.getBaseName(virtualFile)); + qName.append(PropertiesUtil.getBaseName(psiFile)); return qName.toString(); } return null; diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/PropertiesImplUtil.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/PropertiesImplUtil.java index 118437e942aa..5908219098b7 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/PropertiesImplUtil.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/PropertiesImplUtil.java @@ -23,44 +23,64 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author Konstantin Bulenkov */ public class PropertiesImplUtil extends PropertiesUtil { - public static ResourceBundle getResourceBundle(final PsiFile containingFile) { - VirtualFile virtualFile = containingFile.getVirtualFile(); - if (!containingFile.isValid() || virtualFile == null) { + + @Nullable + public static ResourceBundle getResourceBundle(@NotNull final PropertiesFile representative) { + final PsiFile containingFile = representative.getContainingFile(); + if (!containingFile.isValid()) { return EmptyResourceBundle.getInstance(); } - String baseName = getBaseName(virtualFile); - PsiDirectory directory = ApplicationManager.getApplication().runReadAction(new Computable() { + final String baseName = getBaseName(containingFile); + final PsiDirectory directory = ApplicationManager.getApplication().runReadAction(new Computable() { @Nullable public PsiDirectory compute() { return containingFile.getContainingDirectory(); }}); if (directory == null) return EmptyResourceBundle.getInstance(); - - return new ResourceBundleImpl(directory.getVirtualFile(), baseName); + return getResourceBundle(baseName, directory); } - public static boolean isPropertiesFile(VirtualFile file, Project project) { + @Nullable + private static ResourceBundle getResourceBundle(@NotNull final String baseName, @NotNull final PsiDirectory baseDirectory) { + PropertiesFile defaultPropertiesFile = null; + for (final PsiFile psiFile : baseDirectory.getFiles()) { + if (baseName.equals(getBaseName(psiFile))) { + final PropertiesFile propertiesFile = getPropertiesFile(psiFile); + if (propertiesFile != null) { + if (defaultPropertiesFile == null || defaultPropertiesFile.getName().compareTo(propertiesFile.getName()) < 0) { + defaultPropertiesFile = propertiesFile; + } + } + } + } + if (defaultPropertiesFile == null) { + return null; + } + return new ResourceBundleImpl(defaultPropertiesFile); + } + + public static boolean isPropertiesFile(@NotNull VirtualFile file, @NotNull Project project) { return getPropertiesFile(PsiManager.getInstance(project).findFile(file)) != null; } - public static boolean isPropertiesFile(PsiFile file) { + public static boolean isPropertiesFile(@Nullable PsiFile file) { return getPropertiesFile(file) != null; } @@ -70,8 +90,6 @@ public class PropertiesImplUtil extends PropertiesUtil { return file instanceof PropertiesFile ? (PropertiesFile)file : XmlPropertiesFileImpl.getPropertiesFile(file); } - - @NotNull public static List findPropertiesByKey(final Project project, final String key) { final GlobalSearchScope scope = GlobalSearchScope.allScope(project); @@ -94,4 +112,23 @@ public class PropertiesImplUtil extends PropertiesUtil { return properties; } + @Nullable + public static ResourceBundle createByUrl(final @NotNull String url, final @NotNull Project project) { + if (!url.startsWith(ResourceBundleImpl.RESOURCE_BUNDLE_PREFIX)) return null; + + final String defaultPropertiesUrl = url.substring(ResourceBundleImpl.RESOURCE_BUNDLE_PREFIX.length()); + final int idx = defaultPropertiesUrl.lastIndexOf('/'); + if (idx == -1) return null; + final String baseDirectoryName = defaultPropertiesUrl.substring(0, idx); + final String baseName = defaultPropertiesUrl.substring(idx + 1); + final VirtualFile baseDirectoryVirtualFile = VirtualFileManager.getInstance().findFileByUrl(baseDirectoryName); + if (baseDirectoryVirtualFile == null) { + return null; + } + final PsiFile baseDirectory = PsiManager.getInstance(project).findFile(baseDirectoryVirtualFile); + if (baseDirectory == null || !(baseDirectory instanceof PsiDirectory)) { + return null; + } + return getResourceBundle(baseName, (PsiDirectory)baseDirectory); + } } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/ResourceBundleImpl.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/ResourceBundleImpl.java index 1ac96a96f6af..d257824166e9 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/ResourceBundleImpl.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/ResourceBundleImpl.java @@ -23,36 +23,32 @@ import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; import com.intellij.util.SmartList; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.Comparator; import java.util.List; public class ResourceBundleImpl extends ResourceBundle { - @NonNls private static final String RESOURCE_BUNDLE_PREFIX = "resourceBundle:"; - @NotNull protected final VirtualFile myBaseDirectory; - @NotNull protected final String myBaseName; + @NonNls public static final String RESOURCE_BUNDLE_PREFIX = "resourceBundle:"; + @NotNull private final PropertiesFile myDefaultPropertiesFile; - public ResourceBundleImpl(@NotNull VirtualFile baseDirectory, @NotNull String baseName) { - myBaseDirectory = baseDirectory; - myBaseName = baseName; + public ResourceBundleImpl(@NotNull final PropertiesFile defaultPropertiesFile) { + myDefaultPropertiesFile = defaultPropertiesFile; } @NotNull - public List getPropertiesFiles(final Project project) { - VirtualFile[] children = myBaseDirectory.getChildren(); + @Override + public List getPropertiesFiles() { + PsiFile[] children = myDefaultPropertiesFile.getParent().getFiles(); + final String baseName = getBaseName(); List result = new SmartList(); - PsiManager psiManager = PsiManager.getInstance(project); - for (VirtualFile file : children) { - if (!file.isValid() || file.getExtension() == null) continue; - if (Comparing.strEqual(PropertiesUtil.getBaseName(file), myBaseName)) { - PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(psiManager.findFile(file)); + for (PsiFile file : children) { + if (!file.isValid() || file.getVirtualFile().getExtension() == null) continue; + if (Comparing.strEqual(PropertiesUtil.getBaseName(file), baseName)) { + PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(file); if (propertiesFile != null) { result.add(propertiesFile); } @@ -62,20 +58,38 @@ public class ResourceBundleImpl extends ResourceBundle { } @NotNull - public PropertiesFile getDefaultPropertiesFile(final Project project) { - List files = getPropertiesFiles(project); - // put default properties file first - ContainerUtil.quickSort(files, new Comparator() { - public int compare(final PropertiesFile o1, final PropertiesFile o2) { - return Comparing.compare(o1.getName(), o2.getName()); - } - }); - return files.get(0); + @Override + public List getPropertiesFiles(final Project project) { + return getPropertiesFiles(); } @NotNull + @Override + public PropertiesFile getDefaultPropertiesFile() { + return myDefaultPropertiesFile; + } + + @NotNull + @Override + public PropertiesFile getDefaultPropertiesFile(final Project project) { + return getDefaultPropertiesFile(); + } + + @NotNull + @Override public String getBaseName() { - return myBaseName; + return PropertiesUtil.getBaseName(myDefaultPropertiesFile.getContainingFile()); + } + + @NotNull + public VirtualFile getBaseDirectory() { + return myDefaultPropertiesFile.getParent().getVirtualFile(); + } + + @NotNull + @Override + public Project getProject() { + return myDefaultPropertiesFile.getProject(); } public boolean equals(final Object o) { @@ -83,42 +97,15 @@ public class ResourceBundleImpl extends ResourceBundle { if (o == null || getClass() != o.getClass()) return false; final ResourceBundleImpl resourceBundle = (ResourceBundleImpl)o; - - if (!myBaseDirectory.equals(resourceBundle.myBaseDirectory)) return false; - if (!myBaseName.equals(resourceBundle.myBaseName)) return false; - + if (!myDefaultPropertiesFile.equals(resourceBundle.myDefaultPropertiesFile)) return false; return true; } public int hashCode() { - int result = myBaseDirectory.hashCode(); - result = 29 * result + myBaseName.hashCode(); - return result; - } - - @Nullable - public static ResourceBundle createByUrl(String url) { - if (!url.startsWith(RESOURCE_BUNDLE_PREFIX)) return null; - - String defaultPropertiesUrl = url.substring(RESOURCE_BUNDLE_PREFIX.length()); - final int idx = defaultPropertiesUrl.lastIndexOf('/'); - if (idx == -1) return null; - String baseDir = defaultPropertiesUrl.substring(0, idx); - String baseName = defaultPropertiesUrl.substring(idx + 1); - VirtualFile baseDirectory = VirtualFileManager.getInstance().findFileByUrl(baseDir); - if (baseDirectory != null) { - return new ResourceBundleImpl(baseDirectory, baseName); - } - return null; + return myDefaultPropertiesFile.hashCode(); } public String getUrl() { - return RESOURCE_BUNDLE_PREFIX +getBaseDirectory() + "/" + getBaseName(); + return RESOURCE_BUNDLE_PREFIX + getBaseDirectory() + "/" + getBaseName(); } - - @NotNull - public VirtualFile getBaseDirectory() { - return myBaseDirectory; - } - } \ No newline at end of file diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java index f46d207dc935..36915e7e6962 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java @@ -19,31 +19,50 @@ */ package com.intellij.lang.properties.editor; +import com.intellij.ide.presentation.Presentation; +import com.intellij.lang.properties.PropertiesImplUtil; +import com.intellij.lang.properties.PropertiesUtil; import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileSystem; +import com.intellij.psi.PsiManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +@Presentation(icon = "AllIcons.Nodes.ResourceBundle") public class ResourceBundleAsVirtualFile extends VirtualFile { - private final ResourceBundle myResourceBundle; + private final VirtualFile myBasePropertiesFile; - public ResourceBundleAsVirtualFile(@NotNull ResourceBundle resourceBundle) { - myResourceBundle = resourceBundle; + private ResourceBundleAsVirtualFile(@NotNull final VirtualFile basePropertiesFile) { + myBasePropertiesFile = basePropertiesFile; } @NotNull - public ResourceBundle getResourceBundle() { - return myResourceBundle; + public static ResourceBundleAsVirtualFile fromResourceBundle(final @NotNull ResourceBundle resourceBundle) { + return new ResourceBundleAsVirtualFile(resourceBundle.getDefaultPropertiesFile().getVirtualFile()); + } + + @Nullable + public ResourceBundle getResourceBundle(final Project project) { + final PsiManager psiManager = PsiManager.getInstance(project); + final PropertiesFile file = PropertiesImplUtil.getPropertiesFile(psiManager.findFile(myBasePropertiesFile)); + if (file == null) { + return null; + } + return PropertiesImplUtil.getResourceBundle(file); } @Override @NotNull public VirtualFileSystem getFileSystem() { - return myResourceBundle.getBaseDirectory().getFileSystem(); + return LocalFileSystem.getInstance(); } @Override @@ -55,7 +74,7 @@ public class ResourceBundleAsVirtualFile extends VirtualFile { @Override @NotNull public String getName() { - return myResourceBundle.getBaseName(); + return PropertiesUtil.getBaseName(myBasePropertiesFile); } public boolean equals(final Object o) { @@ -64,17 +83,18 @@ public class ResourceBundleAsVirtualFile extends VirtualFile { final ResourceBundleAsVirtualFile resourceBundleAsVirtualFile = (ResourceBundleAsVirtualFile)o; - if (!myResourceBundle.equals(resourceBundleAsVirtualFile.myResourceBundle)) return false; + if (!myBasePropertiesFile.equals(resourceBundleAsVirtualFile.myBasePropertiesFile)) return false; return true; } public int hashCode() { - return myResourceBundle.hashCode(); + return myBasePropertiesFile.hashCode(); } @Override public void rename(Object requestor, @NotNull String newName) throws IOException { + throw new UnsupportedOperationException(); } @Override @@ -94,7 +114,7 @@ public class ResourceBundleAsVirtualFile extends VirtualFile { @Override public VirtualFile getParent() { - return myResourceBundle.getBaseDirectory(); + return myBasePropertiesFile.getParent(); } @Override diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleEditorViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleEditorViewElement.java new file mode 100644 index 000000000000..00c21d8d3811 --- /dev/null +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleEditorViewElement.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties.editor; + + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author Dmitry Batkovich + */ +public interface ResourceBundleEditorViewElement { + + PsiElement[] getPsiElements(@NotNull Project project); + +} diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java index 2a1f94af4c95..25220d35311a 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java @@ -26,6 +26,9 @@ import com.intellij.lang.properties.ResourceBundle; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -34,36 +37,35 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -public class ResourceBundleFileStructureViewElement implements StructureViewTreeElement { - private final Project myProject; +public class ResourceBundleFileStructureViewElement implements StructureViewTreeElement, ResourceBundleEditorViewElement { private final ResourceBundle myResourceBundle; - public ResourceBundleFileStructureViewElement(final Project project, final ResourceBundle resourceBundle) { - myProject = project; + public ResourceBundleFileStructureViewElement(final ResourceBundle resourceBundle) { myResourceBundle = resourceBundle; } + @Override public ResourceBundle getValue() { return myResourceBundle; } @NotNull public StructureViewTreeElement[] getChildren() { - List propertiesFiles = myResourceBundle.getPropertiesFiles(myProject); + List propertiesFiles = myResourceBundle.getPropertiesFiles(); Map propertyNames = new LinkedHashMap(); for (PropertiesFile propertiesFile : propertiesFiles) { List properties = propertiesFile.getProperties(); for (IProperty property : properties) { - String name = property.getUnescapedKey(); + String name = property.getKey(); if (!propertyNames.containsKey(name)) { propertyNames.put(name, property); } } } List result = new ArrayList(propertyNames.size()); - for (String property : propertyNames.keySet()) { - //result.add(new PropertiesStructureViewElement(property)); - result.add(new ResourceBundlePropertyStructureViewElement(myProject, myResourceBundle, property)); + for (Map.Entry propertyEntry : propertyNames.entrySet()) { + //result.add(new PropertiesStructureViewElement(propertyEntry)); + result.add(new ResourceBundlePropertyStructureViewElement(myResourceBundle, propertyEntry.getValue())); } return result.toArray(new StructureViewTreeElement[result.size()]); } @@ -85,6 +87,17 @@ public class ResourceBundleFileStructureViewElement implements StructureViewTree }; } + @Override + public PsiElement[] getPsiElements(@NotNull Project project) { + final List propertiesFiles = getValue().getPropertiesFiles(); + return ContainerUtil.map2Array(propertiesFiles, new PsiElement[propertiesFiles.size()], new Function() { + @Override + public PsiElement fun(PropertiesFile propertiesFile) { + return propertiesFile.getContainingFile(); + } + }); + } + public void navigate(boolean requestFocus) { } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java index e521047cb9ba..8b387e802597 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java @@ -20,6 +20,7 @@ package com.intellij.lang.properties.editor; import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.lang.properties.IProperty; import com.intellij.lang.properties.PropertiesHighlighter; import com.intellij.lang.properties.PropertiesUtil; import com.intellij.lang.properties.ResourceBundle; @@ -29,16 +30,16 @@ import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; -public class ResourceBundlePropertyStructureViewElement implements StructureViewTreeElement { - private final String myPropertyName; - private final Project myProject; +public class ResourceBundlePropertyStructureViewElement implements StructureViewTreeElement, ResourceBundleEditorViewElement { private final ResourceBundle myResourceBundle; + private final IProperty myProperty; private String myPresentableName; private static final TextAttributesKey INCOMPLETE_PROPERTY_KEY; @@ -49,10 +50,19 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView INCOMPLETE_PROPERTY_KEY = TextAttributesKey.createTextAttributesKey("INCOMPLETE_PROPERTY_KEY", textAttributes); } - public ResourceBundlePropertyStructureViewElement(final Project project, final ResourceBundle resourceBundle, String propertyName) { - myProject = project; + + public ResourceBundlePropertyStructureViewElement(final ResourceBundle resourceBundle, final IProperty property) { myResourceBundle = resourceBundle; - myPropertyName = propertyName; + myProperty = property; + } + + public IProperty getProperty() { + return myProperty; + } + + @Override + public PsiElement[] getPsiElements(final @NotNull Project project) { + return new PsiElement[] {getProperty().getPsiElement()}; } public void setPresentableName(final String presentableName) { @@ -61,7 +71,7 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView @Override public String getValue() { - return myPropertyName; + return myProperty.getName(); } @Override @@ -76,7 +86,7 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView return new ColoredItemPresentation() { @Override public String getPresentableText() { - return myPresentableName == null ? myPropertyName : myPresentableName; + return myPresentableName == null ? myProperty.getName() : myPresentableName; } @Override @@ -91,7 +101,7 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView @Override public TextAttributesKey getTextAttributesKey() { - boolean isComplete = PropertiesUtil.isPropertyComplete(myProject, myResourceBundle, myPropertyName); + boolean isComplete = PropertiesUtil.isPropertyComplete(myResourceBundle, myProperty.getName()); if (isComplete) { return PropertiesHighlighter.PROPERTY_KEY; @@ -115,5 +125,4 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView public boolean canNavigateToSource() { return false; } - } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java index 24ba3c513c38..fc1aa60d4ac9 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java @@ -25,7 +25,6 @@ import com.intellij.ide.util.treeView.smartTree.Sorter; import com.intellij.lang.properties.ResourceBundle; import com.intellij.lang.properties.structureView.GroupByWordPrefixes; import com.intellij.lang.properties.structureView.PropertiesSeparatorManager; -import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; /** @@ -33,23 +32,24 @@ import org.jetbrains.annotations.NotNull; */ public class ResourceBundleStructureViewModel implements PropertiesGroupingStructureViewModel, StructureViewModel.ExpandInfoProvider { private final ResourceBundle myResourceBundle; - private final GroupByWordPrefixes myGroupByWordPrefixes; + private final GroupByWordPrefixes myByWordPrefixesGrouper; private final StructureViewTreeElement myRoot; - public ResourceBundleStructureViewModel(final Project project, ResourceBundle root) { + public ResourceBundleStructureViewModel(ResourceBundle root) { myResourceBundle = root; - String separator = PropertiesSeparatorManager.getInstance().getSeparator(project, new ResourceBundleAsVirtualFile(myResourceBundle)); - myGroupByWordPrefixes = new GroupByWordPrefixes(separator); - myRoot = new ResourceBundleFileStructureViewElement(project, myResourceBundle); + String separator = PropertiesSeparatorManager.getInstance(root.getProject()). + getSeparator(myResourceBundle); + myByWordPrefixesGrouper = new GroupByWordPrefixes(separator); + myRoot = new ResourceBundleFileStructureViewElement(myResourceBundle); } public void setSeparator(String separator) { - myGroupByWordPrefixes.setSeparator(separator); - PropertiesSeparatorManager.getInstance().setSeparator(new ResourceBundleAsVirtualFile(myResourceBundle), separator); + myByWordPrefixesGrouper.setSeparator(separator); + PropertiesSeparatorManager.getInstance(myResourceBundle.getProject()).setSeparator(myResourceBundle, separator); } public String getSeparator() { - return myGroupByWordPrefixes.getSeparator(); + return myByWordPrefixesGrouper.getSeparator(); } @NotNull @@ -59,7 +59,7 @@ public class ResourceBundleStructureViewModel implements PropertiesGroupingStruc @NotNull public Grouper[] getGroupers() { - return new Grouper[]{myGroupByWordPrefixes}; + return new Grouper[]{myByWordPrefixesGrouper}; } @NotNull diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java index 53a9bd4dc31a..e4a35e7e677c 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java @@ -54,10 +54,10 @@ public class ResourceBundleUtil { if (virtualFile == null) { return null; } - if (virtualFile instanceof ResourceBundleAsVirtualFile) { - return ((ResourceBundleAsVirtualFile)virtualFile).getResourceBundle(); + final Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (virtualFile instanceof ResourceBundleAsVirtualFile && project != null) { + return ((ResourceBundleAsVirtualFile)virtualFile).getResourceBundle(project); } - Project project = CommonDataKeys.PROJECT.getData(dataContext); if (project != null) { final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); if (psiFile instanceof PropertiesFile) { diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertiesFileImpl.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertiesFileImpl.java index 77be36abc7a7..759ce6cb46fb 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertiesFileImpl.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertiesFileImpl.java @@ -114,7 +114,7 @@ public class PropertiesFileImpl extends PsiFileBase implements PropertiesFile { @Override @NotNull public ResourceBundle getResourceBundle() { - return PropertiesImplUtil.getResourceBundle(getContainingFile()); + return PropertiesImplUtil.getResourceBundle(this); } @Override diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/references/PropertyReferenceBase.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/references/PropertyReferenceBase.java index a740d8ece66f..2cc4851b012f 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/references/PropertyReferenceBase.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/references/PropertyReferenceBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2009 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. diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesFileStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesFileStructureViewElement.java index d0a203a7644f..432739c9df96 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesFileStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesFileStructureViewElement.java @@ -18,9 +18,12 @@ package com.intellij.lang.properties.structureView; import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.ide.structureView.impl.common.PsiTreeElementBase; import com.intellij.lang.properties.IProperty; +import com.intellij.lang.properties.editor.ResourceBundleEditorViewElement; import com.intellij.lang.properties.psi.Property; import com.intellij.lang.properties.psi.impl.PropertiesFileImpl; import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -31,7 +34,7 @@ import java.util.List; /** * @author max */ -public class PropertiesFileStructureViewElement extends PsiTreeElementBase { +public class PropertiesFileStructureViewElement extends PsiTreeElementBase implements ResourceBundleEditorViewElement { protected PropertiesFileStructureViewElement(PropertiesFileImpl propertiesFile) { super(propertiesFile); @@ -48,6 +51,11 @@ public class PropertiesFileStructureViewElement extends PsiTreeElementBase myProperties; private final @NotNull String myPrefix; private final String myPresentableName; private final @NotNull String mySeparator; - public PropertiesPrefixGroup(final Collection properties, String prefix, String presentableName, final String separator) { + public PropertiesPrefixGroup(final Collection properties, + final @NotNull String prefix, + final String presentableName, + final @NotNull String separator) { myProperties = properties; myPrefix = prefix; myPresentableName = presentableName; mySeparator = separator; } + public String getPresentableName() { + return myPresentableName; + } + + @NotNull + public String getSeparator() { + return mySeparator; + } + + @NotNull + public String getPrefix() { + return myPrefix; + } + @NotNull public ItemPresentation getPresentation() { return new ItemPresentation() { @@ -112,8 +136,24 @@ public class PropertiesPrefixGroup implements Group { return result; } - public String getPrefix() { - return myPrefix; + @Override + public PsiElement[] getPsiElements(final @NotNull Project project) { + final List elements = ContainerUtil.mapNotNull(getChildren(), new NullableFunction() { + @Nullable + @Override + public PsiElement fun(final TreeElement treeElement) { + if (treeElement instanceof PropertiesStructureViewElement) { + PropertiesStructureViewElement propertiesElement = (PropertiesStructureViewElement)treeElement; + IProperty property = propertiesElement.getValue(); + return property.getPsiElement(); + } + else if (treeElement instanceof ResourceBundlePropertyStructureViewElement) { + return ((ResourceBundlePropertyStructureViewElement)treeElement).getPsiElements(project)[0]; + } + return null; + } + }); + return elements.toArray(new PsiElement[elements.size()]); } public boolean equals(final Object o) { diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesSeparatorManager.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesSeparatorManager.java index f5e636811b92..cb60de04bc4f 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesSeparatorManager.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesSeparatorManager.java @@ -19,28 +19,18 @@ */ package com.intellij.lang.properties.structureView; -import com.intellij.lang.properties.IProperty; -import com.intellij.lang.properties.PropertiesLanguage; -import com.intellij.lang.properties.ResourceBundle; -import com.intellij.lang.properties.ResourceBundleImpl; -import com.intellij.lang.properties.editor.ResourceBundleAsVirtualFile; +import com.intellij.lang.properties.*; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.psi.FileViewProvider; -import com.intellij.psi.PsiManager; -import com.intellij.util.SmartList; -import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; import gnu.trove.TIntLongHashMap; import gnu.trove.TIntProcedure; import org.jdom.Element; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -48,45 +38,42 @@ import java.util.Map; name="PropertiesSeparatorManager", storages= { @Storage( - file = StoragePathMacros.APP_CONFIG + "/other.xml" + file = StoragePathMacros.PROJECT_FILE )} ) public class PropertiesSeparatorManager implements PersistentStateComponent { @NonNls private static final String FILE_ELEMENT = "file"; @NonNls private static final String URL_ELEMENT = "url"; @NonNls private static final String SEPARATOR_ATTR = "separator"; + private final Project myProject; - public static PropertiesSeparatorManager getInstance() { - return ServiceManager.getService(PropertiesSeparatorManager.class); + public static PropertiesSeparatorManager getInstance(final Project project) { + return ServiceManager.getService(project, PropertiesSeparatorManager.class); } - private final Map mySeparators = new THashMap(); + private final Map mySeparators = new THashMap(); - public String getSeparator(Project project, VirtualFile file) { - String separator = mySeparators.get(file); + public PropertiesSeparatorManager(final Project project) { + myProject = project; + } + + @NotNull + public String getSeparator(final ResourceBundle resourceBundle) { + if (!(resourceBundle instanceof ResourceBundleImpl)) { + return "."; + } + String separator = mySeparators.get(((ResourceBundleImpl)resourceBundle).getUrl()); if (separator == null) { - separator = guessSeparator(project, file); - setSeparator(file, separator); + separator = guessSeparator((ResourceBundleImpl)resourceBundle); + setSeparator(resourceBundle, separator); } return separator; } //returns most probable separator in properties files - private static String guessSeparator(final Project project, final VirtualFile file) { - Collection files; - if (file instanceof ResourceBundleAsVirtualFile) { - files = ((ResourceBundleAsVirtualFile)file).getResourceBundle().getPropertiesFiles(project); - } - else { - PsiManager psiManager = PsiManager.getInstance(project); - final FileViewProvider provider = psiManager.findViewProvider(file); - files = new SmartList(); - if (provider != null) { - ContainerUtil.addIfNotNull((PropertiesFile)provider.getPsi(PropertiesLanguage.INSTANCE), files); - } - } + private static String guessSeparator(final ResourceBundleImpl resourceBundle) { final TIntLongHashMap charCounts = new TIntLongHashMap(); - for (PropertiesFile propertiesFile : files) { + for (PropertiesFile propertiesFile : resourceBundle.getPropertiesFiles()) { if (propertiesFile == null) continue; List properties = propertiesFile.getProperties(); for (IProperty property : properties) { @@ -119,8 +106,10 @@ public class PropertiesSeparatorManager implements PersistentStateComponent - @@ -44,11 +44,11 @@ - - - + + + + - diff --git a/plugins/properties/src/com/intellij/ide/favoritesTreeView/ResourcesFavoriteNodeProvider.java b/plugins/properties/src/com/intellij/ide/favoritesTreeView/ResourcesFavoriteNodeProvider.java index 4b6a574c2024..67ddbf2d10f2 100644 --- a/plugins/properties/src/com/intellij/ide/favoritesTreeView/ResourcesFavoriteNodeProvider.java +++ b/plugins/properties/src/com/intellij/ide/favoritesTreeView/ResourcesFavoriteNodeProvider.java @@ -22,13 +22,13 @@ package com.intellij.ide.favoritesTreeView; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.lang.properties.PropertiesImplUtil; import com.intellij.lang.properties.ResourceBundle; import com.intellij.lang.properties.ResourceBundleImpl; import com.intellij.lang.properties.projectView.ResourceBundleNode; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -64,7 +64,7 @@ public class ResourcesFavoriteNodeProvider extends FavoriteNodeProvider { public boolean elementContainsFile(final Object element, final VirtualFile vFile) { if (element instanceof ResourceBundle) { ResourceBundle bundle = (ResourceBundle)element; - final List propertiesFiles = bundle.getPropertiesFiles(myProject); + final List propertiesFiles = bundle.getPropertiesFiles(); for (PropertiesFile file : propertiesFiles) { final VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile == null) continue; @@ -87,7 +87,7 @@ public class ResourcesFavoriteNodeProvider extends FavoriteNodeProvider { public boolean isInvalidElement(final Object element) { if (element instanceof ResourceBundle) { ResourceBundle resourceBundle = (ResourceBundle)element; - List propertiesFiles = resourceBundle.getPropertiesFiles(myProject); + List propertiesFiles = resourceBundle.getPropertiesFiles(); if (propertiesFiles.size() == 1) { //todo result.add(new PsiFileNode(myProject, propertiesFiles.iterator().next(), this)); return true; @@ -113,6 +113,6 @@ public class ResourcesFavoriteNodeProvider extends FavoriteNodeProvider { } public Object[] createPathFromUrl(final Project project, final String url, final String moduleName) { - return new Object[]{ResourceBundleImpl.createByUrl(url)}; + return new Object[]{PropertiesImplUtil.createByUrl(url, project)}; } } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java b/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java new file mode 100644 index 000000000000..d25b780bd769 --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java @@ -0,0 +1,138 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties.editor; + +import com.intellij.icons.AllIcons; +import com.intellij.lang.properties.PropertiesBundle; +import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.lang.properties.structureView.PropertiesPrefixGroup; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.InputValidator; +import com.intellij.openapi.ui.Messages; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** +* @author Dmitry Batkovich +*/ +class NewPropertyAction extends AnAction { + public NewPropertyAction() { + super("New Property", null, AllIcons.General.Add); + } + + @Override + public void actionPerformed(final AnActionEvent e) { + final Project project = getEventProject(e); + if (project == null) { + return; + } + final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(e.getDataContext()); + if (editor == null || !(editor instanceof ResourceBundleEditor)) { + return; + } + final ResourceBundleEditor resourceBundleEditor = (ResourceBundleEditor)editor; + + final String prefix; + final String separator; + final String place = e.getPlace(); + if (ActionPlaces.STRUCTURE_VIEW_TOOLBAR.equals(place)) { + prefix = null; + separator = null; + } else { + final ResourceBundleEditorViewElement selectedElement = resourceBundleEditor.getSelectedElement(); + if (selectedElement == null) { + return; + } + if (selectedElement instanceof PropertiesPrefixGroup) { + final PropertiesPrefixGroup group = (PropertiesPrefixGroup)selectedElement; + prefix = group.getPrefix(); + separator = group.getSeparator(); + } + else if (selectedElement instanceof ResourceBundlePropertyStructureViewElement || + selectedElement instanceof ResourceBundleFileStructureViewElement) { + prefix = null; + separator = null; + } + else { + throw new IllegalStateException("unsupported type: " + selectedElement.getClass()); + } + } + final ResourceBundle resourceBundle = resourceBundleEditor.getResourceBundle(); + + Messages.showInputDialog(project, + PropertiesBundle.message("new.property.dialog.name.prompt.text"), + PropertiesBundle.message("new.property.dialog.title"), + Messages.getQuestionIcon(), + null, + new NewPropertyNameValidator(resourceBundle, prefix, separator)); + } + + private static class NewPropertyNameValidator implements InputValidator { + private final @NotNull ResourceBundle myResourceBundle; + private final @Nullable String myPrefix; + private final @Nullable String mySeparator; + + + public NewPropertyNameValidator(final @NotNull ResourceBundle resourceBundle, + final @Nullable String prefix, + final @Nullable String separator) { + myResourceBundle = resourceBundle; + myPrefix = prefix; + mySeparator = separator; + } + + @Override + public boolean checkInput(final String inputString) { + return true; + } + + @Override + public boolean canClose(final String inputString) { + final String newPropertyName = myPrefix == null ? inputString : (myPrefix + mySeparator + inputString); + + for (final PropertiesFile propertiesFile : myResourceBundle.getPropertiesFiles()) { + for (final String propertyName : propertiesFile.getNamesMap().keySet()) { + if (newPropertyName.equals(propertyName)) { + Messages.showErrorDialog("Can't add new property. Property with key \'" + newPropertyName + "\' already exists.", "New Property"); + return false; + } + } + } + + final PropertiesFile defaultPropertiesFile = myResourceBundle.getDefaultPropertiesFile(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + @Override + public void run() { + defaultPropertiesFile.addProperty(newPropertyName, ""); + } + }); + } + }); + return true; + } + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/PropertiesGroupingStructureViewComponent.java b/plugins/properties/src/com/intellij/lang/properties/editor/PropertiesGroupingStructureViewComponent.java index 8765943c7c4a..733f229bc687 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/PropertiesGroupingStructureViewComponent.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/PropertiesGroupingStructureViewComponent.java @@ -32,6 +32,7 @@ import java.util.Set; * @author cdr */ public class PropertiesGroupingStructureViewComponent extends StructureViewComponent { + protected PropertiesGroupingStructureViewComponent(Project project, FileEditor editor, PropertiesGroupingStructureViewModel structureViewModel) { diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java index b3799a338b8c..32ba85943826 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java @@ -69,7 +69,8 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.TitledBorder; -import javax.swing.event.*; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import java.awt.*; @@ -85,7 +86,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit private final StructureViewComponent myStructureViewComponent; private final Map myEditors; - private String myOldPropertyName; private final ResourceBundle myResourceBundle; private final Map myTitledPanels; private final JComponent myNoPropertySelectedPanel = new NoPropertySelectedPanel().getComponent(); @@ -102,8 +102,8 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit private VirtualFileListener myVfsListener; private Editor mySelectedEditor; - public ResourceBundleEditor(Project project, ResourceBundle resourceBundle) { - myProject = project; + public ResourceBundleEditor(@NotNull ResourceBundle resourceBundle) { + myProject = resourceBundle.getProject(); final JPanel splitPanel = new JPanel(); myValuesPanel = new JPanel(); @@ -118,7 +118,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit splitPanel.add(splitter, BorderLayout.CENTER); myResourceBundle = resourceBundle; - myStructureViewComponent = new ResourceBundleStructureViewComponent(project, myResourceBundle, this); + myStructureViewComponent = new ResourceBundleStructureViewComponent(myResourceBundle, this); myStructureViewPanel.setLayout(new BorderLayout()); myStructureViewPanel.add(myStructureViewComponent, BorderLayout.CENTER); @@ -130,9 +130,18 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit public void valueChanged(TreeSelectionEvent e) { // filter out temp unselect/select events if (getSelectedPropertyName() == null) return; - if (!Comparing.strEqual(selectedPropertyName, getSelectedPropertyName()) - || !Comparing.equal(selectedPropertiesFile, getSelectedPropertiesFile())) - { + if (!Comparing.strEqual(selectedPropertyName, getSelectedPropertyName()) || + !Comparing.equal(selectedPropertiesFile, getSelectedPropertiesFile())) { + + if (e.getOldLeadSelectionPath() != null) { + for (Map.Entry entry : myEditors.entrySet()) { + if (entry.getValue() == mySelectedEditor) { + writeEditorPropertyValue(mySelectedEditor, entry.getKey(), selectedPropertyName); + break; + } + } + } + selectedPropertyName = getSelectedPropertyName(); selectedPropertiesFile = getSelectedPropertiesFile(); selectionChanged(); @@ -153,7 +162,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit } myDataProviderPanel = new DataProviderPanel(splitPanel); - project.getMessageBus().connect(project).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() { + myProject.getMessageBus().connect(myProject).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() { @Override public void selectionChanged(@NotNull FileEditorManagerEvent event) { onSelectionChanged(event); @@ -161,6 +170,10 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit }); } + public ResourceBundle getResourceBundle() { + return myResourceBundle; + } + private void onSelectionChanged(@NotNull FileEditorManagerEvent event) { // Ignore events which don't target current editor. FileEditor oldEditor = event.getOldEditor(); @@ -194,6 +207,9 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit } private void setStructureViewSelection(@NotNull final String propertyName) { + if (myStructureViewComponent.isDisposed()) { + return; + } JTree tree = myStructureViewComponent.getTree(); if (tree == null) { return; @@ -264,19 +280,24 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit @Nullable private static String getNodeValue(@NotNull DefaultMutableTreeNode node) { - Object userObject = node.getUserObject(); - if (!(userObject instanceof AbstractTreeNode)) return null; - Object value = ((AbstractTreeNode)userObject).getValue(); - return value instanceof ResourceBundlePropertyStructureViewElement ? ((ResourceBundlePropertyStructureViewElement)value).getValue() + final ResourceBundleEditorViewElement element = getSelectedElement(node); + return element instanceof ResourceBundlePropertyStructureViewElement ? ((ResourceBundlePropertyStructureViewElement)element).getValue() : null; } - private void writeEditorPropertyValue(final Editor editor, final PropertiesFile propertiesFile) { - final String currentValue = editor.getDocument().getText(); - final String currentSelectedProperty = getSelectedPropertyName(); - final String selectedProperty = myOldPropertyName == null ? currentSelectedProperty : myOldPropertyName; + @Nullable + private static ResourceBundleEditorViewElement getSelectedElement(@NotNull DefaultMutableTreeNode node) { + Object userObject = node.getUserObject(); + if (!(userObject instanceof AbstractTreeNode)) return null; + Object value = ((AbstractTreeNode)userObject).getValue(); + return value instanceof ResourceBundleEditorViewElement ? (ResourceBundleEditorViewElement) value : null; + } - assert selectedProperty != null; + private void writeEditorPropertyValue(final Editor editor, final PropertiesFile propertiesFile, final @Nullable String propertyName) { + final String currentValue = editor.getDocument().getText(); + final String currentSelectedProperty = propertyName == null ? getSelectedPropertyName() : propertyName; + + assert currentSelectedProperty != null; ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override @@ -284,10 +305,10 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit WriteCommandAction.runWriteCommandAction(myProject, new Runnable() { @Override public void run() { - final IProperty property = propertiesFile.findPropertyByKey(selectedProperty); + final IProperty property = propertiesFile.findPropertyByKey(currentSelectedProperty); try { if (property == null) { - propertiesFile.addProperty(selectedProperty, currentValue); + propertiesFile.addProperty(currentSelectedProperty, currentValue); } else { property.setValue(currentValue); @@ -318,7 +339,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit }, VALUES); myValuesPanel.add(myNoPropertySelectedPanel, NO_PROPERTY_SELECTED); - List propertiesFiles = myResourceBundle.getPropertiesFiles(myProject); + List propertiesFiles = myResourceBundle.getPropertiesFiles(); GridBagConstraints gc = new GridBagConstraints(0, 0, 0, 0, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0); @@ -339,7 +360,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit @Override public void focusLost(final Editor eventEditor) { - writeEditorPropertyValue(editor, propertiesFile); + writeEditorPropertyValue(editor, propertiesFile, null); } }); editor.getDocument().putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); @@ -510,18 +531,10 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit } private void selectionChanged() { myBackSlashPressed.clear(); - final String currentSelectedPropertyName = getSelectedPropertyName(); - UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { - if (myOldPropertyName != null && !myOldPropertyName.equals(currentSelectedPropertyName)) { - for (final Map.Entry entry : myEditors.entrySet()) { - writeEditorPropertyValue(entry.getValue(), entry.getKey()); - } - } updateEditorsFromProperties(); - myOldPropertyName = currentSelectedPropertyName; } }); } @@ -537,7 +550,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit } @Nullable - private String getSelectedPropertyName() { + public String getSelectedPropertyName() { JTree tree = myStructureViewComponent.getTree(); if (tree == null) return null; TreePath selected = tree.getSelectionModel().getSelectionPath(); @@ -545,6 +558,15 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit return getNodeValue((DefaultMutableTreeNode)selected.getLastPathComponent()); } + @Nullable + public ResourceBundleEditorViewElement getSelectedElement() { + JTree tree = myStructureViewComponent.getTree(); + if (tree == null) return null; + TreePath selected = tree.getSelectionModel().getSelectionPath(); + if (selected == null) return null; + return getSelectedElement((DefaultMutableTreeNode)selected.getLastPathComponent()); + } + @Override @NotNull public JComponent getComponent() { @@ -686,7 +708,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit if (mySelectedEditor != null) { for (final Map.Entry entry : myEditors.entrySet()) { if (mySelectedEditor.equals(entry.getValue())) { - writeEditorPropertyValue(mySelectedEditor, entry.getKey()); + writeEditorPropertyValue(mySelectedEditor, entry.getKey(), null); } } } @@ -706,23 +728,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit myEditors.clear(); } - /** - * Renames target property if the one is available. - *

- * Note: is assumed to be called under {@link WriteAction write action}. - * - * @param oldName old property name - * @param newName new property name - */ - public void renameProperty(@NotNull String oldName, @NotNull String newName) { - for (PropertiesFile properties : myResourceBundle.getPropertiesFiles(myProject)) { - IProperty property = properties.findPropertyByKey(oldName); - if (property != null) { - property.setName(newName); - } - } - } - public static class ResourceBundleEditorState implements FileEditorState { private final String myPropertyName; diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorProvider.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorProvider.java index f29a9cbb7b7a..fdbc2c0c5e59 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorProvider.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditorProvider.java @@ -42,7 +42,7 @@ public class ResourceBundleEditorProvider extends FileTypeFactory implements Fil if (!file.isValid()) return false; PsiFile psiFile = PsiManager.getInstance(project).findFile(file); PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(psiFile); - return propertiesFile != null && propertiesFile.getResourceBundle().getPropertiesFiles(project).size() > 1; + return propertiesFile != null && propertiesFile.getResourceBundle().getPropertiesFiles().size() > 1; } @Override @@ -50,7 +50,7 @@ public class ResourceBundleEditorProvider extends FileTypeFactory implements Fil public FileEditor createEditor(@NotNull Project project, @NotNull final VirtualFile file){ ResourceBundle resourceBundle; if (file instanceof ResourceBundleAsVirtualFile) { - resourceBundle = ((ResourceBundleAsVirtualFile)file).getResourceBundle(); + resourceBundle = ((ResourceBundleAsVirtualFile)file).getResourceBundle(project); } else { PsiFile psiFile = PsiManager.getInstance(project).findFile(file); @@ -60,7 +60,7 @@ public class ResourceBundleEditorProvider extends FileTypeFactory implements Fil resourceBundle = PropertiesImplUtil.getPropertiesFile(psiFile).getResourceBundle(); } - return new ResourceBundleEditor(project, resourceBundle); + return new ResourceBundleEditor(resourceBundle); } @Override diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java index 9289eb24a4cb..ee8e47a446e4 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java @@ -15,25 +15,118 @@ */ package com.intellij.lang.properties.editor; +import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter; +import com.intellij.ide.CopyProvider; +import com.intellij.ide.DeleteProvider; +import com.intellij.lang.properties.IProperty; import com.intellij.lang.properties.ResourceBundle; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.lang.properties.psi.Property; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.refactoring.safeDelete.SafeDeleteHandler; +import com.intellij.ui.PopupHandler; +import com.intellij.usages.UsageTarget; +import com.intellij.usages.UsageView; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.awt.datatransfer.StringSelection; +import java.util.ArrayList; +import java.util.List; /** * @author cdr */ class ResourceBundleStructureViewComponent extends PropertiesGroupingStructureViewComponent { + private final static Logger LOG = Logger.getInstance(ResourceBundleStructureViewComponent.class); + private final ResourceBundle myResourceBundle; - public ResourceBundleStructureViewComponent(Project project, ResourceBundle resourceBundle, ResourceBundleEditor editor) { - super(project, editor, new ResourceBundleStructureViewModel(project, resourceBundle)); + public ResourceBundleStructureViewComponent(final ResourceBundle resourceBundle, final ResourceBundleEditor editor) { + super(resourceBundle.getProject(), editor, new ResourceBundleStructureViewModel(resourceBundle)); myResourceBundle = resourceBundle; + tunePopupActionGroup(); } - public Object getData(String dataId) { + @Override + protected void addGroupByActions(final DefaultActionGroup result) { + super.addGroupByActions(result); + result.add(new NewPropertyAction(), Constraints.FIRST); + } + + private void tunePopupActionGroup() { + final DefaultActionGroup propertiesPopupGroup = new DefaultActionGroup(); + propertiesPopupGroup.copyFromGroup((DefaultActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_STRUCTURE_VIEW_POPUP)); + propertiesPopupGroup.add(Separator.getInstance(), Constraints.FIRST); + propertiesPopupGroup.add(new NewPropertyAction(), Constraints.FIRST); + PopupHandler.installPopupHandler(getTree(), propertiesPopupGroup, IdeActions.GROUP_STRUCTURE_VIEW_POPUP, ActionManager.getInstance()); + } + + public Object getData(final String dataId) { if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) { - return new ResourceBundleAsVirtualFile(myResourceBundle); + return ResourceBundleAsVirtualFile.fromResourceBundle(myResourceBundle); + } else if (PlatformDataKeys.FILE_EDITOR.is(dataId)) { + return getFileEditor(); + } else if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) { + final ResourceBundleEditorViewElement selectedElement = ((ResourceBundleEditor)getFileEditor()).getSelectedElement(); + if (selectedElement != null) { + final Project project = CommonDataKeys.PROJECT.getData(this); + if (project != null) { + final PsiElement[] psiElements = selectedElement.getPsiElements(project); + if (psiElements != null) { + return psiElements; + } + } + } + } else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) { + final PsiElement[] psiElements = LangDataKeys.PSI_ELEMENT_ARRAY.getData(this); + if (psiElements != null && psiElements.length > 0) { + return new PsiElementsDeleteProvider(psiElements); + } + } else if (UsageView.USAGE_TARGETS_KEY.is(dataId)) { + final PsiElement[] chosenElements = (PsiElement[]) getData(LangDataKeys.PSI_ELEMENT_ARRAY.getName()); + if (chosenElements != null) { + final UsageTarget[] usageTargets = new UsageTarget[chosenElements.length]; + for (int i = 0; i < chosenElements.length; i++) { + usageTargets[i] = new PsiElement2UsageTargetAdapter(chosenElements[i]); + } + return usageTargets; + } + } else if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) { + return new CopyProvider() { + @Override + public void performCopy(@NotNull final DataContext dataContext) { + final PsiElement[] selectedPsiElements = (PsiElement[])getData(LangDataKeys.PSI_ELEMENT_ARRAY.getName()); + if (selectedPsiElements != null) { + final List names = new ArrayList(selectedPsiElements.length); + for (final PsiElement element : selectedPsiElements) { + if (element instanceof PsiNamedElement) { + names.add(((PsiNamedElement)element).getName()); + } + } + CopyPasteManager.getInstance().setContents(new StringSelection(StringUtil.join(names, "\n"))); + } + } + + @Override + public boolean isCopyEnabled(@NotNull final DataContext dataContext) { + return true; + } + + @Override + public boolean isCopyVisible(@NotNull final DataContext dataContext) { + return true; + } + }; } return super.getData(dataId); } @@ -41,5 +134,40 @@ class ResourceBundleStructureViewComponent extends PropertiesGroupingStructureVi protected boolean showScrollToFromSourceActions() { return false; } + + private class PsiElementsDeleteProvider implements DeleteProvider { + private final PsiElement[] myElements; + + private PsiElementsDeleteProvider(final PsiElement[] elements) { + myElements = elements; + } + + @Override + public void deleteElement(@NotNull final DataContext dataContext) { + final List bundlePropertiesFiles = myResourceBundle.getPropertiesFiles(); + + final List toDelete = new ArrayList(); + for (PsiElement element : myElements) { + final Property property = (Property) element; + final String key = property.getKey(); + if (key == null) { + LOG.error("key must be not null " + element); + } else { + for (PropertiesFile propertiesFile : bundlePropertiesFiles) { + for (final IProperty iProperty : propertiesFile.findPropertiesByKey(key)) { + toDelete.add(iProperty.getPsiElement()); + } + } + } + } + + new SafeDeleteHandler().invoke(myElements[0].getProject(), PsiUtilCore.toPsiElementArray(toDelete), dataContext); + } + + @Override + public boolean canDeleteElement(@NotNull final DataContext dataContext) { + return true; + } + } } diff --git a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleDeleteProvider.java b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleDeleteProvider.java index 7ba44a86c2bd..a681e10f3af1 100644 --- a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleDeleteProvider.java +++ b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleDeleteProvider.java @@ -48,7 +48,7 @@ class ResourceBundleDeleteProvider implements DeleteProvider { public void deleteElement(@NotNull DataContext dataContext) { final Project project = CommonDataKeys.PROJECT.getData(dataContext); - List propertiesFiles = myResourceBundle.getPropertiesFiles(project); + List propertiesFiles = myResourceBundle.getPropertiesFiles(); assert project != null; new SafeDeleteHandler().invoke(project, ContainerUtil.map2Array(propertiesFiles, PsiElement.class, MAPPER), dataContext); } diff --git a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleMoveProvider.java b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleMoveProvider.java index a2b1b621f068..d61282aefba2 100644 --- a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleMoveProvider.java +++ b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleMoveProvider.java @@ -57,7 +57,7 @@ public class ResourceBundleMoveProvider extends MoveHandlerDelegate { final ResourceBundle[] bundles = ResourceBundle.ARRAY_DATA_KEY.getData(dataContext); LOG.assertTrue(bundles != null); for (ResourceBundle bundle : bundles) { - List propertiesFiles = bundle.getPropertiesFiles(CommonDataKeys.PROJECT.getData(dataContext)); + List propertiesFiles = bundle.getPropertiesFiles(); for (PropertiesFile propertiesFile : propertiesFiles) { filesOrDirs.add(propertiesFile.getContainingFile()); } diff --git a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleNode.java b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleNode.java index 7de856226e01..a415e4544aa0 100644 --- a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleNode.java +++ b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleNode.java @@ -25,9 +25,7 @@ import com.intellij.ide.projectView.ProjectViewNode; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.nodes.PsiFileNode; import com.intellij.ide.util.treeView.AbstractTreeNode; -import com.intellij.lang.properties.PropertiesBundle; -import com.intellij.lang.properties.PropertiesImplUtil; -import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.*; import com.intellij.lang.properties.editor.ResourceBundleAsVirtualFile; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.fileEditor.FileEditorManager; @@ -50,7 +48,7 @@ public class ResourceBundleNode extends ProjectViewNode{ @NotNull public Collection getChildren() { - List propertiesFiles = getValue().getPropertiesFiles(myProject); + List propertiesFiles = getValue().getPropertiesFiles(); Collection children = new ArrayList(); for (PropertiesFile propertiesFile : propertiesFiles) { AbstractTreeNode node = new PsiFileNode(myProject, propertiesFile.getContainingFile(), getSettings()); @@ -63,11 +61,11 @@ public class ResourceBundleNode extends ProjectViewNode{ if (!file.isValid()) return false; PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(file); PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(psiFile); - return propertiesFile != null && getValue().getPropertiesFiles(myProject).contains(propertiesFile); + return propertiesFile != null && getValue().getPropertiesFiles().contains(propertiesFile); } public VirtualFile getVirtualFile() { - final List list = getValue().getPropertiesFiles(myProject); + final List list = getValue().getPropertiesFiles(); if (!list.isEmpty()) { return list.get(0).getVirtualFile(); } @@ -88,7 +86,7 @@ public class ResourceBundleNode extends ProjectViewNode{ } public void navigate(final boolean requestFocus) { - OpenFileDescriptor descriptor = new OpenFileDescriptor(getProject(), new ResourceBundleAsVirtualFile(getValue())); + OpenFileDescriptor descriptor = new OpenFileDescriptor(getProject(), ResourceBundleAsVirtualFile.fromResourceBundle(getValue())); FileEditorManager.getInstance(getProject()).openTextEditor(descriptor, requestFocus); } diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/PropertyRenameHandler.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/PropertyRenameHandler.java deleted file mode 100644 index 99355d34bc6e..000000000000 --- a/plugins/properties/src/com/intellij/lang/properties/refactoring/PropertyRenameHandler.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.lang.properties.refactoring; - -import com.intellij.codeInsight.TargetElementUtilBase; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ScrollType; -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; -import com.intellij.refactoring.rename.PsiElementRenameHandler; -import com.intellij.lang.properties.references.PropertyReferenceBase; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author Dmitry Avdeev - */ -public class PropertyRenameHandler extends PsiElementRenameHandler { - - public boolean isAvailableOnDataContext(final DataContext dataContext) { - final Editor editor = LangDataKeys.EDITOR.getData(dataContext); - if (editor != null) { - if (getPsiElement(editor) != null) return true; - } - return false; - } - - @Nullable - private static PsiElement getPsiElement(final Editor editor) { - final PsiReference reference = TargetElementUtilBase.findReference(editor); - if (reference instanceof PropertyReferenceBase) { - final ResolveResult[] resolveResults = ((PropertyReferenceBase)reference).multiResolve(false); - return resolveResults.length > 0 ? resolveResults[0].getElement() : null; - } else if (reference instanceof PsiMultiReference) { - final PsiReference[] references = ((PsiMultiReference)reference).getReferences(); - for (PsiReference psiReference : references) { - if (psiReference instanceof PropertyReferenceBase) { - final ResolveResult[] resolveResults = ((PropertyReferenceBase)psiReference).multiResolve(false); - if (resolveResults.length > 0) return resolveResults[0].getElement(); - } - } - } - return null; - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, final DataContext dataContext) { - PsiElement element = getPsiElement(editor); - editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); - final PsiElement nameSuggestionContext = file.findElementAt(editor.getCaretModel().getOffset()); - invoke(element, project, nameSuggestionContext, editor); - } -} diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/ResourceBundleKeyRenameHandler.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/ResourceBundleKeyRenameHandler.java deleted file mode 100644 index 7d7dbc14aead..000000000000 --- a/plugins/properties/src/com/intellij/lang/properties/refactoring/ResourceBundleKeyRenameHandler.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.lang.properties.refactoring; - -import com.intellij.lang.properties.IProperty; -import com.intellij.lang.properties.PropertiesBundle; -import com.intellij.lang.properties.ResourceBundle; -import com.intellij.lang.properties.editor.ResourceBundleEditor; -import com.intellij.lang.properties.editor.ResourceBundleEditorUtil; -import com.intellij.lang.properties.editor.ResourceBundleUtil; -import com.intellij.lang.properties.psi.PropertiesFile; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorStateLevel; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.InputValidator; -import com.intellij.openapi.ui.Messages; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.refactoring.rename.RenameHandler; -import com.intellij.util.containers.HashSet; -import org.jetbrains.annotations.NotNull; - -import java.util.Set; - -/** - * Encapsulates logic of renaming resource bundle property key. - * - * @author Denis Zhdanov - * @since 11/9/10 4:13 PM - */ -public class ResourceBundleKeyRenameHandler implements RenameHandler { - - @Override - public boolean isAvailableOnDataContext(DataContext dataContext) { - ResourceBundleEditor editor = ResourceBundleEditorUtil.getEditor(dataContext); - if (editor == null) { - return false; - } - return editor.getState(FileEditorStateLevel.NAVIGATION).getPropertyName() != null; - } - - @Override - public boolean isRenaming(DataContext dataContext) { - return isAvailableOnDataContext(dataContext); - } - - @Override - public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { - ResourceBundleEditor bundleEditor = ResourceBundleEditorUtil.getEditor(dataContext); - if (bundleEditor == null) { - return; - } - - String propertyName = bundleEditor.getState(FileEditorStateLevel.NAVIGATION).getPropertyName(); - if (propertyName == null) { - return; - } - - ResourceBundle bundle = ResourceBundleUtil.getResourceBundleFromDataContext(dataContext); - if (bundle == null) { - return; - } - Messages.showInputDialog(project, PropertiesBundle.message("rename.bundle.enter.new.resource.bundle.key.name.prompt.text"), - PropertiesBundle.message("rename.resource.bundle.key.dialog.title"), Messages.getQuestionIcon(), propertyName, - new ResourceBundleKeyRenameValidator(project, bundleEditor, bundle, propertyName)); - } - - @Override - public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { - invoke(project, null, null, dataContext); - } - - private static class ResourceBundleKeyRenameValidator implements InputValidator { - - private final Set myExistingProperties = new HashSet(); - - private final ResourceBundleEditor myEditor; - private final String myOldPropertyName; - - ResourceBundleKeyRenameValidator(Project project, ResourceBundleEditor editor, ResourceBundle bundle, String oldPropertyName) { - myEditor = editor; - myOldPropertyName = oldPropertyName; - for (PropertiesFile file : bundle.getPropertiesFiles(project)) { - for (IProperty property : file.getProperties()) { - myExistingProperties.add(property.getKey()); - } - } - myExistingProperties.remove(oldPropertyName); - } - - @Override - public boolean checkInput(String inputString) { - return inputString != null && !inputString.isEmpty() && !myExistingProperties.contains(inputString); - } - - @Override - public boolean canClose(final String inputString) { - if (!checkInput(inputString)) { - return false; - } - - if (myOldPropertyName.equals(inputString)) { - return true; - } - - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - myEditor.renameProperty(myOldPropertyName, inputString); - } - }); - return true; - } - } -} diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/ResourceBundleRenameHandler.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/ResourceBundleRenameHandler.java deleted file mode 100644 index fe555a7a2ca7..000000000000 --- a/plugins/properties/src/com/intellij/lang/properties/refactoring/ResourceBundleRenameHandler.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @author Alexey - */ -package com.intellij.lang.properties.refactoring; - -import com.intellij.codeInsight.FileModificationService; -import com.intellij.lang.properties.PropertiesBundle; -import com.intellij.lang.properties.ResourceBundle; -import com.intellij.lang.properties.editor.ResourceBundleAsVirtualFile; -import com.intellij.lang.properties.editor.ResourceBundleEditor; -import com.intellij.lang.properties.editor.ResourceBundleEditorUtil; -import com.intellij.lang.properties.editor.ResourceBundleUtil; -import com.intellij.lang.properties.psi.PropertiesFile; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorStateLevel; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.InputValidator; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.refactoring.rename.RenameHandler; -import com.intellij.refactoring.rename.RenameProcessor; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.List; - -public class ResourceBundleRenameHandler implements RenameHandler { - private static final Logger LOG = Logger.getInstance("#" + ResourceBundleRenameHandler.class.getName()); - - public boolean isAvailableOnDataContext(DataContext dataContext) { - final Project project = CommonDataKeys.PROJECT.getData(dataContext); - if (project == null) { - return false; - } - final ResourceBundle bundle = ResourceBundleUtil.getResourceBundleFromDataContext(dataContext); - if (bundle == null) { - return false; - } - - final VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(dataContext); - - ResourceBundleEditor editor = ResourceBundleEditorUtil.getEditor(dataContext); - return (editor == null || editor.getState(FileEditorStateLevel.NAVIGATION).getPropertyName() == null /* user selected non-bundle key element */) - && bundle.getPropertiesFiles(project).size() > 1 && (virtualFile instanceof ResourceBundleAsVirtualFile || virtualFile == null); - } - - public boolean isRenaming(DataContext dataContext) { - return isAvailableOnDataContext(dataContext); - } - - public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { - ResourceBundle resourceBundle = ResourceBundleUtil.getResourceBundleFromDataContext(dataContext); - - assert resourceBundle != null; - Messages.showInputDialog(project, - PropertiesBundle.message("rename.bundle.enter.new.resource.bundle.base.name.prompt.text"), - PropertiesBundle.message("rename.resource.bundle.dialog.title"), - Messages.getQuestionIcon(), - resourceBundle.getBaseName(), - new MyInputValidator(project, resourceBundle)); - } - - public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { - invoke(project, null, null, dataContext); - } - - private static class MyInputValidator implements InputValidator { - private final Project myProject; - private final ResourceBundle myResourceBundle; - - public MyInputValidator(final Project project, final ResourceBundle resourceBundle) { - myProject = project; - myResourceBundle = resourceBundle; - } - - public boolean checkInput(String inputString) { - return inputString.indexOf(File.separatorChar) < 0 && inputString.indexOf('/') < 0; - } - - public boolean canClose(final String inputString) { - return doRename(inputString); - } - private boolean doRename(final String inputString) { - final List propertiesFiles = myResourceBundle.getPropertiesFiles(myProject); - for (PropertiesFile propertiesFile : propertiesFiles) { - if (!FileModificationService.getInstance().prepareFileForWrite(propertiesFile.getContainingFile())) return false; - } - - RenameProcessor renameProcessor = null; - String baseName = myResourceBundle.getBaseName(); - for (PropertiesFile propertiesFile : propertiesFiles) { - final VirtualFile virtualFile = propertiesFile.getVirtualFile(); - if (virtualFile == null) { - continue; - } - final String newName = inputString + virtualFile.getNameWithoutExtension().substring(baseName.length()) + "." - + virtualFile.getExtension(); - if (renameProcessor == null) { - renameProcessor = new RenameProcessor(myProject, propertiesFile.getContainingFile(), newName, false, false); - continue; - } - renameProcessor.addElement(propertiesFile.getContainingFile(), newName); - } - if (renameProcessor == null) { - LOG.assertTrue(false); - return true; - } - renameProcessor.setCommandName(PropertiesBundle.message("rename.resource.bundle.dialog.title")); - renameProcessor.doRun(); - return true; - } - } -} \ No newline at end of file diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/RenamePropertyProcessor.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/RenamePropertyProcessor.java similarity index 59% rename from plugins/properties/src/com/intellij/lang/properties/refactoring/RenamePropertyProcessor.java rename to plugins/properties/src/com/intellij/lang/properties/refactoring/rename/RenamePropertyProcessor.java index 6bdaa0957133..f3c81736673f 100644 --- a/plugins/properties/src/com/intellij/lang/properties/refactoring/RenamePropertyProcessor.java +++ b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/RenamePropertyProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.lang.properties.refactoring; +package com.intellij.lang.properties.refactoring.rename; import com.intellij.lang.properties.IProperty; import com.intellij.lang.properties.PropertiesUtil; import com.intellij.lang.properties.ResourceBundle; import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.lang.properties.refactoring.PropertiesRefactoringSettings; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.intellij.refactoring.rename.RenamePsiElementProcessor; @@ -26,6 +28,7 @@ import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo; import com.intellij.usageView.UsageInfo; import org.jetbrains.annotations.NotNull; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -36,12 +39,17 @@ public class RenamePropertyProcessor extends RenamePsiElementProcessor { public void prepareRenaming(final PsiElement element, final String newName, final Map allRenames) { - IProperty property = (IProperty) element; - ResourceBundle resourceBundle = property.getPropertiesFile().getResourceBundle(); - List properties = PropertiesUtil.findAllProperties(element.getProject(), resourceBundle, property.getUnescapedKey()); + final Project project = element.getProject(); + ResourceBundle resourceBundle = ((IProperty) element).getPropertiesFile().getResourceBundle(); + + final Map allRenamesCopy = new LinkedHashMap(allRenames); allRenames.clear(); - for (IProperty otherProperty : properties) { - allRenames.put(otherProperty.getPsiElement(), newName); + for (final Map.Entry e : allRenamesCopy.entrySet()) { + final IProperty property = (IProperty) e.getKey(); + final List properties = PropertiesUtil.findAllProperties(project, resourceBundle, property.getUnescapedKey()); + for (final IProperty toRename : properties) { + allRenames.put(toRename.getPsiElement(), e.getValue()); + } } } @@ -50,14 +58,16 @@ public class RenamePropertyProcessor extends RenamePsiElementProcessor { final String newName, Map allRenames, List result) { - for (IProperty property : ((PropertiesFile)element.getContainingFile()).getProperties()) { - if (Comparing.strEqual(newName, property.getKey())) { - result.add(new UnresolvableCollisionUsageInfo(property.getPsiElement(), element) { - @Override - public String getDescription() { - return "New property name \'" + newName + "\' hides existing property"; - } - }); + for (final Map.Entry e: allRenames.entrySet()) { + for (IProperty property : ((PropertiesFile)e.getKey().getContainingFile()).getProperties()) { + if (Comparing.strEqual(e.getValue(), property.getKey())) { + result.add(new UnresolvableCollisionUsageInfo(property.getPsiElement(), e.getKey()) { + @Override + public String getDescription() { + return "New property name \'" + e.getValue() + "\' hides existing property"; + } + }); + } } } } diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleFromEditorRenameHandler.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleFromEditorRenameHandler.java new file mode 100644 index 000000000000..0c04bec80620 --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleFromEditorRenameHandler.java @@ -0,0 +1,120 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Alexey + */ +package com.intellij.lang.properties.refactoring.rename; + +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.editor.*; +import com.intellij.lang.properties.structureView.PropertiesPrefixGroup; +import com.intellij.lang.properties.structureView.PropertiesStructureViewElement; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.refactoring.rename.RenameHandler; +import com.intellij.util.NullableFunction; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleFromEditorRenameHandler implements RenameHandler { + + @Override + public boolean isAvailableOnDataContext(DataContext dataContext) { + final Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null) { + return false; + } + final ResourceBundle bundle = ResourceBundleUtil.getResourceBundleFromDataContext(dataContext); + if (bundle == null) { + return false; + } + final FileEditor fileEditor = PlatformDataKeys.FILE_EDITOR.getData(dataContext); + if (fileEditor == null || !(fileEditor instanceof ResourceBundleEditor)) { + return false; + } + final VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(dataContext); + return !(virtualFile == null || !(virtualFile instanceof ResourceBundleAsVirtualFile)); + } + + @Override + public boolean isRenaming(DataContext dataContext) { + return isAvailableOnDataContext(dataContext); + } + + @Override + public void invoke(final @NotNull Project project, Editor editor, final PsiFile file, DataContext dataContext) { + final ResourceBundleEditor resourceBundleEditor = (ResourceBundleEditor)PlatformDataKeys.FILE_EDITOR.getData(dataContext); + assert resourceBundleEditor != null; + final ResourceBundleEditorViewElement selectedElement = resourceBundleEditor.getSelectedElement(); + if (selectedElement != null) { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + @Override + public void run() { + if (selectedElement instanceof PropertiesPrefixGroup) { + final PropertiesPrefixGroup group = (PropertiesPrefixGroup)selectedElement; + ResourceBundleRenameUtil.renameResourceBundleKeySection(getPsiElementsFromGroup(group), + group.getPresentableName(), + group.getPrefix().length() - group.getPresentableName().length()); + } else if (selectedElement instanceof ResourceBundlePropertyStructureViewElement) { + final PsiElement psiElement = ((ResourceBundlePropertyStructureViewElement)selectedElement).getProperty().getPsiElement(); + ResourceBundleRenameUtil.renameResourceBundleKey(psiElement, project); + } else if (selectedElement instanceof ResourceBundleFileStructureViewElement) { + ResourceBundleRenameUtil.renameResourceBundleBaseName(((ResourceBundleFileStructureViewElement)selectedElement).getValue(), project); + } else { + throw new IllegalStateException("unsupported type: " + selectedElement.getClass()); + } + } + }); + } + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + invoke(project, null, null, dataContext); + } + + private static List getPsiElementsFromGroup(final PropertiesPrefixGroup propertiesPrefixGroup) { + return ContainerUtil.mapNotNull(propertiesPrefixGroup.getChildren(), new NullableFunction() { + @Nullable + @Override + public PsiElement fun(TreeElement treeElement) { + if (treeElement instanceof PropertiesStructureViewElement) { + return ((PropertiesStructureViewElement)treeElement).getValue().getPsiElement(); + } + if (treeElement instanceof ResourceBundlePropertyStructureViewElement) { + return ((ResourceBundlePropertyStructureViewElement)treeElement).getProperty().getPsiElement(); + } + return null; + } + }); + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleFromProjectViewRenameHandler.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleFromProjectViewRenameHandler.java new file mode 100644 index 000000000000..f2cbca033b6b --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleFromProjectViewRenameHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties.refactoring.rename; + +import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.editor.*; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.refactoring.RefactoringActionHandlerFactory; +import com.intellij.refactoring.rename.RenameHandler; +import org.jetbrains.annotations.NotNull; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleFromProjectViewRenameHandler implements RenameHandler { + + @Override + public boolean isAvailableOnDataContext(DataContext dataContext) { + final Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null) { + return false; + } + final ResourceBundle bundle = ResourceBundleUtil.getResourceBundleFromDataContext(dataContext); + if (bundle == null || bundle.getPropertiesFiles().size() < 2) { + return false; + } + return PlatformDataKeys.FILE_EDITOR.getData(dataContext) == null && CommonDataKeys.VIRTUAL_FILE.getData(dataContext) == null; + } + + @Override + public boolean isRenaming(DataContext dataContext) { + return isAvailableOnDataContext(dataContext); + } + + @Override + public void invoke(final @NotNull Project project, Editor editor, final PsiFile file, DataContext dataContext) { + final ResourceBundle resourceBundle = ResourceBundleUtil.getResourceBundleFromDataContext(dataContext); + assert resourceBundle != null; + RefactoringActionHandlerFactory.getInstance().createRenameHandler().invoke(project, new PsiElement[] {resourceBundle.getDefaultPropertiesFile().getContainingFile()}, dataContext); + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + invoke(project, null, null, dataContext); + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenameUtil.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenameUtil.java new file mode 100644 index 000000000000..79694f7ae35a --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenameUtil.java @@ -0,0 +1,152 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties.refactoring.rename; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.lang.properties.PropertiesBundle; +import com.intellij.lang.properties.ResourceBundle; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.InputValidator; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; +import com.intellij.refactoring.rename.PsiElementRenameHandler; +import com.intellij.refactoring.rename.RenameProcessor; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleRenameUtil { + private final static Logger LOG = Logger.getInstance(ResourceBundleRenameUtil.class); + + public static void renameResourceBundleKey(final @NotNull PsiElement psiElement, final @NotNull Project project) { + PsiElementRenameHandler.invoke(psiElement, project, psiElement.getContainingFile(), null); + } + + public static void renameResourceBundleBaseName(final @NotNull ResourceBundle resourceBundle, final @NotNull Project project) { + Messages.showInputDialog(project, PropertiesBundle.message("rename.bundle.enter.new.resource.bundle.base.name.prompt.text"), + PropertiesBundle.message("rename.resource.bundle.dialog.title"), Messages.getQuestionIcon(), + resourceBundle.getBaseName(), new ResourceBundleBaseNameInputValidator(project, resourceBundle)); + } + + public static void renameResourceBundleKeySection(final List psiElements, final String section, final int sectionPosition) { + if (psiElements.isEmpty()) { + return; + } + final Project project = psiElements.get(0).getProject(); + Messages.showInputDialog(project, PropertiesBundle.message("rename.bundle.enter.new.resource.bundle.section.name.prompt.text"), + PropertiesBundle.message("rename.resource.bundle.section.dialog.title"), Messages.getQuestionIcon(), section, + new ResourceBundleKeySectionInputValidator(psiElements, section, sectionPosition, project)); + } + + private static class ResourceBundleKeySectionInputValidator implements InputValidator { + + private final List myPsiElements; + private final String mySection; + private final int mySectionPosition; + private final Project myProject; + + private ResourceBundleKeySectionInputValidator(final List psiElements, + final String section, + final int sectionPosition, + final Project project) { + myPsiElements = psiElements; + mySection = section; + mySectionPosition = sectionPosition; + myProject = project; + } + + @Override + public boolean checkInput(final String inputString) { + return inputString.indexOf('.') < 0; + } + + @Override + public boolean canClose(final String inputString) { + RenameProcessor renameProcessor = null; + for (final PsiElement psiElement : myPsiElements) { + assert psiElement instanceof PsiNamedElement; + final String oldName = ((PsiNamedElement)psiElement).getName(); + assert oldName != null; + final String newName = + oldName.substring(0, mySectionPosition) + inputString + oldName.substring(mySectionPosition + mySection.length()); + if (renameProcessor == null) { + renameProcessor = new RenameProcessor(myProject, psiElement, newName, false, false); + } + else { + renameProcessor.addElement(psiElement, newName); + } + } + assert renameProcessor != null; + renameProcessor.setCommandName(PropertiesBundle.message("rename.resource.bundle.section.dialog.title")); + renameProcessor.doRun(); + return true; + } + } + + private static class ResourceBundleBaseNameInputValidator implements InputValidator { + private final Project myProject; + private final ResourceBundle myResourceBundle; + + public ResourceBundleBaseNameInputValidator(final Project project, final ResourceBundle resourceBundle) { + myProject = project; + myResourceBundle = resourceBundle; + } + + public boolean checkInput(String inputString) { + return inputString.indexOf(File.separatorChar) < 0 && inputString.indexOf('/') < 0; + } + + public boolean canClose(final String inputString) { + final List propertiesFiles = myResourceBundle.getPropertiesFiles(); + for (PropertiesFile propertiesFile : propertiesFiles) { + if (!FileModificationService.getInstance().prepareFileForWrite(propertiesFile.getContainingFile())) return false; + } + + RenameProcessor renameProcessor = null; + final String baseName = myResourceBundle.getBaseName(); + for (PropertiesFile propertiesFile : propertiesFiles) { + final VirtualFile virtualFile = propertiesFile.getVirtualFile(); + if (virtualFile == null) { + continue; + } + final String newName = + inputString + virtualFile.getNameWithoutExtension().substring(baseName.length()) + "." + virtualFile.getExtension(); + if (renameProcessor == null) { + renameProcessor = new RenameProcessor(myProject, propertiesFile.getContainingFile(), newName, false, false); + continue; + } + renameProcessor.addElement(propertiesFile.getContainingFile(), newName); + } + if (renameProcessor == null) { + LOG.assertTrue(false); + return true; + } + + renameProcessor.setCommandName(PropertiesBundle.message("rename.resource.bundle.dialog.title")); + renameProcessor.doRun(); + + return true; + } + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenamer.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenamer.java new file mode 100644 index 000000000000..ab1b4391b0f6 --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenamer.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties.refactoring.rename; + +import com.intellij.lang.properties.PropertiesBundle; +import com.intellij.lang.properties.PropertiesUtil; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiNamedElement; +import com.intellij.refactoring.rename.naming.AutomaticRenamer; +import org.jetbrains.annotations.NonNls; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleRenamer extends AutomaticRenamer { + + public ResourceBundleRenamer(final PropertiesFile propertiesFile, final String newName) { + for (final PropertiesFile file : propertiesFile.getResourceBundle().getPropertiesFiles()) { + if (file.equals(propertiesFile)) { + continue; + } + final PsiFile containingFile = file.getContainingFile(); + myElements.add(containingFile); + } + suggestAllNames(propertiesFile.getName(), newName); + } + + @Override + protected String nameToCanonicalName(@NonNls final String name, final PsiNamedElement element) { + return PropertiesUtil.getBaseName((PsiFile)element); + } + + @Override + protected String canonicalNameToName(@NonNls final String canonicalName, final PsiNamedElement element) { + final String oldCanonicalName = PropertiesUtil.getBaseName((PsiFile)element); + final String oldName = element.getName(); + assert oldName != null; + return canonicalName + oldName.substring(oldCanonicalName.length()); + } + + @Override + public boolean isSelectedByDefault() { + return true; + } + + @Override + public String getDialogTitle() { + return PropertiesBundle.message("resource.bundle.renamer"); + } + + @Override + public String getDialogDescription() { + return PropertiesBundle.message("resource.bundle.renamer.dialog.description"); + } + + @Override + public String entityName() { + return PropertiesBundle.message("resource.bundle.renamer.entity.name"); + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenamerFactory.java b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenamerFactory.java new file mode 100644 index 000000000000..ec4bd31acbf3 --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleRenamerFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties.refactoring.rename; + +import com.intellij.lang.properties.PropertiesBundle; +import com.intellij.lang.properties.PropertiesImplUtil; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.refactoring.rename.naming.AutomaticRenamer; +import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory; +import com.intellij.usageView.UsageInfo; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleRenamerFactory implements AutomaticRenamerFactory { + @Override + public boolean isApplicable(final PsiElement element) { + if (!(element instanceof PsiFile)) { + return false; + } + return PropertiesImplUtil.isPropertiesFile((PsiFile)element); + } + + + @Nullable + @Override + public String getOptionName() { + return PropertiesBundle.message("resource.bundle.renamer.option"); + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public void setEnabled(final boolean enabled) { + } + + @Override + public AutomaticRenamer createRenamer(final PsiElement element, final String newName, final Collection usages) { + final PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile((PsiFile)element); + assert propertiesFile != null; + return new ResourceBundleRenamer(propertiesFile, newName); + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java b/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java index b19510be034b..f044a6db681c 100644 --- a/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java +++ b/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java @@ -275,7 +275,7 @@ public class I18nizeQuickFixDialog extends DialogWrapper implements I18nizeQuick private void propertiesFileChanged() { PropertiesFile propertiesFile = getPropertiesFile(); boolean hasResourceBundle = - propertiesFile != null && propertiesFile.getResourceBundle().getPropertiesFiles(propertiesFile.getProject()).size() > 1; + propertiesFile != null && propertiesFile.getResourceBundle().getPropertiesFiles().size() > 1; myUseResourceBundle.setEnabled(hasResourceBundle); } @@ -497,7 +497,7 @@ public class I18nizeQuickFixDialog extends DialogWrapper implements I18nizeQuick if (propertiesFile == null) return Collections.emptySet(); Collection propertiesFiles; if (isUseResourceBundle()) { - propertiesFiles = propertiesFile.getResourceBundle().getPropertiesFiles(myProject); + propertiesFiles = propertiesFile.getResourceBundle().getPropertiesFiles(); } else { propertiesFiles = Collections.singleton(propertiesFile); diff --git a/plugins/properties/testSrc/com/intellij/lang/properties/PropertiesUtilTest.java b/plugins/properties/testSrc/com/intellij/lang/properties/PropertiesUtilTest.java index 5ebf25d4258f..ec1ae720e129 100644 --- a/plugins/properties/testSrc/com/intellij/lang/properties/PropertiesUtilTest.java +++ b/plugins/properties/testSrc/com/intellij/lang/properties/PropertiesUtilTest.java @@ -16,13 +16,16 @@ package com.intellij.lang.properties; import com.intellij.openapi.vfs.newvfs.impl.StubVirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.FakePsiElement; +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; /** * @author Dmitry Batkovich */ -public class PropertiesUtilTest extends TestCase { +public class PropertiesUtilTest extends LightPlatformCodeInsightFixtureTestCase { public void testBaseNameWithoutLocale() { assertBaseNameEquals("property-file.properties", "property-file"); @@ -62,14 +65,8 @@ public class PropertiesUtilTest extends TestCase { assertBaseNameEquals("Base_Page.utf8.properties", "Base_Page.utf8"); } - private static void assertBaseNameEquals(final String propertyFileName, final String expectedBaseName) { - final String actualBaseName = PropertiesUtil.getBaseName(new StubVirtualFile() { - @NotNull - @Override - public String getName() { - return propertyFileName; - } - }); + private void assertBaseNameEquals(final String propertyFileName, final String expectedBaseName) { + final String actualBaseName = PropertiesUtil.getBaseName(myFixture.configureByText(propertyFileName, "")); assertEquals(expectedBaseName, actualBaseName); } } diff --git a/plugins/properties/testSrc/com/intellij/lang/properties/ResourceBundleRenameTest.java b/plugins/properties/testSrc/com/intellij/lang/properties/ResourceBundleRenameTest.java new file mode 100644 index 000000000000..f8c435817ea4 --- /dev/null +++ b/plugins/properties/testSrc/com/intellij/lang/properties/ResourceBundleRenameTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties; + +import com.intellij.lang.properties.refactoring.rename.ResourceBundleRenamerFactory; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.psi.PsiFile; +import com.intellij.refactoring.rename.RenameProcessor; +import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory; +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; + +/** + * @author Dmitry Batkovich + */ +public class ResourceBundleRenameTest extends LightPlatformCodeInsightFixtureTestCase { + + public void testRenameResourceBundleEntryFile() { + final PsiFile toRenameFile = myFixture.addFileToProject("old_p.properties", ""); + final PsiFile toCheck = myFixture.addFileToProject("old_p_en.properties", ""); + + final RenameProcessor processor = new RenameProcessor(getProject(), toRenameFile, "new_p.properties", true, true); + for (AutomaticRenamerFactory factory : Extensions.getExtensions(AutomaticRenamerFactory.EP_NAME)) { + if (factory instanceof ResourceBundleRenamerFactory) { + processor.addRenamerFactory(factory); + } + } + processor.run(); + + assertEquals("new_p_en.properties", toCheck.getName()); + } + + public void testRenamePropertyKey() { + final PsiFile toCheckFile = myFixture.addFileToProject("p.properties", "key=value"); + myFixture.configureByText("p_en.properties", "key=en_value"); + myFixture.renameElementAtCaret("new_key"); + assertEquals(toCheckFile.getText(), "new_key=value"); + } +} diff --git a/plugins/structuralsearch/source/com/intellij/structuralsearch/JavaPredefinedConfigurations.java b/plugins/structuralsearch/source/com/intellij/structuralsearch/JavaPredefinedConfigurations.java index b0cf6c086a25..a82c941c0fa2 100644 --- a/plugins/structuralsearch/source/com/intellij/structuralsearch/JavaPredefinedConfigurations.java +++ b/plugins/structuralsearch/source/com/intellij/structuralsearch/JavaPredefinedConfigurations.java @@ -148,7 +148,7 @@ class JavaPredefinedConfigurations { createSearchTemplateInfo( SSRBundle.message("predefined.configuration.static.fields.without.final"), - "class '_Class {\n static '_Type 'Variable*:[ script( \"!__context__.hasModifierProperty(\"final\")\" ) ] = '_Init?;\n}", + "class '_Class {\n static '_Type 'Variable+:[ script( \"!__context__.hasModifierProperty(\"final\")\" ) ] = '_Init?;\n}", CLASS_TYPE ),