mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
visual appeal of dataflow grouped values
This commit is contained in:
+4
-6
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -97,15 +97,19 @@ public class SliceLeafAnalyzer {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private static void groupByValues(Collection<PsiElement> leaves, SliceRootNode oldRoot, final Map<SliceNode, Collection<PsiElement>> map) {
|
||||
private static void groupByValues(@NotNull Collection<PsiElement> leaves,
|
||||
@NotNull SliceRootNode oldRoot,
|
||||
@NotNull Map<SliceNode, Collection<PsiElement>> 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<PsiElement> leaves, SliceRootNode oldRoot, final Map<SliceNode, Collection<PsiElement>> 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<Collection<PsiElement>> leafExpressions = Ref.create(null);
|
||||
|
||||
@@ -175,7 +179,6 @@ public class SliceLeafAnalyzer {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static Map<SliceNode, Collection<PsiElement>> createMap() {
|
||||
@@ -233,8 +236,9 @@ public class SliceLeafAnalyzer {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<PsiElement> calcLeafExpressions(@NotNull final SliceNode root, AbstractTreeStructure treeStructure,
|
||||
final Map<SliceNode, Collection<PsiElement>> map) {
|
||||
public static Collection<PsiElement> calcLeafExpressions(@NotNull final SliceNode root,
|
||||
@NotNull AbstractTreeStructure treeStructure,
|
||||
@NotNull final Map<SliceNode, Collection<PsiElement>> map) {
|
||||
final SliceNodeGuide guide = new SliceNodeGuide(treeStructure);
|
||||
WalkingState<SliceNode> walkingState = new WalkingState<SliceNode>(guide) {
|
||||
@Override
|
||||
|
||||
@@ -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<TextChunk> result = new ArrayList<TextChunk>();
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<UsageInfo> 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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -89,8 +89,8 @@ public class ChunkExtractor {
|
||||
protected Map<PsiFile, ChunkExtractor> create() {
|
||||
return new FactoryMap<PsiFile, ChunkExtractor>() {
|
||||
@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<TextChunk> result = new ArrayList<TextChunk>();
|
||||
@@ -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<TextChunk> result) {
|
||||
@NotNull
|
||||
public TextChunk[] createTextChunks(@NotNull UsageInfo2UsageAdapter usageInfo2UsageAdapter,
|
||||
@NotNull CharSequence chars,
|
||||
int start,
|
||||
int end,
|
||||
@NotNull List<TextChunk> 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<TextChunk> result) {
|
||||
@NotNull TextAttributesKey[] tokenHighlights,
|
||||
@NotNull final List<TextChunk> result) {
|
||||
final TextAttributes originalAttrs = convertAttributes(tokenHighlights);
|
||||
final int[] lastOffset = {hiStart};
|
||||
usageInfo2UsageAdapter.processRangeMarkers(new Processor<Segment>() {
|
||||
@@ -235,7 +241,12 @@ public class ChunkExtractor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addChunk(CharSequence chars, int start, int end, TextAttributes originalAttrs, boolean bold, List<TextChunk> result) {
|
||||
private static void addChunk(@NotNull CharSequence chars,
|
||||
int start,
|
||||
int end,
|
||||
@NotNull TextAttributes originalAttrs,
|
||||
boolean bold,
|
||||
@NotNull List<TextChunk> 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) {
|
||||
|
||||
@@ -30,10 +30,12 @@ public class TextChunk {
|
||||
myText = text;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextAttributes getAttributes() {
|
||||
return TextAttributes.fromFlyweight(myAttributes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getText() {
|
||||
return myText;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user