diff --git a/pdex/src/processing/mode/experimental/ASTGenerator.java b/pdex/src/processing/mode/experimental/ASTGenerator.java index b18e20c87..8f22b45bf 100644 --- a/pdex/src/processing/mode/experimental/ASTGenerator.java +++ b/pdex/src/processing/mode/experimental/ASTGenerator.java @@ -1,6 +1,8 @@ package processing.mode.experimental; import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -26,6 +28,8 @@ import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.UIManager; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; @@ -73,8 +77,16 @@ public class ASTGenerator { private DefaultMutableTreeNode currentParent = null; - private JFrame frame2, frameAutoComp; + /** + * AST Window + */ + private JFrame frame2; + + private JFrame frameAutoComp; + /** + * Swing component wrapper for AST, used for internal testing + */ private JTree jtree; private CompilationUnit compilationUnit; @@ -121,48 +133,6 @@ public class ASTGenerator { //addCompletionPopupListner(); } - public class ASTNodeWrapper { - private ASTNode node; - - private String label; - - private int lineNumber; - - private int apiLevel; - - public ASTNodeWrapper(ASTNode node) { - if (node == null) - return; - this.node = node; - label = getNodeAsString(node); - if (label == null) - label = node.toString(); - lineNumber = compilationUnit.getLineNumber(node.getStartPosition()); - label += " | Line " + lineNumber; - apiLevel = 0; - } - - public String toString() { - return label; - } - - public ASTNode getNode() { - return node; - } - - public String getLabel() { - return label; - } - - public int getNodeType() { - return node.getNodeType(); - } - - public int getLineNumber() { - return lineNumber; - } - } - private DefaultMutableTreeNode buildAST(String source, CompilationUnit cu) { if (cu == null) { ASTParser parser = ASTParser.newParser(AST.JLS4); @@ -181,10 +151,10 @@ public class ASTGenerator { } // OutlineVisitor visitor = new OutlineVisitor(); // compilationUnit.accept(visitor); -// codeTree = new DefaultMutableTreeNode( -// getNodeAsString((ASTNode) compilationUnit -// .types().get(0))); -// visitRecur((ASTNode) compilationUnit.types().get(0), codeTree); + codeTree = new DefaultMutableTreeNode( + getNodeAsString((ASTNode) compilationUnit + .types().get(0))); + visitRecur((ASTNode) compilationUnit.types().get(0), codeTree); SwingWorker worker = new SwingWorker() { @Override @@ -194,13 +164,13 @@ public class ASTGenerator { protected void done() { if (codeTree != null) { -// if (jtree.hasFocus() || frame2.hasFocus()) -// return; + if (jtree.hasFocus() || frame2.hasFocus()) + return; jtree.setModel(new DefaultTreeModel(codeTree)); ((DefaultTreeModel) jtree.getModel()).reload(); -// if (!frame2.isVisible()) { -// frame2.setVisible(true); -// } + if (!frame2.isVisible()) { + frame2.setVisible(true); + } // if (!frameAutoComp.isVisible()) { // // frameAutoComp.setVisible(true); @@ -438,6 +408,14 @@ public class ASTGenerator { return names; } + /** + * Find the parent of the expression in a().b, this would give me the return + * type of a(), so that we can find all children of a() begininng with b + * + * @param nearestNode + * @param expression + * @return + */ public static ASTNode resolveExpression(ASTNode nearestNode, ASTNode expression) { // ASTNode anode = null; @@ -1117,8 +1095,37 @@ public class ASTGenerator { System.out.println(found); } } + + private void addTreeListner(){ + jtree.addTreeSelectionListener(new TreeSelectionListener() { + + @Override + public void valueChanged(TreeSelectionEvent e) { + SwingWorker worker = new SwingWorker() { + + @Override + protected Object doInBackground() throws Exception { + return null; + } + + protected void done() { + DefaultMutableTreeNode tnode = (DefaultMutableTreeNode) jtree + .getLastSelectedPathComponent(); + ASTNodeWrapper awrap = (ASTNodeWrapper) tnode.getUserObject(); + + } + }; + worker.execute(); + } + }); + } @SuppressWarnings({ "unchecked" }) + /** + * Generates AST Swing component + * @param node + * @param tnode + */ public static void visitRecur(ASTNode node, DefaultMutableTreeNode tnode) { Iterator it = node .structuralPropertiesForType().iterator(); @@ -1136,7 +1143,7 @@ public class ASTGenerator { ASTNode cnode = (ASTNode) node.getStructuralProperty(prop); if (isAddableASTNode(cnode)) { ctnode = new DefaultMutableTreeNode( - getNodeAsString((ASTNode) node + new ASTNodeWrapper((ASTNode) node .getStructuralProperty(prop))); tnode.add(ctnode); visitRecur(cnode, ctnode); @@ -1153,7 +1160,7 @@ public class ASTGenerator { .getStructuralProperty(prop); for (ASTNode cnode : nodelist) { if (isAddableASTNode(cnode)) { - ctnode = new DefaultMutableTreeNode(getNodeAsString(cnode)); + ctnode = new DefaultMutableTreeNode(new ASTNodeWrapper(cnode)); tnode.add(ctnode); visitRecur(cnode, ctnode); } else @@ -1736,6 +1743,45 @@ public class ASTGenerator { public static boolean isAddableASTNode(ASTNode node) { return true; } + + /** + * For any line or expression, finds the line start offset(java code). + * @param node + * @return + */ + public int getASTNodeLineStartOffset(ASTNode node){ + int nodeLineNo = getLineNumber(node); + while(node.getParent() != null){ + if (getLineNumber(node.getParent()) == nodeLineNo) { + node = node.getParent(); + } else { + break; + } + } + return node.getStartPosition(); + } + + /** + * For any node, finds various offsets (java code). + * + * @param node + * @return int[]{line number, line number start offset, node start offset, + * node length} + */ + public int[] getASTNodeAllOffsets(ASTNode node){ + int nodeLineNo = getLineNumber(node), nodeOffset = node.getStartPosition(), nodeLength = node + .getLength(); + while(node.getParent() != null){ + if (getLineNumber(node.getParent()) == nodeLineNo) { + node = node.getParent(); + } else { + break; + } + } + return new int[]{nodeLineNo, node.getStartPosition(), nodeOffset,nodeLength}; + } + + static private String getNodeAsString(ASTNode node) { if (node == null) diff --git a/pdex/src/processing/mode/experimental/ASTNodeWrapper.java b/pdex/src/processing/mode/experimental/ASTNodeWrapper.java new file mode 100644 index 000000000..d2c3bdcf1 --- /dev/null +++ b/pdex/src/processing/mode/experimental/ASTNodeWrapper.java @@ -0,0 +1,142 @@ +package processing.mode.experimental; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ExpressionStatement; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.TypeDeclaration; + +public class ASTNodeWrapper { + private ASTNode node; + + private String label; + + private int lineNumber; + + //private int apiLevel; + + /* + * TODO: Every ASTNode object in ASTGenerator.codetree is stored as a + * ASTNodeWrapper instance. So how resource heavy would it be to store a + * pointer to ECS in every instance of ASTNodeWrapper? Currently I will rather + * pass an ECS pointer in the argument when I need to access a method which + * requires a method defined in ECS, i.e, only on demand. + * Bad design choice for ECS methods? IDK, yet. + */ + + public ASTNodeWrapper(ASTNode node) { + if (node == null) + return; + this.node = node; + label = getNodeAsString(node); + if (label == null) + label = node.toString(); + lineNumber = getLineNumber(node); + label += " | Line " + lineNumber; + //apiLevel = 0; + } + + /** + * For this node, finds various offsets (java code). + * + * @return int[]{line number, line number start offset, node start offset, + * node length} + */ + public int[] getJavaCodeOffsets() { + int nodeLineNo = getLineNumber(node), nodeOffset = node.getStartPosition(), nodeLength = node + .getLength(); + ASTNode thisNode = node; + while (thisNode.getParent() != null) { + if (getLineNumber(node.getParent()) == nodeLineNo) { + node = node.getParent(); + } else { + break; + } + } + return new int[] { + nodeLineNo, node.getStartPosition(), nodeOffset, nodeLength }; + } + + /** + * + * @param ecs + * - ErrorCheckerService instance + */ + public void getPDECodeOffsets(ErrorCheckerService ecs) { + + } + + public String toString() { + return label; + } + + public ASTNode getNode() { + return node; + } + + public String getLabel() { + return label; + } + + public int getNodeType() { + return node.getNodeType(); + } + + public int getLineNumber() { + return lineNumber; + } + + private static int getLineNumber(ASTNode node) { + return ((CompilationUnit) node.getRoot()).getLineNumber(node + .getStartPosition()); + } + + static private String getNodeAsString(ASTNode node) { + if (node == null) + return "NULL"; + String className = node.getClass().getName(); + int index = className.lastIndexOf("."); + if (index > 0) + className = className.substring(index + 1); + + // if(node instanceof BodyDeclaration) + // return className; + + String value = className; + + if (node instanceof TypeDeclaration) + value = ((TypeDeclaration) node).getName().toString() + " | " + className; + else if (node instanceof MethodDeclaration) + value = ((MethodDeclaration) node).getName().toString() + " | " + + className; + else if (node instanceof MethodInvocation) + value = ((MethodInvocation) node).getName().toString() + " | " + + className; + else if (node instanceof FieldDeclaration) + value = ((FieldDeclaration) node).toString() + " FldDecl| "; + else if (node instanceof SingleVariableDeclaration) + value = ((SingleVariableDeclaration) node).getName() + " - " + + ((SingleVariableDeclaration) node).getType() + " | SVD "; + else if (node instanceof ExpressionStatement) + value = node.toString() + className; + else if (node instanceof SimpleName) + value = ((SimpleName) node).getFullyQualifiedName() + " | " + className; + else if (node instanceof QualifiedName) + value = node.toString() + " | " + className; + else if (className.startsWith("Variable")) + value = node.toString() + " | " + className; + else if (className.endsWith("Type")) + value = node.toString() + " |" + className; + value += " [" + node.getStartPosition() + "," + + (node.getStartPosition() + node.getLength()) + "]"; + value += " Line: " + + ((CompilationUnit) node.getRoot()).getLineNumber(node + .getStartPosition()); + return value; + } +} \ No newline at end of file diff --git a/pdex/src/processing/mode/experimental/ErrorCheckerService.java b/pdex/src/processing/mode/experimental/ErrorCheckerService.java index a03965ada..0c4e9bbe1 100644 --- a/pdex/src/processing/mode/experimental/ErrorCheckerService.java +++ b/pdex/src/processing/mode/experimental/ErrorCheckerService.java @@ -725,6 +725,89 @@ public class ErrorCheckerService implements Runnable{ } } + /** + * Maps offset from java code to pde code. Returns a bunch of offsets as array + * + * @param line + * - line number in java code + * @param offset + * - offset from the start of the 'line' + * @return int[0] - tab number, int[1] - line number, int[2] - line start + * offset, int[3] - offset from line start int[2] and int[3] are on + * TODO + */ + public int[] JavaToPdeOffsets(int line, int offset){ + int codeIndex = 0; + + int x = line - mainClassOffset; + if (x < 0) { + // System.out.println("Negative line number " + // + problem.getSourceLineNumber() + " , offset " + // + mainClassOffset); + x = line - 2; // Another -1 for 0 index + if (x < programImports.size() && x >= 0) { + ImportStatement is = programImports.get(x); + // System.out.println(is.importName + ", " + is.tab + ", " + // + is.lineNumber); + return new int[] { is.tab, is.lineNumber }; + } else { + + // Some seriously ugly stray error, just can't find the source + // line! Simply return first line for first tab. + return new int[] { 0, 1 }; + } + + } + + try { + for (SketchCode sc : editor.getSketch().getCode()) { + if (sc.isExtension("pde")) { + int len = 0; + if (editor.getSketch().getCurrentCode().equals(sc)) { + len = Base.countLines(sc.getDocument().getText(0, + sc.getDocument().getLength())) + 1; + } else { + len = Base.countLines(sc.getProgram()) + 1; + } + + // System.out.println("x,len, CI: " + x + "," + len + "," + // + codeIndex); + + if (x >= len) { + + // We're in the last tab and the line count is greater + // than the no. + // of lines in the tab, + if (codeIndex >= editor.getSketch().getCodeCount() - 1) { + // System.out.println("Exceeds lc " + x + "," + len + // + problem.toString()); + // x = len + x = editor.getSketch().getCode(codeIndex) + .getLineCount(); + // TODO: Obtain line having last non-white space + // character in the code. + break; + } else { + x -= len; + codeIndex++; + } + } else { + + if (codeIndex >= editor.getSketch().getCodeCount()) { + codeIndex = editor.getSketch().getCodeCount() - 1; + } + break; + } + + } + } + } catch (Exception e) { + System.err + .println("Things got messed up in ErrorCheckerService.JavaToPdeOffset()"); + } + return new int[] { codeIndex, x }; + } + /** * Calculates the tab number and line number of the error in that particular * tab. Provides mapping between pure java and pde code. diff --git a/pdex/src/processing/mode/experimental/TextAreaPainter.java b/pdex/src/processing/mode/experimental/TextAreaPainter.java index dd20d67e6..9ee6b9539 100644 --- a/pdex/src/processing/mode/experimental/TextAreaPainter.java +++ b/pdex/src/processing/mode/experimental/TextAreaPainter.java @@ -33,7 +33,6 @@ import javax.swing.text.Utilities; import processing.app.syntax.TextAreaDefaults; import processing.app.syntax.TokenMarker; -import processing.mode.experimental.ASTGenerator.ASTNodeWrapper; /** * Customized line painter. Adds support for background colors, left hand gutter