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"); 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)) { 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()) { 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; - } - } } 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/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); + } } 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-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-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/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-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/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-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) { 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/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 {} +} 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/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/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/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); } }); } 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), 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()]); 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; } } 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/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/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index a4391c524793..878f97c9e7f3 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,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } private void finishCompletionProcess() { + myToRestart = false; cancel(); assert !myDisposed; @@ -474,7 +475,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 +529,11 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } public void cancelByWriteAction() { + if (myToRestart != null) { + LOG.assertTrue(myToRestart == Boolean.FALSE); //explicit completionFinished was invoked before this write action + return; + } + myToRestart = true; cancel(); @@ -605,7 +611,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } public void prefixUpdated() { - if (myToRestart) { + if (myToRestart == Boolean.TRUE) { scheduleRestart(); return; } 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){ 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 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/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; } } 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)) { 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/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); + } } } }; 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/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/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(); 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); } 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; } 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/util/gotoByName/matchers/EntityMatcher.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkTestSourceRootAction.java similarity index 63% rename from platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/EntityMatcher.java rename to platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkTestSourceRootAction.java index 7a6dbffc809b..b308ed2974c5 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/matchers/EntityMatcher.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkTestSourceRootAction.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ide.util.gotoByName.matchers; +package com.intellij.ide.projectView.actions; -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); +/** + * @author yole + */ +public class MarkTestSourceRootAction extends MarkRootAction { + public MarkTestSourceRootAction() { + super(true, false); + } } 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); } 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/LangScriptingContextProvider.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java index 957ea71c02ad..1d72952a1162 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextProvider.java @@ -17,9 +17,11 @@ 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 org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Rustam Vishnyakov @@ -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/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 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 6389740fb013..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.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); - myPattern = 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/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/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() : ""); } }); 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; + } + } } 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() { 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) { 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/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/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-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); 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; } 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); } } 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/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()) { 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; } } 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; } 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/vcs/impl/FileStatusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vcs/impl/FileStatusManagerImpl.java index 68299205909e..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 @@ -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; @@ -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); } 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/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; } 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 @@ + + + + + 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() { 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); } 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); } } 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/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()); } } } 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 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); + } } } } 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/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]; } 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/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(); - } - } -} - - 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; } }); 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/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 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 9a16b6b86b2a..c08b985459fb 100644 Binary files a/plugins/maven/facade-impl/lib/maven-embedder-1.1-SNAPSHOT.jar and b/plugins/maven/facade-impl/lib/maven-embedder-1.1-SNAPSHOT.jar differ diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/facade/MavenFacadeManager.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/facade/MavenFacadeManager.java index b9b30f8fdc4c..f54eae427ffc 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/facade/MavenFacadeManager.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/facade/MavenFacadeManager.java @@ -255,11 +255,11 @@ public class MavenFacadeManager extends RemoteObjectWrapper { } 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 "; } }); 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/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