mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
don't use PSI in stub hierarchy: it's expensive (especially if it's Kotlin)
This commit is contained in:
@@ -17,16 +17,12 @@ package com.intellij.psi.stubsHierarchy.impl;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiClassOwner;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.psi.stubsHierarchy.stubs.Unit;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class HierarchyService {
|
||||
|
||||
public static boolean PROCESS_PSI = true;
|
||||
public static boolean IGNORE_LOCAL_CLASSES = true;
|
||||
public static final boolean IGNORE_LOCAL_CLASSES = true;
|
||||
|
||||
private NameEnvironment myNameEnvironment;
|
||||
private SoftReference<NameEnvironment> myNamesCache = new SoftReference<NameEnvironment>(null);
|
||||
@@ -34,7 +30,6 @@ public class HierarchyService {
|
||||
private SingleClassHierarchy mySingleClassHierarchy;
|
||||
private Symbols mySymbols;
|
||||
private StubEnter myStubEnter;
|
||||
private PsiEnter myPsiEnter;
|
||||
|
||||
public static HierarchyService instance(Project project) {
|
||||
return ServiceManager.getService(project, HierarchyService.class);
|
||||
@@ -51,20 +46,13 @@ public class HierarchyService {
|
||||
}
|
||||
}
|
||||
|
||||
public void processPsiClassOwner(@NotNull PsiClassOwner psiClassOwner) {
|
||||
myPsiEnter.enter(psiClassOwner);
|
||||
}
|
||||
|
||||
public void connect1() {
|
||||
myStubEnter.connect1();
|
||||
}
|
||||
|
||||
public void complete2() {
|
||||
myStubEnter.connect2();
|
||||
myPsiEnter.connect();
|
||||
|
||||
myStubEnter = null;
|
||||
myPsiEnter = null;
|
||||
}
|
||||
|
||||
public void connectSubtypes() {
|
||||
@@ -84,7 +72,6 @@ public class HierarchyService {
|
||||
}
|
||||
mySymbols = new Symbols(myNameEnvironment);
|
||||
myStubEnter = new StubEnter(myNameEnvironment, mySymbols);
|
||||
myPsiEnter = new PsiEnter(myNameEnvironment, mySymbols);
|
||||
mySingleClassHierarchy = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.psi.stubsHierarchy.impl;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiClassOwner;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.intellij.psi.stubsHierarchy.impl.Symbol.*;
|
||||
|
||||
public class PsiEnter {
|
||||
private final Symbols mySymbols;
|
||||
private final NameEnvironment myNameEnvironment;
|
||||
private final PsiHierachyConnector myPsiHierachyConnector;
|
||||
private ArrayList<ClassSymbol> myQueue = new ArrayList<ClassSymbol>();
|
||||
|
||||
|
||||
public PsiEnter(NameEnvironment nameEnvironment, Symbols symbols) {
|
||||
this.myNameEnvironment = nameEnvironment;
|
||||
this.mySymbols = symbols;
|
||||
myPsiHierachyConnector = new PsiHierachyConnector(nameEnvironment, symbols);
|
||||
}
|
||||
|
||||
void enter(final PsiClassOwner psiClassOwner) {
|
||||
ApplicationManager.getApplication().runReadAction(() -> enterTopLevels(psiClassOwner));
|
||||
}
|
||||
|
||||
private void enterTopLevels(PsiClassOwner owner) {
|
||||
String pkgName = owner.getPackageName();
|
||||
PackageSymbol pkg = StringUtil.isEmpty(pkgName) ?
|
||||
mySymbols.myRootPackage : mySymbols.enterPackage(myNameEnvironment.fromString(pkgName, true));
|
||||
for (PsiClass psiClass : owner.getClasses()) {
|
||||
enter(psiClass, pkg);
|
||||
}
|
||||
}
|
||||
|
||||
private ClassSymbol enter(PsiClass psiClass, Symbol owner) {
|
||||
if (HierarchyService.IGNORE_LOCAL_CLASSES && psiClass.getName() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int psiFlags = translateFlags(psiClass);
|
||||
int fileId = FileBasedIndex.getFileId(psiClass.getContainingFile().getVirtualFile());
|
||||
ClassAnchor.DirectClassAnchor classAnchor = new ClassAnchor.DirectClassAnchor(fileId, psiClass);
|
||||
int shortName = myNameEnvironment.simpleName(psiClass.getName(), true);
|
||||
ClassSymbol classSymbol = mySymbols.enterClass(classAnchor, psiFlags, shortName, owner, null, null, myPsiHierachyConnector);
|
||||
|
||||
if (myQueue != null) {
|
||||
myQueue.add(classSymbol);
|
||||
}
|
||||
|
||||
ClassSymbol[] members = enter(psiClass.getInnerClasses(), classSymbol);
|
||||
classSymbol.setMembers(members);
|
||||
return classSymbol;
|
||||
}
|
||||
|
||||
private ClassSymbol[] enter(PsiClass[] psiClasses, Symbol owner) {
|
||||
ClassSymbol[] members = new ClassSymbol[psiClasses.length];
|
||||
int i = 0;
|
||||
for (PsiClass psiClass : psiClasses) {
|
||||
ClassSymbol member = enter(psiClass, owner);
|
||||
if (member != null && member.myShortName != 0) {
|
||||
members[i++] = member;
|
||||
}
|
||||
}
|
||||
members = members.length == 0 ? ClassSymbol.EMPTY_ARRAY : Arrays.copyOf(members, i);
|
||||
Arrays.sort(members, CLASS_SYMBOL_BY_NAME_COMPARATOR);
|
||||
return members;
|
||||
}
|
||||
|
||||
public static int translateFlags(PsiClass psiClass) {
|
||||
int flags = 0;
|
||||
if (psiClass.isInterface()) {
|
||||
flags |= IndexTree.INTERFACE;
|
||||
}
|
||||
if (psiClass.isEnum()) {
|
||||
flags |= IndexTree.ENUM;
|
||||
}
|
||||
if (psiClass.isAnnotationType()) {
|
||||
flags |= IndexTree.ANNOTATION;
|
||||
}
|
||||
if (psiClass.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
flags |= IndexTree.STATIC;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
for (ClassSymbol classSymbol : myQueue) {
|
||||
classSymbol.connect();
|
||||
}
|
||||
myQueue = null;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.psi.stubsHierarchy.impl;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.psi.PsiClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PsiHierachyConnector implements HierarchyConnector {
|
||||
|
||||
private final NameEnvironment myNameEnvironment;
|
||||
private final Symbols mySymbols;
|
||||
|
||||
protected PsiHierachyConnector(NameEnvironment nameEnvironment, Symbols symbols) {
|
||||
this.myNameEnvironment = nameEnvironment;
|
||||
this.mySymbols = symbols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(final Symbol sym) {
|
||||
ApplicationManager.getApplication().runReadAction(() -> connectInternal((Symbol.ClassSymbol)sym));
|
||||
}
|
||||
|
||||
private void connectInternal(Symbol.ClassSymbol sym) {
|
||||
if (sym.myOwner instanceof Symbol.ClassSymbol) {
|
||||
((Symbol.ClassSymbol)sym.myOwner).connect();
|
||||
}
|
||||
sym.mySuperClasses = getSuperTypes(sym.myClassAnchor);
|
||||
}
|
||||
|
||||
private Symbol.ClassSymbol[] asSymbol(PsiClass psiClass) {
|
||||
if (psiClass == null) {
|
||||
return Symbol.ClassSymbol.EMPTY_ARRAY;
|
||||
}
|
||||
String qualifiedName = psiClass.getQualifiedName();
|
||||
if (qualifiedName != null) {
|
||||
QualifiedName n = myNameEnvironment.fromString(qualifiedName, false);
|
||||
if (n != null) {
|
||||
return mySymbols.loadClass(n);
|
||||
}
|
||||
}
|
||||
return Symbol.ClassSymbol.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
Symbol.ClassSymbol[] getSuperTypes(SmartClassAnchor anchor) {
|
||||
PsiClass psiClass = ((SmartClassAnchor.DirectSmartClassAnchor)anchor).myPsiClass;
|
||||
PsiClass[] psiInterfaces = psiClass.getInterfaces();
|
||||
ArrayList<Symbol.ClassSymbol> superList = new ArrayList<Symbol.ClassSymbol>();
|
||||
for (PsiClass psiInterface : psiInterfaces) {
|
||||
for (Symbol.ClassSymbol cs : asSymbol(psiInterface)) {
|
||||
if (cs.myQualifiedName != myNameEnvironment.java_lang_Object) {
|
||||
superList.add(cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PsiClass psiSuperClass = psiClass.getSuperClass();
|
||||
for (Symbol.ClassSymbol cs : asSymbol(psiSuperClass)) {
|
||||
if (cs.myQualifiedName != myNameEnvironment.java_lang_Object) {
|
||||
superList.add(cs);
|
||||
}
|
||||
}
|
||||
|
||||
if (superList.isEmpty()) {
|
||||
return Symbol.ClassSymbol.EMPTY_ARRAY;
|
||||
}
|
||||
return superList.isEmpty() ? Symbol.ClassSymbol.EMPTY_ARRAY : superList.toArray(new Symbol.ClassSymbol[superList.size()]);
|
||||
}
|
||||
}
|
||||
+4
-62
@@ -20,27 +20,17 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ContentIterator;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.ui.MessageDialogBuilder;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileWithId;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
|
||||
import com.intellij.psi.PsiClassOwner;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
|
||||
import com.intellij.psi.stubs.StubIndex;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class SingleClassHierarchyBuilder {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubsHierarchy.HierarchyBuilder");
|
||||
private static final boolean TEST_MEMORY_USAGE = false;
|
||||
@@ -53,7 +43,6 @@ public class SingleClassHierarchyBuilder {
|
||||
private static class UnitProcessor implements Processor<IndexTree.Unit> {
|
||||
private final Project myProject;
|
||||
private final HierarchyService myHierarchyService;
|
||||
protected final BitSet myProcessedSet = new BitSet();
|
||||
private final ProjectFileIndex myProjectIndex;
|
||||
boolean isInSourceMode;
|
||||
|
||||
@@ -72,7 +61,6 @@ public class SingleClassHierarchyBuilder {
|
||||
boolean process = isInSourceMode ? myProjectIndex.isInSourceContent(file) : myProjectIndex.isInLibraryClasses(file);
|
||||
if (process) {
|
||||
myHierarchyService.processUnit(unit);
|
||||
myProcessedSet.set(unit.myFileId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -97,7 +85,6 @@ public class SingleClassHierarchyBuilder {
|
||||
final double classes = 0.3;
|
||||
final double completeClasses = 0.1;
|
||||
final double sources = 0.3;
|
||||
final double collectPsiFilesFraction = 0.2;
|
||||
|
||||
final UnitProcessor processor = new UnitProcessor(myProject, service);
|
||||
|
||||
@@ -126,64 +113,19 @@ public class SingleClassHierarchyBuilder {
|
||||
});
|
||||
indicator.setFraction(classes + completeClasses + sources);
|
||||
|
||||
|
||||
// 4. reading PSI
|
||||
LOG.info("read PSI start");
|
||||
indicator.setText("Collecting PSI Files");
|
||||
final Set<VirtualFile> srcSet = new HashSet<VirtualFile>();
|
||||
collectFiles(srcSet, processor.myProcessedSet);
|
||||
|
||||
double total = srcSet.size();
|
||||
LOG.info("Processing PSI");
|
||||
indicator.setText("Processing PSI Files");
|
||||
|
||||
int loadedCound = 0;
|
||||
for (final VirtualFile vFile : srcSet) {
|
||||
String presentableUrl = vFile.getPresentableUrl();
|
||||
if (HierarchyService.PROCESS_PSI) {
|
||||
PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
|
||||
@Override
|
||||
public PsiFile compute() {
|
||||
return PsiManager.getInstance(myProject).findFile(vFile);
|
||||
}
|
||||
});
|
||||
if (psiFile instanceof PsiClassOwner) {
|
||||
service.processPsiClassOwner((PsiClassOwner)psiFile);
|
||||
LOG.info("PSI: " + presentableUrl);
|
||||
}
|
||||
}
|
||||
loadedCound++;
|
||||
indicator.setFraction(classes + completeClasses + sources + collectPsiFilesFraction *(loadedCound / total));
|
||||
}
|
||||
testMemory("2");
|
||||
|
||||
// 5. completing sources + PSI
|
||||
indicator.setText("Completing sources + PSI");
|
||||
// 4. completing sources
|
||||
indicator.setText("Completing sources");
|
||||
service.complete2();
|
||||
LOG.info("Complete end");
|
||||
testMemory("3");
|
||||
testMemory("2");
|
||||
indicator.setFraction(0.9);
|
||||
|
||||
// 6. connect subtypes
|
||||
// 5. connect subtypes
|
||||
indicator.setText("Connecting subtypes");
|
||||
service.connectSubtypes();
|
||||
LOG.info("Subtypes connected");
|
||||
indicator.setFraction(1);
|
||||
}
|
||||
|
||||
private void collectFiles(final Set<VirtualFile> srcSet, final BitSet processed) {
|
||||
final ProjectFileIndex projectIndex = ProjectFileIndex.SERVICE.getInstance(myProject);
|
||||
projectIndex.iterateContent(new ContentIterator() {
|
||||
@Override
|
||||
public boolean processFile(VirtualFile fileOrDir) {
|
||||
int fileId = ((VirtualFileWithId)fileOrDir).getId();
|
||||
if (!processed.get(fileId) && projectIndex.isInSourceContent(fileOrDir)) {
|
||||
srcSet.add(fileOrDir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void testMemory(final String msg) {
|
||||
|
||||
Reference in New Issue
Block a user