mirror of
https://github.com/processing/processing4.git
synced 2026-02-02 13:21:07 +01:00
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
This commit is contained in:
@@ -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<VariableDeclarationFragment> 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<ASTNode> stack = new Stack<>();
|
||||
stack.push(root);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
ASTNode node = stack.pop();
|
||||
//log("Popped from stack: " + getNodeAsString(node));
|
||||
for (StructuralPropertyDescriptor prop : (Iterable<StructuralPropertyDescriptor>) 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<ASTNode> nodelist =
|
||||
(List<ASTNode>) 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<StructuralPropertyDescriptor>) 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<ASTNode> nodelist = (List<ASTNode>) 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<ASTNode> nodelist = (List<ASTNode>) 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 ----------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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<Boolean> 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<Problem> problems = new ArrayList<>();
|
||||
|
||||
final List<ImportStatement> programImports = new ArrayList<>();
|
||||
final List<ImportStatement> coreAndDefaultImports = new ArrayList<>();
|
||||
final List<ImportStatement> codeFolderImports = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected PreprocessedSketch checkCode() {
|
||||
|
||||
PreprocessedSketch result = new PreprocessedSketch();
|
||||
PreprocessedSketch.Builder result = new PreprocessedSketch.Builder();
|
||||
PreprocessedSketch prevResult = latestResult;
|
||||
|
||||
List<ImportStatement> coreAndDefaultImports = result.coreAndDefaultImports;
|
||||
@@ -351,7 +309,7 @@ public class ErrorCheckerService {
|
||||
List<ImportStatement> 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<IProblem> 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<Problem> 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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
194
java/src/processing/mode/java/pdex/PreprocessedSketch.java
Normal file
194
java/src/processing/mode/java/pdex/PreprocessedSketch.java
Normal file
@@ -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<Problem> problems;
|
||||
|
||||
public final List<ImportStatement> programImports;
|
||||
public final List<ImportStatement> coreAndDefaultImports;
|
||||
public final List<ImportStatement> 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<Problem> problems = new ArrayList<>();
|
||||
|
||||
public final List<ImportStatement> programImports = new ArrayList<>();
|
||||
public final List<ImportStatement> coreAndDefaultImports = new ArrayList<>();
|
||||
public final List<ImportStatement> 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user