mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
root index: compute dependent unloaded modules
We need to show a warning about possible incomplete results if there is an unloaded module which depends on a module from the project and e.g. 'Find Usages' is invoked on an element from such module. In order to implement that we need to quickly determine if there exists an unloading module which depends on a given loaded module.
This commit is contained in:
@@ -17,6 +17,7 @@ package com.intellij.openapi.roots.impl;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.roots.DependencyScope;
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -43,6 +44,21 @@ public class DirectoryIndexForUnloadedModuleTest extends DirectoryIndexTestCase
|
||||
assertFromUnloadedModule(contentRoot, "unloaded");
|
||||
}
|
||||
|
||||
public void testDependentUnloadedModules() {
|
||||
Module unloadedModule = createModule("unloaded");
|
||||
Module main = createModule("main");
|
||||
Module util = createModule("util");
|
||||
Module common = createModule("common");
|
||||
ModuleRootModificationUtil.addDependency(unloadedModule, main);
|
||||
ModuleRootModificationUtil.addDependency(main, util);
|
||||
ModuleRootModificationUtil.addDependency(main, common, DependencyScope.COMPILE, true);
|
||||
ModuleManager.getInstance(myProject).setUnloadedModules(Arrays.asList("unloaded"));
|
||||
|
||||
assertSameElements(myIndex.getDependentUnloadedModules(main), "unloaded");
|
||||
assertEmpty(myIndex.getDependentUnloadedModules(util));
|
||||
assertSameElements(myIndex.getDependentUnloadedModules(common), "unloaded");
|
||||
}
|
||||
|
||||
private void assertFromUnloadedModule(VirtualFile file, String moduleName) {
|
||||
DirectoryInfo info = myIndex.getInfoForFile(file);
|
||||
assertTrue(info.toString(), info.isExcluded(file));
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileTypes.FileTypeEvent;
|
||||
import com.intellij.openapi.fileTypes.FileTypeListener;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootEvent;
|
||||
@@ -42,6 +43,7 @@ import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class DirectoryIndexImpl extends DirectoryIndex {
|
||||
private static final Logger LOG = Logger.getInstance(DirectoryIndexImpl.class);
|
||||
@@ -170,6 +172,13 @@ public class DirectoryIndexImpl extends DirectoryIndex {
|
||||
return getRootIndex().getOrderEntries(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Set<String> getDependentUnloadedModules(@NotNull Module module) {
|
||||
checkAvailability();
|
||||
return getRootIndex().getDependentUnloadedModules(module);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void assertConsistency(DirectoryInfo info) {
|
||||
List<OrderEntry> entries = getOrderEntries(info);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.intellij.openapi.roots.impl;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -26,6 +27,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is internal low-level API. Consider using {@link com.intellij.openapi.roots.ProjectFileIndex} instead of using this class directly.
|
||||
@@ -67,4 +69,10 @@ public abstract class DirectoryIndex {
|
||||
|
||||
@NotNull
|
||||
public abstract List<OrderEntry> getOrderEntries(@NotNull DirectoryInfo info);
|
||||
|
||||
/**
|
||||
* @return names of unloaded modules which directly or transitively via exported dependencies depend on the specified module
|
||||
*/
|
||||
@NotNull
|
||||
public abstract Set<String> getDependentUnloadedModules(@NotNull Module module);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.fileTypes.FileNameMatcherEx;
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry;
|
||||
import com.intellij.openapi.fileTypes.impl.FileTypeAssocTable;
|
||||
import com.intellij.openapi.module.UnloadedModuleDescription;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.module.UnloadedModuleDescription;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
|
||||
@@ -264,6 +264,7 @@ public class RootIndex {
|
||||
private static class Node {
|
||||
Module myKey;
|
||||
List<Edge> myEdges = new ArrayList<>();
|
||||
Set<String> myUnloadedDependentModules;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -281,6 +282,7 @@ public class RootIndex {
|
||||
Graph myGraph;
|
||||
MultiMap<VirtualFile, Node> myRoots; // Map of roots to their root nodes, eg. library jar -> library node
|
||||
final SynchronizedSLRUCache<VirtualFile, List<OrderEntry>> myCache;
|
||||
final SynchronizedSLRUCache<Module, Set<String>> myDependentUnloadedModulesCache;
|
||||
private MultiMap<VirtualFile, OrderEntry> myLibClassRootEntries;
|
||||
private MultiMap<VirtualFile, OrderEntry> myLibSourceRootEntries;
|
||||
|
||||
@@ -296,6 +298,14 @@ public class RootIndex {
|
||||
return collectOrderEntries(key);
|
||||
}
|
||||
};
|
||||
int dependentUnloadedModulesCacheSize = ModuleManager.getInstance(project).getModules().length / 2;
|
||||
myDependentUnloadedModulesCache = new SynchronizedSLRUCache<Module, Set<String>>(dependentUnloadedModulesCacheSize, dependentUnloadedModulesCacheSize) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> createValue(Module key) {
|
||||
return collectDependentUnloadedModules(key);
|
||||
}
|
||||
};
|
||||
initGraph();
|
||||
initLibraryRoots();
|
||||
}
|
||||
@@ -305,7 +315,8 @@ public class RootIndex {
|
||||
|
||||
MultiMap<VirtualFile, Node> roots = MultiMap.createSmart();
|
||||
|
||||
for (final Module module : ModuleManager.getInstance(myProject).getModules()) {
|
||||
ModuleManager moduleManager = ModuleManager.getInstance(myProject);
|
||||
for (final Module module : moduleManager.getModules()) {
|
||||
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
|
||||
List<OrderEnumerationHandler> handlers = OrderEnumeratorBase.getCustomHandlers(module);
|
||||
for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) {
|
||||
@@ -336,6 +347,23 @@ public class RootIndex {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (UnloadedModuleDescription description : moduleManager.getUnloadedModuleDescriptions()) {
|
||||
for (String depName : description.getDependencyModuleNames()) {
|
||||
Module depModule = moduleManager.findModuleByName(depName);
|
||||
if (depModule != null) {
|
||||
Node node = graph.myNodes.get(depModule);
|
||||
if (node == null) {
|
||||
node = new Node();
|
||||
node.myKey = depModule;
|
||||
graph.myNodes.put(depModule, node);
|
||||
}
|
||||
if (node.myUnloadedDependentModules == null) {
|
||||
node.myUnloadedDependentModules = new LinkedHashSet<>();
|
||||
}
|
||||
node.myUnloadedDependentModules.add(description.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myGraph = graph;
|
||||
myRoots = roots;
|
||||
@@ -419,8 +447,48 @@ public class RootIndex {
|
||||
Collections.sort(result, BY_OWNER_MODULE);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Set<String> getDependentUnloadedModules(@NotNull Module module) {
|
||||
return myDependentUnloadedModulesCache.get(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return names of unloaded modules which directly or transitively via exported dependencies depend on the specified module
|
||||
*/
|
||||
private Set<String> collectDependentUnloadedModules(@NotNull Module module) {
|
||||
ArrayDeque<OrderEntryGraph.Node> stack = new ArrayDeque<>();
|
||||
Node start = myGraph.myNodes.get(module);
|
||||
if (start == null) return Collections.emptySet();
|
||||
stack.push(start);
|
||||
Set<Node> seen = new HashSet<>();
|
||||
Set<String> result = null;
|
||||
while (!stack.isEmpty()) {
|
||||
Node node = stack.pop();
|
||||
if (!seen.add(node)) {
|
||||
continue;
|
||||
}
|
||||
if (node.myUnloadedDependentModules != null) {
|
||||
if (result == null) {
|
||||
result = new LinkedHashSet<>(node.myUnloadedDependentModules);
|
||||
}
|
||||
else {
|
||||
result.addAll(node.myUnloadedDependentModules);
|
||||
}
|
||||
}
|
||||
for (Edge edge : node.myEdges) {
|
||||
if (edge.myRecursive) {
|
||||
Node targetNode = myGraph.myNodes.get(edge.myKey);
|
||||
if (targetNode != null) {
|
||||
stack.push(targetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result != null ? result : Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int getRootTypeId(@NotNull JpsModuleSourceRootType<?> rootType) {
|
||||
if (myRootTypeId.containsKey(rootType)) {
|
||||
return myRootTypeId.get(rootType);
|
||||
@@ -862,6 +930,11 @@ public class RootIndex {
|
||||
return getOrderEntryGraph().getOrderEntries(((DirectoryInfoImpl)info).getRoot());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<String> getDependentUnloadedModules(@NotNull Module module) {
|
||||
return getOrderEntryGraph().getDependentUnloadedModules(module);
|
||||
}
|
||||
|
||||
public interface InfoCache {
|
||||
@Nullable
|
||||
DirectoryInfo getCachedInfo(@NotNull VirtualFile dir);
|
||||
|
||||
Reference in New Issue
Block a user