IDEA-76142: Gradle support - cannot update IDEA projects once one of build.gradle files changes

1. Module content root is really imported now;
2. Module content root is imported as a part of the module import now;
3. Unnecessary gradle project refresh is not triggered on 'import from gradle' now;
This commit is contained in:
Denis.Zhdanov
2012-02-29 12:19:56 +04:00
parent 12fc535b4b
commit b4576b3c9e
10 changed files with 189 additions and 28 deletions
@@ -45,6 +45,9 @@ public class GradleDiffUtil {
@Override
public void visit(@NotNull GradleModule module) {
context.register(new GradleModulePresenceChange(module, null));
for (GradleContentRoot root : module.getContentRoots()) {
root.invite(this);
}
for (GradleDependency dependency : module.getDependencies()) {
dependency.invite(this);
}
@@ -1,8 +1,15 @@
package org.jetbrains.plugins.gradle.importing;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.gradle.GradleContentRoot;
import org.jetbrains.plugins.gradle.model.gradle.SourceType;
import java.util.Collections;
@@ -18,7 +25,52 @@ public class GradleContentRootImporter {
importContentRoots(Collections.singleton(contentRoot), module);
}
public void importContentRoots(@NotNull Iterable<GradleContentRoot> contentRoots, @NotNull Module module) {
// TODO den implement
@SuppressWarnings("MethodMayBeStatic")
public void importContentRoots(@NotNull final Iterable<GradleContentRoot> contentRoots, @NotNull final Module module) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
doImportContentRoots(contentRoots, module);
}
});
}
}
private static void doImportContentRoots(@NotNull final Iterable<GradleContentRoot> contentRoots, @NotNull final Module module) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
final ModifiableRootModel model = moduleRootManager.getModifiableModel();
final GradleProjectEntityImportListener publisher = module.getProject().getMessageBus()
.syncPublisher(GradleProjectEntityImportListener.TOPIC);
for (GradleContentRoot root : contentRoots) {
publisher.onImportStart(root);
}
try {
for (GradleContentRoot contentRoot : contentRoots) {
ContentEntry contentEntry = model.addContentEntry(toVfsUrl(contentRoot.getRootPath()));
for (String path : contentRoot.getPaths(SourceType.SOURCE)) {
contentEntry.addSourceFolder(toVfsUrl(path), false);
}
for (String path : contentRoot.getPaths(SourceType.TEST)) {
contentEntry.addSourceFolder(toVfsUrl(path), true);
}
for (String path : contentRoot.getPaths(SourceType.EXCLUDED)) {
contentEntry.addExcludeFolder(toVfsUrl(path));
}
}
}
finally {
model.commit();
for (GradleContentRoot root : contentRoots) {
publisher.onImportEnd(root);
}
}
}
});
}
private static String toVfsUrl(@NotNull String path) {
return LocalFileSystem.PROTOCOL_PREFIX + path;
}
}
@@ -9,8 +9,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.config.GradleTextAttributes;
import org.jetbrains.plugins.gradle.model.GradleEntityType;
import org.jetbrains.plugins.gradle.model.gradle.*;
import org.jetbrains.plugins.gradle.model.id.GradleContentRootId;
import org.jetbrains.plugins.gradle.model.id.GradleEntityId;
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper;
import org.jetbrains.plugins.gradle.model.intellij.ModuleAwareContentRoot;
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeDescriptor;
@@ -136,11 +138,24 @@ public class GradleLocalNodeImportHelper {
if (!context.recursive) {
return;
}
for (GradleContentRoot contentRoot : module.getContentRoots()) {
contentRoot.invite(context.visitor);
}
for (GradleDependency dependency : module.getDependencies()) {
dependency.invite(context.visitor);
}
}
private void collectContentRoots(@NotNull GradleContentRoot contentRoot, @NotNull Context context) {
final GradleContentRootId id = GradleEntityIdMapper.mapEntityToId(contentRoot);
final ModuleAwareContentRoot intellijContentRoot = myProjectStructureHelper.findIntellijContentRoot(id);
if (intellijContentRoot != null) {
// Already imported.
return;
}
context.contentRoots.add(contentRoot);
}
private void collectModuleDependencyEntities(@NotNull GradleModuleDependency dependency, @NotNull Context context) {
final ModuleOrderEntry intellijModuleDependency = myProjectStructureHelper.findIntellijModuleDependency(dependency);
if (intellijModuleDependency != null) {
@@ -232,10 +247,11 @@ public class GradleLocalNodeImportHelper {
private class Context {
public final Set<GradleModule> modules = new HashSet<GradleModule>();
public final Set<GradleLibrary> libraries = new HashSet<GradleLibrary>();
public final Set<GradleDependency> dependencies = new HashSet<GradleDependency>();
public final CollectingVisitor visitor = new CollectingVisitor(this);
public final Set<GradleModule> modules = new HashSet<GradleModule>();
public final Set<GradleContentRoot> contentRoots = new HashSet<GradleContentRoot>();
public final Set<GradleLibrary> libraries = new HashSet<GradleLibrary>();
public final Set<GradleDependency> dependencies = new HashSet<GradleDependency>();
public final CollectingVisitor visitor = new CollectingVisitor(this);
public boolean recursive;
@@ -243,6 +259,7 @@ public class GradleLocalNodeImportHelper {
public List<GradleEntity> getAll() {
List<GradleEntity> result = new ArrayList<GradleEntity>();
result.addAll(modules);
result.addAll(contentRoots);
result.addAll(libraries);
result.addAll(dependencies);
return result;
@@ -258,10 +275,7 @@ public class GradleLocalNodeImportHelper {
@Override public void visit(@NotNull GradleProject project) { }
@Override public void visit(@NotNull GradleModule module) { collectModuleEntities(module, myContext); }
@Override
public void visit(@NotNull GradleContentRoot contentRoot) {
// TODO den implement
}
@Override public void visit(@NotNull GradleContentRoot contentRoot) { collectContentRoots(contentRoot, myContext); }
@Override public void visit(@NotNull GradleLibrary library) { /* Assuming that a library may be imported only as a dependency */ }
@Override public void visit(@NotNull GradleModuleDependency dependency) { collectModuleDependencyEntities(dependency, myContext); }
@Override public void visit(@NotNull GradleLibraryDependency dependency) { collectLibraryDependencyEntities(dependency, myContext); }
@@ -366,12 +366,16 @@ public class GradleModulesImporter {
model = projectLibraryTable.getModifiableModel();
List<ModifiableRootModel> modelsToCommit = new ArrayList<ModifiableRootModel>();
Map<GradleLibrary, Library> libraryMappings = registerProjectLibraries(gradleProject, model);
final GradleProjectEntityImportListener publisher
= intellijProject.getMessageBus().syncPublisher(GradleProjectEntityImportListener.TOPIC);
try {
Map<GradleLibrary, Library> libraryMappings = registerProjectLibraries(gradleProject, model);
if (libraryMappings == null) {
return;
}
for (GradleLibrary library : libraryMappings.keySet()) {
publisher.onImportStart(library);
}
modelsToCommit.addAll(configureModulesLibraryDependencies(moduleMappings, libraryMappings, gradleProject));
}
finally {
@@ -379,6 +383,11 @@ public class GradleModulesImporter {
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(intellijProject);
ModifiableRootModel[] modelsAsArray = modelsToCommit.toArray(new ModifiableRootModel[modelsToCommit.size()]);
projectRootManager.multiCommit(modelsAsArray);
if (libraryMappings != null) {
for (GradleLibrary library : libraryMappings.keySet()) {
publisher.onImportEnd(library);
}
}
}
}
@@ -99,11 +99,13 @@ public class GradleContentRoot extends AbstractGradleEntity {
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
StringBuilder buffer = new StringBuilder("content root:");
for (Map.Entry<SourceType, Collection<String>> entry : myData.entrySet()) {
buffer.append(entry.getKey().toString().toLowerCase()).append(": ").append(entry.getValue()).append("; ");
buffer.append(entry.getKey().toString().toLowerCase()).append("=").append(entry.getValue()).append("|");
}
if (!myData.isEmpty()) {
buffer.setLength(buffer.length() - 1);
}
buffer.setLength(buffer.length() - 2);
return buffer.toString();
}
@@ -52,7 +52,7 @@ public class GradleProjectStructureChangesDetector extends AbstractProjectCompon
@Override
public void onImportEnd(@NotNull Object entity) {
myImportCounter.incrementAndGet();
myImportCounter.decrementAndGet();
}
});
}
@@ -65,7 +65,7 @@ public class GradleProjectStructureChangesDetector extends AbstractProjectCompon
@Override
public void rootsChanged(ModuleRootEvent event) {
if (myImportCounter.incrementAndGet() <= 0) {
if (myImportCounter.get() <= 0) {
scheduleUpdate();
}
}
@@ -65,6 +65,7 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
protected JComponent buildContent() {
JPanel result = new JPanel(new GridBagLayout());
myTreeModel = new GradleProjectStructureTreeModel(getProject(), myContext);
myTreeModel.processCurrentChanges(myContext.getChangesModel().getChanges());
myTree = new Tree(myTreeModel);
applyInitialAppearance(myTree, (DefaultMutableTreeNode)myTreeModel.getRoot());
@@ -132,7 +132,7 @@ public class GradleProjectStructureNodeComparator implements Comparator<GradlePr
}
i++;
}
result.set(UNKNOWN_WEIGHT);
result.set(CONTENT_ROOT_WEIGHT);
}
@Override
@@ -146,12 +146,8 @@ public class GradleProjectStructureNodeComparator implements Comparator<GradlePr
}
});
}
try {
return result.get();
}
catch (Exception e) {
throw new RuntimeException(e);
}
final Integer i = result.get();
return i == null ? UNKNOWN_WEIGHT : i;
}
private int getWeight(@NotNull Module module, @NotNull Object entry) {
@@ -8,7 +8,7 @@ import org.picocontainer.MutablePicoContainer
import javax.swing.tree.TreeNode
import static org.junit.Assert.assertEquals
import static org.junit.Assert.fail
/**
* @author Denis Zhdanov
@@ -28,7 +28,7 @@ public class GradleLocalNodeImportHelperTest extends AbstractGradleTest {
}
@Test
public void libraryDependencyToModule() {
public void "library dependency causes module and library to be transitively imported as well"() {
doTest {
project {
module('module1', transitive: true, order: 0) { // Mark that the module should be imported
@@ -41,6 +41,15 @@ public class GradleLocalNodeImportHelperTest extends AbstractGradleTest {
} }
}
@Test
public void "module import covers content root"() {
doTest {
project {
module('module1', initial: true, order: 0) {
contentRoot('1', transitive: true, order: 1)
} } }
}
private def doTest(Closure c) {
myHelper = container.getComponentInstance(GradleLocalNodeImportHelper)
def compositeBuilder = new CompositeProjectBuilder(gradleBuilder: gradle, intellijBuilder: intellij)
@@ -50,7 +59,16 @@ public class GradleLocalNodeImportHelperTest extends AbstractGradleTest {
myHelper
def nodes = collectNodes(treeModel.root as TreeNode, compositeBuilder.initial)
def expectedEntities = compositeBuilder.expected.sort{ a, b -> a[0].compareTo(b[0]) }.collect { it[1] }
assertEquals(expectedEntities, myHelper.deriveEntitiesToImport(nodes))
def actual = myHelper.deriveEntitiesToImport(nodes)
if (expectedEntities == actual) {
return
}
fail("""\
Mismatched 'import nodes with dependencies'.
Initial: ${out -> nodes.each { out << "\n * $it"}}
Expected ordered graph: ${out -> expectedEntities.eachWithIndex { val, i -> out << "\n $i) $val"}}
Actual ordered grapth: ${out -> actual.eachWithIndex { val, i -> out << "\n $i) $val"}}
""")
}
private List collectNodes(TreeNode node, initial, holder = []) {
@@ -599,4 +599,70 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
"content-root:3"()
} } }
}
@Test
public void "module removal at intellij"() {
Closure initial = {
project {
module {
contentRoot('1')
dependencies {
library('lib1')
} } } }
init(gradle: initial, intellij: initial)
checkChanges { }
checkTree {
project {
module {
"content-root"()
dependencies {
lib1()
} } } }
setState(intellij: { project { }})
def m = gradle.modules.values().flatten().first()
checkChanges {
presence {
module(gradle: m)
contentRoot(gradle: gradle.contentRoots[m])
libraryDependency(gradle: gradle.libraryDependencies[m])
} }
checkTree {
project {
module('gradle') {
"content-root"('gradle')
dependencies {
lib1('gradle')
} } } }
}
@Test
public void "content root is correctly highlighted after importing gradle local module"() {
Closure completeProject = {
project {
module {
contentRoot('1')
} } }
init(gradle: completeProject, intellij: { project { }})
def m = gradle.modules.values().flatten().first()
checkChanges {
presence {
module(gradle: m)
contentRoot(gradle: gradle.contentRoots[m])
} }
checkTree {
project {
module('gradle') {
"content-root"('gradle')
} } }
// Import the whole module.
setState(gradle: completeProject, intellij: completeProject)
checkChanges { }
checkTree {
project {
module() {
"content-root"()
} } }
}
}