diff --git a/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java b/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java index d8978670e239..f52ea4483db1 100644 --- a/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java @@ -41,10 +41,7 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; -import com.intellij.slicer.DuplicateMap; -import com.intellij.slicer.SliceAnalysisParams; -import com.intellij.slicer.SliceRootNode; -import com.intellij.slicer.SliceUsage; +import com.intellij.slicer.*; import com.intellij.util.Function; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -640,7 +637,7 @@ public class MagicConstantInspection extends BaseJavaLocalInspectionTool { params.dataFlowToThis = true; params.scope = new AnalysisScope(new LocalSearchScope(scope), manager.getProject()); - SliceRootNode rootNode = new SliceRootNode(manager.getProject(), new DuplicateMap(), SliceUsage.createRootUsage(argument, params)); + SliceRootNode rootNode = new SliceRootNode(manager.getProject(), new DuplicateMap(), LanguageSlicing.getProvider(argument).createRootUsage(argument, params)); Collection children = rootNode.getChildren().iterator().next().getChildren(); for (AbstractTreeNode child : children) { diff --git a/java/java-impl/src/com/intellij/slicer/SliceDereferenceUsage.java b/java/java-impl/src/com/intellij/slicer/JavaSliceDereferenceUsage.java similarity index 89% rename from java/java-impl/src/com/intellij/slicer/SliceDereferenceUsage.java rename to java/java-impl/src/com/intellij/slicer/JavaSliceDereferenceUsage.java index 4961367428ce..2d4f3fa4bd03 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceDereferenceUsage.java +++ b/java/java-impl/src/com/intellij/slicer/JavaSliceDereferenceUsage.java @@ -27,8 +27,8 @@ import javax.swing.*; /** * User: cdr */ -public class SliceDereferenceUsage extends SliceUsage { - public SliceDereferenceUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) { +public class JavaSliceDereferenceUsage extends JavaSliceUsage { + public JavaSliceDereferenceUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) { super(element, parent, substitutor,0,""); } diff --git a/java/java-impl/src/com/intellij/slicer/JavaSliceProvider.java b/java/java-impl/src/com/intellij/slicer/JavaSliceProvider.java new file mode 100644 index 000000000000..87b54bc9daa0 --- /dev/null +++ b/java/java-impl/src/com/intellij/slicer/JavaSliceProvider.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2015 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.slicer; + +import com.intellij.ide.util.treeView.AbstractTreeStructure; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class JavaSliceProvider implements SliceLanguageSupportProvider { + @NotNull + @Override + public SliceUsage createRootUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) { + return JavaSliceUsage.createRootUsage(element, params); + } + + @Nullable + @Override + public PsiElement getExpressionAtCaret(PsiElement atCaret, boolean dataFlowToThis) { + PsiElement element = PsiTreeUtil.getParentOfType(atCaret, PsiExpression.class, PsiVariable.class); + if (dataFlowToThis && element instanceof PsiLiteralExpression) return null; + return element; + } + + @NotNull + @Override + public PsiElement getElementForDescription(@NotNull PsiElement element) { + if (element instanceof PsiReferenceExpression) { + PsiElement elementToSlice = ((PsiReferenceExpression)element).resolve(); + if (elementToSlice != null) { + return elementToSlice; + } + } + return element; + } + + @NotNull + @Override + public SliceUsageCellRendererBase getRenderer() { + return new SliceUsageCellRenderer(); + } + + @Override + public void startAnalyzeLeafValues(AbstractTreeStructure structure, Runnable finalRunnable) { + SliceLeafAnalyzer.startAnalyzeValues(structure, finalRunnable); + } + + @Override + public void startAnalyzeNullness(AbstractTreeStructure structure, Runnable finalRunnable) { + SliceNullnessAnalyzer.startAnalyzeNullness(structure, finalRunnable); + } + + @Override + public void registerExtraPanelActions(DefaultActionGroup actionGroup, SliceTreeBuilder sliceTreeBuilder) { + if (sliceTreeBuilder.dataFlowToThis) { + actionGroup.add(new GroupByLeavesAction(sliceTreeBuilder)); + actionGroup.add(new CanItBeNullAction(sliceTreeBuilder)); + } + } +} diff --git a/java/java-impl/src/com/intellij/slicer/JavaSliceUsage.java b/java/java-impl/src/com/intellij/slicer/JavaSliceUsage.java new file mode 100644 index 000000000000..5d24ef675a58 --- /dev/null +++ b/java/java-impl/src/com/intellij/slicer/JavaSliceUsage.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2015 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.slicer; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.util.Processor; +import org.jetbrains.annotations.NotNull; + +public class JavaSliceUsage extends SliceUsage { + private final PsiSubstitutor mySubstitutor; + protected final int indexNesting; // 0 means bare expression 'x', 1 means x[?], 2 means x[?][?] etc + @NotNull protected final String syntheticField; // "" means no field, otherwise it's a name of fake field of container, e.g. "keys" for Map + + + public JavaSliceUsage(@NotNull PsiElement element, + @NotNull SliceUsage parent, + @NotNull PsiSubstitutor substitutor, + int indexNesting, + @NotNull String syntheticField) { + super(element, parent); + mySubstitutor = substitutor; + this.syntheticField = syntheticField; + this.indexNesting = indexNesting; + } + + // root usage + private JavaSliceUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) { + super(element, params); + mySubstitutor = PsiSubstitutor.EMPTY; + indexNesting = 0; + syntheticField = ""; + } + + @NotNull + public static JavaSliceUsage createRootUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) { + return new JavaSliceUsage(element, params); + } + + @Override + protected void processUsagesFlownFromThe(PsiElement element, Processor uniqueProcessor) { + SliceForwardUtil.processUsagesFlownFromThe(element, uniqueProcessor, this); + } + + @Override + protected void processUsagesFlownDownTo(PsiElement element, Processor uniqueProcessor) { + SliceUtil.processUsagesFlownDownTo(element, uniqueProcessor, this, mySubstitutor, indexNesting,syntheticField); + } + + @Override + @NotNull + protected SliceUsage copy() { + PsiElement element = getUsageInfo().getElement(); + return getParent() == null ? createRootUsage(element, params) : + new JavaSliceUsage(element, getParent(), mySubstitutor, indexNesting, syntheticField); + } + + @NotNull + public PsiSubstitutor getSubstitutor() { + return mySubstitutor; + } +} diff --git a/java/java-impl/src/com/intellij/slicer/SliceForwardUtil.java b/java/java-impl/src/com/intellij/slicer/SliceForwardUtil.java index 254498e5674d..fe847fddcf9d 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceForwardUtil.java +++ b/java/java-impl/src/com/intellij/slicer/SliceForwardUtil.java @@ -38,7 +38,7 @@ import java.util.Set; * @author cdr */ public class SliceForwardUtil { - public static boolean processUsagesFlownFromThe(@NotNull PsiElement element, @NotNull final Processor processor, @NotNull final SliceUsage parent) { + public static boolean processUsagesFlownFromThe(@NotNull PsiElement element, @NotNull final Processor processor, @NotNull final JavaSliceUsage parent) { Pair pair = getAssignmentTarget(element, parent); if (pair != null) { PsiElement target = pair.getFirst(); @@ -94,7 +94,7 @@ public class SliceForwardUtil { private static boolean processAssignedFrom(final PsiElement from, final PsiElement context, - final SliceUsage parent, + final JavaSliceUsage parent, @NotNull final Processor processor) { if (from instanceof PsiLocalVariable) { return searchReferencesAndProcessAssignmentTarget(from, context, parent, processor); @@ -180,7 +180,7 @@ public class SliceForwardUtil { return true; } - private static boolean searchReferencesAndProcessAssignmentTarget(@NotNull PsiElement element, @Nullable final PsiElement context, final SliceUsage parent, + private static boolean searchReferencesAndProcessAssignmentTarget(@NotNull PsiElement element, @Nullable final PsiElement context, final JavaSliceUsage parent, final Processor processor) { return ReferencesSearch.search(element).forEach(new Processor() { @Override @@ -192,7 +192,7 @@ public class SliceForwardUtil { }); } - private static boolean processAssignmentTarget(PsiElement element, final SliceUsage parent, final Processor processor) { + private static boolean processAssignmentTarget(PsiElement element, final JavaSliceUsage parent, final Processor processor) { if (!parent.params.scope.contains(element)) return true; if (element instanceof PsiCompiledElement) element = element.getNavigationElement(); Pair pair = getAssignmentTarget(element, parent); @@ -201,7 +201,7 @@ public class SliceForwardUtil { return processor.process(usage); } if (parent.params.showInstanceDereferences && isDereferenced(element)) { - SliceUsage usage = new SliceDereferenceUsage(element.getParent(), parent, parent.getSubstitutor()); + SliceUsage usage = new JavaSliceDereferenceUsage(element.getParent(), parent, parent.getSubstitutor()); return processor.process(usage); } return true; @@ -214,7 +214,7 @@ public class SliceForwardUtil { return ((PsiReferenceExpression)parent).getQualifierExpression() == element; } - private static Pair getAssignmentTarget(PsiElement element, SliceUsage parentUsage) { + private static Pair getAssignmentTarget(PsiElement element, JavaSliceUsage parentUsage) { element = complexify(element); PsiElement target = null; PsiSubstitutor substitutor = parentUsage.getSubstitutor(); diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java b/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java index af4031841d2c..e122ee3ce24e 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java @@ -253,7 +253,7 @@ public class SliceLeafAnalyzer { ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { - final SliceUsage sliceUsage = element.getValue(); + final JavaSliceUsage sliceUsage = (JavaSliceUsage)element.getValue(); Collection children = element.getChildren(); if (children.isEmpty()) { diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java b/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java index e73da4154084..50faf8ad2e3d 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java @@ -44,7 +44,7 @@ public class SliceLeafValueClassNode extends SliceLeafValueRootNode { } @Override - public void customizeCellRenderer(@NotNull SliceUsageCellRenderer renderer, + public void customizeCellRenderer(@NotNull SliceUsageCellRendererBase renderer, @NotNull JTree tree, Object value, boolean selected, diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java b/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java index dee98f84da84..d11ef5d204e7 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java @@ -40,7 +40,7 @@ public class SliceLeafValueRootNode extends SliceNode implements MyColoredTreeCe public SliceLeafValueRootNode(@NotNull Project project, PsiElement leafExpression, SliceNode root, List children, SliceAnalysisParams params) { - super(project, SliceUsage.createRootUsage(leafExpression, params), root.targetEqualUsages); + super(project, JavaSliceUsage.createRootUsage(leafExpression, params), root.targetEqualUsages); myCachedChildren = children; } @@ -69,7 +69,7 @@ public class SliceLeafValueRootNode extends SliceNode implements MyColoredTreeCe } @Override - public void customizeCellRenderer(@NotNull SliceUsageCellRenderer renderer, + public void customizeCellRenderer(@NotNull SliceUsageCellRendererBase renderer, @NotNull JTree tree, Object value, boolean selected, @@ -96,7 +96,7 @@ public class SliceLeafValueRootNode extends SliceNode implements MyColoredTreeCe private static void appendElementText(@NotNull UsageInfo2UsageAdapter usage, @NotNull final PsiElement element, - @NotNull final SliceUsageCellRenderer renderer) { + @NotNull final SliceUsageCellRendererBase renderer) { PsiFile file = element.getContainingFile(); List result = new ArrayList(); ChunkExtractor.getExtractor(element.getContainingFile()) diff --git a/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java b/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java index 577a1e9d8606..c32fd2314050 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java +++ b/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java @@ -15,58 +15,30 @@ */ package com.intellij.slicer; -import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.SimpleTextAttributes; -import com.intellij.usageView.UsageTreeColors; -import com.intellij.usageView.UsageTreeColorsScheme; import com.intellij.usages.TextChunk; import com.intellij.util.Processor; import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; import java.awt.*; import java.util.List; /** * @author cdr */ -public class SliceUsageCellRenderer extends ColoredTreeCellRenderer { - private static final EditorColorsScheme ourColorsScheme = UsageTreeColorsScheme.getInstance().getScheme(); - public static final SimpleTextAttributes ourInvalidAttributes = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.INVALID_PREFIX)); - - public SliceUsageCellRenderer() { - setOpaque(false); - } +public class SliceUsageCellRenderer extends SliceUsageCellRendererBase { @Override - public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { - assert value instanceof DefaultMutableTreeNode; - DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value; - Object userObject = treeNode.getUserObject(); - if (userObject == null) return; - if (userObject instanceof MyColoredTreeCellRenderer) { - MyColoredTreeCellRenderer node = (MyColoredTreeCellRenderer)userObject; - node.customizeCellRenderer(this, tree, value, selected, expanded, leaf, row, hasFocus); - if (node instanceof SliceNode) { - setToolTipText(((SliceNode)node).getPresentation().getTooltip()); - } - } - else { - append(userObject.toString(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); - } - } - public void customizeCellRendererFor(@NotNull SliceUsage sliceUsage) { - boolean isForcedLeaf = sliceUsage instanceof SliceDereferenceUsage; + boolean isForcedLeaf = sliceUsage instanceof JavaSliceDereferenceUsage; + JavaSliceUsage javaSliceUsage = ((JavaSliceUsage)sliceUsage); TextChunk[] text = sliceUsage.getText(); final List usageRanges = new SmartList(); @@ -77,7 +49,7 @@ public class SliceUsageCellRenderer extends ColoredTreeCellRenderer { return true; } }); - boolean isInsideContainer = sliceUsage.indexNesting != 0; + boolean isInsideContainer = javaSliceUsage.indexNesting != 0; for (TextChunk textChunk : text) { SimpleTextAttributes attributes = textChunk.getSimpleAttributesIgnoreBackground(); if (isForcedLeaf) { @@ -93,8 +65,8 @@ public class SliceUsageCellRenderer extends ColoredTreeCellRenderer { append(textChunk.getText(), attributes); } - for (int i=0; i processor, - @NotNull SliceUsage parent, + @NotNull JavaSliceUsage parent, @NotNull PsiSubstitutor parentSubstitutor, int indexNesting, @NotNull String syntheticField) { @@ -235,7 +235,7 @@ class SliceUtil { private static boolean processMethodReturnValue(@NotNull final PsiMethodCallExpression methodCallExpr, @NotNull final Processor processor, - @NotNull final SliceUsage parent, + @NotNull final JavaSliceUsage parent, @NotNull final PsiSubstitutor parentSubstitutor) { final JavaResolveResult resolved = methodCallExpr.resolveMethodGenerics(); PsiElement r = resolved.getElement(); @@ -293,7 +293,7 @@ class SliceUtil { } private static boolean processFieldUsages(@NotNull final PsiField field, - @NotNull final SliceUsage parent, + @NotNull final JavaSliceUsage parent, @NotNull final PsiSubstitutor parentSubstitutor, @NotNull final Processor processor) { if (field.hasInitializer()) { @@ -342,14 +342,14 @@ class SliceUtil { @NotNull PsiSubstitutor substitutor, int indexNesting, @NotNull String syntheticField) { - return new SliceUsage(simplify(element), parent, substitutor,indexNesting, syntheticField); + return new JavaSliceUsage(simplify(element), parent, substitutor,indexNesting, syntheticField); } @NotNull private static SliceUsage createTooComplexDFAUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) { - return new SliceTooComplexDFAUsage(simplify(element), parent, substitutor); + return new SliceTooComplexDFAUsage(simplify(element), parent); } private static boolean processParameterUsages(@NotNull final PsiParameter parameter, diff --git a/java/java-impl/src/com/intellij/usageView/UsageContextDataflowToPanel.java b/java/java-impl/src/com/intellij/usageView/UsageContextDataflowToPanel.java index 38804dda3051..636d8aca299e 100644 --- a/java/java-impl/src/com/intellij/usageView/UsageContextDataflowToPanel.java +++ b/java/java-impl/src/com/intellij/usageView/UsageContextDataflowToPanel.java @@ -121,7 +121,7 @@ public class UsageContextDataflowToPanel extends UsageContextPanelBase { ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.FIND); SliceAnalysisParams params = createParams(element, dataFlowToThis); - SliceRootNode rootNode = new SliceRootNode(myProject, new DuplicateMap(), SliceUsage.createRootUsage(element, params)); + SliceRootNode rootNode = new SliceRootNode(myProject, new DuplicateMap(), JavaSliceUsage.createRootUsage(element, params)); return new SlicePanel(myProject, dataFlowToThis, rootNode, false, toolWindow) { @Override diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index 8f933bc5bdf8..e45989a2ad8c 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -47,5 +47,6 @@ + \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java b/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java index 2f6ea959a1a6..8d8638dc02d4 100644 --- a/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java +++ b/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java @@ -43,113 +43,19 @@ public class SliceBackwardTest extends SliceTestCase { private void doTest() throws Exception { configureByFile("/codeInsight/slice/backward/"+getTestName(false)+".java"); - Map sliceUsageName2Offset = extractSliceOffsetsFromDocument(getEditor().getDocument()); + Map sliceUsageName2Offset = SliceTestUtil.extractSliceOffsetsFromDocument(getEditor().getDocument()); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); PsiElement element = new SliceHandler(true).getExpressionAtCaret(getEditor(), getFile()); assertNotNull(element); - calcRealOffsets(element, sliceUsageName2Offset, myFlownOffsets); + SliceTestUtil.calcRealOffsets(element, sliceUsageName2Offset, myFlownOffsets); Collection errors = highlightErrors(); assertEmpty(errors); SliceAnalysisParams params = new SliceAnalysisParams(); params.scope = new AnalysisScope(getProject()); params.dataFlowToThis = true; - SliceUsage usage = SliceUsage.createRootUsage(element, params); - checkUsages(usage, myFlownOffsets); - } - - static void checkUsages(final SliceUsage usage, final TIntObjectHashMap flownOffsets) { - final List children = new ArrayList(); - boolean b = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - @Override - public void run() { - usage.processChildren(new CommonProcessors.CollectProcessor(children)); - } - }, "Expanding", true, usage.getElement().getProject()); - assertTrue(b); - int startOffset = usage.getElement().getTextOffset(); - IntArrayList list = flownOffsets.get(startOffset); - int[] offsets = list == null ? new int[0] : list.toArray(); - Arrays.sort(offsets); - - int size = offsets.length; - assertEquals(message(startOffset, usage), size, children.size()); - Collections.sort(children, new Comparator() { - @Override - public int compare(SliceUsage o1, SliceUsage o2) { - return o1.compareTo(o2); - } - }); - - for (int i = 0; i < children.size(); i++) { - SliceUsage child = children.get(i); - int offset = offsets[i]; - assertEquals(message(offset, child), offset, child.getUsageInfo().getElement().getTextOffset()); - - checkUsages(child, flownOffsets); - } - } - - private static String message(int startOffset, SliceUsage usage) { - PsiFile file = usage.getElement().getContainingFile(); - Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); - Editor editor = FileEditorManager.getInstance(file.getProject()).getSelectedTextEditor(); - LogicalPosition position = editor.offsetToLogicalPosition(startOffset); - return position + ": '" + StringUtil.first(file.getText().substring(startOffset), 100, true) + "'"; - } - - static void calcRealOffsets(PsiElement startElement, Map sliceUsageName2Offset, - final TIntObjectHashMap flownOffsets) { - fill(sliceUsageName2Offset, "", startElement.getTextOffset(), flownOffsets); - } - - static Map extractSliceOffsetsFromDocument(final Document document) { - Map sliceUsageName2Offset = new THashMap(); - - extract(document, sliceUsageName2Offset, ""); - int index = document.getText().indexOf(" sliceUsageName2Offset, String name, int offset, - final TIntObjectHashMap flownOffsets) { - for (int i=1;i<9;i++) { - String newName = name + i; - RangeMarker marker = sliceUsageName2Offset.get(newName); - if (marker == null) break; - IntArrayList offsets = flownOffsets.get(offset); - if (offsets == null) { - offsets = new IntArrayList(); - flownOffsets.put(offset, offsets); - } - int newStartOffset = marker.getStartOffset(); - offsets.add(newStartOffset); - fill(sliceUsageName2Offset, newName, newStartOffset, flownOffsets); - } - } - - private static void extract(final Document document, final Map sliceUsageName2Offset, final String name) { - WriteCommandAction.runWriteCommandAction(null, new Runnable() { - @Override - public void run() { - for (int i = 1; i < 9; i++) { - String newName = name + i; - String s = ""; - if (!document.getText().contains(s)) break; - int off = document.getText().indexOf(s); - - document.deleteString(off, off + s.length()); - RangeMarker prev = sliceUsageName2Offset.put(newName, document.createRangeMarker(off, off)); - assertNull(prev); - - extract(document, sliceUsageName2Offset, newName); - } - } - }); + SliceUsage usage = LanguageSlicing.getProvider(element).createRootUsage(element, params); + SliceTestUtil.checkUsages(usage, myFlownOffsets); } public void testSimple() throws Exception { doTest();} diff --git a/java/java-tests/testSrc/com/intellij/slicer/SliceForwardTest.java b/java/java-tests/testSrc/com/intellij/slicer/SliceForwardTest.java index 3ff509d09817..9348a1cb4c8e 100644 --- a/java/java-tests/testSrc/com/intellij/slicer/SliceForwardTest.java +++ b/java/java-tests/testSrc/com/intellij/slicer/SliceForwardTest.java @@ -35,18 +35,18 @@ public class SliceForwardTest extends DaemonAnalyzerTestCase { private void dotest() throws Exception { configureByFile("/codeInsight/slice/forward/"+getTestName(false)+".java"); - Map sliceUsageName2Offset = SliceBackwardTest.extractSliceOffsetsFromDocument(getEditor().getDocument()); + Map sliceUsageName2Offset = SliceTestUtil.extractSliceOffsetsFromDocument(getEditor().getDocument()); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); PsiElement element = new SliceForwardHandler().getExpressionAtCaret(getEditor(), getFile()); assertNotNull(element); - SliceBackwardTest.calcRealOffsets(element, sliceUsageName2Offset, myFlownOffsets); + SliceTestUtil.calcRealOffsets(element, sliceUsageName2Offset, myFlownOffsets); Collection errors = highlightErrors(); assertEmpty(errors); SliceAnalysisParams params = new SliceAnalysisParams(); params.scope = new AnalysisScope(getProject()); params.dataFlowToThis = false; - SliceUsage usage = SliceUsage.createRootUsage(element, params); - SliceBackwardTest.checkUsages(usage, myFlownOffsets); + SliceUsage usage = LanguageSlicing.getProvider(element).createRootUsage(element, params); + SliceTestUtil.checkUsages(usage, myFlownOffsets); } public void testSimple() throws Exception { dotest();} diff --git a/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java b/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java index b6d8840059ba..1434a11766aa 100644 --- a/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java +++ b/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java @@ -46,7 +46,7 @@ public class SliceTreeTest extends SliceTestCase { params.scope = new AnalysisScope(getProject()); params.dataFlowToThis = true; - SliceUsage usage = SliceUsage.createRootUsage(element, params); + SliceUsage usage = LanguageSlicing.getProvider(element).createRootUsage(element, params); ToolWindowHeadlessManagerImpl.MockToolWindow toolWindow = new ToolWindowHeadlessManagerImpl.MockToolWindow(myProject); diff --git a/java/java-impl/src/com/intellij/slicer/DuplicateMap.java b/platform/lang-impl/src/com/intellij/slicer/DuplicateMap.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/DuplicateMap.java rename to platform/lang-impl/src/com/intellij/slicer/DuplicateMap.java diff --git a/platform/lang-impl/src/com/intellij/slicer/LanguageSlicing.java b/platform/lang-impl/src/com/intellij/slicer/LanguageSlicing.java new file mode 100644 index 000000000000..f5be038e6e8c --- /dev/null +++ b/platform/lang-impl/src/com/intellij/slicer/LanguageSlicing.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2015 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.slicer; + +import com.intellij.lang.LanguageExtension; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LanguageSlicing extends LanguageExtension { + public static final LanguageSlicing INSTANCE = new LanguageSlicing(); + + private LanguageSlicing() { + super("com.intellij.lang.sliceProvider"); + } + + @Nullable + public static SliceLanguageSupportProvider getProvider(@NotNull PsiElement element){ + return INSTANCE.forLanguage(element.getLanguage()); + } +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/slicer/MyColoredTreeCellRenderer.java b/platform/lang-impl/src/com/intellij/slicer/MyColoredTreeCellRenderer.java similarity index 90% rename from java/java-impl/src/com/intellij/slicer/MyColoredTreeCellRenderer.java rename to platform/lang-impl/src/com/intellij/slicer/MyColoredTreeCellRenderer.java index c150e0fd8674..b1a03c05edcd 100644 --- a/java/java-impl/src/com/intellij/slicer/MyColoredTreeCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/slicer/MyColoredTreeCellRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -23,7 +23,7 @@ import javax.swing.*; * @author cdr */ public interface MyColoredTreeCellRenderer { - void customizeCellRenderer(@NotNull SliceUsageCellRenderer renderer, + void customizeCellRenderer(@NotNull SliceUsageCellRendererBase renderer, @NotNull JTree tree, Object value, boolean selected, diff --git a/java/java-impl/src/com/intellij/slicer/SliceAnalysisParams.java b/platform/lang-impl/src/com/intellij/slicer/SliceAnalysisParams.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceAnalysisParams.java rename to platform/lang-impl/src/com/intellij/slicer/SliceAnalysisParams.java diff --git a/java/java-impl/src/com/intellij/slicer/SliceBackwardAction.java b/platform/lang-impl/src/com/intellij/slicer/SliceBackwardAction.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceBackwardAction.java rename to platform/lang-impl/src/com/intellij/slicer/SliceBackwardAction.java diff --git a/java/java-impl/src/com/intellij/slicer/SliceForwardAction.java b/platform/lang-impl/src/com/intellij/slicer/SliceForwardAction.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceForwardAction.java rename to platform/lang-impl/src/com/intellij/slicer/SliceForwardAction.java diff --git a/java/java-impl/src/com/intellij/slicer/SliceForwardForm.form b/platform/lang-impl/src/com/intellij/slicer/SliceForwardForm.form similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceForwardForm.form rename to platform/lang-impl/src/com/intellij/slicer/SliceForwardForm.form diff --git a/java/java-impl/src/com/intellij/slicer/SliceForwardForm.java b/platform/lang-impl/src/com/intellij/slicer/SliceForwardForm.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceForwardForm.java rename to platform/lang-impl/src/com/intellij/slicer/SliceForwardForm.java diff --git a/java/java-impl/src/com/intellij/slicer/SliceForwardHandler.java b/platform/lang-impl/src/com/intellij/slicer/SliceForwardHandler.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceForwardHandler.java rename to platform/lang-impl/src/com/intellij/slicer/SliceForwardHandler.java diff --git a/java/java-impl/src/com/intellij/slicer/SliceHandler.java b/platform/lang-impl/src/com/intellij/slicer/SliceHandler.java similarity index 94% rename from java/java-impl/src/com/intellij/slicer/SliceHandler.java rename to platform/lang-impl/src/com/intellij/slicer/SliceHandler.java index e98048382b00..ffe9b47b756d 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceHandler.java +++ b/platform/lang-impl/src/com/intellij/slicer/SliceHandler.java @@ -66,9 +66,11 @@ public class SliceHandler implements CodeInsightActionHandler { } PsiElement atCaret = file.findElementAt(offset); - PsiElement element = PsiTreeUtil.getParentOfType(atCaret, PsiExpression.class, PsiVariable.class); - if (myDataFlowToThis && element instanceof PsiLiteralExpression) return null; - return element; + SliceLanguageSupportProvider provider = LanguageSlicing.getProvider(file); + if(provider == null){ + return null; + } + return provider.getExpressionAtCaret(atCaret, myDataFlowToThis); } public SliceAnalysisParams askForParams(PsiElement element, boolean dataFlowToThis, SliceManager.StoredSettingsBean storedSettingsBean, String dialogTitle) { diff --git a/platform/lang-impl/src/com/intellij/slicer/SliceLanguageSupportProvider.java b/platform/lang-impl/src/com/intellij/slicer/SliceLanguageSupportProvider.java new file mode 100644 index 000000000000..6d01986d75eb --- /dev/null +++ b/platform/lang-impl/src/com/intellij/slicer/SliceLanguageSupportProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2015 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.slicer; + +import com.intellij.ide.util.treeView.AbstractTreeStructure; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface SliceLanguageSupportProvider { + + @NotNull + SliceUsage createRootUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) ; + + @Nullable + PsiElement getExpressionAtCaret(PsiElement atCaret, boolean dataFlowToThis); + + @NotNull + PsiElement getElementForDescription(@NotNull PsiElement element); + + @NotNull + SliceUsageCellRendererBase getRenderer(); + + void startAnalyzeLeafValues(AbstractTreeStructure structure, Runnable finalRunnable); + + void startAnalyzeNullness(AbstractTreeStructure structure, Runnable finalRunnable); + + void registerExtraPanelActions(DefaultActionGroup group, SliceTreeBuilder builder); +} diff --git a/java/java-impl/src/com/intellij/slicer/SliceManager.java b/platform/lang-impl/src/com/intellij/slicer/SliceManager.java similarity index 95% rename from java/java-impl/src/com/intellij/slicer/SliceManager.java rename to platform/lang-impl/src/com/intellij/slicer/SliceManager.java index a8f76905d1f9..1a4b9bb1d579 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceManager.java +++ b/platform/lang-impl/src/com/intellij/slicer/SliceManager.java @@ -125,7 +125,8 @@ public class SliceManager implements PersistentStateComponent" + UIUtil.getCssFontDeclaration(BaseLabel.getLabelFont()) + "" + (prefix == null ? "" : prefix) + StringUtil.first(desc, 100, true)+(suffix == null ? "" : suffix) + ""; diff --git a/java/java-impl/src/com/intellij/slicer/SliceNode.java b/platform/lang-impl/src/com/intellij/slicer/SliceNode.java similarity index 90% rename from java/java-impl/src/com/intellij/slicer/SliceNode.java rename to platform/lang-impl/src/com/intellij/slicer/SliceNode.java index bd8af0e1aeab..93c9ac2461cf 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceNode.java +++ b/platform/lang-impl/src/com/intellij/slicer/SliceNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -23,11 +23,13 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; +import com.intellij.psi.PsiElement; import com.intellij.ui.DuplicateNodeRenderer; import com.intellij.usageView.UsageViewBundle; import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; @@ -207,7 +209,7 @@ public class SliceNode extends AbstractTreeNode implements Duplicate } @Override - public void customizeCellRenderer(@NotNull SliceUsageCellRenderer renderer, @NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + public void customizeCellRenderer(@NotNull SliceUsageCellRendererBase renderer, @NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { renderer.setIcon(getPresentation().getIcon(expanded)); if (isValid()) { SliceUsage sliceUsage = getValue(); @@ -215,7 +217,7 @@ public class SliceNode extends AbstractTreeNode implements Duplicate renderer.setToolTipText(sliceUsage.getPresentation().getTooltipText()); } else { - renderer.append(UsageViewBundle.message("node.invalid") + " ", SliceUsageCellRenderer.ourInvalidAttributes); + renderer.append(UsageViewBundle.message("node.invalid") + " ", SliceUsageCellRendererBase.ourInvalidAttributes); } } @@ -223,6 +225,23 @@ public class SliceNode extends AbstractTreeNode implements Duplicate changed = true; } + @Nullable + public SliceLanguageSupportProvider getProvider(){ + AbstractTreeNode element = getElement(); + if(element == null){ + return null; + } + SliceUsage usage = element.getValue(); + if(usage == null){ + return null; + } + PsiElement psiElement = usage.getElement(); + if(psiElement == null){ + return null; + } + return LanguageSlicing.getProvider(psiElement); + } + @Override public String toString() { return ApplicationManager.getApplication().runReadAction(new Computable() { diff --git a/java/java-impl/src/com/intellij/slicer/SlicePanel.java b/platform/lang-impl/src/com/intellij/slicer/SlicePanel.java similarity index 98% rename from java/java-impl/src/com/intellij/slicer/SlicePanel.java rename to platform/lang-impl/src/com/intellij/slicer/SlicePanel.java index 79501ad5aae9..af668ff64c13 100644 --- a/java/java-impl/src/com/intellij/slicer/SlicePanel.java +++ b/platform/lang-impl/src/com/intellij/slicer/SlicePanel.java @@ -32,6 +32,7 @@ import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.openapi.wm.ex.ToolWindowManagerListener; import com.intellij.pom.Navigatable; +import com.intellij.psi.PsiElement; import com.intellij.ui.*; import com.intellij.ui.treeStructure.Tree; import com.intellij.usageView.UsageInfo; @@ -85,6 +86,7 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, private final Project myProject; private boolean isDisposed; private final ToolWindow myToolWindow; + private final SliceLanguageSupportProvider myProvider; public SlicePanel(@NotNull final Project project, boolean dataFlowToThis, @@ -92,6 +94,7 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, boolean splitByLeafExpressions, @NotNull final ToolWindow toolWindow) { super(new BorderLayout()); + myProvider = rootNode.getProvider(); myToolWindow = toolWindow; final ToolWindowManagerListener listener = new ToolWindowManagerListener() { ToolWindowAnchor myAnchor = toolWindow.getAnchor(); @@ -194,7 +197,7 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, tree.setOpaque(false); tree.setToggleClickCount(-1); - SliceUsageCellRenderer renderer = new SliceUsageCellRenderer(); + SliceUsageCellRendererBase renderer = myProvider.getRenderer(); renderer.setOpaque(false); tree.setCellRenderer(renderer); UIUtil.setLineStyleAngled(tree); @@ -354,10 +357,7 @@ public abstract class SlicePanel extends JPanel implements TypeSafeDataProvider, }); } - if (myBuilder.dataFlowToThis) { - actionGroup.add(new GroupByLeavesAction(myBuilder)); - actionGroup.add(new CanItBeNullAction(myBuilder)); - } + myProvider.registerExtraPanelActions(actionGroup, myBuilder); //actionGroup.add(new ContextHelpAction(HELP_ID)); diff --git a/java/java-impl/src/com/intellij/slicer/SliceRootNode.java b/platform/lang-impl/src/com/intellij/slicer/SliceRootNode.java similarity index 92% rename from java/java-impl/src/com/intellij/slicer/SliceRootNode.java rename to platform/lang-impl/src/com/intellij/slicer/SliceRootNode.java index 2d1ec38f0806..52e28018b45a 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceRootNode.java +++ b/platform/lang-impl/src/com/intellij/slicer/SliceRootNode.java @@ -33,7 +33,10 @@ public class SliceRootNode extends SliceNode { private final SliceUsage myRootUsage; public SliceRootNode(@NotNull Project project, @NotNull DuplicateMap targetEqualUsages, final SliceUsage rootUsage) { - super(project, SliceUsage.createRootUsage(rootUsage.getElement().getContainingFile(), rootUsage.params), targetEqualUsages); + super(project, + LanguageSlicing.getProvider(rootUsage.getElement().getContainingFile()). + createRootUsage(rootUsage.getElement().getContainingFile(), rootUsage.params), + targetEqualUsages); myRootUsage = rootUsage; } @@ -82,7 +85,7 @@ public class SliceRootNode extends SliceNode { @Override - public void customizeCellRenderer(@NotNull SliceUsageCellRenderer renderer, + public void customizeCellRenderer(@NotNull SliceUsageCellRendererBase renderer, @NotNull JTree tree, Object value, boolean selected, diff --git a/java/java-impl/src/com/intellij/slicer/SliceTooComplexDFAUsage.java b/platform/lang-impl/src/com/intellij/slicer/SliceTooComplexDFAUsage.java similarity index 79% rename from java/java-impl/src/com/intellij/slicer/SliceTooComplexDFAUsage.java rename to platform/lang-impl/src/com/intellij/slicer/SliceTooComplexDFAUsage.java index bba6d64e6b8a..86f82ad51bc7 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceTooComplexDFAUsage.java +++ b/platform/lang-impl/src/com/intellij/slicer/SliceTooComplexDFAUsage.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -18,7 +18,6 @@ package com.intellij.slicer; import com.intellij.openapi.editor.markup.EffectType; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiSubstitutor; import com.intellij.ui.JBColor; import com.intellij.usages.TextChunk; import com.intellij.usages.UsagePresentation; @@ -32,8 +31,8 @@ import java.awt.*; * User: cdr */ public class SliceTooComplexDFAUsage extends SliceUsage { - public SliceTooComplexDFAUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) { - super(element, parent, substitutor,0,""); + public SliceTooComplexDFAUsage(@NotNull PsiElement element, @NotNull SliceUsage parent) { + super(element, parent); } @Override @@ -41,6 +40,22 @@ public class SliceTooComplexDFAUsage extends SliceUsage { // no children } + @Override + protected void processUsagesFlownFromThe(PsiElement element, Processor uniqueProcessor) { + // no children + } + + @Override + protected void processUsagesFlownDownTo(PsiElement element, Processor uniqueProcessor) { + // no children + } + + @Override + @NotNull + protected SliceUsage copy() { + return new SliceTooComplexDFAUsage(getUsageInfo().getElement(), getParent()); + } + @NotNull @Override public UsagePresentation getPresentation() { diff --git a/java/java-impl/src/com/intellij/slicer/SliceToolwindowSettings.java b/platform/lang-impl/src/com/intellij/slicer/SliceToolwindowSettings.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceToolwindowSettings.java rename to platform/lang-impl/src/com/intellij/slicer/SliceToolwindowSettings.java diff --git a/java/java-impl/src/com/intellij/slicer/SliceTreeBuilder.java b/platform/lang-impl/src/com/intellij/slicer/SliceTreeBuilder.java similarity index 85% rename from java/java-impl/src/com/intellij/slicer/SliceTreeBuilder.java rename to platform/lang-impl/src/com/intellij/slicer/SliceTreeBuilder.java index e795d62ef7f9..badf32674369 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceTreeBuilder.java +++ b/platform/lang-impl/src/com/intellij/slicer/SliceTreeBuilder.java @@ -85,8 +85,12 @@ public class SliceTreeBuilder extends AbstractTreeBuilder { } public void switchToGroupedByLeavesNodes() { + SliceLanguageSupportProvider provider = getRootSliceNode().getProvider(); + if(provider == null){ + return; + } analysisInProgress = true; - SliceLeafAnalyzer.startAnalyzeValues(getTreeStructure(), new Runnable(){ + provider.startAnalyzeLeafValues(getTreeStructure(), new Runnable(){ @Override public void run() { analysisInProgress = false; @@ -96,8 +100,20 @@ public class SliceTreeBuilder extends AbstractTreeBuilder { public void switchToLeafNulls() { + SliceLanguageSupportProvider provider = getRootSliceNode().getProvider(); + if(provider == null){ + return; + } analysisInProgress = true; - SliceNullnessAnalyzer.startAnalyzeNullness(getTreeStructure(), new Runnable(){ + provider.startAnalyzeLeafValues(getTreeStructure(), new Runnable(){ + @Override + public void run() { + analysisInProgress = false; + } + }); + + analysisInProgress = true; + provider.startAnalyzeNullness(getTreeStructure(), new Runnable(){ @Override public void run() { analysisInProgress = false; diff --git a/java/java-impl/src/com/intellij/slicer/SliceTreeStructure.java b/platform/lang-impl/src/com/intellij/slicer/SliceTreeStructure.java similarity index 100% rename from java/java-impl/src/com/intellij/slicer/SliceTreeStructure.java rename to platform/lang-impl/src/com/intellij/slicer/SliceTreeStructure.java diff --git a/java/java-impl/src/com/intellij/slicer/SliceUsage.java b/platform/lang-impl/src/com/intellij/slicer/SliceUsage.java similarity index 62% rename from java/java-impl/src/com/intellij/slicer/SliceUsage.java rename to platform/lang-impl/src/com/intellij/slicer/SliceUsage.java index ef68b0d6a28e..f63ff6b42316 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceUsage.java +++ b/platform/lang-impl/src/com/intellij/slicer/SliceUsage.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -21,7 +21,6 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Computable; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiSubstitutor; import com.intellij.usageView.UsageInfo; import com.intellij.usages.UsageInfo2UsageAdapter; import com.intellij.util.CommonProcessors; @@ -32,40 +31,23 @@ import org.jetbrains.annotations.NotNull; /** * @author cdr */ -public class SliceUsage extends UsageInfo2UsageAdapter { +public abstract class SliceUsage extends UsageInfo2UsageAdapter { private final SliceUsage myParent; public final SliceAnalysisParams params; - private final PsiSubstitutor mySubstitutor; - protected final int indexNesting; // 0 means bare expression 'x', 1 means x[?], 2 means x[?][?] etc - @NotNull protected final String syntheticField; // "" means no field, otherwise it's a name of fake field of container, e.g. "keys" for Map public SliceUsage(@NotNull PsiElement element, - @NotNull SliceUsage parent, - @NotNull PsiSubstitutor substitutor, - int indexNesting, - @NotNull String syntheticField) { + @NotNull SliceUsage parent) { super(new UsageInfo(element)); myParent = parent; - mySubstitutor = substitutor; - this.syntheticField = syntheticField; params = parent.params; assert params != null; - this.indexNesting = indexNesting; } // root usage - private SliceUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) { + protected SliceUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) { super(new UsageInfo(element)); myParent = null; this.params = params; - mySubstitutor = PsiSubstitutor.EMPTY; - indexNesting = 0; - syntheticField = ""; - } - - @NotNull - public static SliceUsage createRootUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) { - return new SliceUsage(element, params); } public void processChildren(@NotNull Processor processor) { @@ -95,15 +77,19 @@ public class SliceUsage extends UsageInfo2UsageAdapter { @Override public void run() { if (params.dataFlowToThis) { - SliceUtil.processUsagesFlownDownTo(element, uniqueProcessor, SliceUsage.this, mySubstitutor, indexNesting,syntheticField); + processUsagesFlownDownTo(element, uniqueProcessor); } else { - SliceForwardUtil.processUsagesFlownFromThe(element, uniqueProcessor, SliceUsage.this); + processUsagesFlownFromThe(element, uniqueProcessor); } } }); } + protected abstract void processUsagesFlownFromThe(PsiElement element, Processor uniqueProcessor); + + protected abstract void processUsagesFlownDownTo(PsiElement element, Processor uniqueProcessor); + public SliceUsage getParent() { return myParent; } @@ -114,13 +100,5 @@ public class SliceUsage extends UsageInfo2UsageAdapter { } @NotNull - SliceUsage copy() { - PsiElement element = getUsageInfo().getElement(); - return getParent() == null ? createRootUsage(element, params) : new SliceUsage(element, getParent(),mySubstitutor,indexNesting,syntheticField); - } - - @NotNull - public PsiSubstitutor getSubstitutor() { - return mySubstitutor; - } + protected abstract SliceUsage copy(); } diff --git a/platform/lang-impl/src/com/intellij/slicer/SliceUsageCellRendererBase.java b/platform/lang-impl/src/com/intellij/slicer/SliceUsageCellRendererBase.java new file mode 100644 index 000000000000..28a10379d9f0 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/slicer/SliceUsageCellRendererBase.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2015 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.slicer; + +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.usageView.UsageTreeColors; +import com.intellij.usageView.UsageTreeColorsScheme; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; + +/** + * @author cdr + */ +public abstract class SliceUsageCellRendererBase extends ColoredTreeCellRenderer { + private static final EditorColorsScheme ourColorsScheme = UsageTreeColorsScheme.getInstance().getScheme(); + public static final SimpleTextAttributes ourInvalidAttributes = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.INVALID_PREFIX)); + + public SliceUsageCellRendererBase() { + setOpaque(false); + } + + @Override + public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + assert value instanceof DefaultMutableTreeNode; + DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value; + Object userObject = treeNode.getUserObject(); + if (userObject == null) return; + if (userObject instanceof MyColoredTreeCellRenderer) { + MyColoredTreeCellRenderer node = (MyColoredTreeCellRenderer)userObject; + node.customizeCellRenderer(this, tree, value, selected, expanded, leaf, row, hasFocus); + if (node instanceof SliceNode) { + setToolTipText(((SliceNode)node).getPresentation().getTooltip()); + } + } + else { + append(userObject.toString(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + } + } + + public abstract void customizeCellRendererFor(@NotNull SliceUsage sliceUsage); +} + diff --git a/platform/lang-impl/testSources/com/intellij/slicer/SliceTestUtil.java b/platform/lang-impl/testSources/com/intellij/slicer/SliceTestUtil.java new file mode 100644 index 000000000000..144acf4a267d --- /dev/null +++ b/platform/lang-impl/testSources/com/intellij/slicer/SliceTestUtil.java @@ -0,0 +1,136 @@ +/* + * Copyright 2000-2015 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.slicer; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.CommonProcessors; +import com.intellij.util.containers.IntArrayList; +import gnu.trove.THashMap; +import gnu.trove.TIntObjectHashMap; + +import java.util.*; + +import static junit.framework.TestCase.*; + +public class SliceTestUtil { + + private SliceTestUtil() { + } + + public static void calcRealOffsets(PsiElement startElement, Map sliceUsageName2Offset, + final TIntObjectHashMap flownOffsets) { + fill(sliceUsageName2Offset, "", startElement.getTextOffset(), flownOffsets); + } + + public static Map extractSliceOffsetsFromDocument(final Document document) { + Map sliceUsageName2Offset = new THashMap(); + + extract(document, sliceUsageName2Offset, ""); + int index = document.getText().indexOf(" sliceUsageName2Offset, String name, int offset, + final TIntObjectHashMap flownOffsets) { + for (int i=1;i<9;i++) { + String newName = name + i; + RangeMarker marker = sliceUsageName2Offset.get(newName); + if (marker == null) break; + IntArrayList offsets = flownOffsets.get(offset); + if (offsets == null) { + offsets = new IntArrayList(); + flownOffsets.put(offset, offsets); + } + int newStartOffset = marker.getStartOffset(); + offsets.add(newStartOffset); + fill(sliceUsageName2Offset, newName, newStartOffset, flownOffsets); + } + } + + private static void extract(final Document document, final Map sliceUsageName2Offset, final String name) { + WriteCommandAction.runWriteCommandAction(null, new Runnable() { + @Override + public void run() { + for (int i = 1; i < 9; i++) { + String newName = name + i; + String s = ""; + if (!document.getText().contains(s)) break; + int off = document.getText().indexOf(s); + + document.deleteString(off, off + s.length()); + RangeMarker prev = sliceUsageName2Offset.put(newName, document.createRangeMarker(off, off)); + assertNull(prev); + + extract(document, sliceUsageName2Offset, newName); + } + } + }); + } + + public static void checkUsages(final SliceUsage usage, final TIntObjectHashMap flownOffsets) { + final List children = new ArrayList(); + boolean b = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + @Override + public void run() { + usage.processChildren(new CommonProcessors.CollectProcessor(children)); + } + }, "Expanding", true, usage.getElement().getProject()); + assertTrue(b); + int startOffset = usage.getElement().getTextOffset(); + IntArrayList list = flownOffsets.get(startOffset); + int[] offsets = list == null ? new int[0] : list.toArray(); + Arrays.sort(offsets); + + int size = offsets.length; + assertEquals(message(startOffset, usage), size, children.size()); + Collections.sort(children, new Comparator() { + @Override + public int compare(SliceUsage o1, SliceUsage o2) { + return o1.compareTo(o2); + } + }); + + for (int i = 0; i < children.size(); i++) { + SliceUsage child = children.get(i); + int offset = offsets[i]; + assertEquals(message(offset, child), offset, child.getUsageInfo().getElement().getTextOffset()); + + checkUsages(child, flownOffsets); + } + } + + private static String message(int startOffset, SliceUsage usage) { + PsiFile file = usage.getElement().getContainingFile(); + Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); + Editor editor = FileEditorManager.getInstance(file.getProject()).getSelectedTextEditor(); + LogicalPosition position = editor.offsetToLogicalPosition(startOffset); + return position + ": '" + StringUtil.first(file.getText().substring(startOffset), 100, true) + "'"; + } +} diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index ac334ab32112..575142e44c0c 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -849,6 +849,10 @@ + + + + diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index c4fd589d31c0..1276bf5631f4 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -330,6 +330,11 @@ + + + diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 667b46cb8d16..dc491169370d 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -533,10 +533,6 @@ - - @@ -1114,6 +1110,8 @@ + +