mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-21 14:37:45 +07:00
use a dedicated file-based index for stub hierarchy
This commit is contained in:
@@ -15,39 +15,28 @@
|
||||
*/
|
||||
package com.intellij.psi.stubsHierarchy.impl;
|
||||
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
|
||||
import com.intellij.openapi.vfs.VirtualFileWithId;
|
||||
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.psi.search.DelegatingGlobalSearchScope;
|
||||
import com.intellij.psi.search.EverythingGlobalScope;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.stubsHierarchy.HierarchyService;
|
||||
import com.intellij.psi.stubsHierarchy.stubs.Unit;
|
||||
import com.intellij.psi.util.CachedValue;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.util.CachedValueBase;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class HierarchyServiceImpl extends HierarchyService {
|
||||
private static final TObjectHashingStrategy<Unit> UNIT_HASHING_STRATEGY = new TObjectHashingStrategy<Unit>() {
|
||||
@Override
|
||||
public int computeHashCode(Unit object) {
|
||||
return object.myClasses[0].myClassAnchor.myFileId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Unit o1, Unit o2) {
|
||||
return computeHashCode(o1) == computeHashCode(o2);
|
||||
}
|
||||
};
|
||||
private static final SingleClassHierarchy EMPTY_HIERARCHY = new SingleClassHierarchy(Symbol.ClassSymbol.EMPTY_ARRAY);
|
||||
private final Project myProject;
|
||||
private final ProjectFileIndex myFileIndex;
|
||||
@@ -99,32 +88,35 @@ public class HierarchyServiceImpl extends HierarchyService {
|
||||
private void loadSymbols(NameEnvironment names, Symbols symbols) {
|
||||
StubEnter stubEnter = new StubEnter(names, symbols);
|
||||
|
||||
loadUnits(false, names).forEach(stubEnter::unitEnter);
|
||||
loadUnits(false, names, stubEnter);
|
||||
stubEnter.connect1();
|
||||
|
||||
loadUnits(true, names).forEach(stubEnter::unitEnter);
|
||||
loadUnits(true, names, stubEnter);
|
||||
stubEnter.connect2();
|
||||
}
|
||||
|
||||
private Set<Unit> loadUnits(boolean sourceMode, NameEnvironment names) {
|
||||
Set<Unit> result = new THashSet<>(UNIT_HASHING_STRATEGY);
|
||||
StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.UNITS, myProject, unit -> {
|
||||
Unit compact = shouldProcess(sourceMode, unit.myFileId) ? Translator.translate(names, unit) : null;
|
||||
if (compact != null && compact.myClasses.length > 0) {
|
||||
result.remove(compact); // there can be several (outdated) stub keys for the same file id, only the last one counts
|
||||
result.add(compact);
|
||||
private void loadUnits(boolean sourceMode, NameEnvironment names, StubEnter stubEnter) {
|
||||
GlobalSearchScope scope = new DelegatingGlobalSearchScope(new EverythingGlobalScope(myProject)) {
|
||||
@Override
|
||||
public boolean contains(@NotNull VirtualFile file) {
|
||||
return sourceMode ? myFileIndex.isInSourceContent(file) : myFileIndex.isInLibraryClasses(file);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
|
||||
FileBasedIndex index = FileBasedIndex.getInstance();
|
||||
for (String packageName : index.getAllKeys(StubHierarchyIndex.INDEX_ID, myProject)) {
|
||||
QualifiedName pkg = StringUtil.isEmpty(packageName) ? null : names.fromString(packageName, true);
|
||||
index.processValues(StubHierarchyIndex.INDEX_ID, packageName, null, new FileBasedIndex.ValueProcessor<IndexTree.Unit>() {
|
||||
int count = 0;
|
||||
|
||||
private boolean shouldProcess(boolean sourceMode, final int fileId) {
|
||||
VirtualFile file = PersistentFS.getInstance().findFileById(fileId);
|
||||
if (file == null) {
|
||||
return false;
|
||||
@Override
|
||||
public boolean process(VirtualFile file, IndexTree.Unit unit) {
|
||||
if (indicator != null && ++count % 128 == 0) indicator.checkCanceled();
|
||||
stubEnter.unitEnter(Translator.internNames(names, unit, ((VirtualFileWithId)file).getId(), pkg));
|
||||
return true;
|
||||
}
|
||||
}, scope);
|
||||
}
|
||||
return sourceMode ? myFileIndex.isInSourceContent(file) : myFileIndex.isInLibraryClasses(file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.ide.highlighter.JavaClassFileType;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.psi.PsiNameHelper;
|
||||
import com.intellij.psi.PsiReferenceList;
|
||||
import com.intellij.psi.impl.cache.ModifierFlags;
|
||||
import com.intellij.psi.impl.java.stubs.*;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree.*;
|
||||
import com.intellij.psi.impl.java.stubs.impl.PsiClassStubImpl;
|
||||
import com.intellij.psi.stubs.Stub;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.stubs.StubTree;
|
||||
import com.intellij.psi.stubs.StubTreeBuilder;
|
||||
import com.intellij.psi.stubsHierarchy.StubHierarchyIndexer;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.indexing.FileContent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class JavaStubIndexer extends StubHierarchyIndexer {
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesFile(@NotNull VirtualFile file) {
|
||||
FileType fileType = file.getFileType();
|
||||
return fileType == JavaFileType.INSTANCE || fileType == JavaClassFileType.INSTANCE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Pair<String, Unit>> indexFile(@NotNull FileContent content) {
|
||||
Stub stubTree = StubTreeBuilder.buildStubTree(content);
|
||||
if (!(stubTree instanceof PsiJavaFileStub)) return null;
|
||||
|
||||
PsiJavaFileStub javaFileStub = (PsiJavaFileStub)stubTree;
|
||||
new StubTree(javaFileStub, false);
|
||||
|
||||
ArrayList<ClassDecl> classList = new ArrayList<ClassDecl>();
|
||||
Set<String> usedNames = new HashSet<String>();
|
||||
for (StubElement<?> el : javaFileStub.getChildrenStubs()) {
|
||||
if (el instanceof PsiClassStubImpl) {
|
||||
ClassDecl classDecl = processClassDecl((PsiClassStubImpl<?>)el, usedNames);
|
||||
if (classDecl != null) {
|
||||
classList.add(classDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<Import> importList = new ArrayList<Import>();
|
||||
for (StubElement<?> el : javaFileStub.getChildrenStubs()) {
|
||||
if (el instanceof PsiImportListStub) {
|
||||
processImport((PsiImportListStub) el, importList, usedNames);
|
||||
}
|
||||
}
|
||||
ClassDecl[] classes = classList.isEmpty() ? ClassDecl.EMPTY_ARRAY : classList.toArray(new ClassDecl[classList.size()]);
|
||||
Import[] imports = importList.isEmpty() ? Import.EMPTY_ARRAY : importList.toArray(new Import[importList.size()]);
|
||||
byte type = javaFileStub.isCompiled() ? IndexTree.BYTECODE : IndexTree.JAVA;
|
||||
return Collections.singletonList(Pair.create(javaFileStub.getPackageName(), new Unit(type, imports, classes)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Decl processMember(StubElement<?> el, Set<String> namesCache) {
|
||||
if (el instanceof PsiClassStubImpl) {
|
||||
PsiClassStubImpl classStub = (PsiClassStubImpl)el;
|
||||
if (!classStub.isAnonymousInQualifiedNew()) {
|
||||
return processClassDecl(classStub, namesCache);
|
||||
}
|
||||
}
|
||||
ArrayList<Decl> innerList = new ArrayList<Decl>();
|
||||
for (StubElement childElement : el.getChildrenStubs()) {
|
||||
Decl innerDef = processMember(childElement, namesCache);
|
||||
if (innerDef != null) {
|
||||
innerList.add(innerDef);
|
||||
}
|
||||
}
|
||||
return innerList.isEmpty() ? null : new MemberDecl(innerList.toArray(new Decl[innerList.size()]));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ClassDecl processClassDecl(PsiClassStubImpl<?> classStub, Set<String> namesCache) {
|
||||
ArrayList<String> superList = new ArrayList<String>();
|
||||
ArrayList<Decl> innerList = new ArrayList<Decl>();
|
||||
int accessModifiers = 0;
|
||||
if (classStub.isAnonymous()) {
|
||||
if (classStub.getBaseClassReferenceText() != null) {
|
||||
superList.add(id(classStub.getBaseClassReferenceText(), true, namesCache));
|
||||
}
|
||||
}
|
||||
for (StubElement el : classStub.getChildrenStubs()) {
|
||||
if (el instanceof PsiClassReferenceListStub) {
|
||||
PsiClassReferenceListStub refList = (PsiClassReferenceListStub)el;
|
||||
if (refList.getRole() == PsiReferenceList.Role.EXTENDS_LIST) {
|
||||
String[] extendNames = refList.getReferencedNames();
|
||||
for (String extName : extendNames) {
|
||||
superList.add(id(extName, true, namesCache));
|
||||
}
|
||||
}
|
||||
if (refList.getRole() == PsiReferenceList.Role.IMPLEMENTS_LIST) {
|
||||
String[] implementNames = refList.getReferencedNames();
|
||||
for (String impName : implementNames) {
|
||||
superList.add(id(impName, true, namesCache));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (el instanceof PsiModifierListStub) {
|
||||
accessModifiers = ((PsiModifierListStub)el).getModifiersMask();
|
||||
}
|
||||
Decl member = processMember(el, namesCache);
|
||||
if (member != null) {
|
||||
innerList.add(member);
|
||||
}
|
||||
|
||||
}
|
||||
int flags = translateFlags(classStub, accessModifiers);
|
||||
String[] supers = superList.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : ArrayUtil.toStringArray(superList);
|
||||
Decl[] inners = innerList.isEmpty() ? Decl.EMPTY_ARRAY : innerList.toArray(new Decl[innerList.size()]);
|
||||
return new ClassDecl(classStub.id, flags, classStub.getName(), supers, inners);
|
||||
}
|
||||
|
||||
private static int translateFlags(PsiClassStubImpl<?> classStub, int accessModifiers) {
|
||||
int flags = 0;
|
||||
if (classStub.isInterface()) {
|
||||
flags |= IndexTree.INTERFACE;
|
||||
}
|
||||
if (classStub.isEnum()) {
|
||||
flags |= IndexTree.ENUM;
|
||||
}
|
||||
if (classStub.isAnnotationType()) {
|
||||
flags |= IndexTree.ANNOTATION;
|
||||
}
|
||||
if (ModifierFlags.hasModifierProperty(PsiModifier.STATIC, accessModifiers)) {
|
||||
flags |= IndexTree.STATIC;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static void processImport(PsiImportListStub el, List<Import> imports, Set<String> namesCache) {
|
||||
for (StubElement<?> importElem : el.getChildrenStubs()) {
|
||||
PsiImportStatementStub imp = (PsiImportStatementStub)importElem;
|
||||
String importReferenceText = imp.getImportReferenceText();
|
||||
if (importReferenceText != null) {
|
||||
String fullName = PsiNameHelper.getQualifiedClassName(importReferenceText, true);
|
||||
if (imp.isOnDemand() || namesCache.contains(shortName(fullName))) {
|
||||
imports.add(new Import(fullName, imp.isStatic(), imp.isOnDemand(), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String id(String s, boolean cacheFirstId, Set<String> namesCache) {
|
||||
String id = PsiNameHelper.getQualifiedClassName(s, true);
|
||||
if (cacheFirstId) {
|
||||
int index = id.indexOf('.');
|
||||
String firstId = index > 0 ? s.substring(0, index) : id;
|
||||
namesCache.add(firstId);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private static String shortName(String s) {
|
||||
int dotIndex = s.lastIndexOf('.');
|
||||
return dotIndex > 0 ? s.substring(dotIndex + 1) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaUnitDescriptor;
|
||||
import com.intellij.psi.stubsHierarchy.StubHierarchyIndexer;
|
||||
import com.intellij.util.indexing.*;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class StubHierarchyIndex extends FileBasedIndexExtension<String, IndexTree.Unit> implements PsiDependentIndex {
|
||||
static final ID<String, IndexTree.Unit> INDEX_ID = ID.create("jvm.hierarchy");
|
||||
private static final StubHierarchyIndexer[] ourIndexers = StubHierarchyIndexer.EP_NAME.getExtensions();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ID<String, IndexTree.Unit> getName() {
|
||||
return INDEX_ID;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataIndexer<String, IndexTree.Unit, FileContent> getIndexer() {
|
||||
return inputData -> {
|
||||
for (StubHierarchyIndexer indexer : ourIndexers) {
|
||||
List<Pair<String, IndexTree.Unit>> pairs = indexer.handlesFile(inputData.getFile()) ? indexer.indexFile(inputData) : null;
|
||||
if (pairs != null && !pairs.isEmpty()) {
|
||||
Map<String, IndexTree.Unit> answer = new HashMap<>();
|
||||
for (Pair<String, IndexTree.Unit> entry : pairs) {
|
||||
if (entry.second.myDecls.length > 0) {
|
||||
answer.put(StringUtil.notNullize(entry.first), entry.second);
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyDescriptor<String> getKeyDescriptor() {
|
||||
return EnumeratorStringDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataExternalizer<IndexTree.Unit> getValueExternalizer() {
|
||||
return JavaUnitDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return IndexTree.STUB_HIERARCHY_ENABLED ? 1 + Arrays.stream(ourIndexers).mapToInt(StubHierarchyIndexer::getVersion).sum() : 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileBasedIndex.InputFilter getInputFilter() {
|
||||
return file -> IndexTree.STUB_HIERARCHY_ENABLED && Arrays.stream(ourIndexers).anyMatch(indexer -> indexer.handlesFile(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dependsOnFileContent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import com.intellij.psi.PsiNameHelper;
|
||||
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
|
||||
import com.intellij.psi.stubsHierarchy.stubs.*;
|
||||
import com.intellij.util.BitUtil;
|
||||
import gnu.trove.TLongArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -75,24 +75,19 @@ public class Translator {
|
||||
return Import.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public static Unit translate(NameEnvironment nameEnvironment, IndexTree.Unit unit) {
|
||||
if (unit.myDecls.length == 0) {
|
||||
return null;
|
||||
}
|
||||
QualifiedName pid = StringUtil.isEmpty(unit.myPackageId) ? null : nameEnvironment.fromString(unit.myPackageId, true);
|
||||
ArrayList<ClassDeclaration> classesList = new ArrayList<ClassDeclaration>();
|
||||
for (IndexTree.ClassDecl def : unit.myDecls) {
|
||||
ClassDeclaration classDecl = processClassDecl(nameEnvironment, unit.myFileId, def);
|
||||
classesList.add(classDecl);
|
||||
}
|
||||
TLongArrayList importList = new TLongArrayList();
|
||||
for (IndexTree.Import anImport : unit.imports) {
|
||||
importList.add(processImport(nameEnvironment, anImport));
|
||||
@NotNull
|
||||
public static Unit internNames(NameEnvironment nameEnvironment, IndexTree.Unit unit, int fileId, QualifiedName pkg) {
|
||||
ClassDeclaration[] classes = new ClassDeclaration[unit.myDecls.length];
|
||||
for (int i = 0; i < unit.myDecls.length; i++) {
|
||||
classes[i] = processClassDecl(nameEnvironment, fileId, unit.myDecls[i]);
|
||||
}
|
||||
|
||||
long[] imports = importList.isEmpty() ? Import.EMPTY_ARRAY : importList.toNativeArray();
|
||||
ClassDeclaration[] classes = classesList.toArray(new ClassDeclaration[classesList.size()]);
|
||||
return new Unit(pid, imports, classes, unit.myUnitType);
|
||||
long[] imports = unit.imports.length == 0 ? Import.EMPTY_ARRAY : new long[unit.imports.length];
|
||||
for (int i = 0; i < unit.imports.length; i++) {
|
||||
imports[i] = processImport(nameEnvironment, unit.imports[i]);
|
||||
}
|
||||
|
||||
return new Unit(pkg, imports, classes, unit.myUnitType);
|
||||
}
|
||||
|
||||
private static ClassDeclaration processClassDecl(NameEnvironment nameEnvironment, int fileId, IndexTree.ClassDecl def) {
|
||||
|
||||
Reference in New Issue
Block a user