diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/RequestHint.java b/java/debugger/impl/src/com/intellij/debugger/engine/RequestHint.java index 7b6703e2de64..e4f3ae8e97f4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/RequestHint.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/RequestHint.java @@ -207,16 +207,18 @@ public class RequestHint { if (myDepth == StepRequest.STEP_INTO) { final DebuggerSettings settings = DebuggerSettings.getInstance(); - if (settings.SKIP_SYNTHETIC_METHODS) { - final StackFrameProxyImpl frameProxy = context.getFrameProxy(); - Location location = frameProxy.location(); - Method method = location.method(); + final StackFrameProxyImpl frameProxy = context.getFrameProxy(); + + if (settings.SKIP_SYNTHETIC_METHODS && frameProxy != null) { + final Location location = frameProxy.location(); + final Method method = location.method(); if (method != null) { if (myVirtualMachineProxy.canGetSyntheticAttribute()? method.isSynthetic() : method.name().indexOf('$') >= 0) { return myDepth; } } } + if (!myIgnoreFilters) { if(settings.SKIP_GETTERS) { boolean isGetter = ApplicationManager.getApplication().runReadAction(new Computable(){ @@ -231,18 +233,20 @@ public class RequestHint { } } - if (settings.SKIP_CONSTRUCTORS) { - Location location = context.getFrameProxy().location(); - Method method = location.method(); - if (method != null && method.isConstructor()) { - return StepRequest.STEP_OUT; + if (frameProxy != null) { + if (settings.SKIP_CONSTRUCTORS) { + final Location location = frameProxy.location(); + final Method method = location.method(); + if (method != null && method.isConstructor()) { + return StepRequest.STEP_OUT; + } } - } - if (settings.SKIP_CLASSLOADERS) { - Location location = context.getFrameProxy().location(); - if (DebuggerUtilsEx.isAssignableFrom("java.lang.ClassLoader", location.declaringType())) { - return StepRequest.STEP_OUT; + if (settings.SKIP_CLASSLOADERS) { + final Location location = frameProxy.location(); + if (DebuggerUtilsEx.isAssignableFrom("java.lang.ClassLoader", location.declaringType())) { + return StepRequest.STEP_OUT; + } } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/hint/ShowContainerInfoHandler.java b/java/java-impl/src/com/intellij/codeInsight/hint/ShowContainerInfoHandler.java index 17832a103b2c..18524a29fa93 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hint/ShowContainerInfoHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/hint/ShowContainerInfoHandler.java @@ -102,9 +102,11 @@ public class ShowContainerInfoHandler implements CodeInsightActionHandler { final PsiElement _container = container; ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { - LightweightHint hint = EditorFragmentComponent.showEditorFragmentHint(editor, range, true); - hint.putUserData(CONTAINER_KEY, _container); - editor.putUserData(MY_LAST_HINT_KEY, new WeakReference(hint)); + LightweightHint hint = EditorFragmentComponent.showEditorFragmentHint(editor, range, true, true); + if (hint != null) { + hint.putUserData(CONTAINER_KEY, _container); + editor.putUserData(MY_LAST_HINT_KEY, new WeakReference(hint)); + } } }); } diff --git a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java index 043a18232b97..0382f508d83c 100644 --- a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java +++ b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java @@ -16,8 +16,8 @@ package com.intellij.ide; import com.intellij.application.options.codeStyle.LanguageCodeStyleSettingsProvider; -import com.intellij.openapi.fileTypes.LanguageFileType; -import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.lang.Language; +import com.intellij.lang.StdLanguages; import org.jetbrains.annotations.NotNull; /** @@ -25,8 +25,8 @@ import org.jetbrains.annotations.NotNull; */ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider { @Override - public LanguageFileType getLanguageFileType() { - return StdFileTypes.JAVA; + public Language getLanguage() { + return StdLanguages.JAVA; } @Override diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java index 9eb3954df378..f71306d568fc 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java @@ -26,6 +26,7 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.InheritanceImplUtil; import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiImplUtil; @@ -50,6 +51,7 @@ import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.stubs.PsiFileStub; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -300,12 +302,16 @@ public class PsiClassImpl extends JavaStubPsiElement> implements @NotNull public PsiField[] getFields() { - return getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); + final PsiField[] owns = getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); + final List augments = PsiAugmentProvider.collectAugments(this, PsiField.class); + return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiField.ARRAY_FACTORY); } @NotNull public PsiMethod[] getMethods() { - return getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); + final PsiMethod[] owns = getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); + final List augments = PsiAugmentProvider.collectAugments(this, PsiMethod.class); + return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiMethod.ARRAY_FACTORY); } @NotNull diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java index 5934af0584cf..4cec786ca1ac 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java @@ -93,7 +93,7 @@ public class PsiFieldImpl extends JavaStubPsiElement implements Ps } @NotNull - public final PsiIdentifier getNameIdentifier(){ + public PsiIdentifier getNameIdentifier() { return (PsiIdentifier)getNode().findChildByRoleAsPsiElement(ChildRole.NAME); } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java index 5144c7793908..2ce9002a3470 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java @@ -545,7 +545,8 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme final boolean preserveQualification = CodeStyleSettingsManager.getSettings(getProject()).USE_FQ_CLASS_NAMES && isFullyQualified(); final PsiManager manager = aClass.getManager(); - String text = qName + getParameterList().getText(); + final PsiReferenceParameterList parameterList = getParameterList(); + String text = (parameterList != null ? qName + parameterList.getText() : qName); ASTNode ref = Parsing.parseJavaCodeReferenceText(manager, text, SharedImplUtil.findCharTableByTree(this)); LOG.assertTrue(ref != null, "Failed to parse reference from text '" + text + "'"); getTreeParent().replaceChildInternal(this, (TreeElement)ref); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java index a66bc09749ff..c392f81edfbb 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.daemon.impl.analysis.AnnotationsHighlightUtil; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.CheckUtil; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.cache.ModifierFlags; @@ -29,6 +30,7 @@ import com.intellij.psi.impl.source.tree.Factory; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.tree.IElementType; +import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; @@ -254,7 +256,9 @@ public class PsiModifierListImpl extends JavaStubPsiElement @NotNull public PsiAnnotation[] getAnnotations() { - return getStubOrPsiChildren(JavaStubElementTypes.ANNOTATION, PsiAnnotation.ARRAY_FACTORY); + final PsiAnnotation[] owns = getStubOrPsiChildren(JavaStubElementTypes.ANNOTATION, PsiAnnotation.ARRAY_FACTORY); + final List augments = PsiAugmentProvider.collectAugments(this, PsiAnnotation.class); + return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiAnnotation.ARRAY_FACTORY); } @NotNull diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiReferenceListImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiReferenceListImpl.java index 4fd991128651..e4bebf7d64b2 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiReferenceListImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiReferenceListImpl.java @@ -18,13 +18,17 @@ package com.intellij.psi.impl.source; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.java.stubs.PsiClassReferenceListStub; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; +import java.util.List; + public final class PsiReferenceListImpl extends JavaStubPsiElement implements PsiReferenceList { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiReferenceListImpl"); private static final TokenSet REFERENCE_BIT_SET = TokenSet.create(Constants.JAVA_CODE_REFERENCE); @@ -39,7 +43,10 @@ public final class PsiReferenceListImpl extends JavaStubPsiElement augments = PsiAugmentProvider.collectAugments(this, PsiJavaCodeReferenceElement.class); + return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiJavaCodeReferenceElement.ARRAY_FACTORY); } @NotNull diff --git a/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java b/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java index a65fc59100ff..2c15cf9eacae 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java @@ -469,6 +469,20 @@ public class DeclarationParsing extends Parsing { return first; } + @NotNull + public CompositeElement parseAnnotationParamsFromText(PsiManager manager, CharSequence text, final LanguageLevel languageLevel) { + Lexer originalLexer = new JavaLexer(languageLevel); + FilterLexer lexer = new FilterLexer(originalLexer, new FilterLexer.SetFilter(StdTokenSets.WHITE_SPACE_OR_COMMENT_BIT_SET)); + lexer.start(text); + CompositeElement first = parseAnnotationParameterList(lexer); + + final FileElement dummyRoot = DummyHolderFactory.createHolder(manager, null, myContext.getCharTable()).getTreeElement(); + dummyRoot.rawAddChildren(first); + + ParseUtil.insertMissingTokens(dummyRoot, originalLexer, 0, text.length(), -1, WhiteSpaceAndCommentsProcessor.INSTANCE, myContext); + return first; + } + @NotNull CompositeElement parseAnnotation(Lexer lexer) { CompositeElement annotation = ASTFactory.composite(JavaElementType.ANNOTATION); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java index 2134a2161f72..3b7527d33706 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java @@ -54,7 +54,6 @@ public class PsiAnnotationImpl extends JavaStubPsiElement imp return (PsiJavaCodeReferenceElement)getMirrorTreeElement().findChildByRoleAsPsiElement(ChildRole.CLASS_REFERENCE); } - private CompositeElement getMirrorTreeElement() { final PsiAnnotationStub stub = getStub(); if (stub != null) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java index ef6b927611cd..a24afb5db17b 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiNameValuePairImpl.java @@ -126,8 +126,8 @@ public class PsiNameValuePairImpl extends CompositePsiElement implements PsiName public PsiReference getReference() { return new PsiReference() { private PsiClass getReferencedClass () { - LOG.assertTrue(getTreeParent().getElementType() == ANNOTATION_PARAMETER_LIST && getTreeParent().getTreeParent().getElementType() == ANNOTATION); - PsiAnnotationImpl annotation = (PsiAnnotationImpl)getTreeParent().getTreeParent().getPsi(); + LOG.assertTrue(getParent() instanceof PsiAnnotationParameterList && getParent().getParent() instanceof PsiAnnotation); + PsiAnnotation annotation = (PsiAnnotation)getParent().getParent(); PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement(); return nameRef == null ? null : (PsiClass)nameRef.resolve(); } diff --git a/java/openapi/src/com/intellij/psi/PsiJavaCodeReferenceElement.java b/java/openapi/src/com/intellij/psi/PsiJavaCodeReferenceElement.java index d48a153b7538..695d56b9dec7 100644 --- a/java/openapi/src/com/intellij/psi/PsiJavaCodeReferenceElement.java +++ b/java/openapi/src/com/intellij/psi/PsiJavaCodeReferenceElement.java @@ -15,6 +15,7 @@ */ package com.intellij.psi; +import com.intellij.util.ArrayFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,6 +29,12 @@ public interface PsiJavaCodeReferenceElement extends PsiJavaReference, PsiQualif */ PsiJavaCodeReferenceElement[] EMPTY_ARRAY = new PsiJavaCodeReferenceElement[0]; + ArrayFactory ARRAY_FACTORY = new ArrayFactory() { + public PsiJavaCodeReferenceElement[] create(int count) { + return count == 0 ? EMPTY_ARRAY : new PsiJavaCodeReferenceElement[count]; + } + }; + /** * Returns the element representing the name of the referenced element. * diff --git a/java/openapi/src/com/intellij/psi/augment/PsiAugmentProvider.java b/java/openapi/src/com/intellij/psi/augment/PsiAugmentProvider.java new file mode 100644 index 000000000000..6e0c5d121934 --- /dev/null +++ b/java/openapi/src/com/intellij/psi/augment/PsiAugmentProvider.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.psi.augment; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + + +public abstract class PsiAugmentProvider { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.lang.psiAugmentProvider"); + + private static final PsiAugmentProvider[] PROVIDERS = Extensions.getExtensions(EP_NAME); + + @NotNull + public abstract List getAugments(@NotNull PsiElement element, @NotNull Class type); + + @NotNull + public static List collectAugments(@NotNull final PsiElement element, @NotNull final Class type) { + final List augments = new ArrayList(); + for (PsiAugmentProvider provider : PROVIDERS) { + final List list = provider.getAugments(element, type); + augments.addAll(list); + } + + return augments; + } +} diff --git a/platform/lang-api/src/com/intellij/codeInspection/ProblemsHolder.java b/platform/lang-api/src/com/intellij/codeInspection/ProblemsHolder.java index 4d939237a213..de40323c0f24 100644 --- a/platform/lang-api/src/com/intellij/codeInspection/ProblemsHolder.java +++ b/platform/lang-api/src/com/intellij/codeInspection/ProblemsHolder.java @@ -19,9 +19,13 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.ExternallyDefinedPsiElement; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -58,11 +62,17 @@ public class ProblemsHolder { } public void registerProblem(@NotNull ProblemDescriptor problemDescriptor) { - if (myProblems == null) { - myProblems = new ArrayList(1); - } PsiElement element = problemDescriptor.getPsiElement(); if (element != null && !isInPsiFile(element)) { + ExternallyDefinedPsiElement external = PsiTreeUtil.getParentOfType(element, ExternallyDefinedPsiElement.class, false); + if (external != null) { + PsiElement newTarget = external.getProblemTarget(); + if (newTarget != null) { + redirectProblem(problemDescriptor, newTarget); + return; + } + } + PsiFile containingFile = element.getContainingFile(); PsiElement context = containingFile.getContext(); PsiElement myContext = myFile.getContext(); @@ -71,6 +81,10 @@ public class ProblemsHolder { +"Inspection invoked for file: "+ myFile +"; context: "+(myContext == null ? null : myContext.getContainingFile())+"\n" ); } + + if (myProblems == null) { + myProblems = new ArrayList(1); + } myProblems.add(problemDescriptor); } @@ -79,6 +93,31 @@ public class ProblemsHolder { return ArrayUtil.indexOf(myFile.getPsiRoots(), file) != -1; } + private void redirectProblem(@NotNull final ProblemDescriptor problem, @NotNull final PsiElement target) { + final PsiElement original = problem.getPsiElement(); + final VirtualFile vFile = original.getContainingFile().getVirtualFile(); + assert vFile != null; + final String path = FileUtil.toSystemIndependentName(vFile.getPath()); + + String description = problem.getDescriptionTemplate(); + if (description.startsWith("")) { + description = description.replace("", "").replace("", ""); + } + if (description.startsWith("")) { + description = description.replace("", "").replace("", ""); + } + + final String template = + InspectionsBundle.message("inspection.redirect.template", + description, path, original.getTextRange().getStartOffset(), vFile.getName()); + + + final InspectionManager manager = InspectionManager.getInstance(original.getProject()); + final ProblemDescriptor newProblem = + manager.createProblemDescriptor(target, template, (LocalQuickFix)null, problem.getHighlightType(), isOnTheFly()); + registerProblem(newProblem); + } + public void registerProblem(@NotNull PsiReference reference, String descriptionTemplate, ProblemHighlightType highlightType) { LocalQuickFix[] fixes = null; if (reference instanceof LocalQuickFixProvider) { diff --git a/platform/lang-api/src/com/intellij/patterns/PsiFilePattern.java b/platform/lang-api/src/com/intellij/patterns/PsiFilePattern.java index 6966b7966077..8ef24bec536e 100644 --- a/platform/lang-api/src/com/intellij/patterns/PsiFilePattern.java +++ b/platform/lang-api/src/com/intellij/patterns/PsiFilePattern.java @@ -68,7 +68,7 @@ public class PsiFilePattern condition) { + public Capture(@NotNull final InitialPatternCondition condition) { super(condition); } diff --git a/platform/lang-api/src/com/intellij/patterns/VirtualFilePattern.java b/platform/lang-api/src/com/intellij/patterns/VirtualFilePattern.java index a417c914bf28..b5a0244956f8 100644 --- a/platform/lang-api/src/com/intellij/patterns/VirtualFilePattern.java +++ b/platform/lang-api/src/com/intellij/patterns/VirtualFilePattern.java @@ -43,7 +43,15 @@ public class VirtualFilePattern extends TreeElementPattern("withExtension") { + public boolean accepts(@NotNull final VirtualFile virtualFile, final ProcessingContext context) { + return extension.equals(virtualFile.getExtension()); + } + }); + } + public VirtualFilePattern withName(final ElementPattern namePattern) { return with(new PatternCondition("withName") { public boolean accepts(@NotNull final VirtualFile virtualFile, final ProcessingContext context) { diff --git a/platform/lang-api/src/com/intellij/psi/ExternallyDefinedPsiElement.java b/platform/lang-api/src/com/intellij/psi/ExternallyDefinedPsiElement.java new file mode 100644 index 000000000000..a08b5862e735 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/ExternallyDefinedPsiElement.java @@ -0,0 +1,36 @@ +/* + * 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.psi; + +import org.jetbrains.annotations.Nullable; + + +/** + * Interface for PSI elements which may be injected into other elements but physically belongs + * to other file - like AspectJ inter-type fields/methods. + */ +public interface ExternallyDefinedPsiElement extends PsiElement, SyntheticElement { + /** + * If inspection started for files with injections founds any problem in them (or their child) + * it should be able to display them locally. This method allows to define such substitution element. + * E.g. it may be a class name identifier element for fields/methods injected in that class.
+ * See ProblemsHolder.redirectProblem() for details. + * + * @return PSI element to which problem descriptions should be redirected + */ + @Nullable + PsiElement getProblemTarget(); +} 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 a9937d396b91..ba50a6d79dd9 100644 --- a/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java +++ b/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java @@ -172,6 +172,13 @@ public class PsiTreeUtil { } return null; } + + @NotNull public static T getRequiredChildOfType(@NotNull PsiElement element, @NotNull Class aClass) { + final T child = getChildOfType(element, aClass); + assert child != null: "Missing required child of type " + aClass.getName(); + return child; + } + @Nullable public static T[] getChildrenOfType(@NotNull PsiElement element, @NotNull Class aClass) { List result = null; for(PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()){ diff --git a/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractPanel.java b/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractPanel.java index cd46bcdcf1bb..1590d1e1889b 100644 --- a/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractPanel.java @@ -161,11 +161,7 @@ public abstract class CodeStyleAbstractPanel implements Disposable { myTextToReformat = myEditor.getDocument().getText(); } - Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); - if (project == null) { - project = ProjectManager.getInstance().getDefaultProject(); - } - final Project finalProject = project; + final Project finalProject = getCurrentProject(); CommandProcessor.getInstance().executeCommand(finalProject, new Runnable() { public void run() { replaceText(finalProject); @@ -218,6 +214,14 @@ public abstract class CodeStyleAbstractPanel implements Disposable { return psiFile; } + protected Project getCurrentProject() { + Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); + if (project == null) { + project = ProjectManager.getInstance().getDefaultProject(); + } + return project; + } + @NotNull protected abstract FileType getFileType(); @@ -249,7 +253,7 @@ public abstract class CodeStyleAbstractPanel implements Disposable { public abstract JComponent getPanel(); - public final void dispose() { + public void dispose() { myUpdateAlarm.cancelAllRequests(); EditorFactory.getInstance().releaseEditor(myEditor); } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemesPanel.java index c9158078bba1..5480c7e3b7a8 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSchemesPanel.java @@ -19,9 +19,9 @@ package com.intellij.application.options.codeStyle; import com.intellij.application.options.ExportSchemeAction; import com.intellij.application.options.SaveSchemeDialog; import com.intellij.application.options.SchemesToImportPopup; +import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.options.SchemesManager; import com.intellij.openapi.ui.Messages; import com.intellij.psi.codeStyle.CodeStyleScheme; @@ -166,8 +166,8 @@ public class CodeStyleSchemesPanel{ }); } }); - for(LanguageFileType fileType : LanguageCodeStyleSettingsProvider.getLanguageFileTypes()) { - myLanguageCombo.addItem(fileType.getLanguage().getDisplayName()); + for(Language language : LanguageCodeStyleSettingsProvider.getLanguagesWithCodeStyleSettings()) { + myLanguageCombo.addItem(language.getDisplayName()); } myLanguageLabel.setVisible(false); myLanguageCombo.setVisible(false); @@ -334,9 +334,9 @@ public class CodeStyleSchemesPanel{ private void onLanguageCombo() { Object selection = myLanguageCombo.getSelectedItem(); if (selection instanceof String) { - LanguageFileType fileType = LanguageCodeStyleSettingsProvider.getFileType((String)selection); - if (fileType != null && mySettingsPanel != null) { - mySettingsPanel.setLanguage(fileType.getLanguage()); + Language language = LanguageCodeStyleSettingsProvider.getLanguage((String)selection); + if (language != null && mySettingsPanel != null) { + mySettingsPanel.setLanguage(language); } } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java index 6fbae4891c8a..3d4c8a24acfc 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java @@ -18,7 +18,6 @@ package com.intellij.application.options.codeStyle; import com.intellij.lang.Language; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.fileTypes.LanguageFileType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,31 +37,31 @@ public abstract class LanguageCodeStyleSettingsProvider { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.langCodeStyleSettingsProvider"); - public abstract LanguageFileType getLanguageFileType(); + public abstract Language getLanguage(); public abstract String getCodeSample(@NotNull SettingsType settingsType); - public static LanguageFileType[] getLanguageFileTypes() { - ArrayList langFileTypes = new ArrayList(); + public static Language[] getLanguagesWithCodeStyleSettings() { + ArrayList langs = new ArrayList(); for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(EP_NAME)) { - langFileTypes.add(provider.getLanguageFileType()); + langs.add(provider.getLanguage()); } - return langFileTypes.toArray(new LanguageFileType[langFileTypes.size()]); + return langs.toArray(new Language[langs.size()]); } public static @Nullable String getCodeSample(Language lang, @NotNull SettingsType settingsType) { for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(EP_NAME)) { - if (provider.getLanguageFileType().getLanguage().equals(lang)) { + if (provider.getLanguage().equals(lang)) { return provider.getCodeSample(settingsType); } } return null; } - public static @Nullable LanguageFileType getFileType(String langName) { + public static @Nullable Language getLanguage(String langName) { for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(EP_NAME)) { - if (langName.equals(provider.getLanguageFileType().getLanguage().getDisplayName())) { - return provider.getLanguageFileType(); + if (langName.equals(provider.getLanguage().getDisplayName())) { + return provider.getLanguage(); } } return null; diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/MultilanguageCodeStyleAbstractPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/MultilanguageCodeStyleAbstractPanel.java index c5601655cfc9..67a1d4af84e3 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/MultilanguageCodeStyleAbstractPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/MultilanguageCodeStyleAbstractPanel.java @@ -30,6 +30,8 @@ import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.util.Disposer; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; @@ -39,6 +41,8 @@ import com.intellij.util.IncorrectOperationException; import com.intellij.util.LocalTimeCounter; import org.jetbrains.annotations.NotNull; +import java.io.File; + /** * Base class for code style settings panels supporting multiple programming languages. * @@ -48,9 +52,13 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr private Language myLanguage; private static final Logger LOG = Logger.getInstance("#com.intellij.application.options.codeStyle.MultilanguageCodeStyleAbstractPanel"); + private static Project mySettingsProject; + private static int myInstanceCount; protected MultilanguageCodeStyleAbstractPanel(CodeStyleSettings settings) { super(settings); + createSettingsProject(); + myInstanceCount++; } /** @@ -80,10 +88,11 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr if (myLanguage != null) { return myLanguage.getAssociatedFileType(); } - LanguageFileType availTypes[] = LanguageCodeStyleSettingsProvider.getLanguageFileTypes(); - if (availTypes.length > 0) { - myLanguage = availTypes[0].getLanguage(); - return availTypes[0]; + Language langs[] = LanguageCodeStyleSettingsProvider.getLanguagesWithCodeStyleSettings(); + if (langs.length > 0) { + myLanguage = langs[0]; + FileType type = langs[0].getAssociatedFileType(); + if (type != null) return type; } return StdFileTypes.JAVA; } @@ -130,4 +139,41 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr manager.commitDocument(doc); return psiFile; } + + @Override + protected final synchronized Project getCurrentProject() { + return mySettingsProject; + } + + @Override + public void dispose() { + myInstanceCount--; + if (myInstanceCount == 0) { + disposeSettingsProject(); + } + super.dispose(); + } + + /** + * A physical settings project is created to ensure that all formatters in preview panels work correctly. + */ + private synchronized static void createSettingsProject() { + if (mySettingsProject != null) return; + try { + File tempFile = File.createTempFile("idea-", "-settings.tmp"); + tempFile.deleteOnExit(); + mySettingsProject = ProjectManagerEx.getInstanceEx().newProject("settings.tmp", tempFile.getPath(), true, false); + } + catch (Exception e) { + LOG.error(e); + } + } + + private synchronized static void disposeSettingsProject() { + if (mySettingsProject == null) return; + Disposer.dispose(mySettingsProject); + mySettingsProject = null; + } + + } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java index 9bdd820585e2..3dca0c9f3c69 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/CodeFoldingManagerImpl.java @@ -116,7 +116,7 @@ public class CodeFoldingManagerImpl extends CodeFoldingManager implements Projec myCurrentHint = null; } TextRange textRange = new TextRange(textOffset, fold.getStartOffset()); - hint = EditorFragmentComponent.showEditorFragmentHint(editor, textRange, true); + hint = EditorFragmentComponent.showEditorFragmentHint(editor, textRange, true, true); myCurrentFold = fold; myCurrentHint = hint; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/BraceHighlightingHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/BraceHighlightingHandler.java index c5629dba053f..b53d9137d539 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/BraceHighlightingHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/BraceHighlightingHandler.java @@ -393,7 +393,7 @@ public class BraceHighlightingHandler { int line2 = myDocument.getLineNumber(range.getEndOffset()); line1 = Math.max(line1, line2 - 5); range = new TextRange(myDocument.getLineStartOffset(line1), range.getEndOffset()); - EditorFragmentComponent.showEditorFragmentHint(myEditor, range, true); + EditorFragmentComponent.showEditorFragmentHint(myEditor, range, true, true); } } }, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/NavigationLinkHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/NavigationLinkHandler.java new file mode 100644 index 000000000000..de9cbf93c29a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/NavigationLinkHandler.java @@ -0,0 +1,62 @@ +/* + * 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.codeInsight.hint; + +import com.intellij.codeInsight.highlighting.TooltipLinkHandler; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + + +/** + * Handles tooltip links in format #navigation/file path:offset. + */ +public class NavigationLinkHandler extends TooltipLinkHandler { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.hint.NavigationLinkHandler"); + + @Override + public void handleLink(@NotNull final String suffix, @NotNull final Editor editor, @NotNull final JEditorPane tooltip) { + final int pos = suffix.lastIndexOf(':'); + if (pos <= 0 || pos == suffix.length()-1) { + LOG.error("Malformed suffix: " + suffix); + return; + } + + final String path = suffix.substring(0, pos); + final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(path); + if (vFile == null) { + LOG.error("Unknown file: " + path); + return; + } + + final int offset; + try { + offset = Integer.parseInt(suffix.substring(pos+1)); + } + catch (NumberFormatException e) { + LOG.error("Malformed suffix: " + suffix); + return; + } + + tooltip.setVisible(false); + new OpenFileDescriptor(editor.getProject(), vFile, offset).navigate(true); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/LazyEditor.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/LazyEditor.java index f719f6a4ef8e..cf3925ee4d0c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/LazyEditor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/LazyEditor.java @@ -204,4 +204,8 @@ class LazyEditor extends UserDataHolderBase implements Editor { public JComponent getHeaderComponent() { return getEditor().getHeaderComponent(); } + + public IndentGuideDescriptor getCaretIndentGuide() { + return getEditor().getCaretIndentGuide(); + } } diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java index 9c7f2eecb194..c5be4b493b33 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java @@ -596,4 +596,8 @@ public class EditorWindow implements EditorEx, UserDataHolderEx { int hostOffset = myDocumentWindow.injectedToHost(offset); return myDelegate.calcColumnNumber(myDelegate.getDocument().getText(), hostStart, hostOffset, tabSize); } -} \ No newline at end of file + + public IndentGuideDescriptor getCaretIndentGuide() { + return null; // Caret guide is purely text-based thing so it is handled at top editor level. + } +} diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/AbstractPostFormatProcessor.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/AbstractPostFormatProcessor.java index 5d1cb0e7f039..4a0ceb18e09c 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/AbstractPostFormatProcessor.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/AbstractPostFormatProcessor.java @@ -24,21 +24,21 @@ import org.jetbrains.annotations.Nullable; * @author lesya */ public class AbstractPostFormatProcessor { - protected final CodeStyleSettings mySettings; + public final CodeStyleSettings mySettings; private TextRange myResultTextRange; public AbstractPostFormatProcessor(final CodeStyleSettings settings) { mySettings = settings; } - protected void updateResultRange(final int oldTextLength, final int newTextLength) { + public void updateResultRange(final int oldTextLength, final int newTextLength) { if (myResultTextRange == null) return; myResultTextRange = new TextRange(myResultTextRange.getStartOffset(), myResultTextRange.getEndOffset() - oldTextLength + newTextLength); } - protected boolean checkElementContainsRange(final PsiElement element) { + public boolean checkElementContainsRange(final PsiElement element) { if (myResultTextRange == null) return true; final TextRange elementRange = element.getTextRange(); @@ -47,7 +47,7 @@ public class AbstractPostFormatProcessor { } - protected boolean checkRangeContainsElement(final PsiElement element) { + public boolean checkRangeContainsElement(final PsiElement element) { if (myResultTextRange == null) return true; final TextRange elementRange = element.getTextRange(); @@ -56,7 +56,7 @@ public class AbstractPostFormatProcessor { && elementRange.getEndOffset() <= myResultTextRange.getEndOffset(); } - protected static boolean isMultiline(@Nullable PsiElement statement) { + public static boolean isMultiline(@Nullable PsiElement statement) { if (statement == null) { return false; } else { diff --git a/platform/platform-api/src/com/intellij/openapi/editor/Editor.java b/platform/platform-api/src/com/intellij/openapi/editor/Editor.java index 875a0402afa6..b9f1ff229286 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/Editor.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/Editor.java @@ -308,4 +308,7 @@ public interface Editor extends UserDataHolder { */ @Nullable JComponent getHeaderComponent(); + + @Nullable + IndentGuideDescriptor getCaretIndentGuide(); } diff --git a/platform/platform-api/src/com/intellij/openapi/editor/IndentGuideDescriptor.java b/platform/platform-api/src/com/intellij/openapi/editor/IndentGuideDescriptor.java new file mode 100644 index 000000000000..9c49d6e86a48 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/editor/IndentGuideDescriptor.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/* + * @author max + */ +package com.intellij.openapi.editor; + +public class IndentGuideDescriptor { + public final int indentLevel; + public final int startLine; + public final int endLine; + + public IndentGuideDescriptor(int indentLevel, int startLine, int endLine) { + this.indentLevel = indentLevel; + this.startLine = startLine; + this.endLine = endLine; + } + + @Override + public boolean equals(Object obj) { + IndentGuideDescriptor other = (IndentGuideDescriptor)obj; + return indentLevel == other.indentLevel && startLine == other.startLine && endLine == other.endLine; + } +} diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java index 1cc4fb59610e..ed11745842d1 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java @@ -72,7 +72,7 @@ public class DocumentFragmentTooltipRenderer implements TooltipRenderer { FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); foldingModel.setFoldingEnabled(false); TextRange textRange = new TextRange(startOffset, endOffset); - hint = EditorFragmentComponent.showEditorFragmentHintAt(editor, textRange, p.x, p.y, false, false); + hint = EditorFragmentComponent.showEditorFragmentHintAt(editor, textRange, p.x, p.y, false, false, true); foldingModel.setFoldingEnabled(true); return hint; } 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 e6877edb31b5..7336727ed6ef 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java @@ -121,15 +121,17 @@ public class EditorFragmentComponent extends JPanel { } /** + * @param hideByAnyKey * @param x x coordinate in layered pane coordinate system. * @param y y coordinate in layered pane coordinate system. */ - public static LightweightHint showEditorFragmentHintAt( - Editor editor, - TextRange range, - int x, - int y, - boolean showUpward, boolean showFolding) { + public static LightweightHint showEditorFragmentHintAt(Editor editor, + TextRange range, + int x, + int y, + boolean showUpward, + boolean showFolding, + boolean hideByAnyKey) { if (ApplicationManager.getApplication().isUnitTestMode()) return null; int startLine = editor.offsetToLogicalPosition(range.getStartOffset()).line; @@ -147,7 +149,7 @@ public class EditorFragmentComponent extends JPanel { Point p = new Point(x, y); LightweightHint hint = new MyComponentHint(fragmentComponent); - HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, p, HintManagerImpl.HIDE_BY_ANY_KEY | HintManagerImpl.HIDE_BY_TEXT_CHANGE, 0, false); + HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, p, (hideByAnyKey ? HintManagerImpl.HIDE_BY_ANY_KEY : 0) | HintManagerImpl.HIDE_BY_TEXT_CHANGE, 0, false); return hint; } @@ -166,7 +168,7 @@ public class EditorFragmentComponent extends JPanel { return fragmentComponent; } - public static LightweightHint showEditorFragmentHint(Editor editor,TextRange range, boolean showFolding){ + public static LightweightHint showEditorFragmentHint(Editor editor, TextRange range, boolean showFolding, boolean hideByAnyKey){ int x = -2; int y = 0; @@ -174,7 +176,7 @@ public class EditorFragmentComponent extends JPanel { JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane(); Point point = SwingUtilities.convertPoint(editorComponent, x, y, layeredPane); - return showEditorFragmentHintAt(editor, range, point.x, point.y, true, showFolding); + return showEditorFragmentHintAt(editor, range, point.x, point.y, true, showFolding, hideByAnyKey); } public static Color getBackgroundColor(Editor editor){ @@ -204,4 +206,4 @@ public class EditorFragmentComponent extends JPanel { ); } } -} \ No newline at end of file +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index 259746120e1e..88773955bd3a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -54,7 +54,7 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex public boolean IS_BLOCK_CURSOR = false; public boolean IS_WHITESPACES_SHOWN = false; - public boolean IS_INDENT_GUIDES_SHOWN = false; + public boolean IS_INDENT_GUIDES_SHOWN = true; public boolean IS_ANIMATED_SCROLLING = true; public boolean IS_CAMEL_WORDS = false; public boolean ADDITIONAL_PAGE_AT_BOTTOM = false; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 0c9b03673fa3..12da3a2f36a8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -17,6 +17,7 @@ package com.intellij.openapi.editor.impl; import com.intellij.Patches; import com.intellij.codeInsight.hint.DocumentFragmentTooltipRenderer; +import com.intellij.codeInsight.hint.EditorFragmentComponent; import com.intellij.codeInsight.hint.TooltipController; import com.intellij.codeInsight.hint.TooltipGroup; import com.intellij.concurrency.JobScheduler; @@ -60,6 +61,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.ui.GuiUtils; import com.intellij.ui.JScrollPane2; +import com.intellij.ui.LightweightHint; import com.intellij.util.Alarm; import com.intellij.util.IJSwingUtilities; import com.intellij.util.containers.ContainerUtil; @@ -224,6 +226,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private char[] myPrefixText; private TextAttributes myPrefixAttributes; + private IndentGuideDescriptor myCaretIndentGuide = null; static { ourCaretBlinkingCommand = new RepaintCursorCommand(); @@ -282,6 +285,36 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myDocument.addDocumentListener(mySelectionModel); myDocument.addDocumentListener(myEditorDocumentAdapter); + myCaretModel.addCaretListener(new CaretListener() { + LightweightHint myCurrentHint = null; + + public void caretPositionChanged(CaretEvent e) { + + final IndentGuideDescriptor newGuide = getCaretIndentGuide(); + if (!Comparing.equal(newGuide, myCaretIndentGuide)) { + repaintGuide(newGuide); + repaintGuide(myCaretIndentGuide); + myCaretIndentGuide = newGuide; + + if (myCurrentHint != null) { + myCurrentHint.hide(); + myCurrentHint = null; + } + + if (newGuide != null) { + final Rectangle visibleArea = getScrollingModel().getVisibleArea(); + final int line = newGuide.startLine - 1; + if (logicalLineToY(line) < visibleArea.y) { + TextRange textRange = new TextRange(myDocument.getLineStartOffset(line), + myDocument.getLineEndOffset(line)); + + myCurrentHint = EditorFragmentComponent.showEditorFragmentHint(EditorImpl.this, textRange, false, false); + } + } + } + } + }); + myCaretCursor = new CaretCursor(); myFoldingModel.flushCaretShift(); @@ -342,6 +375,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } + private void repaintGuide(IndentGuideDescriptor guide) { + if (guide != null) { + repaintLines(guide.startLine, guide.endLine); + } + } + public void setPrefixTextAndAttributes(String prefixText, TextAttributes attributes) { myPrefixText = prefixText == null? null: prefixText.toCharArray(); myPrefixAttributes = attributes; @@ -1190,7 +1229,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi int y = clip.y; int line = xyToLogicalPosition(new Point(0, y)).line; - int gapWidth = EditorUtil.getSpaceWidth(Font.PLAIN, this) * getIndentSize(); + final int indentSize = getIndentSize(); + int gapWidth = EditorUtil.getSpaceWidth(Font.PLAIN, this) * indentSize; do { final int indents = getIndents(line); @@ -1203,9 +1243,46 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi y = logicalLineToY(line); } while (y < clip.y + clip.height); + + if (myCaretIndentGuide != null) { + int x = myCaretIndentGuide.indentLevel * gapWidth + 1; + int y1 = logicalLineToY(myCaretIndentGuide.startLine); + int y2 = logicalLineToY(myCaretIndentGuide.endLine); + UIUtil.drawDottedLine((Graphics2D)g, x, y1, x, y2, getBackroundColor(), getForegroundColor()); + } } } + @Nullable + public IndentGuideDescriptor getCaretIndentGuide() { + final int indentSize = getIndentSize(); + + final LogicalPosition caretPosition = myCaretModel.getLogicalPosition(); + int startLine = caretPosition.line; + int endLine = startLine; + int indents = caretPosition.column / indentSize; + + if (indents > 0 && caretPosition.column % indentSize == 0) { + while (startLine > 0) { + if (getIndents(startLine - 1) <= indents) break; + startLine--; + } + + if (getIndents(endLine + 1) > indents) endLine++; + + while (endLine < myDocument.getLineCount() - 1) { + if (getIndents(endLine) <= indents) break; + endLine++; + } + + if (indents > 0 && startLine < endLine) { + return new IndentGuideDescriptor(indents, startLine, endLine); + } + } + + return null; + } + public void setHeaderComponent(JComponent header) { myHeaderPanel.removeAll(); if (header != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentEditor.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentEditor.java index a587f26bebff..9ecc0e61bb9f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentEditor.java @@ -226,4 +226,8 @@ public class TextComponentEditor extends UserDataHolderBase implements Editor { public JComponent getHeaderComponent() { return null; } + + public IndentGuideDescriptor getCaretIndentGuide() { + return null; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java index 4db774472abd..891ecebb62a7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java @@ -185,8 +185,9 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { for (String name : names) { findChild(name, false, false); } - - return ensureAsMap().values(); + + // important: should return a copy here for safe iterations + return new ArrayList(ensureAsMap().values()); } @NotNull diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 464ad78aea1f..c4ba9849fdbc 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -617,3 +617,5 @@ inspection.application.chosen.profile.log\ message=Inspecting with profile ''{0} detach.library.quickfix.name=Detach library detach.library.roots.quickfix.name=Detach unused library roots inspection.javadoc.problem.pointing.to.itself=Javadoc pointing to itself + +inspection.redirect.template=Injected element has problem: {0} (in {3}). diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index f7e1a8408572..20ab2cd85189 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -115,6 +115,8 @@ + + + diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java index e58188cb7149..469ca5d8c8fa 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java @@ -33,6 +33,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiReference; import com.intellij.testFramework.TestDataFile; +import com.intellij.testFramework.TestDataPath; import com.intellij.usageView.UsageInfo; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -156,7 +157,7 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture { * @see #getReferenceAtCaretPositionWithAssertion(String...) */ @Nullable - PsiReference getReferenceAtCaretPosition(@NonNls String... filePaths) throws Exception; + PsiReference getReferenceAtCaretPosition(@TestDataFile @NonNls String... filePaths) throws Exception; /** * Finds the reference in position marked by {@link #CARET_MARKER}. diff --git a/platform/util/src/com/intellij/util/ArrayUtil.java b/platform/util/src/com/intellij/util/ArrayUtil.java index 879d4539fffc..444f20fe425b 100644 --- a/platform/util/src/com/intellij/util/ArrayUtil.java +++ b/platform/util/src/com/intellij/util/ArrayUtil.java @@ -155,6 +155,32 @@ public class ArrayUtil { return a; } + /** + * Allocates new array of size array.length + collection.size() and copies elements of array and + * collection to it. + * @param array source array + * @param collection source collection + * @param factory array factory used to create destination array of type T + * @return destination array + */ + @NotNull + public static T[] mergeArrayAndCollection(@NotNull T[] array, @NotNull Collection collection, + @NotNull final ArrayFactory factory) { + if (collection.size() == 0) { + return array; + } + + final T[] array2 = collection.toArray(factory.create(collection.size())); + if (array.length == 0) { + return array2; + } + + final T[] result = factory.create(array.length + collection.size()); + System.arraycopy(array, 0, result, 0, array.length); + System.arraycopy(array2, 0, result, array.length, array2.length); + return result; + } + /** * Appends element to the src array. As you can * imagine the appended element will be the last one in the returned result. diff --git a/plugins/groovy/resources/standardDsls/metaDsl.gdsl b/plugins/groovy/resources/standardDsls/metaDsl.gdsl index f7c9e0de6451..d18a881a9ebe 100644 --- a/plugins/groovy/resources/standardDsls/metaDsl.gdsl +++ b/plugins/groovy/resources/standardDsls/metaDsl.gdsl @@ -1,4 +1,4 @@ -def gdslScriptContext = context(scope: scriptScope(), filetypes: ["gdsl"]) +def gdslScriptContext = context(scope: scriptScope(), filetypes:['gdsl']) contributor([gdslScriptContext]) { method name: "context", params: [args: [:]], type: "java.lang.Object" @@ -7,15 +7,15 @@ contributor([gdslScriptContext]) { method name: "contributor", params: [contexts: "java.lang.Object", body: {}], type: void // scopes - property name: "closureScope", type: {} - property name: "scriptScope", type: {} + property name: "closureScope", params: [contexts: "java.util.Map"], type: {} + property name: "scriptScope", params: [contexts: "java.util.Map"], type: {} method name: "hasAnnotation", params:[fqn: "java.lang.String"], type: "java.lang.Object" method name: "hasMethod", params:[memberPattern: "java.lang.Object"], type: "java.lang.Object" method name: "hasField", params:[memberPattern: "java.lang.Object"], type: "java.lang.Object" } -def contributorBody = context(scope: closureScope(isArg: true)) +def contributorBody = context(scope: closureScope(isArg: true), filetypes:['gdsl']) contributor([contributorBody]) { if (enclosingCall("contributor")) { @@ -39,41 +39,38 @@ contributor([contributorBody]) { } } -def psiClassContext = context(scope: closureScope(isArg: true), ctype: "com.intellij.psi.PsiClass") -contributor([psiClassContext]) { +def enrich(String className) { + context(scope: closureScope(isArg: true), ctype: className, filetypes:['gdsl']) +} + +contributor(enrich("com.intellij.psi.PsiClass")) { method name: "getMethods", type: "java.util.Collection" method name: "getQualName", type: "java.lang.String" } -def psiMemberContext = context(scope: closureScope(isArg: true), ctype: "com.intellij.psi.PsiMember") -contributor([psiMemberContext]) { +contributor(enrich("com.intellij.psi.PsiMember")) { method name: "hasAnnotation", params: [name: "java.lang.String"], type: "boolean" method name: "hasAnnotation", type: "boolean" method name: "getAnnotation", params: [name: "java.lang.String"], type: "com.intellij.psi.PsiAnnotation" method name: "getAnnotations", params: [name: "java.lang.String"], type: "java.util.Collection" } -def psiFieldContext = context(scope: closureScope(isArg: true), ctype: "com.intellij.psi.PsiField") -contributor([psiFieldContext]) { +contributor(enrich("com.intellij.psi.PsiField")) { method name: "getClassType", type: "com.intellij.psi.PsiClass" } -def psiMethodContext = context(scope: closureScope(isArg: true), ctype: "com.intellij.psi.PsiMethod") -contributor([psiMethodContext]) { +contributor(enrich("com.intellij.psi.PsiMethod")) { method name: "getParamStringVector", type: "java.util.Map" } -def psiElementContext = context(scope: closureScope(isArg: true), ctype: "com.intellij.psi.PsiElement") -contributor([psiElementContext]) { +contributor(enrich("com.intellij.psi.PsiElement")) { method name: "bind", type: "com.intellij.psi.PsiElement" method name: "eval", type: "java.lang.Object" method name: "asList", type: "java.util.collection" method name: "getQualifier", type: "com.intellij.psi.PsiElement" } -def expressionContext = context(scope: closureScope(isArg: true), - ctype: "org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression") -contributor([expressionContext]) { +contributor(enrich("org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression")) { method name: "getArguments", type: "java.util.Collection" method name: "getClassType", type: "com.intellij.psi.PsiClass" } diff --git a/plugins/groovy/resources/standardDsls/newifyTransform.gdsl b/plugins/groovy/resources/standardDsls/newifyTransform.gdsl index 95c31019c7d2..303c5ea1bb61 100644 --- a/plugins/groovy/resources/standardDsls/newifyTransform.gdsl +++ b/plugins/groovy/resources/standardDsls/newifyTransform.gdsl @@ -1,13 +1,18 @@ contributor(context()) { // For methods def memb = enclosingMember() - // For classes - def clazz = enclosingClass() if (memb) { + // For classes def newifyName = "groovy.lang.Newify" - for (a in memb?.getAnnotations(newifyName) + clazz?.getAnnotations(newifyName)) { - def refs = a?.findAttributeValue("value") - def auto = a?.findAttributeValue("auto") + def annotated = memb.getAnnotations(newifyName) + + def clazz = enclosingClass() + if (clazz) { + annotated += clazz.getAnnotations(newifyName) + } + for (a in annotated) { + def refs = a.findAttributeValue("value") + def auto = a.findAttributeValue("auto") //For Python-like style if (refs && !place.qualifier) { for (c in refs.asList()) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 033a556a9216..fd9960457e28 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -1148,29 +1148,47 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { private static void checkMethodApplicability(GroovyResolveResult methodResolveResult, PsiElement place, AnnotationHolder holder) { final PsiElement element = methodResolveResult.getElement(); if (!(element instanceof PsiMethod)) return; + final PsiMethod method = (PsiMethod)element; PsiType[] argumentTypes = PsiUtil.getArgumentTypes(place, method.isConstructor(), true); - if (argumentTypes != null && - !PsiUtil.isApplicable(argumentTypes, method, methodResolveResult.getSubstitutor(), - methodResolveResult.getCurrentFileResolveContext() instanceof GrMethodCallExpression)) { - PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place); - if (elementToHighlight == null) { - elementToHighlight = place; + if ("call".equals(method.getName()) && place instanceof GrReferenceExpression) { + final GrExpression qualifierExpression = ((GrReferenceExpression)place).getQualifierExpression(); + if (qualifierExpression != null) { + final PsiType type = qualifierExpression.getType(); + if (type instanceof GrClosureType) { + if (!PsiUtil.isApplicable(argumentTypes, (GrClosureType)type, element.getManager())) { + highlightInapplicableMethodUsage(methodResolveResult, place, holder, method, argumentTypes); + return; + } + } } - - final String typesString = buildArgTypesList(argumentTypes); - String message; - final PsiClass containingClass = method.getContainingClass(); - if (containingClass != null) { - final PsiClassType containingType = JavaPsiFacade.getInstance(method.getProject()).getElementFactory() - .createType(containingClass, methodResolveResult.getSubstitutor()); - message = GroovyBundle.message("cannot.apply.method1", method.getName(), containingType.getInternalCanonicalText(), typesString); - } - else { - message = GroovyBundle.message("cannot.apply.method.or.closure", method.getName(), typesString); - } - holder.createWarningAnnotation(elementToHighlight, message); } + if (argumentTypes != null && + !PsiUtil.isApplicable(argumentTypes, method, methodResolveResult.getSubstitutor(), + methodResolveResult.getCurrentFileResolveContext() instanceof GrMethodCallExpression)) { + highlightInapplicableMethodUsage(methodResolveResult, place, holder, method, argumentTypes); + } + } + + private static void highlightInapplicableMethodUsage(GroovyResolveResult methodResolveResult, PsiElement place, AnnotationHolder holder, + PsiMethod method, PsiType[] argumentTypes) { + PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place); + if (elementToHighlight == null) { + elementToHighlight = place; + } + + final String typesString = buildArgTypesList(argumentTypes); + String message; + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null) { + final PsiClassType containingType = JavaPsiFacade.getInstance(method.getProject()).getElementFactory() + .createType(containingClass, methodResolveResult.getSubstitutor()); + message = GroovyBundle.message("cannot.apply.method1", method.getName(), containingType.getInternalCanonicalText(), typesString); + } + else { + message = GroovyBundle.message("cannot.apply.method.or.closure", method.getName(), typesString); + } + holder.createWarningAnnotation(elementToHighlight, message); } public static boolean isDeclarationAssignment(GrReferenceExpression refExpr) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.groovy b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.groovy index 0e16ec33582f..994cb82d621b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.groovy +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.groovy @@ -77,7 +77,7 @@ public class CustomMembersGenerator implements GdslMembersHolderConsumer { public CustomMembersHolder getMembersHolder() { // Add non-code members holder if (myClassText.length() > 0) { - addMemberHolder(new NonCodeMembersHolder(myClassText.toString(), myProject)); + addMemberHolder(NonCodeMembersHolder.fromText(myClassText.toString(), myProject)); } return myDepot; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java index c46ea9942eec..4b4c87f2e268 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java @@ -16,11 +16,17 @@ package org.jetbrains.plugins.groovy.dsl.holders; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.psi.PsiField; import com.intellij.psi.PsiMethod; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.NameHint; import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.CachedValue; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.util.containers.ConcurrentFactoryMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; @@ -30,8 +36,23 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini */ public class NonCodeMembersHolder implements CustomMembersHolder { private final GrTypeDefinition myPsiClass; + private static final Key>> CACHED_HOLDERS = Key.create("CACHED_HOLDERS"); - public NonCodeMembersHolder(@NotNull String classText, Project project) { + public static NonCodeMembersHolder fromText(@NotNull String classText, final Project project) { + return CachedValuesManager.getManager(project).getCachedValue(project, CACHED_HOLDERS, new CachedValueProvider>() { + public Result> compute() { + final ConcurrentFactoryMap map = new ConcurrentFactoryMap() { + @Override + protected NonCodeMembersHolder create(String key) { + return new NonCodeMembersHolder(key, project); + } + }; + return Result.create(map, PsiModificationTracker.MODIFICATION_COUNT); + } + }, false).get(classText); + } + + private NonCodeMembersHolder(@NotNull String classText, Project project) { final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project); myPsiClass = factory.createGroovyFile("class GroovyEnhanced {\n" + classText + "}", false, null).getTypeDefinitions()[0]; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/Context.groovy b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/Context.groovy index d4e93e18bcda..0b451c19b30a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/Context.groovy +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/Context.groovy @@ -54,7 +54,11 @@ class Context { def scope = (ScriptScope) args.scope //first, it should be inside groovy script - addFilter new PlaceContextFilter(PlatformPatterns.psiElement().inFile(GroovyPatterns.groovyScript())) + def scriptPattern = GroovyPatterns.groovyScript() + if (scope.extension) { + scriptPattern = scriptPattern.withVirtualFile(PlatformPatterns.virtualFile().withExtension(scope.extension)) + } + addFilter new PlaceContextFilter(PlatformPatterns.psiElement().inFile(scriptPattern)) // Name matcher def namePattern = scope.namePattern diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/scopes/Scope.groovy b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/scopes/Scope.groovy index e2d814192436..b5889e89da88 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/scopes/Scope.groovy +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/scopes/Scope.groovy @@ -37,10 +37,15 @@ class ClosureScope extends Scope { class ScriptScope extends Scope { final String namePattern + final String extension ScriptScope(Map args) { - if (args && args.name) { - namePattern = args.name + if (args) { + if (args.name) { + namePattern = args.name + } else if (args.extension) { + extension = args.extension + } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java index 8fc0485912b3..9e852fecdd33 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java @@ -19,8 +19,10 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.UserDataCache; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -32,6 +34,9 @@ import com.intellij.psi.scope.NameHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.util.CachedValue; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; import com.intellij.util.IncorrectOperationException; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -69,6 +74,7 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile { private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.lang.psi.impl.GroovyFileImpl"); private static final Object lock = new Object(); + private volatile Boolean myScript; private GroovyScriptClass myScriptClass; private static final String SYNTHETIC_PARAMETER_NAME = "args"; private GrParameter mySyntheticArgsParameter = null; @@ -368,12 +374,26 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile { if (stub instanceof GrFileStub) { return ((GrFileStub)stub).isScript(); } - GrTopStatement[] top = findChildrenByClass(GrTopStatement.class); - for (GrTopStatement st : top) { - if (!(st instanceof GrTypeDefinition || st instanceof GrImportStatement || st instanceof GrPackageDefinition)) return true; + + Boolean isScript = myScript; + if (isScript == null) { + isScript = Boolean.FALSE; + for (GrTopStatement st : findChildrenByClass(GrTopStatement.class)) { + if (!(st instanceof GrTypeDefinition || st instanceof GrImportStatement || st instanceof GrPackageDefinition)) { + isScript = Boolean.TRUE; + break; + } + } + myScript = isScript; } - return false; + return isScript; + } + + @Override + public void subtreeChanged() { + myScript = null; + super.subtreeChanged(); } public GroovyScriptClass getScriptClass() { @@ -490,15 +510,26 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile { return this; } + private static final UserDataCache, GroovyFile, GlobalSearchScope> RESOLVE_SCOPE_CACHE = new UserDataCache, GroovyFile, GlobalSearchScope>("RESOLVE_SCOPE_CACHE") { + @Override + protected CachedValue compute(final GroovyFile file, final GlobalSearchScope baseScope) { + return CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider() { + public Result compute() { + GlobalSearchScope scope = GroovyScriptType.getScriptType(file).patchResolveScope(file, baseScope); + return Result.create(scope, file, ProjectRootManager.getInstance(file.getProject())); + } + }, false); + } + }; public GlobalSearchScope getFileResolveScope() { - final VirtualFile vFile = getOriginalFile().getVirtualFile(); + final VirtualFile vFile = getOriginalFile().getVirtualFile(); if (vFile == null) { return GlobalSearchScope.allScope(getProject()); } final GlobalSearchScope baseScope = ((FileManagerImpl)((PsiManagerEx)getManager()).getFileManager()).getDefaultResolveScope(vFile); if (isScript()) { - return GroovyScriptType.getScriptType(this).patchResolveScope(this, baseScope); + return RESOLVE_SCOPE_CACHE.get(this, baseScope).getValue(); } return baseScope; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 6175091a67af..fce06d1b78d4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -407,7 +407,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements return GroovyResolveResult.EMPTY_ARRAY; } - private void resolveImpl(GrReferenceExpressionImpl refExpr, ResolverProcessor processor) { + private static void resolveImpl(GrReferenceExpressionImpl refExpr, ResolverProcessor processor) { GrExpression qualifier = refExpr.getQualifierExpression(); if (qualifier == null) { ResolveUtil.treeWalkUp(refExpr, processor, true); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java index 1c08b2cec925..435bad0d6092 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java @@ -327,33 +327,30 @@ public abstract class GrTypeDefinitionImpl extends GroovyBaseElementImpl methods = new ArrayList(); + List cached = myMethods; + if (cached == null) { + cached = new ArrayList(); GrTypeDefinitionBody body = getBody(); if (body != null) { - methods.addAll(body.getMethods()); + cached.addAll(body.getMethods()); } - myMethods = methods; + myMethods = cached; } - List result = new ArrayList(myMethods); + List result = new ArrayList(cached); GrClassImplUtil.addGroovyObjectMethods(this, result); return result.toArray(new PsiMethod[result.size()]); } @NotNull public GrMethod[] getGroovyMethods() { - if (myGroovyMethods == null) { + GrMethod[] cached = myGroovyMethods; + if (cached == null) { GrTypeDefinitionBody body = getBody(); - if (body != null) { - myGroovyMethods = body.getGroovyMethods(); - } - else { - myGroovyMethods = GrMethod.EMPTY_ARRAY; - } + myGroovyMethods = cached = body != null ? body.getGroovyMethods() : GrMethod.EMPTY_ARRAY; } - return myGroovyMethods; + return cached; } public void subtreeChanged() { @@ -366,7 +363,8 @@ public abstract class GrTypeDefinitionImpl extends GroovyBaseElementImpl result = new ArrayList(); for (final PsiMethod method : getMethods()) { if (method.isConstructor()) { @@ -374,20 +372,20 @@ public abstract class GrTypeDefinitionImpl extends GroovyBaseElementImpl groovyScript() { - return new GroovyElementPattern.Capture(new InitialPatternCondition(GroovyFile.class) { + public static PsiFilePattern.Capture groovyScript() { + return new PsiFilePattern.Capture(new InitialPatternCondition(GroovyFile.class) { @Override public boolean accepts(@Nullable Object o, ProcessingContext context) { return o instanceof GroovyFile && ((GroovyFile)o).isScript(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/CollectClassMembersUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/CollectClassMembersUtil.java index 8d0c91cbbda4..2b55e7647e72 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/CollectClassMembersUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/CollectClassMembersUtil.java @@ -22,6 +22,7 @@ import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.util.*; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import java.util.ArrayList; @@ -37,42 +38,34 @@ public class CollectClassMembersUtil { private static final Key, Map>, Map>>> CACHED_MEMBERS_INCLUDING_SYNTHETIC = Key.create("CACHED_MEMBERS_INCLUDING_SYNTHETIC"); + private CollectClassMembersUtil() { + } + public static Map> getAllMethods(final PsiClass aClass, boolean includeSynthetic) { - Key, Map>, Map>>> key = includeSynthetic ? - CACHED_MEMBERS_INCLUDING_SYNTHETIC : CACHED_MEMBERS; + return getCachedMembers(aClass, includeSynthetic).getSecond(); + } + + @NotNull + private static Trinity, Map>, Map> getCachedMembers( + PsiClass aClass, + boolean includeSynthetic) { + Key, Map>, Map>>> key = + includeSynthetic ? CACHED_MEMBERS_INCLUDING_SYNTHETIC : CACHED_MEMBERS; CachedValue, Map>, Map>> cachedValue = aClass.getUserData(key); if (cachedValue == null) { cachedValue = buildCache(aClass, includeSynthetic); + aClass.putUserData(key, cachedValue); } - - Trinity, Map>, Map> value = cachedValue.getValue(); - assert value != null; - return value.getSecond(); + return cachedValue.getValue(); } public static Map getAllInnerClasses(final PsiClass aClass, boolean includeSynthetic) { - Key, Map>, Map>>> key = includeSynthetic ? - CACHED_MEMBERS_INCLUDING_SYNTHETIC : CACHED_MEMBERS; - CachedValue, Map>, Map>> cachedValue = aClass.getUserData(key); - if (cachedValue == null) { - cachedValue = buildCache(aClass, includeSynthetic); - } - - Trinity, Map>, Map> value = cachedValue.getValue(); - assert value != null; - return value.getThird(); + return getCachedMembers(aClass, includeSynthetic).getThird(); } public static Map getAllFields(final PsiClass aClass) { - CachedValue, Map>, Map>> cachedValue = aClass.getUserData(CACHED_MEMBERS); - if (cachedValue == null) { - cachedValue = buildCache(aClass, false); - } - - Trinity, Map>, Map> value = cachedValue.getValue(); - assert value != null; - return value.getFirst(); + return getCachedMembers(aClass, false).getFirst(); } private static CachedValue, Map>, Map>> buildCache(final PsiClass aClass, final boolean includeSynthetic) { @@ -83,7 +76,7 @@ public class CollectClassMembersUtil { Map allInnerClasses = new HashMap(); processClass(aClass, allFields, allMethods, allInnerClasses, new HashSet(), PsiSubstitutor.EMPTY, includeSynthetic); - return Result.create(new Trinity, Map>, Map>(allFields, allMethods, allInnerClasses), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); + return Result.create(Trinity.create(allFields, allMethods, allInnerClasses), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } }, false); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/MethodResolverProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/MethodResolverProcessor.java index cf905db7672f..b24d3fef81d5 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/MethodResolverProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/MethodResolverProcessor.java @@ -34,6 +34,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssign import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; @@ -83,16 +84,19 @@ public class MethodResolverProcessor extends ResolverProcessor { } return true; - } else if (element instanceof PsiVariable) { - if (element instanceof GrField && ((GrField) element).isProperty() || - isClosure((PsiVariable) element)) { - return super.execute(element, state); - } else { - myInapplicableCandidates.add(new GroovyResolveResultImpl(element, myCurrentFileResolveContext, substitutor, isAccessible((PsiVariable)element), isStaticsOK((PsiVariable)element))); + } + else if (element instanceof PsiVariable) { + if (isApplicableClosure((PsiVariable)element)) { + myCandidates.add(new GroovyResolveResultImpl(element, myCurrentFileResolveContext, substitutor, isAccessible((PsiVariable)element), + isStaticsOK((PsiVariable)element))); + } + else { + myInapplicableCandidates.add( + new GroovyResolveResultImpl(element, myCurrentFileResolveContext, substitutor, isAccessible((PsiVariable)element), + isStaticsOK((PsiVariable)element))); } } - return true; } @@ -126,10 +130,14 @@ public class MethodResolverProcessor extends ResolverProcessor { return substitutor; } - private static boolean isClosure(PsiVariable variable) { + private boolean isApplicableClosure(PsiVariable variable) { if (variable instanceof GrVariable) { - final PsiType type = ((GrVariable) variable).getTypeGroovy(); - return type != null && type.equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE); + final PsiType type = ((GrVariable)variable).getTypeGroovy(); + if (type == null) return false; + if (type instanceof GrClosureType) { + return PsiUtil.isApplicable(myArgumentTypes, (GrClosureType)type, variable.getManager()); + } + if (type.equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE)) return true; } return variable.getType().equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE); } @@ -317,10 +325,6 @@ public class MethodResolverProcessor extends ResolverProcessor { return myArgumentTypes; } - public void setArgumentTypes(@Nullable PsiType[] argumentTypes) { - myArgumentTypes = argumentTypes; - } - @Override public void handleEvent(Event event, Object associated) { super.handleEvent(event, associated); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java index 7bd8b61e0714..4d65aedae35e 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java @@ -194,6 +194,7 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { public void testMethodCallWithDefaultParameters() throws Exception {doTest();} public void testClosureWithDefaultParameters() throws Exception {doTest();} + public void testClosureCallMethodWithInapplicableArguments() throws Exception {doTest();} public void testOverlyLongMethodInspection() throws Exception { doTest(new GroovyOverlyLongMethodInspection()); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.java index 146af08a9ae4..2303bf5f7d4c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.java @@ -546,4 +546,10 @@ public class ResolveMethodTest extends GroovyResolveTestCase { final PsiElement resolved = ref.resolve(); assertInstanceOf(resolved, PsiMethod.class); } + + public void testMethodVsField() throws Exception { + final PsiReference ref = configureByFile("methodVsField/A.groovy"); + final PsiElement element = ref.resolve(); + assertInstanceOf(element, PsiMethod.class); + } } diff --git a/plugins/groovy/testdata/highlighting/ClosureCallMethodWithInapplicableArguments.groovy b/plugins/groovy/testdata/highlighting/ClosureCallMethodWithInapplicableArguments.groovy new file mode 100644 index 000000000000..58275e39aac1 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/ClosureCallMethodWithInapplicableArguments.groovy @@ -0,0 +1,7 @@ +def foo={x, y->} + +print foo.call(1) + +def bar={3} +print bar.call() +print bar.call(3) \ No newline at end of file diff --git a/plugins/groovy/testdata/resolve/method/methodVsField/A.groovy b/plugins/groovy/testdata/resolve/method/methodVsField/A.groovy new file mode 100644 index 000000000000..aaed6df8dc90 --- /dev/null +++ b/plugins/groovy/testdata/resolve/method/methodVsField/A.groovy @@ -0,0 +1,17 @@ +class Foo { + + + def bar = { 2} +} + +class Bar extends Foo { + def bar(def it) { 3 } + + public static void main(String[] args) { + def f = new Bar() + + + println f.bar(3) + } + +} diff --git a/xml/impl/src/com/intellij/lang/xml/XmlFoldingBuilder.java b/xml/impl/src/com/intellij/lang/xml/XmlFoldingBuilder.java index 700905129986..e8f398487e11 100644 --- a/xml/impl/src/com/intellij/lang/xml/XmlFoldingBuilder.java +++ b/xml/impl/src/com/intellij/lang/xml/XmlFoldingBuilder.java @@ -133,7 +133,7 @@ public class XmlFoldingBuilder implements FoldingBuilder, DumbAware { if (tagNameElement == null) return null; int nameEnd = tagNameElement.getTextRange().getEndOffset(); - int end = tagNode.getLastChildNode().getTextRange().getStartOffset(); + int end = tagNode.getLastChildNode().getTextRange().getEndOffset() - 1; // last child node can be another tag in unbalanced tree ASTNode[] attributes = tagNode.getChildren(XML_ATTRIBUTE_SET); if (attributes.length > 0) {