don't invoke all gdsls for every reference, only those that are really context-dependent

This commit is contained in:
peter
2010-06-29 01:57:58 +04:00
parent 26800d7734
commit 90f4133650
17 changed files with 269 additions and 185 deletions
@@ -102,6 +102,13 @@ public class PsiClassPattern extends PsiMemberPattern<PsiClass, PsiClassPattern>
}
});
}
public PsiClassPattern withQualifiedName(@NonNls @NotNull final ElementPattern<String> qname) {
return with(new PatternCondition<PsiClass>("withQualifiedName") {
public boolean accepts(@NotNull final PsiClass psiClass, final ProcessingContext context) {
return qname.accepts(psiClass.getQualifiedName(), context);
}
});
}
}
@@ -89,20 +89,22 @@ public abstract class CachedValuesManager {
public <T, D extends UserDataHolder> T getCachedValue(@NotNull D dataHolder,
@NotNull CachedValueProvider<T> provider) {
class MemoizationKey<T> extends Key<T> {
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<T>>("$CachedValue$" + provider.getClass().getName()), provider, false);
}
public static class MemoizationKey<T> extends Key<T> {
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());
}
}
}
@@ -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
@@ -22,23 +22,23 @@ import java.util.Set;
public class CustomMembersGenerator implements GdslMembersHolderConsumer {
private final Set<Map> myMethods = new HashSet<Map>();
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;
}
@@ -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) {
@@ -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;
}
}
@@ -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
@@ -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<String> {
public static final Key<CachedValue<ConcurrentFactoryMap<GroovyClassDescriptor, CustomMembersHolder>>> CACHED_ENHANCEMENTS =
Key.create("CACHED_ENHANCEMENTS");
public static final Key<Pair<GroovyDslExecutor, Long>> 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<String> {
private final MyDataIndexer myDataIndexer = new MyDataIndexer();
private final MyInputFilter myInputFilter = new MyInputFilter();
private static final Map<String, Pair<GroovyDslExecutor, Long>> ourMapping =
new ConcurrentHashMap<String, Pair<GroovyDslExecutor, Long>>();
private static final MultiMap<String, LinkedBlockingQueue<Pair<GroovyFile, GroovyDslExecutor>>> filesInProcessing =
new ConcurrentMultiMap<String, LinkedBlockingQueue<Pair<GroovyFile, GroovyDslExecutor>>>();
@@ -153,13 +151,13 @@ public class GroovyDslFileIndex extends ScalarIndexExtension<String> {
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<GroovyDslExecutor, Long> pair = ourMapping.get(file.getUrl());
final Pair<GroovyDslExecutor, Long> pair = file.getUserData(CACHED_EXECUTOR);
if (pair == null || pair.second.longValue() != stamp) {
return null;
}
@@ -173,11 +171,20 @@ public class GroovyDslFileIndex extends ScalarIndexExtension<String> {
return true;
}
final PsiFile placeFile = place.getContainingFile().getOriginalFile();
final LinkedBlockingQueue<Pair<GroovyFile, GroovyDslExecutor>> queue = new LinkedBlockingQueue<Pair<GroovyFile, GroovyDslExecutor>>();
final ArrayList<Pair<GroovyFile, GroovyDslExecutor>> ready = new ArrayList<Pair<GroovyFile, GroovyDslExecutor>>();
int count = queueExecutors(psiClass.getProject(), queue);
int count = queueExecutors(psiClass.getProject(), queue, ready);
for (Pair<GroovyFile, GroovyDslExecutor> 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<String> {
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<String> {
}
};
private static int queueExecutors(Project project, LinkedBlockingQueue<Pair<GroovyFile, GroovyDslExecutor>> queue) {
private static int queueExecutors(Project project, LinkedBlockingQueue<Pair<GroovyFile, GroovyDslExecutor>> queue, ArrayList<Pair<GroovyFile, GroovyDslExecutor>> 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<String> {
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<String> {
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<String, Boolean> 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<GroovyClassDescriptor, CustomMembersHolder> 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 <T, V> ConcurrentMap<T, V> getCachedMap(GroovyFile dslFile, String id) {
final Project project = dslFile.getProject();
final ConcurrentFactoryMap<GroovyClassDescriptor, CustomMembersHolder> map = CachedValuesManager.getManager(dslFile.getProject()).getCachedValue(dslFile, CACHED_ENHANCEMENTS, new CachedValueProvider<ConcurrentFactoryMap<GroovyClassDescriptor, CustomMembersHolder>>() {
public Result<ConcurrentFactoryMap<GroovyClassDescriptor, CustomMembersHolder>> compute() {
final ConcurrentFactoryMap<GroovyClassDescriptor, CustomMembersHolder> result =
new ConcurrentFactoryMap<GroovyClassDescriptor, CustomMembersHolder>() {
@Override
protected CustomMembersHolder create(GroovyClassDescriptor key) {
final PsiElement place = key.getPlace();
final String fqn = key.getQualifiedName();
final Key<CachedValue<ConcurrentMap<T, V>>> key = new CachedValuesManager.MemoizationKey<CachedValue<ConcurrentMap<T, V>>>("GroovyDsl" + id);
return CachedValuesManager.getManager(project).getCachedValue(dslFile, key, new CachedValueProvider<ConcurrentMap<T, V>>() {
@Override
public Result<ConcurrentMap<T, V>> compute() {
final ConcurrentMap<T, V> map = new ConcurrentHashMap<T, V>();
final Result<ConcurrentMap<T, V>> 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<ContextFilter, Closure> 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<ContextFilter, Closure> 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<String> {
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<LinkedBlockingQueue<Pair<GroovyFile, GroovyDslExecutor>>> queuesForFile = filesInProcessing.remove(fileUrl);
@@ -379,6 +415,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension<String> {
}
};
//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<String> {
}
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<String> {
}
final StringWriter writer = new StringWriter();
//noinspection IOResourceOpenedButNotSafelyClosed
e.printStackTrace(new PrintWriter(writer));
final String exceptionText = writer.toString();
LOG.info(exceptionText);
@@ -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();
}
@@ -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);
}
@@ -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;
}
@@ -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;
}
}
@@ -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)
}
@@ -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);
}
@@ -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<? extends PsiFile> myPattern;
public FileContextFilter(ElementPattern<? extends PsiFile> pattern) {
myPattern = pattern;
}
public boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx) {
return myPattern.accepts(descriptor.getPlaceFile(), ctx);
}
}
@@ -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);
}
}
@@ -67,13 +67,22 @@ public class ResolveUtil {
private static final TObjectHashingStrategy<PsiType> RAW_TYPE_HASHING_STRATEGY = new TObjectHashingStrategy<PsiType>() {
@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<String, PsiType> getSuperTypes(PsiType type, Project project) {
final HashMap<String, PsiType> visited = new HashMap<String, PsiType>();
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<String, PsiType> visited) {
String qName = rawCanonicalText(type);
@@ -193,7 +194,14 @@ public class ResolveUtil {
@Override
protected Map<String, PsiType> create(PsiType key) {
return getSuperTypes(key, project);
final HashMap<String, PsiType> visited = new HashMap<String, PsiType>();
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));