From 6aedf098542a66f77edf67c9a4e864a20db76bdc Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Fri, 6 May 2016 23:40:42 +0200 Subject: [PATCH] Handle edge cases in offset mapping --- .../mode/java/pdex/JavaTextAreaPainter.java | 7 ++ java/src/processing/mode/java/pdex/PDEX.java | 95 ++++++++++--------- .../mode/java/pdex/PreprocessedSketch.java | 34 ++++++- .../mode/java/pdex/TextTransform.java | 23 ++++- 4 files changed, 108 insertions(+), 51 deletions(-) diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index 24a69f857..e3f3b2495 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -305,8 +305,15 @@ public class JavaTextAreaPainter extends TextAreaPainter int rightTrimmedLength = trimRight(badCode).length(); int leftTrimLength = rightTrimmedLength - trimmedLength; + // Fix offsets when bad code is just whitespace + if (trimmedLength == 0) { + leftTrimLength = 0; + rightTrimmedLength = badCode.length(); + } + int x1 = textArea.offsetToX(line, goodCode.length() + leftTrimLength); int x2 = textArea.offsetToX(line, goodCode.length() + rightTrimmedLength); + if (x1 == x2) x2 += fm.stringWidth(" "); int y1 = y + fm.getHeight() - 2; if (line != problem.getLineNumber()) { diff --git a/java/src/processing/mode/java/pdex/PDEX.java b/java/src/processing/mode/java/pdex/PDEX.java index cf683ae25..9482439cd 100644 --- a/java/src/processing/mode/java/pdex/PDEX.java +++ b/java/src/processing/mode/java/pdex/PDEX.java @@ -188,6 +188,7 @@ public class PDEX { } else { Messages.log("found declaration, offset " + decl.getStartPosition() + ", name: " + declName); SketchInterval si = ps.mapJavaToSketch(declName); + if (!ps.inRange(si)) return; EventQueue.invokeLater(() -> { editor.highlight(si.tabIndex, si.startTabOffset, si.stopTabOffset); }); @@ -315,59 +316,59 @@ public class PDEX { break; } - String elementName = binding.getName(); + // Find usages, map to tree nodes, add to root node + String bindingKey = binding.getKey(); + List intervals = + findAllOccurrences(ps.compilationUnit, bindingKey).stream() + .map(ps::mapJavaToSketch) + // remove occurrences which fall into generated header + .filter(ps::inRange) + .collect(Collectors.toList()); + + int usageCount = intervals.size(); + + // Get element name from PDE code if possible, otherwise use one from Java + String elementName = intervals.stream() + .findAny() + .map(si -> ps.pdeCode.substring(si.startPdeOffset, si.stopPdeOffset)) + .orElseGet(binding::getName); // Create root node DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(bindingType + ": " + elementName); - int usageCount; + intervals.stream() + // Convert to TreeNodes + .map(in -> ShowUsageTreeNode.fromSketchInterval(ps, in)) + // Group by tab index + .collect(Collectors.groupingBy(node -> node.tabIndex)) + // Stream Map Entries of (tab index) <-> (List) + .entrySet().stream() + // Sort by tab index + .sorted(Comparator.comparing(Map.Entry::getKey)) + .map(entry -> { + Integer tabIndex = entry.getKey(); + List nodes = entry.getValue(); - { // Find usages, map to tree nodes, add to root node - String bindingKey = binding.getKey(); - List intervals = - findAllOccurrences(ps.compilationUnit, bindingKey).stream() - .map(ps::mapJavaToSketch) - // TODO: this has to be fixed with better token mapping - // remove occurrences which fall into generated header - .filter(in -> in.tabIndex != 0 || - (in.startTabOffset >= 0 && in.stopTabOffset > 0)) - .collect(Collectors.toList()); + int count = nodes.size(); + String usageLabel = count == 1 ? "usage" : "usages"; - usageCount = intervals.size(); + // Create new DefaultMutableTreeNode for this tab + String tabLabel = "" + + ps.sketch.getCode(tabIndex).getPrettyName() + + " " + count + " " + usageLabel + ""; + DefaultMutableTreeNode tabNode = new DefaultMutableTreeNode(tabLabel); - Map> tabGroupedTreeNodes = intervals.stream() - // Convert to TreeNodes - .map(in -> ShowUsageTreeNode.fromSketchInterval(ps, in)) - // Group by tab - .collect(Collectors.groupingBy(node -> node.tabIndex)); - - tabGroupedTreeNodes.entrySet().stream() - // Sort by tab index - .sorted(Comparator.comparing(Map.Entry::getKey)) - .map(entry -> { - Integer tabIndex = entry.getKey(); - List nodes = entry.getValue(); - - int count = nodes.size(); - String usageLabel = count == 1 ? "usage" : "usages"; - - // Create new DefaultMutableTreeNode for this tab - String tabLabel = "" + - ps.sketch.getCode(tabIndex).getPrettyName() + - " " + count + " " + usageLabel + ""; - DefaultMutableTreeNode tabNode = new DefaultMutableTreeNode(tabLabel); - - nodes.stream() - // Convert TreeNodes to DefaultMutableTreeNodes - .map(DefaultMutableTreeNode::new) - // Add all as children of tab node - .forEach(tabNode::add); - return tabNode; - }) - // Add all tab nodes as children of root node - .forEach(rootNode::add); - } + // Stream nodes belonging to this tab + nodes.stream() + // Convert TreeNodes to DefaultMutableTreeNodes + .map(DefaultMutableTreeNode::new) + // Add all as children of tab node + .forEach(tabNode::add); + return tabNode; + }) + // Add all tab nodes as children of root node + .forEach(rootNode::add); TreeModel treeModel = new DefaultTreeModel(rootNode); @@ -654,6 +655,7 @@ public class PDEX { Map> mappedNodes = occurrences.stream() .map(ps::mapJavaToSketch) + .filter(ps::inRange) .collect(Collectors.groupingBy(interval -> interval.tabIndex)); Sketch sketch = ps.sketch; @@ -774,6 +776,7 @@ public class PDEX { ASTNode node = (ASTNode) tnode.getUserObject(); pps.whenDone(ps -> { SketchInterval si = ps.mapJavaToSketch(node); + if (!ps.inRange(si)) return; EventQueue.invokeLater(() -> { editor.highlight(si.tabIndex, si.startTabOffset, si.stopTabOffset); }); @@ -886,11 +889,13 @@ public class PDEX { int start = iproblem.getSourceStart(); int stop = iproblem.getSourceEnd() + 1; // make it exclusive SketchInterval in = ps.mapJavaToSketch(start, stop); + if (in == SketchInterval.BEFORE_START) return null; int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset); Problem p = new Problem(iproblem, in.tabIndex, line); p.setPDEOffsets(in.startTabOffset, in.stopTabOffset); return p; }) + .filter(p -> p != null) .collect(Collectors.toList()); // Handle import suggestions diff --git a/java/src/processing/mode/java/pdex/PreprocessedSketch.java b/java/src/processing/mode/java/pdex/PreprocessedSketch.java index a8131af6e..fb7d8a5ac 100644 --- a/java/src/processing/mode/java/pdex/PreprocessedSketch.java +++ b/java/src/processing/mode/java/pdex/PreprocessedSketch.java @@ -50,6 +50,9 @@ public class PreprocessedSketch { public static class SketchInterval { + + public static final SketchInterval BEFORE_START = new SketchInterval(-1, -1, -1, -1, -1); + private SketchInterval(int tabIndex, int startTabOffset, int stopTabOffset, int startPdeOffset, int stopPdeOffset) { @@ -69,6 +72,12 @@ public class PreprocessedSketch { } + public boolean inRange(SketchInterval interval) { + return interval != SketchInterval.BEFORE_START && + interval.stopPdeOffset < pdeCode.length(); + } + + public SketchInterval mapJavaToSketch(ASTNode node) { return mapJavaToSketch(node.getStartPosition(), node.getStartPosition() + node.getLength()); @@ -76,13 +85,29 @@ public class PreprocessedSketch { public SketchInterval mapJavaToSketch(int startJavaOffset, int stopJavaOffset) { - boolean zeroLength = stopJavaOffset == startJavaOffset; + int length = stopJavaOffset - startJavaOffset; int startPdeOffset = javaOffsetToPdeOffset(startJavaOffset); - int stopPdeOffset = zeroLength ? - javaOffsetToPdeOffset(stopJavaOffset) : - javaOffsetToPdeOffset(stopJavaOffset-1)+1; + int stopPdeOffset; + if (length == 0) { + stopPdeOffset = startPdeOffset; + } else { + stopPdeOffset = javaOffsetToPdeOffset(stopJavaOffset-1); + if (stopPdeOffset >= 0 && (stopPdeOffset > startPdeOffset || length == 1)) { + stopPdeOffset += 1; + } + } + + if (startPdeOffset < 0 || stopPdeOffset < 0) { + return SketchInterval.BEFORE_START; + } + int tabIndex = pdeOffsetToTabIndex(startPdeOffset); + if (startPdeOffset >= pdeCode.length()) { + startPdeOffset = pdeCode.length() - 1; + stopPdeOffset = startPdeOffset + 1; + } + return new SketchInterval(tabIndex, pdeOffsetToTabOffset(tabIndex, startPdeOffset), pdeOffsetToTabOffset(tabIndex, stopPdeOffset), @@ -96,6 +121,7 @@ public class PreprocessedSketch { private int pdeOffsetToTabIndex(int pdeOffset) { + pdeOffset = Math.max(0, pdeOffset); int tab = Arrays.binarySearch(tabStartOffsets, pdeOffset); if (tab < 0) { tab = -(tab + 1) - 1; diff --git a/java/src/processing/mode/java/pdex/TextTransform.java b/java/src/processing/mode/java/pdex/TextTransform.java index 88761d84c..180546439 100644 --- a/java/src/processing/mode/java/pdex/TextTransform.java +++ b/java/src/processing/mode/java/pdex/TextTransform.java @@ -132,7 +132,7 @@ public class TextTransform { // Process encountered input edits while (inEdit != null && inOffset >= inEditOff) { inOffset += inEdit.fromLength; - inMap.add(inEdit); + if (inEdit.fromLength > 0) inMap.add(inEdit); inEdit = inIt.hasNext() ? inIt.next() : null; inEditOff = inEdit != null ? inEdit.fromOffset : inLength; } @@ -140,7 +140,7 @@ public class TextTransform { // Process encountered output edits while (outEdit != null && inOffset >= outEditOff) { outEdit.toOffset = outOffset; - outMap.add(outEdit); + if (outEdit.toLength > 0) outMap.add(outEdit); outOffset += outEdit.toLength; outEdit = outIt.hasNext() ? outIt.next() : null; outEditOff = outEdit != null ? outEdit.toOffset : inLength; @@ -225,13 +225,31 @@ public class TextTransform { private List inMap = new ArrayList<>(); private List outMap = new ArrayList<>(); + private int outputOffsetOfInputStart; + private int inputOffsetOfOutputStart; + private SimpleOffsetMapper(List inMap, List outMap) { this.inMap.addAll(inMap); this.outMap.addAll(outMap); + + Edit inStart = null; + for (Edit in : this.inMap) { + inStart = in; + if (in.fromLength > 0) break; + } + outputOffsetOfInputStart = inStart == null ? 0 : inStart.toOffset; + + Edit outStart = null; + for (Edit out : this.inMap) { + outStart = out; + if (out.toLength > 0) break; + } + inputOffsetOfOutputStart = outStart == null ? 0 : outStart.fromOffset; } @Override public int getInputOffset(int outputOffset) { + if (outputOffset < outputOffsetOfInputStart) return -1; Edit searchKey = new Edit(0, 0, outputOffset, Integer.MAX_VALUE, null); int i = Collections.binarySearch(outMap, searchKey, OUTPUT_OFFSET_COMP); if (i < 0) { @@ -246,6 +264,7 @@ public class TextTransform { @Override public int getOutputOffset(int inputOffset) { + if (inputOffset < inputOffsetOfOutputStart) return -1; Edit searchKey = new Edit(inputOffset, Integer.MAX_VALUE, 0, 0, null); int i = Collections.binarySearch(inMap, searchKey, INPUT_OFFSET_COMP); if (i < 0) {