From 55e955b9fb2770cbefdc40d6852cf2e58fd722af Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Sat, 16 Apr 2016 22:55:21 +0200 Subject: [PATCH] ECS + ASTGen: big offset fix - PreprocessedSketch is now top level class - go do declaration (ctrl+click) was fixed and is now handled via JDT - various offset conversions were simplified and code completion now works as before --- .../mode/java/pdex/ASTGenerator.java | 366 ++++-------------- .../mode/java/pdex/ErrorCheckerService.java | 134 ++----- .../mode/java/pdex/JavaTextArea.java | 7 +- .../mode/java/pdex/JavaTextAreaPainter.java | 4 +- .../mode/java/pdex/PreprocessedSketch.java | 194 ++++++++++ 5 files changed, 313 insertions(+), 392 deletions(-) create mode 100644 java/src/processing/mode/java/pdex/PreprocessedSketch.java diff --git a/java/src/processing/mode/java/pdex/ASTGenerator.java b/java/src/processing/mode/java/pdex/ASTGenerator.java index ec0b50976..c580dd57e 100644 --- a/java/src/processing/mode/java/pdex/ASTGenerator.java +++ b/java/src/processing/mode/java/pdex/ASTGenerator.java @@ -57,9 +57,6 @@ import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.Element; -import javax.swing.text.PlainDocument; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; @@ -75,9 +72,11 @@ import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.QualifiedName; @@ -95,13 +94,10 @@ import org.jsoup.nodes.Document; import org.jsoup.select.Elements; import processing.app.Messages; -import processing.app.SketchCode; -import processing.app.Util; import processing.app.syntax.JEditTextArea; import processing.app.ui.EditorStatus; import processing.app.ui.Toolkit; import processing.mode.java.JavaEditor; -import processing.mode.java.pdex.ErrorCheckerService.PreprocessedSketch; import com.google.classpath.ClassPath; import com.google.classpath.RegExpResourceFilter; @@ -1028,122 +1024,36 @@ public class ASTGenerator { * @param scrollOnly * @return */ - // TODO: nuke this in favor of NodeFinder - public ASTNode getASTNodeAt(int lineNumber, String name, int offset, - boolean scrollOnly) { + public ASTNode getASTNodeAt(int offset) { + Messages.log("* getASTNodeAt"); - // Convert tab based pde line number to actual line number - int pdeLineNumber = lineNumber + errorCheckerService.mainClassOffset; -// log("----getASTNodeAt---- CU State: " -// + errorCheckerService.compilationUnitState); - int codeIndex = editor.getSketch().getCodeIndex(editor.getCurrentTab()); - if (codeIndex > 0) { - for (int i = 0; i < codeIndex; i++) { - SketchCode sc = editor.getSketch().getCode(i); - int len = Util.countLines(sc.getProgram()) + 1; - pdeLineNumber += len; - } + PreprocessedSketch ps = errorCheckerService.latestResult; + int tabIndex = ps.sketch.getCodeIndex(editor.getCurrentTab()); + int javaOffset = ps.tabOffsetToJavaOffset(tabIndex, offset); + ASTNode node = NodeFinder.perform(ps.compilationUnit, javaOffset, 0); + + if (node == null) { + Messages.log("no node found"); + return null; } - // Find closest ASTNode to the linenumber -// log("getASTNodeAt: Node line number " + pdeLineNumber); - CompilationUnit compilationUnit = - errorCheckerService.latestResult.compilationUnit; - ASTNode lineNode = findLineOfNode(compilationUnit, pdeLineNumber, offset, - name); - -// log("Node text +> " + lineNode); - ASTNode decl = null; - String nodeLabel = null; - String nameOfNode = null; // The node name which is to be scrolled to - - // Obtain correspondin java code at that line, match offsets - if (lineNode != null) { - // TODO - String pdeCodeLine = ""; //errorCheckerService.getPdeCodeAtLine(editor - // .getSketch().getCurrentCodeIndex(), lineNumber); - String javaCodeLine = getJavaSourceCodeLine(pdeLineNumber); - -// log(lineNumber + " Original Line num.\nPDE :" + pdeCodeLine); -// log("JAVA:" + javaCodeLine); -// log("Clicked on: " + name + " start offset: " + offset); - // Calculate expected java offset based on the pde line - OffsetMatcher ofm = new OffsetMatcher(pdeCodeLine, javaCodeLine); - int javaOffset = ofm.getJavaOffForPdeOff(offset, name.length()) - + lineNode.getStartPosition(); -// log("JAVA ast offset: " + (javaOffset)); - - // Find the corresponding node in the AST - ASTNode simpName = dfsLookForASTNode(errorCheckerService.getLatestCU(), - name, javaOffset, - javaOffset + name.length()); - - // If node wasn't found in the AST, lineNode may contain something - if (simpName == null && lineNode instanceof SimpleName) { - switch (lineNode.getParent().getNodeType()) { - case ASTNode.TYPE_DECLARATION: - - case ASTNode.METHOD_DECLARATION: - - case ASTNode.FIELD_DECLARATION: - - case ASTNode.VARIABLE_DECLARATION_FRAGMENT: - return lineNode.getParent(); - default: - break; - } - } - - // SimpleName instance found, now find its declaration in code - if (simpName instanceof SimpleName) { - nameOfNode = simpName.toString(); - // log(getNodeAsString(simpName)); - decl = findDeclaration((SimpleName) simpName); - if (decl != null) { -// Base.loge("DECLA: " + decl.getClass().getName()); - nodeLabel = getLabelIfType(decl); - //retLabelString = getNodeAsString(decl); - } else { - if (scrollOnly) { - editor.statusMessage(simpName + " is not defined in this sketch", - EditorStatus.ERROR); - } - } - -// log(getNodeAsString(decl)); - - /* - // - findDecl3 testing - - ASTNode nearestNode = findClosestNode(lineNumber, - (ASTNode) compilationUnit.types() - .get(0)); - ClassMember cmem = resolveExpression3rdParty(nearestNode, - (SimpleName) simpName, - false); - if (cmem != null) { - log("CMEM-> " + cmem); - } else - log("CMEM-> null"); - */ - } - } - - if (decl != null && scrollOnly) { - /* - * For scrolling, we highlight just the name of the node, i.e., a - * SimpleName instance. But the declared node always points to the - * declared node itself, like TypeDecl, MethodDecl, etc. This is important - * since it contains all the properties. - */ - ASTNode simpName2 = getNodeName(decl, nameOfNode); - // TODO: highlight ASTNode (should not be here though) - } - - // Return the declaration wrapped as ASTNodeWrapper - return decl; + Messages.log("found " + node.toString()); + return node; } + + public static IBinding resolveBinding(ASTNode node) { + Messages.log("* resolveBinding " + node.getClass().getSimpleName()); + switch (node.getNodeType()) { + case ASTNode.SIMPLE_NAME: + return ((SimpleName) node).resolveBinding(); + // For now only used for SimpleNames, add more as needed + default: + return null; + } + } + + /** * Given a declaration type astnode, returns the SimpleName peroperty * of that node. @@ -1151,15 +1061,19 @@ public class ASTGenerator { * @param name - The name we're looking for. * @return SimpleName */ - protected static ASTNode getNodeName(ASTNode node, String name){ + protected static SimpleName getNodeName(ASTNode node, String name){ List vdfs = null; switch (node.getNodeType()) { + case ASTNode.SIMPLE_NAME: + return (SimpleName) node; case ASTNode.TYPE_DECLARATION: return ((TypeDeclaration) node).getName(); case ASTNode.METHOD_DECLARATION: return ((MethodDeclaration) node).getName(); case ASTNode.SINGLE_VARIABLE_DECLARATION: return ((SingleVariableDeclaration) node).getName(); + case ASTNode.VARIABLE_DECLARATION_FRAGMENT: + return ((VariableDeclarationFragment) node).getName(); case ASTNode.FIELD_DECLARATION: vdfs = ((FieldDeclaration) node).fragments(); break; @@ -1174,12 +1088,15 @@ public class ASTGenerator { } if (vdfs != null) { - for (VariableDeclarationFragment vdf : vdfs) { - if (vdf.getName().toString().equals(name)) { - return vdf.getName(); + if (vdfs.size() == 1) { + return vdfs.get(0).getName(); + } else { + for (VariableDeclarationFragment vdf : vdfs) { + if (vdf.getName().toString().equals(name)) { + return vdf.getName(); + } } } - } return null; } @@ -1318,10 +1235,10 @@ public class ASTGenerator { return lastClickedWord; } - public void setLastClickedWord(int lineNumber, String lastClickedWord, int offset) { + public void setLastClickedWord(int offset, String lastClickedWord) { Messages.log("* setLastClickedWord"); this.lastClickedWord = lastClickedWord; - lastClickedWordNode = getASTNodeAt(lineNumber, lastClickedWord, offset, false); + lastClickedWordNode = getASTNodeAt(offset); log("Last clicked node: " + lastClickedWordNode); } @@ -1339,11 +1256,9 @@ public class ASTGenerator { + (ta.getSelectionStart() - ta.getLineStartOffset(line)) + ", " + (ta.getSelectionStop() - ta.getLineStartOffset(line))); - int offwhitespace = ta.getLineStartNonWhiteSpaceOffset(line); ASTNode wnode; if (lastClickedWord == null || lastClickedWordNode == null) { - wnode = getASTNodeAt(line + errorCheckerService.mainClassOffset, selText, - ta.getSelectionStart() - offwhitespace, false); + wnode = getASTNodeAt(ta.getSelectionStart()); } else{ wnode = lastClickedWordNode; @@ -1459,59 +1374,6 @@ public class ASTGenerator { } } - public ASTNode dfsLookForASTNode(ASTNode root, String name, int startOffset, - int endOffset) { -// log("dfsLookForASTNode() lookin for " + name + " Offsets: " + startOffset -// + "," + endOffset); - Stack stack = new Stack<>(); - stack.push(root); - - while (!stack.isEmpty()) { - ASTNode node = stack.pop(); - //log("Popped from stack: " + getNodeAsString(node)); - for (StructuralPropertyDescriptor prop : (Iterable) node.structuralPropertiesForType()) { - if (prop.isChildProperty() || prop.isSimpleProperty()) { - if (node.getStructuralProperty(prop) instanceof ASTNode) { - ASTNode temp = (ASTNode) node.getStructuralProperty(prop); - if (temp.getStartPosition() <= startOffset - && (temp.getStartPosition() + temp.getLength()) >= endOffset) { - if (temp instanceof SimpleName) { - if (name.equals(temp.toString())) { -// log("Found simplename: " + getNodeAsString(temp)); - return temp; - } -// log("Bummer, didn't match"); - } else - stack.push(temp); - //log("Pushed onto stack: " + getNodeAsString(temp)); - } - } - } else if (prop.isChildListProperty()) { - List nodelist = - (List) node.getStructuralProperty(prop); - for (ASTNode temp : nodelist) { - if (temp.getStartPosition() <= startOffset - && (temp.getStartPosition() + temp.getLength()) >= endOffset) { - stack.push(temp); -// log("Pushed onto stack: " + getNodeAsString(temp)); - if (temp instanceof SimpleName) { - if (name.equals(temp.toString())) { -// log("Found simplename: " + getNodeAsString(temp)); - return temp; - } -// log("Bummer, didn't match"); - } else - stack.push(temp); - //log("Pushed onto stack: " + getNodeAsString(temp)); - } - } - } - } - } -// log("dfsLookForASTNode() not found " + name); - return null; - } - /* protected SketchOutline sketchOutline; @@ -1597,79 +1459,6 @@ public class ASTGenerator { } - public static void printRecur(ASTNode node) { - //Base.loge("Props of " + node.getClass().getName()); - for (StructuralPropertyDescriptor prop : (Iterable) node - .structuralPropertiesForType()) { - if (prop.isChildProperty() || prop.isSimpleProperty()) { - if (node.getStructuralProperty(prop) != null) { -// System.out -// .println(node.getStructuralProperty(prop) + " -> " + (prop)); - if (node.getStructuralProperty(prop) instanceof ASTNode) { - ASTNode cnode = (ASTNode) node.getStructuralProperty(prop); - log(getNodeAsString(cnode)); - printRecur(cnode); - } - } - } else if (prop.isChildListProperty()) { - List nodelist = (List) node - .getStructuralProperty(prop); - for (ASTNode cnode : nodelist) { - log(getNodeAsString(cnode)); - printRecur(cnode); - } - } - } - } - - - protected static ASTNode findLineOfNode(ASTNode node, int lineNumber, - int offset, String name) { - if (node == null) return null; - - CompilationUnit root = (CompilationUnit) node.getRoot(); -// log("Inside "+getNodeAsString(node) + " | " + root.getLineNumber(node.getStartPosition())); - if (root.getLineNumber(node.getStartPosition()) == lineNumber) { - // Base.loge(3 + getNodeAsString(node) + " len " + node.getLength()); - return node; -// if (offset < node.getLength()) -// return node; -// else { -// Base.loge(-11); -// return null; -// } - } - for (Object oprop : node.structuralPropertiesForType()) { - StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) oprop; - if (prop.isChildProperty() || prop.isSimpleProperty()) { - if (node.getStructuralProperty(prop) != null) { - if (node.getStructuralProperty(prop) instanceof ASTNode) { - ASTNode retNode = findLineOfNode((ASTNode) node - .getStructuralProperty(prop), - lineNumber, offset, name); - if (retNode != null) { -// Base.loge(11 + getNodeAsString(retNode)); - return retNode; - } - } - } - } else if (prop.isChildListProperty()) { - List nodelist = (List) node - .getStructuralProperty(prop); - for (ASTNode retNode : nodelist) { - - ASTNode rr = findLineOfNode(retNode, lineNumber, offset, name); - if (rr != null) { -// Base.loge(12 + getNodeAsString(rr)); - return rr; - } - } - } - } -// Base.loge("-1"); - return null; - } - /** * Give this thing a {@link Name} instance - a {@link SimpleName} from the * ASTNode for ex, and it tries its level best to locate its declaration in @@ -2543,16 +2332,11 @@ public class ASTGenerator { return candidates; } - int lineNumber = line; + PreprocessedSketch ps = errorCheckerService.latestResult; + // Adjust line number for tabbed sketches int codeIndex = editor.getSketch().getCodeIndex(editor.getCurrentTab()); - if (codeIndex > 0) { - for (int i = 0; i < codeIndex; i++) { - SketchCode sc = editor.getSketch().getCode(i); - int len = Util.countLines(sc.getProgram()) + 1; - lineNumber += len; - } - } + int lineNumber = ps.tabLineToJavaLine(codeIndex, line); // Ensure that we're not inside a comment. TODO: Binary search @@ -2914,9 +2698,38 @@ public class ASTGenerator { /// Editor stuff ------------------------------------------------------------- - public void scrollToDeclaration(int lineNumber, String name, int offset) { + public void scrollToDeclaration(int offset, String name) { Messages.log("* scrollToDeclaration"); - getASTNodeAt(lineNumber, name, offset, true); + + ASTNode node = getASTNodeAt(offset); + + if (node == null) { + return; + } + + PreprocessedSketch ps = errorCheckerService.latestResult; + + IBinding binding = resolveBinding(node); + if (binding == null) { + Messages.log("binding not resolved"); + return; + } + + String key = binding.getKey(); + ASTNode decl = ps.compilationUnit.findDeclaringNode(key); + if (decl == null) { + Messages.log("decl not found"); + return; + } + + SimpleName declName = getNodeName(decl, name); + if (declName == null) { + Messages.log("decl name not found " + decl); + return; + } + + Messages.log("decl " + decl.getStartPosition() + " " + declName); + errorCheckerService.highlightJavaRange(declName.getStartPosition(), declName.getLength()); } @@ -2925,33 +2738,6 @@ public class ASTGenerator { } - /** - * Returns the java source code line at the given line number - * @param javaLineNumber - * @return - */ - public String getJavaSourceCodeLine(int javaLineNumber) { - Messages.log("* getJavaSourceCodeLine"); - try { - PlainDocument javaSource = new PlainDocument(); - javaSource.insertString(0, errorCheckerService.latestResult.preprocessedCode, null); - Element lineElement = javaSource.getDefaultRootElement() - .getElement(javaLineNumber - 1); - if (lineElement == null) { - log("Couldn't fetch jlinenum " + javaLineNumber); - return null; - } - String javaLine = javaSource.getText(lineElement.getStartOffset(), - lineElement.getEndOffset() - - lineElement.getStartOffset()); - return javaLine; - } catch (BadLocationException e) { - Messages.loge(e + " in getJavaSourceCodeline() for jinenum: " + javaLineNumber); - } - return null; - } - - /// GUI ---------------------------------------------------------------------- diff --git a/java/src/processing/mode/java/pdex/ErrorCheckerService.java b/java/src/processing/mode/java/pdex/ErrorCheckerService.java index a68f57fae..d7d7b2aed 100644 --- a/java/src/processing/mode/java/pdex/ErrorCheckerService.java +++ b/java/src/processing/mode/java/pdex/ErrorCheckerService.java @@ -37,8 +37,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -111,14 +109,6 @@ public class ErrorCheckerService { */ private volatile boolean running; - /** - * How many lines are present till the initial class declaration? In static - * mode, this would include imports, class declaration and setup - * declaration. In nomral mode, this would include imports, class - * declaration only. It's fate is decided inside preprocessCode() - */ - public int mainClassOffset; - /** * ASTGenerator for operations on AST */ @@ -144,7 +134,7 @@ public class ErrorCheckerService { */ private final static long errorCheckInterval = 650; - protected volatile PreprocessedSketch latestResult = new PreprocessedSketch(); + protected volatile PreprocessedSketch latestResult = PreprocessedSketch.empty(); private Thread errorCheckerThread; private final BlockingQueue requestQueue = new ArrayBlockingQueue<>(1); @@ -309,41 +299,9 @@ public class ErrorCheckerService { }; - public static class PreprocessedSketch { - - Sketch sketch; - - Mode mode; - - String className; - - CompilationUnit compilationUnit; - - String[] classPathArray; - ClassPath classPath; - URLClassLoader classLoader; - - int[] tabStarts; - - String pdeCode; - String preprocessedCode; - - SourceMapping syntaxMapping; - SourceMapping compilationMapping; - - boolean hasSyntaxErrors; - boolean hasCompilationErrors; - - final List problems = new ArrayList<>(); - - final List programImports = new ArrayList<>(); - final List coreAndDefaultImports = new ArrayList<>(); - final List codeFolderImports = new ArrayList<>(); - } - protected PreprocessedSketch checkCode() { - PreprocessedSketch result = new PreprocessedSketch(); + PreprocessedSketch.Builder result = new PreprocessedSketch.Builder(); PreprocessedSketch prevResult = latestResult; List coreAndDefaultImports = result.coreAndDefaultImports; @@ -351,7 +309,7 @@ public class ErrorCheckerService { List programImports = result.programImports; Sketch sketch = result.sketch = editor.getSketch(); - String className = result.className = sketch.getName(); + String className = sketch.getName(); StringBuilder workBuffer = new StringBuilder(); @@ -438,7 +396,6 @@ public class ErrorCheckerService { List syntaxProblems = Arrays.asList(syntaxCU.getProblems()); // Update result - result.mode = mode; result.syntaxMapping = syntaxMapping; result.compilationUnit = syntaxCU; result.preprocessedCode = syntaxStage; @@ -489,7 +446,6 @@ public class ErrorCheckerService { result.classPathArray = classPathArray; } - if (!result.hasSyntaxErrors) { {{ // COMPILATION CHECK @@ -520,6 +476,7 @@ public class ErrorCheckerService { result.preprocessedCode = javaStage; result.compilationUnit = compilationCU; + // TODO: handle error stuff *after* building PreprocessedSketch List mappedCompilationProblems = mapProblems(compilationProblems, result.tabStarts, result.pdeCode, result.compilationMapping, result.syntaxMapping); @@ -556,7 +513,7 @@ public class ErrorCheckerService { }} } - return result; + return result.build(); } @@ -1016,58 +973,12 @@ public class ErrorCheckerService { } - protected static int mapJavaToTab(PreprocessedSketch sketch, int offset) { - int tab = Arrays.binarySearch(sketch.tabStarts, offset); - if (tab < 0) { - tab = -(tab + 1) - 1; - } - - return sketch.tabStarts[tab]; - } - - - protected static int mapJavaToProcessing(PreprocessedSketch sketch, int offset) { - SourceMapping syntaxMapping = sketch.syntaxMapping; - SourceMapping compilationMapping = sketch.compilationMapping; - - if (compilationMapping != null) { - offset = compilationMapping.getInputOffset(offset); - } - - if (syntaxMapping != null) { - offset = syntaxMapping.getInputOffset(offset); - } - - return offset; - } - - - protected static int mapProcessingToJava(PreprocessedSketch sketch, int offset) { - SourceMapping syntaxMapping = sketch.syntaxMapping; - SourceMapping compilationMapping = sketch.compilationMapping; - - if (syntaxMapping != null) { - offset = syntaxMapping.getOutputOffset(offset); - } - - if (compilationMapping != null) { - offset = compilationMapping.getOutputOffset(offset); - } - - return offset; - } - - // TODO: does this belong here? // Thread: EDT public void scrollToErrorLine(Problem p) { if (editor == null) return; if (p == null) return; - // Switch to tab - editor.toFront(); - editor.getSketch().setCurrentCode(p.getTabIndex()); - // Highlight the code int startOffset = p.getStartOffset(); int stopOffset = p.getStopOffset(); @@ -1075,7 +986,19 @@ public class ErrorCheckerService { int length = editor.getTextArea().getDocumentLength(); startOffset = PApplet.constrain(startOffset, 0, length); stopOffset = PApplet.constrain(stopOffset, 0, length); - editor.getTextArea().select(startOffset, stopOffset); + + highlightTabRange(p.getTabIndex(), startOffset, stopOffset); + } + + // TODO: does this belong here? + // Thread: EDT + public void highlightTabRange(int tabIndex, int startTabOffset, int stopTabOffset) { + // Switch to tab + editor.toFront(); + editor.getSketch().setCurrentCode(tabIndex); + + // Highlight the code + editor.getTextArea().select(startTabOffset, stopTabOffset); // Scroll to error line editor.getTextArea().scrollToCaret(); @@ -1083,6 +1006,27 @@ public class ErrorCheckerService { } + public void highlightJavaRange(int startJavaOffset, int javaLength) { + PreprocessedSketch ps = latestResult; + + int stopJavaOffset = startJavaOffset + javaLength; + + int startPdeOffset = ps.javaOffsetToPdeOffset(startJavaOffset); + + int stopPdeOffset = javaLength == 0 ? + startPdeOffset : + // Make the stop inclusive for the purpose of mapping + ps.javaOffsetToPdeOffset(stopJavaOffset - 1) + 1; + + int tabIndex = ps.pdeOffsetToTabIndex(startPdeOffset); + + int startTabOffset = ps.pdeOffsetToTabOffset(tabIndex, startPdeOffset); + int stopTabOffset = ps.pdeOffsetToTabOffset(tabIndex, stopPdeOffset); + + EventQueue.invokeLater(() -> highlightTabRange(tabIndex, startTabOffset, stopTabOffset)); + } + + /** * Checks if import statements in the sketch have changed. If they have, * compiler classpath needs to be updated. diff --git a/java/src/processing/mode/java/pdex/JavaTextArea.java b/java/src/processing/mode/java/pdex/JavaTextArea.java index 287042541..a46e76c8a 100644 --- a/java/src/processing/mode/java/pdex/JavaTextArea.java +++ b/java/src/processing/mode/java/pdex/JavaTextArea.java @@ -277,7 +277,6 @@ public class JavaTextArea extends JEditTextArea { return null; else { int x = xToOffset(line, evt.getX()), x2 = x + 1, x1 = x - 1; - int xLS = off - getLineStartNonWhiteSpaceOffset(line); Messages.log("x=" + x); if (x < 0 || x >= s.length()) return null; @@ -293,7 +292,6 @@ public class JavaTextArea extends JEditTextArea { if (x1 >= 0 && x1 < s.length()) { if (Character.isLetter(s.charAt(x1)) || s.charAt(x1) == '_') { word = s.charAt(x1--) + word; - xLS--; } else x1 = -1; } else @@ -321,7 +319,7 @@ public class JavaTextArea extends JEditTextArea { Messages.log("Mouse click, word: " + word.trim()); ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator(); synchronized (astGenerator) { - astGenerator.setLastClickedWord(line, word, xLS); + astGenerator.setLastClickedWord(off, word); } return word.trim(); } @@ -404,8 +402,7 @@ public class JavaTextArea extends JEditTextArea { ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator(); synchronized (astGenerator) { - int lineOffset = caretLineIndex + - editor.getErrorChecker().mainClassOffset; + int lineOffset = caretLineIndex; candidates = astGenerator.preparePredictions(phrase, lineOffset); } diff --git a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java index c8124e291..1a81aa600 100644 --- a/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java +++ b/java/src/processing/mode/java/pdex/JavaTextAreaPainter.java @@ -175,10 +175,10 @@ public class JavaTextAreaPainter extends TextAreaPainter if (Character.isDigit(word.charAt(0))) return; - Messages.log(getJavaEditor().getErrorChecker().mainClassOffset + line + "|" + line + "| offset " + xLS + word + " <= \n"); + Messages.log(line + "|" + line + "| offset " + xLS + word + " <= \n"); ASTGenerator astGenerator = getJavaEditor().getErrorChecker().getASTGenerator(); synchronized (astGenerator) { - astGenerator.scrollToDeclaration(line, word, xLS); + astGenerator.scrollToDeclaration(off, word); } } } diff --git a/java/src/processing/mode/java/pdex/PreprocessedSketch.java b/java/src/processing/mode/java/pdex/PreprocessedSketch.java new file mode 100644 index 000000000..22050832c --- /dev/null +++ b/java/src/processing/mode/java/pdex/PreprocessedSketch.java @@ -0,0 +1,194 @@ +package processing.mode.java.pdex; + +import com.google.classpath.ClassPath; + +import org.eclipse.jdt.core.dom.CompilationUnit; + +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import processing.app.Sketch; + +public class PreprocessedSketch { + + public final Sketch sketch; + + public final CompilationUnit compilationUnit; + + public final String[] classPathArray; + public final ClassPath classPath; + public final URLClassLoader classLoader; + + public final int[] tabStarts; + + public final String pdeCode; + public final String preprocessedCode; + + public final SourceMapping syntaxMapping; + public final SourceMapping compilationMapping; + + public final boolean hasSyntaxErrors; + public final boolean hasCompilationErrors; + + public final List problems; + + public final List programImports; + public final List coreAndDefaultImports; + public final List codeFolderImports; + + + // TODO: optimize + public static int lineToOffset(String text, int line) { + int lineOffset = 0; + for (int i = 0; i < line && lineOffset >= 0; i++) { + lineOffset = text.indexOf('\n', lineOffset+1); + } + return lineOffset + 1; + } + + + // TODO: optimize + public static int offsetToLine(String text, int offset) { + int line = 0; + while (offset >= 0) { + offset = text.lastIndexOf('\n', offset-1); + line++; + } + return line - 1; + } + + + // TODO: optimize, build lookup together with tabStarts + public int tabIndexToTabStartLine(int tabIndex) { + int pdeLineNumber = 0; + for (int i = 0; i < tabIndex; i++) { + pdeLineNumber += sketch.getCode(i).getLineCount(); + } + return pdeLineNumber; + } + + + public int tabLineToJavaLine(int tabIndex, int tabLine) { + int tabStartLine = tabIndexToTabStartLine(tabIndex); + int pdeLine = tabStartLine + tabLine; + int pdeLineOffset = lineToOffset(pdeCode, pdeLine); + int javaLineOffset = syntaxMapping.getOutputOffset(pdeLineOffset); + if (compilationMapping != null) { + javaLineOffset = compilationMapping.getOutputOffset(javaLineOffset); + } + return offsetToLine(preprocessedCode, javaLineOffset); + } + + + public int tabOffsetToJavaOffset(int tabIndex, int tabOffset) { + int tabStartLine = tabIndexToTabStartLine(tabIndex); + int tabStartOffset = lineToOffset(pdeCode, tabStartLine); + int pdeOffset = tabStartOffset + tabOffset; + int javaOffset = syntaxMapping.getOutputOffset(pdeOffset); + if (compilationMapping != null) { + javaOffset = compilationMapping.getOutputOffset(javaOffset); + } + return javaOffset; + } + + + public int javaOffsetToPdeOffset(int javaOffset) { + int pdeOffset = javaOffset; + if (compilationMapping != null) { + pdeOffset = compilationMapping.getInputOffset(pdeOffset); + } + if (syntaxMapping != null) { + pdeOffset = syntaxMapping.getInputOffset(pdeOffset); + } + return pdeOffset; + } + + + public int pdeOffsetToTabIndex(int pdeOffset) { + int tab = Arrays.binarySearch(tabStarts, pdeOffset); + if (tab < 0) { + tab = -(tab + 1) - 1; + } + return tab; + } + + + public int pdeOffsetToTabOffset(int tabIndex, int pdeOffset) { + int tabStartOffset = tabStarts[tabIndex]; + return pdeOffset - tabStartOffset; + } + + + /// BUILDER BUSINESS ///////////////////////////////////////////////////////// + + /** + * There is a lot of fields and having constructor with this many parameters + * is just not practical. Fill stuff into builder and then simply build it. + * Builder also guards against calling methods in the middle of building process. + */ + + public static class Builder { + public Sketch sketch; + + public CompilationUnit compilationUnit; + + public String[] classPathArray; + public ClassPath classPath; + public URLClassLoader classLoader; + + public int[] tabStarts = new int[0]; + + public String pdeCode; + public String preprocessedCode; + + public SourceMapping syntaxMapping; + public SourceMapping compilationMapping; + + public boolean hasSyntaxErrors; + public boolean hasCompilationErrors; + + public final List problems = new ArrayList<>(); + + public final List programImports = new ArrayList<>(); + public final List coreAndDefaultImports = new ArrayList<>(); + public final List codeFolderImports = new ArrayList<>(); + + public PreprocessedSketch build() { + return new PreprocessedSketch(this); + } + } + + public static PreprocessedSketch empty() { + return new Builder().build(); + } + + private PreprocessedSketch(Builder b) { + sketch = b.sketch; + + compilationUnit = b.compilationUnit; + + classPathArray = b.classPathArray; + classPath = b.classPath; + classLoader = b.classLoader; + + tabStarts = b.tabStarts; + + pdeCode = b.pdeCode; + preprocessedCode = b.preprocessedCode; + + syntaxMapping = b.syntaxMapping; + compilationMapping = b.compilationMapping; + + hasSyntaxErrors = b.hasSyntaxErrors; + hasCompilationErrors = b.hasCompilationErrors; + + problems = Collections.unmodifiableList(b.problems); + + programImports = Collections.unmodifiableList(b.programImports); + coreAndDefaultImports = Collections.unmodifiableList(b.coreAndDefaultImports); + codeFolderImports = Collections.unmodifiableList(b.codeFolderImports); + } +}