stub hierarchy: don't process duplicate units

This commit is contained in:
peter
2016-06-08 19:25:52 +02:00
parent 13f042f26d
commit ca91a72a45
2 changed files with 56 additions and 59 deletions
@@ -20,6 +20,8 @@ import com.intellij.openapi.project.Project;
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;
import org.jetbrains.annotations.Nullable;
public class HierarchyService {
private NameEnvironment myNameEnvironment;
@@ -37,11 +39,12 @@ public class HierarchyService {
clear();
}
public void processUnit(IndexTree.Unit unit) {
Unit compUnit = Translator.translate(myNameEnvironment, unit);
if (compUnit != null) {
myStubEnter.unitEnter(compUnit);
}
void processUnit(@NotNull Unit compUnit) {
myStubEnter.unitEnter(compUnit);
}
@Nullable Unit compact(@NotNull IndexTree.Unit unit) {
return Translator.translate(myNameEnvironment, unit);
}
public void connect1() {
@@ -15,7 +15,7 @@
*/
package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
@@ -24,107 +24,101 @@ import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
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.psi.stubsHierarchy.stubs.Unit;
import com.intellij.util.ui.UIUtil;
import gnu.trove.THashSet;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
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;
public static void build(@NotNull Project project) {
ProgressManager progress = ProgressManager.getInstance();
progress.runProcessWithProgressSynchronously(new BuildSingleClassHierarchy(project), "Building Hierarchy", false, project);
}
private static class UnitProcessor implements Processor<IndexTree.Unit> {
private final Project myProject;
private final HierarchyService myHierarchyService;
private final ProjectFileIndex myProjectIndex;
boolean isInSourceMode;
private UnitProcessor(Project project, HierarchyService hierarchyService) {
myProject = project;
myHierarchyService = hierarchyService;
myProjectIndex = ProjectFileIndex.SERVICE.getInstance(myProject);
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 process(IndexTree.Unit unit) {
VirtualFile file = PersistentFS.getInstance().findFileById(unit.myFileId);
if (file == null) {
return true;
}
boolean process = isInSourceMode ? myProjectIndex.isInSourceContent(file) : myProjectIndex.isInLibraryClasses(file);
if (process) {
myHierarchyService.processUnit(unit);
}
return true;
public boolean equals(Unit o1, Unit o2) {
return computeHashCode(o1) == computeHashCode(o2);
}
};
public static void build(@NotNull Project project) {
ProgressManager progress = ProgressManager.getInstance();
progress.runProcessWithProgressSynchronously(() -> ReadAction.run(() -> new BuildSingleClassHierarchy(project).run()),
"Building Hierarchy", false, project);
}
private static class BuildSingleClassHierarchy implements Runnable {
Project myProject;
private ProjectFileIndex myFileIndex;
BuildSingleClassHierarchy(Project project) {
myProject = project;
myFileIndex = ProjectFileIndex.SERVICE.getInstance(myProject);
}
@Override
public void run() {
HierarchyService service = HierarchyService.instance(myProject);
if (service.getSingleClassHierarchy() != null) {
return;
}
service.clear();
LOG.info("BuildHierarchy started");
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
final double classes = 0.3;
final double completeClasses = 0.1;
final double sources = 0.3;
final UnitProcessor processor = new UnitProcessor(myProject, service);
// 1. entering classes
LOG.info("read classes start");
indicator.setText("Reading classes");
ApplicationManager.getApplication().runReadAction(() -> {
StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.UNITS, myProject, processor);
});
indicator.setFraction(classes);
processUnits(service, false);
indicator.setFraction(0.3);
testMemory("0");
// 2. completing classes
LOG.info("complete classes start");
indicator.setText("Completing classes");
service.connect1();
indicator.setFraction(classes + completeClasses);
indicator.setFraction(0.4);
testMemory("1");
// 3. reading sources
LOG.info("read sources start");
indicator.setText("Reading sources");
processor.isInSourceMode = true;
ApplicationManager.getApplication().runReadAction(() -> {
StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.UNITS, myProject, processor);
});
indicator.setFraction(classes + completeClasses + sources);
processUnits(service, true);
indicator.setFraction(0.7);
// 4. completing sources
indicator.setText("Completing sources");
service.complete2();
LOG.info("Complete end");
testMemory("2");
indicator.setFraction(0.9);
// 5. connect subtypes
indicator.setText("Connecting subtypes");
service.connectSubtypes();
LOG.info("Subtypes connected");
indicator.setFraction(1);
}
private void processUnits(HierarchyService service, final boolean sourceMode) {
Set<Unit> result = new THashSet<>(UNIT_HASHING_STRATEGY);
StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.UNITS, myProject, unit -> {
Unit compact = shouldProcess(sourceMode, unit.myFileId) ? service.compact(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);
}
return true;
});
result.forEach(service::processUnit);
}
private boolean shouldProcess(boolean sourceMode, final int fileId) {
VirtualFile file = PersistentFS.getInstance().findFileById(fileId);
if (file == null) {
return false;
}
return sourceMode ? myFileIndex.isInSourceContent(file) : myFileIndex.isInLibraryClasses(file);
}
}