use a dedicated file-based index for stub hierarchy

This commit is contained in:
peter
2016-06-10 09:04:34 +02:00
parent df50e33a11
commit d412c4d972
10 changed files with 256 additions and 108 deletions
@@ -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);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* 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.
@@ -13,28 +13,55 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.java.stubs.hierarchy;
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.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
public class JavaStubIndexer {
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
public static Unit translate(int fileId, PsiJavaFileStub javaFileStub) {
@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()) {
@@ -54,7 +81,7 @@ public class JavaStubIndexer {
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 new Unit(fileId, javaFileStub.getPackageName(), type, imports, classes);
return Collections.singletonList(Pair.create(javaFileStub.getPackageName(), new Unit(type, imports, classes)));
}
@Nullable
@@ -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) {
@@ -16,22 +16,19 @@
package com.intellij.psi.impl.java.stubs.index;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.DataInputOutputUtil;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class JavaUnitKeyDescriptor implements KeyDescriptor<IndexTree.Unit> {
public static JavaUnitKeyDescriptor INSTANCE = new JavaUnitKeyDescriptor();
public class JavaUnitDescriptor implements DataExternalizer<IndexTree.Unit> {
public static final JavaUnitDescriptor INSTANCE = new JavaUnitDescriptor();
@Override
public void save(@NotNull DataOutput out, IndexTree.Unit value) throws IOException {
out.writeInt(value.myFileId);
String pidToWrite = value.myPackageId == null ? "" : value.myPackageId;
out.writeUTF(pidToWrite);
out.writeByte(value.myUnitType);
if (value.myUnitType != IndexTree.BYTECODE) {
DataInputOutputUtil.writeINT(out, value.imports.length);
@@ -78,11 +75,6 @@ public class JavaUnitKeyDescriptor implements KeyDescriptor<IndexTree.Unit> {
@Override
public IndexTree.Unit read(@NotNull DataInput in) throws IOException {
int fileId = in.readInt();
String pid = in.readUTF();
if (pid.isEmpty()) {
pid = null;
}
byte type = in.readByte();
IndexTree.Import[] imports = IndexTree.Import.EMPTY_ARRAY;
if (type != IndexTree.BYTECODE) {
@@ -95,7 +87,7 @@ public class JavaUnitKeyDescriptor implements KeyDescriptor<IndexTree.Unit> {
for (int i = 0; i < classes.length; i++) {
classes[i] = readClassDecl(in);
}
return new IndexTree.Unit(fileId, pid, type, imports, classes);
return new IndexTree.Unit(type, imports, classes);
}
private IndexTree.ClassDecl readClassDecl(DataInput in) throws IOException {
@@ -130,13 +122,4 @@ public class JavaUnitKeyDescriptor implements KeyDescriptor<IndexTree.Unit> {
}
}
@Override
public int getHashCode(IndexTree.Unit value) {
return value.hashCode();
}
@Override
public boolean isEqual(IndexTree.Unit val1, IndexTree.Unit val2) {
return val1.equals(val2);
}
}
@@ -0,0 +1,44 @@
/*
* 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;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.util.indexing.FileContent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author peter
*/
public abstract class StubHierarchyIndexer {
public static final ExtensionPointName<StubHierarchyIndexer> EP_NAME = ExtensionPointName.create("com.intellij.hierarchy.indexer");
public abstract int getVersion();
/**
* @return a list of pairs <packageName, compilation unit> for a specified file content
*/
@Nullable
public abstract List<Pair<String, IndexTree.Unit>> indexFile(@NotNull FileContent content);
public abstract boolean handlesFile(@NotNull VirtualFile file);
}
@@ -37,15 +37,11 @@ public class IndexTree {
public static final byte GROOVY = 2;
public static class Unit {
public final int myFileId;
public final String myPackageId;
public final byte myUnitType;
public final Import[] imports;
public final ClassDecl[] myDecls;
public Unit(int fileId, String packageId, byte unitType, Import[] imports, ClassDecl[] decls) {
this.myFileId = fileId;
this.myPackageId = packageId;
public Unit(byte unitType, Import[] imports, ClassDecl[] decls) {
this.myUnitType = unitType;
this.imports = imports;
this.myDecls = decls;
@@ -58,8 +54,6 @@ public class IndexTree {
Unit unit = (Unit)o;
if (myFileId != unit.myFileId) return false;
if (myPackageId != null ? !myPackageId.equals(unit.myPackageId) : unit.myPackageId != null) return false;
if (!Arrays.equals(imports, unit.imports)) return false;
if (!Arrays.equals(myDecls, unit.myDecls)) return false;
@@ -68,11 +62,7 @@ public class IndexTree {
@Override
public int hashCode() {
int result = myFileId;
result = 31 * result + (myPackageId != null ? myPackageId.hashCode() : 0);
result = 31 * result + Arrays.hashCode(imports);
result = 31 * result + Arrays.hashCode(myDecls);
return result;
return Arrays.hashCode(myDecls);
}
}
@@ -133,6 +123,7 @@ public class IndexTree {
public ClassDecl(int stubId, int mods, String name, String[] supers, Decl[] decls) {
super(decls);
assert stubId > 0;
this.myStubId = stubId;
this.myMods = mods;
this.myName = name;
@@ -158,8 +149,6 @@ public class IndexTree {
int result = myStubId;
result = 31 * result + myMods;
result = 31 * result + (myName != null ? myName.hashCode() : 0);
result = 31 * result + Arrays.hashCode(mySupers);
result = 31 * result + Arrays.hashCode(myDecls);
return result;
}
}
@@ -15,25 +15,46 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.stubs.hierarchy;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree.*;
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 org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.psi.stubs.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
public class GrStubIndexer {
public class GrStubIndexer extends StubHierarchyIndexer {
@Override
public int getVersion() {
return 0;
}
@Override
public boolean handlesFile(@NotNull VirtualFile file) {
return file.getFileType() == GroovyFileType.GROOVY_FILE_TYPE;
}
@Nullable
@Override
public List<Pair<String, Unit>> indexFile(@NotNull FileContent content) {
Stub stubTree = StubTreeBuilder.buildStubTree(content);
if (!(stubTree instanceof GrFileStub)) return null;
GrFileStub grFileStub = (GrFileStub)stubTree;
new StubTree(grFileStub, false);
@NotNull
public static Unit translate(int fileId, GrFileStub grFileStub) {
String pid = null;
ArrayList<ClassDecl> classList = new ArrayList<ClassDecl>();
Set<String> usedNames = new HashSet<String>();
@@ -61,8 +82,7 @@ public class GrStubIndexer {
}
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()]);
return new Unit(fileId, pid, IndexTree.GROOVY, imports, classes);
return Collections.singletonList(Pair.create(pid, new Unit(IndexTree.GROOVY, imports, classes)));
}
@Nullable
+2
View File
@@ -573,6 +573,8 @@
<stubIndex implementation="org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrScriptClassNameIndex"/>
<stubIndex implementation="org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrDirectInheritorsIndex"/>
<stubIndex implementation="org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrAnonymousClassIndex"/>
<hierarchy.indexer implementation="org.jetbrains.plugins.groovy.lang.psi.stubs.hierarchy.GrStubIndexer"/>
<referencesSearch implementation="org.jetbrains.plugins.groovy.findUsages.ConstructorReferencesSearcher"/>
<referencesSearch implementation="org.jetbrains.plugins.groovy.findUsages.GrAliasedImportedElementSearcher"/>
+5 -1
View File
@@ -180,6 +180,8 @@
<extensionPoint name="functionalExpressionSearch" interface="com.intellij.util.QueryExecutor"/>
<extensionPoint name="classesWithAnnotatedMembersSearch" interface="com.intellij.util.QueryExecutor"/>
<extensionPoint name="hierarchy.indexer" interface="com.intellij.psi.stubsHierarchy.StubHierarchyIndexer"/>
<extensionPoint name="compiler.inspectionValidator"
interface="com.intellij.openapi.compiler.util.InspectionValidator"
area="IDEA_PROJECT"/>
@@ -1542,7 +1544,6 @@
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaShortClassNameIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaSuperClassNameOccurenceIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaMethodParameterTypesIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaUnitIndex"/>
<fileBasedIndex implementation="com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisIndex"/>
<fileBasedIndex implementation="com.intellij.psi.RefQueueIndex"/>
@@ -1736,6 +1737,9 @@
serviceImplementation="com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis"/>
<projectService serviceInterface="com.intellij.psi.stubsHierarchy.HierarchyService"
serviceImplementation="com.intellij.psi.stubsHierarchy.impl.HierarchyServiceImpl"/>
<fileBasedIndex implementation="com.intellij.psi.stubsHierarchy.impl.StubHierarchyIndex"/>
<hierarchy.indexer implementation="com.intellij.psi.stubsHierarchy.impl.JavaStubIndexer"/>
<spellchecker.support language="JAVA" implementationClass="com.intellij.spellchecker.JavaSpellcheckingStrategy"/>
<codeInsight.template.postfixTemplateProvider language="JAVA"