Merge branch 'master' of github.com:processing/processing

This commit is contained in:
Ben Fry
2015-09-21 15:54:51 -04:00
8 changed files with 701 additions and 450 deletions

View File

@@ -31,6 +31,7 @@ import processing.app.ui.Toolkit;
import processing.mode.java.debug.LineBreakpoint;
import processing.mode.java.debug.LineHighlight;
import processing.mode.java.debug.LineID;
import processing.mode.java.pdex.ASTGenerator;
import processing.mode.java.pdex.ErrorCheckerService;
import processing.mode.java.pdex.LineMarker;
import processing.mode.java.pdex.ErrorMessageSimplifier;
@@ -2638,14 +2639,20 @@ public class JavaEditor extends Editor {
/** Handle refactor operation */
private void handleRefactor() {
Messages.log("Caret at:" + textarea.getLineText(textarea.getCaretLine()));
errorCheckerService.getASTGenerator().handleRefactor();
ASTGenerator astGenerator = errorCheckerService.getASTGenerator();
synchronized (astGenerator) {
astGenerator.handleRefactor();
}
}
/** Handle show usage operation */
private void handleShowUsage() {
Messages.log("Caret at:" + textarea.getLineText(textarea.getCaretLine()));
errorCheckerService.getASTGenerator().handleShowUsage();
ASTGenerator astGenerator = errorCheckerService.getASTGenerator();
synchronized (astGenerator) {
astGenerator.handleShowUsage();
}
}

View File

@@ -35,7 +35,6 @@ import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -43,7 +42,6 @@ import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import javax.swing.BorderFactory;
@@ -157,7 +155,6 @@ public class ASTGenerator {
//addCompletionPopupListner();
addListeners();
//loadJavaDoc();
predictionOngoing = new AtomicBoolean(false);
}
@@ -222,7 +219,7 @@ public class ASTGenerator {
protected DefaultMutableTreeNode buildAST(String source, CompilationUnit cu) {
if (cu == null) {
ASTParser parser = ASTParser.newParser(AST.JLS4);
ASTParser parser = ASTParser.newParser(AST.JLS8);
parser.setSource(source.toCharArray());
parser.setKind(ASTParser.K_COMPILATION_UNIT);
@@ -398,7 +395,7 @@ public class ASTGenerator {
}
jdocMap.put(methodName, msg);
}
System.out.println("JDoc loaded "+jdocMap.size());
System.out.println("JDoc loaded " + jdocMap.size());
}
@@ -539,11 +536,42 @@ public class ASTGenerator {
if(decl != null){
// see if locally defined
log(getNodeAsString(astNode)+" found decl -> " + getNodeAsString(decl));
{
if (decl.getNodeType() == ASTNode.TYPE_DECLARATION) {
TypeDeclaration td = (TypeDeclaration) decl;
return new ClassMember(td);
}
}
{ // Handle "array." x "array[1]."
Type type = extracTypeInfo2(decl);
if (type != null && type.isArrayType() &&
astNode.getParent().getNodeType() != ASTNode.ARRAY_ACCESS) {
// No array access, we want members of the array itself
Type elementType = ((ArrayType) type).getElementType();
// Get name of the element class
String name = "";
if (elementType.isSimpleType()) {
Class<?> c = findClassIfExists(elementType.toString());
if (c != null) name = c.getName();
} else if (elementType.isPrimitiveType()) {
name = ((PrimitiveType) elementType).getPrimitiveTypeCode().toString();
}
// Convert element class to array class
Class<?> arrayClass = getArrayClass(name);
return arrayClass == null ? null : new ClassMember(arrayClass);
}
}
return new ClassMember(extracTypeInfo(decl));
}
else {
// or in a predefined class?
Class<?> tehClass = findClassIfExists(((SimpleName) astNode).toString());
Class<?> tehClass = findClassIfExists(astNode.toString());
if (tehClass != null) {
return new ClassMember(tehClass);
}
@@ -596,6 +624,30 @@ public class ASTGenerator {
if(temp instanceof MethodDeclaration){
// method is locally defined
log(mi.getName() + " was found locally," + getNodeAsString(extracTypeInfo(temp)));
{ // Handle "array." x "array[1]."
Type type = extracTypeInfo2(temp);
if (type != null && type.isArrayType() &&
astNode.getParent().getNodeType() != ASTNode.ARRAY_ACCESS) {
// No array access, we want members of the array itself
Type elementType = ((ArrayType) type).getElementType();
// Get name of the element class
String name = "";
if (elementType.isSimpleType()) {
Class<?> c = findClassIfExists(elementType.toString());
if (c != null) name = c.getName();
} else if (elementType.isPrimitiveType()) {
name = ((PrimitiveType) elementType).getPrimitiveTypeCode().toString();
}
// Convert element class to array class
Class<?> arrayClass = getArrayClass(name);
return arrayClass == null ? null : new ClassMember(arrayClass);
}
}
return new ClassMember(extracTypeInfo(temp));
}
if (mi.getExpression() == null) {
@@ -605,38 +657,46 @@ public class ASTGenerator {
return null;
} else {
if (mi.getExpression() instanceof SimpleName) {
stp = extracTypeInfo(findDeclaration2((SimpleName) mi.getExpression(),
nearestNode));
if(stp == null){
ASTNode decl = findDeclaration2((SimpleName) mi.getExpression(),
nearestNode);
if (decl != null) {
if (decl.getNodeType() == ASTNode.TYPE_DECLARATION) {
TypeDeclaration td = (TypeDeclaration) decl;
return new ClassMember(td);
}
stp = extracTypeInfo(decl);
if(stp == null){
/*The type wasn't found in local code, so it might be something like
* System.console()., or maybe belonging to super class, etc.
*/
Class<?> tehClass = findClassIfExists(((SimpleName)mi.getExpression()).toString());
if (tehClass != null) {
// Method Expression is a simple name and wasn't located locally, but found in a class
// so look for method in this class.
return definedIn3rdPartyClass(new ClassMember(tehClass), mi
.getName().toString());
Class<?> tehClass = findClassIfExists(((SimpleName)mi.getExpression()).toString());
if (tehClass != null) {
// Method Expression is a simple name and wasn't located locally, but found in a class
// so look for method in this class.
return definedIn3rdPartyClass(new ClassMember(tehClass), mi
.getName().toString());
}
log("MI resolve 3rd par, Can't resolve " + mi.getExpression());
return null;
}
log("MI resolve 3rd par, Can't resolve " + mi.getExpression());
return null;
}
log("MI, SN Type " + getNodeAsString(stp));
ASTNode typeDec = findDeclaration2(stp.getName(),nearestNode);
if(typeDec == null){
log(stp.getName() + " couldn't be found locally..");
Class<?> tehClass = findClassIfExists(stp.getName().toString());
if (tehClass != null) {
// Method Expression is a simple name and wasn't located locally, but found in a class
// so look for method in this class.
return definedIn3rdPartyClass(new ClassMember(tehClass), mi
.getName().toString());
log("MI, SN Type " + getNodeAsString(stp));
ASTNode typeDec = findDeclaration2(stp.getName(),nearestNode);
if(typeDec == null){
log(stp.getName() + " couldn't be found locally..");
Class<?> tehClass = findClassIfExists(stp.getName().toString());
if (tehClass != null) {
// Method Expression is a simple name and wasn't located locally, but found in a class
// so look for method in this class.
return definedIn3rdPartyClass(new ClassMember(tehClass), mi
.getName().toString());
}
//return new ClassMember(findClassIfExists(stp.getName().toString()));
}
//return new ClassMember(findClassIfExists(stp.getName().toString()));
//scopeParent = definedIn3rdPartyClass(stp.getName().toString(), "THIS");
return definedIn3rdPartyClass(new ClassMember(typeDec), mi
.getName().toString());
}
//scopeParent = definedIn3rdPartyClass(stp.getName().toString(), "THIS");
return definedIn3rdPartyClass(new ClassMember(typeDec), mi
.getName().toString());
} else {
log("MI EXP.."+getNodeAsString(mi.getExpression()));
// return null;
@@ -711,6 +771,36 @@ public class ASTGenerator {
return null;
}
public Class<?> getArrayClass(String elementClass) {
String name;
if (elementClass.startsWith("[")) {
// just add a leading "["
name = "[" + elementClass;
} else if (elementClass.equals("boolean")) {
name = "[Z";
} else if (elementClass.equals("byte")) {
name = "[B";
} else if (elementClass.equals("char")) {
name = "[C";
} else if (elementClass.equals("double")) {
name = "[D";
} else if (elementClass.equals("float")) {
name = "[F";
} else if (elementClass.equals("int")) {
name = "[I";
} else if (elementClass.equals("long")) {
name = "[J";
} else if (elementClass.equals("short")) {
name = "[S";
} else {
// must be an object non-array class
name = "[L" + elementClass + ";";
}
return loadClass(name);
}
/**
* For a().abc.a123 this would return a123
*
@@ -753,151 +843,113 @@ public class ASTGenerator {
return null;
}
protected void trimCandidates(String newWord) {
ArrayList<CompletionCandidate> newCandidate = new ArrayList<CompletionCandidate>();
protected static List<CompletionCandidate> trimCandidates(String newWord, List<CompletionCandidate> candidates) {
ArrayList<CompletionCandidate> newCandidate = new ArrayList<>();
newWord = newWord.toLowerCase();
for (CompletionCandidate comp : candidates) {
if(comp.getNoHtmlLabel().toLowerCase().startsWith(newWord)){
newCandidate.add(comp);
}
}
candidates = newCandidate;
return newCandidate;
}
protected List<CompletionCandidate> candidates;
protected String lastPredictedWord = " ";
protected int predictionMinLength = 2;
private AtomicBoolean predictionOngoing;
protected String lastPredictedPhrase = " ";
protected static final int predictionMinLength = 2;
/**
* The main function that calculates possible code completion candidates
*
* @param word
* @param pdePhrase
* @param line
* @param lineStartNonWSOffset
*/
public void preparePredictions(final String word, final int line,
final int lineStartNonWSOffset) {
// EventQueue.invokeLater(new Runnable() {
// public void run() {
// preparePredictions2(word, line, lineStartNonWSOffset);
// }
// });
// }
//
// public void preparePredictions2(final String word, final int line,
// final int lineStartNonWSOffset) {
// System.out.println(EventQueue.isDispatchThread() + " " + predictionOngoing.get() + " " + (!JavaMode.codeCompletionsEnabled) + " " + (word.length() < predictionMinLength));
// new Exception("preparing predictions " + EventQueue.isDispatchThread() + " " + predictionOngoing.get() + " " + (!JavaMode.codeCompletionsEnabled) + " " + (word.length() < predictionMinLength)).printStackTrace(System.out);
if (predictionOngoing.get()) return;
if (!JavaMode.codeCompletionsEnabled) return;
if (word.length() < predictionMinLength) return;
predictionOngoing.set(true);
// This method is called from TextArea.fetchPhrase, which is called via a SwingWorker instance
// in TextArea.processKeyEvent
if (caretWithinLineComment()) {
log("No predictions.");
predictionOngoing.set(false);
return;
}
// presumably this was removed because the caller is running from a SwingWorker [fry]
// SwingWorker worker = new SwingWorker() {
//
// @Override
// protected Object doInBackground() throws Exception {
// return null;
// }
//
// protected void done() {
public List<CompletionCandidate> preparePredictions(final String pdePhrase,
final int line) {
ErrorCheckerService errorCheckerService = editor.getErrorChecker();
ASTNode astRootNode = (ASTNode) errorCheckerService.getLatestCU().types().get(0);
// If the parsed code contains pde enhancements, take 'em out.
String word2 = ASTNodeWrapper.getJavaCode(word);
String phrase = ASTNodeWrapper.getJavaCode(pdePhrase);
//After typing 'arg.' all members of arg type are to be listed. This one is a flag for it
boolean noCompare = false;
if (word2.endsWith(".")) {
// return all matches
word2 = word2.substring(0, word2.length() - 1);
noCompare = true;
boolean noCompare = phrase.endsWith(".");
if (noCompare) {
phrase = phrase.substring(0, phrase.length() - 1);
}
if (word2.length() >= predictionMinLength && !noCompare
&& word2.length() > lastPredictedWord.length()) {
if (word2.startsWith(lastPredictedWord)) {
log(word + " starts with " + lastPredictedWord);
log("Don't recalc");
if (word2.contains(".")) {
int x = word2.lastIndexOf('.');
trimCandidates(word2.substring(x + 1));
} else {
trimCandidates(word2);
}
showPredictions(word);
lastPredictedWord = word2;
predictionOngoing.set(false);
return;
boolean incremental = !noCompare &&
phrase.length() > lastPredictedPhrase.length() &&
phrase.startsWith(lastPredictedPhrase);
if (incremental) {
log(pdePhrase + " starts with " + lastPredictedPhrase);
log("Don't recalc");
if (phrase.contains(".")) {
int x = phrase.lastIndexOf('.');
candidates = trimCandidates(phrase.substring(x + 1), candidates);
} else {
candidates = trimCandidates(phrase, candidates);
}
lastPredictedPhrase = phrase;
return candidates;
}
int lineNumber = line;
// Adjust line number for tabbed sketches
if (errorCheckerService != null) {
editor = errorCheckerService.getEditor();
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 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;
}
}
// Ensure that we're not inside a comment. TODO: Binary search
// Ensure that we're not inside a comment. TODO: Binary search
/*for (Comment comm : getCodeComments()) {
int commLineNo = PdeToJavaLineNumber(compilationUnit
.getLineNumber(comm.getStartPosition()));
if(commLineNo == lineNumber){
log("Found a comment line " + comm);
log("Comment LSO "
+ javaCodeOffsetToLineStartOffset(compilationUnit
.getLineNumber(comm.getStartPosition()),
comm.getStartPosition()));
break;
}
}*/
/*for (Comment comm : getCodeComments()) {
int commLineNo = PdeToJavaLineNumber(compilationUnit
.getLineNumber(comm.getStartPosition()));
if(commLineNo == lineNumber){
log("Found a comment line " + comm);
log("Comment LSO "
+ javaCodeOffsetToLineStartOffset(compilationUnit
.getLineNumber(comm.getStartPosition()),
comm.getStartPosition()));
break;
}
}*/
// Now parse the expression into an ASTNode object
ASTNode nearestNode = null;
ASTParser parser = ASTParser.newParser(AST.JLS4);
ASTParser parser = ASTParser.newParser(AST.JLS8);
parser.setKind(ASTParser.K_EXPRESSION);
parser.setSource(word2.toCharArray());
parser.setSource(phrase.toCharArray());
ASTNode testnode = parser.createAST(null);
//Base.loge("PREDICTION PARSER PROBLEMS: " + parser);
// Find closest ASTNode of the document to this word
Messages.loge("Typed: " + word2 + "|" + " temp Node type: " + testnode.getClass().getSimpleName());
Messages.loge("Typed: " + phrase + "|" + " temp Node type: " + testnode.getClass().getSimpleName());
if(testnode instanceof MethodInvocation){
MethodInvocation mi = (MethodInvocation)testnode;
log(mi.getName() + "," + mi.getExpression() + "," + mi.typeArguments().size());
}
// find nearest ASTNode
nearestNode = findClosestNode(lineNumber, (ASTNode) errorCheckerService.getLastCorrectCU().types()
.get(0));
nearestNode = findClosestNode(lineNumber, astRootNode);
if (nearestNode == null) {
// Make sure nearestNode is not NULL if couldn't find a closeset node
nearestNode = (ASTNode) errorCheckerService.getLastCorrectCU().types().get(0);
nearestNode = astRootNode;
}
Messages.loge(lineNumber + " Nearest ASTNode to PRED "
+ getNodeAsString(nearestNode));
candidates = new ArrayList<CompletionCandidate>();
lastPredictedWord = word2;
candidates = new ArrayList<>();
lastPredictedPhrase = phrase;
// Determine the expression typed
if (testnode instanceof SimpleName && !noCompare) {
@@ -916,7 +968,7 @@ public class ASTGenerator {
SimpleType st = (SimpleType) td.getStructuralProperty(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY);
log("Superclass " + st.getName());
ArrayList<CompletionCandidate> tempCandidates =
getMembersForType(st.getName().toString(), word2, noCompare, false);
getMembersForType(st.getName().toString(), phrase, noCompare, false);
for (CompletionCandidate can : tempCandidates) {
candidates.add(can);
}
@@ -933,7 +985,7 @@ public class ASTGenerator {
CompletionCandidate[] types = checkForTypes(cnode);
if (types != null) {
for (int i = 0; i < types.length; i++) {
if (types[i].getElementName().toLowerCase().startsWith(word2.toLowerCase()))
if (types[i].getElementName().toLowerCase().startsWith(phrase.toLowerCase()))
candidates.add(types[i]);
}
}
@@ -946,7 +998,7 @@ public class ASTGenerator {
CompletionCandidate[] types = checkForTypes(clnode);
if (types != null) {
for (int i = 0; i < types.length; i++) {
if (types[i].getElementName().toLowerCase().startsWith(word2.toLowerCase()))
if (types[i].getElementName().toLowerCase().startsWith(phrase.toLowerCase()))
candidates.add(types[i]);
}
}
@@ -957,11 +1009,11 @@ public class ASTGenerator {
}
// We're seeing a simple name that's not defined locally or in
// the parent class. So most probably a pre-defined type.
log("Empty can. " + word2);
log("Empty can. " + phrase);
if (classPath != null) {
RegExpResourceFilter regExpResourceFilter =
new RegExpResourceFilter(Pattern.compile(".*"),
Pattern.compile(word2 + "[a-zA-Z_0-9]*.class",
Pattern.compile(phrase + "[a-zA-Z_0-9]*.class",
Pattern.CASE_INSENSITIVE));
String[] resources = classPath.findResources("", regExpResourceFilter);
@@ -969,7 +1021,7 @@ public class ASTGenerator {
matchedClass2 = matchedClass2.replace('/', '.'); //package name
String matchedClass = matchedClass2.substring(0, matchedClass2.length() - 6);
int d = matchedClass.lastIndexOf('.');
if (!ignorableImport(matchedClass,matchedClass.substring(d + 1))) {
if (!errorCheckerService.ignorableSuggestionImport(matchedClass)) {
matchedClass = matchedClass.substring(d + 1); //class name
// display package name in grey
String html = "<html>" + matchedClass + " : <font color=#777777>" +
@@ -988,62 +1040,33 @@ public class ASTGenerator {
ASTNode childExpr = getChildExpression(testnode);
log("Parent expression : " + getParentExpression(testnode));
log("Child expression : " + childExpr);
if (childExpr != null) {
if (!noCompare) {
log("Original testnode " + getNodeAsString(testnode));
testnode = getParentExpression(testnode);
log("Corrected testnode " + getNodeAsString(testnode));
}
ClassMember expr =
if (!noCompare) {
log("Original testnode " + getNodeAsString(testnode));
testnode = getParentExpression(testnode);
log("Corrected testnode " + getNodeAsString(testnode));
}
ClassMember expr =
resolveExpression3rdParty(nearestNode, testnode, noCompare);
if (expr == null) {
log("Expr is null");
} else {
log("Expr is " + expr.toString());
candidates = getMembersForType(expr, childExpr.toString(),
noCompare, false);
}
if (expr == null) {
log("Expr is null");
} else {
log("ChildExpr is null");
boolean isArray = expr.thisclass != null && expr.thisclass.isArray();
boolean isSimpleType = (expr.astNode != null) &&
expr.astNode.getNodeType() == ASTNode.SIMPLE_TYPE;
boolean isMethod = expr.method != null;
boolean staticOnly = !isMethod && !isArray && !isSimpleType;
log("Expr is " + expr.toString());
String lookFor = (noCompare || (childExpr == null)) ?
"" : childExpr.toString();
candidates = getMembersForType(expr, lookFor, noCompare, staticOnly);
}
}
showPredictions(word);
predictionOngoing.set(false);
// }
// };
//
// worker.execute();
return candidates;
}
protected void showPredictions(final String word) {
if (sketchOutline != null && sketchOutline.isVisible()) {
// don't show completions when the outline is visible
return;
}
Collections.sort(candidates);
// CompletionCandidate[][] candi = new CompletionCandidate[candidates.size()][1];
// DefaultListModel<CompletionCandidate> defListModel = new DefaultListModel<CompletionCandidate>();
//
// for (int i = 0; i < candidates.size(); i++) {
//// candi[i][0] = candidates.get(i);
// defListModel.addElement(candidates.get(i));
// }
// log("Total preds = " + candidates.size());
DefaultListModel<CompletionCandidate> defListModel = filterPredictions();
// DefaultTableModel tm = new DefaultTableModel(candi,
// new String[] { "Suggestions" });
// if (tableAuto.isVisible()) {
// tableAuto.setModel(tm);
// tableAuto.validate();
// tableAuto.repaint();
// }
errorCheckerService.getEditor().getJavaTextArea().showSuggestion(defListModel, word);
}
private DefaultListModel<CompletionCandidate> filterPredictions(){
DefaultListModel<CompletionCandidate> defListModel = new DefaultListModel<CompletionCandidate>();
protected static DefaultListModel<CompletionCandidate> filterPredictions(List<CompletionCandidate> candidates){
DefaultListModel<CompletionCandidate> defListModel = new DefaultListModel<>();
if (candidates.isEmpty())
return defListModel;
// check if first & last CompCandidate are the same methods, only then show all overloaded methods
@@ -1117,7 +1140,7 @@ public class ASTGenerator {
boolean noCompare,
boolean staticOnly) {
String child = childToLookFor.toLowerCase();
ArrayList<CompletionCandidate> candidates = new ArrayList<CompletionCandidate>();
ArrayList<CompletionCandidate> candidates = new ArrayList<>();
log("getMemFoType-> Looking for match " + child.toString()
+ " inside " + tehClass + " noCompare " + noCompare + " staticOnly "
+ staticOnly);
@@ -1126,28 +1149,34 @@ public class ASTGenerator {
}
// tehClass will either be a TypeDecl defined locally
if(tehClass.getDeclaringNode() instanceof TypeDeclaration){
TypeDeclaration td = (TypeDeclaration) tehClass.getDeclaringNode();
for (int i = 0; i < td.getFields().length; i++) {
List<VariableDeclarationFragment> vdfs = td.getFields()[i]
.fragments();
for (VariableDeclarationFragment vdf : vdfs) {
if (noCompare) {
candidates
.add(new CompletionCandidate(vdf));
} else if (vdf.getName().toString().toLowerCase()
.startsWith(child))
candidates
.add(new CompletionCandidate(vdf));
{
FieldDeclaration[] fields = td.getFields();
for (int i = 0; i < fields.length; i++) {
if (staticOnly && !isStatic(fields[i].modifiers())) {
continue;
}
List<VariableDeclarationFragment> vdfs = fields[i].fragments();
for (VariableDeclarationFragment vdf : vdfs) {
if (noCompare) {
candidates.add(new CompletionCandidate(vdf));
} else if (vdf.getName().toString().toLowerCase().startsWith(child))
candidates.add(new CompletionCandidate(vdf));
}
}
}
for (int i = 0; i < td.getMethods().length; i++) {
if (noCompare) {
candidates.add(new CompletionCandidate(td.getMethods()[i]));
} else if (td.getMethods()[i].getName().toString().toLowerCase()
.startsWith(child))
candidates.add(new CompletionCandidate(td.getMethods()[i]));
{
MethodDeclaration[] methods = td.getMethods();
for (int i = 0; i < methods.length; i++) {
if (staticOnly && !isStatic(methods[i].modifiers())) {
continue;
}
if (noCompare) {
candidates.add(new CompletionCandidate(methods[i]));
} else if (methods[i].getName().toString().toLowerCase()
.startsWith(child))
candidates.add(new CompletionCandidate(methods[i]));
}
}
ArrayList<CompletionCandidate> superClassCandidates = new ArrayList<CompletionCandidate>();
@@ -1209,9 +1238,35 @@ public class ASTGenerator {
candidates.add(new CompletionCandidate(field));
}
}
if (probableClass.isArray() && !staticOnly) {
// add array members manually, they can't be fetched through code
String className = probableClass.getSimpleName();
if (noCompare || "clone()".startsWith(child)) {
String methodLabel = "<html>clone() : " + className +
" - <font color=#777777>" + className + "</font></html>";
candidates.add(new CompletionCandidate("clone()", methodLabel, "clone()",
CompletionCandidate.PREDEF_METHOD));
}
if ("length".startsWith(child)) {
String fieldLabel = "<html>length : int - <font color=#777777>" +
className + "</font></html>";
candidates.add(new CompletionCandidate("length", fieldLabel, "length",
CompletionCandidate.PREDEF_FIELD));
}
}
return candidates;
}
private static boolean isStatic(List<org.eclipse.jdt.core.dom.Modifier> modifiers) {
for (org.eclipse.jdt.core.dom.Modifier m : modifiers) {
if (m.isStatic()) return true;
}
return false;
}
public String getPDESourceCodeLine(int javaLineNumber) {
int res[] = errorCheckerService
.calculateTabIndexAndLineNumber(javaLineNumber);
@@ -1752,7 +1807,7 @@ public class ASTGenerator {
//errorCheckerService.highlightNode(simpName2);
ASTNodeWrapper declWrap = new ASTNodeWrapper(simpName2, nodeLabel);
//errorCheckerService.highlightNode(declWrap);
if (!declWrap.highlightNode(this)) {
if (!declWrap.highlightNode(editor)) {
Messages.loge("Highlighting failed.");
}
}
@@ -1885,7 +1940,7 @@ public class ASTGenerator {
.getLastSelectedPathComponent();
if (tnode.getUserObject() instanceof ASTNodeWrapper) {
ASTNodeWrapper awrap = (ASTNodeWrapper) tnode.getUserObject();
awrap.highlightNode(thisASTGenerator);
awrap.highlightNode(editor);
// errorCheckerService.highlightNode(awrap);
//--
@@ -1984,7 +2039,7 @@ public class ASTGenerator {
if (tnode.getUserObject() instanceof ASTNodeWrapper) {
ASTNodeWrapper awrap = (ASTNodeWrapper) tnode.getUserObject();
//errorCheckerService.highlightNode(awrap);
awrap.highlightNode(thisASTGenerator);
awrap.highlightNode(editor);
}
}
};
@@ -3022,19 +3077,6 @@ public class ASTGenerator {
}
protected boolean caretWithinLineComment() {
final JEditTextArea ta = editor.getTextArea();
String pdeLine = editor.getLineText(ta.getCaretLine()).trim();
int caretPos = ta.getCaretPosition() - ta.getLineStartNonWhiteSpaceOffset(ta.getCaretLine());
int x = pdeLine.indexOf("//");
if (x >= 0 && caretPos > x) {
return true;
}
return false;
}
/**
* A wrapper for java.lang.reflect types.
* Will have to see if the usage turns out to be internal only here or not
@@ -3161,9 +3203,9 @@ public class ASTGenerator {
return null;
} else if (t instanceof ArrayType) {
ArrayType at = (ArrayType) t;
log(at.getComponentType() + " <-comp type, ele type-> "
+ at.getElementType() + ", "
+ at.getElementType().getClass().getName());
log("ele type "
+ at.getElementType() + ", "
+ at.getElementType().getClass().getName());
if (at.getElementType() instanceof PrimitiveType) {
return null;
} else if (at.getElementType() instanceof SimpleType) {
@@ -3521,43 +3563,6 @@ public class ASTGenerator {
}
protected boolean ignorableImport(String impName, String fullClassName) {
for (ImportStatement impS : errorCheckerService.getProgramImports()) {
if (impName.toLowerCase().startsWith(impS.getPackageName().toLowerCase())) {
return false;
}
}
for (ImportStatement impS : errorCheckerService.codeFolderImports) {
if (impName.toLowerCase().startsWith(impS.getPackageName().toLowerCase())) {
return false;
}
}
if (JavaMode.suggestionsMap == null
|| JavaMode.suggestionsMap.keySet().size() == 0) {
log("SuggestionsMap is null or empty, won't be able to trim class names");
return true;
}
final String include = "include";
final String exclude = "exclude";
if (impName.startsWith("processing")) {
if (JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
} else if (JavaMode.suggestionsMap.get(exclude).contains(impName)) {
return true;
}
} else if (impName.startsWith("java")) {
if (JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
}
}
return true;
}
public static boolean isAddableASTNode(ASTNode node) {
switch (node.getNodeType()) {
// case ASTNode.STRING_LITERAL:

View File

@@ -46,6 +46,7 @@ import org.eclipse.jdt.core.dom.TypeDeclaration;
import processing.app.Base;
import processing.app.Messages;
import processing.mode.java.JavaEditor;
/**
@@ -54,7 +55,7 @@ import processing.app.Messages;
*
*/
public class ASTNodeWrapper {
private ASTNode Node;
private ASTNode node;
private String label;
private int lineNumber;
@@ -72,7 +73,7 @@ public class ASTNodeWrapper {
if (node == null){
return;
}
this.Node = node;
this.node = node;
label = getNodeAsString(node);
if (label == null)
label = node.toString();
@@ -85,7 +86,7 @@ public class ASTNodeWrapper {
if (node == null){
return;
}
this.Node = node;
this.node = node;
if(label != null)
this.label = label;
else{
@@ -105,10 +106,10 @@ public class ASTNodeWrapper {
* node length}
*/
public int[] getJavaCodeOffsets(ErrorCheckerService ecs) {
int nodeOffset = Node.getStartPosition(), nodeLength = Node
int nodeOffset = node.getStartPosition(), nodeLength = node
.getLength();
Messages.log("0.nodeOffset " + nodeOffset);
ASTNode thisNode = Node;
ASTNode thisNode = node;
while (thisNode.getParent() != null) {
if (getLineNumber(thisNode.getParent()) == lineNumber) {
thisNode = thisNode.getParent();
@@ -178,12 +179,12 @@ public class ASTNodeWrapper {
flag = false;
} else {
if (cnode == Node) {
if (cnode == node) {
// loop only till the current node.
break;
}
// We've located the first node in the line.
// Now normalize offsets till Node
// Now normalize offsets till node
//altStartPos += normalizeOffsets(cnode);
}
@@ -356,7 +357,7 @@ public class ASTNodeWrapper {
* @param source
* @return int[0] - java code offsets, int[1] = pde code offsets
*/
public int[][] getOffsetMapping(ErrorCheckerService ecs, String source){
public int[][] getOffsetMapping(ErrorCheckerService ecs, String source) {
/*
* This is some tricky shiz. So detailed explanation follows:
@@ -390,7 +391,10 @@ public class ASTNodeWrapper {
// Instead of converting pde into java, how can I simply extract the same source
// from the java code? Think. TODO
String sourceAlt = new String(source);
String sourceJava = ecs.astGenerator.getJavaSourceCodeLine(lineNumber);
String sourceJava;
synchronized (ecs.astGenerator) {
sourceJava = ecs.astGenerator.getJavaSourceCodeLine(lineNumber);
}
TreeMap<Integer, Integer> offsetmap = new TreeMap<Integer, Integer>();
if(sourceJava.trim().startsWith("public") && !source.startsWith("public")){
@@ -538,21 +542,20 @@ public class ASTNodeWrapper {
/**
* Highlight the ASTNode in the editor, if it's of type
* SimpleName
* @param astGenerator
* @param editor
* @return - true if highlighting was successful
*/
public boolean highlightNode(ASTGenerator astGenerator){
if (!(Node instanceof SimpleName)) {
public boolean highlightNode(JavaEditor editor){
if (!(node instanceof SimpleName)) {
return false;
}
SimpleName nodeName = (SimpleName) Node;
SimpleName nodeName = (SimpleName) node;
try {
//TODO: Redundant code. See ASTGenerator.getJavaSourceCodeline()
int javaLineNumber = getLineNumber(nodeName);
int pdeOffs[] = astGenerator.errorCheckerService
.calculateTabIndexAndLineNumber(javaLineNumber);
int pdeOffs[] = editor.getErrorChecker().calculateTabIndexAndLineNumber(javaLineNumber);
PlainDocument javaSource = new PlainDocument();
javaSource.insertString(0, astGenerator.errorCheckerService.sourceCode, null);
javaSource.insertString(0, editor.getErrorChecker().sourceCode, null);
Element lineElement = javaSource.getDefaultRootElement()
.getElement(javaLineNumber-1);
if(lineElement == null) {
@@ -563,8 +566,8 @@ public class ASTNodeWrapper {
String javaLine = javaSource.getText(lineElement.getStartOffset(),
lineElement.getEndOffset()
- lineElement.getStartOffset());
astGenerator.editor.getSketch().setCurrentCode(pdeOffs[0]);
String pdeLine = astGenerator.editor.getLineText(pdeOffs[1]);
editor.getSketch().setCurrentCode(pdeOffs[0]);
String pdeLine = editor.getLineText(pdeOffs[1]);
String lookingFor = nodeName.toString();
Messages.log(lookingFor + ", " + nodeName.getStartPosition());
Messages.log(javaLineNumber +" JL " + javaLine + " LSO " + lineElement.getStartOffset() + ","
@@ -584,9 +587,9 @@ public class ASTNodeWrapper {
"Please file a bug report.");
return false;
}
int lso = astGenerator.editor.getTextArea().getLineStartOffset(pdeOffs[1]);
int lso = editor.getTextArea().getLineStartOffset(pdeOffs[1]);
highlightStart += lso;
astGenerator.editor.setSelection(highlightStart, highlightStart
editor.setSelection(highlightStart, highlightStart
+ nodeName.getLength());
/*
// First find the name in the java line, and marks its index
@@ -649,18 +652,18 @@ public class ASTNodeWrapper {
* int[3] are on TODO
*/
public int[] getPDECodeOffsets(ErrorCheckerService ecs) {
return ecs.JavaToPdeOffsets(lineNumber + 1, Node.getStartPosition());
return ecs.JavaToPdeOffsets(lineNumber + 1, node.getStartPosition());
}
public int getPDECodeOffsetForSN(ASTGenerator astGen){
if (Node instanceof SimpleName) {
if (node instanceof SimpleName) {
Element lineElement = astGen.getJavaSourceCodeElement(lineNumber);
Messages.log("Line element off " + lineElement.getStartOffset());
OffsetMatcher ofm = new OffsetMatcher(astGen.getPDESourceCodeLine(lineNumber),
astGen.getJavaSourceCodeLine(lineNumber));
//log("");
int pdeOffset = ofm.getPdeOffForJavaOff(Node.getStartPosition()
- lineElement.getStartOffset(), Node.toString().length());
int pdeOffset = ofm.getPdeOffForJavaOff(node.getStartPosition()
- lineElement.getStartOffset(), node.toString().length());
return pdeOffset;
}
return -1;
@@ -671,7 +674,7 @@ public class ASTNodeWrapper {
}
public ASTNode getNode() {
return Node;
return node;
}
public String getLabel() {
@@ -679,7 +682,7 @@ public class ASTNodeWrapper {
}
public int getNodeType() {
return Node.getNodeType();
return node.getNodeType();
}
public int getLineNumber() {

View File

@@ -112,7 +112,7 @@ public class CompletionPanel {
this.textarea = (JavaTextArea) textarea;
this.editor = editor;
this.insertionPosition = position;
if (subWord.indexOf('.') != -1) {
if (subWord.indexOf('.') != -1 && subWord.indexOf('.') != subWord.length()-1) {
this.subWord = subWord.substring(subWord.lastIndexOf('.') + 1);
} else {
this.subWord = subWord;
@@ -129,8 +129,11 @@ public class CompletionPanel {
scrollPane.setViewportView(completionList = createSuggestionList(position, items));
popupMenu.add(scrollPane, BorderLayout.CENTER);
popupMenu.setPopupSize(calcWidth(), calcHeight(items.getSize())); //TODO: Eradicate this evil
editor.getErrorChecker().getASTGenerator().updateJavaDoc(completionList.getSelectedValue());
textarea.requestFocusInWindow();
popupMenu.setFocusable(false);
ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator();
synchronized (astGenerator) {
astGenerator.updateJavaDoc(completionList.getSelectedValue());
}
popupMenu.show(textarea, location.x, textarea.getBaseline(0, 0) + location.y);
//log("Suggestion shown: " + System.currentTimeMillis());
}
@@ -395,13 +398,7 @@ public class CompletionPanel {
if(mouseClickOnOverloadedMethods) {
// See #2755
SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
protected Object doInBackground() throws Exception {
((JavaTextArea) editor.getTextArea()).fetchPhrase(null);
return null;
}
};
worker.execute();
((JavaTextArea) editor.getTextArea()).fetchPhrase();
}
return true;
@@ -506,7 +503,10 @@ public class CompletionPanel {
.getVerticalScrollBar()
.getValue()
- step);
editor.getErrorChecker().getASTGenerator().updateJavaDoc(completionList.getSelectedValue());
ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator();
synchronized (astGenerator) {
astGenerator.updateJavaDoc(completionList.getSelectedValue());
}
}
}
@@ -523,7 +523,10 @@ public class CompletionPanel {
int index = Math.min(completionList.getSelectedIndex() + 1,
completionList.getModel().getSize() - 1);
selectIndex(index);
editor.getErrorChecker().getASTGenerator().updateJavaDoc(completionList.getSelectedValue());
ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator();
synchronized (astGenerator) {
astGenerator.updateJavaDoc(completionList.getSelectedValue());
}
int step = scrollPane.getVerticalScrollBar().getMaximum() / completionList.getModel().getSize();
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getValue() + step);
}

View File

@@ -316,7 +316,9 @@ public class ErrorCheckerService implements Runnable {
// This is when the loaded sketch already has syntax errors.
// Completion wouldn't be complete, but it'd be still something
// better than nothing
astGenerator.buildAST(cu);
synchronized (astGenerator) {
astGenerator.buildAST(cu);
}
handleErrorCheckingToggle();
while (!stopThread.get()) {
try {
@@ -349,7 +351,9 @@ public class ErrorCheckerService implements Runnable {
}
}
astGenerator.disposeAllWindows();
synchronized (astGenerator) {
astGenerator.disposeAllWindows();
}
compilationChecker = null;
checkerClass = null;
classLoader = null;
@@ -503,7 +507,9 @@ public class ErrorCheckerService implements Runnable {
// log(editor.getSketch().getName() + "2 MCO " + mainClassOffset);
}
astGenerator.buildAST(cu);
synchronized (astGenerator) {
astGenerator.buildAST(cu);
}
if (!JavaMode.errorCheckEnabled) {
problemsList.clear();
Messages.log("Error Check disabled, so not updating UI.");
@@ -897,7 +903,9 @@ public class ErrorCheckerService implements Runnable {
}
new Thread(new Runnable() {
public void run() {
astGenerator.loadJars(); // update jar file for completion lookup
synchronized (astGenerator) {
astGenerator.loadJars(); // update jar file for completion lookup
}
}
}).start();
}
@@ -912,6 +920,40 @@ public class ErrorCheckerService implements Runnable {
}
protected boolean ignorableSuggestionImport(String impName) {
String impNameLc = impName.toLowerCase();
for (ImportStatement impS : programImports) {
if (impNameLc.startsWith(impS.getPackageName().toLowerCase())) {
return false;
}
}
for (ImportStatement impS : codeFolderImports) {
if (impNameLc.startsWith(impS.getPackageName().toLowerCase())) {
return false;
}
}
final String include = "include";
final String exclude = "exclude";
if (impName.startsWith("processing")) {
if (JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
} else if (JavaMode.suggestionsMap.get(exclude).contains(impName)) {
return true;
}
} else if (impName.startsWith("java")) {
if (JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
}
}
return true;
}
/** Options for the JDT Compiler */
protected Map<String, String> compilerSettings;
@@ -956,7 +998,10 @@ public class ErrorCheckerService implements Runnable {
String[] args = p.getIProblem().getArguments();
if (args.length > 0) {
String missingClass = args[0];
String[] si = astGenerator.getSuggestImports(missingClass);
String[] si;
synchronized (astGenerator) {
si = astGenerator.getSuggestImports(missingClass);
}
if (si != null && si.length > 0) {
p.setImportSuggestions(si);
// errorData[index][0] = "<html>" + p.getMessage() +
@@ -1353,7 +1398,9 @@ public class ErrorCheckerService implements Runnable {
// Footer
if (mode != PdePreprocessor.Mode.JAVA) {
if (mode == PdePreprocessor.Mode.STATIC) {
sb.append("\nnoLoop();\n}");
// no noLoop() here so it does not tell you
// "can't invoke noLoop() on obj" when you type "obj."
sb.append("\n}");
}
sb.append("\n}");
}

View File

@@ -20,6 +20,7 @@ along with this program; if not, write to the Free Software Foundation, Inc.
package processing.mode.java.pdex;
import processing.core.PVector;
import processing.mode.java.JavaInputHandler;
import processing.mode.java.JavaMode;
import processing.mode.java.JavaEditor;
@@ -28,12 +29,22 @@ import processing.mode.java.tweak.Handle;
import java.awt.*;
import java.awt.event.*;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.text.BadLocationException;
import processing.app.Messages;
import processing.app.Mode;
@@ -222,7 +233,8 @@ public class JavaTextArea extends JEditTextArea {
if (!editor.hasJavaTabs()) {
if (evt.getID() == KeyEvent.KEY_TYPED) {
processCompletionKeys(evt);
} else if (!Platform.isMacOS() && evt.getID() == KeyEvent.KEY_RELEASED) {
processCompletionKeys(evt);
} else if (Platform.isMacOS() && evt.getID() == KeyEvent.KEY_RELEASED) {
processControlSpace(evt);
}
@@ -234,28 +246,24 @@ public class JavaTextArea extends JEditTextArea {
// https://github.com/processing/processing/issues/2699
private void processControlSpace(final KeyEvent event) {
if (event.getKeyCode() == KeyEvent.VK_SPACE && event.isControlDown()) {
SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
protected Object doInBackground() throws Exception {
// Provide completions only if it's enabled
if (JavaMode.codeCompletionsEnabled) {
Messages.log("[KeyEvent]" + KeyEvent.getKeyText(event.getKeyCode()) + " |Prediction started");
Messages.log("Typing: " + fetchPhrase(event));
}
return null;
}
};
worker.execute();
// Provide completions only if it's enabled
if (JavaMode.codeCompletionsEnabled) {
Messages.log("[KeyEvent]" + KeyEvent.getKeyText(event.getKeyCode()) + " |Prediction started");
fetchPhrase();
}
}
}
private void processCompletionKeys(final KeyEvent event) {
char keyChar = event.getKeyChar();
int keyCode = event.getKeyCode();
if (keyChar == KeyEvent.VK_ENTER ||
keyChar == KeyEvent.VK_ESCAPE ||
keyChar == KeyEvent.VK_TAB ||
keyChar == KeyEvent.CHAR_UNDEFINED) {
(event.getID() == KeyEvent.KEY_RELEASED &&
keyCode != KeyEvent.VK_LEFT && keyCode != KeyEvent.VK_RIGHT)) {
// ignore
} else if (keyChar == ')') {
// https://github.com/processing/processing/issues/2741
hideSuggestion();
@@ -263,28 +271,33 @@ public class JavaTextArea extends JEditTextArea {
} else if (keyChar == '.') {
if (JavaMode.codeCompletionsEnabled) {
Messages.log("[KeyEvent]" + KeyEvent.getKeyText(event.getKeyCode()) + " |Prediction started");
Messages.log("Typing: " + fetchPhrase(event));
fetchPhrase();
}
} else if (keyChar == ' ') { // Trigger on Ctrl-Space
if (!Platform.isMacOS() && JavaMode.codeCompletionsEnabled &&
(event.isControlDown() || event.isMetaDown())) {
SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
protected Object doInBackground() throws Exception {
//SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
// protected Object doInBackground() throws Exception {
// Provide completions only if it's enabled
if (JavaMode.codeCompletionsEnabled) {
getDocument().remove(getCaretPosition() - 1, 1); // Remove the typed space
Messages.log("[KeyEvent]" + event.getKeyChar() + " |Prediction started");
Messages.log("Typing: " + fetchPhrase(event));
try {
getDocument().remove(getCaretPosition() - 1, 1); // Remove the typed space
Messages.log("[KeyEvent]" + event.getKeyChar() + " |Prediction started");
fetchPhrase();
} catch (BadLocationException e) {
e.printStackTrace();
}
}
return null;
}
};
worker.execute();
// return null;
// }
//};
//worker.execute();
} else {
hideSuggestion(); // hide on spacebar
}
} else {
if (JavaMode.codeCompletionsEnabled) {
//fetchPhrase();
prepareSuggestions(event);
}
}
@@ -293,17 +306,13 @@ public class JavaTextArea extends JEditTextArea {
/** Kickstart auto-complete suggestions */
private void prepareSuggestions(final KeyEvent evt) {
new SwingWorker<Object, Object>() {
protected Object doInBackground() throws Exception {
// Provide completions only if it's enabled
if (JavaMode.codeCompletionsEnabled &&
(JavaMode.ccTriggerEnabled || suggestion.isVisible())) {
Messages.log("[KeyEvent]" + evt.getKeyChar() + " |Prediction started");
Messages.log("Typing: " + fetchPhrase(evt));
}
return null;
}
}.execute();
// Provide completions only if it's enabled
if (JavaMode.codeCompletionsEnabled &&
(JavaMode.ccTriggerEnabled ||
(suggestion != null && suggestion.isVisible()))) {
Messages.log("[KeyEvent]" + evt.getKeyChar() + " |Prediction started");
fetchPhrase();
}
}
@@ -368,134 +377,309 @@ public class JavaTextArea extends JEditTextArea {
return null;
}
Messages.log("Mouse click, word: " + word.trim());
editor.getErrorChecker().getASTGenerator().setLastClickedWord(line, word, xLS);
ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator();
synchronized (astGenerator) {
astGenerator.setLastClickedWord(line, word, xLS);
}
return word.trim();
}
}
SwingWorker<Void, Void> suggestionWorker = null;
int lastCaretPosition = 0;
String lastPhrase = "";
volatile boolean suggestionRunning = false;
volatile boolean suggestionRequested = false;
/**
* Retrieves the current word typed just before the caret.
* Then triggers code completion for that word.
* @param evt - the KeyEvent which triggered this method
*/
protected String fetchPhrase(KeyEvent evt) {
int off = getCaretPosition();
Messages.log("off " + off);
if (off < 0)
return null;
int line = getCaretLine();
if (line < 0)
return null;
String s = getLineText(line);
Messages.log(" line " + line);
protected void fetchPhrase() {
//log2(s + " len " + s.length());
int x = getCaretPosition() - getLineStartOffset(line) - 1, x1 = x - 1;
if(x >= s.length() || x < 0) {
//log("X is " + x + ". Returning null");
hideSuggestion();
return null; //TODO: Does this check cause problems? Verify.
if (suggestionRunning) {
suggestionRequested = true;
return;
}
Messages.log(" x char: " + s.charAt(x));
suggestionRunning = true;
suggestionRequested = false;
if (!(Character.isLetterOrDigit(s.charAt(x)) || s.charAt(x) == '_'
|| s.charAt(x) == '(' || s.charAt(x) == '.')) {
//log("Char before caret isn't a letter/digit/_(. so no predictions");
hideSuggestion();
return null;
} else if (x > 0 && (s.charAt(x - 1) == ' ' || s.charAt(x - 1) == '(')
&& Character.isDigit(s.charAt(x))) {
//log("Char before caret isn't a letter, but ' ' or '(', so no predictions");
hideSuggestion(); // See #2755, Option 2 comment
return null;
} else if (x == 0){
//log("X is zero");
hideSuggestion();
return null;
final String text;
final int caretLineIndex;
final int caretLinePosition;
{
// Get caret position
int caretPosition = getCaretPosition();
if (caretPosition < 0) {
suggestionRunning = false;
return;
}
// Get line index
caretLineIndex = getCaretLine();
if (caretLineIndex < 0) {
suggestionRunning = false;
return;
}
// Get text of the line
String lineText = getLineText(caretLineIndex);
if (lineText == null) {
suggestionRunning = false;
return;
}
// Get caret position on the line
caretLinePosition = getCaretPosition() - getLineStartOffset(caretLineIndex);
if (caretLinePosition <= 0) {
suggestionRunning = false;
return;
}
// Get part of the line to the left of the caret
if (caretLinePosition > lineText.length()) {
suggestionRunning = false;
return;
}
text = lineText.substring(0, caretLinePosition);
}
//int xLS = off - getLineStartNonWhiteSpaceOffset(line);
suggestionWorker = new SwingWorker<Void, Void>() {
String word = (x < s.length() ? s.charAt(x) : "") + "";
if (s.trim().length() == 1) {
// word = ""
// + (keyChar == KeyEvent.CHAR_UNDEFINED ? s.charAt(x - 1) : keyChar);
//word = (x < s.length()?s.charAt(x):"") + "";
word = word.trim();
if (word.endsWith("."))
word = word.substring(0, word.length() - 1);
String phrase = null;
DefaultListModel<CompletionCandidate> defListModel = null;
editor.getErrorChecker().getASTGenerator().preparePredictions(word, line + editor.getErrorChecker().mainClassOffset,0);
return word;
}
@Override
protected Void doInBackground() throws Exception {
Messages.log("phrase parse start");
phrase = parsePhrase(text, caretLinePosition);
Messages.log("phrase: " + phrase);
if (phrase == null) return null;
int i = 0;
int closeB = 0;
List<CompletionCandidate> candidates = null;
while (true) {
i++;
//TODO: currently works on single line only. "a. <new line> b()" won't be detected
if (x1 >= 0) {
// if (s.charAt(x1) != ';' && s.charAt(x1) != ',' && s.charAt(x1) != '(')
if (Character.isLetterOrDigit(s.charAt(x1)) || s.charAt(x1) == '_'
|| s.charAt(x1) == '.' || s.charAt(x1) == ')' || s.charAt(x1) == ']') {
ASTGenerator astGenerator = editor.getErrorChecker().getASTGenerator();
synchronized (astGenerator) {
int lineOffset = caretLineIndex +
editor.getErrorChecker().mainClassOffset;
if (s.charAt(x1) == ')') {
word = s.charAt(x1--) + word;
closeB++;
while (x1 >= 0 && closeB > 0) {
word = s.charAt(x1) + word;
if (s.charAt(x1) == '(')
closeB--;
if (s.charAt(x1) == ')')
closeB++;
x1--;
candidates = astGenerator.preparePredictions(phrase, lineOffset);
}
if (suggestionRequested) return null;
// don't show completions when the outline is visible
boolean showSuggestions = astGenerator.sketchOutline == null ||
!astGenerator.sketchOutline.isVisible();
if (showSuggestions && phrase != null &&
candidates != null && !candidates.isEmpty()) {
Collections.sort(candidates);
defListModel = ASTGenerator.filterPredictions(candidates);
Messages.log("Got: " + candidates.size() + " candidates, " + defListModel.size() + " filtered");
}
return null;
}
@Override
protected void done() {
try {
get();
} catch (ExecutionException e) {
Messages.loge("error while preparing suggestions", e.getCause());
} catch (InterruptedException e) {
// don't care
}
suggestionRunning = false;
if (suggestionRequested) {
Messages.log("completion invalidated");
hideSuggestion();
fetchPhrase();
return;
}
Messages.log("completion finishing");
if (defListModel != null) {
showSuggestion(defListModel, phrase);
} else {
hideSuggestion();
}
}
};
suggestionWorker.execute();
}
protected static String parsePhrase(String lineText, int caretLinePosition) {
boolean overloading = false;
{ // Check if we can provide suggestions for this phrase ending
String trimmedLineText = lineText.trim();
if (trimmedLineText.length() == 0) return null;
int lastCodePoint = trimmedLineText.codePointAt(trimmedLineText.length() - 1);
if (lastCodePoint == '.') {
trimmedLineText = trimmedLineText.substring(0, trimmedLineText.length() - 1).trim();
if (trimmedLineText.length() == 0) return null;
lastCodePoint = trimmedLineText.codePointAt(trimmedLineText.length() - 1);
switch (lastCodePoint) {
case ')':
case ']':
case '"':
break; // We can suggest for these
default:
if (!Character.isJavaIdentifierPart(lastCodePoint)) {
return null; // Not something we can suggest
}
}
else if (s.charAt(x1) == ']') {
word = s.charAt(x1--) + word;
closeB++;
while (x1 >= 0 && closeB > 0) {
word = s.charAt(x1) + word;
if (s.charAt(x1) == '[')
closeB--;
if (s.charAt(x1) == ']')
closeB++;
x1--;
}
}
else {
word = s.charAt(x1--) + word;
break;
}
} else if (lastCodePoint == '(') {
overloading = true; // We can suggest overloaded methods
} else if (!Character.isJavaIdentifierPart(lastCodePoint)) {
return null; // Not something we can suggest
}
}
final int currentCharIndex = caretLinePosition - 1;
{ // Check if the caret is in the comment
int commentStart = lineText.indexOf("//", 0);
if (commentStart >= 0 && currentCharIndex > commentStart) {
return null;
}
}
// Index the line
BitSet isInLiteral = new BitSet(lineText.length());
BitSet isInBrackets = new BitSet(lineText.length());
{ // Mark parts in literals
boolean inString = false;
boolean inChar = false;
boolean inEscaped = false;
for (int i = 0; i < lineText.length(); i++) {
if (!inEscaped) {
switch (lineText.codePointAt(i)) {
case '\"':
if (!inChar) inString = !inString;
break;
case '\'':
if (!inString) inChar = !inChar;
break;
case '\\':
if (inString || inChar) {
inEscaped = true;
}
break;
}
} else {
break;
inEscaped = false;
}
} else {
break;
}
if (i > 200) {
// time out!
break;
isInLiteral.set(i, inString || inChar);
}
}
if (Character.isDigit(word.charAt(0)))
return null;
word = word.trim();
// if (word.endsWith("."))
// word = word.substring(0, word.length() - 1);
int lineStartNonWSOffset = 0;
if (word.length() >= JavaMode.codeCompletionTriggerLength) {
editor.getErrorChecker().getASTGenerator()
.preparePredictions(word, line + editor.getErrorChecker().mainClassOffset,
lineStartNonWSOffset);
}
return word;
if (isInLiteral.get(currentCharIndex)) return null;
{ // Mark parts in top level brackets
int depth = overloading ? 1 : 0;
int bracketStart = overloading ? lineText.length() : 0;
int squareDepth = 0;
int squareBracketStart = 0;
bracketLoop: for (int i = lineText.length() - 1; i >= 0; i--) {
if (!isInLiteral.get(i)) {
switch (lineText.codePointAt(i)) {
case ')':
if (depth == 0) bracketStart = i;
depth++;
break;
case '(':
depth--;
if (depth == 0) {
isInBrackets.set(i, bracketStart);
} else if (depth < 0) {
break bracketLoop;
}
break;
case ']':
if (squareDepth == 0) squareBracketStart = i;
squareDepth++;
break;
case '[':
squareDepth--;
if (squareDepth == 0) {
isInBrackets.set(i, squareBracketStart);
} else if (squareDepth < 0) {
break bracketLoop;
}
break;
}
}
}
if (depth > 0) isInBrackets.set(0, bracketStart);
if (squareDepth > 0) isInBrackets.set(0, squareBracketStart);
}
// Walk the line from the end until it makes sense
int position = currentCharIndex;
parseLoop: while (position >= 0) {
int codePoint = lineText.codePointAt(position);
switch (codePoint) {
case '.': // Grab it
position--;
break;
case '[':
break parseLoop; // End of scope
case ']': // Grab the whole region in square brackets
position = isInBrackets.previousClearBit(position-1);
break;
case '(':
if (isInBrackets.get(position)) {
position--; // This checks for first bracket while overloading
break;
}
break parseLoop; // End of scope
case ')': // Grab the whole region in brackets
position = isInBrackets.previousClearBit(position-1);
break;
case '"': // Grab the whole literal and quit
position = isInLiteral.previousClearBit(position - 1);
break parseLoop;
default:
if (Character.isJavaIdentifierPart(codePoint)) {
position--; // Grab the identifier
} else if (Character.isWhitespace(codePoint)) {
position--; // Grab whitespace too
} else {
break parseLoop; // Got a char ending the phrase
}
break;
}
}
position++;
// Extract phrase
String phrase = lineText.substring(position, caretLinePosition).trim();
Messages.log(phrase);
if (phrase.length() == 0 || Character.isDigit(phrase.codePointAt(0))) {
return null; // Can't suggest for numbers or empty phrases
}
return phrase;
}
@@ -796,9 +980,6 @@ public class JavaTextArea extends JEditTextArea {
return;
}
if (subWord.length() < 2) {
return;
}
suggestion = new CompletionPanel(this, position, subWord,
listModel, location, editor);
requestFocusInWindow();
@@ -811,7 +992,7 @@ public class JavaTextArea extends JEditTextArea {
if (suggestion != null) {
suggestion.setInvisible();
//log("Suggestion hidden.");
suggestion = null;
suggestion = null; // TODO: check if we dispose the window properly
}
}

View File

@@ -207,7 +207,10 @@ public class JavaTextAreaPainter extends TextAreaPainter
return;
Messages.log(getJavaEditor().getErrorChecker().mainClassOffset + line + "|" + line + "| offset " + xLS + word + " <= \n");
getJavaEditor().getErrorChecker().getASTGenerator().scrollToDeclaration(line, word, xLS);
ASTGenerator astGenerator = getJavaEditor().getErrorChecker().getASTGenerator();
synchronized (astGenerator) {
astGenerator.scrollToDeclaration(line, word, xLS);
}
}
}
@@ -515,12 +518,14 @@ public class JavaTextAreaPainter extends TextAreaPainter
return super.getToolTipText(event);
}
ASTGenerator ast = getJavaEditor().getErrorChecker().getASTGenerator();
String tooltipText = ast.getLabelForASTNode(line, word, xLS);
synchronized (ast) {
String tooltipText = ast.getLabelForASTNode(line, word, xLS);
// log(errorCheckerService.mainClassOffset + " MCO "
// + "|" + line + "| offset " + xLS + word + " <= offf: "+off+ "\n");
if (tooltipText != null) {
return tooltipText;
// log(errorCheckerService.mainClassOffset + " MCO "
// + "|" + line + "| offset " + xLS + word + " <= offf: "+off+ "\n");
if (tooltipText != null) {
return tooltipText;
}
}
}
}

View File

@@ -157,7 +157,7 @@ public class SketchOutline {
.getLastSelectedPathComponent();
if (tnode.getUserObject() instanceof ASTNodeWrapper) {
ASTNodeWrapper awrap = (ASTNodeWrapper) tnode.getUserObject();
awrap.highlightNode(errorCheckerService.astGenerator);
awrap.highlightNode(editor);
//errorCheckerService.highlightNode(awrap);
close();
}
@@ -284,7 +284,7 @@ public class SketchOutline {
.getLastSelectedPathComponent();
if (tnode.getUserObject() instanceof ASTNodeWrapper) {
ASTNodeWrapper awrap = (ASTNodeWrapper) tnode.getUserObject();
awrap.highlightNode(errorCheckerService.astGenerator);
awrap.highlightNode(editor);
// log(awrap);
//errorCheckerService.highlightNode(awrap);
close();