From 3d309f7765816b866b2f2a1a2187273a9c6f06ec Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Wed, 10 Nov 2010 18:45:03 +0300 Subject: [PATCH 01/55] IDEA-59753 "Un-inject language" and "Language injection settings" intentions should not be shown for non-configurable injections --- .../tree/injected/MultiHostRegistrarImpl.java | 4 + .../inject/java/ConcatenationInjector.java | 133 +++++++++--------- .../inject/DefaultLanguageInjector.java | 4 +- .../inject/EditInjectionSettingsAction.java | 28 ++-- .../inject/InjectLanguageAction.java | 20 ++- .../intelliLang/inject/InjectorUtils.java | 13 ++ .../inject/LanguageInjectionSupport.java | 2 + .../inject/TemporaryPlacesInjector.java | 1 + .../inject/TemporaryPlacesRegistry.java | 36 ++++- .../inject/UnInjectLanguageAction.java | 25 ++-- .../inject/xml/XmlLanguageInjector.java | 4 +- 11 files changed, 160 insertions(+), 110 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java index 4012bd83dc09..8f1afc94defb 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java @@ -82,6 +82,10 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar { cleared = true; } + public List> getResult() { + return result; + } + @NotNull public MultiHostRegistrar startInjecting(@NotNull Language language) { escapers = new SmartList>(); diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java index 3d93f59e4594..0d349a04ea03 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/ConcatenationInjector.java @@ -53,13 +53,66 @@ import java.util.*; public class ConcatenationInjector implements ConcatenationAwareInjector { private final Configuration myConfiguration; private final Project myProject; - private Pair>> myAnnoIndex; - private CachedValue> myXmlIndex; + private final CachedValue> myAnnoIndex; + private final CachedValue> myXmlIndex; + private final LanguageInjectionSupport mySupport; public ConcatenationInjector(Configuration configuration, Project project) { myConfiguration = configuration; myProject = project; + mySupport = InjectorUtils.findInjectionSupport(LanguageInjectionSupport.JAVA_SUPPORT_ID); + myXmlIndex = CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider>() { + public Result> compute() { + final Map, BaseInjection> map = new THashMap, BaseInjection>(); + for (BaseInjection injection : myConfiguration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) { + for (InjectionPlace place : injection.getInjectionPlaces()) { + if (!place.isEnabled() || place.getElementPattern() == null) continue; + map.put(place.getElementPattern(), injection); + } + } + final Set stringSet = PatternValuesIndex.buildStringIndex(map.keySet()); + final Result> r = new Result>(stringSet, myConfiguration); + r.setLockValue(true); + return r; + } + }, false); + myAnnoIndex = CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider>() { + public Result> compute() { + final String annotationClass = myConfiguration.getLanguageAnnotationClass(); + final Collection result = new THashSet(); + final ArrayList annoClasses = new ArrayList(3); + annoClasses.add(StringUtil.getShortName(annotationClass)); + for (int cursor = 0; cursor < annoClasses.size(); cursor++) { + final String annoClass = annoClasses.get(cursor); + for (PsiAnnotation annotation : JavaAnnotationIndex.getInstance() + .get(annoClass, myProject, GlobalSearchScope.allScope(myProject))) { + final PsiElement modList = annotation.getParent(); + if (!(modList instanceof PsiModifierList)) continue; + final PsiElement element = modList.getParent(); + if (element instanceof PsiParameter) { + final PsiElement scope = ((PsiParameter)element).getDeclarationScope(); + if (scope instanceof PsiNamedElement) { + ContainerUtil.addIfNotNull(((PsiNamedElement)scope).getName(), result); + } + else { + ContainerUtil.addIfNotNull(((PsiNamedElement)element).getName(), result); + } + } + else if (element instanceof PsiNamedElement) { + if (element instanceof PsiClass && ((PsiClass)element).isAnnotationType()) { + final String s = ((PsiClass)element).getName(); + if (!annoClasses.contains(s)) annoClasses.add(s); + } + else { + ContainerUtil.addIfNotNull(((PsiNamedElement)element).getName(), result); + } + } + } + } + return new Result>(result, PsiModificationTracker.MODIFICATION_COUNT, myConfiguration); + } + }, false); } public void getLanguagesToInject(@NotNull final MultiHostRegistrar registrar, @NotNull PsiElement... operands) { @@ -67,8 +120,11 @@ public class ConcatenationInjector implements ConcatenationAwareInjector { final PsiFile containingFile = operands[0].getContainingFile(); new InjectionProcessor(myConfiguration, operands) { @Override - protected void processInjection(Language language, List> list) { + protected void processInjection(Language language, + List> list, + boolean xmlInjection) { InjectorUtils.registerInjection(language, list, containingFile, registrar); + InjectorUtils.registerSupport(mySupport, xmlInjection, registrar); } @Override @@ -236,21 +292,21 @@ public class ConcatenationInjector implements ConcatenationAwareInjector { if (prefix != null) injection.setPrefix(prefix); if (suffix != null) injection.setSuffix(suffix); if (id != null) injection.setInjectedLanguageId(id); - processInjectionWithContext(myUnparsable, injection); + processInjectionWithContext(myUnparsable, injection, false); return false; } return true; } protected boolean processXmlInjections(BaseInjection injection, PsiModifierListOwner owner, PsiMethod method, int paramIndex) { - processInjectionWithContext(myUnparsable, injection); + processInjectionWithContext(myUnparsable, injection, true); if (injection.isTerminal()) { return false; } return true; } - private void processInjectionWithContext(boolean unparsable, BaseInjection injection) { + private void processInjectionWithContext(boolean unparsable, BaseInjection injection, boolean xmlInjection) { final Language language = InjectedLanguage.findLanguageById(injection.getInjectedLanguageId()); if (language == null) return; final boolean separateFiles = !injection.isSingleFile() && StringUtil.isNotEmpty(injection.getValuePattern()); @@ -305,19 +361,21 @@ public class ConcatenationInjector implements ConcatenationAwareInjector { if (!result.isEmpty()) { if (separateFiles) { for (Trinity trinity : result) { - processInjection(language, Collections.singletonList(trinity)); + processInjection(language, Collections.singletonList(trinity), xmlInjection); } } else { for (Trinity trinity : result) { trinity.first.putUserData(LanguageInjectionSupport.HAS_UNPARSABLE_FRAGMENTS, unparsableRef.get()); } - processInjection(language, result); + processInjection(language, result, xmlInjection); } } } - protected void processInjection(Language language, List> list) { + protected void processInjection(Language language, + List> list, + boolean xmlInjection) { } protected boolean areThereInjectionsWithName(String methodName, boolean annoOnly) { @@ -343,65 +401,10 @@ public class ConcatenationInjector implements ConcatenationAwareInjector { public Collection getAnnotatedElementsValue() { // note: external annotations not supported - final String annotationClass = myConfiguration.getLanguageAnnotationClass(); - if (myAnnoIndex == null || !Comparing.equal(myAnnoIndex.first, annotationClass)) { - myAnnoIndex = Pair.create(annotationClass, CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider>() { - public Result> compute() { - final Collection result = new THashSet(); - final ArrayList annoClasses = new ArrayList(3); - annoClasses.add(StringUtil.getShortName(annotationClass)); - for (int cursor = 0; cursor < annoClasses.size(); cursor++) { - final String annoClass = annoClasses.get(cursor); - for (PsiAnnotation annotation : JavaAnnotationIndex.getInstance() - .get(annoClass, myProject, GlobalSearchScope.allScope(myProject))) { - final PsiElement modList = annotation.getParent(); - if (!(modList instanceof PsiModifierList)) continue; - final PsiElement element = modList.getParent(); - if (element instanceof PsiParameter) { - final PsiElement scope = ((PsiParameter)element).getDeclarationScope(); - if (scope instanceof PsiNamedElement) { - ContainerUtil.addIfNotNull(((PsiNamedElement)scope).getName(), result); - } - else { - ContainerUtil.addIfNotNull(((PsiNamedElement)element).getName(), result); - } - } - else if (element instanceof PsiNamedElement) { - if (element instanceof PsiClass && ((PsiClass)element).isAnnotationType()) { - final String s = ((PsiClass)element).getName(); - if (!annoClasses.contains(s)) annoClasses.add(s); - } - else { - ContainerUtil.addIfNotNull(((PsiNamedElement)element).getName(), result); - } - } - } - } - return new Result>(result, PsiModificationTracker.MODIFICATION_COUNT, myConfiguration); - } - }, false)); - } - return myAnnoIndex.second.getValue(); + return myAnnoIndex.getValue(); } private Collection getXmlAnnotatedElementsValue() { - if (myXmlIndex == null) { - myXmlIndex = CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider>() { - public Result> compute() { - final Map, BaseInjection> map = new THashMap, BaseInjection>(); - for (BaseInjection injection : myConfiguration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) { - for (InjectionPlace place : injection.getInjectionPlaces()) { - if (!place.isEnabled() || place.getElementPattern() == null) continue; - map.put(place.getElementPattern(), injection); - } - } - final Set stringSet = PatternValuesIndex.buildStringIndex(map.keySet()); - final Result> r = new Result>(stringSet, myConfiguration); - r.setLockValue(true); - return r; - } - }, false); - } return myXmlIndex.getValue(); } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/DefaultLanguageInjector.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/DefaultLanguageInjector.java index 782832dfb68e..6515bbdf3b8a 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/DefaultLanguageInjector.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/DefaultLanguageInjector.java @@ -24,9 +24,6 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.Trinity; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLanguageInjectionHost; -import com.intellij.util.NullableFunction; -import com.intellij.util.containers.ContainerUtil; -import gnu.trove.THashSet; import org.intellij.plugins.intelliLang.Configuration; import org.intellij.plugins.intelliLang.inject.config.BaseInjection; import org.jetbrains.annotations.NotNull; @@ -68,6 +65,7 @@ public final class DefaultLanguageInjector implements MultiHostInjector { list.add(Trinity.create((PsiLanguageInjectionHost)host, injectedLanguage, range)); } InjectorUtils.registerInjection(language, list, host.getContainingFile(), registrar); + InjectorUtils.registerSupport(support, true, registrar); break; } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/EditInjectionSettingsAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/EditInjectionSettingsAction.java index e287084d6107..931620e9c4ba 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/EditInjectionSettingsAction.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/EditInjectionSettingsAction.java @@ -18,15 +18,12 @@ package org.intellij.plugins.intelliLang.inject; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.LowPriorityAction; +import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; @@ -37,9 +34,6 @@ import org.intellij.plugins.intelliLang.InjectionsSettingsUI; import org.jetbrains.annotations.NotNull; import java.util.Collections; -import java.util.List; - -import static org.intellij.plugins.intelliLang.inject.InjectLanguageAction.findInjectionHost; /** * @author Gregory.Shrago @@ -58,10 +52,11 @@ public class EditInjectionSettingsAction implements IntentionAction, LowPriority } public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - final PsiLanguageInjectionHost host = findInjectionHost(editor, file); - if (host == null) return false; - final List> injectedPsi = InjectedLanguageUtil.getInjectedPsiFiles(host); - return injectedPsi != null && !injectedPsi.isEmpty(); + final int offset = editor.getCaretModel().getOffset(); + final PsiFile psiFile = InjectedLanguageUtil.findInjectedPsiNoCommit(file, offset); + if (psiFile == null) return false; + final LanguageInjectionSupport support = psiFile.getUserData(LanguageInjectionSupport.SETTINGS_EDITOR); + return support != null; } public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { @@ -73,13 +68,16 @@ public class EditInjectionSettingsAction implements IntentionAction, LowPriority } private static void invokeImpl(Project project, Editor editor, PsiFile file) { - final PsiLanguageInjectionHost host = findInjectionHost(editor, file); + final PsiFile psiFile = InjectedLanguageUtil.findInjectedPsiNoCommit(file, editor.getCaretModel().getOffset()); + if (psiFile == null) return; + final PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(project).getInjectionHost(psiFile); if (host == null) return; + final LanguageInjectionSupport support = psiFile.getUserData(LanguageInjectionSupport.SETTINGS_EDITOR); + if (support == null) return; try { - for (LanguageInjectionSupport support : Extensions.getExtensions(LanguageInjectionSupport.EP_NAME)) { - if (support.editInjectionInPlace(host)) return; + if (!support.editInjectionInPlace(host)) { + ShowSettingsUtil.getInstance().editConfigurable(project, new InjectionsSettingsUI(project, Configuration.getInstance())); } - ShowSettingsUtil.getInstance().editConfigurable(project, new InjectionsSettingsUI(project, Configuration.getInstance())); } finally { FileContentUtil.reparseFiles(project, Collections.emptyList(), true); diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java index 8719cbdb552d..20b7c449627d 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java @@ -16,7 +16,7 @@ package org.intellij.plugins.intelliLang.inject; import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.ide.DataManager; +import com.intellij.ide.ui.ListCellRendererWrapper; import com.intellij.injected.editor.EditorWindow; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; @@ -45,7 +45,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -86,7 +85,7 @@ public class InjectLanguageAction implements IntentionAction { } public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - doChooseLanguageToInject(new Processor() { + doChooseLanguageToInject(editor, new Processor() { public boolean process(final String languageId) { if (project.isDisposed()) return false; ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -108,7 +107,7 @@ public class InjectLanguageAction implements IntentionAction { for (LanguageInjectionSupport support : Extensions.getExtensions(LanguageInjectionSupport.EP_NAME)) { if (support.addInjectionInPlace(language, host)) return; } - TemporaryPlacesRegistry.getInstance(project).addHostWithUndo(host, InjectedLanguage.create(languageId)); + TemporaryPlacesRegistry.getInstance(project).getLanguageInjectionSupport().addInjectionInPlace(language, host); } finally { FileContentUtil.reparseFiles(project, Collections.emptyList(), true); @@ -119,22 +118,19 @@ public class InjectLanguageAction implements IntentionAction { return Configuration.getInstance().setHostInjectionEnabled(host, Collections.singleton(languageId), true); } - private static boolean doChooseLanguageToInject(final Processor onChosen) { + private static boolean doChooseLanguageToInject(Editor editor, final Processor onChosen) { final String[] langIds = InjectedLanguage.getAvailableLanguageIDs(); Arrays.sort(langIds); final JList list = new JBList(langIds); - list.setCellRenderer(new DefaultListCellRenderer() { + list.setCellRenderer(new ListCellRendererWrapper(list.getCellRenderer()) { @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - final String aValue = (String)value; - final Language language = InjectedLanguage.findLanguageById(aValue); + public void customize(JList list, String value, int index, boolean selected, boolean hasFocus) { + final Language language = InjectedLanguage.findLanguageById(value); assert language != null; final FileType ft = language.getAssociatedFileType(); setIcon(ft != null ? ft.getIcon() : new EmptyIcon(16)); setText(value + (ft != null ? " (" + ft.getDescription() + ")" : "")); - return this; } }); new PopupChooserBuilder(list).setItemChoosenCallback(new Runnable() { @@ -143,7 +139,7 @@ public class InjectLanguageAction implements IntentionAction { onChosen.process(string); } }).setFilteringEnabled(new Function.Self()) - .createPopup().showInBestPositionFor(DataManager.getInstance().getDataContext()); + .createPopup().showInBestPositionFor(editor); return true; } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java index 5eff854e0266..32ba3b3a162c 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java @@ -22,10 +22,13 @@ import com.intellij.lang.LiteralEscaper; import com.intellij.lang.injection.MultiHostRegistrar; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.Trinity; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiLanguageInjectionHost; +import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl; +import com.intellij.psi.impl.source.tree.injected.Place; import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -211,4 +214,14 @@ public class InjectorUtils { } return false; } + + public static void registerSupport(@NotNull LanguageInjectionSupport support, boolean settingsAvailable, @NotNull MultiHostRegistrar registrar) { + final List> result = ((MultiHostRegistrarImpl)registrar).getResult(); + if (result == null || result.isEmpty()) return; + final PsiFile psiFile = result.get(result.size() - 1).second; + psiFile.putUserData(LanguageInjectionSupport.INJECTOR_SUPPORT, support); + if (settingsAvailable) { + psiFile.putUserData(LanguageInjectionSupport.SETTINGS_EDITOR, support); + } + } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java index f2614f6ed6dc..21ebfb1acb4e 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java @@ -40,6 +40,8 @@ public abstract class LanguageInjectionSupport { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.intelliLang.languageSupport"); public static Key HAS_UNPARSABLE_FRAGMENTS = Key.create("HAS_UNPARSABLE_FRAGMENTS"); + public static Key INJECTOR_SUPPORT = Key.create("INJECTOR_SUPPORT"); + public static Key SETTINGS_EDITOR = Key.create("SETTINGS_EDITOR"); @NonNls public static final String XML_SUPPORT_ID = "xml"; @NonNls public static final String JAVA_SUPPORT_ID = "java"; diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesInjector.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesInjector.java index dfaa756395f7..b29eeda1e1c0 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesInjector.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesInjector.java @@ -60,6 +60,7 @@ public class TemporaryPlacesInjector implements MultiHostInjector { InjectorUtils.registerInjection( place.language.getLanguage(), Collections.singletonList(Trinity.create(host, place.language, manipulator.getRangeInElement(host))), context.getContainingFile(), registrar); + InjectorUtils.registerSupport(myRegistry.getLanguageInjectionSupport(), false, registrar); } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesRegistry.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesRegistry.java index a05a3fd82bde..227ed9d95187 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesRegistry.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/TemporaryPlacesRegistry.java @@ -16,6 +16,7 @@ package org.intellij.plugins.intelliLang.inject; +import com.intellij.lang.Language; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; @@ -23,9 +24,11 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.SmartPointerManager; import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.util.ArrayUtil; import com.intellij.util.PairProcessor; import com.intellij.util.containers.ContainerUtil; import org.intellij.plugins.intelliLang.Configuration; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.List; @@ -36,6 +39,30 @@ import java.util.List; public class TemporaryPlacesRegistry { private final Project myProject; private final List myTempPlaces = ContainerUtil.createEmptyCOWList(); + private final LanguageInjectionSupport myInjectorSupport = new AbstractLanguageInjectionSupport() { + @NotNull + @Override + public String getId() { + return "temp"; + } + + @NotNull + @Override + public Class[] getPatternClasses() { + return ArrayUtil.EMPTY_CLASS_ARRAY; + } + + @Override + public boolean addInjectionInPlace(Language language, PsiLanguageInjectionHost host) { + addHostWithUndo(host, InjectedLanguage.create(language.getID())); + return true; + } + + @Override + public boolean removeInjectionInPlace(PsiLanguageInjectionHost psiElement) { + return removeHostWithUndo(myProject, psiElement); + } + }; public static TemporaryPlacesRegistry getInstance(final Project project) { return ServiceManager.getService(project, TemporaryPlacesRegistry.class); @@ -65,9 +92,9 @@ public class TemporaryPlacesRegistry { }); } - public void removeHostWithUndo(final Project project, final PsiLanguageInjectionHost host) { + public boolean removeHostWithUndo(final Project project, final PsiLanguageInjectionHost host) { final List places = getTempInjectionsSafe(host); - if (places.isEmpty()) return; + if (places.isEmpty()) return false; Configuration.replaceInjectionsWithUndo(project, Collections.emptyList(), places, Collections.emptyList(), new PairProcessor, List>() { public boolean process(final List add, final List remove) { @@ -76,6 +103,7 @@ public class TemporaryPlacesRegistry { return true; } }); + return true; } public void addHostWithUndo(final PsiLanguageInjectionHost host, final InjectedLanguage language) { @@ -92,6 +120,10 @@ public class TemporaryPlacesRegistry { }); } + public LanguageInjectionSupport getLanguageInjectionSupport() { + return myInjectorSupport; + } + public static class TemporaryPlace { final InjectedLanguage language; final SmartPsiElementPointer elementPointer; diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java index 2c8187e64c38..a826ff343b4f 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java @@ -18,9 +18,9 @@ package org.intellij.plugins.intelliLang.inject; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.lang.Language; +import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; @@ -38,8 +38,6 @@ import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.List; -import static org.intellij.plugins.intelliLang.inject.InjectLanguageAction.findInjectionHost; - /** * @author Dmitry Avdeev */ @@ -56,10 +54,11 @@ public class UnInjectLanguageAction implements IntentionAction, LowPriorityActio } public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - PsiLanguageInjectionHost host = findInjectionHost(editor, file); - if (host == null) return false; - final List> injectedPsi = InjectedLanguageUtil.getInjectedPsiFiles(host); - return injectedPsi != null && !injectedPsi.isEmpty(); + final int offset = editor.getCaretModel().getOffset(); + final PsiFile psiFile = InjectedLanguageUtil.findInjectedPsiNoCommit(file, offset); + if (psiFile == null) return false; + final LanguageInjectionSupport support = psiFile.getUserData(LanguageInjectionSupport.INJECTOR_SUPPORT); + return support != null; } public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { @@ -71,14 +70,16 @@ public class UnInjectLanguageAction implements IntentionAction, LowPriorityActio } private static void invokeImpl(Project project, Editor editor, PsiFile file) { - final PsiLanguageInjectionHost host = findInjectionHost(editor, file); + final PsiFile psiFile = InjectedLanguageUtil.findInjectedPsiNoCommit(file, editor.getCaretModel().getOffset()); + if (psiFile == null) return; + final PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(project).getInjectionHost(psiFile); if (host == null) return; + final LanguageInjectionSupport support = psiFile.getUserData(LanguageInjectionSupport.INJECTOR_SUPPORT); + if (support == null) return; try { - if (defaultFunctionalityWorked(host)) return; - for (LanguageInjectionSupport support : Extensions.getExtensions(LanguageInjectionSupport.EP_NAME)) { - if (support.removeInjectionInPlace(host)) return; + if (!support.removeInjectionInPlace(host)) { + defaultFunctionalityWorked(host); } - TemporaryPlacesRegistry.getInstance(project).removeHostWithUndo(project, host); } finally { FileContentUtil.reparseFiles(project, Collections.emptyList(), true); diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java index 42f43a7af672..23790afe6421 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java @@ -19,7 +19,6 @@ package org.intellij.plugins.intelliLang.inject.xml; import com.intellij.lang.Language; import com.intellij.lang.injection.MultiHostInjector; import com.intellij.lang.injection.MultiHostRegistrar; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.Trinity; @@ -58,9 +57,11 @@ public final class XmlLanguageInjector implements MultiHostInjector { private final Configuration myConfiguration; private volatile Trinity> myXmlIndex; + private final LanguageInjectionSupport mySupport; public XmlLanguageInjector(Configuration configuration) { myConfiguration = configuration; + mySupport = InjectorUtils.findInjectionSupport(LanguageInjectionSupport.JAVA_SUPPORT_ID); } @NotNull @@ -85,6 +86,7 @@ public final class XmlLanguageInjector implements MultiHostInjector { ranges.add(textRange.shiftRight(host.getTextRange().getStartOffset())); } InjectorUtils.registerInjection(language, list, containingFile, registrar); + InjectorUtils.registerSupport(mySupport, true, registrar); return true; } }); From ff5edae47c4778fd34677ac6c785527ec2031d91 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 10 Nov 2010 18:50:51 +0300 Subject: [PATCH 02/55] hide disabled action in context menu --- .../openapi/editor/actions/SimplePasteAction.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/SimplePasteAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/SimplePasteAction.java index 9612b0d095d9..bb62fcecc9f3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/SimplePasteAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/SimplePasteAction.java @@ -24,6 +24,8 @@ */ package com.intellij.openapi.editor.actions; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorModificationUtil; @@ -36,6 +38,14 @@ public class SimplePasteAction extends EditorAction { super(new Handler()); } + @Override + public void update(AnActionEvent e) { + super.update(e); + if (ActionPlaces.isPopupPlace(e.getPlace())) { + e.getPresentation().setVisible(e.getPresentation().isEnabled()); + } + } + private static class Handler extends EditorWriteActionHandler { public void executeWriteAction(Editor editor, DataContext dataContext) { if (editor.isColumnMode()) { From b757c46918ab3944411170293765a36cbadfec45 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 10 Nov 2010 16:58:10 +0100 Subject: [PATCH 03/55] IDEA-54767 (Wrong "while statement cannot complete without throwing an exception") --- .../InfiniteLoopStatementInspection.java | 12 +++- .../siyeh/ig/psiutils/ControlFlowUtils.java | 59 ++++++++++++++++++- .../InfiniteLoopStatement.java | 28 +++++++++ .../infinite_loop_statement/expected.xml | 10 ++++ .../InfiniteLoopStatementInspectionTest.java | 11 ++++ 5 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/InfiniteLoopStatement.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/expected.xml create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/InfiniteLoopStatementInspectionTest.java diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/InfiniteLoopStatementInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/InfiniteLoopStatementInspection.java index c6f79d148fcb..c6f3ceab4262 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/InfiniteLoopStatementInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/controlflow/InfiniteLoopStatementInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,22 +27,26 @@ import org.jetbrains.annotations.NotNull; public class InfiniteLoopStatementInspection extends BaseInspection { + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( "infinite.loop.statement.display.name"); } + @Override public boolean isEnabledByDefault() { return true; } + @Override @NotNull protected String buildErrorString(Object... infos) { return InspectionGadgetsBundle.message( "infinite.loop.statement.problem.descriptor"); } + @Override public BaseInspectionVisitor buildVisitor() { return new InfiniteLoopStatementsVisitor(); } @@ -50,12 +54,14 @@ public class InfiniteLoopStatementInspection extends BaseInspection { private static class InfiniteLoopStatementsVisitor extends BaseInspectionVisitor { - @Override public void visitForStatement(@NotNull PsiForStatement statement) { + @Override public void visitForStatement( + @NotNull PsiForStatement statement) { super.visitForStatement(statement); checkStatement(statement); } - @Override public void visitWhileStatement(@NotNull PsiWhileStatement statement) { + @Override public void visitWhileStatement( + @NotNull PsiWhileStatement statement) { super.visitWhileStatement(statement); checkStatement(statement); } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java index 549108284e79..c26c40aa2763 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ControlFlowUtils.java @@ -103,14 +103,16 @@ public class ControlFlowUtils{ final PsiExpression test = loopStatement.getCondition(); final PsiStatement body = loopStatement.getBody(); return statementMayCompleteNormally(body) && !BoolUtils.isTrue(test) - || statementIsBreakTarget(loopStatement); + || statementIsBreakTarget(loopStatement) + || statementContainsContinueToAncestor(loopStatement); } private static boolean whileStatementMayReturnNormally( @NotNull PsiWhileStatement loopStatement){ final PsiExpression test = loopStatement.getCondition(); return !BoolUtils.isTrue(test) - || statementIsBreakTarget(loopStatement); + || statementIsBreakTarget(loopStatement) + || statementContainsContinueToAncestor(loopStatement); } private static boolean forStatementMayReturnNormally( @@ -119,6 +121,9 @@ public class ControlFlowUtils{ if(statementIsBreakTarget(loopStatement)){ return true; } + if (statementContainsContinueToAncestor(loopStatement)) { + return true; + } if(test == null){ return false; } @@ -276,6 +281,19 @@ public class ControlFlowUtils{ return breakFinder.breakFound(); } + private static boolean statementContainsContinueToAncestor( + @NotNull PsiStatement statement){ + PsiElement parent = statement.getParent(); + while (parent instanceof PsiLabeledStatement) { + statement = (PsiStatement) parent; + parent = parent.getParent(); + } + final ContinueToAncestorFinder continueToAncestorFinder = + new ContinueToAncestorFinder(statement); + statement.accept(continueToAncestorFinder); + return continueToAncestorFinder.continueToAncestorFound(); + } + public static boolean statementContainsReturn( @NotNull PsiStatement statement){ final ReturnFinder returnFinder = new ReturnFinder(); @@ -693,4 +711,41 @@ public class ControlFlowUtils{ containsCallToMethod = true; } } + + private static class ContinueToAncestorFinder + extends JavaRecursiveElementVisitor { + + private final PsiStatement statement; + private boolean found = false; + + public ContinueToAncestorFinder(PsiStatement statement) { + this.statement = statement; + } + + @Override + public void visitContinueStatement( + PsiContinueStatement continueStatement) { + if (found) { + return; + } + super.visitContinueStatement(continueStatement); + final PsiIdentifier labelIdentifier = + continueStatement.getLabelIdentifier(); + if (labelIdentifier == null) { + return; + } + final PsiStatement continuedStatement = + continueStatement.findContinuedStatement(); + if (continuedStatement == null) { + return; + } + if (PsiTreeUtil.isAncestor(continuedStatement, statement, true)) { + found = true; + } + } + + public boolean continueToAncestorFound() { + return found; + } + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/InfiniteLoopStatement.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/InfiniteLoopStatement.java new file mode 100644 index 000000000000..58e2475d3892 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/InfiniteLoopStatement.java @@ -0,0 +1,28 @@ +package com.siyeh.igtest.controlflow.infinite_loop_statement; + +public class InfiniteLoopStatement { + + void bla() { + int x = 0; + c: + b: + while (true) { + if (x == 0) { + a: + while (true) { // A warning issued here + x++; + continue b; + } + } + System.out.println("Loop"); + } + } + + void notInfinite1(String s) { + while (true) { + if (s.equals("exit")) { + System.exit(1); + } + } + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/expected.xml new file mode 100644 index 000000000000..ab0e2a3ead56 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/infinite_loop_statement/expected.xml @@ -0,0 +1,10 @@ + + + + InfiniteLoopStatement.java + 9 + Infinite loop statement + <code>while</code> statement cannot complete without throwing an exception #loc + + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/InfiniteLoopStatementInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/InfiniteLoopStatementInspectionTest.java new file mode 100644 index 000000000000..bb81dae6bbb4 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/InfiniteLoopStatementInspectionTest.java @@ -0,0 +1,11 @@ +package com.siyeh.ig.controlflow; + +import com.IGInspectionTestCase; + +public class InfiniteLoopStatementInspectionTest extends IGInspectionTestCase { + + public void test() throws Exception { + doTest("com/siyeh/igtest/controlflow/infinite_loop_statement", + new InfiniteLoopStatementInspection()); + } +} \ No newline at end of file From dc72dcefe9424f79cd2a56f8d8971f433d247f7c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 10 Nov 2010 19:01:06 +0300 Subject: [PATCH 04/55] context menu action to mark directory as source or test source root (IDEA-60259) --- .../actions/MarkExcludeRootAction.java | 45 +++++++ .../projectView/actions/MarkRootAction.java | 122 ++++++++++++++++++ .../projectView/actions/MarkRootGroup.java | 29 +++++ .../actions/MarkTestSourceRootAction.java | 25 ++++ .../src/messages/ActionsBundle.properties | 4 + .../src/idea/LangActions.xml | 5 + 6 files changed, 230 insertions(+) create mode 100644 platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkExcludeRootAction.java create mode 100644 platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java create mode 100644 platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootGroup.java create mode 100644 platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkTestSourceRootAction.java diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkExcludeRootAction.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkExcludeRootAction.java new file mode 100644 index 000000000000..054001b07770 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkExcludeRootAction.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2010 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.ide.projectView.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; + +/** + * @author yole + */ +public class MarkExcludeRootAction extends MarkRootAction { + public MarkExcludeRootAction() { + super(false, true); + } + + @Override + public void actionPerformed(AnActionEvent e) { + VirtualFile[] vFiles = e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY); + String message = vFiles.length == 1 ? FileUtil.toSystemDependentName(vFiles [0].getPath()) : vFiles.length + " selected files"; + final int rc = Messages.showOkCancelDialog(e.getData(PlatformDataKeys.PROJECT), + "Are you sure you would you like to exclude " + message + + " from the project?\nYou can restore excluded folders later using the Project Structure dialog.", + "Mark as Excluded", Messages.getQuestionIcon()); + if (rc != 0) { + return; + } + super.actionPerformed(e); + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java new file mode 100644 index 000000000000..c6fde5bd0de6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2010 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.ide.projectView.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.*; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public class MarkRootAction extends AnAction { + private final boolean myMarkAsTestSources; + private final boolean myMarkAsExcluded; + + public MarkRootAction() { + myMarkAsTestSources = false; + myMarkAsExcluded = false; + } + + protected MarkRootAction(boolean markAsTestSources, boolean markAsExcluded) { + myMarkAsTestSources = markAsTestSources; + myMarkAsExcluded = markAsExcluded; + } + + @Override + public void actionPerformed(AnActionEvent e) { + Module module = e.getData(LangDataKeys.MODULE); + VirtualFile[] vFiles = e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY); + assert vFiles != null; + final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); + for (VirtualFile vFile : vFiles) { + ContentEntry entry = findContentEntry(model, vFile); + if (entry != null) { + final SourceFolder[] sourceFolders = entry.getSourceFolders(); + for (SourceFolder sourceFolder : sourceFolders) { + if (sourceFolder.getFile() == vFile) { + entry.removeSourceFolder(sourceFolder); + break; + } + } + if (myMarkAsExcluded) { + entry.addExcludeFolder(vFile); + } + else { + entry.addSourceFolder(vFile, myMarkAsTestSources); + } + } + } + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + model.commit(); + } + }); + } + + @Nullable + private static ContentEntry findContentEntry(ModifiableRootModel model, VirtualFile vFile) { + final ContentEntry[] contentEntries = model.getContentEntries(); + for (ContentEntry contentEntry : contentEntries) { + final VirtualFile contentEntryFile = contentEntry.getFile(); + if (contentEntryFile != null && VfsUtil.isAncestor(contentEntryFile, vFile, false)) { + return contentEntry; + } + } + return null; + } + + @Override + public void update(AnActionEvent e) { + boolean enabled = canMark(e, myMarkAsTestSources || myMarkAsExcluded, !myMarkAsTestSources || myMarkAsExcluded); + e.getPresentation().setVisible(enabled); + e.getPresentation().setEnabled(enabled); + } + + public static boolean canMark(AnActionEvent e, boolean acceptSourceRoot, boolean acceptTestSourceRoot) { + Module module = e.getData(LangDataKeys.MODULE); + VirtualFile[] vFiles = e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY); + if (module == null || vFiles == null) { + return false; + } + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(module.getProject()).getFileIndex(); + for (VirtualFile vFile : vFiles) { + if (!vFile.isDirectory()) { + return false; + } + if (!fileIndex.isInContent(vFile)) { + return false; + } + if (fileIndex.getSourceRootForFile(vFile) == vFile) { + boolean isTestSourceRoot = fileIndex.isInTestSourceContent(vFile); + if (acceptSourceRoot && !isTestSourceRoot) return true; + if (acceptTestSourceRoot && isTestSourceRoot) return true; + } + if (fileIndex.isInSourceContent(vFile)) { + return false; + } + } + return true; + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootGroup.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootGroup.java new file mode 100644 index 000000000000..f61c8e01f21c --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootGroup.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2010 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.ide.projectView.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; + +/** + * @author yole + */ +public class MarkRootGroup extends DefaultActionGroup { + @Override + public void update(AnActionEvent e) { + e.getPresentation().setVisible(MarkRootAction.canMark(e, true, true)); + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkTestSourceRootAction.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkTestSourceRootAction.java new file mode 100644 index 000000000000..b308ed2974c5 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkTestSourceRootAction.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2010 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.ide.projectView.actions; + +/** + * @author yole + */ +public class MarkTestSourceRootAction extends MarkRootAction { + public MarkTestSourceRootAction() { + super(true, false); + } +} diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 73915631deb4..fdb7e90904d3 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1192,3 +1192,7 @@ action.ConvertIndentsToTabs.description=Convert the indentation in the file or s group.Vcs.Import.text=Import into Version Control group.Vcs.Browse.text=Browse VCS Repository action.Graph.print.reset=Reset +action.MarkSourceRoot.text=Source Root +action.MarkTestSourceRoot.text=Test Source Root +action.MarkExcludeRoot.text=Excluded +group.MarkRootGroup.text=Mark Directory As diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 326a7e36df01..0c163e2eb78a 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -402,6 +402,11 @@ + + + + + From c5a3b5379bcce4c74859d162224a3dc011e723f4 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 10 Nov 2010 11:45:54 +0300 Subject: [PATCH 05/55] eclipse: distinguish src roots corresponding to .classpath root and additional - old roots caused by .classpath root should be deleted from .eml (IDEA-60901) --- .../conversion/IdeaSpecificSettings.java | 28 ++++++++++++++++--- .../libAddLibSource/expected/ws-internals.eml | 2 +- .../libAddVarSource/expected/ws-internals.eml | 2 +- .../expected/ws-internals.eml | 2 +- .../varAddVarSource/expected/ws-internals.eml | 2 +- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java index a624a66d326e..857ea4f603ca 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java @@ -63,6 +63,7 @@ public class IdeaSpecificSettings { @NonNls private static final String PROJECT_RELATED = "project-related"; @NonNls private static final String SRCROOT_ATTR = "srcroot"; + @NonNls private static final String SRCROOT_BIND_ATTR = "bind"; private static final Logger LOG = Logger.getInstance("#" + IdeaSpecificSettings.class.getName()); @NonNls private static final String JAVADOCROOT_ATTR = "javadocroot_attr"; @@ -155,12 +156,20 @@ public class IdeaSpecificSettings { private static void replaceCollapsedByEclipseSourceRoots(Element libElement, Library.ModifiableModel modifiableModel) { String[] srcUrlsFromClasspath = modifiableModel.getUrls(OrderRootType.SOURCES); LOG.assertTrue(srcUrlsFromClasspath.length <= 1); + final String eclipseUrl = srcUrlsFromClasspath.length > 0 ? srcUrlsFromClasspath[0] : null; for (Object r : libElement.getChildren(SRCROOT_ATTR)) { final String url = ((Element)r).getAttributeValue("url"); - modifiableModel.addRoot(url, OrderRootType.SOURCES); - if (srcUrlsFromClasspath != null && srcUrlsFromClasspath.length == 1 && areUrlsPointTheSame(url, srcUrlsFromClasspath[0])) { //remove compound root - modifiableModel.removeRoot(srcUrlsFromClasspath[0], OrderRootType.SOURCES); - srcUrlsFromClasspath = null; + final String bindAttr = ((Element)r).getAttributeValue(SRCROOT_BIND_ATTR); + boolean notBind = bindAttr != null && !Boolean.parseBoolean(bindAttr); + if (notBind) { + modifiableModel.addRoot(url, OrderRootType.SOURCES); + } + else if (eclipseUrl != null && areUrlsPointTheSame(url, eclipseUrl) && !Comparing.strEqual(url, eclipseUrl)) { //todo lost already configured additional src roots + modifiableModel.addRoot(url, OrderRootType.SOURCES); + if (srcUrlsFromClasspath != null && srcUrlsFromClasspath.length == 1) { //remove compound root + modifiableModel.removeRoot(eclipseUrl, OrderRootType.SOURCES); + srcUrlsFromClasspath = null; + } } } } @@ -278,9 +287,20 @@ public class IdeaSpecificSettings { element.setAttribute("scope", scope.name()); if (libraryEntry.isModuleLevel()) { final String[] urls = libraryEntry.getRootUrls(OrderRootType.SOURCES); + String eclipseUrl = null; + if (urls.length > 0) { + eclipseUrl = urls[0]; + final int jarSeparatorIdx = urls[0].indexOf(JarFileSystem.JAR_SEPARATOR); + if (jarSeparatorIdx > -1) { + eclipseUrl = eclipseUrl.substring(0, jarSeparatorIdx); + } + } for (String url : urls) { Element srcElement = new Element(SRCROOT_ATTR); srcElement.setAttribute("url", url); + if (!areUrlsPointTheSame(url, eclipseUrl)) { + srcElement.setAttribute(SRCROOT_BIND_ATTR, String.valueOf(false)); + } element.addContent(srcElement); } diff --git a/plugins/eclipse/testData/modification/libAddLibSource/expected/ws-internals.eml b/plugins/eclipse/testData/modification/libAddLibSource/expected/ws-internals.eml index 704aac1ff256..7a601331a8cb 100644 --- a/plugins/eclipse/testData/modification/libAddLibSource/expected/ws-internals.eml +++ b/plugins/eclipse/testData/modification/libAddLibSource/expected/ws-internals.eml @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/plugins/eclipse/testData/modification/libAddVarSource/expected/ws-internals.eml b/plugins/eclipse/testData/modification/libAddVarSource/expected/ws-internals.eml index 647f0441dee2..e5440bbb9821 100644 --- a/plugins/eclipse/testData/modification/libAddVarSource/expected/ws-internals.eml +++ b/plugins/eclipse/testData/modification/libAddVarSource/expected/ws-internals.eml @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/plugins/eclipse/testData/modification/libvarAddVarSource/expected/ws-internals.eml b/plugins/eclipse/testData/modification/libvarAddVarSource/expected/ws-internals.eml index 647f0441dee2..e5440bbb9821 100644 --- a/plugins/eclipse/testData/modification/libvarAddVarSource/expected/ws-internals.eml +++ b/plugins/eclipse/testData/modification/libvarAddVarSource/expected/ws-internals.eml @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/plugins/eclipse/testData/modification/varAddVarSource/expected/ws-internals.eml b/plugins/eclipse/testData/modification/varAddVarSource/expected/ws-internals.eml index 1dad506da7c1..9d4e01e07d67 100644 --- a/plugins/eclipse/testData/modification/varAddVarSource/expected/ws-internals.eml +++ b/plugins/eclipse/testData/modification/varAddVarSource/expected/ws-internals.eml @@ -4,6 +4,6 @@ - + \ No newline at end of file From 92b0210e9f35b3af6579ece60fd1677ba45ba4d1 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 10 Nov 2010 15:22:38 +0300 Subject: [PATCH 06/55] do not mix PsiImmediateClassType and PsiReferenceClassType: presentable name for ref type is calc based on reference name only (IDEA-56135) --- .../daemon/impl/analysis/HighlightClassUtil.java | 6 +++--- .../quickFix/changeExtendsToImplements/after7.java | 9 +++++++++ .../quickFix/changeExtendsToImplements/before7.java | 9 +++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/after7.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/before7.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index e9da4dfba81a..2a7c843c2ae3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -399,7 +399,7 @@ public class HighlightClassUtil { static HighlightInfo checkExtendsClassAndImplementsInterface(PsiReferenceList referenceList, JavaResolveResult resolveResult, - PsiJavaCodeReferenceElement context) { + PsiJavaCodeReferenceElement ref) { PsiClass aClass = (PsiClass)referenceList.getParent(); boolean isImplements = referenceList.equals(aClass.getImplementsList()); boolean isInterface = aClass.isInterface(); @@ -409,10 +409,10 @@ public class HighlightClassUtil { PsiClass extendFrom = (PsiClass)resolveResult.getElement(); if (extendFrom.isInterface() != mustBeInterface) { errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, - context, + ref, mustBeInterface ? INTERFACE_EXPECTED : NO_INTERFACE_EXPECTED); PsiClassType type = - JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor()); + JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(ref); QuickFixAction.registerQuickFixAction(errorResult, new ChangeExtendsToImplementsFix(aClass, type)); } return errorResult; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/after7.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/after7.java new file mode 100644 index 000000000000..82c28405a968 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/after7.java @@ -0,0 +1,9 @@ +// "Change 'implements b' to 'extends b'" "true" +class a extends b { +} + +class b {} + +class C { + static class D {} +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/before7.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/before7.java new file mode 100644 index 000000000000..7c7a75fc8ef8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeExtendsToImplements/before7.java @@ -0,0 +1,9 @@ +// "Change 'implements b' to 'extends b'" "true" +class a implements b { +} + +class b {} + +class C { + static class D {} +} From 1e430d23b53e43c6971b233ef14eaff8a272c9d7 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 10 Nov 2010 16:01:29 +0300 Subject: [PATCH 07/55] pull up: protect pulled class private members (IDEA-60729) --- .../memberPullUp/PullUpHelper.java | 27 +++++++++++++++++++ ...ivateInnerClassWithPrivateConstructor.java | 15 +++++++++++ ...nnerClassWithPrivateConstructor_after.java | 15 +++++++++++ .../com/intellij/refactoring/PullUpTest.java | 4 +++ 4 files changed, 61 insertions(+) create mode 100644 java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor.java create mode 100644 java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor_after.java diff --git a/java/java-impl/src/com/intellij/refactoring/memberPullUp/PullUpHelper.java b/java/java-impl/src/com/intellij/refactoring/memberPullUp/PullUpHelper.java index 8b5b90821a0a..ba0e17e0adea 100644 --- a/java/java-impl/src/com/intellij/refactoring/memberPullUp/PullUpHelper.java +++ b/java/java-impl/src/com/intellij/refactoring/memberPullUp/PullUpHelper.java @@ -124,6 +124,33 @@ public class PullUpHelper extends BaseRefactoringProcessor{ if (info.isToAbstract() || willBeUsedInSubclass(modifierListOwner, movedMembers, myTargetSuperClass, mySourceClass)) { PsiUtil.setModifierProperty(modifierListOwner, PsiModifier.PROTECTED, true); } + if (modifierListOwner instanceof PsiClass) { + modifierListOwner.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitMethod(PsiMethod method) { + check(method); + } + + @Override + public void visitField(PsiField field) { + check(field); + } + + @Override + public void visitClass(PsiClass aClass) { + check(aClass); + super.visitClass(aClass); + } + + private void check(PsiMember member) { + if (member.hasModifierProperty(PsiModifier.PRIVATE)) { + if (willBeUsedInSubclass(member, movedMembers, myTargetSuperClass, mySourceClass)) { + PsiUtil.setModifierProperty(member, PsiModifier.PROTECTED, true); + } + } + } + }); + } } ChangeContextUtil.encodeContextInfo(info.getMember(), true); } diff --git a/java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor.java b/java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor.java new file mode 100644 index 000000000000..40e3e394af48 --- /dev/null +++ b/java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor.java @@ -0,0 +1,15 @@ +public class B extends A { + private void f(){ + new C(); + } + + private static class C{ + private C(){ + + } + } +} + +//A.java +class A { +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor_after.java b/java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor_after.java new file mode 100644 index 000000000000..0c9baaf55407 --- /dev/null +++ b/java/java-tests/testData/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor_after.java @@ -0,0 +1,15 @@ +public class B extends A { + private void f(){ + new C(); + } + +} + +//A.java +class A { + protected static class C{ + protected C(){ + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/PullUpTest.java b/java/java-tests/testSrc/com/intellij/refactoring/PullUpTest.java index d1973047161b..53ef64abd01f 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/PullUpTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/PullUpTest.java @@ -42,6 +42,10 @@ public class PullUpTest extends LightCodeInsightTestCase { doTest(new RefactoringTestUtil.MemberDescriptor("C", PsiClass.class), new RefactoringTestUtil.MemberDescriptor("D", PsiClass.class)); } + + public void testPullUpPrivateInnerClassWithPrivateConstructor() throws Exception { + doTest(new RefactoringTestUtil.MemberDescriptor("C", PsiClass.class)); + } public void testPullUpAndAbstractize() throws Exception { doTest(new RefactoringTestUtil.MemberDescriptor("a", PsiMethod.class), From d9ac38574e29f57869634ab0fb8f178d4f1d61fe Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 10 Nov 2010 19:01:25 +0300 Subject: [PATCH 08/55] validate reference types after template was done --- .../IntroduceVariableBase.java | 9 ++-- .../ReassignVariableUtil.java | 45 +++++++++---------- .../ui/TypeSelectorManagerImpl.java | 32 ++++++++----- 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index c8c42e8440f0..8704779e2662 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -26,8 +26,10 @@ package com.intellij.refactoring.introduceVariable; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.codeInsight.highlighting.HighlightManager; +import com.intellij.codeInsight.intention.impl.TypeExpression; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupManager; -import com.intellij.codeInsight.template.TemplateBuilderImpl; +import com.intellij.codeInsight.template.*; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.util.PropertiesComponent; import com.intellij.lang.LanguageRefactoringSupport; @@ -463,6 +465,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme getSettings(project, editor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, choice); if (!settings.isOK()) return; typeSelectorManager.setAllOccurences(choice != OccurrencesChooser.ReplaceChoice.NO); + final TypeExpression expression = new TypeExpression(project, typeSelectorManager.getTypesForAll()); final RangeMarker exprMarker = editor.getDocument().createRangeMarker(expr.getTextRange()); final SuggestedNameInfo suggestedName = getSuggestedName(settings.getSelectedType(), expr); final Runnable runnable = @@ -482,8 +485,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme @Override protected void addAdditionalVariables(TemplateBuilderImpl builder) { final PsiTypeElement typeElement = elementToRename.getTypeElement(); - builder.replaceElement(typeElement, "Variable_Type", ReassignVariableUtil - .createExpression(typeSelectorManager, typeElement.getText()), false, true); + builder.replaceElement(typeElement, "Variable_Type", + ReassignVariableUtil.createExpression(expression, typeElement.getText()), false, true); } }; renamer.setAdvertisementText( diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/ReassignVariableUtil.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/ReassignVariableUtil.java index aab01479d103..18741296d3d9 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/ReassignVariableUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/ReassignVariableUtil.java @@ -12,6 +12,7 @@ */ package com.intellij.refactoring.introduceVariable; +import com.intellij.codeInsight.intention.impl.TypeExpression; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.codeInsight.lookup.PsiTypeLookupItem; @@ -29,6 +30,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.VisualPosition; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Key; import com.intellij.psi.*; @@ -151,30 +153,6 @@ public class ReassignVariableUtil { } } - static Expression createExpression(final TypeSelectorManagerImpl typeSelectorManager, final String defaultText) { - final PsiType[] types = typeSelectorManager.getTypesForAll(); - return new Expression() { - @Override - public com.intellij.codeInsight.template.Result calculateResult(ExpressionContext context) { - return new TextResult(defaultText); - } - - @Override - public com.intellij.codeInsight.template.Result calculateQuickResult(ExpressionContext context) { - return new TextResult(defaultText); - } - - @Override - public LookupElement[] calculateLookupItems(ExpressionContext context) { - LookupElement[] result = new LookupElement[types.length]; - for (int i = 0, typesLength = types.length; i < typesLength; i++) { - result[i] = PsiTypeLookupItem.createLookupItem(types[i], null); - } - return result; - } - }; - } - @Nullable static String getAdvertisementText(Editor editor, PsiDeclarationStatement declaration, PsiType type, PsiType[] typesForAll) { final VariablesProcessor processor = findVariablesOfType(editor, declaration, type); @@ -187,4 +165,23 @@ public class ReassignVariableUtil { } return typesForAll.length > 1 ? "Press Shift Tab to change type" : null; } + + public static Expression createExpression(final TypeExpression expression, final String defaultType) { + return new Expression() { + @Override + public com.intellij.codeInsight.template.Result calculateResult(ExpressionContext context) { + return new TextResult(defaultType); + } + + @Override + public com.intellij.codeInsight.template.Result calculateQuickResult(ExpressionContext context) { + return new TextResult(defaultType); + } + + @Override + public LookupElement[] calculateLookupItems(ExpressionContext context) { + return expression.calculateLookupItems(context); + } + }; + } } diff --git a/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java b/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java index 839f9180ed29..8137af5677ad 100644 --- a/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java +++ b/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java @@ -38,6 +38,7 @@ import java.util.*; * @author dsl */ public class TypeSelectorManagerImpl implements TypeSelectorManager { + private SmartTypePointer myPointer; private PsiType myDefaultType; private final PsiExpression myMainOccurence; private final PsiExpression[] myOccurrences; @@ -46,6 +47,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { private final boolean myIsOneSuggestion; private TypeSelector myTypeSelector; private final PsiElementFactory myFactory; + private final SmartTypePointerManager mySmartTypePointerManager; private ExpectedTypesProvider.ExpectedClassProvider myOccurrenceClassProvider; private ExpectedTypesProvider myExpectedTypesProvider; @@ -59,7 +61,8 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { public TypeSelectorManagerImpl(Project project, PsiType type, PsiExpression[] occurrences, boolean areTypesDirected) { myFactory = JavaPsiFacade.getInstance(project).getElementFactory(); - myDefaultType = type; + mySmartTypePointerManager = SmartTypePointerManager.getInstance(project); + setDefaultType(type); myMainOccurence = null; myOccurrences = occurrences; myExpectedTypesProvider = ExpectedTypesProvider.getInstance(project); @@ -83,7 +86,8 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { PsiExpression mainOccurence, PsiExpression[] occurrences) { myFactory = JavaPsiFacade.getInstance(project).getElementFactory(); - myDefaultType = type; + mySmartTypePointerManager = SmartTypePointerManager.getInstance(project); + setDefaultType(type); myMainOccurence = mainOccurence; myOccurrences = occurrences; myExpectedTypesProvider = ExpectedTypesProvider.getInstance(project); @@ -94,7 +98,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { if (containingMethod != null) { if (PsiUtil.resolveClassInType(type) != null) { - myDefaultType = checkIfTypeAccessible(type, project, containingMethod); + setDefaultType(checkIfTypeAccessible(type, project, containingMethod)); } } @@ -132,7 +136,15 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { } public PsiType getDefaultType() { - return myDefaultType; + if (myDefaultType.isValid()) { + return myDefaultType; + } + return myPointer.getType(); + } + + public void setDefaultType(PsiType defaultType) { + myDefaultType = defaultType; + myPointer = mySmartTypePointerManager.createSmartTypePointer(defaultType); } private ExpectedTypesProvider.ExpectedClassProvider createOccurrenceClassProvider() { @@ -151,7 +163,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { final ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(myMainOccurence, false, myOccurrenceClassProvider, false); final ArrayList allowedTypes = new ArrayList(); - RefactoringHierarchyUtil.processSuperTypes(myDefaultType, new RefactoringHierarchyUtil.SuperTypeVisitor() { + RefactoringHierarchyUtil.processSuperTypes(getDefaultType(), new RefactoringHierarchyUtil.SuperTypeVisitor() { public void visitType(PsiType aType) { checkIfAllowed(aType); } @@ -193,7 +205,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { } final ArrayList allowedTypes = new ArrayList(); - RefactoringHierarchyUtil.processSuperTypes(myDefaultType, new RefactoringHierarchyUtil.SuperTypeVisitor() { + RefactoringHierarchyUtil.processSuperTypes(getDefaultType(), new RefactoringHierarchyUtil.SuperTypeVisitor() { public void visitType(PsiType aType) { checkIfAllowed(aType); } @@ -234,18 +246,18 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { for (int index = 0; index < result.size(); index++) { PsiType psiType = result.get(index); - if (psiType.equals(myDefaultType)) { + if (psiType.equals(getDefaultType())) { result.remove(index); break; } } - final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(myDefaultType); + final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(getDefaultType()); if (unboxedType != null) { result.remove(unboxedType); result.add(0, unboxedType); } - result.add(0, myDefaultType); + result.add(0, getDefaultType()); return result; } @@ -292,7 +304,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { } private String getStatsKey() { - return "IntroduceVariable##" + serialize(myDefaultType); + return "IntroduceVariable##" + serialize(getDefaultType()); } private String serialize(PsiType type) { From dcfcced72c29262f079c9a48a51bd9a67ccf9d89 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Wed, 10 Nov 2010 19:11:28 +0300 Subject: [PATCH 09/55] IDEA-60793 Language Injection on annotation parameter --- .../intelliLang/inject/java/JavaLanguageInjectionSupport.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java index 2d91d1a81caf..dbb1063450fe 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java @@ -274,7 +274,8 @@ public class JavaLanguageInjectionSupport extends AbstractLanguageInjectionSuppo if (referenceElement != null) { PsiElement resolved = referenceElement.resolve(); if (resolved != null) { - PsiMethod[] methods = ((PsiClass)resolved).findMethodsByName(((PsiNameValuePair)parent).getName(), false); + final String name = ((PsiNameValuePair)parent).getName(); + PsiMethod[] methods = ((PsiClass)resolved).findMethodsByName(name == null? PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME : name, false); if (methods.length == 1) { return methods[0]; } From a37ee5e0cfd79ea4747c45105eb995a7570c3138 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Wed, 10 Nov 2010 19:17:33 +0300 Subject: [PATCH 10/55] IDEA-60793 red methods problem part --- .../config/ui/MethodParameterPanel.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java index c5b55a4329e7..7838e6a13023 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java @@ -191,16 +191,16 @@ public class MethodParameterPanel extends AbstractInjectionPanel() { - public boolean value(PsiParameter p) { - return MethodParameterInjection.isInjectable(p.getType(), p.getProject()); - } - }) == null) continue; - final MethodParameterInjection.MethodInfo info = MethodParameterInjection.createMethodInfo(method); - if (!visitedSignatures.add(info.getMethodSignature())) continue; - myData.put(method, info); + if (MethodParameterInjection.isInjectable(method.getReturnType(), method.getProject()) || + ContainerUtil.find(method.getParameterList().getParameters(), new Condition() { + public boolean value(PsiParameter p) { + return MethodParameterInjection.isInjectable(p.getType(), p.getProject()); + } + }) != null) { + final MethodParameterInjection.MethodInfo info = MethodParameterInjection.createMethodInfo(method); + if (!visitedSignatures.add(info.getMethodSignature())) continue; + myData.put(method, info); + } } } } From 8171c11d0f350c136f0bc1bbe9b84f36cb97ab67 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 10 Nov 2010 19:47:52 +0300 Subject: [PATCH 11/55] LST to use own lock instead of write action --- .../openapi/vcs/ex/LineStatusTracker.java | 377 +++++++++--------- .../vcs/impl/LineStatusTrackerManager.java | 156 ++++---- 2 files changed, 262 insertions(+), 271 deletions(-) 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 165b10347f56..4223b9a1547e 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 @@ -20,14 +20,13 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsBundle; @@ -46,6 +45,7 @@ import java.util.ListIterator; */ public class LineStatusTracker { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.ex.LineStatusTracker"); + private final Object myLock = new Object(); // true -> have contents private BaseLoadState myBaseLoaded; @@ -72,36 +72,36 @@ public class LineStatusTracker { } public void initialize(@NotNull final String upToDateContent) { - ApplicationManager.getApplication().isReadAccessAllowed(); + ApplicationManager.getApplication().assertIsDispatchThread(); LOG.assertTrue(BaseLoadState.LOADING == myBaseLoaded); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - try { - myUpToDateDocument.setReadOnly(false); - myUpToDateDocument.replaceString(0, myUpToDateDocument.getTextLength(), upToDateContent); - myUpToDateDocument.setReadOnly(true); - reinstallRanges(); + synchronized (myLock) { + try { + myUpToDateDocument.setReadOnly(false); + myUpToDateDocument.replaceString(0, myUpToDateDocument.getTextLength(), upToDateContent); + myUpToDateDocument.setReadOnly(true); + reinstallRanges(); - if (myDocumentListener == null) { - myDocumentListener = new MyDocumentListener(); - myDocument.addDocumentListener(myDocumentListener); - } - } - finally { - myBaseLoaded = BaseLoadState.LOADED; + if (myDocumentListener == null) { + myDocumentListener = new MyDocumentListener(); + myDocument.addDocumentListener(myDocumentListener); } } - }); + finally { + myBaseLoaded = BaseLoadState.LOADED; + } + } } private void reinstallRanges() { - myApplication.assertWriteAccessAllowed(); + myApplication.assertReadAccessAllowed(); - removeHighlightersFromMarkupModel(); - myRanges = new RangesBuilder(myDocument, myUpToDateDocument).getRanges(); - for (final Range range : myRanges) { - range.setHighlighter(createHighlighter(range)); + synchronized (myLock) { + removeHighlightersFromMarkupModel(); + myRanges = new RangesBuilder(myDocument, myUpToDateDocument).getRanges(); + for (final Range range : myRanges) { + range.setHighlighter(createHighlighter(range)); + } } } @@ -137,16 +137,13 @@ public class LineStatusTracker { } public void release() { - myApplication.runWriteAction(new Runnable() { - @Override - public void run() { - if (myDocumentListener != null) { - myDocument.removeDocumentListener(myDocumentListener); - } - removeHighlightersFromMarkupModel(); - myRanges.clear(); + synchronized (myLock) { + if (myDocumentListener != null) { + myDocument.removeDocumentListener(myDocumentListener); } - }); + removeHighlightersFromMarkupModel(); + myRanges.clear(); + } } public Document getDocument() { @@ -160,39 +157,38 @@ public class LineStatusTracker { public List getRanges() { myApplication.assertReadAccessAllowed(); - return myRanges; + synchronized (myLock) { + return myRanges; + } } public Document getUpToDateDocument() { + myApplication.assertIsDispatchThread(); return myUpToDateDocument; } public void startBulkUpdate() { - myApplication.runWriteAction(new Runnable() { - @Override - public void run() { - myBulkUpdate = true; - removeHighlightersFromMarkupModel(); - myRanges.clear(); - } - }); + synchronized (myLock) { + myBulkUpdate = true; + removeHighlightersFromMarkupModel(); + myRanges.clear(); + } } private void removeHighlightersFromMarkupModel() { final MarkupModel markupModel = myDocument.getMarkupModel(myProject); - for (Range range : myRanges) { - markupModel.removeHighlighter(range.getHighlighter()); + synchronized (myLock) { + for (Range range : myRanges) { + markupModel.removeHighlighter(range.getHighlighter()); + } } } public void finishBulkUpdate() { - myApplication.runWriteAction(new Runnable() { - @Override - public void run() { - myBulkUpdate = false; - reinstallRanges(); - } - }); + synchronized (myLock) { + myBulkUpdate = false; + reinstallRanges(); + } } /** @@ -200,24 +196,18 @@ public class LineStatusTracker { * false -> load was already started; after contents is loaded, */ public boolean resetForBaseRevisionLoad() { - return myApplication.runReadAction(new Computable() { - @Override - public Boolean compute() { - if (BaseLoadState.LOADING == myBaseLoaded) return false; - return myApplication.runWriteAction(new Computable() { - @Override - public Boolean compute() { - myUpToDateDocument.setReadOnly(false); - myUpToDateDocument.setText(""); - myUpToDateDocument.setReadOnly(true); - removeHighlightersFromMarkupModel(); - myRanges.clear(); - myBaseLoaded = BaseLoadState.LOADING; - return true; - } - }); - } - }); + myApplication.assertReadAccessAllowed(); + + synchronized (myLock) { + if (BaseLoadState.LOADING == myBaseLoaded) return false; + myUpToDateDocument.setReadOnly(false); + myUpToDateDocument.setText(""); + myUpToDateDocument.setReadOnly(true); + removeHighlightersFromMarkupModel(); + myRanges.clear(); + myBaseLoaded = BaseLoadState.LOADING; + return true; + } } private class MyDocumentListener extends DocumentAdapter { @@ -231,39 +221,41 @@ public class LineStatusTracker { if (myBulkUpdate || (BaseLoadState.LOADED != myBaseLoaded)) return; myApplication.assertWriteAccessAllowed(); - try { - myFirstChangedLine = myDocument.getLineNumber(e.getOffset()); - myLastChangedLine = myDocument.getLineNumber(e.getOffset() + e.getOldLength()); - if (StringUtil.endsWithChar(e.getOldFragment(), '\n')) myLastChangedLine++; + synchronized (myLock) { + try { + myFirstChangedLine = myDocument.getLineNumber(e.getOffset()); + myLastChangedLine = myDocument.getLineNumber(e.getOffset() + e.getOldLength()); + if (StringUtil.endsWithChar(e.getOldFragment(), '\n')) myLastChangedLine++; - myLinesBeforeChange = myDocument.getLineNumber(e.getOffset() + e.getOldLength()) - myDocument.getLineNumber(e.getOffset()); + myLinesBeforeChange = myDocument.getLineNumber(e.getOffset() + e.getOldLength()) - myDocument.getLineNumber(e.getOffset()); - Range firstChangedRange = getLastRangeBeforeLine(myFirstChangedLine); + Range firstChangedRange = getLastRangeBeforeLine(myFirstChangedLine); - if (firstChangedRange == null) { - myUpToDateFirstLine = myFirstChangedLine; - } - else if (firstChangedRange.containsLine(myFirstChangedLine)) { - myFirstChangedLine = firstChangedRange.getOffset1(); - myUpToDateFirstLine = firstChangedRange.getUOffset1(); - } - else { - myUpToDateFirstLine = firstChangedRange.getUOffset2() + (myFirstChangedLine - firstChangedRange.getOffset2()); - } + if (firstChangedRange == null) { + myUpToDateFirstLine = myFirstChangedLine; + } + else if (firstChangedRange.containsLine(myFirstChangedLine)) { + myFirstChangedLine = firstChangedRange.getOffset1(); + myUpToDateFirstLine = firstChangedRange.getUOffset1(); + } + else { + myUpToDateFirstLine = firstChangedRange.getUOffset2() + (myFirstChangedLine - firstChangedRange.getOffset2()); + } - Range myLastChangedRange = getLastRangeBeforeLine(myLastChangedLine); + Range myLastChangedRange = getLastRangeBeforeLine(myLastChangedLine); - if (myLastChangedRange == null) { - myUpToDateLastLine = myLastChangedLine; + if (myLastChangedRange == null) { + myUpToDateLastLine = myLastChangedLine; + } + else if (myLastChangedRange.containsLine(myLastChangedLine)) { + myUpToDateLastLine = myLastChangedRange.getUOffset2(); + myLastChangedLine = myLastChangedRange.getOffset2(); + } + else { + myUpToDateLastLine = myLastChangedRange.getUOffset2() + (myLastChangedLine - myLastChangedRange.getOffset2()); + } + } catch (ProcessCanceledException ignore) { } - else if (myLastChangedRange.containsLine(myLastChangedLine)) { - myUpToDateLastLine = myLastChangedRange.getUOffset2(); - myLastChangedLine = myLastChangedRange.getOffset2(); - } - else { - myUpToDateLastLine = myLastChangedRange.getUOffset2() + (myLastChangedLine - myLastChangedRange.getOffset2()); - } - } catch (ProcessCanceledException ignore) { } } @@ -281,47 +273,49 @@ public class LineStatusTracker { if (myBulkUpdate || (BaseLoadState.LOADED != myBaseLoaded)) return; myApplication.assertWriteAccessAllowed(); - try { + synchronized (myLock) { + try { - int line = myDocument.getLineNumber(e.getOffset() + e.getNewLength()); - int linesAfterChange = line - myDocument.getLineNumber(e.getOffset()); - int linesShift = linesAfterChange - myLinesBeforeChange; + int line = myDocument.getLineNumber(e.getOffset() + e.getNewLength()); + int linesAfterChange = line - myDocument.getLineNumber(e.getOffset()); + int linesShift = linesAfterChange - myLinesBeforeChange; - List rangesAfterChange = getRangesAfter(myRanges, myLastChangedLine); - List rangesBeforeChange = getRangesBefore(myRanges, myFirstChangedLine); + List rangesAfterChange = getRangesAfter(myRanges, myLastChangedLine); + List rangesBeforeChange = getRangesBefore(myRanges, myFirstChangedLine); - List changedRanges = getChangedRanges(myFirstChangedLine, myLastChangedLine); + List changedRanges = getChangedRanges(myFirstChangedLine, myLastChangedLine); - int newSize = rangesBeforeChange.size() + changedRanges.size() + rangesAfterChange.size(); - if (myRanges.size() != newSize) { - LOG.info("Ranges: " + myRanges + "; first changed line: " + myFirstChangedLine + "; last changed line: " + myLastChangedLine); - LOG.assertTrue(false); - } - - - myLastChangedLine += linesShift; - - - List newChangedRanges = getNewChangedRanges(); - - shiftRanges(rangesAfterChange, linesShift); - - if (!changedRanges.equals(newChangedRanges)) { - replaceRanges(changedRanges, newChangedRanges); - - myRanges = new ArrayList(); - - myRanges.addAll(rangesBeforeChange); - myRanges.addAll(newChangedRanges); - myRanges.addAll(rangesAfterChange); - - myRanges = mergeRanges(myRanges); - - for (Range range : myRanges) { - if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range)); + int newSize = rangesBeforeChange.size() + changedRanges.size() + rangesAfterChange.size(); + if (myRanges.size() != newSize) { + LOG.info("Ranges: " + myRanges + "; first changed line: " + myFirstChangedLine + "; last changed line: " + myLastChangedLine); + LOG.assertTrue(false); } + + + myLastChangedLine += linesShift; + + + List newChangedRanges = getNewChangedRanges(); + + shiftRanges(rangesAfterChange, linesShift); + + if (!changedRanges.equals(newChangedRanges)) { + replaceRanges(changedRanges, newChangedRanges); + + myRanges = new ArrayList(); + + myRanges.addAll(rangesBeforeChange); + myRanges.addAll(newChangedRanges); + myRanges.addAll(rangesAfterChange); + + myRanges = mergeRanges(myRanges); + + for (Range range : myRanges) { + if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range)); + } + } + } catch (ProcessCanceledException ignore) { } - } catch (ProcessCanceledException ignore) { } } @@ -390,49 +384,57 @@ public class LineStatusTracker { @Nullable Range getNextRange(final Range range) { - final int index = myRanges.indexOf(range); - if (index == myRanges.size() - 1) return null; - return myRanges.get(index + 1); + synchronized (myLock) { + final int index = myRanges.indexOf(range); + if (index == myRanges.size() - 1) return null; + return myRanges.get(index + 1); + } } @Nullable Range getPrevRange(final Range range) { - final int index = myRanges.indexOf(range); - if (index <= 0) return null; - return myRanges.get(index - 1); + synchronized (myLock) { + final int index = myRanges.indexOf(range); + if (index <= 0) return null; + return myRanges.get(index - 1); + } } @Nullable public Range getNextRange(final int line) { - final Range currentRange = getRangeForLine(line); - if (currentRange != null) { - return getNextRange(currentRange); - } - - for (final Range range : myRanges) { - if (line > range.getOffset1() || line > range.getOffset2()) { - continue; + synchronized (myLock) { + final Range currentRange = getRangeForLine(line); + if (currentRange != null) { + return getNextRange(currentRange); } - return range; + + for (final Range range : myRanges) { + if (line > range.getOffset1() || line > range.getOffset2()) { + continue; + } + return range; + } + return null; } - return null; } @Nullable public Range getPrevRange(final int line) { - final Range currentRange = getRangeForLine(line); - if (currentRange != null) { - return getPrevRange(currentRange); - } - - for (ListIterator iterator = myRanges.listIterator(myRanges.size()); iterator.hasPrevious();) { - final Range range = iterator.previous(); - if (range.getOffset1() > line) { - continue; + synchronized (myLock) { + final Range currentRange = getRangeForLine(line); + if (currentRange != null) { + return getPrevRange(currentRange); } - return range; + + for (ListIterator iterator = myRanges.listIterator(myRanges.size()); iterator.hasPrevious();) { + final Range range = iterator.previous(); + if (range.getOffset1() > line) { + continue; + } + return range; + } + return null; } - return null; } public static List getRangesBefore(List ranges, int line) { @@ -455,43 +457,49 @@ public class LineStatusTracker { @Nullable public Range getRangeForLine(final int line) { - for (final Range range : myRanges) { - if (range.getType() == Range.DELETED && line == range.getOffset1()) { - return range; - } - else if (line >= range.getOffset1() && line < range.getOffset2()) { - return range; + synchronized (myLock) { + for (final Range range : myRanges) { + if (range.getType() == Range.DELETED && line == range.getOffset1()) { + return range; + } + else if (line >= range.getOffset1() && line < range.getOffset2()) { + return range; + } } + return null; } - return null; } public void rollbackChanges(final Range range) { myApplication.assertWriteAccessAllowed(); - TextRange currentTextRange = getCurrentTextRange(range); + synchronized (myLock) { + TextRange currentTextRange = getCurrentTextRange(range); - if (range.getType() == Range.INSERTED) { - myDocument - .replaceString(currentTextRange.getStartOffset(), Math.min(currentTextRange.getEndOffset() + 1, myDocument.getTextLength()), ""); - } - else if (range.getType() == Range.DELETED) { - String upToDateContent = getUpToDateContent(range); - myDocument.insertString(currentTextRange.getStartOffset(), upToDateContent); - } - else { + if (range.getType() == Range.INSERTED) { + myDocument + .replaceString(currentTextRange.getStartOffset(), Math.min(currentTextRange.getEndOffset() + 1, myDocument.getTextLength()), ""); + } + else if (range.getType() == Range.DELETED) { + String upToDateContent = getUpToDateContent(range); + myDocument.insertString(currentTextRange.getStartOffset(), upToDateContent); + } + else { - String upToDateContent = getUpToDateContent(range); - myDocument.replaceString(currentTextRange.getStartOffset(), Math.min(currentTextRange.getEndOffset() + 1, myDocument.getTextLength()), - upToDateContent); + String upToDateContent = getUpToDateContent(range); + myDocument.replaceString(currentTextRange.getStartOffset(), Math.min(currentTextRange.getEndOffset() + 1, myDocument.getTextLength()), + upToDateContent); + } } } public String getUpToDateContent(Range range) { - TextRange textRange = getUpToDateRange(range); - final int startOffset = textRange.getStartOffset(); - final int endOffset = Math.min(textRange.getEndOffset() + 1, myUpToDateDocument.getTextLength()); - return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset).toString(); + synchronized (myLock) { + TextRange textRange = getUpToDateRange(range); + final int startOffset = textRange.getStartOffset(); + final int endOffset = Math.min(textRange.getEndOffset() + 1, myUpToDateDocument.getTextLength()); + return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset).toString(); + } } Project getProject() { @@ -539,7 +547,7 @@ public class LineStatusTracker { } public static LineStatusTracker createOn(final Document doc, final Project project) { - final Document document = EditorFactory.getInstance().createDocument(""); + final Document document = new DocumentImpl(true); return new LineStatusTracker(doc, document, project); } @@ -548,8 +556,9 @@ public class LineStatusTracker { } public void baseRevisionLoadFailed() { - myApplication.assertWriteAccessAllowed(); - myBaseLoaded = BaseLoadState.FAILED; + synchronized (myLock) { + myBaseLoaded = BaseLoadState.FAILED; + } } public static enum BaseLoadState { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java index 9e899d0d94e9..bee5b12b96bf 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/LineStatusTrackerManager.java @@ -42,7 +42,6 @@ import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; @@ -68,6 +67,7 @@ import java.util.Map; public class LineStatusTrackerManager implements ProjectComponent, LineStatusTrackerManagerI { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.impl.LineStatusTrackerManager"); + public final Object myLock = new Object(); public static LineStatusTrackerManagerI getInstance(final Project project) { if (System.getProperty(IGNORE_CHANGEMARKERS_KEY) != null) { @@ -116,15 +116,17 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra myDisposable = new Disposable() { @Override public void dispose() { - final Collection trackers = myLineStatusTrackers.values(); - final LineStatusTracker[] lineStatusTrackers = trackers.toArray(new LineStatusTracker[trackers.size()]); - for (final LineStatusTracker tracker : lineStatusTrackers) { - releaseTracker(tracker.getDocument()); - } + synchronized (myLock) { + final Collection trackers = myLineStatusTrackers.values(); + final LineStatusTracker[] lineStatusTrackers = trackers.toArray(new LineStatusTracker[trackers.size()]); + for (final LineStatusTracker tracker : lineStatusTrackers) { + releaseTracker(tracker.getDocument()); + } - myLineStatusTrackers.clear(); - assert myLineStatusUpdateAlarms.isEmpty(); - myLineStatusUpdateAlarms.clear(); + myLineStatusTrackers.clear(); + assert myLineStatusUpdateAlarms.isEmpty(); + myLineStatusUpdateAlarms.clear(); + } } }; Disposer.register(myProject, myDisposable); @@ -178,12 +180,15 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra @Override public LineStatusTracker getLineStatusTracker(final Document document) { myApplication.assertReadAccessAllowed(); - if ((! myProject.isOpen()) || myProject.isDisposed()) return null; - return myLineStatusTrackers.get(document); + + synchronized (myLock) { + return myLineStatusTrackers.get(document); + } } private void resetTracker(@NotNull final VirtualFile virtualFile) { + myApplication.assertReadAccessAllowed(); if ((! myProject.isOpen()) || myProject.isDisposed()) return; final Document document = FileDocumentManager.getInstance().getCachedDocument(virtualFile); @@ -194,49 +199,43 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra log("resetting tracker for file " + virtualFile.getPath()); - final LineStatusTracker tracker = myLineStatusTrackers.get(document); final boolean editorOpened = myFileEditorManager.isFileOpen(virtualFile); final boolean shouldBeInstalled = shouldBeInstalled(virtualFile) && editorOpened; - if (tracker == null && (! shouldBeInstalled)) return; + synchronized (myLock) { + final LineStatusTracker tracker = myLineStatusTrackers.get(document); - myApplication.runWriteAction(new Runnable() { - @Override - public void run() { - // remove ? - if (tracker != null) { - if (! shouldBeInstalled) { - releaseTracker(document); - return; - } else if ((LineStatusTracker.BaseLoadState.LOADING == tracker.getBaseLoaded())) { - return; // will be recalculated - } else { - tracker.resetForBaseRevisionLoad(); - startAlarm(document, virtualFile); - } - } else if (shouldBeInstalled) { - installTracker(virtualFile, document); + if (tracker == null && (! shouldBeInstalled)) return; + + if (tracker != null) { + if (! shouldBeInstalled) { + releaseTracker(document); + return; + } else if ((LineStatusTracker.BaseLoadState.LOADING == tracker.getBaseLoaded())) { + return; // will be recalculated + } else { + tracker.resetForBaseRevisionLoad(); + startAlarm(document, virtualFile); } + } else if (shouldBeInstalled) { + installTracker(virtualFile, document); } - }); + } } private void releaseTracker(final Document document) { if ((! myProject.isOpen()) || myProject.isDisposed()) return; - myApplication.runWriteAction(new Runnable() { - @Override - public void run() { - final Alarm alarm = myLineStatusUpdateAlarms.remove(document); - if (alarm != null) { - alarm.cancelAllRequests(); - } - final LineStatusTracker tracker = myLineStatusTrackers.remove(document); - if (tracker != null) { - tracker.release(); - } + synchronized (myLock) { + final Alarm alarm = myLineStatusUpdateAlarms.remove(document); + if (alarm != null) { + alarm.cancelAllRequests(); } - }); + final LineStatusTracker tracker = myLineStatusTrackers.remove(document); + if (tracker != null) { + tracker.release(); + } + } } private boolean shouldBeInstalled(final VirtualFile virtualFile) { @@ -261,25 +260,24 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra } private void installTracker(final VirtualFile virtualFile, final Document document) { - myApplication.runWriteAction(new Runnable() { - @Override - public void run() { - if (myLineStatusTrackers.containsKey(document)) return; - assert !myLineStatusUpdateAlarms.containsKey(document); + synchronized (myLock) { + if (myLineStatusTrackers.containsKey(document)) return; + assert !myLineStatusUpdateAlarms.containsKey(document); - final LineStatusTracker tracker = LineStatusTracker.createOn(document, myProject); - myLineStatusTrackers.put(document, tracker); + final LineStatusTracker tracker = LineStatusTracker.createOn(document, myProject); + myLineStatusTrackers.put(document, tracker); - startAlarm(document, virtualFile); - } - }); + startAlarm(document, virtualFile); + } } private void startAlarm(final Document document, final VirtualFile virtualFile) { - myApplication.assertWriteAccessAllowed(); + myApplication.assertReadAccessAllowed(); final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); - myLineStatusUpdateAlarms.put(document, alarm); + synchronized (myLock) { + myLineStatusUpdateAlarms.put(document, alarm); + } alarm.addRequest(new BaseRevisionLoader(alarm, document, virtualFile), 10); } @@ -297,14 +295,12 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra @Override public void run() { myAlarm.cancelAllRequests(); - final Boolean removed = myApplication.runReadAction(new Computable() { - @Override - public Boolean compute() { - final Alarm removed = myLineStatusUpdateAlarms.remove(myDocument); - return removed != null; + synchronized (myLock) { + final Alarm removed = myLineStatusUpdateAlarms.remove(myDocument); + if (removed == null) { + return; } - }); - if (! Boolean.TRUE.equals(removed)) return; + } if ((! myProject.isOpen()) || myProject.isDisposed()) return; if (! myVirtualFile.isValid()) { @@ -323,15 +319,13 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra final String converted = StringUtil.convertLineSeparators(lastUpToDateContent); myApplication.invokeLater(new Runnable() { public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - log("initializing tracker for file " + myVirtualFile.getPath()); - final LineStatusTracker tracker = myLineStatusTrackers.get(myDocument); - if (tracker != null) { - tracker.initialize(converted); - } + synchronized (myLock) { + log("initializing tracker for file " + myVirtualFile.getPath()); + final LineStatusTracker tracker = myLineStatusTrackers.get(myDocument); + if (tracker != null) { + tracker.initialize(converted); } - }); + } } }, new Condition() { @Override @@ -342,24 +336,13 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra } private void reportTrackerBaseLoadFailed() { - myApplication.invokeLater(new Runnable() { - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - log("base revision load failed for file " + myVirtualFile.getPath()); - final LineStatusTracker tracker = myLineStatusTrackers.get(myDocument); - if (tracker != null) { - tracker.baseRevisionLoadFailed(); - } - } - }); + synchronized (myLock) { + log("base revision load failed for file " + myVirtualFile.getPath()); + final LineStatusTracker tracker = myLineStatusTrackers.get(myDocument); + if (tracker != null) { + tracker.baseRevisionLoadFailed(); } - }, new Condition() { - @Override - public boolean value(final Object ignore) { - return (! myProject.isOpen()) || myProject.isDisposed(); - } - }); + } } } @@ -369,7 +352,6 @@ public class LineStatusTrackerManager implements ProjectComponent, LineStatusTra final VirtualFile[] openFiles = myFileEditorManager.getOpenFiles(); for(final VirtualFile openFile: openFiles) { - // write action inside is sufficient level resetTracker(openFile); } } From 74b938172c593c71fabca9715957c8ca031333ab Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Wed, 10 Nov 2010 19:55:07 +0300 Subject: [PATCH 12/55] EA-23341 - NPE: BreadcrumbsXmlWrapper$1.fileStatusesChanged --- .../src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index 1636c1aa0e4a..95b2b2815ecd 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -86,7 +86,7 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener Date: Tue, 9 Nov 2010 16:26:45 +0300 Subject: [PATCH 13/55] We don't need 20 velocity parsers taking 2+ megabytes. --- .../src/com/intellij/ide/fileTemplates/FileTemplateUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java index e7d9109ff31e..0c8b736da947 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java @@ -222,6 +222,7 @@ public class FileTemplateUtil{ Velocity.setProperty("class.resource.loader.class", MyClasspathResourceLoader.class.getName()); Velocity.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, modifiedPatternsPath.getAbsolutePath()); Velocity.setProperty(RuntimeConstants.INPUT_ENCODING, FileTemplate.ourEncoding); + Velocity.setProperty(RuntimeConstants.PARSER_POOL_SIZE, 3); Velocity.init(); ourVelocityInitialized = true; } From 918fcfcfbd97875e03ee74ef6b3d0aee0c066e40 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Tue, 9 Nov 2010 20:15:12 +0300 Subject: [PATCH 14/55] Lighter cache for scopes --- .../psi/impl/file/impl/FileManagerImpl.java | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java index 5c45c9c3bd68..ed82047b391b 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java @@ -50,11 +50,9 @@ import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.impl.source.resolve.FileContextUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.FileContentUtil; +import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ConcurrentHashMap; import com.intellij.util.containers.ConcurrentWeakValueHashMap; import com.intellij.util.messages.MessageBusConnection; @@ -81,6 +79,21 @@ public class FileManagerImpl implements FileManager { private final ConcurrentMap myVFileToPsiDirMap = new ConcurrentHashMap(); private final ConcurrentWeakValueHashMap myVFileToViewProviderMap = new ConcurrentWeakValueHashMap(); + private final Map myDefaultUseScopesCache = new ConcurrentFactoryMap() { + @Override + protected GlobalSearchScope create(VirtualFile key) { + GlobalSearchScope scope = getInherentResolveScope(key); + for (ResolveScopeEnlarger enlarger : ResolveScopeEnlarger.EP_NAME.getExtensions()) { + final SearchScope extra = enlarger.getAdditionalResolveScope(key, myManager.getProject()); + if (extra != null) { + scope = scope.union(extra); + } + } + + return scope; + } + }; + private boolean myInitialized = false; private boolean myDisposed = false; @@ -108,6 +121,13 @@ public class FileManagerImpl implements FileManager { recalcAllViewProviders(); } }); + + manager.registerRunnableToRunOnChange(new Runnable() { + @Override + public void run() { + myDefaultUseScopesCache.clear(); + } + }); } public void processQueue() { @@ -394,19 +414,7 @@ public class FileManagerImpl implements FileManager { } private GlobalSearchScope getDefaultResolveScope(@NotNull final Project project, @NotNull PsiFile psiFile, @NotNull final VirtualFile vFile) { - return CachedValuesManager.getManager(project).getCachedValue(psiFile, new CachedValueProvider() { - @Override - public Result compute() { - GlobalSearchScope scope = getInherentResolveScope(vFile); - for (ResolveScopeEnlarger enlarger : ResolveScopeEnlarger.EP_NAME.getExtensions()) { - final SearchScope extra = enlarger.getAdditionalResolveScope(vFile, project); - if (extra != null) { - scope = scope.union(extra); - } - } - return Result.create(scope, PsiModificationTracker.MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); - } - }); + return myDefaultUseScopesCache.get(vFile); } private GlobalSearchScope getInherentResolveScope(VirtualFile vFile) { From c0c76dfb895c2342605222665a5a6921505f5851 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Wed, 10 Nov 2010 19:49:47 +0300 Subject: [PATCH 15/55] Revert "+misha: really cache matcher." This reverts commit 656ae6c3391bd1eca2077d9136a035989908f98d. --- .../intellij/ide/util/gotoByName/matchers/DefaultMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java index 6389740fb013..3ba89706b309 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java @@ -18,6 +18,7 @@ package com.intellij.ide.util.gotoByName.matchers; import com.intellij.ide.util.gotoByName.ChooseByNameBase; import com.intellij.ide.util.gotoByName.ChooseByNameModel; import com.intellij.ide.util.gotoByName.CustomMatcherModel; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; @@ -47,7 +48,6 @@ public class DefaultMatcher implements EntityMatcher { public boolean nameMatches(String shortPattern, String shortName) { if (myPattern == null || !myPattern.equals(shortPattern)) { myMatcher = buildPatternMatcher(shortPattern); - myPattern = shortPattern; } return matches(shortPattern, myMatcher, shortName); From 45b7b2d77dd39127f4ce958f4cfa32b24a783b98 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Wed, 10 Nov 2010 20:04:18 +0300 Subject: [PATCH 16/55] Revert "ChooseByNameBase - extract Matcher (now a custom matcher can be defined)" This reverts commit b9c9dcce0006e1a07a348081546b2232d4d513c2. --- .../ide/util/gotoByName/ChooseByNameBase.java | 307 ++++++++++++------ .../util/gotoByName/ChooseByNamePopup.java | 30 +- .../gotoByName/matchers/DefaultMatcher.java | 194 ----------- .../gotoByName/matchers/EntityMatcher.java | 26 -- 4 files changed, 213 insertions(+), 344 deletions(-) delete mode 100644 platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java delete mode 100644 platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/EntityMatcher.java diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index f08e4072b3d8..56a061a95da7 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -20,12 +20,11 @@ import com.intellij.Patches; import com.intellij.ide.IdeBundle; import com.intellij.ide.actions.CopyReferenceAction; import com.intellij.ide.ui.UISettings; -import com.intellij.ide.util.gotoByName.matchers.DefaultMatcher; -import com.intellij.ide.util.gotoByName.matchers.EntityMatcher; import com.intellij.openapi.MnemonicHelper; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.keymap.KeymapManager; @@ -33,15 +32,19 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.*; import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.NameUtil; import com.intellij.psi.statistics.StatisticsInfo; import com.intellij.psi.statistics.StatisticsManager; +import com.intellij.psi.util.proximity.PsiProximityComparator; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.ListScrollingUtil; import com.intellij.ui.ScrollPaneFactory; @@ -49,6 +52,9 @@ import com.intellij.ui.components.JBList; import com.intellij.ui.popup.PopupOwner; import com.intellij.ui.popup.PopupUpdateProcessor; import com.intellij.util.Alarm; +import com.intellij.util.Function; +import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.diff.Diff; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; @@ -62,14 +68,19 @@ import javax.swing.event.ListSelectionListener; import javax.swing.text.DefaultEditorKit; import java.awt.*; import java.awt.event.*; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.*; import java.util.List; public abstract class ChooseByNameBase { + private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.gotoByName.ChooseByNameBase"); + protected final Project myProject; protected final ChooseByNameModel myModel; protected final String myInitialText; private boolean myPreselectInitialText; + private final Reference myContext; protected Component myPreviouslyFocusedComponent; @@ -104,14 +115,10 @@ public abstract class ChooseByNameBase { private static int VISIBLE_LIST_SIZE_LIMIT = 10; private static final int MAXIMUM_LIST_SIZE_LIMIT = 30; private int myMaximumListSizeLimit = MAXIMUM_LIST_SIZE_LIMIT; - @NonNls - private static final String NOT_FOUND_IN_PROJECT_CARD = "syslib"; - @NonNls - private static final String NOT_FOUND_CARD = "nfound"; - @NonNls - private static final String CHECK_BOX_CARD = "chkbox"; - @NonNls - private static final String SEARCHING_CARD = "searching"; + @NonNls private static final String NOT_FOUND_IN_PROJECT_CARD = "syslib"; + @NonNls private static final String NOT_FOUND_CARD = "nfound"; + @NonNls private static final String CHECK_BOX_CARD = "chkbox"; + @NonNls private static final String SEARCHING_CARD = "searching"; private static final int REBUILD_DELAY = 300; private final Alarm myHideAlarm = new Alarm(); @@ -119,26 +126,32 @@ public abstract class ChooseByNameBase { protected JBPopup myTextPopup; protected JBPopup myDropdownPopup; - private EntityMatcher myMatcher; + private static class MatchesComparator implements Comparator { + private final String myOriginalPattern; + + private MatchesComparator(final String originalPattern) { + myOriginalPattern = originalPattern.trim(); + } + + public int compare(final String a, final String b) { + boolean aStarts = a.startsWith(myOriginalPattern); + boolean bStarts = b.startsWith(myOriginalPattern); + if (aStarts && bStarts) return a.compareToIgnoreCase(b); + if (aStarts && !bStarts) return -1; + if (bStarts && !aStarts) return 1; + return a.compareToIgnoreCase(b); + } + } /** * @param initialText initial text which will be in the lookup text field * @param context */ protected ChooseByNameBase(Project project, ChooseByNameModel model, String initialText, final PsiElement context) { - this(project, model, initialText); - myMatcher = new DefaultMatcher(model, context); - } - - protected ChooseByNameBase(Project project, ChooseByNameModel model, String initialText, EntityMatcher matcher) { - this(project, model, initialText); - myMatcher = matcher; - } - - private ChooseByNameBase(Project project, ChooseByNameModel model, String initialText) { myProject = project; myModel = model; myInitialText = initialText; + myContext = new WeakReference(context); } public boolean isPreselectInitialText() { @@ -259,7 +272,7 @@ public abstract class ChooseByNameBase { /** * @param callback - * @param modalityState - if not null rebuilds list in given {@link com.intellij.openapi.application.ModalityState} + * @param modalityState - if not null rebuilds list in given {@link ModalityState} * @param allowMultipleSelection */ protected void initUI(final ChooseByNamePopupComponent.Callback callback, @@ -284,8 +297,7 @@ public abstract class ChooseByNameBase { GridBagLayout gb = new GridBagLayout(); JPanel eastWrapper = new JPanel(gb); - gb.setConstraints(hBox, new GridBagConstraints(0, 0, 0, 0, 1, 1, GridBagConstraints.SOUTHEAST, GridBagConstraints.NONE, - new Insets(0, 0, 0, 0), 0, 0)); + gb.setConstraints(hBox, new GridBagConstraints(0, 0, 0, 0, 1, 1, GridBagConstraints.SOUTHEAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); eastWrapper.add(hBox); caption2Tools.add(eastWrapper, BorderLayout.CENTER); @@ -435,7 +447,8 @@ public abstract class ChooseByNameBase { myListModel = new DefaultListModel(); myList = new JBList(myListModel); myList.setFocusable(false); - myList.setSelectionMode(allowMultipleSelection ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION); + myList.setSelectionMode(allowMultipleSelection ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : + ListSelectionModel.SINGLE_SELECTION); myList.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (!myTextField.hasFocus()) { @@ -577,8 +590,8 @@ public abstract class ChooseByNameBase { final int paneHeight = layeredPane.getHeight(); final int y = paneHeight / 3 - preferredTextFieldPanelSize.height / 2; - VISIBLE_LIST_SIZE_LIMIT = - Math.max(10, (paneHeight - (y + preferredTextFieldPanelSize.height)) / (preferredTextFieldPanelSize.height / 2) - 1); + VISIBLE_LIST_SIZE_LIMIT = Math.max + (10, (paneHeight - (y + preferredTextFieldPanelSize.height)) / (preferredTextFieldPanelSize.height / 2) - 1); ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(myTextFieldPanel, myTextField); builder.setCancelCallback(new Computable() { @@ -614,11 +627,9 @@ public abstract class ChooseByNameBase { layeredPane = ((JDialog)parent).getLayeredPane(); } else { - throw new IllegalStateException("cannot find parent window: project=" + - myProject + + throw new IllegalStateException("cannot find parent window: project=" + myProject + (myProject != null ? "; open=" + myProject.isOpen() : "") + - "; window=" + - window); + "; window=" + window); } return layeredPane; } @@ -639,7 +650,8 @@ public abstract class ChooseByNameBase { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { final String text = myTextField.getText(); - if (!canShowListForEmptyPattern() && (text == null || text.trim().length() == 0)) { + if (!canShowListForEmptyPattern() && + (text == null || text.trim().length() == 0)) { myListModel.clear(); hideList(); myCard.show(myCardContainer, CHECK_BOX_CARD); @@ -779,12 +791,17 @@ public abstract class ChooseByNameBase { return "choose_by_name#" + myModel.getPromptText() + "#" + myCheckBox.isSelected() + "#" + myTextField.getText(); } - public String getNamePattern(String pattern) { - return getNamePattern_static(myModel, pattern); + private String getQualifierPattern(String pattern) { + final String[] separators = myModel.getSeparators(); + int lastSeparatorOccurence = 0; + for (String separator : separators) { + lastSeparatorOccurence = Math.max(lastSeparatorOccurence, pattern.lastIndexOf(separator)); + } + return pattern.substring(0, lastSeparatorOccurence); } - public static String getNamePattern_static(ChooseByNameModel model, String pattern) { - final String[] separators = model.getSeparators(); + public String getNamePattern(String pattern) { + final String[] separators = myModel.getSeparators(); int lastSeparatorOccurence = 0; for (String separator : separators) { final int idx = pattern.lastIndexOf(separator); @@ -970,7 +987,7 @@ public abstract class ChooseByNameBase { protected void processKeyEvent(KeyEvent e) { final KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e); - + if (myCompletionKeyStroke != null && keyStroke.equals(myCompletionKeyStroke)) { completionKeyStrokeHappened = true; e.consume(); @@ -1021,7 +1038,8 @@ public abstract class ChooseByNameBase { } private void fillInCommonPrefix(final String pattern) { - final List list = getNamesByPattern(pattern, getNames()); + final ArrayList list = new ArrayList(); + getNamesByPattern(myCheckBox.isSelected(), null, list, pattern); if (isComplexPattern(pattern)) return; //TODO: support '*' final String oldText = myTextField.getText(); @@ -1087,34 +1105,6 @@ public abstract class ChooseByNameBase { } } - public List getNamesByPattern(String pattern, String[] names) { - if (pattern.startsWith("@")) { - pattern = pattern.substring(1); - } - - boolean empty = pattern.length() == 0; - if (empty) { - if (!canShowListForEmptyPattern()) return Collections.emptyList(); - return Arrays.asList(names); - } - - ArrayList result = new ArrayList(); - try { - for (String name : names) { - if (myCalcElementsThread.myCancelled) { - break; - } - if (myMatcher.nameMatches(pattern, name)) { - result.add(name); - } - } - } - catch (Exception e) { - // Do nothing. No matches appears valid result for "bad" pattern - } - return result; - } - private static final String EXTRA_ELEM = "..."; private class CalcElementsThread implements Runnable { @@ -1203,31 +1193,55 @@ public abstract class ChooseByNameBase { } private void addElementsByPattern(Set elementsArray, String pattern) { - String[] names = myCheckboxState ? myNames[1] : myNames[0]; String namePattern = getNamePattern(pattern); + String qualifierPattern = getQualifierPattern(pattern); - List namesList = getNamesByPattern(namePattern, names); + boolean empty = namePattern.length() == 0 || namePattern.equals("@"); // TODO[yole]: remove implicit dependency + if (empty && !canShowListForEmptyPattern()) return; + List namesList = new ArrayList(); + getNamesByPattern(myCheckboxState, this, namesList, namePattern); + if (myCancelled) { + throw new ProcessCanceledException(); + } // Here we sort using namePattern to have similar logic with empty qualified patten case Collections.sort(namesList, new MatchesComparator(namePattern)); + boolean overflow = false; + List sameNameElements = new SmartList(); + All: for (String name : namesList) { - Set elems = myMatcher.getElementsByPattern(namePattern, name, myCheckboxState,new Computable() { - public Boolean compute() { - return myCancelled; - } - }); - if (elementsArray.size() + elems.size() <= myMaximumListSizeLimit) { - elementsArray.addAll(elems); + if (myCancelled) { + throw new ProcessCanceledException(); } - else { - Iterator iter = elems.iterator(); - while (elementsArray.size() < myMaximumListSizeLimit) { - elementsArray.add(iter.next()); + final Object[] elements = myModel.getElementsByName(name, myCheckboxState, namePattern); + if (elements.length > 1) { + sameNameElements.clear(); + for (final Object element : elements) { + if (matchesQualifier(element, qualifierPattern)) { + sameNameElements.add(element); + } + } + sortByProximity(sameNameElements); + for (Object element : sameNameElements) { + elementsArray.add(element); + if (elementsArray.size() >= myMaximumListSizeLimit) { + overflow = true; + break All; + } } - elementsArray.add(EXTRA_ELEM); - break; } + else if (elements.length == 1 && matchesQualifier(elements[0], qualifierPattern)) { + elementsArray.add(elements[0]); + if (elementsArray.size() >= myMaximumListSizeLimit) { + overflow = true; + break; + } + } + } + + if (overflow) { + elementsArray.add(EXTRA_ELEM); } } @@ -1238,8 +1252,92 @@ public abstract class ChooseByNameBase { } } - private String[] getNames() { - return myCheckBox.isSelected() ? myNames[1] : myNames[0]; + private void sortByProximity(final List sameNameElements) { + Collections.sort(sameNameElements, new PathProximityComparator(myModel, myContext.get())); + } + + private List split(String s) { + List answer = new ArrayList(); + for (String token : StringUtil.tokenize(s, StringUtil.join(myModel.getSeparators(), ""))) { + if (token.length() > 0) { + answer.add(token); + } + } + + return answer.isEmpty() ? Collections.singletonList(s) : answer; + } + + private boolean matchesQualifier(final Object element, final String qualifierPattern) { + final String name = myModel.getFullName(element); + if (name == null) return false; + + final List suspects = split(name); + final List> patternsAndMatchers = + ContainerUtil.map2List(split(qualifierPattern), new Function>() { + public Pair fun(String s) { + final String pattern = getNamePattern(s); + final NameUtil.Matcher matcher = buildPatternMatcher(pattern); + + return new Pair(pattern, matcher); + } + }); + + int matchPosition = 0; + + try { + patterns: + for (Pair patternAndMatcher : patternsAndMatchers) { + final String pattern = patternAndMatcher.first; + final NameUtil.Matcher matcher = patternAndMatcher.second; + if (pattern.length() > 0) { + for (int j = matchPosition; j < suspects.size() - 1; j++) { + String suspect = suspects.get(j); + if (matches(pattern, matcher, suspect)) { + matchPosition = j + 1; + continue patterns; + } + } + + return false; + } + } + } + catch (Exception e) { + // Do nothing. No matches appears valid result for "bad" pattern + return false; + } + + return true; + } + + private void getNamesByPattern(final boolean checkboxState, + CalcElementsThread calcElementsThread, + final List list, + String pattern) throws ProcessCanceledException { + if (!canShowListForEmptyPattern()) { + LOG.assertTrue(pattern.length() > 0); + } + + if (pattern.startsWith("@")) { + pattern = pattern.substring(1); + } + + final String[] names = checkboxState ? myNames[1] : myNames[0]; + final NameUtil.Matcher matcher = buildPatternMatcher(pattern); + + try { + for (String name : names) { + if (calcElementsThread != null && calcElementsThread.myCancelled) { + break; + } + if (matches(pattern, matcher, name)) { + list.add(name); + } + } + } + catch (Exception e) { + // Do nothing. No matches appears valid result for "bad" pattern + } } private boolean canShowListForEmptyPattern() { @@ -1247,27 +1345,46 @@ public abstract class ChooseByNameBase { } protected boolean lastKeyStrokeIsCompletion() { - return myTextField.isCompletionKeyStroke(); + return myTextField.isCompletionKeyStroke(); + } + + private boolean matches(String pattern, NameUtil.Matcher matcher, String name) { + boolean matches = false; + if (name != null) { + if (myModel instanceof CustomMatcherModel) { + if (((CustomMatcherModel)myModel).matches(name, pattern)) { + matches = true; + } + } + else if (pattern.length() == 0 || matcher.matches(name)) { + matches = true; + } + } + return matches; + } + + private NameUtil.Matcher buildPatternMatcher(String pattern) { + return NameUtil.buildMatcher(pattern, 0, true, true, pattern.toLowerCase().equals(pattern)); } private interface CalcElementsCallback { void run(Set elements); } - private static class MatchesComparator implements Comparator { - private final String myOriginalPattern; + private static class PathProximityComparator implements Comparator { + private final ChooseByNameModel myModel; + private final PsiProximityComparator myProximityComparator; - private MatchesComparator(final String originalPattern) { - myOriginalPattern = originalPattern.trim(); + private PathProximityComparator(final ChooseByNameModel model, final PsiElement context) { + myModel = model; + myProximityComparator = new PsiProximityComparator(context); } - public int compare(final String a, final String b) { - boolean aStarts = a.startsWith(myOriginalPattern); - boolean bStarts = b.startsWith(myOriginalPattern); - if (aStarts && bStarts) return a.compareToIgnoreCase(b); - if (aStarts && !bStarts) return -1; - if (bStarts && !aStarts) return 1; - return a.compareToIgnoreCase(b); + public int compare(final Object o1, final Object o2) { + int rc = myProximityComparator.compare(o1, o2); + if (rc != 0) return rc; + + return Comparing.compare(myModel.getFullName(o1), myModel.getFullName(o2)); } } diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java index 97b892324624..a262d28b8be1 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java @@ -17,7 +17,6 @@ package com.intellij.ide.util.gotoByName; import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.ide.util.gotoByName.matchers.EntityMatcher; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.ComponentPopupBuilder; @@ -43,7 +42,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNamePopupComponent { +public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNamePopupComponent{ private static final Key CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY = new Key("ChooseByNamePopup"); private Component myOldFocusOwner = null; private boolean myShowListForEmptyPattern = false; @@ -59,17 +58,6 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP } } - private ChooseByNamePopup(final Project project, final ChooseByNameModel model, final ChooseByNamePopup oldPopup, - EntityMatcher matcher,@Nullable final String predefinedText) { - super(project, model, oldPopup != null ? oldPopup.getEnteredText() : predefinedText, matcher); - if (oldPopup == null && predefinedText != null) { - setPreselectInitialText(true); - } - if (oldPopup != null) { //inherit old focus owner - myOldFocusOwner = oldPopup.myPreviouslyFocusedComponent; - } - } - public String getEnteredText() { return myTextField.getText(); } @@ -215,22 +203,6 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP public static ChooseByNamePopup createPopup(final Project project, final ChooseByNameModel model, final PsiElement context) { return createPopup(project, model, context, null); } - public static ChooseByNamePopup createPopup(final Project project, final ChooseByNameModel model, EntityMatcher matcher) { - return createPopup(project, model, matcher, null); - } - - public static ChooseByNamePopup createPopup(final Project project, final ChooseByNameModel model, EntityMatcher matcher, - @Nullable final String predefinedText) { - final ChooseByNamePopup oldPopup = project.getUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY); - if (oldPopup != null) { - oldPopup.close(false); - } - ChooseByNamePopup newPopup = new ChooseByNamePopup(project, model, oldPopup, matcher, predefinedText); - - project.putUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY, newPopup); - return newPopup; - } - public static ChooseByNamePopup createPopup(final Project project, final ChooseByNameModel model, final PsiElement context, @Nullable final String predefinedText) { final ChooseByNamePopup oldPopup = project.getUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY); diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java deleted file mode 100644 index 3ba89706b309..000000000000 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/DefaultMatcher.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2000-2010 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.ide.util.gotoByName.matchers; - -import com.intellij.ide.util.gotoByName.ChooseByNameBase; -import com.intellij.ide.util.gotoByName.ChooseByNameModel; -import com.intellij.ide.util.gotoByName.CustomMatcherModel; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiElement; -import com.intellij.psi.codeStyle.NameUtil; -import com.intellij.psi.util.proximity.PsiProximityComparator; -import com.intellij.util.Function; -import com.intellij.util.SmartList; -import com.intellij.util.containers.ContainerUtil; - -import java.lang.ref.WeakReference; -import java.util.*; - -public class DefaultMatcher implements EntityMatcher { - private ChooseByNameModel myModel; - private WeakReference myContext; - - private String myPattern = null; - private NameUtil.Matcher myMatcher = null; - - public DefaultMatcher(ChooseByNameModel model, PsiElement context) { - myModel = model; - myContext = new WeakReference(context); - } - - public boolean nameMatches(String shortPattern, String shortName) { - if (myPattern == null || !myPattern.equals(shortPattern)) { - myMatcher = buildPatternMatcher(shortPattern); - } - - return matches(shortPattern, myMatcher, shortName); - } - - public Set getElementsByPattern(String fullPattern, String shortName, boolean checkboxState, Computable isCancelled) { - String namePattern = getShortNamePattern(fullPattern); - String qualifierPattern = getQualifierPattern(fullPattern); - - //todo this is a code duplicate - remove it - String newPattern = namePattern.startsWith("@") ? namePattern.substring(1) : namePattern; - if (!nameMatches(newPattern, shortName)) return Collections.emptySet(); - - List sameNameElements = new SmartList(); - final Object[] elements = myModel.getElementsByName(shortName, checkboxState, namePattern); - - Set result = new HashSet(); - if (elements.length == 1) { - if (matchesQualifier(elements[0], qualifierPattern)) { - result.add(elements[0]); - } - return result; - } - - sameNameElements.clear(); - for (final Object element : elements) { - if (isCancelled.compute()) return result; - if (matchesQualifier(element, qualifierPattern)) { - sameNameElements.add(element); - } - } - sortByProximity(sameNameElements); - result.addAll(sameNameElements); - return result; - } - - private void sortByProximity(final List sameNameElements) { - Collections.sort(sameNameElements, new PathProximityComparator(myModel, myContext.get())); - } - - private boolean matchesQualifier(final Object element, final String qualifierPattern) { - final String name = myModel.getFullName(element); - if (name == null) return false; - - final List suspects = split(name); - final List> patternsAndMatchers = - ContainerUtil.map2List(split(qualifierPattern), new Function>() { - public Pair fun(String s) { - final String pattern = getShortNamePattern(s); - final NameUtil.Matcher matcher = buildPatternMatcher(pattern); - - return new Pair(pattern, matcher); - } - }); - - int matchPosition = 0; - - try { - patterns: - for (Pair patternAndMatcher : patternsAndMatchers) { - final String pattern = patternAndMatcher.first; - final NameUtil.Matcher matcher = patternAndMatcher.second; - if (pattern.length() > 0) { - for (int j = matchPosition; j < suspects.size() - 1; j++) { - String suspect = suspects.get(j); - if (matches(pattern, matcher, suspect)) { - matchPosition = j + 1; - continue patterns; - } - } - - return false; - } - } - } - catch (Exception e) { - // Do nothing. No matches appears valid result for "bad" pattern - return false; - } - - return true; - } - - public String getShortNamePattern(String s) { - return ChooseByNameBase.getNamePattern_static(myModel, s); - } - - private String getQualifierPattern(String pattern) { - final String[] separators = myModel.getSeparators(); - int lastSeparatorOccurence = 0; - for (String separator : separators) { - lastSeparatorOccurence = Math.max(lastSeparatorOccurence, pattern.lastIndexOf(separator)); - } - return pattern.substring(0, lastSeparatorOccurence); - } - - private boolean matches(String pattern, NameUtil.Matcher matcher, String name) { - boolean matches = false; - if (name != null) { - if (myModel instanceof CustomMatcherModel) { - if (((CustomMatcherModel)myModel).matches(name, pattern)) { - matches = true; - } - } - else if (pattern.length() == 0 || matcher.matches(name)) { - matches = true; - } - } - return matches; - } - - private static NameUtil.Matcher buildPatternMatcher(String pattern) { - return NameUtil.buildMatcher(pattern, 0, true, true, pattern.toLowerCase().equals(pattern)); - } - - private List split(String s) { - List answer = new ArrayList(); - for (String token : StringUtil.tokenize(s, StringUtil.join(myModel.getSeparators(), ""))) { - if (token.length() > 0) { - answer.add(token); - } - } - - return answer.isEmpty() ? Collections.singletonList(s) : answer; - } - - private static class PathProximityComparator implements Comparator { - private final ChooseByNameModel myModel; - private final PsiProximityComparator myProximityComparator; - - private PathProximityComparator(final ChooseByNameModel model, final PsiElement context) { - myModel = model; - myProximityComparator = new PsiProximityComparator(context); - } - - public int compare(final Object o1, final Object o2) { - int rc = myProximityComparator.compare(o1, o2); - if (rc != 0) return rc; - - return Comparing.compare(myModel.getFullName(o1), myModel.getFullName(o2)); - } - } - -} diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/EntityMatcher.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/EntityMatcher.java deleted file mode 100644 index 7a6dbffc809b..000000000000 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/EntityMatcher.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2000-2010 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.ide.util.gotoByName.matchers; - -import com.intellij.openapi.util.Computable; - -import java.util.Set; - -public interface EntityMatcher { - boolean nameMatches(String shortPattern, String shortName); - - Set getElementsByPattern(String fullPattern, String shortName, boolean checkboxState, Computable isCancelled); -} From 9026f7026d7fa86e6787ca6eabed9790097f7034 Mon Sep 17 00:00:00 2001 From: "Rustam.Vishnyakov" Date: Wed, 10 Nov 2010 18:18:07 +0300 Subject: [PATCH 17/55] Rename JS library types in UI as Debug/Release --- .../LangScriptingContextProvider.java | 5 +++++ .../scriptingContext/ui/EditLibraryDialog.java | 15 +++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java index 957ea71c02ad..2f975a75bfb2 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java @@ -17,8 +17,10 @@ package com.intellij.ide.scriptingContext; import com.intellij.lang.Language; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.LibraryType; import com.intellij.openapi.vfs.VirtualFile; +import com.sun.istack.internal.Nullable; import org.jetbrains.annotations.NotNull; /** @@ -37,4 +39,7 @@ public abstract class LangScriptingContextProvider { public abstract boolean isCompact(VirtualFile file); + @Nullable + public abstract String getLibraryTypeName(OrderRootType rootType); + } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java index f83dc4dfef9e..0a43981da562 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java @@ -19,6 +19,7 @@ import com.intellij.ide.scriptingContext.LangScriptingContextProvider; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.scripting.ScriptingLibraryTable; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; @@ -42,8 +43,8 @@ public class EditLibraryDialog extends DialogWrapper { private static final int FILE_LOCATION_COL = 0; private static final int FILE_TYPE_COL = 1; - private static final String SOURCE_TYPE = "Source"; - private static final String COMPACT_TYPE = "Compact"; + private final String mySourceTypeName; + private final String myCompactTypeName; private JPanel contentPane; private JTextField myLibName; @@ -60,6 +61,8 @@ public class EditLibraryDialog extends DialogWrapper { super(true); setTitle(title); myProvider = provider; + mySourceTypeName = provider.getLibraryTypeName(OrderRootType.SOURCES); + myCompactTypeName = provider.getLibraryTypeName(OrderRootType.CLASSES); myAddFileButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -95,7 +98,7 @@ public class EditLibraryDialog extends DialogWrapper { TableColumn typeCol = myFileTable.getColumnModel().getColumn(FILE_TYPE_COL); typeCol.setMaxWidth(80); - MyTableCellEditor cellEditor = new MyTableCellEditor(new JComboBox(new String[] {SOURCE_TYPE, COMPACT_TYPE}), myFileTableModel); + MyTableCellEditor cellEditor = new MyTableCellEditor(new JComboBox(new String[] {mySourceTypeName, myCompactTypeName}), myFileTableModel); typeCol.setCellEditor(cellEditor); } @@ -105,7 +108,7 @@ public class EditLibraryDialog extends DialogWrapper { myFileTableModel.setFiles(lib.getSourceFiles(), lib.getCompactFiles()); } - private static class MyTableCellEditor extends DefaultCellEditor { + private class MyTableCellEditor extends DefaultCellEditor { private FileTableModel myFileTableModel; private VirtualFile myFile; @@ -119,7 +122,7 @@ public class EditLibraryDialog extends DialogWrapper { public boolean stopCellEditing() { if (myFile != null) { Object value = getCellEditorValue(); - myFileTableModel.setFileType(myFile, value.equals(COMPACT_TYPE)); + myFileTableModel.setFileType(myFile, value.equals(myCompactTypeName)); } return super.stopCellEditing(); } @@ -254,7 +257,7 @@ public class EditLibraryDialog extends DialogWrapper { case FILE_LOCATION_COL: return file; case FILE_TYPE_COL: - return myCompactFiles.contains(file) ? COMPACT_TYPE : SOURCE_TYPE; + return myCompactFiles.contains(file) ? myCompactTypeName : mySourceTypeName; } return ""; } From b2bf336567af942b9d78f693477d48a8308cc1c5 Mon Sep 17 00:00:00 2001 From: "Rustam.Vishnyakov" Date: Wed, 10 Nov 2010 20:26:35 +0300 Subject: [PATCH 18/55] Correctly add/remove library to/from JavaScript library scope (broken with another fix) --- .../LangScriptingContextConfigurable.java | 3 ++- .../ScriptingLibraryMappings.java | 22 ++++++++++--------- .../ui/ScriptingContextsConfigurable.java | 19 +++++++++++++--- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java index 82158e23cf03..6a5952fe2ae2 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java @@ -39,7 +39,7 @@ public abstract class LangScriptingContextConfigurable implements Configurable, public LangScriptingContextConfigurable(Project project, LangScriptingContextProvider provider) { myLibManager = new ScriptingLibraryManager(project, provider.getLibraryType()); myPanel = useDedicatedLibraryUI() ? new ScriptingLibrariesPanel(provider, project, myLibManager) : new ScriptingLibrariesPanelStub(); - myContextsConfigurable = new ScriptingContextsConfigurable(project, provider); + myContextsConfigurable = new ScriptingContextsConfigurable(project, provider.getLibraryMappings(project)); } private boolean useDedicatedLibraryUI() { @@ -70,6 +70,7 @@ public abstract class LangScriptingContextConfigurable implements Configurable, public void run() { myLibManager.commitChanges(); myPanel.resetTable(); + myContextsConfigurable.resetMappings(); } }); } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java index 2f9bebde96ec..c796667b7a03 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java @@ -47,6 +47,10 @@ public class ScriptingLibraryMappings extends LanguagePerFileMappings getAvailableValues(VirtualFile file) { - myLibraryManager.reset(); List libraries = getSingleLibraries(); if (myCompoundLibMap.containsKey(file)) { libraries.add(myCompoundLibMap.get(file)); @@ -127,7 +130,7 @@ public class ScriptingLibraryMappings extends LanguagePerFileMappings myLibraries = new ArrayList(); + private final Map myLibraries = new TreeMap(); public CompoundLibrary() { super(null); @@ -138,20 +141,19 @@ public class ScriptingLibraryMappings extends LanguagePerFileMappings { - public ScriptingContextsConfigurable(final Project project, final LangScriptingContextProvider provider) { - super(project, ScriptingLibraryTable.LibraryModel.class, provider.getLibraryMappings(project), + private final ScriptingLibraryMappings myScriptingLibraryMappings; + + public ScriptingContextsConfigurable(final Project project, final ScriptingLibraryMappings mappings) { + super(project, ScriptingLibraryTable.LibraryModel.class, mappings, "Specify which libraries are used in specific files and/or directories.", "Library", "Override library settings for child directories and files?", "Override Library Settings"); + myScriptingLibraryMappings = mappings; + } + + public void resetMappings() { + myScriptingLibraryMappings.reset(); + } + + @Override + public void reset() { + resetMappings(); + super.reset(); } @Override From fc0064899926adccff092245d52402d502c4b4ea Mon Sep 17 00:00:00 2001 From: Mihail Muhin Date: Tue, 2 Nov 2010 20:15:03 +0300 Subject: [PATCH 19/55] RunManagerImpl - myOrdered is not null (can't do an additional read-write cycle because of it) --- .../com/intellij/execution/impl/RunManagerImpl.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java index 329392e2ed1e..7abf57264399 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java @@ -73,6 +73,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, private List myUnloadedElements = null; private JDOMExternalizableStringList myOrder = new JDOMExternalizableStringList(); + private boolean myOrdered = true; private final EventDispatcher myDispatcher = EventDispatcher.create(RunManagerListener.class); @@ -286,7 +287,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, @Override public Collection getSortedConfigurations() { - if (myOrder != null && !myOrder.isEmpty()) { //compatibility + if (!myOrdered && !myOrder.isEmpty()) { //compatibility final HashMap settings = new HashMap(myConfigurations); //sort shared and local configurations myConfigurations.clear(); @@ -299,7 +300,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, for (String configName : order) { myConfigurations.put(configName, settings.get(configName)); } - myOrder = null; + myOrdered = true; } return myConfigurations.values(); } @@ -474,9 +475,8 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, } } - if (myOrder != null) { - myOrder.readExternal(parentNode); - } + myOrder.readExternal(parentNode); + myOrdered = false; mySelectedConfig = parentNode.getAttributeValue(SELECTED_ATTR); fireBeforeRunTasksUpdated(); @@ -799,7 +799,6 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, } public void shareConfiguration(final RunConfiguration runConfiguration, final boolean shareConfiguration) { - if (shareConfiguration && isTemporary(runConfiguration)) makeStable(runConfiguration); mySharedConfigurations.put(runConfiguration.getUniqueID(), shareConfiguration); } From f91a43d831001ce21799e0758ae6e0a766c9e409 Mon Sep 17 00:00:00 2001 From: Mihail Muhin Date: Tue, 2 Nov 2010 20:21:52 +0300 Subject: [PATCH 20/55] file status can be provided for non-local files by extensions --- .../openapi/vcs/impl/FileStatusManagerImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java index 68299205909e..63afdb8941b4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java @@ -75,13 +75,13 @@ public class FileStatusManagerImpl extends FileStatusManager implements ProjectC } public FileStatus calcStatus(@NotNull VirtualFile virtualFile) { - if (virtualFile.isInLocalFileSystem() && myFileStatusProvider != null) { - for (FileStatusProvider extension : myExtensions.getValue()) { - FileStatus status = extension.getFileStatus(virtualFile); - if (status != null) { - return status; - } + for (FileStatusProvider extension : myExtensions.getValue()) { + FileStatus status = extension.getFileStatus(virtualFile); + if (status != null) { + return status; } + } + if (virtualFile.isInLocalFileSystem() && myFileStatusProvider != null) { return myFileStatusProvider.getFileStatus(virtualFile); } else { return FileStatus.NOT_CHANGED; From da2d36f153f4d3f5a19157ea38bf7e01bb46e5a2 Mon Sep 17 00:00:00 2001 From: Mihail Muhin Date: Tue, 2 Nov 2010 20:23:45 +0300 Subject: [PATCH 21/55] FileStatusManagerImpl - add @NotNull annotation for added listener --- .../com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java index 63afdb8941b4..789a97b5e969 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java @@ -114,7 +114,7 @@ public class FileStatusManagerImpl extends FileStatusManager implements ProjectC public void initComponent() { } - public void addFileStatusListener(FileStatusListener listener) { + public void addFileStatusListener(@NotNull FileStatusListener listener) { myListeners.add(listener); } From 041abc147bece1b1fcf72e8da365bb3fe7615eec Mon Sep 17 00:00:00 2001 From: Mihail Muhin Date: Tue, 2 Nov 2010 21:53:14 +0300 Subject: [PATCH 22/55] ProjectViewImpl - setShowMembers method added --- .../com/intellij/ide/projectView/impl/ProjectViewImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java index 89458c21f188..986d135eea71 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java @@ -1497,6 +1497,10 @@ public final class ProjectViewImpl extends ProjectView implements PersistentStat return getPaneOptionValue(myShowMembers, paneId, ourShowMembersDefaults); } + public void setShowMembers(boolean showMembers, String paneId) { + setPaneOption(myShowMembers, showMembers, paneId, true); + } + public boolean isHideEmptyMiddlePackages(String paneId) { return getPaneOptionValue(myHideEmptyPackages, paneId, ourHideEmptyPackagesDefaults); } From cf0eea315e47613e75139642a60542ba69481b11 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Wed, 10 Nov 2010 21:20:16 +0300 Subject: [PATCH 23/55] compilation fix --- .../ide/scriptingContext/LangScriptingContextProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java index 2f975a75bfb2..1d72952a1162 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java @@ -20,8 +20,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.LibraryType; import com.intellij.openapi.vfs.VirtualFile; -import com.sun.istack.internal.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Rustam Vishnyakov From 3b496bb37171dde54551526d2a6307b547d53bed Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 18:32:45 +0300 Subject: [PATCH 24/55] less read actions --- .../completion/CompletionData.java | 81 +++++++------------ .../impl/CompletionServiceImpl.java | 27 ++----- 2 files changed, 37 insertions(+), 71 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionData.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionData.java index 83657d7b1caa..4384f57240b9 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionData.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionData.java @@ -19,10 +19,8 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.TailType; import com.intellij.codeInsight.lookup.*; import com.intellij.codeInsight.template.Template; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.paths.PsiDynaReference; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.patterns.ElementPattern; @@ -104,21 +102,17 @@ public class CompletionData { public void completeReference(final PsiReference reference, final Set set, @NotNull final PsiElement position, final PsiFile file, final int offset){ final CompletionVariant[] variants = findVariants(position, file); - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - boolean hasApplicableVariants = false; - for (CompletionVariant variant : variants) { - if (variant.hasReferenceFilter()) { - variant.addReferenceCompletions(reference, position, set, file, CompletionData.this); - hasApplicableVariants = true; - } - } - - if (!hasApplicableVariants) { - myGenericVariant.addReferenceCompletions(reference, position, set, file, CompletionData.this); - } + boolean hasApplicableVariants = false; + for (CompletionVariant variant : variants) { + if (variant.hasReferenceFilter()) { + variant.addReferenceCompletions(reference, position, set, file, CompletionData.this); + hasApplicableVariants = true; } - }); + } + + if (!hasApplicableVariants) { + myGenericVariant.addReferenceCompletions(reference, position, set, file, CompletionData.this); + } } public void addKeywordVariants(Set set, PsiElement position, final PsiFile file) { @@ -129,11 +123,7 @@ public class CompletionData { final PrefixMatcher matcher, final PsiFile file){ for (final CompletionVariant variant : variants) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - variant.addKeywords(set, position, matcher, file, CompletionData.this); - } - }); + variant.addKeywords(set, position, matcher, file, CompletionData.this); } } @@ -142,34 +132,30 @@ public class CompletionData { } public CompletionVariant[] findVariants(final PsiElement position, final PsiFile file){ - return ApplicationManager.getApplication().runReadAction(new Computable() { - public CompletionVariant[] compute() { - final List variants = new ArrayList(); - PsiElement scope = position; - if(scope == null){ - scope = file; - } - while (scope != null) { - boolean breakFlag = false; - if (isScopeAcceptable(scope)){ + final List variants = new ArrayList(); + PsiElement scope = position; + if(scope == null){ + scope = file; + } + while (scope != null) { + boolean breakFlag = false; + if (isScopeAcceptable(scope)){ - for (final CompletionVariant variant : myCompletionVariants) { - if (variant.isVariantApplicable(position, scope) && !variants.contains(variant)) { - variants.add(variant); - if (variant.isScopeFinal(scope)) { - breakFlag = true; - } + for (final CompletionVariant variant : myCompletionVariants) { + if (variant.isVariantApplicable(position, scope) && !variants.contains(variant)) { + variants.add(variant); + if (variant.isScopeFinal(scope)) { + breakFlag = true; } } } - if(breakFlag || isScopeFinal(scope.getClass())) - break; - scope = scope.getContext(); - if (scope instanceof PsiDirectory) break; } - return variants.toArray(new CompletionVariant[variants.size()]); + if(breakFlag || isScopeFinal(scope.getClass())) + break; + scope = scope.getContext(); + if (scope instanceof PsiDirectory) break; } - }); + return variants.toArray(new CompletionVariant[variants.size()]); } protected final CompletionVariant myGenericVariant = new CompletionVariant() { @@ -207,14 +193,7 @@ public class CompletionData { public static String findPrefixStatic(final PsiElement insertedElement, final int offsetInFile, ElementPattern prefixStartTrim) { if(insertedElement == null) return ""; - final String prefix = ApplicationManager.getApplication().runReadAction(new Computable() { - public String compute() { - if (!insertedElement.isValid()) { - return ""; - } - return getReferencePrefix(insertedElement, offsetInFile); - } - }); + final String prefix = getReferencePrefix(insertedElement, offsetInFile); if (prefix != null) return prefix; if (insertedElement instanceof PsiPlainText || insertedElement instanceof PsiComment) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java index 3dd4c2cecd7d..42ef142195d2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java @@ -17,15 +17,12 @@ package com.intellij.codeInsight.completion.impl; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.ElementPattern; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,21 +53,11 @@ public class CompletionServiceImpl extends CompletionService{ } public CompletionResultSet createResultSet(final CompletionParameters parameters, final Consumer consumer, - @NotNull final CompletionContributor contributor) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - public CompletionResultSet compute() { - final PsiElement position = parameters.getPosition(); - if (!position.isValid()) { - throw new ProcessCanceledException(); - } - - final String prefix = CompletionData.findPrefixStatic(position, parameters.getOffset()); - - final String textBeforePosition = parameters.getPosition().getContainingFile().getText().substring(0, parameters.getOffset()); - - return new CompletionResultSetImpl(consumer, textBeforePosition, new CamelHumpMatcher(prefix), contributor); - } - }); + @NotNull final CompletionContributor contributor) { + final PsiElement position = parameters.getPosition(); + final String prefix = CompletionData.findPrefixStatic(position, parameters.getOffset()); + final String textBeforePosition = parameters.getPosition().getContainingFile().getText().substring(0, parameters.getOffset()); + return new CompletionResultSetImpl(consumer, textBeforePosition, new CamelHumpMatcher(prefix), contributor); } @Override From 3ddff02a4b57396f3a0ac417c79d5766aaf6bd6d Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 19:16:12 +0300 Subject: [PATCH 25/55] cleanup --- .../codeInsight/completion/CompletionVariant.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionVariant.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionVariant.java index ab9607da4ab8..737e43c8c0b8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionVariant.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionVariant.java @@ -26,7 +26,6 @@ import com.intellij.psi.PsiReference; import com.intellij.psi.filters.ContextGetter; import com.intellij.psi.filters.ElementFilter; import com.intellij.psi.filters.position.PatternFilter; -import com.intellij.util.PairConsumer; import com.intellij.util.ReflectionCache; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; @@ -192,14 +191,6 @@ public class CompletionVariant { } } - public void processReferenceCompletions(PairConsumer consumer) { - for (final CompletionVariantItem ce : myCompletionsList) { - if(ce.myCompletion instanceof ElementFilter){ - consumer.consume((ElementFilter)ce.myCompletion, ce.myTailType); - } - } - } - public void addKeywords(Set set, PsiElement position, final PrefixMatcher matcher, final PsiFile file, final CompletionData completionData){ From 307365aaea3d70482ba0d6c5e4fd825ea7ac1d36 Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 19:50:36 +0300 Subject: [PATCH 26/55] less read actions --- .../BasicExpressionCompletionContributor.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java index e171f3f27fb0..562465af4319 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/BasicExpressionCompletionContributor.java @@ -20,8 +20,6 @@ import com.intellij.codeInsight.lookup.*; import com.intellij.codeInsight.template.SmartCompletionContextType; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.codeInsight.template.impl.TemplateSettings; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.util.Computable; import com.intellij.patterns.PsiJavaPatterns; import com.intellij.psi.*; import com.intellij.psi.filters.getters.ClassLiteralGetter; @@ -52,17 +50,13 @@ public class BasicExpressionCompletionContributor extends ExpressionSmartComplet } public static LookupElement createKeywordLookupItem(final PsiElement element, final String s) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - public LookupItem compute() { - try { - final PsiKeyword keyword = JavaPsiFacade.getInstance(element.getProject()).getElementFactory().createKeyword(s); - return new KeywordLookupItem(keyword, element).setAutoCompletionPolicy(AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE); - } - catch (IncorrectOperationException e) { - throw new RuntimeException(e); - } - } - }); + try { + final PsiKeyword keyword = JavaPsiFacade.getInstance(element.getProject()).getElementFactory().createKeyword(s); + return new KeywordLookupItem(keyword, element).setAutoCompletionPolicy(AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE); + } + catch (IncorrectOperationException e) { + throw new RuntimeException(e); + } } From 391a9f4e2bf9d56c9430e1d9408a30844c24cd51 Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 20:31:39 +0300 Subject: [PATCH 27/55] no superfluous spaces when completing primitive types in for loop (IDEA-61052) --- .../completion/JavaAwareCompletionData.java | 63 +++++++++++++++++-- .../completion/JavaCompletionContributor.java | 58 ----------------- .../completion/JavaCompletionData.java | 1 + .../completion/ModifierChooser.java | 2 +- .../normal/PrimitiveTypesInForLoopSpace.java | 8 +++ .../PrimitiveTypesInForLoopSpace_after.java | 8 +++ .../completion/NormalCompletionTest.groovy | 6 ++ .../codeInsight/lookup/TailTypeDecorator.java | 2 +- 8 files changed, 82 insertions(+), 66 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java index 073e3cf18d1c..0def3c85f6ba 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaAwareCompletionData.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.TailType; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupItem; import com.intellij.codeInsight.lookup.LookupItemUtil; +import com.intellij.codeInsight.lookup.TailTypeDecorator; import com.intellij.psi.*; import com.intellij.psi.filters.ContextGetter; import com.intellij.util.IncorrectOperationException; @@ -32,6 +33,45 @@ import java.util.Set; */ public class JavaAwareCompletionData extends CompletionData{ + @NotNull + static TailType analyzeItem(final Object completion, final PsiElement position) { + if(completion instanceof PsiKeyword){ + final String text = ((PsiKeyword)completion).getText(); + if(PsiKeyword.BREAK.equals(text) || PsiKeyword.CONTINUE.equals(text)) { + PsiElement scope = position; + while(true){ + if (scope instanceof PsiFile || scope instanceof PsiMethod || scope instanceof PsiClassInitializer){ + return TailType.SEMICOLON; + } + + if (scope instanceof PsiLabeledStatement){ + return TailType.NONE; + } + scope = scope.getParent(); + } + } + if(PsiKeyword.RETURN.equals(text)){ + PsiElement scope = position; + while(true){ + if (scope instanceof PsiFile || scope instanceof PsiClassInitializer){ + return TailType.NONE; + } + + if (scope instanceof PsiMethod){ + final PsiMethod method = (PsiMethod)scope; + if(method.isConstructor() || PsiType.VOID.equals(method.getReturnType())) { + return TailType.SEMICOLON; + } + + return TailType.SPACE; + } + scope = scope.getParent(); + } + } + } + return TailType.NONE; + } + protected void addLookupItem(Set set, TailType tailType, @NotNull Object completion, final PsiFile file, final CompletionVariant variant) { if (completion instanceof LookupElement && !(completion instanceof LookupItem)) { set.add((LookupElement)completion); @@ -44,18 +84,29 @@ public class JavaAwareCompletionData extends CompletionData{ LookupItem ret = (LookupItem)_ret; final InsertHandler insertHandler = variant.getInsertHandler(); if(insertHandler != null && ret.getInsertHandler() == null) { - ret.setInsertHandler(insertHandler); - ret.setTailType(TailType.UNKNOWN); - } - else if (tailType != TailType.NONE) { - ret.setTailType(tailType); } + ret.setInsertHandler(new InsertHandler() { + @Override + public void handleInsert(InsertionContext context, LookupElement item) { + final TailType type = analyzeItem(item.getObject(), context.getFile().findElementAt(context.getStartOffset())); + new DefaultInsertHandler().handleInsert(context, item); + if (type != TailType.NONE) { + context.setAddCompletionChar(false); + type.processTail(context.getEditor(), context.getTailOffset()); + } + } + }); final Map itemProperties = variant.getItemProperties(); for (final Object key : itemProperties.keySet()) { ret.setAttribute(key, itemProperties.get(key)); } - set.add(ret); + + if ((insertHandler == null || ret.getInsertHandler() != null) && tailType != TailType.NONE) { + set.add(TailTypeDecorator.withTail(ret, tailType)); + } else { + set.add(ret); + } } protected void addKeywords(final Set set, final PsiElement position, final PrefixMatcher matcher, final PsiFile file, 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 7a97465c0655..01a93aab9dca 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -16,7 +16,6 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.TailType; -import com.intellij.codeInsight.TailTypes; import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix; import com.intellij.codeInsight.hint.ShowParameterInfoHandler; import com.intellij.codeInsight.lookup.LookupElement; @@ -250,15 +249,6 @@ public class JavaCompletionContributor extends CompletionContributor { completionData.fillCompletions(parameters, result); for (final LookupElement item : lookupSet) { - if (item instanceof LookupItem && ((LookupItem)item).getInsertHandler() == null) { - ((LookupItem)item).setInsertHandler(new InsertHandler() { - public void handleInsert(final InsertionContext context, final LookupElement item) { - analyzeItem((LookupItem)item, item.getObject(), parameters.getPosition()); - new DefaultInsertHandler().handleInsert(context, item); - } - }); - } - result.addElement(item); } @@ -469,54 +459,6 @@ public class JavaCompletionContributor extends CompletionContributor { return parent.getParent() instanceof PsiTypeElement || parent.getParent() instanceof PsiExpressionStatement || parent.getParent() instanceof PsiReferenceList; } - public static void analyzeItem(final LookupItem item, final Object completion, final PsiElement position) { - if(completion instanceof PsiKeyword){ - if(PsiKeyword.BREAK.equals(((PsiKeyword)completion).getText()) - || PsiKeyword.CONTINUE.equals(((PsiKeyword)completion).getText())){ - PsiElement scope = position; - while(true){ - if (scope instanceof PsiFile - || scope instanceof PsiMethod - || scope instanceof PsiClassInitializer){ - item.setTailType(TailType.SEMICOLON); - break; - } - else if (scope instanceof PsiLabeledStatement){ - item.setTailType(TailType.NONE); - break; - } - scope = scope.getParent(); - } - } - if(PsiKeyword.RETURN.equals(((PsiKeyword)completion).getText())){ - PsiElement scope = position; - while(true){ - if (scope instanceof PsiFile - || scope instanceof PsiClassInitializer){ - item.setTailType(TailType.NONE); - break; - } - else if (scope instanceof PsiMethod){ - final PsiMethod method = (PsiMethod)scope; - if(method.isConstructor() || PsiType.VOID.equals(method.getReturnType())) { - item.setTailType(TailType.SEMICOLON); - } - else item.setTailType(TailType.SPACE); - - break; - } - scope = scope.getParent(); - } - } - if(PsiKeyword.SYNCHRONIZED.equals(((PsiKeyword)completion).getText())){ - if (PsiTreeUtil.getParentOfType(position, PsiMember.class, PsiCodeBlock.class) instanceof PsiCodeBlock){ - item.setTailType(TailTypes.SYNCHRONIZED_LPARENTH); - } - } - } - - } - public void beforeCompletion(@NotNull final CompletionInitializationContext context) { final PsiFile file = context.getFile(); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java index 45a7965df590..d1a8955f8fc4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java @@ -541,6 +541,7 @@ public class JavaCompletionData extends JavaAwareCompletionData{ variant.addCompletion(PsiKeyword.RETURN, TailType.SPACE); variant.addCompletion(PsiKeyword.NEW, TailType.SPACE); variant.addCompletion(PsiKeyword.ASSERT, TailType.SPACE); + variant.addCompletion(PsiKeyword.SYNCHRONIZED, TailTypes.SYNCHRONIZED_LPARENTH); } @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ModifierChooser.java b/java/java-impl/src/com/intellij/codeInsight/completion/ModifierChooser.java index 15a704e21bb4..4326012d0bb7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ModifierChooser.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ModifierChooser.java @@ -68,7 +68,7 @@ public class ModifierChooser }); myMap.put(new OrFilter(new ClassFilter(PsiStatement.class), new ClassFilter(PsiCodeBlock.class)), new String[][]{ - new String[]{"final", "synchronized"} + new String[]{"final"} }); myMap.put(new ClassFilter(PsiParameterList.class), new String[][]{ diff --git a/java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace.java b/java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace.java new file mode 100644 index 000000000000..f1918d7ca687 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace.java @@ -0,0 +1,8 @@ +public class Foooo { + + { + int intaaaa; + for (in) + } + +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace_after.java b/java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace_after.java new file mode 100644 index 000000000000..b5b04b7c477a --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/PrimitiveTypesInForLoopSpace_after.java @@ -0,0 +1,8 @@ +public class Foooo { + + { + int intaaaa; + for (int ) + } + +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 30f7cdb853d1..661ec57195c4 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -620,6 +620,12 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { public void testPrimitiveTypesInForLoop5() throws Throwable { doPrimitiveTypeTest() } public void testPrimitiveTypesInForLoop6() throws Throwable { doPrimitiveTypeTest() } + public void testPrimitiveTypesInForLoopSpace() throws Throwable { + configure() + myFixture.type ' ' + checkResultByFile(getTestName(false) + "_after.java") + } + public void testSecondInvocationToFillCommonPrefix() throws Throwable { configure() type('a'); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/TailTypeDecorator.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/TailTypeDecorator.java index 34245381345c..264afef7d421 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/TailTypeDecorator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/TailTypeDecorator.java @@ -48,7 +48,7 @@ public abstract class TailTypeDecorator extends LookupE final LookupItem lookupItem = delegate.as(LookupItem.class); if (lookupItem != null && tailType != null) { - lookupItem.setTailType(TailType.NONE); + lookupItem.setTailType(TailType.UNKNOWN); } delegate.handleInsert(context); if (tailType != null && tailType.isApplicable(context)) { From 9d4c174c66e7f8c514ec178e84fcab650cbb9d7a Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 20:31:55 +0300 Subject: [PATCH 28/55] "St"+ should always work like a live template expansion --- .../codeInsight/lookup/impl/LookupImpl.java | 11 ---- .../lookup/impl/actions/ChooseItemAction.java | 5 +- .../impl/actions/ChooseItemReplaceAction.java | 21 +++++++- .../LiveTemplateCompletionContributor.java | 54 ++++++++++++++----- .../template/impl/TemplateSettings.java | 2 +- 5 files changed, 65 insertions(+), 28 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index c3d1ec195f9e..829910392524 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -77,7 +77,6 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { private int myMinPrefixLength; private int myPreferredItemsCount; - private long myShownStamp = -1; private String myInitialPrefix; private LookupArranger myArranger; @@ -493,10 +492,6 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { } public void finishLookup(final char completionChar) { - if (justShown()) { - return; - } - final LookupElement item = (LookupElement)myList.getSelectedValue(); doHide(false, true); if (item == null || @@ -537,10 +532,6 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { fireItemSelected(item, completionChar); } - public boolean justShown() { - return myShownStamp > 0 && System.currentTimeMillis() - myShownStamp < 42 && !ApplicationManager.getApplication().isUnitTestMode(); - } - public int getLookupStart() { if (myLookupStartMarker == null) { final int start = calcLookupStart(); @@ -581,8 +572,6 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { hintManager.showEditorHint(this, myEditor, p, HintManagerImpl.HIDE_BY_ESCAPE | HintManagerImpl.UPDATE_BY_SCROLLING, 0, false); getComponent().getRootPane().getLayeredPane().add(myIconPanel, 42, 0); - - myShownStamp = System.currentTimeMillis(); } private void addListeners() { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java index 80f9c4b5da78..ecaf3f02a204 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java @@ -19,6 +19,7 @@ package com.intellij.codeInsight.lookup.impl.actions; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.lookup.impl.LookupImpl; +import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Editor; @@ -33,6 +34,7 @@ public class ChooseItemAction extends EditorAction { private static class Handler extends EditorActionHandler { public void execute(final Editor editor, final DataContext dataContext) { LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(editor); + assert lookup != null; lookup.finishLookup(Lookup.NORMAL_SELECT_CHAR); } } @@ -41,7 +43,8 @@ public class ChooseItemAction extends EditorAction { LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(editor); if (lookup != null) { lookup.refreshUi(); // to bring the list model up to date - presentation.setEnabled(!lookup.justShown() && (lookup.isFocused() || lookup.isCompletion() && !lookup.getItems().isEmpty())); + presentation.setEnabled(lookup.isFocused() && + !ChooseItemReplaceAction.hasTemplatePrefix(lookup, TemplateSettings.ENTER_CHAR)); } else { presentation.setEnabled(false); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java index fb3ea96fabf1..08052e24bd8a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java @@ -17,15 +17,21 @@ package com.intellij.codeInsight.lookup.impl.actions; import com.intellij.codeInsight.completion.CodeCompletionFeatures; +import com.intellij.codeInsight.completion.CompletionUtil; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.lookup.impl.LookupImpl; +import com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; public class ChooseItemReplaceAction extends EditorAction { public ChooseItemReplaceAction(){ @@ -36,15 +42,28 @@ public class ChooseItemReplaceAction extends EditorAction { public void executeWriteAction(Editor editor, DataContext dataContext) { FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_REPLACE); LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(editor); + assert lookup != null; lookup.finishLookup(Lookup.REPLACE_SELECT_CHAR); } } + static boolean hasTemplatePrefix(LookupImpl lookup, char shortcutChar) { + final PsiFile file = lookup.getPsiFile(); + final Editor editor = lookup.getEditor(); + PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument()); + + final int offset = editor.getCaretModel().getOffset(); + final String prefix = CompletionUtil.findJavaIdentifierPrefix(file, offset); + final TemplateImpl template = LiveTemplateCompletionContributor.findApplicableTemplate(file, offset, prefix); + return template != null && shortcutChar == TemplateSettings.getInstance().getShortcutChar(template); + } + public void update(Editor editor, Presentation presentation, DataContext dataContext){ LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(editor); if (lookup != null) { lookup.refreshUi(); // to bring the list model up to date - presentation.setEnabled(!lookup.justShown() && (lookup.isFocused() || lookup.isCompletion() && !lookup.getItems().isEmpty())); + presentation.setEnabled((lookup.isFocused() || lookup.isCompletion() && !lookup.getItems().isEmpty()) && + !hasTemplatePrefix(lookup, TemplateSettings.TAB_CHAR)); } else { presentation.setEnabled(false); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateCompletionContributor.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateCompletionContributor.java index 4124b438c120..bb3b5915a753 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateCompletionContributor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateCompletionContributor.java @@ -19,10 +19,17 @@ import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.codeInsight.template.TemplateManager; +import com.intellij.openapi.util.Condition; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiFile; import com.intellij.util.ProcessingContext; +import com.intellij.util.containers.CollectionFactory; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; /** * @author peter @@ -39,23 +46,42 @@ public class LiveTemplateCompletionContributor extends CompletionContributor { final PsiFile file = parameters.getOriginalFile(); final int offset = parameters.getOffset(); final String prefix = result.getPrefixMatcher().getPrefix(); - for (final TemplateImpl template : TemplateSettings.getInstance().getTemplates()) { - final String key = template.getKey(); - if (!template.isDeactivated() && !template.isSelectionTemplate() && TemplateManagerImpl.isApplicable(file, offset, template)) { - if (prefix.equals(key)) { - result.addElement(LookupElementBuilder.create(key).setTypeText(template.getDescription()).setInsertHandler(new InsertHandler() { - @Override - public void handleInsert(InsertionContext context, LookupElement item) { - context.getDocument().deleteString(context.getStartOffset(), context.getTailOffset()); - context.setAddCompletionChar(false); - TemplateManager.getInstance(context.getProject()).startTemplate(context.getEditor(), template); - } - })); - } else { - result.restartCompletionOnPrefixChange(key); + final TemplateImpl template = findApplicableTemplate(file, offset, prefix); + if (template != null) { + result.addElement(LookupElementBuilder.create(prefix).setTypeText(template.getDescription()).setInsertHandler(new InsertHandler() { + @Override + public void handleInsert(InsertionContext context, LookupElement item) { + context.getDocument().deleteString(context.getStartOffset(), context.getTailOffset()); + context.setAddCompletionChar(false); + TemplateManager.getInstance(context.getProject()).startTemplate(context.getEditor(), template); } + })); + } else { + for (final TemplateImpl possible : listApplicableTemplates(file, offset)) { + result.restartCompletionOnPrefixChange(possible.getKey()); } } + + } + }); + } + + private static List listApplicableTemplates(PsiFile file, int offset) { + final ArrayList result = CollectionFactory.arrayList(); + for (final TemplateImpl template : TemplateSettings.getInstance().getTemplates()) { + if (!template.isDeactivated() && !template.isSelectionTemplate() && TemplateManagerImpl.isApplicable(file, offset, template)) { + result.add(template); + } + } + return result; + } + + @Nullable + public static TemplateImpl findApplicableTemplate(PsiFile file, int offset, final String key) { + return ContainerUtil.find(listApplicableTemplates(file, offset), new Condition() { + @Override + public boolean value(TemplateImpl template) { + return key.equals(template.getKey()); } }); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java index ffc78ecdb415..6eb72386deba 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java @@ -690,7 +690,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo return candidates; } - private char getShortcutChar(TemplateImpl template) { + public char getShortcutChar(TemplateImpl template) { char c = template.getShortcutChar(); if (c == DEFAULT_CHAR) { return getDefaultShortcutChar(); From d1fe69e7882f7dbc7183ce8827c80ebc5e93283e Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 21:14:27 +0300 Subject: [PATCH 29/55] if I invoked another completion instance, the previous one should be canceled, and not restarted --- .../completion/CompletionProgressIndicator.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index a4391c524793..629c37434e2a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -88,7 +88,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement }; private LightweightHint myHint; private final Semaphore myFreezeSemaphore; - private boolean myToRestart; + private Boolean myToRestart; private boolean myModifiersReleased; @@ -436,6 +436,8 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } private void finishCompletionProcess() { + assert myToRestart == null; + myToRestart = false; cancel(); assert !myDisposed; @@ -474,7 +476,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement invokeLaterIfNotDispatch(new Runnable() { public void run() { if (isOutdated()) return; - if (isCanceled() && !myToRestart) return; // otherwise + if (isCanceled() && myToRestart != Boolean.TRUE) return; //what if a new completion was invoked by the user before this 'later'? if (CompletionProgressIndicator.this != CompletionServiceImpl.getCompletionService().getCurrentCompletion()) return; @@ -528,6 +530,11 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } public void cancelByWriteAction() { + if (myToRestart != null) { + assert myToRestart == Boolean.FALSE; //explicit completionFinished was invoked before this write action + return; + } + myToRestart = true; cancel(); @@ -605,7 +612,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } public void prefixUpdated() { - if (myToRestart) { + if (myToRestart == Boolean.TRUE) { scheduleRestart(); return; } From 8f93281caf9fe84e6635c464ae9b77fc6e84fabe Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 21:15:44 +0300 Subject: [PATCH 30/55] remove unneeded read action --- .../codeInsight/completion/CompletionService.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java index 94c607d7f167..e4911ca18ca1 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java @@ -140,13 +140,9 @@ public abstract class CompletionService { getVariantsFromContributors(parameters, null, new Consumer() { public void consume(final LookupElement lookupElement) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (lookupSet.add(lookupElement)) { - consumer.consume(lookupElement); - } - } - }); + if (lookupSet.add(lookupElement)) { + consumer.consume(lookupElement); + } } }); return lookupSet.toArray(new LookupElement[lookupSet.size()]); From 103701b6bf75654145ea3e97baadbae91c360826 Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 21:52:55 +0300 Subject: [PATCH 31/55] less destructive assertions --- .../codeInsight/completion/CompletionProgressIndicator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index 629c37434e2a..fc360e0b4295 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -436,7 +436,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } private void finishCompletionProcess() { - assert myToRestart == null; + LOG.assertTrue(myToRestart == null); myToRestart = false; cancel(); @@ -531,7 +531,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement public void cancelByWriteAction() { if (myToRestart != null) { - assert myToRestart == Boolean.FALSE; //explicit completionFinished was invoked before this write action + LOG.assertTrue(myToRestart == Boolean.FALSE); //explicit completionFinished was invoked before this write action return; } From 404c5818432f3c76d4bc9e4ef4ac76bb8b70fb44 Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 21:56:07 +0300 Subject: [PATCH 32/55] explicit completion cancellation is more important than the one by write action --- .../codeInsight/completion/CompletionProgressIndicator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index fc360e0b4295..878f97c9e7f3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -436,7 +436,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } private void finishCompletionProcess() { - LOG.assertTrue(myToRestart == null); myToRestart = false; cancel(); From 8fbbb158de9a099bbcb2a2e602cbc7d2d59d6811 Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 10 Nov 2010 21:59:44 +0300 Subject: [PATCH 33/55] don't show quick doc automatically in autopopup completion (IDEA-60080) --- .../codeInsight/lookup/impl/LookupManagerImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupManagerImpl.java index bbab5fcab3b1..bd18aa1f081f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupManagerImpl.java @@ -17,6 +17,8 @@ package com.intellij.codeInsight.lookup.impl; import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.completion.CompletionProcess; +import com.intellij.codeInsight.completion.CompletionService; import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; @@ -119,7 +121,10 @@ public class LookupManagerImpl extends LookupManager { final Runnable request = new Runnable() { public void run() { if (myActiveLookup == lookup) { - DocumentationManager.getInstance(myProject).showJavaDocInfo(editor, psiFile, false); + final CompletionProcess completion = CompletionService.getCompletionService().getCurrentCompletion(); + if (completion == null || !completion.isAutopopupCompletion()) { + DocumentationManager.getInstance(myProject).showJavaDocInfo(editor, psiFile, false); + } } } }; From 10e14c1ac8da7aea30b6078819b801f50b1d0c0a Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Wed, 10 Nov 2010 13:45:53 +0300 Subject: [PATCH 34/55] error detection --- .../util/indexing/FileBasedIndex.java | 34 ++++++++++++++----- .../util/io/EnumeratorStringDescriptor.java | 4 ++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java index a06c532ebe92..ee72101f67c8 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java @@ -437,7 +437,7 @@ public class FileBasedIndex implements ApplicationComponent { return index; } - private static PersistentHashMap> createIdToDataKeysIndex(ID indexId, + private static PersistentHashMap> createIdToDataKeysIndex(final ID indexId, final KeyDescriptor keyDescriptor, MemoryIndexStorage storage) throws IOException { final File indexStorageFile = IndexInfrastructure.getInputIndexStorageFile(indexId); @@ -446,19 +446,35 @@ public class FileBasedIndex implements ApplicationComponent { final DataExternalizer> dataExternalizer = new DataExternalizer>() { public void save(DataOutput out, Collection value) throws IOException { - DataInputOutputUtil.writeINT(out, value.size()); - for (K key : value) { - keyDescriptor.save(out, key); + try { + DataInputOutputUtil.writeINT(out, value.size()); + for (K key : value) { + keyDescriptor.save(out, key); + } + } + catch (IOException e) { + throw e; + } + catch (IllegalArgumentException e) { + throw new IOException("Error saving data for index " + indexId, e); } } public Collection read(DataInput in) throws IOException { - final int size = DataInputOutputUtil.readINT(in); - final List list = new ArrayList(); - for (int idx = 0; idx < size; idx++) { - list.add(keyDescriptor.read(in)); + try { + final int size = DataInputOutputUtil.readINT(in); + final List list = new ArrayList(); + for (int idx = 0; idx < size; idx++) { + list.add(keyDescriptor.read(in)); + } + return list; + } + catch (IOException e) { + throw e; + } + catch (IllegalArgumentException e) { + throw new IOException("Error reading data for index " + indexId, e); } - return list; } }; diff --git a/platform/util/src/com/intellij/util/io/EnumeratorStringDescriptor.java b/platform/util/src/com/intellij/util/io/EnumeratorStringDescriptor.java index d48acab44957..46a1a3c52771 100644 --- a/platform/util/src/com/intellij/util/io/EnumeratorStringDescriptor.java +++ b/platform/util/src/com/intellij/util/io/EnumeratorStringDescriptor.java @@ -15,6 +15,8 @@ */ package com.intellij.util.io; +import org.jetbrains.annotations.NotNull; + import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; @@ -34,7 +36,7 @@ public class EnumeratorStringDescriptor implements KeyDescriptor { return val1.equals(val2); } - public void save(final DataOutput storage, final String value) throws IOException { + public void save(final DataOutput storage, @NotNull final String value) throws IOException { IOUtil.writeUTFFast(buffer, storage, value); } From 9cfbb3cec723c321e8b501dcc6e4676a96142aac Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Wed, 10 Nov 2010 21:21:34 +0300 Subject: [PATCH 35/55] show "val$" fields only when stepping in anonymous class only --- .../src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index b9ac30c5c6c8..1e3048069631 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -505,7 +505,7 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi if (thisObjectReference != null && evaluationContext.getDebugProcess().getVirtualMachineProxy().canGetSyntheticAttribute()) { final ReferenceType thisRefType = thisObjectReference.referenceType(); - if (thisRefType instanceof ClassType && thisRefType.name().contains("$")) { // makes sense for nested classes only + if (thisRefType instanceof ClassType && thisRefType.equals(location.declaringType()) && thisRefType.name().contains("$")) { // makes sense for nested classes only final ClassType clsType = (ClassType)thisRefType; for (Field field : clsType.fields()) { if (field.isSynthetic() && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { From 9f8cdadd02b1d7e0a4a5dbfcd0d7f2c073c86710 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Wed, 10 Nov 2010 21:36:13 +0300 Subject: [PATCH 36/55] show "val$" fields only when stepping in anonymous class only, part 2 --- .../debugger/ui/tree/render/ClassRenderer.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java index eddc91bf1e26..201d63fd3038 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java @@ -21,12 +21,14 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; +import com.intellij.debugger.engine.jdi.StackFrameProxy; import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl; import com.intellij.debugger.ui.impl.watch.MessageDescriptor; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; import com.intellij.debugger.ui.tree.*; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; @@ -171,8 +173,16 @@ public class ClassRenderer extends NodeRendererImpl{ return false; } if (isSynthetic) { - if (objInstance.equals(context.getThisObject()) && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { - return false; + try { + final StackFrameProxy frameProxy = context.getFrameProxy(); + if (frameProxy != null) { + final Location location = frameProxy.location(); + if (location != null && objInstance.equals(context.getThisObject()) && Comparing.equal(objInstance.referenceType(), location.declaringType()) && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { + return false; + } + } + } + catch (EvaluateException ignored) { } } if(!SHOW_STATIC && field.isStatic()) { From c0a5801a9bb51d1c16afed403f9b3337366a8a29 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Wed, 10 Nov 2010 20:11:15 +0300 Subject: [PATCH 37/55] cleanup --- .../inject/quickedit/QuickEditAction.java | 36 ++-- .../inject/quickedit/QuickEditEditor.java | 182 ------------------ 2 files changed, 13 insertions(+), 205 deletions(-) delete mode 100644 plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditEditor.java diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java index 4bb9a1a5e4b5..0580a0487202 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.injected.editor.DocumentWindow; import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonShortcuts; @@ -40,10 +41,7 @@ import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.source.PostprocessReformattingAspect; @@ -65,18 +63,10 @@ import java.util.Map; import java.util.Set; /** - * "Quick Edit Language" intention action that provides a popup which shows an injected language + * "Quick Edit Language" intention action that provides an editor which shows an injected language * fragment's complete prefix and suffix in non-editable areas and allows to edit the fragment * without having to consider any additional escaping rules (e.g. when editing regexes in String * literals). - *

- * This is a bit experimental because it doesn't play very well with some quickfixes, such as the - * JavaScript's "Create Method/Function" one which opens another editor window. Though harmless, - * this is quite confusing. - *

- * I wonder if such QuickFixes should try to get an Editor from the DataContext - * (see {@link QuickEditEditor.MyPanel#getData(java.lang.String)}) instead of using the "tactical nuke" - * com.intellij.openapi.fileEditor.FileEditorManager#openTextEditor(com.intellij.openapi.fileEditor.OpenFileDescriptor, boolean). */ public class QuickEditAction implements IntentionAction, LowPriorityAction { @@ -139,7 +129,7 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction { return handler; } - private static class MyHandler extends DocumentAdapter { + private static class MyHandler extends DocumentAdapter implements Disposable { private final Project myProject; private final PsiFile myInjectedFile; @@ -199,20 +189,19 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction { closeEditor(); } }.registerCustomShortcutSet(CommonShortcuts.ESCAPE, event.getEditor().getContentComponent()); - } } @Override public void editorReleased(EditorFactoryEvent event) { if (event.getEditor().getDocument() == myNewDocument) { + Disposer.dispose(MyHandler.this); myReleased = true; - event.getFactory().removeEditorFactoryListener(this); myOrigDocument.removeDocumentListener(MyHandler.this); myInjectedFile.putUserData(QUICK_EDIT_HANDLER, null); } } - }, project); + }, this); initMarkers(shreds); } @@ -261,15 +250,11 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction { myOrigDocument.createRangeMarker(rangeInsideHost.shiftRight(shred.host.getTextRange().getStartOffset())); myMarkers.put(smartPointerManager.createSmartPsiElementPointer(shred.host), Pair.create(origMarker, rangeMarker)); } - boolean first = true; for (Pair markers : myMarkers.values()) { - //if (first) { - markers.first.setGreedyToLeft(true); - markers.second.setGreedyToLeft(true); - //} + markers.first.setGreedyToLeft(true); + markers.second.setGreedyToLeft(true); markers.first.setGreedyToRight(true); markers.second.setGreedyToRight(true); - first = false; } int curOffset = 0; for (Pair markerPair : myMarkers.values()) { @@ -346,5 +331,10 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction { ElementManipulators.getManipulator(host).handleContentChange(host, insideHost, sb.toString()); } } + + @Override + public void dispose() { + // noop + } } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditEditor.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditEditor.java deleted file mode 100644 index d028b9e401dc..000000000000 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditEditor.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * 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.intellij.plugins.intelliLang.inject.quickedit; - -import com.intellij.ide.highlighter.HighlighterFactory; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.Result; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.EditorSettings; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.impl.EditorImpl; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.wm.StatusBar; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.ui.plaf.beg.BegBorders; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; - -// Adapted from QuickEditHotspot that came with the source of the first designer release ;) -public class QuickEditEditor { - - private final Project myProject; - private final JComponent myPanel; - private final QuickEditSaver mySaver; - - private EditorEx myEditor; - private JBPopup myPopup; - private Boolean myCancelFlag; - - public QuickEditEditor(Document document, Project project, FileType ft, @NotNull QuickEditSaver saver) { - myProject = project; - mySaver = saver; - myEditor = (EditorImpl)EditorFactory.getInstance().createEditor(document, project); - myEditor.setHighlighter(HighlighterFactory.createHighlighter(project, ft)); - myEditor.setEmbeddedIntoDialogWrapper(true); - - EditorSettings settings = myEditor.getSettings(); - settings.setFoldingOutlineShown(false); - settings.setLineMarkerAreaShown(false); - settings.setIndentGuidesShown(false); - settings.setLineNumbersShown(false); - settings.setVirtualSpace(false); - settings.setAdditionalLinesCount(2); - - myPanel = new MyPanel(); - } - - public JComponent getPreferredFocusedComponent() { - return myEditor.getContentComponent(); - } - - public Editor getEditor() { - return myEditor; - } - - private class MyPanel extends JPanel implements DataProvider { - - public MyPanel() { - super(new BorderLayout()); - - add(myEditor.getComponent(), BorderLayout.CENTER); - - setBorder(new BegBorders.FlatLineBorder()); - setPreferredSize(new Dimension(400, 100)); - } - - public Object getData(String s) { - if (LangDataKeys.EDITOR.is(s)) { - return myEditor; - } - return null; - } - } - - public void setCancel(boolean cancel) { - if (myCancelFlag == null) myCancelFlag = cancel; - } - - private String releaseEditor() { - if (myEditor != null) { - final Document document = myEditor.getDocument(); - final String text = document.getText(); - PsiDocumentManager.getInstance(myProject).commitDocument(document); - EditorFactory.getInstance().releaseEditor(myEditor); - myEditor = null; - return text; - } - return ""; - } - - public void install(JBPopup popup) { - myPopup = popup; - final JComponent component = myEditor.getContentComponent(); - component.requestFocus(); - - new EscAction(this).registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)), component); - new SaveAction(this) - .registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_MASK)), component); - - setStatusBarText("Press Ctrl+Enter to save, Escape to cancel."); - } - - private void setStatusBarText(String text) { - StatusBar.Info.set(text, myProject); - } - - public void uninstall() { - setStatusBarText(""); - - final String text = releaseEditor(); - - assert myCancelFlag != null; - if (!myCancelFlag) { - new WriteCommandAction(myProject) { - protected void run(Result result) throws Throwable { - mySaver.save(text); - } - }.execute(); - } - myPopup = null; - } - - public JComponent getComponent() { - return myPanel; - } - - public interface QuickEditSaver { - void save(String text); - } - - private static class EscAction extends AnAction { - private final QuickEditEditor myEditor; - - public EscAction(QuickEditEditor editor) { - super("Esc"); - myEditor = editor; - } - - public void actionPerformed(AnActionEvent event) { - myEditor.setCancel(true); - myEditor.myPopup.cancel(); - } - } - - private static class SaveAction extends AnAction { - private final QuickEditEditor myEditor; - - public SaveAction(QuickEditEditor editor) { - super("Save"); - myEditor = editor; - } - - public void actionPerformed(AnActionEvent event) { - myEditor.setCancel(false); - myEditor.myPopup.cancel(); - } - } -} - - From d407e35c185ef835dd57bfc738e1b75d5f0fefdd Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Wed, 10 Nov 2010 22:24:06 +0300 Subject: [PATCH 38/55] fix empty splitter when reloading in presence of invalid/missing files --- .../fileEditor/impl/EditorsSplitters.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index 5b3ca6a70e9d..0d03a6a7ad0c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -221,6 +221,14 @@ public class EditorsSplitters extends JPanel { } mySplittersElement = null; } + // clear empty splitters + for (EditorWindow window : getWindows()) { + if (window.getEditors().length == 0) { + for (EditorWindow sibling : window.findSiblings()) { + sibling.unsplit(false); + } + } + } } } @@ -228,6 +236,7 @@ public class EditorsSplitters extends JPanel { mySplittersElement = element; } + @Nullable @SuppressWarnings({"HardCodedStringLiteral"}) public JPanel readExternalPanel(final Element element, @Nullable JPanel panel) { final Element splitterElement = element.getChild("splitter"); @@ -263,11 +272,11 @@ public class EditorsSplitters extends JPanel { } else { window = findWindowWith(panel); } - try { - //noinspection unchecked - final List children = leaf.getChildren("file"); - VirtualFile currentFile = null; - for (final Element file : children) { + //noinspection unchecked + final List children = leaf.getChildren("file"); + VirtualFile currentFile = null; + for (final Element file : children) { + try { final HistoryEntry entry = new HistoryEntry(getManager().getProject(), file.getChild(HistoryEntry.TAG)); boolean isCurrent = Boolean.valueOf(file.getAttributeValue("current")).booleanValue(); getManager().openFileImpl3(window, entry.myFile, false, entry, isCurrent); @@ -281,15 +290,15 @@ public class EditorsSplitters extends JPanel { } } } - if (currentFile != null) { - final EditorComposite editor = window.findFileComposite(currentFile); - if (editor != null) { - window.setSelectedEditor(editor, true); - } + catch (InvalidDataException e) { + // OK } } - catch (InvalidDataException e) { - // OK + if (currentFile != null) { + final EditorComposite editor = window.findFileComposite(currentFile); + if (editor != null) { + window.setSelectedEditor(editor, true); + } } return window.myPanel; } From c305d9c014dd1671ac4b9420f5b9e265df876d6d Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Wed, 10 Nov 2010 23:05:09 +0300 Subject: [PATCH 39/55] IDEA-52375 Smart quote doesn't work in SQL string, adds two extra quotes instead of none. --- .../intellij/codeInsight/editorActions/TypedHandler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java index e96388493ee2..8064fecf5760 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java @@ -266,7 +266,13 @@ public class TypedHandler implements TypedActionHandler { for (DocumentWindow documentWindow : InjectedLanguageUtil.getCachedInjectedDocuments(oldFile)) { if (documentWindow.isValid() && documentWindow.containsRange(offset, offset)) { PsiFile injectedFile = PsiDocumentManager.getInstance(oldFile.getProject()).getPsiFile(documentWindow); - return InjectedLanguageUtil.getInjectedEditorForInjectedFile(editor, injectedFile); + final Editor injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(editor, injectedFile); + // IDEA-52375 fix: last quote sign should be handled by outer language quote handler + if (injectedEditor.getCaretModel().getOffset() == injectedEditor.getDocument().getTextLength() && + charTyped == editor.getDocument().getCharsSequence().charAt(offset)) { + return editor; + } + return injectedEditor; } } From 7a1117dfa50cdcffc6c0733f664e624e4112d489 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 10 Nov 2010 22:36:08 +0100 Subject: [PATCH 40/55] fix inspections I broke while under the influence of stupidity --- ...cessarilyQualifiedInnerClassAccessInspection.java | 12 ++++++------ .../style/UnqualifiedInnerClassAccessInspection.java | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessarilyQualifiedInnerClassAccessInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessarilyQualifiedInnerClassAccessInspection.java index aa32f2d187b8..02e7e14dae0c 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessarilyQualifiedInnerClassAccessInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessarilyQualifiedInnerClassAccessInspection.java @@ -94,13 +94,13 @@ public class UnnecessarilyQualifiedInnerClassAccessInspection extends BaseInspectionVisitor { @Override - public void visitReferenceExpression(PsiReferenceExpression expression) { - super.visitReferenceExpression(expression); - final PsiElement parent = expression.getParent(); + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + final PsiElement parent = reference.getParent(); if (parent instanceof PsiImportStatement) { return; } - final PsiElement qualifier = expression.getQualifier(); + final PsiElement qualifier = reference.getQualifier(); if (!(qualifier instanceof PsiJavaCodeReferenceElement)) { return; } @@ -110,7 +110,7 @@ public class UnnecessarilyQualifiedInnerClassAccessInspection if (!(qualifierTarget instanceof PsiClass)) { return; } - final PsiElement target = expression.resolve(); + final PsiElement target = reference.resolve(); if (!(target instanceof PsiClass)) { return; } @@ -123,7 +123,7 @@ public class UnnecessarilyQualifiedInnerClassAccessInspection return; } final String shortName = aClass.getName(); - if (!isReferenceToTargetClass(shortName, aClass, expression)) { + if (!isReferenceToTargetClass(shortName, aClass, reference)) { return; } registerError(qualifier, aClass); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspection.java index 4d87601434d1..4bb3f25da48c 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspection.java @@ -261,13 +261,13 @@ public class UnqualifiedInnerClassAccessInspection extends BaseInspection { extends BaseInspectionVisitor { @Override - public void visitReferenceExpression( - PsiReferenceExpression expression) { - super.visitReferenceExpression(expression); - if (expression.isQualified()) { + public void visitReferenceElement( + PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + if (reference.isQualified()) { return; } - final PsiElement target = expression.resolve(); + final PsiElement target = reference.resolve(); if (!(target instanceof PsiClass)) { return; } @@ -276,7 +276,7 @@ public class UnqualifiedInnerClassAccessInspection extends BaseInspection { if (containingClass == null) { return; } - registerError(expression, containingClass.getName()); + registerError(reference, containingClass.getName()); } } } From 8462127d4dcb1a78caf3e29250630b7b90df9add Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Thu, 11 Nov 2010 02:11:46 +0300 Subject: [PATCH 41/55] Copy-on-write synchronization strategy for DirectoryIndex --- .../roots/impl/DirectoryIndexImpl.java | 1035 +++++++++-------- 1 file changed, 525 insertions(+), 510 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java index 3dee16480dbb..ee81f60378c5 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java @@ -57,12 +57,7 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone private boolean myInitialized = false; private boolean myDisposed = false; - - private Map> myExcludeRootsMap; - private Set myProjectExcludeRoots; - private Map myDirToInfoMap = new THashMap(); - private Map> myPackageNameToDirsMap = new THashMap>(); - private final Map myDirToPackageName = new THashMap(); + private volatile IndexState myState; private final DirectoryIndexExcludePolicy[] myExcludePolicies; private final MessageBusConnection myConnection; @@ -77,6 +72,7 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone } }); myExcludePolicies = Extensions.getExtensions(DirectoryIndexExcludePolicy.EP_NAME, myProject); + myState = new IndexState(); } @NotNull @@ -87,7 +83,7 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone public void initComponent() { } - public synchronized void disposeComponent() { + public void disposeComponent() { myDisposed = true; } @@ -108,27 +104,24 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone assert myInitialized; assert !myDisposed; - Map oldDirToInfoMap = myDirToInfoMap; - myDirToInfoMap = new THashMap(); + IndexState oldState = myState; + myState = myState.copy(); - Map> oldPackageNameToDirsMap = myPackageNameToDirsMap; - myPackageNameToDirsMap = new THashMap>(); + myState.doInitialize(reverseAllSets); - doInitialize(reverseAllSets); - - Set keySet = myDirToInfoMap.keySet(); - assert keySet.size() == oldDirToInfoMap.keySet().size(); + Set keySet = myState.myDirToInfoMap.keySet(); + assert keySet.size() == oldState.myDirToInfoMap.keySet().size(); for (VirtualFile file : keySet) { - DirectoryInfo info1 = myDirToInfoMap.get(file); - DirectoryInfo info2 = oldDirToInfoMap.get(file); + DirectoryInfo info1 = myState.myDirToInfoMap.get(file); + DirectoryInfo info2 = oldState.myDirToInfoMap.get(file); assert info1.equals(info2); } - assert myPackageNameToDirsMap.keySet().size() == oldPackageNameToDirsMap.keySet().size(); - for (Map.Entry> entry : myPackageNameToDirsMap.entrySet()) { + assert myState.myPackageNameToDirsMap.keySet().size() == oldState.myPackageNameToDirsMap.keySet().size(); + for (Map.Entry> entry : myState.myPackageNameToDirsMap.entrySet()) { String packageName = entry.getKey(); List dirs = entry.getValue(); - List dirs1 = oldPackageNameToDirsMap.get(packageName); + List dirs1 = oldState.myPackageNameToDirsMap.get(packageName); HashSet set1 = new HashSet(); set1.addAll(dirs); @@ -138,11 +131,11 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone } } - public synchronized boolean isInitialized() { + public boolean isInitialized() { return myInitialized; } - public synchronized void initialize() { + public void initialize() { if (myInitialized) { LOG.error("Directory index is already initialized."); return; @@ -182,106 +175,8 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone } private void doInitialize() { - doInitialize(false); - } - - private synchronized void doInitialize(boolean reverseAllSets/* for testing order independence*/) { - ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - if (progress == null) progress = new EmptyProgressIndicator(); - - progress.pushState(); - - progress.checkCanceled(); - progress.setText(ProjectBundle.message("project.index.scanning.files.progress")); - - cleanAllMaps(); - - Module[] modules = ModuleManager.getInstance(myProject).getModules(); - if (reverseAllSets) modules = ArrayUtil.reverseArray(modules); - - initExcludedDirMap(modules, progress); - - for (Module module : modules) { - initModuleContents(module, reverseAllSets, progress); - } - // Important! Because module's contents may overlap, - // first modules should be marked and only after that sources markup - // should be added. (src markup depends on module markup) - for (Module module : modules) { - initModuleSources(module, reverseAllSets, progress); - initLibrarySources(module, progress); - initLibraryClasses(module, progress); - } - - progress.checkCanceled(); - progress.setText2(""); - - for (Module module : modules) { - initOrderEntries(module); - } - - progress.popState(); - } - - private void cleanAllMaps() { - myDirToInfoMap.clear(); - myPackageNameToDirsMap.clear(); - myDirToPackageName.clear(); - } - - private void initExcludedDirMap(Module[] modules, ProgressIndicator progress) { - progress.checkCanceled(); - progress.setText2(ProjectBundle.message("project.index.building.exclude.roots.progress")); - - // exclude roots should be merged to prevent including excluded dirs of an inner module into the outer - // exclude root should exclude from its content root and all outer content roots - Map> result = new THashMap>(); - Set projectExcludeRoots = new THashSet(); - - for (Module module : modules) { - for (ContentEntry contentEntry : getContentEntries(module)) { - VirtualFile contentRoot = contentEntry.getFile(); - if (contentRoot == null) continue; - - ExcludeFolder[] excludeRoots = contentEntry.getExcludeFolders(); - for (ExcludeFolder excludeRoot : excludeRoots) { - // Output paths should be excluded (if marked as such) regardless if they're under corresponding module's content root - if (excludeRoot.getFile() != null) { - if (!FileUtil.startsWith(contentRoot.getUrl(), excludeRoot.getUrl())) { - if (isExcludeRootForModule(module, excludeRoot.getFile())) { - putForFileAndAllAncestors(result, excludeRoot.getFile(), excludeRoot.getUrl()); - } - } - } - - putForFileAndAllAncestors(result, contentRoot, excludeRoot.getUrl()); - } - } - } - - for (DirectoryIndexExcludePolicy policy : myExcludePolicies) { - for (VirtualFile file : policy.getExcludeRootsForProject()) { - putForFileAndAllAncestors(result, file, file.getUrl()); - projectExcludeRoots.add(file); - } - } - - myExcludeRootsMap = result; - myProjectExcludeRoots = projectExcludeRoots; - } - - private static void putForFileAndAllAncestors(Map> map, VirtualFile file, String value) { - while (true) { - Set set = map.get(file); - if (set == null) { - set = new HashSet(); - map.put(file, set); - } - set.add(value); - - file = file.getParent(); - if (file == null) break; - } + myState = new IndexState(); + myState.doInitialize(false); } private boolean isExcludeRootForModule(Module module, VirtualFile excludeRoot) { @@ -299,297 +194,21 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone return ModuleRootManager.getInstance(module).getOrderEntries(); } - private void initModuleContents(Module module, boolean reverseAllSets, ProgressIndicator progress) { - progress.checkCanceled(); - progress.setText2(ProjectBundle.message("project.index.processing.module.content.progress", module.getName())); - - ModuleRootManager rootManager = ModuleRootManager.getInstance(module); - VirtualFile[] contentRoots = rootManager.getContentRoots(); - if (reverseAllSets) { - contentRoots = ArrayUtil.reverseArray(contentRoots); - } - - for (final VirtualFile contentRoot : contentRoots) { - fillMapWithModuleContent(contentRoot, module, contentRoot); - } - } - - private void fillMapWithModuleContent(VirtualFile dir, Module module, VirtualFile contentRoot) { - if (isExcluded(contentRoot, dir)) return; - if (isIgnored(dir)) return; - - DirectoryInfo info = getOrCreateDirInfo(dir); - - if (info.module != null) { // module contents overlap - DirectoryInfo parentInfo = myDirToInfoMap.get(dir.getParent()); - if (parentInfo == null || !info.module.equals(parentInfo.module)) return; // content of another module is below this module's content - } - - VirtualFile[] children = dir.getChildren(); - for (VirtualFile child : children) { - if (child.isDirectory()) { - fillMapWithModuleContent(child, module, contentRoot); - } - } - - // important to change module AFTER processing children - to handle overlapping modules - info.module = module; - info.contentRoot = contentRoot; - } - - private boolean isExcluded(VirtualFile root, VirtualFile dir) { - Set excludes = myExcludeRootsMap.get(root); - return excludes != null && excludes.contains(dir.getUrl()); - } - - private void initModuleSources(Module module, boolean reverseAllSets, ProgressIndicator progress) { - progress.checkCanceled(); - progress.setText2(ProjectBundle.message("project.index.processing.module.sources.progress", module.getName())); - - ContentEntry[] contentEntries = getContentEntries(module); - - if (reverseAllSets) { - contentEntries = ArrayUtil.reverseArray(contentEntries); - } - - for (ContentEntry contentEntry : contentEntries) { - SourceFolder[] sourceFolders = contentEntry.getSourceFolders(); - if (reverseAllSets) { - sourceFolders = ArrayUtil.reverseArray(sourceFolders); - } - for (SourceFolder sourceFolder : sourceFolders) { - VirtualFile dir = sourceFolder.getFile(); - if (dir != null) { - fillMapWithModuleSource(dir, module, sourceFolder.getPackagePrefix(), dir, sourceFolder.isTestSource()); - } - } - } - } - - private void fillMapWithModuleSource(VirtualFile dir, Module module, String packageName, VirtualFile sourceRoot, boolean isTestSource) { - DirectoryInfo info = myDirToInfoMap.get(dir); - if (info == null) return; - if (!module.equals(info.module)) return; - - if (info.isInModuleSource) { // module sources overlap - String definedPackage = myDirToPackageName.get(dir); - if (definedPackage != null && definedPackage.length() == 0) return; // another source root starts here - } - - info.isInModuleSource = true; - info.isTestSource = isTestSource; - info.sourceRoot = sourceRoot; - setPackageName(dir, packageName); - - VirtualFile[] children = dir.getChildren(); - for (VirtualFile child : children) { - if (child.isDirectory()) { - String childPackageName = getPackageNameForSubdir(packageName, child.getName()); - fillMapWithModuleSource(child, module, childPackageName, sourceRoot, isTestSource); - } - } - } - - private void initLibrarySources(Module module, ProgressIndicator progress) { - progress.checkCanceled(); - progress.setText2(ProjectBundle.message("project.index.processing.library.sources.progress", module.getName())); - - for (OrderEntry orderEntry : getOrderEntries(module)) { - boolean isLibrary = orderEntry instanceof LibraryOrderEntry || orderEntry instanceof JdkOrderEntry; - if (isLibrary) { - VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); - for (final VirtualFile sourceRoot : sourceRoots) { - fillMapWithLibrarySources(sourceRoot, "", sourceRoot); - } - } - } - } - - private void fillMapWithLibrarySources(VirtualFile dir, String packageName, VirtualFile sourceRoot) { - if (isIgnored(dir)) return; - - DirectoryInfo info = getOrCreateDirInfo(dir); - - if (info.isInLibrarySource) { // library sources overlap - String definedPackage = myDirToPackageName.get(dir); - if (definedPackage != null && definedPackage.length() == 0) return; // another library source root starts here - } - - info.isInModuleSource = false; - info.isInLibrarySource = true; - info.sourceRoot = sourceRoot; - setPackageName(dir, packageName); - - VirtualFile[] children = dir.getChildren(); - for (VirtualFile child : children) { - if (child.isDirectory()) { - String childPackageName = getPackageNameForSubdir(packageName, child.getName()); - fillMapWithLibrarySources(child, childPackageName, sourceRoot); - } - } - } - - private void initLibraryClasses(Module module, ProgressIndicator progress) { - progress.checkCanceled(); - progress.setText2(ProjectBundle.message("project.index.processing.library.classes.progress", module.getName())); - - for (OrderEntry orderEntry : getOrderEntries(module)) { - boolean isLibrary = orderEntry instanceof LibraryOrderEntry || orderEntry instanceof JdkOrderEntry; - if (isLibrary) { - VirtualFile[] classRoots = orderEntry.getFiles(OrderRootType.CLASSES); - for (final VirtualFile classRoot : classRoots) { - fillMapWithLibraryClasses(classRoot, "", classRoot); - } - } - } - } - - private void fillMapWithLibraryClasses(VirtualFile dir, String packageName, VirtualFile classRoot) { - if (isIgnored(dir)) return; - - DirectoryInfo info = getOrCreateDirInfo(dir); - - if (info.libraryClassRoot != null) { // library classes overlap - String definedPackage = myDirToPackageName.get(dir); - if (definedPackage != null && definedPackage.length() == 0) return; // another library root starts here - } - - info.libraryClassRoot = classRoot; - - if (!info.isInModuleSource && !info.isInLibrarySource) { - setPackageName(dir, packageName); - } - - VirtualFile[] children = dir.getChildren(); - for (VirtualFile child : children) { - if (child.isDirectory()) { - String childPackageName = getPackageNameForSubdir(packageName, child.getName()); - fillMapWithLibraryClasses(child, childPackageName, classRoot); - } - } - } - - private void initOrderEntries(Module module) { - Map> depEntries = new HashMap>(); - Map> libClassRootEntries = new HashMap>(); - Map> libSourceRootEntries = new HashMap>(); - - for (OrderEntry orderEntry : getOrderEntries(module)) { - if (orderEntry instanceof ModuleOrderEntry) { - final Module depModule = ((ModuleOrderEntry)orderEntry).getModule(); - if (depModule != null) { - VirtualFile[] importedClassRoots = - OrderEnumerator.orderEntries(depModule).exportedOnly().recursively().classes().usingCache().getRoots(); - for (VirtualFile importedClassRoot : importedClassRoots) { - addEntryToMap(importedClassRoot, orderEntry, depEntries); - } - } - VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); - for (VirtualFile sourceRoot : sourceRoots) { - addEntryToMap(sourceRoot, orderEntry, depEntries); - } - } - else if (orderEntry instanceof ModuleSourceOrderEntry) { - List oneEntryList = Arrays.asList(orderEntry); - Module entryModule = orderEntry.getOwnerModule(); - - VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); - for (VirtualFile sourceRoot : sourceRoots) { - fillMapWithOrderEntries(sourceRoot, oneEntryList, entryModule, null, null, null, null); - } - } - else if (orderEntry instanceof LibraryOrderEntry || orderEntry instanceof JdkOrderEntry) { - VirtualFile[] classRoots = orderEntry.getFiles(OrderRootType.CLASSES); - for (VirtualFile classRoot : classRoots) { - addEntryToMap(classRoot, orderEntry, libClassRootEntries); - } - VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); - for (VirtualFile sourceRoot : sourceRoots) { - addEntryToMap(sourceRoot, orderEntry, libSourceRootEntries); - } - } - } - - for (Map.Entry> mapEntry : depEntries.entrySet()) { - final VirtualFile vRoot = mapEntry.getKey(); - final List entries = mapEntry.getValue(); - fillMapWithOrderEntries(vRoot, entries, null, null, null, null, null); - } - - for (Map.Entry> mapEntry : libClassRootEntries.entrySet()) { - final VirtualFile vRoot = mapEntry.getKey(); - final List entries = mapEntry.getValue(); - fillMapWithOrderEntries(vRoot, entries, null, vRoot, null, null, null); - } - - for (Map.Entry> mapEntry : libSourceRootEntries.entrySet()) { - final VirtualFile vRoot = mapEntry.getKey(); - final List entries = mapEntry.getValue(); - fillMapWithOrderEntries(vRoot, entries, null, null, vRoot, null, null); - } - } - - private static void addEntryToMap(final VirtualFile vRoot, final OrderEntry entry, final Map> map) { - List list = map.get(vRoot); - if (list == null) { - list = new ArrayList(); - map.put(vRoot, list); - } - list.add(entry); - } - - private void fillMapWithOrderEntries(VirtualFile dir, - List orderEntries, - Module module, - VirtualFile libraryClassRoot, - VirtualFile librarySourceRoot, - DirectoryInfo parentInfo, - final List oldParentEntries) { - if (isIgnored(dir)) return; - - DirectoryInfo info = myDirToInfoMap.get(dir); // do not create it here! - if (info == null) return; - - if (module != null) { - if (info.module != module) return; - if (!info.isInModuleSource) return; - } - else if (libraryClassRoot != null) { - if (info.libraryClassRoot != libraryClassRoot) return; - if (info.isInModuleSource) return; - } - else if (librarySourceRoot != null) { - if (!info.isInLibrarySource) return; - if (info.sourceRoot != librarySourceRoot) return; - if (info.libraryClassRoot != null) return; - } - - final List oldEntries = info.getOrderEntries(); - info.addOrderEntries(orderEntries, parentInfo, oldParentEntries); - - final VirtualFile[] children = dir.getChildren(); - for (VirtualFile child : children) { - if (child.isDirectory()) { - fillMapWithOrderEntries(child, orderEntries, module, libraryClassRoot, librarySourceRoot, info, oldEntries); - } - } - } - private static boolean isIgnored(@NotNull VirtualFile f) { return FileTypeManager.getInstance().isFileIgnored(f.getName()); } - public synchronized DirectoryInfo getInfoForDirectory(VirtualFile dir) { + public DirectoryInfo getInfoForDirectory(VirtualFile dir) { checkAvailability(); dispatchPendingEvents(); - return myDirToInfoMap.get(dir); + return myState.myDirToInfoMap.get(dir); } @Override - public synchronized boolean isProjectExcludeRoot(VirtualFile dir) { + public boolean isProjectExcludeRoot(VirtualFile dir) { checkAvailability(); - return myProjectExcludeRoots.contains(dir); + return myState.myProjectExcludeRoots.contains(dir); } private final PackageSink mySink = new PackageSink(); @@ -625,22 +244,22 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone } @NotNull - public synchronized Query getDirectoriesByPackageName(@NotNull String packageName, boolean includeLibrarySources) { + public Query getDirectoriesByPackageName(@NotNull String packageName, boolean includeLibrarySources) { checkAvailability(); return mySink.search(packageName, includeLibrarySources); } @Override - public synchronized String getPackageName(VirtualFile dir) { + public String getPackageName(VirtualFile dir) { checkAvailability(); - return myDirToPackageName.get(dir); + return myState.myDirToPackageName.get(dir); } @NotNull - private synchronized List doGetDirectoriesByPackageName(@NotNull String packageName) { + private List doGetDirectoriesByPackageName(@NotNull String packageName) { dispatchPendingEvents(); - List dirs = myPackageNameToDirsMap.get(packageName); + List dirs = myState.myPackageNameToDirsMap.get(packageName); return dirs != null ? dirs : Collections.emptyList(); } @@ -658,44 +277,6 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone } } - private DirectoryInfo getOrCreateDirInfo(VirtualFile dir) { - DirectoryInfo info = myDirToInfoMap.get(dir); - if (info == null) { - info = new DirectoryInfo(); - myDirToInfoMap.put(dir, info); - } - return info; - } - - private void setPackageName(VirtualFile dir, String newPackageName) { - assert dir != null; - - String oldPackageName = myDirToPackageName.get(dir); - if (oldPackageName != null) { - List oldPackageDirs = myPackageNameToDirsMap.get(oldPackageName); - final boolean removed = oldPackageDirs.remove(dir); - assert removed; - - if (oldPackageDirs.size() == 0) { - myPackageNameToDirsMap.remove(oldPackageName); - } - } - - if (newPackageName != null) { - List newPackageDirs = myPackageNameToDirsMap.get(newPackageName); - if (newPackageDirs == null) { - newPackageDirs = new SmartList(); - myPackageNameToDirsMap.put(newPackageName, newPackageDirs); - } - newPackageDirs.add(dir); - - myDirToPackageName.put(dir, newPackageName); - } - else { - myDirToPackageName.remove(dir); - } - } - @Nullable private static String getPackageNameForSubdir(String parentPackageName, String subdirName) { if (parentPackageName == null) return null; @@ -713,85 +294,73 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone VirtualFile parent = file.getParent(); if (parent == null) return; - synchronized (DirectoryIndexImpl.this) { - DirectoryInfo parentInfo = myDirToInfoMap.get(parent); + IndexState state = myState.copy(); - // fill info for all nested roots - for (Module eachModule : ModuleManager.getInstance(myProject).getModules()) { - for (ContentEntry eachRoot : getContentEntries(eachModule)) { - if (parentInfo != null && eachRoot == parentInfo.contentRoot) continue; + DirectoryInfo parentInfo = state.myDirToInfoMap.get(parent); - if (FileUtil.startsWith(eachRoot.getUrl(), file.getUrl())) { - String rel = FileUtil.getRelativePath(file.getUrl(), eachRoot.getUrl(), '/'); - if (rel != null) { - VirtualFile f = file.findFileByRelativePath(rel); - if (f != null) { - fillMapWithModuleContent(f, eachModule, f); - } + // fill info for all nested roots + for (Module eachModule : ModuleManager.getInstance(myProject).getModules()) { + for (ContentEntry eachRoot : getContentEntries(eachModule)) { + if (parentInfo != null && eachRoot == parentInfo.contentRoot) continue; + + if (FileUtil.startsWith(eachRoot.getUrl(), file.getUrl())) { + String rel = FileUtil.getRelativePath(file.getUrl(), eachRoot.getUrl(), '/'); + if (rel != null) { + VirtualFile f = file.findFileByRelativePath(rel); + if (f != null) { + state.fillMapWithModuleContent(f, eachModule, f); } } } } + } - if (parentInfo == null) return; + if (parentInfo == null) return; - Module module = parentInfo.module; + Module module = parentInfo.module; - for (DirectoryIndexExcludePolicy policy : myExcludePolicies) { - if (policy.isExcludeRoot(file)) return; - } + for (DirectoryIndexExcludePolicy policy : myExcludePolicies) { + if (policy.isExcludeRoot(file)) return; + } - fillMapWithModuleContent(file, module, parentInfo.contentRoot); + state.fillMapWithModuleContent(file, module, parentInfo.contentRoot); - String parentPackage = myDirToPackageName.get(parent); + String parentPackage = state.myDirToPackageName.get(parent); - if (module != null) { - if (parentInfo.isInModuleSource) { - String newDirPackageName = getPackageNameForSubdir(parentPackage, file.getName()); - fillMapWithModuleSource(file, module, newDirPackageName, parentInfo.sourceRoot, parentInfo.isTestSource); - } - } - - if (parentInfo.libraryClassRoot != null) { + if (module != null) { + if (parentInfo.isInModuleSource) { String newDirPackageName = getPackageNameForSubdir(parentPackage, file.getName()); - fillMapWithLibraryClasses(file, newDirPackageName, parentInfo.libraryClassRoot); - } - - if (parentInfo.isInLibrarySource) { - String newDirPackageName = getPackageNameForSubdir(parentPackage, file.getName()); - fillMapWithLibrarySources(file, newDirPackageName, parentInfo.sourceRoot); - } - - if (!parentInfo.getOrderEntries().isEmpty()) { - fillMapWithOrderEntries(file, parentInfo.getOrderEntries(), null, null, null, parentInfo, null); + state.fillMapWithModuleSource(file, module, newDirPackageName, parentInfo.sourceRoot, parentInfo.isTestSource); } } + + if (parentInfo.libraryClassRoot != null) { + String newDirPackageName = getPackageNameForSubdir(parentPackage, file.getName()); + state.fillMapWithLibraryClasses(file, newDirPackageName, parentInfo.libraryClassRoot); + } + + if (parentInfo.isInLibrarySource) { + String newDirPackageName = getPackageNameForSubdir(parentPackage, file.getName()); + state.fillMapWithLibrarySources(file, newDirPackageName, parentInfo.sourceRoot); + } + + if (!parentInfo.getOrderEntries().isEmpty()) { + state.fillMapWithOrderEntries(file, parentInfo.getOrderEntries(), null, null, null, parentInfo, null); + } + myState = state; } public void beforeFileDeletion(VirtualFileEvent event) { - synchronized (DirectoryIndexImpl.this) { - VirtualFile file = event.getFile(); - if (!file.isDirectory()) return; - if (!myDirToInfoMap.containsKey(file)) return; + VirtualFile file = event.getFile(); + if (!file.isDirectory()) return; + if (!myState.myDirToInfoMap.containsKey(file)) return; - ArrayList list = new ArrayList(); - addDirsRecursively(list, file); - file.putUserData(FILES_TO_RELEASE_KEY, list); - } - } + final IndexState state = myState.copy(); - private void addDirsRecursively(ArrayList list, VirtualFile dir) { - synchronized (DirectoryIndexImpl.this) { - if (!myDirToInfoMap.containsKey(dir) || !(dir instanceof NewVirtualFile)) return; - - list.add(dir); - - for (VirtualFile child : ((NewVirtualFile)dir).getCachedChildren()) { - if (child.isDirectory()) { - addDirsRecursively(list, child); - } - } - } + ArrayList list = new ArrayList(); + state.addDirsRecursively(list, file); + file.putUserData(FILES_TO_RELEASE_KEY, list); + myState = state; } public void fileDeleted(VirtualFileEvent event) { @@ -799,14 +368,18 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone List list = file.getUserData(FILES_TO_RELEASE_KEY); if (list == null) return; - synchronized (DirectoryIndexImpl.this) { - for (VirtualFile dir : list) { - DirectoryInfo info = myDirToInfoMap.remove(dir); - if (info != null) { - setPackageName(dir, null); - } + IndexState copy = null; + for (VirtualFile dir : list) { + DirectoryInfo info = myState.myDirToInfoMap.remove(dir); + if (info != null) { + if (copy == null) copy = myState.copy(); + copy.setPackageName(dir, null); } } + + if (copy != null) { + myState = copy; + } } public void fileMoved(VirtualFileMoveEvent event) { @@ -825,4 +398,446 @@ public class DirectoryIndexImpl extends DirectoryIndex implements ProjectCompone } } } + + private class IndexState { + final Map> myExcludeRootsMap = new THashMap>(); + final Set myProjectExcludeRoots = new THashSet(); + final Map myDirToInfoMap = new THashMap(); + final Map> myPackageNameToDirsMap = new THashMap>(); + final Map myDirToPackageName = new THashMap(); + + public IndexState() { + } + + DirectoryInfo getOrCreateDirInfo(VirtualFile dir) { + DirectoryInfo info = myDirToInfoMap.get(dir); + if (info == null) { + info = new DirectoryInfo(); + myDirToInfoMap.put(dir, info); + } + return info; + } + + private void fillMapWithModuleContent(VirtualFile dir, Module module, VirtualFile contentRoot) { + if (isExcluded(contentRoot, dir)) return; + if (isIgnored(dir)) return; + + DirectoryInfo info = getOrCreateDirInfo(dir); + + if (info.module != null) { // module contents overlap + DirectoryInfo parentInfo = myDirToInfoMap.get(dir.getParent()); + if (parentInfo == null || !info.module.equals(parentInfo.module)) return; // content of another module is below this module's content + } + + VirtualFile[] children = dir.getChildren(); + for (VirtualFile child : children) { + if (child.isDirectory()) { + fillMapWithModuleContent(child, module, contentRoot); + } + } + + // important to change module AFTER processing children - to handle overlapping modules + info.module = module; + info.contentRoot = contentRoot; + } + + private boolean isExcluded(VirtualFile root, VirtualFile dir) { + Set excludes = myExcludeRootsMap.get(root); + return excludes != null && excludes.contains(dir.getUrl()); + } + + private void initModuleContents(Module module, boolean reverseAllSets, ProgressIndicator progress) { + progress.checkCanceled(); + progress.setText2(ProjectBundle.message("project.index.processing.module.content.progress", module.getName())); + + ModuleRootManager rootManager = ModuleRootManager.getInstance(module); + VirtualFile[] contentRoots = rootManager.getContentRoots(); + if (reverseAllSets) { + contentRoots = ArrayUtil.reverseArray(contentRoots); + } + + for (final VirtualFile contentRoot : contentRoots) { + fillMapWithModuleContent(contentRoot, module, contentRoot); + } + } + + private void initModuleSources(Module module, boolean reverseAllSets, ProgressIndicator progress) { + progress.checkCanceled(); + progress.setText2(ProjectBundle.message("project.index.processing.module.sources.progress", module.getName())); + + ContentEntry[] contentEntries = getContentEntries(module); + + if (reverseAllSets) { + contentEntries = ArrayUtil.reverseArray(contentEntries); + } + + for (ContentEntry contentEntry : contentEntries) { + SourceFolder[] sourceFolders = contentEntry.getSourceFolders(); + if (reverseAllSets) { + sourceFolders = ArrayUtil.reverseArray(sourceFolders); + } + for (SourceFolder sourceFolder : sourceFolders) { + VirtualFile dir = sourceFolder.getFile(); + if (dir != null) { + fillMapWithModuleSource(dir, module, sourceFolder.getPackagePrefix(), dir, sourceFolder.isTestSource()); + } + } + } + } + + private void fillMapWithModuleSource(VirtualFile dir, Module module, String packageName, VirtualFile sourceRoot, boolean isTestSource) { + DirectoryInfo info = myDirToInfoMap.get(dir); + if (info == null) return; + if (!module.equals(info.module)) return; + + if (info.isInModuleSource) { // module sources overlap + String definedPackage = myDirToPackageName.get(dir); + if (definedPackage != null && definedPackage.length() == 0) return; // another source root starts here + } + + info.isInModuleSource = true; + info.isTestSource = isTestSource; + info.sourceRoot = sourceRoot; + setPackageName(dir, packageName); + + VirtualFile[] children = dir.getChildren(); + for (VirtualFile child : children) { + if (child.isDirectory()) { + String childPackageName = getPackageNameForSubdir(packageName, child.getName()); + fillMapWithModuleSource(child, module, childPackageName, sourceRoot, isTestSource); + } + } + } + + private void initLibrarySources(Module module, ProgressIndicator progress) { + progress.checkCanceled(); + progress.setText2(ProjectBundle.message("project.index.processing.library.sources.progress", module.getName())); + + for (OrderEntry orderEntry : getOrderEntries(module)) { + boolean isLibrary = orderEntry instanceof LibraryOrderEntry || orderEntry instanceof JdkOrderEntry; + if (isLibrary) { + VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); + for (final VirtualFile sourceRoot : sourceRoots) { + fillMapWithLibrarySources(sourceRoot, "", sourceRoot); + } + } + } + } + + private void fillMapWithLibrarySources(VirtualFile dir, String packageName, VirtualFile sourceRoot) { + if (isIgnored(dir)) return; + + DirectoryInfo info = getOrCreateDirInfo(dir); + + if (info.isInLibrarySource) { // library sources overlap + String definedPackage = myDirToPackageName.get(dir); + if (definedPackage != null && definedPackage.length() == 0) return; // another library source root starts here + } + + info.isInModuleSource = false; + info.isInLibrarySource = true; + info.sourceRoot = sourceRoot; + setPackageName(dir, packageName); + + VirtualFile[] children = dir.getChildren(); + for (VirtualFile child : children) { + if (child.isDirectory()) { + String childPackageName = getPackageNameForSubdir(packageName, child.getName()); + fillMapWithLibrarySources(child, childPackageName, sourceRoot); + } + } + } + + private void initLibraryClasses(Module module, ProgressIndicator progress) { + progress.checkCanceled(); + progress.setText2(ProjectBundle.message("project.index.processing.library.classes.progress", module.getName())); + + for (OrderEntry orderEntry : getOrderEntries(module)) { + boolean isLibrary = orderEntry instanceof LibraryOrderEntry || orderEntry instanceof JdkOrderEntry; + if (isLibrary) { + VirtualFile[] classRoots = orderEntry.getFiles(OrderRootType.CLASSES); + for (final VirtualFile classRoot : classRoots) { + fillMapWithLibraryClasses(classRoot, "", classRoot); + } + } + } + } + + private void fillMapWithLibraryClasses(VirtualFile dir, String packageName, VirtualFile classRoot) { + if (isIgnored(dir)) return; + + DirectoryInfo info = getOrCreateDirInfo(dir); + + if (info.libraryClassRoot != null) { // library classes overlap + String definedPackage = myDirToPackageName.get(dir); + if (definedPackage != null && definedPackage.length() == 0) return; // another library root starts here + } + + info.libraryClassRoot = classRoot; + + if (!info.isInModuleSource && !info.isInLibrarySource) { + setPackageName(dir, packageName); + } + + VirtualFile[] children = dir.getChildren(); + for (VirtualFile child : children) { + if (child.isDirectory()) { + String childPackageName = getPackageNameForSubdir(packageName, child.getName()); + fillMapWithLibraryClasses(child, childPackageName, classRoot); + } + } + } + + private void initOrderEntries(Module module) { + Map> depEntries = new HashMap>(); + Map> libClassRootEntries = new HashMap>(); + Map> libSourceRootEntries = new HashMap>(); + + for (OrderEntry orderEntry : getOrderEntries(module)) { + if (orderEntry instanceof ModuleOrderEntry) { + final Module depModule = ((ModuleOrderEntry)orderEntry).getModule(); + if (depModule != null) { + VirtualFile[] importedClassRoots = + OrderEnumerator.orderEntries(depModule).exportedOnly().recursively().classes().usingCache().getRoots(); + for (VirtualFile importedClassRoot : importedClassRoots) { + addEntryToMap(importedClassRoot, orderEntry, depEntries); + } + } + VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); + for (VirtualFile sourceRoot : sourceRoots) { + addEntryToMap(sourceRoot, orderEntry, depEntries); + } + } + else if (orderEntry instanceof ModuleSourceOrderEntry) { + List oneEntryList = Arrays.asList(orderEntry); + Module entryModule = orderEntry.getOwnerModule(); + + VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); + for (VirtualFile sourceRoot : sourceRoots) { + fillMapWithOrderEntries(sourceRoot, oneEntryList, entryModule, null, null, null, null); + } + } + else if (orderEntry instanceof LibraryOrderEntry || orderEntry instanceof JdkOrderEntry) { + VirtualFile[] classRoots = orderEntry.getFiles(OrderRootType.CLASSES); + for (VirtualFile classRoot : classRoots) { + addEntryToMap(classRoot, orderEntry, libClassRootEntries); + } + VirtualFile[] sourceRoots = orderEntry.getFiles(OrderRootType.SOURCES); + for (VirtualFile sourceRoot : sourceRoots) { + addEntryToMap(sourceRoot, orderEntry, libSourceRootEntries); + } + } + } + + for (Map.Entry> mapEntry : depEntries.entrySet()) { + final VirtualFile vRoot = mapEntry.getKey(); + final List entries = mapEntry.getValue(); + fillMapWithOrderEntries(vRoot, entries, null, null, null, null, null); + } + + for (Map.Entry> mapEntry : libClassRootEntries.entrySet()) { + final VirtualFile vRoot = mapEntry.getKey(); + final List entries = mapEntry.getValue(); + fillMapWithOrderEntries(vRoot, entries, null, vRoot, null, null, null); + } + + for (Map.Entry> mapEntry : libSourceRootEntries.entrySet()) { + final VirtualFile vRoot = mapEntry.getKey(); + final List entries = mapEntry.getValue(); + fillMapWithOrderEntries(vRoot, entries, null, null, vRoot, null, null); + } + } + + private void setPackageName(VirtualFile dir, String newPackageName) { + assert dir != null; + + String oldPackageName = myDirToPackageName.get(dir); + if (oldPackageName != null) { + List oldPackageDirs = myPackageNameToDirsMap.get(oldPackageName); + final boolean removed = oldPackageDirs.remove(dir); + assert removed; + + if (oldPackageDirs.size() == 0) { + myPackageNameToDirsMap.remove(oldPackageName); + } + } + + if (newPackageName != null) { + List newPackageDirs = myPackageNameToDirsMap.get(newPackageName); + if (newPackageDirs == null) { + newPackageDirs = new SmartList(); + myPackageNameToDirsMap.put(newPackageName, newPackageDirs); + } + newPackageDirs.add(dir); + + myDirToPackageName.put(dir, newPackageName); + } + else { + myDirToPackageName.remove(dir); + } + } + + private void addEntryToMap(final VirtualFile vRoot, final OrderEntry entry, final Map> map) { + List list = map.get(vRoot); + if (list == null) { + list = new ArrayList(); + map.put(vRoot, list); + } + list.add(entry); + } + + private void fillMapWithOrderEntries(VirtualFile dir, + List orderEntries, + Module module, + VirtualFile libraryClassRoot, + VirtualFile librarySourceRoot, + DirectoryInfo parentInfo, + final List oldParentEntries) { + if (isIgnored(dir)) return; + + DirectoryInfo info = myDirToInfoMap.get(dir); // do not create it here! + if (info == null) return; + + if (module != null) { + if (info.module != module) return; + if (!info.isInModuleSource) return; + } + else if (libraryClassRoot != null) { + if (info.libraryClassRoot != libraryClassRoot) return; + if (info.isInModuleSource) return; + } + else if (librarySourceRoot != null) { + if (!info.isInLibrarySource) return; + if (info.sourceRoot != librarySourceRoot) return; + if (info.libraryClassRoot != null) return; + } + + final List oldEntries = info.getOrderEntries(); + info.addOrderEntries(orderEntries, parentInfo, oldParentEntries); + + final VirtualFile[] children = dir.getChildren(); + for (VirtualFile child : children) { + if (child.isDirectory()) { + fillMapWithOrderEntries(child, orderEntries, module, libraryClassRoot, librarySourceRoot, info, oldEntries); + } + } + } + + private void doInitialize(boolean reverseAllSets/* for testing order independence*/) { + ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); + if (progress == null) progress = new EmptyProgressIndicator(); + + progress.pushState(); + + progress.checkCanceled(); + progress.setText(ProjectBundle.message("project.index.scanning.files.progress")); + + Module[] modules = ModuleManager.getInstance(myProject).getModules(); + if (reverseAllSets) modules = ArrayUtil.reverseArray(modules); + + initExcludedDirMap(modules, progress); + + for (Module module : modules) { + initModuleContents(module, reverseAllSets, progress); + } + // Important! Because module's contents may overlap, + // first modules should be marked and only after that sources markup + // should be added. (src markup depends on module markup) + for (Module module : modules) { + initModuleSources(module, reverseAllSets, progress); + initLibrarySources(module, progress); + initLibraryClasses(module, progress); + } + + progress.checkCanceled(); + progress.setText2(""); + + for (Module module : modules) { + initOrderEntries(module); + } + + progress.popState(); + } + + private void initExcludedDirMap(Module[] modules, ProgressIndicator progress) { + progress.checkCanceled(); + progress.setText2(ProjectBundle.message("project.index.building.exclude.roots.progress")); + + // exclude roots should be merged to prevent including excluded dirs of an inner module into the outer + // exclude root should exclude from its content root and all outer content roots + + for (Module module : modules) { + for (ContentEntry contentEntry : getContentEntries(module)) { + VirtualFile contentRoot = contentEntry.getFile(); + if (contentRoot == null) continue; + + ExcludeFolder[] excludeRoots = contentEntry.getExcludeFolders(); + for (ExcludeFolder excludeRoot : excludeRoots) { + // Output paths should be excluded (if marked as such) regardless if they're under corresponding module's content root + if (excludeRoot.getFile() != null) { + if (!FileUtil.startsWith(contentRoot.getUrl(), excludeRoot.getUrl())) { + if (isExcludeRootForModule(module, excludeRoot.getFile())) { + putForFileAndAllAncestors(myExcludeRootsMap, excludeRoot.getFile(), excludeRoot.getUrl()); + } + } + } + + putForFileAndAllAncestors(myExcludeRootsMap, contentRoot, excludeRoot.getUrl()); + } + } + } + + for (DirectoryIndexExcludePolicy policy : myExcludePolicies) { + for (VirtualFile file : policy.getExcludeRootsForProject()) { + putForFileAndAllAncestors(myExcludeRootsMap, file, file.getUrl()); + myProjectExcludeRoots.add(file); + } + } + } + + private void putForFileAndAllAncestors(Map> map, VirtualFile file, String value) { + while (true) { + Set set = map.get(file); + if (set == null) { + set = new HashSet(); + map.put(file, set); + } + set.add(value); + + file = file.getParent(); + if (file == null) break; + } + } + + private void addDirsRecursively(ArrayList list, VirtualFile dir) { + if (!myDirToInfoMap.containsKey(dir) || !(dir instanceof NewVirtualFile)) return; + + list.add(dir); + + for (VirtualFile child : ((NewVirtualFile)dir).getCachedChildren()) { + if (child.isDirectory()) { + addDirsRecursively(list, child); + } + } + } + + public IndexState copy() { + IndexState copy = new IndexState(); + + for (Map.Entry> entry : myExcludeRootsMap.entrySet()) { + copy.myExcludeRootsMap.put(entry.getKey(), new HashSet(entry.getValue())); + } + + copy.myProjectExcludeRoots.addAll(myProjectExcludeRoots); + copy.myDirToInfoMap.putAll(myDirToInfoMap); + + for (Map.Entry> entry : myPackageNameToDirsMap.entrySet()) { + copy.myPackageNameToDirsMap.put(entry.getKey(), new SmartList(entry.getValue())); + } + + copy.myDirToPackageName.putAll(myDirToPackageName); + + return copy; + } + } } From 85b4c00117649e9f6a6332c9cd3d57573cf3c5b9 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 11 Nov 2010 10:41:14 +0300 Subject: [PATCH 42/55] fix java.lang.IndexOutOfBoundsException: Wrong line; --- .../intellij/debugger/actions/SmartStepIntoActionHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/SmartStepIntoActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/SmartStepIntoActionHandler.java index d656c0712124..3786d50a9f8c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/SmartStepIntoActionHandler.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/SmartStepIntoActionHandler.java @@ -97,7 +97,9 @@ public class SmartStepIntoActionHandler extends DebuggerActionHandler { } final Document doc = FileDocumentManager.getInstance().getDocument(vFile); - + if (line >= doc.getLineCount()) { + return Collections.emptyList(); // the document has been changed + } final int startOffset = doc.getLineStartOffset(line); final TextRange lineRange = new TextRange(startOffset, doc.getLineEndOffset(line)); final int offset = CharArrayUtil.shiftForward(doc.getCharsSequence(), startOffset, " \t"); From f1c027f3790fbd75875274be55173c9882e47114 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 10 Nov 2010 13:59:06 +0300 Subject: [PATCH 43/55] IDEA-57556 (blame C-style brackets after vararg parameter) --- .../lang/java/parser/DeclarationParser.java | 48 ++++++++++-------- .../lang/java/parser/ExpressionParser.java | 8 ++- .../lang/java/parser/ReferenceParser.java | 49 ++++++++++--------- .../lang/java/parser/StatementParser.java | 2 +- .../psi/impl/source/tree/JavaElementType.java | 4 +- .../declarations/VarArgBrackets.txt | 32 ++++++++++++ .../parser/partial/DeclarationParserTest.java | 1 + .../parser/partial/ReferenceParserTest.java | 23 ++++----- 8 files changed, 101 insertions(+), 66 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 47b60ef7d599..f99d8355c309 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -357,9 +357,9 @@ public class DeclarationParser { @NotNull private static PsiBuilder.Marker parseTypeNotNull(final PsiBuilder builder) { - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseType(builder); - assert typeInfo != null : builder.getOriginalText(); - return typeInfo.marker; + final PsiBuilder.Marker type = ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + assert type != null : builder.getOriginalText(); + return type; } @NotNull @@ -399,14 +399,7 @@ public class DeclarationParser { final boolean anno, final boolean constructor) { parseParameterList(builder); - if (constructor && builder.getTokenType() == JavaTokenType.LBRACKET) { - final PsiBuilder.Marker marker = builder.mark(); - eatBrackets(builder, false); - marker.error(JavaErrorMessages.message("expected.semicolon")); - } - else { - eatBrackets(builder, true); - } + eatBrackets(builder, constructor, JavaErrorMessages.message("expected.semicolon")); if (areTypeAnnotationsSupported(builder)) { final PsiBuilder.Marker receiver = builder.mark(); @@ -544,21 +537,23 @@ public class DeclarationParser { final PsiBuilder.Marker param = builder.mark(); final Pair modListInfo = parseModifierList(builder); - final PsiBuilder.Marker type = ellipsis ? ReferenceParser.parseTypeWithEllipsis(builder, true, true) : - ReferenceParser.parseType(builder, true, true, false); - if (type == null && modListInfo.second) { + int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD; + if (ellipsis) flags |= ReferenceParser.ELLIPSIS; + final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, flags); + + if (typeInfo == null && modListInfo.second) { param.rollbackTo(); return null; } - if (type == null) { + if (typeInfo == null) { error(builder, JavaErrorMessages.message("expected.type")); emptyElement(builder, JavaElementType.TYPE); } if (expect(builder, JavaTokenType.IDENTIFIER)) { - eatBrackets(builder, true); + eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen")); done(param, JavaElementType.PARAMETER); return param; } @@ -593,7 +588,7 @@ public class DeclarationParser { while (true) { shouldRollback = true; - if (!eatBrackets(builder, true)) { + if (!eatBrackets(builder, false, null)) { unclosed = true; } @@ -655,14 +650,25 @@ public class DeclarationParser { return declaration; } - private static boolean eatBrackets(final PsiBuilder builder, final boolean withError) { + private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, @Nullable final String error) { + if (builder.getTokenType() != JavaTokenType.LBRACKET) return true; + + final PsiBuilder.Marker marker = isError ? builder.mark() : null; + + boolean result = true; while (expect(builder, JavaTokenType.LBRACKET)) { if (!expect(builder, JavaTokenType.RBRACKET)) { - if (withError) error(builder, JavaErrorMessages.message("expected.rbracket")); - return false; + if (!isError) error(builder, JavaErrorMessages.message("expected.rbracket")); + result = false; + break; } } - return true; + + if (marker != null) { + marker.error(error); + } + + return result; } @Nullable diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index a7d00b29c069..c04f0e698962 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -161,8 +161,7 @@ public class ExpressionParser { return parseUnary(builder); case TYPE: - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseType(builder); - return typeInfo != null ? typeInfo.marker : null; + return ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); default: assert false : "Unexpected type: " + type; @@ -255,7 +254,7 @@ public class ExpressionParser { final PsiBuilder.Marker typeCast = builder.mark(); builder.advanceLexer(); - final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseType(builder); + final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); if (typeInfo == null || builder.getTokenType() != JavaTokenType.RPARENTH) { typeCast.rollbackTo(); @@ -733,8 +732,7 @@ public class ExpressionParser { private static PsiBuilder.Marker parseClassObjectAccess(final PsiBuilder builder) { final PsiBuilder.Marker expr = builder.mark(); - final PsiBuilder.Marker type = ReferenceParser.parseType(builder, false, false, false); - if (type == null) { + if (ReferenceParser.parseType(builder, 0) == null) { expr.drop(); return null; } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index baf996675c0e..fb87ee3275ef 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -32,10 +32,16 @@ import static com.intellij.lang.java.parser.JavaParserUtil.emptyElement; public class ReferenceParser { + public static final int EAT_LAST_DOT = 0x01; + public static final int ELLIPSIS = 0x02; + public static final int WILDCARD = 0x04; + public static final int DIAMONDS = 0x08; + public static class TypeInfo { public boolean isPrimitive = false; public boolean isParameterized = false; public boolean isArray = false; + public boolean isVarArg = false; public boolean hasErrors = false; public PsiBuilder.Marker marker = null; } @@ -45,33 +51,19 @@ public class ReferenceParser { private ReferenceParser() { } @Nullable - public static TypeInfo parseType(final PsiBuilder builder) { - return parseTypeWithInfo(builder, true, true, false); - } - - @Nullable - public static PsiBuilder.Marker parseType(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard, final boolean diamonds) { - final TypeInfo typeInfo = parseTypeWithInfo(builder, eatLastDot, wildcard, diamonds); + public static PsiBuilder.Marker parseType(final PsiBuilder builder, final int flags) { + final TypeInfo typeInfo = parseTypeInfo(builder, flags); return typeInfo != null ? typeInfo.marker : null; } @Nullable - public static PsiBuilder.Marker parseTypeWithEllipsis(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard) { - final TypeInfo typeInfo = parseTypeWithInfo(builder, eatLastDot, wildcard, false); - if (typeInfo == null) return null; - - PsiBuilder.Marker type = typeInfo.marker; - if (builder.getTokenType() == JavaTokenType.ELLIPSIS) { - type = typeInfo.marker.precede(); - builder.advanceLexer(); - type.done(JavaElementType.TYPE); - } - - return type; + public static TypeInfo parseTypeInfo(final PsiBuilder builder, final int flags) { + return parseTypeInfo(builder, isSet(flags, EAT_LAST_DOT), isSet(flags, WILDCARD), isSet(flags, DIAMONDS), isSet(flags, ELLIPSIS)); } @Nullable - private static TypeInfo parseTypeWithInfo(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard, final boolean diamonds) { + private static TypeInfo parseTypeInfo(final PsiBuilder builder, + final boolean eatLastDot, final boolean wildcard, final boolean diamonds, final boolean ellipsis) { if (builder.getTokenType() == null) return null; final TypeInfo typeInfo = new TypeInfo(); @@ -126,18 +118,28 @@ public class ReferenceParser { type = type.precede(); } + if (ellipsis && builder.getTokenType() == JavaTokenType.ELLIPSIS) { + type = type.precede(); + builder.advanceLexer(); + type.done(JavaElementType.TYPE); + typeInfo.isVarArg = true; + } + typeInfo.marker = type; return typeInfo; } + private static boolean isSet(final int mask, final int flag) { + return (mask & flag) != 0; + } + @NotNull private static PsiBuilder.Marker parseWildcardType(final PsiBuilder builder) { final PsiBuilder.Marker type = builder.mark(); builder.advanceLexer(); if (expect(builder, WILDCARD_KEYWORD_SET)) { - final PsiBuilder.Marker boundType = parseType(builder, true, false, false); - if (boundType == null) { + if (parseTypeInfo(builder, EAT_LAST_DOT) == null) { error(builder, JavaErrorMessages.message("expected.type")); } } @@ -248,8 +250,7 @@ public class ReferenceParser { boolean isOk = true; while (true) { - final PsiBuilder.Marker type = parseType(builder, true, wildcard, diamonds); - if (type == null) { + if (parseTypeInfo(builder, true, wildcard, diamonds, false) == null) { error(builder, JavaErrorMessages.message("expected.identifier")); } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index 6ccee7a13939..561e249289cb 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -232,7 +232,7 @@ public class StatementParser { final PsiBuilder.Marker declStatement = builder.mark(); final PsiBuilder.Marker decl = DeclarationParser.parse(builder, DeclarationParser.Context.CODE_BLOCK); if (decl == null) { - ReferenceParser.parseType(builder, false, false, false); + ReferenceParser.parseType(builder, 0); error(builder, JavaErrorMessages.message("expected.identifier")); } done(declStatement, JavaElementType.DECLARATION_STATEMENT); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 4ce85e1d9090..d8d36729a3eb 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -253,8 +253,8 @@ public interface JavaElementType { return JavaParserUtil.parseFragment(chameleon, new JavaParserUtil.ParserWrapper() { public void parse(final PsiBuilder builder) { - ReferenceParser.parseType(builder); - expect(builder, JavaTokenType.ELLIPSIS); + ReferenceParser.parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); + expect(builder, JavaTokenType.ELLIPSIS); // todo[r.sh] parse ellipsis and fix PsiTypeCodeFragmentImpl.getType() } }); } diff --git a/java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt b/java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt new file mode 100644 index 000000000000..da117679ea18 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/VarArgBrackets.txt @@ -0,0 +1,32 @@ +PsiJavaFile:VarArgBrackets.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:foo + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:foo('foo') + PsiParameterList:(int... x[]) + PsiJavaToken:LPARENTH('(') + PsiParameter:x + PsiModifierList: + + PsiTypeElement:int... + PsiTypeElement:int + PsiKeyword:int('int') + PsiJavaToken:ELLIPSIS('...') + PsiWhiteSpace(' ') + PsiIdentifier:x('x') + PsiErrorElement:')' expected + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 0638d948b230..be3eb7d94aa5 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -90,6 +90,7 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testUnclosed4() { doParserTest("{ void f(int a,, }", false, false); } public void testUnclosed5() { doParserTest("{ void f(int a,); }", false, false); } public void testConstructorBrackets() { doParserTest("{ A() [] { } }", false, false); } + public void testVarArgBrackets() { doParserTest("{ void foo(int... x[]); }", false, false); } public void testGenericMethod() { doParserTest("{ public static test();\n" + " void test1();\n" + diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java index 6cd12d1baad5..805970e0b8bd 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java @@ -30,18 +30,18 @@ public class ReferenceParserTest extends JavaParsingTestCase { public void testReference1() { doRefParserTest("a.", true); } public void testReference2() { doRefParserTest("a.b", false); } - public void testType0() { doTypeParserTest("int", false); } - public void testType1() { doTypeParserTest("a.b", false); } - public void testType2() { doTypeParserTest("int[]", false); } - public void testType3() { doTypeParserTest("int[][", false); } - public void testType4() { doTypeParserTest("Map>", false); } - public void testType5() { doTypeParserTest("Object[]...", false); } + public void testType0() { doTypeParserTest("int"); } + public void testType1() { doTypeParserTest("a.b"); } + public void testType2() { doTypeParserTest("int[]"); } + public void testType3() { doTypeParserTest("int[]["); } + public void testType4() { doTypeParserTest("Map>"); } + public void testType5() { doTypeParserTest("Object[]..."); } public void testType6() { withLevel(LanguageLevel.JDK_1_7, new Runnable() { @Override - public void run() { doTypeParserTest("@English String @NonEmpty []", false); } }); + public void run() { doTypeParserTest("@English String @NonEmpty []"); } }); } - public void testType7() { doTypeParserTest("Diamond<>", true); } + public void testType7() { doTypeParserTest("Diamond<>"); } public void testTypeParams0() { doTypeParamsParserTest(""); } public void testTypeParams1() { doTypeParamsParserTest(""); } @@ -61,14 +61,11 @@ public class ReferenceParserTest extends JavaParsingTestCase { }); } - private void doTypeParserTest(final String text, final boolean diamonds) { + private void doTypeParserTest(final String text) { doParserTest(text, new TestParser() { @Override public void parse(final PsiBuilder builder) { - if (diamonds) - ReferenceParser.parseType(builder, false, false, true); - else - ReferenceParser.parseTypeWithEllipsis(builder, false, false); + ReferenceParser.parseType(builder, ReferenceParser.DIAMONDS | ReferenceParser.ELLIPSIS); } }); } From f8759fed9eb2b53a23ab79a8cc8e3c0c81910ec9 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 10 Nov 2010 15:42:33 +0300 Subject: [PATCH 44/55] EA-22762 (attempt to work around impossible CCEs) --- .../com/intellij/psi/util/PsiTreeUtil.java | 63 +++++++++++-------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java b/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java index 078e1b16e29c..4e564a232413 100644 --- a/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java +++ b/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java @@ -230,7 +230,8 @@ public class PsiTreeUtil { } }; - PsiTreeUtil.processElements(element, processor); + processElements(element, processor); + //noinspection unchecked return (T)processor.getFoundElement(); } @@ -238,7 +239,8 @@ public class PsiTreeUtil { public static T getChildOfType(@Nullable PsiElement element, @NotNull Class aClass) { if (element == null) return null; for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { - if (instanceOf(child, aClass)) { + if (aClass.isInstance(child)) { + //noinspection unchecked return (T)child; } } @@ -258,8 +260,9 @@ public class PsiTreeUtil { List result = null; for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { - if (instanceOf(child, aClass)) { + if (aClass.isInstance(child)) { if (result == null) result = new SmartList(); + //noinspection unchecked result.add((T)child); } } @@ -272,29 +275,16 @@ public class PsiTreeUtil { List result = new SmartList(); for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { - if (instanceOf(child, aClass)) { + if (aClass.isInstance(child)) { + //noinspection unchecked result.add((T)child); } } return result; } - - private static boolean instanceOf(final PsiElement child, final Class... classes) { - /* - if (aClass == PsiClass.class) return child instanceof PsiClass; - else if (aClass == PsiMethod.class) return child instanceof PsiMethod; - else if (aClass == PsiField.class) return child instanceof PsiField; - else if (aClass == PsiMember.class) return child instanceof PsiMember; - else if (aClass == PsiDocCommentOwner.class) return child instanceof PsiDocCommentOwner; - else if (aClass == PsiStatement.class) return child instanceof PsiStatement; - else if (aClass == PsiCodeBlock.class) return child instanceof PsiCodeBlock; - else if (aClass == PsiClassInitializer.class) return child instanceof PsiClassInitializer; - else if (aClass == XmlTag.class) return child instanceof XmlTag; - else if (aClass == XmlDocument.class) return child instanceof XmlDocument; - */ - - for (Class each : classes) { + private static boolean instanceOf(final PsiElement child, final Class... classes) { + for (final Class each : classes) { if (each.isInstance(child)) return true; } return false; @@ -313,7 +303,10 @@ public class PsiTreeUtil { if (element == null) return null; for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { for (Class aClass : classes) { - if (instanceOf(child, aClass)) return (T)child; + if (aClass.isInstance(child)) { + //noinspection unchecked + return (T)child; + } } } return null; @@ -323,7 +316,10 @@ public class PsiTreeUtil { public static T getNextSiblingOfType(@Nullable PsiElement sibling, @NotNull Class aClass) { if (sibling == null) return null; for (PsiElement child = sibling.getNextSibling(); child != null; child = child.getNextSibling()) { - if (instanceOf(child, aClass)) return (T)child; + if (aClass.isInstance(child)) { + //noinspection unchecked + return (T)child; + } } return null; } @@ -332,7 +328,10 @@ public class PsiTreeUtil { public static T getPrevSiblingOfType(@Nullable PsiElement sibling, @NotNull Class aClass) { if (sibling == null) return null; for (PsiElement child = sibling.getPrevSibling(); child != null; child = child.getPrevSibling()) { - if (instanceOf(child, aClass)) return (T)child; + if (aClass.isInstance(child)) { + //noinspection unchecked + return (T)child; + } } return null; } @@ -391,11 +390,12 @@ public class PsiTreeUtil { element = element.getContext(); } - while (element != null && !instanceOf(element, aClass)) { + while (element != null && !aClass.isInstance(element)) { if (instanceOf(element, stopAt)) return null; element = element.getContext(); } + //noinspection unchecked return (T)element; } @@ -424,6 +424,7 @@ public class PsiTreeUtil { element = element.getContext(); } + //noinspection unchecked return (T)element; } @@ -435,7 +436,8 @@ public class PsiTreeUtil { } while (element != null) { - if (instanceOf(element, aClass)) { + if (aClass.isInstance(element)) { + //noinspection unchecked return (T)element; } if (element instanceof PsiFile) return null; @@ -455,12 +457,13 @@ public class PsiTreeUtil { element = element.getParent(); } - while (element != null && !instanceOf(element, aClass)) { + while (element != null && !aClass.isInstance(element)) { if (instanceOf(element, stopAt)) return null; if (element instanceof PsiFile) return null; element = element.getParent(); } + //noinspection unchecked return (T)element; } @@ -509,7 +512,10 @@ public class PsiTreeUtil { public static T getNonStrictParentOfType(@NotNull PsiElement element, @NotNull Class... classes) { PsiElement run = element; while (run != null) { - if (instanceOf(run, classes)) return (T)run; + if (instanceOf(run, classes)) { + //noinspection unchecked + return (T)run; + } if (run instanceof PsiFile) break; run = run.getParent(); } @@ -546,6 +552,7 @@ public class PsiTreeUtil { public static boolean processElements(@Nullable PsiElement element, @NotNull PsiElementProcessor processor) { if (element == null) return true; + //noinspection unchecked if (!processor.execute(element)) return false; for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { if (!processElements(child, processor)) return false; @@ -832,11 +839,13 @@ public class PsiTreeUtil { TextRange injectionRange = outerLanguageElement.getTextRange(); List res = Lists.newArrayList(); + assert psi != null : outerLanguageElement; for (PsiElement element = psi.findElementAt(injectionRange.getStartOffset()); element != null && injectionRange.intersectsStrict(element.getTextRange()); element = element.getNextSibling()) { res.add(element); } + return res; } } From ae65079a6c17ee6ce2efd117a2dee5965c935abe Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 10 Nov 2010 18:11:30 +0300 Subject: [PATCH 45/55] Fix inspection profile combo for GTK+ L&F --- .../ui/InspectionToolsConfigurable.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java index 30e44085259a..e4f212c4ab54 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/InspectionToolsConfigurable.java @@ -27,6 +27,7 @@ import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; import com.intellij.codeInspection.ModifiableModel; import com.intellij.codeInspection.ex.InspectionProfileImpl; import com.intellij.codeInspection.ex.InspectionToolRegistrar; +import com.intellij.ide.ui.ListCellRendererWrapper; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.CodeInsightColors; @@ -259,19 +260,13 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable imple } public JComponent createComponent() { - myProfiles.setRenderer(new DefaultListCellRenderer(){ + myProfiles.setRenderer(new ListCellRendererWrapper(myProfiles.getRenderer()) { @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - final String profileName = ((Profile)value).getName(); + public void customize(final JList list, final Profile value, final int index, final boolean selected, final boolean hasFocus) { + final String profileName = value.getName(); setText(profileName); final SingleInspectionProfilePanel panel = myPanels.get(profileName); - if (panel != null && panel.isProfileShared()) { - setIcon(Profile.PROJECT_PROFILE); - } else { - setIcon(Profile.LOCAL_PROFILE); - } - return rendererComponent; + setIcon(panel != null && panel.isProfileShared() ? Profile.PROJECT_PROFILE : Profile.LOCAL_PROFILE); } }); myProfiles.addActionListener(new ActionListener() { From af6ee7fb5b821a583e64275341d316e8ade483d8 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 10 Nov 2010 18:12:03 +0300 Subject: [PATCH 46/55] Fix PSI viewer dialects combo for GTK+ L&F --- .../com/intellij/internal/psiView/PsiViewerDialog.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/internal/psiView/PsiViewerDialog.java b/platform/lang-impl/src/com/intellij/internal/psiView/PsiViewerDialog.java index ab79e8a40154..e0d4cf02f03e 100644 --- a/platform/lang-impl/src/com/intellij/internal/psiView/PsiViewerDialog.java +++ b/platform/lang-impl/src/com/intellij/internal/psiView/PsiViewerDialog.java @@ -15,6 +15,7 @@ */ package com.intellij.internal.psiView; +import com.intellij.ide.ui.ListCellRendererWrapper; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.lang.LanguageUtil; @@ -357,13 +358,10 @@ public class PsiViewerDialog extends DialogWrapper implements DataProvider { updateDialectsCombo(); updateExtentionsCombo(); - myDialectsComboBox.setRenderer(new DefaultListCellRenderer() { + myDialectsComboBox.setRenderer(new ListCellRendererWrapper(myDialectsComboBox.getRenderer()) { @Override - public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - final Component result = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value == null) setText(""); - return result; + public void customize(final JList list, final Language value, final int index, final boolean selected, final boolean hasFocus) { + setText(value != null ? value.getDisplayName() : ""); } }); From cc0d2384d8b72354c79107e6d68dec344deb2037 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 10 Nov 2010 18:24:18 +0300 Subject: [PATCH 47/55] Fix language level combo for GTK+ L&F --- .../ui/configuration/LanguageLevelCombo.java | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.java index 9ee7e7ec2029..b854fbad9839 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.roots.ui.configuration; +import com.intellij.ide.ui.ListCellRendererWrapper; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.LanguageLevelProjectExtension; @@ -22,7 +23,6 @@ import com.intellij.openapi.ui.ComboBox; import com.intellij.pom.java.LanguageLevel; import javax.swing.*; -import java.awt.*; /** * @author ven @@ -34,30 +34,27 @@ public class LanguageLevelCombo extends ComboBox { for (LanguageLevel level : LanguageLevel.values()) { addItem(level); } - setRenderer(new MyDefaultListCellRenderer()); + setRenderer(new ListCellRendererWrapper(getRenderer()) { + @Override + public void customize(final JList list, final Object value, final int index, final boolean selected, final boolean hasFocus) { + if (value instanceof LanguageLevel) { + setText(((LanguageLevel)value).getPresentableText()); + } + else if (value instanceof String) { + setText((String)value); + } + } + }); } - public void reset(Project project){ + public void reset(Project project) { setSelectedItem(LanguageLevelProjectExtension.getInstance(project).getLanguageLevel()); } public void setSelectedItem(Object anObject) { - if (anObject == null){ + if (anObject == null) { anObject = USE_PROJECT_LANGUAGE_LEVEL; } super.setSelectedItem(anObject); } - - private static class MyDefaultListCellRenderer extends DefaultListCellRenderer { - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof LanguageLevel) { - setText(((LanguageLevel)value).getPresentableText()); - } - else if (value instanceof String) { - setText((String)value); - } - return this; - } - } } From 514cdd1fd45d86b3af3458a0cd5e7e9fea80eacb Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 10 Nov 2010 18:59:19 +0300 Subject: [PATCH 48/55] Cleanup --- .../openapi/roots/ui/configuration/ProjectJdkConfigurable.java | 1 - .../src/com/intellij/openapi/ui/ComboBoxWithWidePopup.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdkConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdkConfigurable.java index f03f13b0b953..376c88ca09b8 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdkConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdkConfigurable.java @@ -108,7 +108,6 @@ public class ProjectJdkConfigurable implements UnnamedConfigurable { myCbProjectJdk.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (myFreeze) return; - final Sdk oldJdk = myJdksModel.getProjectSdk(); myJdksModel.setProjectSdk(myCbProjectJdk.getSelectedJdk()); clearCaches(); } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/ComboBoxWithWidePopup.java b/platform/platform-api/src/com/intellij/openapi/ui/ComboBoxWithWidePopup.java index 9af7d9f2fc05..9f211b15429e 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ComboBoxWithWidePopup.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ComboBoxWithWidePopup.java @@ -33,7 +33,7 @@ public class ComboBoxWithWidePopup extends JComboBox { if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) setMaximumRowCount(25); } - public ComboBoxWithWidePopup(final Object items[]) { + public ComboBoxWithWidePopup(final Object[] items) { super(items); if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) setMaximumRowCount(25); From bf85c140b24443cfbdfa9563730d6ef952f7bab1 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Thu, 11 Nov 2010 12:00:15 +0300 Subject: [PATCH 49/55] Maven: reinit embedder on settings changes, e.g. offline mode settings (IDEA-17589) --- .../lib/maven-embedder-1.1-SNAPSHOT.jar | Bin 23051 -> 23104 bytes .../idea/maven/facade/MavenFacadeManager.java | 2 +- .../project/MavenGeneralConfigurable.java | 4 + .../maven/project/MavenGeneralSettings.java | 89 +++++++++++------- .../project/MavenProjectsManagerWatcher.java | 4 +- .../jetbrains/idea/maven/MavenTestCase.java | 2 +- .../indices/MavenIndicesTestFixture.java | 3 +- .../idea/maven/project/MavenSettingsTest.java | 2 +- 8 files changed, 65 insertions(+), 41 deletions(-) diff --git a/plugins/maven/facade-impl/lib/maven-embedder-1.1-SNAPSHOT.jar b/plugins/maven/facade-impl/lib/maven-embedder-1.1-SNAPSHOT.jar index 9a16b6b86b2ae8dd02c89210def9436d0ee2f058..c08b985459fba1072173ccd85a137e1d064cc709 100644 GIT binary patch delta 11777 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 #i#h;yTTIlryAl|YR5 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 Ut^njr1r>6mi~S;B^`FH54^a0=j{pDw delta 11829 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

Fv&Et8VyYU%byY_3I8kGM1n=ZtLY>VxEgv zl_X9qUg$44a7wYcl4GpBBYK|oYdp9(# z=cTwHKn_pDDfs0<@txtK3*@Fzo~0TwMFmEnnNv6K_GY0~uL20P1sPUp>#bM$_3lyLZrNX}#J^8Z9_yiJ`~CsGI^Caw_lk_z zs%{mPtd_Bm;Dh=6v#S!~m8HQBU{ej0-VdXn^1)Rm8bqnwE8W?r=3O_Kw~w%fnsLv|oav1Du@;nnzVWJk6*bfuh5bjwnG@=nPD*G zX>Xw_3U0SO%nh~)T2knJAJP_ApiR=6TA#B6H;nHlcBjtFFh0Dukhn4Q?=f%lVpY`se#lT{*nW>X|9}bmQ>^FnwVY)?2%)eKe<%2a zk_Z=xgi#8u8Uwao|2~Yn=);0qw=oJt-S;qzMc$YITU_WJLw0;1`LhWv+9!u03YRKA z6tAd(9Xs}1dJ!mNOsXAEcBD<5FFS&22p^&;hb|weA*f7+BE0L8qT(rpf0G~@&{nGz z?f~uYDp#-4Alxp{$KoLoL8oLZGwL71$Ifl^qowuX11iz>sk;;k=L@d29$%jbE0XM9 zr%Oc~SK)9OK7=ca1<?N`k*wGAkWs9bs!kfQ+TVB`nA85RVzEP?~Y~){g zvI$_a%NgzVTI+r@7yRBkx3yOJ$m_}}j9x@Q27L!|u9~Bej@h}5!X&tShyHJj?;lR} zj28*|FQ@t>P!IKYP$kR=`FF4^()?daq8JR^UnO0p;lC7l`NaS7tSbmZLjU1n!Nf|8 zf3I>%BL7^QVw7lM{t8WMT>nkXg0IzdApd-CQD=wT0{f{8LLozfLo{%~lj@96!v74( z!1x*{hJQd>VNuJM^p-!+Ey*9g5dANH`(LNx5TAdjLq{`rcNa4cTcDkjrInAW0wfeR z^gom?0>qzL2_GODV28m#K#U_pK=AylSRWj$Aw~X=CxdQOFZECO@E;zR?Oz^t_`jWe zgah%dKn9y>G5=wDvC01j9{DdLJKA64^#3;gUqI5~|80W?)2GZ2 zzF?C-J&3|WK=A&n2Tib?4l@Hqu7 { } public MavenEmbedderWrapper createEmbedder(final Project project) { - final MavenFacadeSettings settings = convertSettings(MavenProjectsManager.getInstance(project).getGeneralSettings()); return new MavenEmbedderWrapper(this) { @NotNull @Override protected MavenFacadeEmbedder create() throws RemoteException { + final MavenFacadeSettings settings = convertSettings(MavenProjectsManager.getInstance(project).getGeneralSettings()); return MavenFacadeManager.this.getOrCreateWrappee().createEmbedder(settings); } }; diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralConfigurable.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralConfigurable.java index a2857989698e..366a5d6a7442 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralConfigurable.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralConfigurable.java @@ -133,6 +133,8 @@ public abstract class MavenGeneralConfigurable implements SearchableConfigurable } private void setData(MavenGeneralSettings data) { + data.beginUpdate(); + data.setWorkOffline(checkboxWorkOffline.isSelected()); mavenPathsForm.setData(data); @@ -145,6 +147,8 @@ public abstract class MavenGeneralConfigurable implements SearchableConfigurable data.setFailureBehavior((MavenExecutionOptions.FailureMode)ComboBoxUtil.getSelectedValue(failPolicyComboModel)); data.setPluginUpdatePolicy((MavenExecutionOptions.PluginUpdatePolicy)ComboBoxUtil.getSelectedValue(pluginUpdatePolicyComboModel)); data.setSnapshotUpdatePolicy((MavenExecutionOptions.SnapshotUpdatePolicy)ComboBoxUtil.getSelectedValue(snapshotUpdatePolicyComboModel)); + + data.endUpdate(); } private void getData(MavenGeneralSettings data) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralSettings.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralSettings.java index 8294bbeec711..80e427bd65ff 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralSettings.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenGeneralSettings.java @@ -32,35 +32,54 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -@SuppressWarnings({"UnusedDeclaration"}) public class MavenGeneralSettings implements Cloneable { private boolean workOffline = false; private String mavenHome = ""; private String mavenSettingsFile = ""; - private String overridenLocalRepository = ""; + private String overriddenLocalRepository = ""; private boolean printErrorStackTraces = false; private boolean usePluginRegistry = false; private boolean nonRecursive = false; + private MavenExecutionOptions.LoggingLevel outputLevel = MavenExecutionOptions.LoggingLevel.INFO; private MavenExecutionOptions.ChecksumPolicy checksumPolicy = MavenExecutionOptions.ChecksumPolicy.FAIL; private MavenExecutionOptions.FailureMode failureBehavior = MavenExecutionOptions.FailureMode.FAST; private MavenExecutionOptions.SnapshotUpdatePolicy snapshotUpdatePolicy = MavenExecutionOptions.SnapshotUpdatePolicy.ALWAYS_UPDATE; private MavenExecutionOptions.PluginUpdatePolicy pluginUpdatePolicy = MavenExecutionOptions.PluginUpdatePolicy.DO_NOT_UPDATE; - private volatile File myEffectiveLocalRepositoryCache; - private volatile Set myDefaultPluginsCache; + private File myEffectiveLocalRepositoryCache; + private Set myDefaultPluginsCache; + private int myBulkUpdateLevel = 0; private List myListeners = ContainerUtil.createEmptyCOWList(); - public + public void beginUpdate() { + myBulkUpdateLevel++; + } + + public void endUpdate() { + if (--myBulkUpdateLevel == 0) { + changed(); + } + } + + public void changed() { + if (myBulkUpdateLevel > 0) return; + + myEffectiveLocalRepositoryCache = null; + myDefaultPluginsCache = null; + fireChanged(); + } + @NotNull - MavenExecutionOptions.PluginUpdatePolicy getPluginUpdatePolicy() { + public MavenExecutionOptions.PluginUpdatePolicy getPluginUpdatePolicy() { return pluginUpdatePolicy; } public void setPluginUpdatePolicy(MavenExecutionOptions.PluginUpdatePolicy value) { if (value == null) return; // null may come from deserializator this.pluginUpdatePolicy = value; + changed(); } @NotNull @@ -71,6 +90,7 @@ public class MavenGeneralSettings implements Cloneable { public void setChecksumPolicy(MavenExecutionOptions.ChecksumPolicy value) { if (value == null) return; // null may come from deserializator this.checksumPolicy = value; + changed(); } @NotNull @@ -81,6 +101,7 @@ public class MavenGeneralSettings implements Cloneable { public void setFailureBehavior(MavenExecutionOptions.FailureMode value) { if (value == null) return; // null may come from deserializator this.failureBehavior = value; + changed(); } public @@ -92,6 +113,7 @@ public class MavenGeneralSettings implements Cloneable { public void setOutputLevel(MavenExecutionOptions.LoggingLevel value) { if (value == null) return; // null may come from deserializator this.outputLevel = value; + changed(); } public @@ -103,6 +125,7 @@ public class MavenGeneralSettings implements Cloneable { public void setSnapshotUpdatePolicy(MavenExecutionOptions.SnapshotUpdatePolicy value) { if (value == null) return; // null may come from deserializator this.snapshotUpdatePolicy = value; + changed(); } public boolean isWorkOffline() { @@ -111,29 +134,30 @@ public class MavenGeneralSettings implements Cloneable { public void setWorkOffline(boolean workOffline) { this.workOffline = workOffline; + changed(); } @NotNull - public String getOverridenLocalRepository() { - return overridenLocalRepository; + public String getOverriddenLocalRepository() { + return overriddenLocalRepository; } public File getEffectiveLocalRepository() { File result = myEffectiveLocalRepositoryCache; if (result != null) return result; - result = MavenUtil.resolveLocalRepository(overridenLocalRepository, mavenHome, mavenSettingsFile); + result = MavenUtil.resolveLocalRepository(overriddenLocalRepository, mavenHome, mavenSettingsFile); myEffectiveLocalRepositoryCache = result; return result; } - public void setOverridenLocalRepository(final @Nullable String overridenLocalRepository) { - if (overridenLocalRepository != null) { - if (!Comparing.equal(this.overridenLocalRepository, overridenLocalRepository)) { - this.overridenLocalRepository = overridenLocalRepository; + public void setOverriddenLocalRepository(final @Nullable String overridenLocalRepository) { + if (overridenLocalRepository == null) return; - localRepositoryChanged(); - } + if (!Comparing.equal(this.overriddenLocalRepository, overridenLocalRepository)) { + this.overriddenLocalRepository = overridenLocalRepository; + + changed(); } } @@ -147,7 +171,7 @@ public class MavenGeneralSettings implements Cloneable { this.mavenHome = mavenHome; myDefaultPluginsCache = null; - pathsChanged(); + changed(); } } @@ -162,23 +186,14 @@ public class MavenGeneralSettings implements Cloneable { } public void setMavenSettingsFile(@Nullable String mavenSettingsFile) { - if (mavenSettingsFile != null) { - if (!Comparing.equal(this.mavenSettingsFile, mavenSettingsFile)) { - this.mavenSettingsFile = mavenSettingsFile; - pathsChanged(); - } + if (mavenSettingsFile == null) return; + + if (!Comparing.equal(this.mavenSettingsFile, mavenSettingsFile)) { + this.mavenSettingsFile = mavenSettingsFile; + changed(); } } - public void localRepositoryChanged() { - pathsChanged(); - } - - private void pathsChanged() { - myEffectiveLocalRepositoryCache = null; - fidePathsChanged(); - } - @Nullable public File getEffectiveUserSettingsIoFile() { return MavenUtil.resolveUserSettingsFile(getMavenSettingsFile()); @@ -242,6 +257,7 @@ public class MavenGeneralSettings implements Cloneable { public void setPrintErrorStackTraces(boolean value) { printErrorStackTraces = value; + changed(); } public boolean isUsePluginRegistry() { @@ -250,6 +266,7 @@ public class MavenGeneralSettings implements Cloneable { public void setUsePluginRegistry(final boolean usePluginRegistry) { this.usePluginRegistry = usePluginRegistry; + changed(); } public boolean isNonRecursive() { @@ -258,6 +275,7 @@ public class MavenGeneralSettings implements Cloneable { public void setNonRecursive(final boolean nonRecursive) { this.nonRecursive = nonRecursive; + changed(); } public boolean equals(final Object o) { @@ -275,7 +293,7 @@ public class MavenGeneralSettings implements Cloneable { if (workOffline != that.workOffline) return false; if (!checksumPolicy.equals(that.checksumPolicy)) return false; if (!failureBehavior.equals(that.failureBehavior)) return false; - if (!overridenLocalRepository.equals(that.overridenLocalRepository)) return false; + if (!overriddenLocalRepository.equals(that.overriddenLocalRepository)) return false; if (!mavenHome.equals(that.mavenHome)) return false; if (!mavenSettingsFile.equals(that.mavenSettingsFile)) return false; @@ -287,7 +305,7 @@ public class MavenGeneralSettings implements Cloneable { result = (workOffline ? 1 : 0); result = 31 * result + mavenHome.hashCode(); result = 31 * result + mavenSettingsFile.hashCode(); - result = 31 * result + overridenLocalRepository.hashCode(); + result = 31 * result + overriddenLocalRepository.hashCode(); result = 31 * result + (printErrorStackTraces ? 1 : 0); result = 31 * result + (usePluginRegistry ? 1 : 0); result = 31 * result + (nonRecursive ? 1 : 0); @@ -304,6 +322,7 @@ public class MavenGeneralSettings implements Cloneable { try { MavenGeneralSettings result = (MavenGeneralSettings)super.clone(); result.myListeners = ContainerUtil.createEmptyCOWList(); + result.myBulkUpdateLevel = 0; return result; } catch (CloneNotSupportedException e) { @@ -319,13 +338,13 @@ public class MavenGeneralSettings implements Cloneable { myListeners.remove(l); } - private void fidePathsChanged() { + private void fireChanged() { for (Listener each : myListeners) { - each.pathsChanged(); + each.changed(); } } public interface Listener { - void pathsChanged(); + void changed(); } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java index 416b72e9da9f..71b8d64b0726 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java @@ -142,7 +142,7 @@ public class MavenProjectsManagerWatcher { EditorFactory.getInstance().getEventMulticaster().addDocumentListener(myDocumentListener, myBusConnection); final MavenGeneralSettings.Listener mySettingsPathsChangesListener = new MavenGeneralSettings.Listener() { - public void pathsChanged() { + public void changed() { updateSettingsFilePointers(); onSettingsChange(); } @@ -256,7 +256,7 @@ public class MavenProjectsManagerWatcher { } private void onSettingsXmlChange() { - myGeneralSettings.localRepositoryChanged(); + myGeneralSettings.changed(); // onSettingsChange() will be called indirectly by pathsChanged listener on GeneralSettings object } diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java index a208f6155e52..59284f64d705 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java @@ -251,7 +251,7 @@ public abstract class MavenTestCase extends UsefulTestCase { } protected void setRepositoryPath(String path) { - getMavenGeneralSettings().setOverridenLocalRepository(path); + getMavenGeneralSettings().setOverriddenLocalRepository(path); } protected String getProjectPath() { diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/indices/MavenIndicesTestFixture.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/indices/MavenIndicesTestFixture.java index 32acbc98daa2..6ad7a3facbb8 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/indices/MavenIndicesTestFixture.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/indices/MavenIndicesTestFixture.java @@ -50,7 +50,8 @@ public class MavenIndicesTestFixture { addToRepository(each); } - MavenProjectsManager.getInstance(myProject).getGeneralSettings().setOverridenLocalRepository(myRepositoryHelper.getTestDataPath(myLocalRepoDir)); + MavenProjectsManager.getInstance(myProject).getGeneralSettings().setOverriddenLocalRepository( + myRepositoryHelper.getTestDataPath(myLocalRepoDir)); getIndicesManager().setTestIndexDir(new File(myDir, "MavenIndices")); myIndicesManager = MavenProjectIndicesManager.getInstance(myProject); diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/MavenSettingsTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/MavenSettingsTest.java index 2dda71beb851..165014f8db84 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/MavenSettingsTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/project/MavenSettingsTest.java @@ -26,7 +26,7 @@ public class MavenSettingsTest extends MavenTestCase { MavenGeneralSettings s = new MavenGeneralSettings(); s.addListener(new MavenGeneralSettings.Listener() { @Override - public void pathsChanged() { + public void changed() { log[0] += "changed "; } }); From 15b098514b471913cffe414bed519fa96dce8aca Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 10 Nov 2010 15:13:25 +0300 Subject: [PATCH 50/55] cleanup --- .../openapi/application/ex/ApplicationManagerEx.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java index 0d43ab2172e4..d2adb5695810 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java @@ -44,11 +44,9 @@ public class ApplicationManagerEx extends ApplicationManager { final Application old = ourApplication; Disposer.register(parent, new Disposable() { public void dispose() { - ourApplication = old; - CachedSingletonsRegistry.cleanupCachedFields(); + setApplication(old); } }); - ourApplication = instance; - CachedSingletonsRegistry.cleanupCachedFields(); + setApplication(instance); } } From da2a3d802753cc7873c525f1622e6579173baeaa Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 10 Nov 2010 16:04:28 +0300 Subject: [PATCH 51/55] assertion for IDEA-59154 --- .../hint/EditorFragmentComponent.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java index 353aac65b256..f2d42203256f 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.hint; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; @@ -35,11 +36,13 @@ import java.awt.*; import java.awt.image.BufferedImage; public class EditorFragmentComponent extends JPanel { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.hint.EditorFragmentComponent"); private EditorFragmentComponent(EditorEx editor, int startLine, int endLine, boolean showFolding, boolean showGutter) { Document doc = editor.getDocument(); final int endOffset = endLine < doc.getLineCount() ? doc.getLineEndOffset(endLine) : doc.getTextLength(); int textWidth = Math.min(editor.getMaxWidthInRange(doc.getLineStartOffset(startLine), endOffset), ScreenUtil.getScreenRectangle(1, 1).width); + LOG.assertTrue(textWidth > 0, "TextWidth: "+textWidth+"; startLine:" + startLine + "; endLine:" + endLine + ";"); FoldingModelEx foldingModel = editor.getFoldingModel(); boolean isFoldingEnabled = foldingModel.isFoldingEnabled(); @@ -51,6 +54,8 @@ public class EditorFragmentComponent extends JPanel { Point p2 = editor.logicalPositionToXY(new LogicalPosition(Math.max(endLine, startLine + 1), 0)); int y1 = p1.y; int y2 = p2.y; + int height = y2 - y1; + LOG.assertTrue(height > 0, "Height: " + height + "; startLine:" + startLine + "; endLine:" + endLine + "; p1:" + p1 + "; p2:" + p2); int savedScrollOffset = editor.getScrollingModel().getHorizontalScrollOffset(); if (savedScrollOffset > 0) { @@ -58,20 +63,20 @@ public class EditorFragmentComponent extends JPanel { editor.getScrollingModel().scrollHorizontally(0); } - final Image textImage = new BufferedImage(textWidth, y2 - y1, BufferedImage.TYPE_INT_RGB); + final Image textImage = new BufferedImage(textWidth, height, BufferedImage.TYPE_INT_RGB); Graphics textGraphics = textImage.getGraphics(); final JComponent rowHeader; final Image markersImage; if (showGutter) { rowHeader = editor.getGutterComponentEx(); - markersImage = new BufferedImage(Math.max(1, rowHeader.getWidth()), y2 - y1, BufferedImage.TYPE_INT_RGB); + markersImage = new BufferedImage(Math.max(1, rowHeader.getWidth()), height, BufferedImage.TYPE_INT_RGB); Graphics markerGraphics = markersImage.getGraphics(); markerGraphics.translate(0, -y1); - markerGraphics.setClip(0, y1, rowHeader.getWidth(), y2 - y1); + markerGraphics.setClip(0, y1, rowHeader.getWidth(), height); markerGraphics.setColor(getBackgroundColor(editor)); - markerGraphics.fillRect(0, y1, rowHeader.getWidth(), y2 - y1); + markerGraphics.fillRect(0, y1, rowHeader.getWidth(), height); rowHeader.paint(markerGraphics); } else { @@ -80,7 +85,7 @@ public class EditorFragmentComponent extends JPanel { } textGraphics.translate(0, -y1); - textGraphics.setClip(0, y1, textWidth, y2 - y1); + textGraphics.setClip(0, y1, textWidth, height); final boolean wasVisible = editor.setCaretVisible(false); editor.getContentComponent().paint(textGraphics); if (wasVisible) { @@ -153,7 +158,8 @@ public class EditorFragmentComponent extends JPanel { Point p = new Point(x, y); LightweightHint hint = new MyComponentHint(fragmentComponent); - HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, p, (hideByAnyKey ? HintManagerImpl.HIDE_BY_ANY_KEY : 0) | HintManagerImpl.HIDE_BY_TEXT_CHANGE, 0, false, new HintHint(editor, p)); + HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, p, (hideByAnyKey ? HintManager.HIDE_BY_ANY_KEY : 0) | + HintManager.HIDE_BY_TEXT_CHANGE, 0, false, new HintHint(editor, p)); return hint; } From 95bab486a0dfa53521cec4be70ad2d5a7c96de5a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 10 Nov 2010 16:17:59 +0300 Subject: [PATCH 52/55] cleanup --- .../impl/quickfix/CreateConstructorFromThisOrSuperFix.java | 2 +- .../daemon/impl/quickfix/CreateMethodFromUsageFix.java | 2 +- .../src/com/intellij/codeInsight/CodeInsightUtilBase.java | 2 +- .../com/intellij/codeInsight/daemon/impl/FileStatusMap.java | 6 +++--- .../codeInsight/template/impl/TemplateSegments.java | 2 +- .../com/intellij/injected/editor/DocumentWindowImpl.java | 2 +- .../com/intellij/openapi/editor/impl/CaretModelImpl.java | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorFromThisOrSuperFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorFromThisOrSuperFix.java index 915be6fad966..feef7fce0cda 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorFromThisOrSuperFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorFromThisOrSuperFix.java @@ -98,7 +98,7 @@ public abstract class CreateConstructorFromThisOrSuperFix extends CreateFromUsag targetClass = constructor.getContainingClass(); myMethodCall = CodeInsightUtil.findElementInRange(psiFile, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), myMethodCall.getClass()); - ((RangeMarkerEx)rangeMarker).dispose(); + rangeMarker.dispose(); Template template = templateBuilder.buildTemplate(); final Editor editor = positionCursor(project, targetClass.getContainingFile(), targetClass); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java index efd87ce80e6f..02870ac48ecc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java @@ -229,7 +229,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix { Template template = builder.buildTemplate(); newEditor.getCaretModel().moveToOffset(rangeMarker.getStartOffset()); newEditor.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()); - ((RangeMarkerEx)rangeMarker).dispose(); + rangeMarker.dispose(); if (!shouldBeAbstract) { startTemplate(newEditor, template, project, new TemplateEditingAdapter() { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightUtilBase.java b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightUtilBase.java index f51bc212edd8..2c3e9fa0f14b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightUtilBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightUtilBase.java @@ -85,7 +85,7 @@ public class CodeInsightUtilBase { T elementInRange = findElementInRange(psiFile, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), (Class)element.getClass(), language); - ((RangeMarkerEx)rangeMarker).dispose(); + rangeMarker.dispose(); return elementInRange; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java index 9b50ebd65e0f..d89fb0377d99 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java @@ -171,7 +171,7 @@ public class FileStatusMap implements Disposable { else if (status.dirtyScopes.containsKey(passId)) { RangeMarker marker = status.dirtyScopes.get(passId); if (marker != null) { - ((RangeMarkerEx)marker).dispose(); + marker.dispose(); status.dirtyScopes.put(passId, null); } } @@ -217,7 +217,7 @@ public class FileStatusMap implements Disposable { LOG.assertTrue(status.dirtyScopes.containsKey(passId)); RangeMarker marker = status.dirtyScopes.get(passId); if (marker != null) { - ((RangeMarkerEx)marker).dispose(); + marker.dispose(); } marker = document.createRangeMarker(0, document.getTextLength()); status.dirtyScopes.put(passId, marker); @@ -269,7 +269,7 @@ public class FileStatusMap implements Disposable { if (union.getEndOffset() > textLength) { union = union.intersection(new TextRange(0, textLength)); } - ((RangeMarkerEx)old).dispose(); + old.dispose(); return document.createRangeMarker(union); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java index 48b022d846dd..abb26c704eba 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java @@ -73,7 +73,7 @@ public class TemplateSegments { public void replaceSegmentAt(int index, int start, int end) { RangeMarker rangeMarker = mySegments.get(index); - ((RangeMarkerEx)rangeMarker).dispose(); + rangeMarker.dispose(); Document doc = myEditor.getDocument(); rangeMarker = doc.createRangeMarker(start, end); rangeMarker.setGreedyToLeft(true); diff --git a/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java b/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java index ea2d87e29d38..c4cb198940af 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java @@ -758,7 +758,7 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable public void dispose() { for (PsiLanguageInjectionHost.Shred shred : myShreds) { RangeMarker rangeMarker = shred.getHostRangeMarker(); - ((RangeMarkerEx)rangeMarker).dispose(); + rangeMarker.dispose(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java index 4608f9b53834..72f79aad4f61 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java @@ -87,7 +87,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, private void releaseBulkCaretMarker() { if (savedBeforeBulkCaretMarker != null) { - ((RangeMarkerEx)savedBeforeBulkCaretMarker).dispose(); + savedBeforeBulkCaretMarker.dispose(); savedBeforeBulkCaretMarker = null; } } From 7e37b7c23028387657166bb453b79cea0a5e9832 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 11 Nov 2010 12:31:52 +0300 Subject: [PATCH 53/55] traverseUI test fixed --- .../intellij/openapi/components/ComponentManager.java | 8 ++++++++ .../src/com/intellij/openapi/project/Project.java | 3 --- .../openapi/application/impl/ApplicationImpl.java | 11 +++++++++-- .../application/impl/ModalityInvokatorImpl.java | 6 +++--- .../intellij/openapi/command/impl/DummyProject.java | 1 + .../openapi/components/impl/ComponentManagerImpl.java | 11 +++++++++++ .../intellij/openapi/project/impl/ProjectImpl.java | 11 ----------- .../intellij/openapi/wm/impl/CommandProcessor.java | 4 +++- .../openapi/wm/impl/ToolWindowManagerImpl.java | 2 +- .../src/com/intellij/mock/MockComponentManager.java | 7 +++++++ .../src/com/intellij/mock/MockProject.java | 1 + 11 files changed, 44 insertions(+), 21 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/components/ComponentManager.java b/platform/platform-api/src/com/intellij/openapi/components/ComponentManager.java index 45a0dff95ad6..12c201ac33f0 100644 --- a/platform/platform-api/src/com/intellij/openapi/components/ComponentManager.java +++ b/platform/platform-api/src/com/intellij/openapi/components/ComponentManager.java @@ -17,6 +17,7 @@ package com.intellij.openapi.components; import com.intellij.openapi.Disposable; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.UserDataHolder; import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; @@ -98,4 +99,11 @@ public interface ComponentManager extends UserDataHolder, Disposable { T[] getExtensions(ExtensionPointName extensionPointName); ComponentConfig getConfig(Class componentImplementation); + + /** + * @return condition for this component being disposed. + * see {@link com.intellij.openapi.application.Application#invokeLater(Runnable, Condition)} for the usage example. + */ + @NotNull + Condition getDisposed(); } diff --git a/platform/platform-api/src/com/intellij/openapi/project/Project.java b/platform/platform-api/src/com/intellij/openapi/project/Project.java index 0fd1fea3a314..b05098b9c086 100644 --- a/platform/platform-api/src/com/intellij/openapi/project/Project.java +++ b/platform/platform-api/src/com/intellij/openapi/project/Project.java @@ -17,7 +17,6 @@ package com.intellij.openapi.project; import com.intellij.openapi.components.ComponentManager; import com.intellij.openapi.extensions.AreaInstance; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -61,8 +60,6 @@ public interface Project extends ComponentManager, AreaInstance { void save(); - Condition getDisposed(); - boolean isOpen(); boolean isInitialized(); diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 73d6008c4535..372c3f13a8c9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -87,7 +87,7 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application private final EventDispatcher myDispatcher = EventDispatcher.create(ApplicationListener.class); - private final boolean myTestModeFlag; + private boolean myTestModeFlag; private final boolean myHeadlessMode; private final boolean myCommandLineMode; @@ -357,6 +357,10 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application return myTestModeFlag; } + public void setUnitTestMode(boolean testModeFlag) { + myTestModeFlag = testModeFlag; + } + public boolean isHeadlessEnvironment() { return myHeadlessMode; } @@ -689,7 +693,10 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application if (!canExit()) return; - if (disposeSelf()) System.exit(myExitCode); + boolean success = disposeSelf(); + if (success && !isUnitTestMode()) { + System.exit(myExitCode); + } } }; diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java index 0420f4804eb5..f557edee8075 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java @@ -19,16 +19,16 @@ */ package com.intellij.openapi.application.impl; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityInvokator; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Conditions; import org.jetbrains.annotations.NotNull; public class ModalityInvokatorImpl implements ModalityInvokator { public ActionCallback invokeLater(Runnable runnable) { - return invokeLater(runnable, Conditions.FALSE); + return invokeLater(runnable, ApplicationManager.getApplication().getDisposed()); } public ActionCallback invokeLater(final Runnable runnable, @NotNull final Condition expired) { @@ -40,6 +40,6 @@ public class ModalityInvokatorImpl implements ModalityInvokator { } public ActionCallback invokeLater(Runnable runnable, @NotNull ModalityState state) { - return invokeLater(runnable, state, Conditions.FALSE); + return invokeLater(runnable, state, ApplicationManager.getApplication().getDisposed()); } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DummyProject.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DummyProject.java index 8802c59c55ad..d4c68497096a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DummyProject.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DummyProject.java @@ -124,6 +124,7 @@ public class DummyProject extends UserDataHolderBase implements Project { return false; } + @NotNull public Condition getDisposed() { return new Condition() { public boolean value(final Object o) { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java index 534fa9481095..8186d5401b87 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java @@ -30,6 +30,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.util.ArrayUtil; @@ -75,6 +76,11 @@ public abstract class ComponentManagerImpl extends UserDataHolderBase implements private Boolean myHeadless; private ComponentsRegistry myComponentsRegistry = new ComponentsRegistry(); private boolean myHaveProgressManager = false; + private final Condition myDisposedCondition = new Condition() { + public boolean value(final Object o) { + return isDisposed(); + } + }; protected ComponentManagerImpl(ComponentManager parentComponentManager) { myParentComponentManager = parentComponentManager; @@ -431,6 +437,11 @@ public abstract class ComponentManagerImpl extends UserDataHolderBase implements return myComponentsRegistry.getConfig(componentImplementation); } + @NotNull + public Condition getDisposed() { + return myDisposedCondition; + } + private class ComponentsRegistry { private final Map myInterfaceToLockMap = new HashMap(); private final Map myInterfaceToClassMap = new HashMap(); diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java index 04e882a6a1d9..baf26ab22705 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java @@ -44,7 +44,6 @@ import com.intellij.openapi.project.ex.ProjectEx; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.ex.MessagesEx; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -83,12 +82,6 @@ public class ProjectImpl extends ComponentManagerImpl implements ProjectEx { public boolean myOptimiseTestLoadSpeed; @NonNls public static final String TEMPLATE_PROJECT_NAME = "Default (Template) Project"; - private final Condition myDisposedCondition = new Condition() { - public boolean value(final Object o) { - return isDisposed(); - } - }; - private String myName; private String myOldName; @@ -174,10 +167,6 @@ public class ProjectImpl extends ComponentManagerImpl implements ProjectEx { return ProjectManagerEx.getInstanceEx().isProjectOpened(this); } - public Condition getDisposed() { - return myDisposedCondition; - } - public boolean isInitialized() { return isOpen() && !isDisposed() && StartupManagerEx.getInstanceEx(this).startupActivityPassed(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java index ecb1b78e5ea2..15996cb6c624 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.wm.impl; +import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; @@ -76,7 +77,8 @@ public final class CommandProcessor implements Runnable { // max. I'm not actually quite sure this should have NON_MODAL modality but it should // definitely have some since runnables in command list may (and do) request some PSI activity final boolean queueNext = myCommandCount > 0; - ApplicationManager.getApplication().getInvokator().invokeLater(command, ModalityState.NON_MODAL, expire == null ? Condition.FALSE : expire).doWhenDone(new Runnable() { + Application application = ApplicationManager.getApplication(); + application.getInvokator().invokeLater(command, ModalityState.NON_MODAL, expire == null ? application.getDisposed() : expire).doWhenDone(new Runnable() { public void run() { if (queueNext) { CommandProcessor.this.run(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java index 45a2f7b137da..77e00f6ae0ee 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java @@ -1823,7 +1823,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements @Nullable public Condition getExpireCondition() { - return Condition.FALSE; + return ApplicationManager.getApplication().getDisposed(); } } diff --git a/platform/testFramework/src/com/intellij/mock/MockComponentManager.java b/platform/testFramework/src/com/intellij/mock/MockComponentManager.java index 921772e3baee..4382b8d79bb7 100644 --- a/platform/testFramework/src/com/intellij/mock/MockComponentManager.java +++ b/platform/testFramework/src/com/intellij/mock/MockComponentManager.java @@ -20,6 +20,7 @@ import com.intellij.openapi.components.BaseComponent; import com.intellij.openapi.components.ComponentConfig; import com.intellij.openapi.components.ComponentManager; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.util.ArrayUtil; @@ -149,4 +150,10 @@ public class MockComponentManager extends UserDataHolderBase implements Componen public ComponentConfig getConfig(Class componentImplementation) { throw new UnsupportedOperationException("Method getConfig not implemented in " + getClass()); } + + @NotNull + @Override + public Condition getDisposed() { + return Condition.FALSE; + } } diff --git a/platform/testFramework/src/com/intellij/mock/MockProject.java b/platform/testFramework/src/com/intellij/mock/MockProject.java index 3bc0fb8c32df..51edbd17e8f0 100644 --- a/platform/testFramework/src/com/intellij/mock/MockProject.java +++ b/platform/testFramework/src/com/intellij/mock/MockProject.java @@ -50,6 +50,7 @@ public class MockProject extends MockComponentManager implements ProjectEx { return ServiceManager.getService(this, PomModel.class); } + @NotNull @Override public Condition getDisposed() { return new Condition() { From 51fd1dc4bde89493460d2432004d150abdcc2282 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 11 Nov 2010 12:33:07 +0300 Subject: [PATCH 54/55] IDEA-55575 _ --- .../psi/impl/reference/AntFileReferenceSet.java | 4 +--- .../daemon/impl/analysis/XmlHighlightVisitor.java | 2 +- .../psi/impl/source/xml/XmlAttributeValueImpl.java | 14 +++++++++++++- .../xml/util/XmlDuplicatedIdInspection.java | 3 +-- .../documentation/XmlDocumentationProvider.java | 3 ++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntFileReferenceSet.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntFileReferenceSet.java index 4dab2fe00a4b..eb15b0c665d6 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntFileReferenceSet.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/reference/AntFileReferenceSet.java @@ -19,7 +19,6 @@ import com.intellij.lang.ant.psi.*; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.ManagingFS; @@ -31,7 +30,6 @@ import com.intellij.psi.xml.XmlAttributeValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.util.Collection; import java.util.Collections; @@ -40,7 +38,7 @@ public class AntFileReferenceSet extends FileReferenceSet { private final XmlAttributeValue myValue; public AntFileReferenceSet(final AntStructuredElement element, final XmlAttributeValue value, final PsiReferenceProvider provider) { - super(cutTrailingSlash(FileUtil.toSystemIndependentName(StringUtil.stripQuotesAroundValue(value.getText()))), element, + super(cutTrailingSlash(FileUtil.toSystemIndependentName(value.getValue())), element, value.getTextRange().getStartOffset() - element.getTextRange().getStartOffset() + 1, provider, SystemInfo.isFileSystemCaseSensitive); myValue = value; } diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java index 1aa4e3b06fa6..fd481f50f11a 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java @@ -747,7 +747,7 @@ public class XmlHighlightVisitor extends XmlElementVisitor implements HighlightV } public static String getUnquotedValue(XmlAttributeValue value, XmlTag tag) { - String unquotedValue = StringUtil.stripQuotesAroundValue(value.getText()); + String unquotedValue = value.getValue(); if (tag instanceof HtmlTag) { unquotedValue = unquotedValue.toLowerCase(); diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeValueImpl.java b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeValueImpl.java index e83a20d806c5..eda9db9ca4c3 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeValueImpl.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlAttributeValueImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.impl.source.xml; +import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; @@ -29,6 +30,7 @@ import com.intellij.psi.meta.PsiMetaOwner; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlElementType; +import com.intellij.psi.xml.XmlTokenType; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -58,7 +60,17 @@ public class XmlAttributeValueImpl extends XmlElementImpl implements XmlAttribut } public String getValue() { - return StringUtil.stripQuotesAroundValue(getText()); + // it is more correct way to strip quotes since injected xml may have quotes encoded + String text = getText(); + ASTNode startQuote = findChildByType(XmlTokenType.XML_ATTRIBUTE_VALUE_START_DELIMITER); + if (startQuote != null) { + text = StringUtil.trimStart(text, startQuote.getText()); + } + ASTNode endQuote = findChildByType(XmlTokenType.XML_ATTRIBUTE_VALUE_END_DELIMITER); + if (endQuote != null) { + text = StringUtil.trimEnd(text, endQuote.getText()); + } + return text; } public TextRange getValueTextRange() { diff --git a/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java b/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java index 9360f89702ac..e0a185402210 100644 --- a/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java +++ b/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java @@ -22,7 +22,6 @@ import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.html.HtmlTag; import com.intellij.psi.xml.XmlAttribute; @@ -73,7 +72,7 @@ public class XmlDuplicatedIdInspection extends LocalInspectionTool { if (XmlUtil.isSimpleXmlAttributeValue(idRef, value) && refHolder.isIdReferenceValue(value)) { boolean hasIdDeclaration = refHolder.hasIdDeclaration(idRef); if (!hasIdDeclaration && tag instanceof HtmlTag) { - hasIdDeclaration = refHolder.hasIdDeclaration(StringUtil.stripQuotesAroundValue(value.getText())); + hasIdDeclaration = refHolder.hasIdDeclaration(value.getValue()); } if (!hasIdDeclaration) { diff --git a/xml/impl/src/com/intellij/xml/util/documentation/XmlDocumentationProvider.java b/xml/impl/src/com/intellij/xml/util/documentation/XmlDocumentationProvider.java index 04e2b5c2d312..7446c52e97d4 100644 --- a/xml/impl/src/com/intellij/xml/util/documentation/XmlDocumentationProvider.java +++ b/xml/impl/src/com/intellij/xml/util/documentation/XmlDocumentationProvider.java @@ -113,7 +113,8 @@ public class XmlDocumentationProvider implements DocumentationProvider { String typeName = null; if (originalElement != null && originalElement.getParent() instanceof XmlAttributeValue) { - String toSearch = StringUtil.stripQuotesAroundValue(originalElement.getText()); + XmlAttributeValue value = (XmlAttributeValue)originalElement.getParent(); + String toSearch = value.getValue(); XmlTag enumerationTag; if (XmlUtil.ENUMERATION_TAG_NAME.equals(tag.getLocalName())) { From 7603d117817a914525dc296efe50966b03399a19 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 11 Nov 2010 12:35:01 +0300 Subject: [PATCH 55/55] removed lock --- .../com/intellij/util/CachedValueBase.java | 66 ++++--------------- 1 file changed, 13 insertions(+), 53 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/util/CachedValueBase.java b/platform/platform-impl/src/com/intellij/util/CachedValueBase.java index 80021042ac54..305e21b700d1 100644 --- a/platform/platform-impl/src/com/intellij/util/CachedValueBase.java +++ b/platform/platform-impl/src/com/intellij/util/CachedValueBase.java @@ -24,9 +24,6 @@ import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.Ref; import com.intellij.psi.util.CachedValueProvider; import com.intellij.reference.SoftReference; -import com.intellij.util.concurrency.JBLock; -import com.intellij.util.concurrency.JBReentrantReadWriteLock; -import com.intellij.util.concurrency.LockFactory; import gnu.trove.TLongArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,23 +32,12 @@ import java.lang.ref.Reference; import java.util.ArrayList; import java.util.List; -import static com.intellij.util.ObjectUtils.*; - /** * @author Dmitry Avdeev */ public abstract class CachedValueBase { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.CachedValueImpl"); - private final MyTimedReference myData = new MyTimedReference(); - private final JBLock r; - private final JBLock w; - - public CachedValueBase() { - JBReentrantReadWriteLock rw = LockFactory.createReadWriteLock(); - r = rw.readLock(); - w = rw.writeLock(); - } protected Data computeData(T value, Object[] dependencies) { if (dependencies == null) { @@ -66,7 +52,7 @@ public abstract class CachedValueBase { } protected void setValue(final T value, final CachedValueProvider.Result result) { - myData.setData(computeData(value == null ? (T)NULL : value, getDependencies(result))); + myData.setData(computeData(value == null ? (T)ObjectUtils.NULL : value, getDependencies(result))); myData.setIsLocked(result != null && result.isLockValue()); } @@ -82,7 +68,8 @@ public abstract class CachedValueBase { } else { Object[] items = result.getDependencyItems(); - return result.getValue() == null ? items : items == null ? new Object[] {result.getValue()}: ArrayUtil.append(items, result.getValue()); + T value = result.getValue(); + return value == null ? items : items == null ? new Object[] {value}: ArrayUtil.append(items, value); } } @@ -133,7 +120,7 @@ public abstract class CachedValueBase { protected void collectDependencies(TLongArrayList timeStamps, List resultingDeps, Object[] dependencies) { for (Object dependency : dependencies) { - if (dependency == null || dependency == NULL) continue; + if (dependency == null || dependency == ObjectUtils.NULL) continue; if (dependency instanceof Object[]) { collectDependencies(timeStamps, resultingDeps, (Object[])dependency); } @@ -168,16 +155,9 @@ public abstract class CachedValueBase { } public T setValue(final CachedValueProvider.Result result) { - w.lock(); - - try { - T value = result.getValue(); - setValue(value, result); - return value; - } - finally { - w.unlock(); - } + T value = result == null ? null : result.getValue(); + setValue(value, result); + return value; } public abstract boolean isFromMyProject(Project project); @@ -202,34 +182,15 @@ public abstract class CachedValueBase { @Nullable protected

T getValueWithLock(P param) { - r.lock(); - try { - T value = getUpToDateOrNull(true); - if (value != null) { - return value == NULL ? null : value; - } - } - finally { - r.unlock(); + T value = getUpToDateOrNull(true); + if (value != null) { + return value == ObjectUtils.NULL ? null : value; } // compute outside lock to avoid deadlock CachedValueProvider.Result result = doCompute(param); - T computed = result == null ? null : result.getValue(); - w.lock(); - try { - //T value = getUpToDateOrNull(false); - //if (value != null) { - // return value == NULL ? null : value; - //} - - setValue(computed, result); - return computed; - } - finally { - w.unlock(); - } + return setValue(result); } protected abstract

CachedValueProvider.Result doCompute(P param); @@ -237,16 +198,15 @@ public abstract class CachedValueBase { private static class MyTimedReference extends TimedReference>> { private boolean myIsLocked; - public MyTimedReference() { super(null); } - public void setIsLocked(final boolean isLocked) { + public synchronized void setIsLocked(final boolean isLocked) { myIsLocked = isLocked; } - protected boolean isLocked() { + protected synchronized boolean isLocked() { return super.isLocked() || myIsLocked; }