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

'Filter by change type' actions perform actual filtering of the gradle project structure tree
This commit is contained in:
Denis.Zhdanov
2012-03-11 15:30:43 +04:00
parent 1a4c0b3a8e
commit 3ea2ee2318
7 changed files with 171 additions and 49 deletions
@@ -1,12 +1,16 @@
package org.jetbrains.plugins.gradle.action;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.options.colors.AttributesDescriptor;
import com.intellij.util.ui.ColorIcon;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel;
import org.jetbrains.plugins.gradle.ui.GradleDataKeys;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeFilter;
import javax.swing.*;
import java.awt.*;
@@ -17,10 +21,10 @@ import java.awt.*;
*/
public abstract class GradleAbstractSyncTreeFilterAction extends ToggleAction {
@NotNull private final AttributesDescriptor myDescriptor;
@NotNull private final MyFilter myFilter;
protected GradleAbstractSyncTreeFilterAction(@NotNull AttributesDescriptor descriptor) {
myDescriptor = descriptor;
myFilter = new MyFilter(descriptor.getKey());
getTemplatePresentation().setText(descriptor.getDisplayName());
final Color color = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(descriptor.getKey()).getForegroundColor();
getTemplatePresentation().setIcon(new ColorIcon(new JLabel("").getFont().getSize(), color));
@@ -28,13 +32,39 @@ public abstract class GradleAbstractSyncTreeFilterAction extends ToggleAction {
@Override
public boolean isSelected(AnActionEvent e) {
// TODO den implement
return false;
final GradleProjectStructureTreeModel model = GradleDataKeys.SYNC_TREE_MODEL.getData(e.getDataContext());
if (model == null) {
return false;
}
return model.hasFilter(myFilter);
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
// TODO den implement
System.out.println("GradleAbstractSyncTreeFilterAction.setSelected(): " + state);
final GradleProjectStructureTreeModel treeModel = GradleDataKeys.SYNC_TREE_MODEL.getData(e.getDataContext());
if (treeModel == null) {
return;
}
if (state) {
treeModel.addFilter(myFilter);
}
else {
treeModel.removeFilter(myFilter);
}
}
private static class MyFilter implements GradleProjectStructureNodeFilter {
@NotNull private final TextAttributesKey myKey;
MyFilter(@NotNull TextAttributesKey key) {
myKey = key;
}
@Override
public boolean isVisible(@NotNull GradleProjectStructureNode<?> node) {
return myKey.equals(node.getDescriptor().getAttributes());
}
}
}
@@ -12,13 +12,10 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Alarm;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.config.GradleToolWindowPanel;
import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChange;
import org.jetbrains.plugins.gradle.ui.GradleDataKeys;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode;
import org.jetbrains.plugins.gradle.ui.GradleUiListener;
@@ -33,7 +30,6 @@ import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
@@ -61,20 +57,6 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
public GradleProjectStructureChangesPanel(@NotNull Project project, @NotNull GradleProjectStructureContext context) {
super(project, GradleConstants.TOOL_WINDOW_TOOLBAR_PLACE);
myContext = context;
context.getChangesModel().addListener(new GradleProjectStructureChangeListener() {
@Override
public void onChanges(@NotNull final Collection<GradleProjectStructureChange> oldChanges,
@NotNull final Collection<GradleProjectStructureChange> currentChanges)
{
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
myTreeModel.processObsoleteChanges(ContainerUtil.subtract(oldChanges, currentChanges));
myTreeModel.processCurrentChanges(currentChanges);
}
});
}
});
myToolbarControls.add(new GradleProjectStructureFiltersPanel());
initContent();
}
@@ -89,7 +71,6 @@ 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());
@@ -265,6 +246,9 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
if (GradleDataKeys.SYNC_TREE.is(dataId)) {
return myTree;
}
else if (GradleDataKeys.SYNC_TREE_MODEL.is(dataId)) {
return myTreeModel;
}
else if (GradleDataKeys.SYNC_TREE_SELECTED_NODE.is(dataId)) {
TreePath[] paths = myTree.getSelectionPaths();
if (paths == null) {
@@ -4,17 +4,22 @@ import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.plugins.gradle.config.GradleTextAttributes;
import org.jetbrains.plugins.gradle.diff.*;
import org.jetbrains.plugins.gradle.model.GradleEntityOwner;
import org.jetbrains.plugins.gradle.model.gradle.GradleModule;
import org.jetbrains.plugins.gradle.model.id.*;
import org.jetbrains.plugins.gradle.model.intellij.ModuleAwareContentRoot;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeComparator;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeComparator;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeDescriptor;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeFilter;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import org.jetbrains.plugins.gradle.util.GradleProjectStructureContext;
@@ -49,24 +54,57 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
private final Map<String, GradleProjectStructureNode<GradleModuleId>> myModules
= new HashMap<String, GradleProjectStructureNode<GradleModuleId>>();
private final TreeNode[] myNodeHolder = new TreeNode[1];
private final int[] myIndexHolder = new int[1];
private final NodeListener myNodeListener = new NodeListener();
private final ObsoleteChangesDispatcher myObsoleteChangesDispatcher = new ObsoleteChangesDispatcher();
private final NewChangesDispatcher myNewChangesDispatcher = new NewChangesDispatcher();
private final Set<GradleProjectStructureNodeFilter> myFilters = new HashSet<GradleProjectStructureNodeFilter>();
private final TreeNode[] myNodeHolder = new TreeNode[1];
private final int[] myIndexHolder = new int[1];
private final NodeListener myNodeListener = new NodeListener();
private final ObsoleteChangesDispatcher myObsoleteChangesDispatcher = new ObsoleteChangesDispatcher();
private final NewChangesDispatcher myNewChangesDispatcher = new NewChangesDispatcher();
@NotNull private final Project myProject;
@NotNull private final PlatformFacade myPlatformFacade;
@NotNull private final GradleProjectStructureHelper myProjectStructureHelper;
@NotNull private final Comparator<GradleProjectStructureNode<?>> myNodeComparator;
@NotNull private final GradleProjectStructureChangesModel myChangesModel;
private Comparator<GradleProjectStructureChange> myChangesComparator;
private boolean myProcessChangesAtTheSameThread;
public GradleProjectStructureTreeModel(@NotNull Project project, @NotNull GradleProjectStructureContext context) {
super(null);
myProject = project;
myPlatformFacade = context.getPlatformFacade();
myProjectStructureHelper = context.getProjectStructureHelper();
myChangesModel = context.getChangesModel();
myNodeComparator = new GradleProjectStructureNodeComparator(context);
rebuild();
context.getChangesModel().addListener(new GradleProjectStructureChangeListener() {
@Override
public void onChanges(@NotNull final Collection<GradleProjectStructureChange> oldChanges,
@NotNull final Collection<GradleProjectStructureChange> currentChanges)
{
final Runnable task = new Runnable() {
@Override
public void run() {
Collection<GradleProjectStructureChange> c = ContainerUtil.subtract(oldChanges, currentChanges);
if (myChangesComparator != null) {
List<GradleProjectStructureChange> toSort = new ArrayList<GradleProjectStructureChange>(c);
Collections.sort(toSort, myChangesComparator);
c = toSort;
}
processObsoleteChanges(c);
processCurrentChanges(currentChanges);
}
};
if (myProcessChangesAtTheSameThread) {
task.run();
}
else {
UIUtil.invokeLaterIfNeeded(task);
}
}
});
}
public void rebuild() {
@@ -121,10 +159,70 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
dependenciesNode.add(dependency);
}
}
processCurrentChanges(myChangesModel.getChanges());
filterNodes(root);
setRoot(root);
}
@TestOnly
public void setProcessChangesAtTheSameThread(boolean processChangesAtTheSameThread) {
myProcessChangesAtTheSameThread = processChangesAtTheSameThread;
}
@TestOnly
public void setChangesComparator(@Nullable Comparator<GradleProjectStructureChange> changesComparator) {
myChangesComparator = changesComparator;
}
private void filterNodes(@NotNull GradleProjectStructureNode<?> root) {
if (myFilters.isEmpty()) {
return;
}
Deque<GradleProjectStructureNode<?>> toRemove = new ArrayDeque<GradleProjectStructureNode<?>>();
Stack<GradleProjectStructureNode<?>> toProcess = new Stack<GradleProjectStructureNode<?>>();
toProcess.push(root);
while (!toProcess.isEmpty()) {
final GradleProjectStructureNode<?> current = toProcess.pop();
toRemove.add(current);
for (GradleProjectStructureNodeFilter filter : myFilters) {
if (filter.isVisible(current)) {
toRemove.remove(current);
// Keep all nodes up to the hierarchy.
for (GradleProjectStructureNode<?> parent = current.getParent(); parent != null; parent = parent.getParent()) {
if (!toRemove.remove(parent)) {
break;
}
}
break;
}
}
for (GradleProjectStructureNode<?> child : current) {
toProcess.push(child);
}
}
for (GradleProjectStructureNode<?> node = toRemove.pollLast(); node != null; node = toRemove.pollLast()) {
final GradleProjectStructureNode<?> parent = node.getParent();
if (parent != null) {
parent.remove(node);
}
}
}
public void addFilter(@NotNull GradleProjectStructureNodeFilter filter) {
myFilters.add(filter);
rebuild();
}
public boolean hasFilter(@NotNull GradleProjectStructureNodeFilter filter) {
return myFilters.contains(filter);
}
public void removeFilter(@NotNull GradleProjectStructureNodeFilter filter) {
myFilters.remove(filter);
rebuild();
}
@NotNull
private static String getContentRootNodeName(@NotNull GradleContentRootId id, boolean singleRoot) {
final String name = GradleBundle.message("gradle.import.structure.tree.node.content.root");
@@ -2,6 +2,7 @@ package org.jetbrains.plugins.gradle.ui;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.ui.treeStructure.Tree;
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel;
import java.util.Collection;
@@ -13,6 +14,9 @@ public class GradleDataKeys {
/** Key for obtaining 'sync project structure' tree. */
public static final DataKey<Tree> SYNC_TREE = DataKey.create("gradle.sync.tree");
/** Key for obtaining 'sync project structure' tree model. */
public static final DataKey<GradleProjectStructureTreeModel> SYNC_TREE_MODEL = DataKey.create("gradle.sync.tree.model");
/** Key for obtaining currently selected nodes at the gradle 'sync project structure' tree. */
public static final DataKey<Collection<GradleProjectStructureNode<?>>> SYNC_TREE_SELECTED_NODE
@@ -100,8 +100,15 @@ public class GradleProjectStructureNode<T extends GradleEntityId> extends Defaul
@Override
public void remove(MutableTreeNode aChild) {
boolean b = mySkipNotification;
mySkipNotification = true;
final int index = getIndex(aChild);
super.remove(aChild);
try {
super.remove(aChild);
}
finally {
mySkipNotification = b;
}
onNodeRemoved((GradleProjectStructureNode<?>)aChild, index);
}
@@ -0,0 +1,12 @@
package org.jetbrains.plugins.gradle.ui;
import org.jetbrains.annotations.NotNull;
/**
* @author Denis Zhdanov
* @since 3/11/12 11:40 AM
*/
public interface GradleProjectStructureNodeFilter {
boolean isVisible(@NotNull GradleProjectStructureNode<?> node);
}
@@ -1,19 +1,17 @@
package org.jetbrains.plugins.gradle.testutil
import com.intellij.openapi.project.Project
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangeListener
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangesModel
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel
import org.jetbrains.plugins.gradle.util.GradleProjectStructureContext
import org.junit.Before
import org.picocontainer.MutablePicoContainer
import org.picocontainer.defaults.DefaultPicoContainer
import org.jetbrains.plugins.gradle.diff.*
import static org.junit.Assert.fail
import org.jetbrains.plugins.gradle.util.GradleProjectStructureContext
/**
* @author Denis Zhdanov
@@ -28,7 +26,6 @@ public abstract class AbstractGradleTest {
def changesBuilder
def treeChecker
def container
private Closure changesComparator
@Before
public void setUp() {
@@ -61,28 +58,18 @@ public abstract class AbstractGradleTest {
@SuppressWarnings("GroovyAssignabilityCheck")
protected def init(map = [:]) {
treeModel = container.getComponentInstance(GradleProjectStructureTreeModel) as GradleProjectStructureTreeModel
changesModel.addListener({ old, current ->
treeModel.processObsoleteChanges(sortChanges(ContainerUtil.subtract(old, current)));
treeModel.processCurrentChanges(sortChanges(current))
} as GradleProjectStructureChangeListener)
treeModel.processChangesAtTheSameThread = true;
setState(map, false)
treeModel.rebuild()
changesModel.update(gradle.project)
}
def sortChanges(changes) {
if (changesComparator) {
return changes.toList().sort(changesComparator)
}
return changes
}
protected def setState(map, update = true) {
map.intellij?.delegate = intellij
map.intellij?.call()
map.gradle?.delegate = gradle
map.gradle?.call()
changesComparator = map.changesSorter
treeModel.changesComparator = map.changesSorter as Comparator
if (update) {
changesModel.update(gradle.project)
}