mirror of
https://github.com/processing/processing4.git
synced 2026-02-04 06:09:17 +01:00
Merge pull request #3845 from JakubValtar/code-completion
Code suggestions refactoring
This commit is contained in:
@@ -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;
|
||||
@@ -2674,14 +2675,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user