mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
don't invoke all gdsls for every reference, only those that are really context-dependent
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
+2
-8
@@ -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);
|
||||
}
|
||||
|
||||
+7
-5
@@ -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;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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
-2
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user