mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
cache declarations when walking up
This commit is contained in:
@@ -28,6 +28,7 @@ import com.intellij.psi.stubs.StubBase;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.templateLanguages.OuterLanguageElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.PairProcessor;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -938,6 +939,24 @@ public class PsiTreeUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean treeWalkUp(@NotNull final PsiElement entrance,
|
||||
@Nullable final PsiElement maxScope,
|
||||
PairProcessor<PsiElement, PsiElement> eachScopeAndLastParent) {
|
||||
PsiElement prevParent = null;
|
||||
PsiElement scope = entrance;
|
||||
|
||||
while (scope != null) {
|
||||
if (!eachScopeAndLastParent.process(scope, prevParent)) return false;
|
||||
|
||||
if (scope == maxScope) break;
|
||||
prevParent = scope;
|
||||
scope = prevParent.getContext();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiElement findPrevParent(@NotNull PsiElement ancestor, @NotNull PsiElement descendant) {
|
||||
PsiElement cur = descendant;
|
||||
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.lang.resolve;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.ResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.PairProcessor;
|
||||
import com.intellij.util.containers.CollectionFactory;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
class DeclarationCacheKey {
|
||||
private static final CachedValueProvider<ConcurrentMap<DeclarationCacheKey, List<DeclarationHolder>>> VALUE_PROVIDER =
|
||||
new CachedValueProvider<ConcurrentMap<DeclarationCacheKey, List<DeclarationHolder>>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result<ConcurrentMap<DeclarationCacheKey, List<DeclarationHolder>>> compute() {
|
||||
ConcurrentMap<DeclarationCacheKey, List<DeclarationHolder>> map = ContainerUtil.newConcurrentMap();
|
||||
return Result.create(map, PsiModificationTracker.MODIFICATION_COUNT);
|
||||
}
|
||||
};
|
||||
@Nullable private final String name;
|
||||
@NotNull private final EnumSet<ClassHint.ResolveKind> kinds;
|
||||
private final boolean nonCode;
|
||||
|
||||
DeclarationCacheKey(@Nullable String name, ClassHint hint, boolean nonCode) {
|
||||
this.name = name;
|
||||
this.kinds = getResolveKinds(hint);
|
||||
this.nonCode = nonCode;
|
||||
}
|
||||
|
||||
private static EnumSet<ClassHint.ResolveKind> getResolveKinds(ClassHint hint) {
|
||||
EnumSet<ClassHint.ResolveKind> set = EnumSet.noneOf(ClassHint.ResolveKind.class);
|
||||
for (ClassHint.ResolveKind kind : ClassHint.ResolveKind.values()) {
|
||||
if (hint.shouldProcess(kind)) {
|
||||
set.add(kind);
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof DeclarationCacheKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DeclarationCacheKey key = (DeclarationCacheKey)o;
|
||||
|
||||
if (nonCode != key.nonCode) {
|
||||
return false;
|
||||
}
|
||||
if (!kinds.equals(key.kinds)) {
|
||||
return false;
|
||||
}
|
||||
if (name != null ? !name.equals(key.name) : key.name != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + kinds.hashCode();
|
||||
result = 31 * result + (nonCode ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DeclarationCacheKey{" +
|
||||
"name='" + name + '\'' +
|
||||
", kinds=" + kinds +
|
||||
", nonCode=" + nonCode +
|
||||
'}';
|
||||
}
|
||||
|
||||
private List<DeclarationHolder> collectDeclarations(final GroovyPsiElement place) {
|
||||
final ArrayList<DeclarationHolder> result = new ArrayList<DeclarationHolder>();
|
||||
PsiTreeUtil.treeWalkUp(place, null, new PairProcessor<PsiElement, PsiElement>() {
|
||||
@Override
|
||||
public boolean process(PsiElement scope, PsiElement lastParent) {
|
||||
result.add(collectScopeDeclarations(scope, lastParent, place));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private DeclarationHolder collectScopeDeclarations(PsiElement scope, PsiElement lastParent, GroovyPsiElement place) {
|
||||
MyCollectProcessor plainCollector = new MyCollectProcessor(scope);
|
||||
MyCollectProcessor nonCodeCollector = new MyCollectProcessor(scope);
|
||||
ResolveUtil.doProcessDeclarations(place, lastParent, scope, plainCollector, nonCode ? nonCodeCollector : null);
|
||||
return new DeclarationHolder(scope, plainCollector.declarations, nonCodeCollector.declarations);
|
||||
}
|
||||
|
||||
private List<DeclarationHolder> getAllDeclarations(GroovyPsiElement place) {
|
||||
ConcurrentMap<DeclarationCacheKey, List<DeclarationHolder>> cache =
|
||||
CachedValuesManager.getManager(place.getProject()).getCachedValue(place, VALUE_PROVIDER);
|
||||
List<DeclarationHolder> declarations = cache.get(this);
|
||||
if (declarations == null) {
|
||||
cache.putIfAbsent(this, declarations = collectDeclarations(place));
|
||||
}
|
||||
return declarations;
|
||||
}
|
||||
|
||||
boolean processCachedDeclarations(GroovyPsiElement place, PsiScopeProcessor processor) {
|
||||
for (DeclarationHolder holder : getAllDeclarations(place)) {
|
||||
if (!holder.processCachedDeclarations(processor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class DeclarationHolder {
|
||||
final PsiElement scope;
|
||||
final List<Pair<PsiElement, ResolveState>> plainDeclarations;
|
||||
final List<Pair<PsiElement, ResolveState>> nonCodeDeclarations;
|
||||
|
||||
private DeclarationHolder(PsiElement scope,
|
||||
List<Pair<PsiElement, ResolveState>> plainDeclarations,
|
||||
List<Pair<PsiElement, ResolveState>> nonCodeDeclarations) {
|
||||
this.scope = scope;
|
||||
this.plainDeclarations = plainDeclarations;
|
||||
this.nonCodeDeclarations = nonCodeDeclarations;
|
||||
}
|
||||
|
||||
boolean processCachedDeclarations(PsiScopeProcessor processor) {
|
||||
PsiScopeProcessor realProcessor = ResolveUtil.substituteProcessor(processor, scope);
|
||||
for (Pair<PsiElement, ResolveState> pair : plainDeclarations) {
|
||||
if (!realProcessor.execute(pair.first, pair.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (Pair<PsiElement, ResolveState> pair : nonCodeDeclarations) {
|
||||
if (!processor.execute(pair.first, pair.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ResolveUtil.issueLevelChangeEvents(processor, scope);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MyCollectProcessor extends ResolverProcessor {
|
||||
final List<Pair<PsiElement, ResolveState>> declarations = CollectionFactory.arrayList();
|
||||
|
||||
public MyCollectProcessor(PsiElement scope) {
|
||||
super(DeclarationCacheKey.this.name, DeclarationCacheKey.this.kinds, scope, PsiType.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, ResolveState state) {
|
||||
declarations.add(Pair.create(element, state));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.psi.scope.JavaScopeProcessorEvent;
|
||||
import com.intellij.psi.scope.NameHint;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.PairProcessor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -86,33 +87,38 @@ public class ResolveUtil {
|
||||
*
|
||||
* @param place - place to start tree walk up
|
||||
* @param processor
|
||||
* @param processNonCodeMethods - this parameter tells us if we need non code members. But non code members are started to process only after we walk up any code block or script
|
||||
* @param processNonCodeMethods - this parameter tells us if we need non code members
|
||||
* @return
|
||||
*/
|
||||
public static boolean treeWalkUp(@NotNull GroovyPsiElement place, PsiScopeProcessor processor, boolean processNonCodeMethods) {
|
||||
PsiElement lastParent = null;
|
||||
PsiElement run = place;
|
||||
|
||||
final Project project = place.getProject();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
|
||||
while (run != null) {
|
||||
if (!run.processDeclarations(substituteProcessor(processor, run), ResolveState.initial(), lastParent, place)) return false;
|
||||
|
||||
if (processNonCodeMethods && !processScopeNonCodeMethods(place, processor, run, factory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
issueLevelChangeEvents(processor, run);
|
||||
|
||||
lastParent = run;
|
||||
run = run.getContext();
|
||||
public static boolean treeWalkUp(@NotNull final GroovyPsiElement place, final PsiScopeProcessor processor, boolean processNonCodeMethods) {
|
||||
ClassHint hint = processor.getHint(ClassHint.KEY);
|
||||
if (hint != null) {
|
||||
return new DeclarationCacheKey(getNameHint(processor), hint, processNonCodeMethods).processCachedDeclarations(place, processor);
|
||||
}
|
||||
|
||||
final PsiScopeProcessor nonCodeProcessor = processNonCodeMethods ? processor : null;
|
||||
return PsiTreeUtil.treeWalkUp(place, null, new PairProcessor<PsiElement, PsiElement>() {
|
||||
@Override
|
||||
public boolean process(PsiElement scope, PsiElement lastParent) {
|
||||
if (!doProcessDeclarations(place, lastParent, scope, substituteProcessor(processor, scope), nonCodeProcessor)) {
|
||||
return false;
|
||||
}
|
||||
issueLevelChangeEvents(processor, scope);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static boolean doProcessDeclarations(GroovyPsiElement place,
|
||||
PsiElement lastParent,
|
||||
PsiElement scope,
|
||||
@NotNull PsiScopeProcessor plainProcessor, @Nullable PsiScopeProcessor nonCodeProcessor) {
|
||||
if (!scope.processDeclarations(plainProcessor, ResolveState.initial(), lastParent, place)) return false;
|
||||
if (nonCodeProcessor != null && !processScopeNonCodeMethods(place, nonCodeProcessor, scope)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void issueLevelChangeEvents(PsiScopeProcessor processor, PsiElement run) {
|
||||
static void issueLevelChangeEvents(PsiScopeProcessor processor, PsiElement run) {
|
||||
processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
|
||||
if (run instanceof GrClosableBlock && GrClosableBlock.OWNER_NAME.equals(getNameHint(processor))) {
|
||||
processor.handleEvent(DECLARATION_SCOPE_PASSED, run);
|
||||
@@ -122,7 +128,7 @@ public class ResolveUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiScopeProcessor substituteProcessor(PsiScopeProcessor processor, PsiElement scope) {
|
||||
static PsiScopeProcessor substituteProcessor(PsiScopeProcessor processor, PsiElement scope) {
|
||||
//hack for walking up in java code
|
||||
//java's processDeclarations don't check names so we should do it manually
|
||||
if (scope.getLanguage() != GroovyFileType.GROOVY_LANGUAGE && processor.getHint(NameHint.KEY) != null) {
|
||||
@@ -131,29 +137,32 @@ public class ResolveUtil {
|
||||
return processor;
|
||||
}
|
||||
|
||||
private static boolean processScopeNonCodeMethods(GroovyPsiElement place,
|
||||
PsiScopeProcessor processor,
|
||||
PsiElement scope,
|
||||
PsiElementFactory factory) {
|
||||
static boolean processScopeNonCodeMethods(GroovyPsiElement place, PsiScopeProcessor processor, PsiElement scope) {
|
||||
if (scope instanceof GrTypeDefinition) {
|
||||
if (!processNonCodeMembers(factory.createType(((GrTypeDefinition)scope)), processor, place, ResolveState.initial())) return false;
|
||||
return processNonCodeMembers(createPsiType((GrTypeDefinition)scope), processor, place, ResolveState.initial());
|
||||
}
|
||||
else if ((scope instanceof GroovyFileBase) && ((GroovyFileBase)scope).isScript()) {
|
||||
|
||||
if (scope instanceof GroovyFileBase && ((GroovyFileBase)scope).isScript()) {
|
||||
final PsiClass psiClass = ((GroovyFileBase)scope).getScriptClass();
|
||||
if (psiClass != null) {
|
||||
if (!processNonCodeMembers(factory.createType(psiClass), processor, place, ResolveState.initial())) return false;
|
||||
}
|
||||
return psiClass == null || processNonCodeMembers(createPsiType(psiClass), processor, place, ResolveState.initial());
|
||||
}
|
||||
else if (scope instanceof GrClosableBlock) {
|
||||
|
||||
if (scope instanceof GrClosableBlock) {
|
||||
PsiClass superClass = getLiteralSuperClass((GrClosableBlock)scope);
|
||||
if (superClass != null && !superClass.processDeclarations(processor, ResolveState.initial(), null, place)) return false;
|
||||
|
||||
if (!GdkMethodUtil.categoryIteration((GrClosableBlock)scope, processor, ResolveState.initial())) return false;
|
||||
if (!GdkMethodUtil.withIteration((GrClosableBlock)scope, processor)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PsiClassType createPsiType(PsiClass psiClass) {
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
|
||||
return factory.createType(psiClass);
|
||||
}
|
||||
|
||||
public static boolean processChildren(PsiElement element,
|
||||
PsiScopeProcessor processor,
|
||||
ResolveState state,
|
||||
|
||||
+32
-4
@@ -163,18 +163,46 @@ class Foo implements Bar {
|
||||
measureHighlighting(text, 200)
|
||||
}
|
||||
|
||||
public void "test using SSA variables in a for loop"() {
|
||||
public void "test using non-reassigned for loop parameters"() {
|
||||
RecursionManager.assertOnRecursionPrevention(testRootDisposable)
|
||||
def text = """
|
||||
def foo(List<File list) {
|
||||
def foo(List<File> list) {
|
||||
for (file in list) {
|
||||
${
|
||||
" println bar(file)\n" * 10
|
||||
" println bar(file)\n" * 20
|
||||
}
|
||||
}
|
||||
}
|
||||
def bar(File file) { file.path }
|
||||
"""
|
||||
measureHighlighting(text, 300)
|
||||
measureHighlighting(text, 2000)
|
||||
}
|
||||
|
||||
public void "test using SSA variables in a for loop"() {
|
||||
//todo RecursionManager.assertOnRecursionPrevention(testRootDisposable)
|
||||
def text = """
|
||||
def foo(List<String> list, SomeClass sc) {
|
||||
List<String> result
|
||||
for (s in list) {
|
||||
${
|
||||
'''
|
||||
bar(s, result)
|
||||
bar2(s, result, sc)
|
||||
bar3(foo:s, bar:result, sc)
|
||||
sc.someMethod(s)
|
||||
''' * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
def bar(String s, List<String> result) { result << s }
|
||||
def bar2(String s, List<String> result) { result << s }
|
||||
def bar2(int s, List<String> result, SomeClass sc) { result << s as String }
|
||||
def bar3(Map args, List<String> result, SomeClass sc) { result << s as String }
|
||||
|
||||
class SomeClass {
|
||||
void someMethod(String s) {}
|
||||
}
|
||||
"""
|
||||
measureHighlighting(text, 1500)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user