diff --git a/java/java-tests/testData/moduleRootManager/unloadedModules/.idea/modules.xml b/java/java-tests/testData/moduleRootManager/unloadedModules/.idea/modules.xml
new file mode 100644
index 000000000000..f7490c160221
--- /dev/null
+++ b/java/java-tests/testData/moduleRootManager/unloadedModules/.idea/modules.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/java/java-tests/testData/moduleRootManager/unloadedModules/.idea/workspace.xml b/java/java-tests/testData/moduleRootManager/unloadedModules/.idea/workspace.xml
new file mode 100644
index 000000000000..cc4eb0e03b1f
--- /dev/null
+++ b/java/java-tests/testData/moduleRootManager/unloadedModules/.idea/workspace.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/java/java-tests/testData/moduleRootManager/unloadedModules/dep.iml b/java/java-tests/testData/moduleRootManager/unloadedModules/dep.iml
new file mode 100644
index 000000000000..92baf1cafcc8
--- /dev/null
+++ b/java/java-tests/testData/moduleRootManager/unloadedModules/dep.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/java/java-tests/testData/moduleRootManager/unloadedModules/main.iml b/java/java-tests/testData/moduleRootManager/unloadedModules/main.iml
new file mode 100644
index 000000000000..feb23b47ceff
--- /dev/null
+++ b/java/java-tests/testData/moduleRootManager/unloadedModules/main.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/java/java-tests/testData/moduleRootManager/unloadedModules/util.iml b/java/java-tests/testData/moduleRootManager/unloadedModules/util.iml
new file mode 100644
index 000000000000..fefaba3e3a7b
--- /dev/null
+++ b/java/java-tests/testData/moduleRootManager/unloadedModules/util.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/java/java-tests/testSrc/com/intellij/roots/UnloadedModulesConfigurationTest.kt b/java/java-tests/testSrc/com/intellij/roots/UnloadedModulesConfigurationTest.kt
new file mode 100644
index 000000000000..34ee06d9369f
--- /dev/null
+++ b/java/java-tests/testSrc/com/intellij/roots/UnloadedModulesConfigurationTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2000-2017 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.roots
+
+import com.intellij.openapi.application.ex.PathManagerEx
+import com.intellij.openapi.module.ModuleManager
+import com.intellij.openapi.project.ex.ProjectManagerEx
+import com.intellij.openapi.project.impl.ProjectManagerImpl
+import com.intellij.openapi.roots.ModuleRootModificationUtil
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.openapi.vfs.VfsUtilCore
+import com.intellij.testFramework.ModuleTestCase
+import java.io.File
+
+/**
+ * @author nik
+ */
+class UnloadedModulesConfigurationTest : ModuleTestCase() {
+ fun `test load project`() {
+ val projectPath = File(PathManagerEx.getTestDataPath(), "moduleRootManager/unloadedModules").absolutePath
+ val project = getProjectManager().loadAndOpenProject(projectPath)!!
+ try {
+ val moduleManager = ModuleManager.getInstance(project)
+ assertEquals(3, moduleManager.allModuleDescriptions.size)
+ assertEquals(2, moduleManager.unloadedModuleDescriptions.size)
+
+ val util = moduleManager.unloadedModuleDescriptions.find { it.name == "util" }!!
+ val projectDirUrl = VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(projectPath))
+ assertEquals("$projectDirUrl/util", assertOneElement(util.contentRoots).url)
+ assertEmpty(util.dependencyModuleNames)
+
+ val dep = moduleManager.unloadedModuleDescriptions.find { it.name == "dep" }!!
+ assertEquals("$projectDirUrl/dep", assertOneElement(dep.contentRoots).url)
+ assertEquals("util", assertOneElement(dep.dependencyModuleNames))
+ }
+ finally {
+ getProjectManager().forceCloseProject(project, true)
+ }
+ }
+
+ fun `test set unloaded modules`() {
+ val a = createModule("a")
+ val b = createModule("b")
+ ModuleRootModificationUtil.addDependency(a, b)
+ val moduleManager = ModuleManager.getInstance(project)
+ moduleManager.setUnloadedModules(listOf("a"))
+ assertEquals("a", assertOneElement(moduleManager.unloadedModuleDescriptions).name)
+ assertNull(moduleManager.findModuleByName("a"))
+ assertNotNull(moduleManager.findModuleByName("b"))
+
+ moduleManager.setUnloadedModules(listOf("b"))
+ assertEquals("b", assertOneElement(moduleManager.unloadedModuleDescriptions).name)
+ assertNotNull(moduleManager.findModuleByName("a"))
+ assertNull(moduleManager.findModuleByName("b"))
+ }
+
+ private fun getProjectManager() = ProjectManagerEx.getInstanceEx() as ProjectManagerImpl
+}
\ No newline at end of file
diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java
index 5ab57a25f8b0..94b5e9a96d8c 100644
--- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java
+++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java
@@ -23,7 +23,9 @@ import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.JpsElementChildRole;
+import org.jetbrains.jps.model.JpsElementFactory;
import org.jetbrains.jps.model.JpsGlobal;
+import org.jetbrains.jps.model.JpsModel;
import org.jetbrains.jps.model.ex.JpsElementChildRoleBase;
import org.jetbrains.jps.model.serialization.impl.JpsPathVariablesConfigurationImpl;
import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer;
@@ -55,11 +57,21 @@ public class JpsGlobalLoader extends JpsLoaderBase {
public static void loadGlobalSettings(JpsGlobal global, String optionsPath) throws IOException {
Path optionsDir = Paths.get(FileUtil.toCanonicalPath(optionsPath));
- new JpsGlobalLoader(global, Collections.emptyMap()).loadGlobalComponents(optionsDir, optionsDir.resolve("other.xml"), new PathVariablesSerializer());
- Map pathVariables = JpsModelSerializationDataService.computeAllPathVariables(global);
+ Map pathVariables = loadPathVariables(global, optionsDir);
new JpsGlobalLoader(global, pathVariables).load(optionsDir);
}
+ private static Map loadPathVariables(JpsGlobal global, Path optionsDir) {
+ new JpsGlobalLoader(global, Collections.emptyMap()).loadGlobalComponents(optionsDir, optionsDir.resolve("other.xml"), new PathVariablesSerializer());
+ return JpsModelSerializationDataService.computeAllPathVariables(global);
+ }
+
+ public static Map computeAllPathVariables(@NotNull String optionsPath) {
+ JpsModel model = JpsElementFactory.getInstance().createModel();
+ Path optionsDir = Paths.get(FileUtil.toCanonicalPath(optionsPath));
+ return loadPathVariables(model.getGlobal(), optionsDir);
+ }
+
/**
* @deprecated use {@link JpsModelSerializationDataService#getPathVariableValue(org.jetbrains.jps.model.JpsGlobal, String)} instead
*/
diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java
index 63fbcdccc34e..12d52fe9000e 100644
--- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java
+++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java
@@ -124,7 +124,8 @@ public class JpsProjectLoader extends JpsLoaderBase {
loadComponents(dir, defaultConfigFile, serializer, myProject);
}
}
- loadModules(loadRootElement(dir.resolve("modules.xml")), projectSdkType);
+ Path workspaceFile = dir.resolve("workspace.xml");
+ loadModules(loadRootElement(dir.resolve("modules.xml")), projectSdkType, workspaceFile);
Runnable timingLog = TimingLog.startActivity("loading project libraries");
for (Path libraryFile : listXmlFiles(dir.resolve("libraries"))) {
@@ -150,7 +151,7 @@ public class JpsProjectLoader extends JpsLoaderBase {
for (Path configurationFile : listXmlFiles(dir.resolve("runConfigurations"))) {
JpsRunConfigurationSerializer.loadRunConfigurations(myProject, loadRootElement(configurationFile));
}
- JpsRunConfigurationSerializer.loadRunConfigurations(myProject, JDomSerializationUtil.findComponent(loadRootElement(dir.resolve("workspace.xml")), "RunManager"));
+ JpsRunConfigurationSerializer.loadRunConfigurations(myProject, JDomSerializationUtil.findComponent(loadRootElement(workspaceFile), "RunManager"));
runConfTimingLog.run();
}
}
@@ -197,7 +198,7 @@ public class JpsProjectLoader extends JpsLoaderBase {
}
}
}
- loadModules(iprRoot, projectSdkType);
+ loadModules(iprRoot, projectSdkType, iwsFile);
loadProjectLibraries(JDomSerializationUtil.findComponent(iprRoot, "libraryTable"));
loadArtifacts(JDomSerializationUtil.findComponent(iprRoot, "ArtifactManager"));
if (hasRunConfigurationSerializers()) {
@@ -229,18 +230,25 @@ public class JpsProjectLoader extends JpsLoaderBase {
JpsLibraryTableSerializer.loadLibraries(libraryTableElement, myProject.getLibraryCollection());
}
- private void loadModules(@Nullable Element root, final @Nullable JpsSdkType> projectSdkType) {
+ private void loadModules(@Nullable Element root, final @Nullable JpsSdkType> projectSdkType, Path workspaceFile) {
Runnable timingLog = TimingLog.startActivity("loading modules");
Element componentRoot = JDomSerializationUtil.findComponent(root, "ProjectModuleManager");
if (componentRoot == null) return;
+ Set unloadedModules = new HashSet<>();
+ if (Files.exists(workspaceFile)) {
+ Element unloadedModulesList = JDomSerializationUtil.findComponent(loadRootElement(workspaceFile), "UnloadedModulesList");
+ for (Element element : JDOMUtil.getChildren(unloadedModulesList, "module")) {
+ unloadedModules.add(element.getAttributeValue("name"));
+ }
+ }
final Set foundFiles = new THashSet<>();
final List moduleFiles = new ArrayList<>();
for (Element moduleElement : JDOMUtil.getChildren(componentRoot.getChild("modules"), "module")) {
final String path = moduleElement.getAttributeValue("filepath");
final Path file = Paths.get(path);
- if (foundFiles.add(file)) {
+ if (foundFiles.add(file) && !unloadedModules.contains(getModuleName(file))) {
moduleFiles.add(file);
}
}
@@ -285,7 +293,7 @@ public class JpsProjectLoader extends JpsLoaderBase {
}
if (data == null) {
- LOG.info("Module '" + FileUtil.getNameWithoutExtension(file.getFileName().toString()) + "' is skipped: " + file.toAbsolutePath() + " doesn't exist");
+ LOG.info("Module '" + getModuleName(file) + "' is skipped: " + file.toAbsolutePath() + " doesn't exist");
}
return Pair.create(file, data);
@@ -323,7 +331,7 @@ public class JpsProjectLoader extends JpsLoaderBase {
@NotNull
private static JpsModule loadModule(@NotNull Path file, @NotNull Element moduleRoot, List paths,
@Nullable JpsSdkType> projectSdkType, Map pathVariables) {
- String name = FileUtil.getNameWithoutExtension(file.getFileName().toString());
+ String name = getModuleName(file);
final String typeId = moduleRoot.getAttributeValue("type");
final JpsModulePropertiesSerializer> serializer = getModulePropertiesSerializer(typeId);
final JpsModule module = createModule(name, moduleRoot, serializer);
@@ -354,6 +362,11 @@ public class JpsProjectLoader extends JpsLoaderBase {
return module;
}
+ @NotNull
+ private static String getModuleName(@NotNull Path file) {
+ return FileUtil.getNameWithoutExtension(file.getFileName().toString());
+ }
+
static JpsMacroExpander createModuleMacroExpander(final Map pathVariables, @NotNull Path moduleFile) {
final JpsMacroExpander expander = new JpsMacroExpander(pathVariables);
String moduleDirPath = PathMacroUtil.getModuleDir(moduleFile.toAbsolutePath().toString());
diff --git a/jps/model-serialization/testData/unloadedModule/.idea/misc.xml b/jps/model-serialization/testData/unloadedModule/.idea/misc.xml
new file mode 100644
index 000000000000..0a1e1b4e681d
--- /dev/null
+++ b/jps/model-serialization/testData/unloadedModule/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/jps/model-serialization/testData/unloadedModule/.idea/modules.xml b/jps/model-serialization/testData/unloadedModule/.idea/modules.xml
new file mode 100644
index 000000000000..a071ba6d9b45
--- /dev/null
+++ b/jps/model-serialization/testData/unloadedModule/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/jps/model-serialization/testData/unloadedModule/.idea/workspace.xml b/jps/model-serialization/testData/unloadedModule/.idea/workspace.xml
new file mode 100644
index 000000000000..3ce6a2c59848
--- /dev/null
+++ b/jps/model-serialization/testData/unloadedModule/.idea/workspace.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/jps/model-serialization/testData/unloadedModule/main.iml b/jps/model-serialization/testData/unloadedModule/main.iml
new file mode 100644
index 000000000000..feb23b47ceff
--- /dev/null
+++ b/jps/model-serialization/testData/unloadedModule/main.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/jps/model-serialization/testData/unloadedModule/util.iml b/jps/model-serialization/testData/unloadedModule/util.iml
new file mode 100644
index 000000000000..fefaba3e3a7b
--- /dev/null
+++ b/jps/model-serialization/testData/unloadedModule/util.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java b/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java
index 51aa9efb7f01..f41ea73480a6 100644
--- a/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java
+++ b/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java
@@ -230,6 +230,12 @@ public class JpsProjectSerializationTest extends JpsSerializationTestCase {
}
}
+ public void testUnloadedModule() {
+ String projectPath = "/jps/model-serialization/testData/unloadedModule";
+ loadProject(projectPath);
+ assertEquals("main", assertOneElement(myProject.getModules()).getName());
+ }
+
private void doTestSaveLibrary(@NotNull Path libFile, String libName, JpsLibrary library) {
Element actual = new Element("library");
JpsLibraryTableSerializer.saveLibrary(library, actual, libName);
diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml
index 02d78afdf412..9796ea2be590 100644
--- a/platform/platform-resources/src/META-INF/LangExtensions.xml
+++ b/platform/platform-resources/src/META-INF/LangExtensions.xml
@@ -917,6 +917,7 @@
+
diff --git a/platform/projectModel-api/src/com/intellij/openapi/module/ModuleDescription.kt b/platform/projectModel-api/src/com/intellij/openapi/module/ModuleDescription.kt
new file mode 100644
index 000000000000..4329f318c042
--- /dev/null
+++ b/platform/projectModel-api/src/com/intellij/openapi/module/ModuleDescription.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2017 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.openapi.module
+
+import com.intellij.openapi.vfs.pointers.VirtualFilePointer
+import org.jetbrains.annotations.ApiStatus
+
+/**
+ * Represents a description of a module which may be either loaded into the project or unloaded. Use this class only if you need to process
+ * all modules including unloaded, in other cases [Module] should be used instead.
+ * @see [ModuleManager.getUnloadedModuleDescriptions]
+ * @author nik
+ */
+@ApiStatus.Experimental
+interface ModuleDescription {
+ val name: String
+
+ /**
+ * Names of the modules on which the current module depend.
+ */
+ val dependencyModuleNames: List
+}
+
+@ApiStatus.Experimental
+interface UnloadedModuleDescription : ModuleDescription {
+ val contentRoots: List
+}
\ No newline at end of file
diff --git a/platform/projectModel-api/src/com/intellij/openapi/module/ModuleManager.java b/platform/projectModel-api/src/com/intellij/openapi/module/ModuleManager.java
index 0e25a83c0d61..ee89d6438ed8 100644
--- a/platform/projectModel-api/src/com/intellij/openapi/module/ModuleManager.java
+++ b/platform/projectModel-api/src/com/intellij/openapi/module/ModuleManager.java
@@ -20,11 +20,13 @@ import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.SimpleModificationTracker;
import com.intellij.util.graph.Graph;
import org.jdom.JDOMException;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
+import java.util.Collection;
import java.util.Comparator;
import java.util.List;
@@ -170,4 +172,20 @@ public abstract class ModuleManager extends SimpleModificationTracker {
public abstract String[] getModuleGroupPath(@NotNull Module module);
public abstract boolean hasModuleGroups();
+
+ /**
+ * @return description of all modules in the project including unloaded
+ */
+ @ApiStatus.Experimental
+ public abstract Collection getAllModuleDescriptions();
+
+ @ApiStatus.Experimental
+ public abstract Collection extends UnloadedModuleDescription> getUnloadedModuleDescriptions();
+
+ /**
+ * Specify list of modules which will be unloaded from the project. These modules won't be shown in UI, all of their contents will be excluded
+ * from indexing and compilation.
+ */
+ @ApiStatus.Experimental
+ public abstract void setUnloadedModules(@NotNull List unloadedModuleNames);
}
diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/EmptyModuleManager.kt b/platform/projectModel-impl/src/com/intellij/openapi/module/EmptyModuleManager.kt
index 07f679f812ff..45e102b62949 100644
--- a/platform/projectModel-impl/src/com/intellij/openapi/module/EmptyModuleManager.kt
+++ b/platform/projectModel-impl/src/com/intellij/openapi/module/EmptyModuleManager.kt
@@ -48,4 +48,11 @@ class EmptyModuleManager(project: Project, messageBus: MessageBus) : ModuleManag
override fun getModifiableModel() = throw UnsupportedOperationException()
override fun getModuleGroupPath(module: Module) = emptyArray()
+
+ override fun setUnloadedModules(unloadedModuleNames: MutableList) {
+ }
+
+ override fun getAllModuleDescriptions() = emptyList()
+
+ override fun getUnloadedModuleDescriptions() = emptyList()
}
\ No newline at end of file
diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/LoadedModuleDescription.kt b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/LoadedModuleDescription.kt
new file mode 100644
index 000000000000..25b8f5b1c55b
--- /dev/null
+++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/LoadedModuleDescription.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2000-2017 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.openapi.module.impl
+
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.module.ModuleDescription
+import com.intellij.openapi.roots.ModuleRootManager
+
+/**
+ * @author nik
+ */
+class LoadedModuleDescription(private val module: Module): ModuleDescription {
+ override val name: String
+ get() = module.name
+
+ override val dependencyModuleNames: List
+ get() = ModuleRootManager.getInstance(module).dependencyModuleNames.asList()
+}
\ No newline at end of file
diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java
index 8529d94a7dd2..50c375de5954 100644
--- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java
+++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java
@@ -43,6 +43,8 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.util.SmartList;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.containers.ContainerUtil;
@@ -62,6 +64,7 @@ import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.util.stream.Collectors;
/**
* @author max
@@ -86,6 +89,7 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Disposa
private LinkedHashSet myModulePathsToLoad;
private final Set myFailedModulePaths = new THashSet<>();
+ private Map myUnloadedModules = new LinkedHashMap<>();
public static ModuleManagerImpl getInstanceImpl(Project project) {
return (ModuleManagerImpl)getInstance(project);
@@ -161,6 +165,22 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Disposa
public void loadState(Element state) {
boolean isFirstLoadState = myModulePathsToLoad == null;
myModulePathsToLoad = getPathsToModuleFiles(state);
+ Set unloadedModuleNames = new HashSet<>(UnloadedModulesListStorage.getInstance(myProject).getUnloadedModuleNames());
+ Iterator iterator = myModulePathsToLoad.iterator();
+ List unloadedModulePaths = new ArrayList<>();
+ while (iterator.hasNext()) {
+ ModulePath modulePath = iterator.next();
+ if (unloadedModuleNames.contains(modulePath.getModuleName())) {
+ unloadedModulePaths.add(modulePath);
+ iterator.remove();
+ }
+ }
+ List descriptions = UnloadedModuleDescriptionImpl.createFromPaths(unloadedModulePaths, this);
+ myUnloadedModules.clear();
+ for (UnloadedModuleDescriptionImpl description : descriptions) {
+ myUnloadedModules.put(description.getName(), description);
+ }
+
if (isFirstLoadState) {
// someone else must call loadModules in a appropriate time (e.g. on projectComponentsInitialized)
return;
@@ -421,13 +441,16 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Disposa
public void writeExternal(@NotNull Element element) {
final Module[] collection = getModules();
- List sorted = new ArrayList<>(collection.length + myFailedModulePaths.size());
+ List sorted = new ArrayList<>(collection.length + myFailedModulePaths.size() + myUnloadedModules.size());
for (Module module : collection) {
sorted.add(new ModuleSaveItem(module));
}
for (ModulePath modulePath : myFailedModulePaths) {
sorted.add(new ModulePathSaveItem(modulePath));
}
+ for (UnloadedModuleDescriptionImpl description : myUnloadedModules.values()) {
+ sorted.add(new ModulePathSaveItem(description.getModulePath()));
+ }
if (!sorted.isEmpty()) {
Collections.sort(sorted, Comparator.comparing(SaveItem::getModuleName));
@@ -957,6 +980,59 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Disposa
return myModuleModel.hasModuleGroups();
}
+ @Override
+ public Collection getAllModuleDescriptions() {
+ Module[] modules = getModules();
+ List descriptions = new ArrayList<>(modules.length + myUnloadedModules.size());
+ for (Module module : modules) {
+ descriptions.add(new LoadedModuleDescription(module));
+ }
+ descriptions.addAll(myUnloadedModules.values());
+ return descriptions;
+ }
+
+ @Override
+ public Collection extends UnloadedModuleDescription> getUnloadedModuleDescriptions() {
+ return myUnloadedModules.values();
+ }
+
+ @Override
+ public void setUnloadedModules(@NotNull List unloadedModuleNames) {
+ if (myUnloadedModules.keySet().equals(unloadedModuleNames)) {
+ return;
+ }
+
+ UnloadedModulesListStorage.getInstance(myProject).setUnloadedModuleNames(unloadedModuleNames);
+
+ final ModifiableModuleModel model = getModifiableModel();
+ Map toLoad = new LinkedHashMap<>(myUnloadedModules);
+ myUnloadedModules.clear();
+ for (String name : unloadedModuleNames) {
+ if (toLoad.containsKey(name)) {
+ myUnloadedModules.put(name, toLoad.remove(name));
+ }
+ else {
+ Module module = findModuleByName(name);
+ if (module != null) {
+ LoadedModuleDescription description = new LoadedModuleDescription(module);
+ ModuleSaveItem saveItem = new ModuleSaveItem(module);
+ ModulePath modulePath = new ModulePath(saveItem.getModuleFilePath(), saveItem.getGroupPathString());
+ VirtualFilePointerManager pointerManager = VirtualFilePointerManager.getInstance();
+ List contentRoots = ContainerUtil.map(ModuleRootManager.getInstance(module).getContentRootUrls(), url -> pointerManager.create(url, this, null));
+ UnloadedModuleDescriptionImpl unloadedModuleDescription = new UnloadedModuleDescriptionImpl(modulePath, description.getDependencyModuleNames(), contentRoots);
+ model.disposeModule(module);
+ myUnloadedModules.put(name, unloadedModuleDescription);
+ }
+ }
+ }
+ List oldFailedPaths = new ArrayList<>(myFailedModulePaths);
+ myModulePathsToLoad = toLoad.values().stream().map(d -> d.getModulePath()).collect(Collectors.toCollection(LinkedHashSet::new));
+ loadModules((ModuleModelImpl)model);
+ ApplicationManager.getApplication().runWriteAction(() -> model.commit());
+ myFailedModulePaths.addAll(oldFailedPaths);
+ myModulePathsToLoad.clear();
+ }
+
public void setModuleGroupPath(Module module, String[] groupPath) {
myModuleModel.setModuleGroupPath(module, groupPath);
}
diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/UnloadedModuleDescriptionImpl.kt b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/UnloadedModuleDescriptionImpl.kt
new file mode 100644
index 000000000000..9fbab89bd3ad
--- /dev/null
+++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/UnloadedModuleDescriptionImpl.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2017 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.openapi.module.impl
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.PathManager
+import com.intellij.openapi.module.UnloadedModuleDescription
+import com.intellij.openapi.vfs.pointers.VirtualFilePointer
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager
+import org.jetbrains.jps.model.module.JpsModule
+import org.jetbrains.jps.model.module.JpsModuleDependency
+import org.jetbrains.jps.model.serialization.JpsGlobalLoader
+import org.jetbrains.jps.model.serialization.JpsProjectLoader
+import java.nio.file.Paths
+
+/**
+ * @author nik
+ */
+class UnloadedModuleDescriptionImpl(val modulePath: ModulePath,
+ override val dependencyModuleNames: List,
+ override val contentRoots: List) : UnloadedModuleDescription {
+ override val name: String
+ get() = modulePath.moduleName
+
+ companion object {
+ @JvmStatic
+ fun createFromPaths(paths: List, parentDisposable: Disposable): List {
+ val pathVariables = JpsGlobalLoader.computeAllPathVariables(PathManager.getOptionsPath())
+ val modules = JpsProjectLoader.loadModules(paths.map { Paths.get(it.path) }, null, pathVariables)
+ val pathsByName = paths.associateBy { it.moduleName }
+ return modules.map { create(pathsByName[it.name]!!, it, parentDisposable) }
+ }
+
+ private fun create(path: ModulePath, module: JpsModule, parentDisposable: Disposable): UnloadedModuleDescriptionImpl {
+ val dependencyModuleNames = module.dependenciesList.dependencies
+ .filterIsInstance(JpsModuleDependency::class.java)
+ .map { it.moduleReference.moduleName }
+
+ val pointerManager = VirtualFilePointerManager.getInstance()
+ return UnloadedModuleDescriptionImpl(path, dependencyModuleNames, module.contentRootsList.urls.map {pointerManager.create(it, parentDisposable, null)})
+ }
+ }
+}
diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/UnloadedModulesListStorage.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/UnloadedModulesListStorage.java
new file mode 100644
index 000000000000..14b532c087b4
--- /dev/null
+++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/UnloadedModulesListStorage.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2017 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.openapi.module.impl;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.Property;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+@State(name = "UnloadedModulesList", storages = {@Storage(StoragePathMacros.WORKSPACE_FILE)})
+public class UnloadedModulesListStorage implements PersistentStateComponent {
+ @Property(surroundWithTag = false)
+ @AbstractCollection(surroundWithTag = false, elementTag = "module", elementValueAttribute = "name")
+ public List moduleNames = new ArrayList<>();
+
+ static UnloadedModulesListStorage getInstance(@NotNull Project project) {
+ return ServiceManager.getService(project, UnloadedModulesListStorage.class);
+ }
+
+ public List getUnloadedModuleNames() {
+ return Collections.unmodifiableList(moduleNames);
+ }
+
+ public void setUnloadedModuleNames(List names) {
+ moduleNames.clear();
+ moduleNames.addAll(names);
+ }
+
+ @Nullable
+ @Override
+ public UnloadedModulesListStorage getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(UnloadedModulesListStorage state) {
+ XmlSerializerUtil.copyBean(state, this);
+ }
+}