mirror of
https://github.com/processing/processing4.git
synced 2026-02-04 06:09:17 +01:00
Handle edge cases in offset mapping
This commit is contained in:
@@ -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()) {
|
||||
|
||||
@@ -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<SketchInterval> 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<ShowUsageTreeNode>)
|
||||
.entrySet().stream()
|
||||
// Sort by tab index
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.map(entry -> {
|
||||
Integer tabIndex = entry.getKey();
|
||||
List<ShowUsageTreeNode> nodes = entry.getValue();
|
||||
|
||||
{ // Find usages, map to tree nodes, add to root node
|
||||
String bindingKey = binding.getKey();
|
||||
List<SketchInterval> 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 = "<html><font color=#222222>" +
|
||||
ps.sketch.getCode(tabIndex).getPrettyName() +
|
||||
"</font> <font color=#999999>" + count + " " + usageLabel + "</font></html>";
|
||||
DefaultMutableTreeNode tabNode = new DefaultMutableTreeNode(tabLabel);
|
||||
|
||||
Map<Integer, List<ShowUsageTreeNode>> 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<ShowUsageTreeNode> nodes = entry.getValue();
|
||||
|
||||
int count = nodes.size();
|
||||
String usageLabel = count == 1 ? "usage" : "usages";
|
||||
|
||||
// Create new DefaultMutableTreeNode for this tab
|
||||
String tabLabel = "<html><font color=#222222>" +
|
||||
ps.sketch.getCode(tabIndex).getPrettyName() +
|
||||
"</font> <font color=#999999>" + count + " " + usageLabel + "</font></html>";
|
||||
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<Integer, List<SketchInterval>> 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Edit> inMap = new ArrayList<>();
|
||||
private List<Edit> outMap = new ArrayList<>();
|
||||
|
||||
private int outputOffsetOfInputStart;
|
||||
private int inputOffsetOfOutputStart;
|
||||
|
||||
private SimpleOffsetMapper(List<Edit> inMap, List<Edit> 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) {
|
||||
|
||||
Reference in New Issue
Block a user