From 8cdc222c5e679b59ccc455a1ec3dcf0866397fda Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 18 Oct 2012 18:49:54 +0400 Subject: [PATCH] visual appeal of dataflow grouped values --- ...esAction.java => GroupByLeavesAction.java} | 10 ++-- .../intellij/slicer/SliceLeafAnalyzer.java | 16 +++--- .../slicer/SliceLeafValueRootNode.java | 18 ++++++- .../src/com/intellij/slicer/SlicePanel.java | 42 ++++++++++----- .../com/intellij/slicer/SliceTreeBuilder.java | 3 +- .../openapi/editor/markup/TextAttributes.java | 3 ++ .../com/intellij/usages/ChunkExtractor.java | 52 ++++++++++++------- .../src/com/intellij/usages/TextChunk.java | 2 + 8 files changed, 97 insertions(+), 49 deletions(-) rename java/java-impl/src/com/intellij/slicer/{AnalyzeLeavesAction.java => GroupByLeavesAction.java} (82%) diff --git a/java/java-impl/src/com/intellij/slicer/AnalyzeLeavesAction.java b/java/java-impl/src/com/intellij/slicer/GroupByLeavesAction.java similarity index 82% rename from java/java-impl/src/com/intellij/slicer/AnalyzeLeavesAction.java rename to java/java-impl/src/com/intellij/slicer/GroupByLeavesAction.java index 7e6c9e2e25d4..d24294b09a5f 100644 --- a/java/java-impl/src/com/intellij/slicer/AnalyzeLeavesAction.java +++ b/java/java-impl/src/com/intellij/slicer/GroupByLeavesAction.java @@ -22,11 +22,11 @@ import com.intellij.util.PlatformIcons; /** * @author cdr */ -public class AnalyzeLeavesAction extends AnAction { +class GroupByLeavesAction extends AnAction { private final SliceTreeBuilder myTreeBuilder; private static final String TEXT = "Group by leaf expression"; - public AnalyzeLeavesAction(SliceTreeBuilder treeBuilder) { + public GroupByLeavesAction(SliceTreeBuilder treeBuilder) { super(TEXT, "Show original expression values that might appear in this place", PlatformIcons.XML_TAG_ICON); myTreeBuilder = treeBuilder; } @@ -38,13 +38,11 @@ public class AnalyzeLeavesAction extends AnAction { } private boolean isAvailabale() { - if (myTreeBuilder.analysisInProgress) return false; - - return !myTreeBuilder.splitByLeafExpressions; + return !myTreeBuilder.analysisInProgress && !myTreeBuilder.splitByLeafExpressions; } @Override public void actionPerformed(AnActionEvent e) { - myTreeBuilder.switchToSplittedNodes(myTreeBuilder.getTreeStructure()); + myTreeBuilder.switchToGroupedByLeavesNodes(); } } diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java b/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java index 84f198f0df90..302ad18a0df1 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java @@ -97,15 +97,19 @@ public class SliceLeafAnalyzer { return filtered; } - private static void groupByValues(Collection leaves, SliceRootNode oldRoot, final Map> map) { + private static void groupByValues(@NotNull Collection leaves, + @NotNull SliceRootNode oldRoot, + @NotNull Map> map) { assert oldRoot.myCachedChildren.size() == 1; SliceRootNode root = createTreeGroupedByValues(leaves, oldRoot, map); SliceNode oldRootStart = oldRoot.myCachedChildren.get(0); SliceUsage rootUsage = oldRootStart.getValue(); - SliceManager.getInstance(root.getProject()).createToolWindow(true, root, true, SliceManager.getElementDescription(null, rootUsage.getElement(), " Grouped by Value") ); + String description = SliceManager.getElementDescription(null, rootUsage.getElement(), " (grouped by value)"); + SliceManager.getInstance(root.getProject()).createToolWindow(true, root, true, description); } + @NotNull public static SliceRootNode createTreeGroupedByValues(Collection leaves, SliceRootNode oldRoot, final Map> map) { SliceNode oldRootStart = oldRoot.myCachedChildren.get(0); SliceRootNode root = oldRoot.copy(); @@ -139,7 +143,7 @@ public class SliceLeafAnalyzer { return root; } - public static void startAnalyzeValues(final AbstractTreeStructure treeStructure, final Runnable finish) { + public static void startAnalyzeValues(@NotNull final AbstractTreeStructure treeStructure, @NotNull final Runnable finish) { final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement(); final Ref> leafExpressions = Ref.create(null); @@ -175,7 +179,6 @@ public class SliceLeafAnalyzer { } } }); - } public static Map> createMap() { @@ -233,8 +236,9 @@ public class SliceLeafAnalyzer { } @NotNull - public static Collection calcLeafExpressions(@NotNull final SliceNode root, AbstractTreeStructure treeStructure, - final Map> map) { + public static Collection calcLeafExpressions(@NotNull final SliceNode root, + @NotNull AbstractTreeStructure treeStructure, + @NotNull final Map> map) { final SliceNodeGuide guide = new SliceNodeGuide(treeStructure); WalkingState walkingState = new WalkingState(guide) { @Override diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java b/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java index 89d7ce8c202d..b44d9aaae44f 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java @@ -18,13 +18,17 @@ package com.intellij.slicer; import com.intellij.ide.projectView.PresentationData; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.ui.SimpleTextAttributes; import com.intellij.usageView.UsageViewBundle; +import com.intellij.usages.ChunkExtractor; +import com.intellij.usages.TextChunk; import com.intellij.usages.Usage; import com.intellij.usages.UsageInfo2UsageAdapter; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -82,11 +86,23 @@ public class SliceLeafValueRootNode extends SliceNode implements MyColoredTreeCe renderer.append(UsageViewBundle.message("node.invalid") + " ", SliceUsageCellRenderer.ourInvalidAttributes); } else { - renderer.append(element.getText(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + appendElementText((UsageInfo2UsageAdapter)usage, element, renderer); } } else { renderer.append("Other", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); } } + + private static void appendElementText(@NotNull UsageInfo2UsageAdapter usage, + @NotNull final PsiElement element, + @NotNull final SliceUsageCellRenderer renderer) { + PsiFile file = element.getContainingFile(); + List result = new ArrayList(); + ChunkExtractor.getExtractor(element.getContainingFile()).createTextChunks(usage, file.getText(), element.getTextRange().getStartOffset(), element.getTextRange().getEndOffset(), result); + + for (TextChunk chunk : result) { + renderer.append(chunk.getText(), SimpleTextAttributes.fromTextAttributes(chunk.getAttributes())); + } + } } diff --git a/java/java-impl/src/com/intellij/slicer/SlicePanel.java b/java/java-impl/src/com/intellij/slicer/SlicePanel.java index 64e741d3bfab..27a098ee73ca 100644 --- a/java/java-impl/src/com/intellij/slicer/SlicePanel.java +++ b/java/java-impl/src/com/intellij/slicer/SlicePanel.java @@ -43,6 +43,7 @@ import com.intellij.util.EditSourceOnDoubleClickHandler; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; @@ -84,7 +85,11 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, private boolean isDisposed; private final ToolWindow myToolWindow; - public SlicePanel(final Project project, boolean dataFlowToThis, SliceNode rootNode, boolean splitByLeafExpressions, final ToolWindow toolWindow) { + public SlicePanel(@NotNull final Project project, + boolean dataFlowToThis, + @NotNull SliceNode rootNode, + boolean splitByLeafExpressions, + @NotNull final ToolWindow toolWindow) { super(new BorderLayout()); myToolWindow = toolWindow; final ToolWindowManagerListener listener = new ToolWindowManagerListener() { @@ -181,6 +186,7 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, ToolTipManager.sharedInstance().unregisterComponent(myTree); } + @NotNull private JTree createTree() { DefaultMutableTreeNode root = new DefaultMutableTreeNode(); final Tree tree = new Tree(new DefaultTreeModel(root)){ @@ -282,6 +288,7 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, return null; } + @Nullable private List getSelectedUsageInfos() { TreePath[] paths = myTree.getSelectionPaths(); if (paths == null) return null; @@ -327,26 +334,31 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, return navigatables; } + @NotNull private ActionToolbar createToolbar() { final DefaultActionGroup actionGroup = new DefaultActionGroup(); actionGroup.add(new MyRefreshAction(myTree)); - actionGroup.add(myAutoScrollToSourceHandler.createToggleAction()); + if (isToShowAutoScrollButton()) { + actionGroup.add(myAutoScrollToSourceHandler.createToggleAction()); + } actionGroup.add(new CloseAction()); - actionGroup.add(new ToggleAction(UsageViewBundle.message("preview.usages.action.text"), "preview", AllIcons.Actions.Preview) { - @Override - public boolean isSelected(AnActionEvent e) { - return isPreview(); - } + if (isToShowPreviewButton()) { + actionGroup.add(new ToggleAction(UsageViewBundle.message("preview.usages.action.text"), "preview", AllIcons.Actions.Preview) { + @Override + public boolean isSelected(AnActionEvent e) { + return isPreview(); + } - @Override - public void setSelected(AnActionEvent e, boolean state) { - setPreview(state); - layoutPanel(); - } - }); + @Override + public void setSelected(AnActionEvent e, boolean state) { + setPreview(state); + layoutPanel(); + } + }); + } if (myBuilder.dataFlowToThis) { - actionGroup.add(new AnalyzeLeavesAction(myBuilder)); + actionGroup.add(new GroupByLeavesAction(myBuilder)); actionGroup.add(new CanItBeNullAction(myBuilder)); } @@ -355,10 +367,12 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, return ActionManager.getInstance().createActionToolbar(ActionPlaces.TYPE_HIERARCHY_VIEW_TOOLBAR, actionGroup, false); } + public boolean isToShowAutoScrollButton() {return true;} public abstract boolean isAutoScroll(); public abstract void setAutoScroll(boolean autoScroll); + public boolean isToShowPreviewButton() {return true;} public abstract boolean isPreview(); public abstract void setPreview(boolean preview); diff --git a/java/java-impl/src/com/intellij/slicer/SliceTreeBuilder.java b/java/java-impl/src/com/intellij/slicer/SliceTreeBuilder.java index 32a6cd4d6d4b..e795d62ef7f9 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceTreeBuilder.java +++ b/java/java-impl/src/com/intellij/slicer/SliceTreeBuilder.java @@ -16,7 +16,6 @@ package com.intellij.slicer; import com.intellij.ide.util.treeView.AbstractTreeBuilder; -import com.intellij.ide.util.treeView.AbstractTreeStructure; import com.intellij.ide.util.treeView.AlphaComparator; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.project.Project; @@ -85,7 +84,7 @@ public class SliceTreeBuilder extends AbstractTreeBuilder { return false; } - public void switchToSplittedNodes(final AbstractTreeStructure treeStructure) { + public void switchToGroupedByLeavesNodes() { analysisInProgress = true; SliceLeafAnalyzer.startAnalyzeValues(getTreeStructure(), new Runnable(){ @Override diff --git a/platform/core-api/src/com/intellij/openapi/editor/markup/TextAttributes.java b/platform/core-api/src/com/intellij/openapi/editor/markup/TextAttributes.java index 461bcf70473d..f5701b208f06 100644 --- a/platform/core-api/src/com/intellij/openapi/editor/markup/TextAttributes.java +++ b/platform/core-api/src/com/intellij/openapi/editor/markup/TextAttributes.java @@ -19,6 +19,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.*; import org.intellij.lang.annotations.JdkConstants; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; import java.awt.*; @@ -161,10 +162,12 @@ public class TextAttributes implements JDOMExternalizable, Cloneable { return getForegroundColor() == null && getBackgroundColor() == null && getEffectColor() == null && getFontType() == Font.PLAIN; } + @NotNull public AttributesFlyweight getFlyweight() { return myAttrs; } + @NotNull public static TextAttributes fromFlyweight(AttributesFlyweight flyweight) { TextAttributes f = new TextAttributes(); f.myAttrs = flyweight; diff --git a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java index 39d84cc6c19a..18f30ff6b917 100644 --- a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java +++ b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java @@ -89,8 +89,8 @@ public class ChunkExtractor { protected Map create() { return new FactoryMap() { @Override - protected ChunkExtractor create(PsiFile key) { - return new ChunkExtractor(key); + protected ChunkExtractor create(PsiFile psiFile) { + return new ChunkExtractor(psiFile); } }; } @@ -98,10 +98,15 @@ public class ChunkExtractor { } }; - public static TextChunk[] extractChunks(@NotNull PsiFile file, UsageInfo2UsageAdapter usageAdapter) { - return ourExtractors.get().getValue().get(file).extractChunks(usageAdapter, file); + @NotNull + public static TextChunk[] extractChunks(@NotNull PsiFile file, @NotNull UsageInfo2UsageAdapter usageAdapter) { + return getExtractor(file).extractChunks(usageAdapter, file); } + @NotNull + public static ChunkExtractor getExtractor(@NotNull PsiFile file) { + return ourExtractors.get().getValue().get(file); + } private ChunkExtractor(@NotNull PsiFile file) { myColorsScheme = UsageTreeColorsScheme.getInstance().getScheme(); @@ -127,6 +132,7 @@ public class ChunkExtractor { return minStart == Integer.MAX_VALUE ? -1 : minStart; } + @NotNull private TextChunk[] extractChunks(@NotNull UsageInfo2UsageAdapter usageInfo2UsageAdapter, @NotNull PsiFile file) { int absoluteStartOffset = usageInfo2UsageAdapter.getNavigationOffset(); if (absoluteStartOffset == -1) return TextChunk.EMPTY_ARRAY; @@ -135,7 +141,6 @@ public class ChunkExtractor { int visibleStartOffset = myDocument instanceof DocumentWindow ? ((DocumentWindow)myDocument).injectedToHost(absoluteStartOffset) : absoluteStartOffset; int lineNumber = myDocument.getLineNumber(absoluteStartOffset); - //int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber); int visibleLineNumber = visibleDocument.getLineNumber(visibleStartOffset); int visibleColumnNumber = visibleStartOffset - visibleDocument.getLineStartOffset(visibleLineNumber); final List result = new ArrayList(); @@ -146,9 +151,6 @@ public class ChunkExtractor { if (lineStartOffset > lineEndOffset) return TextChunk.EMPTY_ARRAY; final CharSequence chars = myDocument.getCharsSequence(); - if (myLexer.getTokenStart() > absoluteStartOffset) { - myLexer.start(chars); - } if (lineEndOffset - lineStartOffset > MAX_LINE_TO_SHOW) { lineStartOffset = Math.max(lineStartOffset, absoluteStartOffset - OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE); lineEndOffset = Math.min(lineEndOffset, absoluteStartOffset + OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE); @@ -164,11 +166,12 @@ public class ChunkExtractor { return createTextChunks(usageInfo2UsageAdapter, chars, lineStartOffset, lineEndOffset, result); } - private TextChunk[] createTextChunks(final UsageInfo2UsageAdapter usageInfo2UsageAdapter, - final CharSequence chars, - int start, - int end, - final List result) { + @NotNull + public TextChunk[] createTextChunks(@NotNull UsageInfo2UsageAdapter usageInfo2UsageAdapter, + @NotNull CharSequence chars, + int start, + int end, + @NotNull List result) { final Lexer lexer = myLexer; final SyntaxHighlighter highlighter = myHighlighter; @@ -177,6 +180,9 @@ public class ChunkExtractor { int i = StringUtil.indexOf(chars, '\n', start, end); if (i != -1) end = i; + if (lexer.getTokenStart() > start) { + lexer.start(chars); + } boolean isBeginning = true; while (lexer.getTokenType() != null) { @@ -191,7 +197,7 @@ public class ChunkExtractor { if (hiStart >= hiEnd) { continue; } String text = chars.subSequence(hiStart, hiEnd).toString(); - if (isBeginning && text.trim().length() == 0) continue; + if (isBeginning && text.trim().isEmpty()) continue; isBeginning = false; IElementType tokenType = lexer.getTokenType(); TextAttributesKey[] tokenHighlights = highlighter.getTokenHighlights(tokenType); @@ -206,12 +212,12 @@ public class ChunkExtractor { return result.toArray(new TextChunk[result.size()]); } - private void processIntersectingRange(UsageInfo2UsageAdapter usageInfo2UsageAdapter, - final CharSequence chars, + private void processIntersectingRange(@NotNull UsageInfo2UsageAdapter usageInfo2UsageAdapter, + @NotNull final CharSequence chars, int hiStart, final int hiEnd, - TextAttributesKey[] tokenHighlights, - final List result) { + @NotNull TextAttributesKey[] tokenHighlights, + @NotNull final List result) { final TextAttributes originalAttrs = convertAttributes(tokenHighlights); final int[] lastOffset = {hiStart}; usageInfo2UsageAdapter.processRangeMarkers(new Processor() { @@ -235,7 +241,12 @@ public class ChunkExtractor { } } - private static void addChunk(CharSequence chars, int start, int end, TextAttributes originalAttrs, boolean bold, List result) { + private static void addChunk(@NotNull CharSequence chars, + int start, + int end, + @NotNull TextAttributes originalAttrs, + boolean bold, + @NotNull List result) { if (start >= end) return; TextAttributes attrs = bold @@ -250,7 +261,8 @@ public class ChunkExtractor { || s1 == s2 && e1 == e2; } - private TextAttributes convertAttributes(TextAttributesKey[] keys) { + @NotNull + private TextAttributes convertAttributes(@NotNull TextAttributesKey[] keys) { TextAttributes attrs = myColorsScheme.getAttributes(HighlighterColors.TEXT); for (TextAttributesKey key : keys) { diff --git a/platform/usageView/src/com/intellij/usages/TextChunk.java b/platform/usageView/src/com/intellij/usages/TextChunk.java index 4fdf1a39b209..066ff766f734 100644 --- a/platform/usageView/src/com/intellij/usages/TextChunk.java +++ b/platform/usageView/src/com/intellij/usages/TextChunk.java @@ -30,10 +30,12 @@ public class TextChunk { myText = text; } + @NotNull public TextAttributes getAttributes() { return TextAttributes.fromFlyweight(myAttributes); } + @NotNull public String getText() { return myText; }