From 90f4133650d412289fcb549e7270cea391a2cc3c Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 29 Jun 2010 01:09:47 +0400 Subject: [PATCH] don't invoke all gdsls for every reference, only those that are really context-dependent --- .../intellij/patterns/PsiClassPattern.java | 9 +- .../psi/util/CachedValuesManager.java | 30 +-- .../resources/standardDsls/metaDsl.gdsl | 1 + .../groovy/dsl/CustomMembersGenerator.java | 16 +- .../ExtensibleCustomMembersGenerator.groovy | 10 +- .../groovy/dsl/GroovyClassDescriptor.java | 39 +++- .../groovy/dsl/GroovyDslExecutor.groovy | 4 +- .../groovy/dsl/GroovyDslFileIndex.java | 190 +++++++++++------- .../plugins/groovy/dsl/descriptors.groovy | 17 -- .../dsl/holders/CustomMembersHolder.java | 7 + .../dsl/toplevel/ClassContextFilter.java | 12 +- .../dsl/toplevel/CompositeContextFilter.java | 6 +- .../groovy/dsl/toplevel/Context.groovy | 46 ++--- .../groovy/dsl/toplevel/ContextFilter.java | 4 +- .../dsl/toplevel/FileContextFilter.java | 22 ++ .../dsl/toplevel/PlaceContextFilter.java | 5 +- .../groovy/lang/resolve/ResolveUtil.java | 36 ++-- 17 files changed, 269 insertions(+), 185 deletions(-) delete mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/descriptors.groovy create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/FileContextFilter.java diff --git a/java/openapi/src/com/intellij/patterns/PsiClassPattern.java b/java/openapi/src/com/intellij/patterns/PsiClassPattern.java index 95754a9ba8d3..8fc9f8fe5dcd 100644 --- a/java/openapi/src/com/intellij/patterns/PsiClassPattern.java +++ b/java/openapi/src/com/intellij/patterns/PsiClassPattern.java @@ -102,6 +102,13 @@ public class PsiClassPattern extends PsiMemberPattern } }); } - + public PsiClassPattern withQualifiedName(@NonNls @NotNull final ElementPattern qname) { + return with(new PatternCondition("withQualifiedName") { + public boolean accepts(@NotNull final PsiClass psiClass, final ProcessingContext context) { + return qname.accepts(psiClass.getQualifiedName(), context); + } + }); + } + } diff --git a/platform/platform-api/src/com/intellij/psi/util/CachedValuesManager.java b/platform/platform-api/src/com/intellij/psi/util/CachedValuesManager.java index e2cebe84643f..9d133d787585 100644 --- a/platform/platform-api/src/com/intellij/psi/util/CachedValuesManager.java +++ b/platform/platform-api/src/com/intellij/psi/util/CachedValuesManager.java @@ -89,20 +89,22 @@ public abstract class CachedValuesManager { public T getCachedValue(@NotNull D dataHolder, @NotNull CachedValueProvider provider) { - class MemoizationKey extends Key { - MemoizationKey(@NotNull @NonNls String name) { - super(name); - } - - public int hashCode() { - return toString().hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof MemoizationKey && toString().equals(obj.toString()); - } - } return getCachedValue(dataHolder, new MemoizationKey>("$CachedValue$" + provider.getClass().getName()), provider, false); } + + public static class MemoizationKey extends Key { + public MemoizationKey(@NotNull @NonNls String name) { + super(name); + } + + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof MemoizationKey && toString().equals(obj.toString()); + } + } + } diff --git a/plugins/groovy/resources/standardDsls/metaDsl.gdsl b/plugins/groovy/resources/standardDsls/metaDsl.gdsl index d18a881a9ebe..763f6714630f 100644 --- a/plugins/groovy/resources/standardDsls/metaDsl.gdsl +++ b/plugins/groovy/resources/standardDsls/metaDsl.gdsl @@ -2,6 +2,7 @@ def gdslScriptContext = context(scope: scriptScope(), filetypes:['gdsl']) contributor([gdslScriptContext]) { method name: "context", params: [args: [:]], type: "java.lang.Object" + method name: "context", params: [:], type: "java.lang.Object" method name: "contributor", params: [contexts: "java.util.List", body: {}], type: void method name: "contributor", params: [contexts: "java.util.Map", body: {}], type: void method name: "contributor", params: [contexts: "java.lang.Object", body: {}], type: void diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java index eac5f3b4b1d8..99e0df61843d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java @@ -22,23 +22,23 @@ import java.util.Set; public class CustomMembersGenerator implements GdslMembersHolderConsumer { private final Set myMethods = new HashSet(); private final Project myProject; - private final PsiElement myPlace; private final String myQualifiedName; private final CompoundMembersHolder myDepot = new CompoundMembersHolder(); + private final GroovyClassDescriptor myDescriptor; - public CustomMembersGenerator(Project project, PsiElement place, String qualifiedName) { - myProject = project; - myPlace = place; - myQualifiedName = qualifiedName; + public CustomMembersGenerator(GroovyClassDescriptor descriptor) { + myDescriptor = descriptor; + myProject = descriptor.getProject(); + myQualifiedName = descriptor.getQualifiedName(); } public PsiElement getPlace() { - return myPlace; + return myDescriptor.getPlace(); } @Nullable public PsiClass getClassType() { - return JavaPsiFacade.getInstance(myProject).findClass(myQualifiedName, myPlace.getResolveScope()); + return JavaPsiFacade.getInstance(myProject).findClass(myQualifiedName, myDescriptor.getResolveScope()); } public Project getProject() { @@ -49,7 +49,7 @@ public class CustomMembersGenerator implements GdslMembersHolderConsumer { public CustomMembersHolder getMembersHolder() { // Add non-code members holder if (!myMethods.isEmpty()) { - addMemberHolder(NonCodeMembersHolder.generateMembers(myMethods, myPlace.getContainingFile())); + addMemberHolder(NonCodeMembersHolder.generateMembers(myMethods, myDescriptor.getPlaceFile())); } return myDepot; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy index eb12c8948b6f..b8132af68fd7 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy @@ -9,15 +9,9 @@ import org.jetbrains.plugins.groovy.dsl.dsltop.GdslMembersProvider * @author peter, ilyas */ public class ExtensibleCustomMembersGenerator extends CustomMembersGenerator { - private final Project myProject; - private final PsiElement myPlace; - private final String myQualifiedName; - public ExtensibleCustomMembersGenerator(Project project, PsiElement place, String qualifiedName) { - super(project, place, qualifiedName) - myProject = project; - myPlace = place; - myQualifiedName = qualifiedName; + public ExtensibleCustomMembersGenerator(GroovyClassDescriptor descriptor) { + super(descriptor) } def methodMissing(String name, args) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java index b4c7cf2f62b7..cd0456df2ac8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java @@ -15,8 +15,11 @@ */ package org.jetbrains.plugins.groovy.dsl; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import org.codehaus.groovy.runtime.GroovyCategorySupport; import org.jetbrains.annotations.NotNull; @@ -27,7 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** * @author peter */ -class GroovyClassDescriptor implements ClassDescriptor { +public class GroovyClassDescriptor { static { try { final AtomicInteger integer = GroovyCategorySupport.getCategoryNameUsage("aaa"); @@ -39,10 +42,23 @@ class GroovyClassDescriptor implements ClassDescriptor { private final PsiClass myPsiClass; private final PsiElement myPlace; + private final PsiFile myFile; + private final boolean myPlaceDependent; + private boolean myPlaceElementAccessed; - public GroovyClassDescriptor(@NotNull PsiClass psiClass, PsiElement place) { + public GroovyClassDescriptor(@NotNull PsiClass psiClass, PsiElement place, boolean placeDependent, final PsiFile placeFile) { myPsiClass = psiClass; myPlace = place; + myPlaceDependent = placeDependent; + myFile = placeFile; + } + + public Project getProject() { + return myPlace.getProject(); + } + + public GlobalSearchScope getResolveScope() { + return myPlace.getResolveScope(); } @Nullable @@ -55,6 +71,7 @@ class GroovyClassDescriptor implements ClassDescriptor { } public PsiElement getPlace() { + myPlaceElementAccessed = true; return myPlace; } @@ -62,6 +79,14 @@ class GroovyClassDescriptor implements ClassDescriptor { return myPsiClass; } + public PsiFile getPlaceFile() { + return myFile; + } + + public boolean placeAccessed() { + return myPlaceElementAccessed; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -71,11 +96,19 @@ class GroovyClassDescriptor implements ClassDescriptor { if (!myPsiClass.equals(that.myPsiClass)) return false; + if (myPlaceDependent) { + return myPlace.equals(that.myPlace); + } + return true; } @Override public int hashCode() { - return myPsiClass.hashCode() * 31 + myPlace.hashCode(); + int result = myPsiClass.hashCode(); + if (myPlaceDependent) { + return result * 31 + myPlace.hashCode(); + } + return result; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslExecutor.groovy b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslExecutor.groovy index 5cd8cb04296a..4b9b75a44054 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslExecutor.groovy +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslExecutor.groovy @@ -43,9 +43,9 @@ public class GroovyDslExecutor { enhancers << Pair.create(CompositeContextFilter.compose(cts, false), toDo) } - def processVariants(ClassDescriptor descriptor, consumer, PsiElement place, String fqn, ProcessingContext ctx) { + def processVariants(GroovyClassDescriptor descriptor, consumer, ProcessingContext ctx) { for (pair in enhancers) { - if (pair.first.isApplicable(place, fqn, ctx)) { + if (pair.first.isApplicable(descriptor, ctx)) { Closure f = pair.second.clone() f.delegate = consumer f.resolveStrategy = Closure.DELEGATE_FIRST diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java index af216dc41df9..ea6e05a9a808 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java @@ -61,6 +61,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -68,8 +69,7 @@ import java.util.concurrent.TimeUnit; * @author peter */ public class GroovyDslFileIndex extends ScalarIndexExtension { - public static final Key>> CACHED_ENHANCEMENTS = - Key.create("CACHED_ENHANCEMENTS"); + public static final Key> CACHED_EXECUTOR = Key.create("CachedGdslExecutor"); private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.dsl.GroovyDslFileIndex"); private static final FileAttribute ENABLED = new FileAttribute("ENABLED", 0); @@ -78,8 +78,6 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { private final MyDataIndexer myDataIndexer = new MyDataIndexer(); private final MyInputFilter myInputFilter = new MyInputFilter(); - private static final Map> ourMapping = - new ConcurrentHashMap>(); private static final MultiMap>> filesInProcessing = new ConcurrentMultiMap>>(); @@ -153,13 +151,13 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { catch (IOException e1) { LOG.error(e1); } - ourMapping.remove(vfile.getUrl()); + vfile.putUserData(CACHED_EXECUTOR, null); } @Nullable private static GroovyDslExecutor getCachedExecutor(@NotNull final VirtualFile file, final long stamp) { - final Pair pair = ourMapping.get(file.getUrl()); + final Pair pair = file.getUserData(CACHED_EXECUTOR); if (pair == null || pair.second.longValue() != stamp) { return null; } @@ -173,11 +171,20 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { return true; } + final PsiFile placeFile = place.getContainingFile().getOriginalFile(); + final LinkedBlockingQueue> queue = new LinkedBlockingQueue>(); + final ArrayList> ready = new ArrayList>(); - int count = queueExecutors(psiClass.getProject(), queue); + int count = queueExecutors(psiClass.getProject(), queue, ready); + + for (Pair pair : ready) { + if (!processExecutor(pair.second, processor, pair.first, psiClass, place, placeFile)) { + return false; + } + count--; + } - final GroovyClassDescriptor descriptor = new GroovyClassDescriptor(psiClass, place); try { while (count > 0) { ProgressManager.checkCanceled(); @@ -185,7 +192,8 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { if (pair != null) { final GroovyDslExecutor executor = pair.second; final GroovyFile dslFile = pair.first; - if (executor != null && !processExecutor(executor, descriptor, processor, dslFile)) { + if (executor != null && !processExecutor(executor, processor, dslFile, psiClass, place, + placeFile)) { return false; } @@ -237,7 +245,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { } }; - private static int queueExecutors(Project project, LinkedBlockingQueue> queue) { + private static int queueExecutors(Project project, LinkedBlockingQueue> queue, ArrayList> ready) { int count = 0; for (GroovyFile file : DSL_FILES_CACHE.get(project, null).getValue()) { final long stamp = file.getModificationStamp(); @@ -246,11 +254,10 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { final GroovyDslExecutor cached = getCachedExecutor(vfile, stamp); count++; if (cached == null) { - file.putUserData(CACHED_ENHANCEMENTS, null); //otherwise an old executor instance will be executed inside cachedValue scheduleParsing(queue, file, vfile, stamp, file.getText()); } else { - queue.offer(Pair.create(file, cached)); + ready.add(Pair.create(file, cached)); } } return count; @@ -258,69 +265,98 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { private static boolean processExecutor(final GroovyDslExecutor executor, - final GroovyClassDescriptor descriptor, PsiScopeProcessor processor, - final GroovyFile dslFile) { + final GroovyFile dslFile, final PsiClass psiClass, final PsiElement place, final PsiFile placeFile) { + final String qname = psiClass.getQualifiedName(); + if (qname == null) { + return true; + } + + Map specificities = getCachedMap(dslFile, "placeDependent"); + boolean firstTime = !specificities.containsKey(qname); + + final boolean placeDependent = firstTime || specificities.get(qname); + GroovyClassDescriptor descriptor = new GroovyClassDescriptor(psiClass, place, placeDependent, + placeFile); + + final ConcurrentMap members = getCachedMap(dslFile, "members"); + CustomMembersHolder holder = members.get(descriptor); + if (holder == null) { + holder = addGdslMembers(executor, descriptor, dslFile); + + if (firstTime) { + final boolean placeAccessed = descriptor.placeAccessed(); + specificities.put(qname, placeAccessed); + final GroovyClassDescriptor newDescriptor = new GroovyClassDescriptor(psiClass, place, placeAccessed, + placeFile); + members.putIfAbsent(newDescriptor, holder); + } else { + members.putIfAbsent(descriptor, holder); + } + } + + return holder.processMembers(processor); + } + + private static ConcurrentMap getCachedMap(GroovyFile dslFile, String id) { final Project project = dslFile.getProject(); - final ConcurrentFactoryMap map = CachedValuesManager.getManager(dslFile.getProject()).getCachedValue(dslFile, CACHED_ENHANCEMENTS, new CachedValueProvider>() { - public Result> compute() { - final ConcurrentFactoryMap result = - new ConcurrentFactoryMap() { - @Override - protected CustomMembersHolder create(GroovyClassDescriptor key) { - final PsiElement place = key.getPlace(); - final String fqn = key.getQualifiedName(); + final Key>> key = new CachedValuesManager.MemoizationKey>>("GroovyDsl" + id); + return CachedValuesManager.getManager(project).getCachedValue(dslFile, key, new CachedValueProvider>() { + @Override + public Result> compute() { + final ConcurrentMap map = new ConcurrentHashMap(); + final Result> result = + Result.create(map, PsiModificationTracker.MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); + result.setLockValue(true); + return result; + } + }, false); + } - final ProcessingContext ctx = new ProcessingContext(); - ctx.put(ClassContextFilter.getClassKey(fqn), key.getPsiClass()); - try { - if (!isApplicable(place, fqn, ctx)) { - return null; - } + private static CustomMembersHolder addGdslMembers(GroovyDslExecutor executor, GroovyClassDescriptor descriptor, GroovyFile dslFile) { + final String fqn = descriptor.getQualifiedName(); + final Project project = descriptor.getProject(); - final ExtensibleCustomMembersGenerator generator = new ExtensibleCustomMembersGenerator(project, place, fqn); + final ProcessingContext ctx = new ProcessingContext(); + ctx.put(ClassContextFilter.getClassKey(fqn), descriptor.getPsiClass()); + try { + if (!isApplicable(executor, descriptor, ctx)) { + return CustomMembersHolder.EMPTY; + } - executor.processVariants(key, generator, place, fqn, ctx); - return generator.getMembersHolder(); - } - catch (InvokerInvocationException e) { - Throwable cause = e.getCause(); - if (cause instanceof ProcessCanceledException) { - throw (ProcessCanceledException)cause; - } - if (cause instanceof OutOfMemoryError) { - throw (OutOfMemoryError)cause; - } - handleDslError(e, project, dslFile); - } - catch (ProcessCanceledException e) { - throw e; - } - catch (OutOfMemoryError e) { - throw e; - } - catch (Throwable e) { // To handle exceptions in definition script - handleDslError(e, project, dslFile); - } - return null; - } + final ExtensibleCustomMembersGenerator generator = new ExtensibleCustomMembersGenerator(descriptor); + executor.processVariants(descriptor, generator, ctx); + return generator.getMembersHolder(); + } + catch (InvokerInvocationException e) { + Throwable cause = e.getCause(); + if (cause instanceof ProcessCanceledException) { + throw (ProcessCanceledException)cause; + } + if (cause instanceof OutOfMemoryError) { + throw (OutOfMemoryError)cause; + } + handleDslError(e, project, dslFile); + } + catch (ProcessCanceledException e) { + throw e; + } + catch (OutOfMemoryError e) { + throw e; + } + catch (Throwable e) { // To handle exceptions in definition script + handleDslError(e, project, dslFile); + } + return CustomMembersHolder.EMPTY; + } - private boolean isApplicable(PsiElement place, String fqn, final ProcessingContext ctx) { - for (Pair pair : executor.getEnhancers()) { - if (pair.first.isApplicable(place, fqn, ctx)) { - return true; - } - } - return false; - } - - }; - return Result.create(result, PsiModificationTracker.MODIFICATION_COUNT, dslFile); - } - }, false); - assert map != null; - final CustomMembersHolder holder = map.get(descriptor); - return holder == null || holder.processMembers(processor); + private static boolean isApplicable(GroovyDslExecutor executor, GroovyClassDescriptor descriptor, final ProcessingContext ctx) { + for (Pair pair : executor.getEnhancers()) { + if (pair.first.isApplicable(descriptor, ctx)) { + return true; + } + } + return false; } private static boolean handleDslError(Throwable e, Project project, GroovyFile dslFile) { @@ -361,14 +397,14 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { executor = createExecutor(text, vfile, project); // executor is not only time-consuming to create, but also takes some PermGenSpace // => we can't afford garbage-collecting it together with PsiFile - // => cache globally by file path - ourMapping.put(vfile.getUrl(), Pair.create(executor, stamp)); + // => cache globally by file instance + vfile.putUserData(CACHED_EXECUTOR, Pair.create(executor, stamp)); if (executor != null) { activateUntilModification(vfile); } } - // access to our multimap should be synchronized + // access to our MultiMap should be synchronized synchronized (vfile) { // put evaluated executor to all queues final Collection>> queuesForFile = filesInProcessing.remove(fileUrl); @@ -379,6 +415,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { } }; + //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (vfile) { //ensure that only one thread calculates dsl executor final boolean isNewRequest = !filesInProcessing.containsKey(fileUrl); filesInProcessing.putValue(fileUrl, queue); @@ -400,12 +437,8 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { } if (ApplicationManager.getApplication().isUnitTestMode()) { - try { - LOG.error(e); - } - finally { - return null; - } + LOG.error(e); + return null; } invokeDslErrorPopup(e, project, vfile); return null; @@ -417,6 +450,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { } final StringWriter writer = new StringWriter(); + //noinspection IOResourceOpenedButNotSafelyClosed e.printStackTrace(new PrintWriter(writer)); final String exceptionText = writer.toString(); LOG.info(exceptionText); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/descriptors.groovy b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/descriptors.groovy deleted file mode 100644 index b3280ff6cdfe..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/descriptors.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package org.jetbrains.plugins.groovy.dsl - -import com.intellij.psi.PsiElement - -/** - * @author peter - */ - -interface ClassDescriptor { - - String getQualifiedName() - - boolean isInheritor(String qname) - - PsiElement getPlace(); - -} \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/CustomMembersHolder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/CustomMembersHolder.java index 24634bc80e6c..89127ceafebb 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/CustomMembersHolder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/CustomMembersHolder.java @@ -22,5 +22,12 @@ import com.intellij.psi.scope.PsiScopeProcessor; * @author ilyas */ public interface CustomMembersHolder { + CustomMembersHolder EMPTY = new CustomMembersHolder() { + @Override + public boolean processMembers(PsiScopeProcessor processor) { + return true; + } + }; + boolean processMembers(PsiScopeProcessor processor); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ClassContextFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ClassContextFilter.java index 462c2ab52962..45863f0d619e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ClassContextFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ClassContextFilter.java @@ -1,11 +1,13 @@ package org.jetbrains.plugins.groovy.dsl.toplevel; +import com.intellij.openapi.project.Project; import com.intellij.patterns.ElementPattern; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.dsl.GroovyClassDescriptor; /** * @author peter @@ -17,12 +19,12 @@ public class ClassContextFilter implements ContextFilter { myPattern = pattern; } - public boolean isApplicable(PsiElement place, String fqName, ProcessingContext ctx) { - return myPattern.accepts(findPsiClass(place, fqName, ctx), ctx); + public boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx) { + return myPattern.accepts(findPsiClass(descriptor.getProject(), descriptor.getResolveScope(), descriptor.getQualifiedName(), ctx), ctx); } @Nullable - private static PsiClass findPsiClass(PsiElement place, String fqName, ProcessingContext ctx) { + private static PsiClass findPsiClass(Project project, GlobalSearchScope scope, String fqName, ProcessingContext ctx) { final String key = getClassKey(fqName); final Object cached = ctx.get(key); if (cached == Boolean.FALSE) { @@ -32,7 +34,7 @@ public class ClassContextFilter implements ContextFilter { return (PsiClass)cached; } - final PsiClass found = JavaPsiFacade.getInstance(place.getProject()).findClass(fqName, place.getResolveScope()); + final PsiClass found = JavaPsiFacade.getInstance(project).findClass(fqName, scope); ctx.put(key, found == null ? Boolean.FALSE : found); return found; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/CompositeContextFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/CompositeContextFilter.java index b0a371c20bed..9e4ea899129e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/CompositeContextFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/CompositeContextFilter.java @@ -1,7 +1,7 @@ package org.jetbrains.plugins.groovy.dsl.toplevel; -import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; +import org.jetbrains.plugins.groovy.dsl.GroovyClassDescriptor; import java.util.List; @@ -17,9 +17,9 @@ public class CompositeContextFilter implements ContextFilter { myAnd = and; } - public boolean isApplicable(PsiElement place, String fqName, ProcessingContext ctx) { + public boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx) { for (ContextFilter filter : myFilters) { - if (myAnd != filter.isApplicable(place, fqName, ctx)) { + if (myAnd != filter.isApplicable(descriptor, ctx)) { return !myAnd; } } 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 12dc50e24240..8a091683e15f 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 @@ -5,17 +5,13 @@ import com.intellij.patterns.PlatformPatterns import com.intellij.patterns.PsiElementPattern import com.intellij.patterns.PsiJavaPatterns import com.intellij.patterns.StandardPatterns -import com.intellij.psi.PsiElement import com.intellij.psi.SyntheticElement -import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.plugins.groovy.dsl.toplevel.scopes.ClassScope import org.jetbrains.plugins.groovy.dsl.toplevel.scopes.ClosureScope import org.jetbrains.plugins.groovy.dsl.toplevel.scopes.ScriptScope import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition import org.jetbrains.plugins.groovy.lang.psi.patterns.GroovyPatterns import com.intellij.openapi.util.text.StringUtil @@ -35,7 +31,7 @@ class Context { if (extensions instanceof List) { extensions = extensions.collect { StringUtil.trimStart(it, '.') } def vfilePattern = extensions.size() == 1 ? virtualFile().withExtension(extensions[0]) : virtualFile().withExtension(extensions as String[]) - addFilter new PlaceContextFilter(psiElement().inFile(psiFile().withOriginalFile(psiFile().withVirtualFile(vfilePattern)))) + addFilter new FileContextFilter(psiFile().withVirtualFile(vfilePattern)) } // filter by scope first, then by ctype @@ -52,12 +48,12 @@ class Context { if (scope.extension) { scriptPattern = scriptPattern.withVirtualFile(PlatformPatterns.virtualFile().withExtension(scope.extension)) } - addFilter new PlaceContextFilter(PlatformPatterns.psiElement().inFile(scriptPattern)) + addFilter new FileContextFilter(scriptPattern) // Name matcher def namePattern = scope.namePattern if (namePattern) { - addFilter {PsiElement elem, fqn, ctx -> elem.containingFile.name.matches(namePattern)} + addFilter new FileContextFilter(psiFile().withName(PlatformPatterns.string().matches(namePattern))) } // Process unqualified references only @@ -71,14 +67,11 @@ class Context { final def classScope = (ClassScope) args.scope def namePattern = classScope.getName() if (namePattern) { - addFilter {GrReferenceExpression elem, fqn, ctx -> - final GrTypeDefinition clazz = PsiTreeUtil.getParentOfType(elem, GrTypeDefinition) - if (clazz) { - final def qualName = clazz.getQualifiedName() - return clazz.getName().matches(namePattern) || qualName && qualName.matches(namePattern) - } - return false - } + def match = PlatformPatterns.string().matches(namePattern) + addFilter new PlaceContextFilter(psiElement().inside( + PlatformPatterns.or( + PsiJavaPatterns.psiClass().withQualifiedName(match), + PsiJavaPatterns.psiClass().withName(match)))) } break @@ -88,21 +81,17 @@ class Context { if (((ClosureScope) args.scope).isArg()) { // Filter for call parameter - addFilter {GrReferenceExpression elem, fqn, ctx -> - def closParent = PsiTreeUtil.getParentOfType(elem, GrClosableBlock.class) - assert closParent != null + addFilter new PlaceContextFilter(psiElement().inside( + psiElement(GrClosableBlock).withParent( + PlatformPatterns.or( + psiElement(GrCall), + psiElement(GrArgumentList).withParent(GrCall))))) - def parent = closParent.getParent() - if (parent instanceof GrArgumentList) { - parent = parent.parent - } - return parent instanceof GrCall - } } // Enhance only unqualified expressions if (!args.ctype) { - addFilter getClassTypeFilter("groovy.lang.Closure") + addFilter getClassTypeFilter(GrClosableBlock.GROOVY_LANG_CLOSURE) } break @@ -122,14 +111,15 @@ class Context { new ClassContextFilter(PsiJavaPatterns.psiClass().inheritorOf(false, ctype)) } - private def addFilter(Closure cl) { - addFilter (cl as ContextFilter) - } private void addFilter(ContextFilter cl) { myFilters << cl } ContextFilter getFilter() { + if (myFilters.size() == 1) { + return myFilters[0] + } + return CompositeContextFilter.compose(myFilters, true) } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ContextFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ContextFilter.java index b1e7f9995e4b..1b192b9029ab 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ContextFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/ContextFilter.java @@ -1,13 +1,13 @@ package org.jetbrains.plugins.groovy.dsl.toplevel; -import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; +import org.jetbrains.plugins.groovy.dsl.GroovyClassDescriptor; /** * @author peter */ public interface ContextFilter { - boolean isApplicable(PsiElement place, String fqName, ProcessingContext ctx); + boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/FileContextFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/FileContextFilter.java new file mode 100644 index 000000000000..ecdfdbddeb65 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/FileContextFilter.java @@ -0,0 +1,22 @@ +package org.jetbrains.plugins.groovy.dsl.toplevel; + +import com.intellij.patterns.ElementPattern; +import com.intellij.psi.PsiFile; +import com.intellij.util.ProcessingContext; +import org.jetbrains.plugins.groovy.dsl.GroovyClassDescriptor; + +/** + * @author peter + */ +public class FileContextFilter implements ContextFilter { + private final ElementPattern myPattern; + + public FileContextFilter(ElementPattern pattern) { + myPattern = pattern; + } + + public boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx) { + return myPattern.accepts(descriptor.getPlaceFile(), ctx); + } + +} \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/PlaceContextFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/PlaceContextFilter.java index c040f3171da2..a82b4b58ff56 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/PlaceContextFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/PlaceContextFilter.java @@ -3,6 +3,7 @@ package org.jetbrains.plugins.groovy.dsl.toplevel; import com.intellij.patterns.ElementPattern; import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; +import org.jetbrains.plugins.groovy.dsl.GroovyClassDescriptor; /** * @author peter @@ -14,8 +15,8 @@ public class PlaceContextFilter implements ContextFilter { myPattern = pattern; } - public boolean isApplicable(PsiElement place, String fqName, ProcessingContext ctx) { - return myPattern.accepts(place, ctx); + public boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx) { + return myPattern.accepts(descriptor.getPlace(), ctx); } } \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index 3b592ea7c8d3..66d99eeeec66 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -67,13 +67,22 @@ public class ResolveUtil { private static final TObjectHashingStrategy RAW_TYPE_HASHING_STRATEGY = new TObjectHashingStrategy() { @Override public int computeHashCode(PsiType object) { - return rawCanonicalText(object).hashCode(); + return stringify(object).hashCode(); } @Override public boolean equals(PsiType o1, PsiType o2) { - return rawCanonicalText(o1).equals(rawCanonicalText(o2)); + return stringify(o1).equals(stringify(o2)); } + + private String stringify(PsiType type) { + final PsiClass cls = PsiUtil.resolveClassInType(type); + if (cls instanceof PsiTypeParameter) { + return cls.getName() + cls.getSuperClass().getName(); + } + return rawCanonicalText(type); + } + }; private ResolveUtil() { @@ -142,6 +151,9 @@ public class ResolveUtil { if (type instanceof PsiClassType) { PsiClass psiClass = ((PsiClassType)type).resolve(); + if (psiClass instanceof PsiTypeParameter) { + psiClass = psiClass.getSuperClass(); + } if (psiClass != null && !GroovyDslFileIndex.processExecutors(psiClass, place, processor)) { return false; } @@ -155,17 +167,6 @@ public class ResolveUtil { return true; } - private static HashMap getSuperTypes(PsiType type, Project project) { - final HashMap visited = new HashMap(); - collectSuperTypes(type, visited); - if (type instanceof PsiArrayType) { - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_LANG_COMPARABLE, null), visited); - collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_IO_SERIALIZABLE, null), visited); - } - return visited; - } - private static void collectSuperTypes(PsiType type, Map visited) { String qName = rawCanonicalText(type); @@ -193,7 +194,14 @@ public class ResolveUtil { @Override protected Map create(PsiType key) { - return getSuperTypes(key, project); + final HashMap visited = new HashMap(); + collectSuperTypes(key, visited); + if (key instanceof PsiArrayType) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_LANG_COMPARABLE, null), visited); + collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_IO_SERIALIZABLE, null), visited); + } + return visited; } }; return Result.create(map, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project));