Rename ECS to preprocessing service, move error checking to PDEX

This commit is contained in:
Jakub Valtar
2016-05-06 17:44:15 +02:00
parent 4fd2ba5e5d
commit 08508072ea
7 changed files with 344 additions and 630 deletions

View File

@@ -29,7 +29,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.ErrorCheckerService;
import processing.mode.java.pdex.PreprocessingService;
import processing.mode.java.pdex.ImportStatement;
import processing.mode.java.pdex.JavaTextArea;
import processing.mode.java.pdex.PDEX;
@@ -70,7 +70,7 @@ public class JavaEditor extends Editor {
private boolean hasJavaTabs;
private boolean javaTabWarned;
protected ErrorCheckerService errorCheckerService;
protected PreprocessingService preprocessingService;
protected PDEX pdex;
protected List<Problem> problems = Collections.emptyList();
@@ -150,7 +150,7 @@ public class JavaEditor extends Editor {
getJavaTextArea().setMode(jmode);
initErrorChecker();
initPDEX();
// ensure completion is hidden when editor loses focus
addWindowFocusListener(new WindowFocusListener() {
@@ -174,18 +174,9 @@ public class JavaEditor extends Editor {
}
protected void initErrorChecker() {
errorCheckerService = new ErrorCheckerService(this);
for (SketchCode code : getSketch().getCode()) {
Document document = code.getDocument();
if (document != null) {
errorCheckerService.addDocumentListener(document);
}
}
errorCheckerService.start();
errorCheckerService.notifySketchChanged();
pdex = new PDEX(this, errorCheckerService);
protected void initPDEX() {
preprocessingService = new PreprocessingService(this);
pdex = new PDEX(this, preprocessingService);
// Add ctrl+click listener
getJavaTextArea().getPainter().addMouseListener(new MouseAdapter() {
@@ -199,6 +190,44 @@ public class JavaEditor extends Editor {
}
}
});
sketchChanged();
for (SketchCode code : getSketch().getCode()) {
Document document = code.getDocument();
if (document != null) {
addDocumentListener(document);
}
}
}
public void addDocumentListener(Document doc) {
if (doc != null) doc.addDocumentListener(sketchChangedListener);
}
protected final DocumentListener sketchChangedListener = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
sketchChanged();
}
@Override
public void removeUpdate(DocumentEvent e) {
sketchChanged();
}
@Override
public void changedUpdate(DocumentEvent e) {
sketchChanged();
}
};
protected void sketchChanged() {
pdex.notifySketchChanged();
preprocessingService.notifySketchChanged();
}
@@ -214,6 +243,7 @@ public class JavaEditor extends Editor {
private int previousTabCount = 1;
// TODO: this is a clumsy way to get notified when tabs get added/deleted
// Override the parent call to add hook to the rebuild() method
public EditorHeader createHeader() {
return new EditorHeader(this) {
@@ -225,9 +255,9 @@ public class JavaEditor extends Editor {
boolean hasJavaTabsChanged = hasJavaTabs != newHasJavaTabs;
hasJavaTabs = newHasJavaTabs;
if (errorCheckerService != null) {
if (preprocessingService != null) {
if (hasJavaTabsChanged) {
errorCheckerService.handleHasJavaTabsChange(hasJavaTabs);
preprocessingService.handleHasJavaTabsChange(hasJavaTabs);
pdex.handleHasJavaTabsChange(hasJavaTabs);
if (hasJavaTabs) {
setProblemList(Collections.emptyList());
@@ -237,7 +267,7 @@ public class JavaEditor extends Editor {
int currentTabCount = sketch.getCodeCount();
if (currentTabCount != previousTabCount) {
previousTabCount = currentTabCount;
errorCheckerService.notifySketchChanged();
sketchChanged();
}
}
}
@@ -1327,13 +1357,13 @@ public class JavaEditor extends Editor {
@Override
public void librariesChanged() {
errorCheckerService.notifyLibrariesChanged();
preprocessingService.notifyLibrariesChanged();
}
@Override
public void codeFolderChanged() {
errorCheckerService.notifyCodeFolderChanged();
preprocessingService.notifyCodeFolderChanged();
}
@@ -1374,111 +1404,12 @@ public class JavaEditor extends Editor {
if (inspector != null) {
inspector.dispose();
}
errorCheckerService.stop();
preprocessingService.dispose();
pdex.dispose();
super.dispose();
}
// Not sure how this was supposed to work, tempErrorLog is always empty [jv]
/**
* Writes all error messages to a csv file.
* For analytics purposes only.
*/
/*
private void writeErrorsToFile() {
if (errorCheckerService.tempErrorLog.size() == 0) return;
try {
System.out.println("Writing errors");
StringBuilder sb = new StringBuilder();
sb.append("Sketch: " + getSketch().getFolder() + ", "
+ new java.sql.Timestamp(new java.util.Date().getTime())
+ "\nComma in error msg is substituted with ^ symbol\nFor separating arguments in error args | symbol is used\n");
sb.append("ERROR TYPE, ERROR ARGS, ERROR MSG\n");
for (String errMsg : errorCheckerService.tempErrorLog.keySet()) {
IProblem ip = errorCheckerService.tempErrorLog.get(errMsg);
if (ip != null) {
sb.append(ErrorMessageSimplifier.getIDName(ip.getID()));
sb.append(',');
sb.append("{");
for (int i = 0; i < ip.getArguments().length; i++) {
sb.append(ip.getArguments()[i]);
if (i < ip.getArguments().length-1)
sb.append("| ");
}
sb.append("}");
sb.append(',');
sb.append(ip.getMessage().replace(',', '^'));
sb.append("\n");
}
}
System.out.println(sb);
File opFile = new File(getSketch().getFolder(), "ErrorLogs"
+ File.separator + "ErrorLog_" + System.currentTimeMillis() + ".csv");
PApplet.saveStream(opFile, new ByteArrayInputStream(sb.toString()
.getBytes(Charset.defaultCharset())));
} catch (Exception e) {
System.err.println("Failed to save log file for sketch " + getSketch().getName());
e.printStackTrace();
}
}*/
/*
private AtomicBoolean debugToolbarEnabled;
public boolean isDebugToolbarEnabled() {
return debugToolbarEnabled != null && debugToolbarEnabled.get();
}
/// Toggles between java mode and debug mode toolbar
protected void switchToolbars(){
final EditorToolbar nextToolbar;
if(debugToolbarEnabled.get()){
// switch to java
if(javaToolbar == null)
javaToolbar = createToolbar();
nextToolbar = javaToolbar;
debugToolbarEnabled.set(false);
Base.log("Switching to Java Mode Toolbar");
}
else{
// switch to debug
if(debugToolbar == null)
debugToolbar = new DebugToolbar(this, getBase());
nextToolbar = debugToolbar;
debugToolbarEnabled.set(true);
Base.log("Switching to Debugger Toolbar");
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Box upper = (Box)splitPane.getComponent(0);
upper.remove(0);
upper.add(nextToolbar, 0);
upper.validate();
nextToolbar.repaint();
toolbar = nextToolbar;
// The toolbar responds to shift down/up events
// in order to show the alt version of toolbar buttons.
// With toolbar switch, KeyListener has to be changed as well
for (KeyListener kl : textarea.getKeyListeners()) {
if(kl instanceof EditorToolbar)
{
textarea.removeKeyListener(kl);
textarea.addKeyListener(toolbar);
break;
}
}
textarea.repaint();
}
});
}
*/
/**
* Creates the debug menu. Includes ActionListeners for the menu items.
* Intended for adding to the menu bar.
@@ -1660,26 +1591,6 @@ public class JavaEditor extends Editor {
// });
// debugMenu.add(item);
/*
item = Toolkit.newJMenuItem(Language.text("menu.debug.show_sketch_outline"), KeyEvent.VK_L);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Messages.log("Show Sketch Outline:");
errorCheckerService.getASTGenerator().showSketchOutline();
}
});
debugMenu.add(item);
item = Toolkit.newJMenuItem(Language.text("menu.debug.show_tabs_list"), KeyEvent.VK_Y);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Messages.log("Show Tab Outline:");
errorCheckerService.getASTGenerator().showTabOutline();
}
});
debugMenu.add(item);
*/
return debugMenu;
}
@@ -1928,8 +1839,8 @@ public class JavaEditor extends Editor {
}
public ErrorCheckerService getErrorChecker() {
return errorCheckerService;
public PreprocessingService getPreprocessingService() {
return preprocessingService;
}
@@ -1942,7 +1853,7 @@ public class JavaEditor extends Editor {
autoSave();
super.prepareRun();
downloadImports();
errorCheckerService.cancel();
preprocessingService.cancel();
}
@@ -2388,8 +2299,8 @@ public class JavaEditor extends Editor {
super.setCode(code);
Document newDoc = code.getDocument();
if (oldDoc != newDoc && errorCheckerService != null) {
errorCheckerService.addDocumentListener(newDoc);
if (oldDoc != newDoc && preprocessingService != null) {
addDocumentListener(newDoc);
}
// set line background colors for tab
@@ -2529,24 +2440,6 @@ public class JavaEditor extends Editor {
*/
// /**
// * Initializes and starts Error Checker Service
// */
// private void initializeErrorChecker() {
// Thread errorCheckerThread = null;
//
// if (errorCheckerThread == null) {
// errorCheckerService = new ErrorCheckerService(this);
// errorCheckerThread = new Thread(errorCheckerService);
// try {
// errorCheckerThread.start();
// } catch (Exception e) {
// Base.loge("Error Checker Service not initialized", e);
// }
// }
// }
public void setProblemList(List<Problem> problems) {
this.problems = problems;
boolean hasErrors = problems.stream().anyMatch(Problem::isError);
@@ -2566,7 +2459,7 @@ public class JavaEditor extends Editor {
for (Problem p : problems) {
String message = p.getMessage();
if (Preferences.getBoolean(JavaMode.SUGGEST_IMPORTS_PREF) &&
if (JavaMode.importSuggestEnabled &&
p.getImportSuggestions() != null &&
p.getImportSuggestions().length > 0) {
message += " (double-click for suggestions)";
@@ -2616,19 +2509,17 @@ public class JavaEditor extends Editor {
* line or not
*/
public void updateEditorStatus() {
if (JavaMode.errorCheckEnabled) {
Problem problem = findError(textarea.getCaretLine());
if (problem != null) {
int type = problem.isError() ?
EditorStatus.CURSOR_LINE_ERROR : EditorStatus.CURSOR_LINE_WARNING;
statusMessage(problem.getMessage(), type);
} else {
switch (getStatusMode()) {
case EditorStatus.CURSOR_LINE_ERROR:
case EditorStatus.CURSOR_LINE_WARNING:
statusEmpty();
break;
}
Problem problem = findError(textarea.getCaretLine());
if (problem != null) {
int type = problem.isError() ?
EditorStatus.CURSOR_LINE_ERROR : EditorStatus.CURSOR_LINE_WARNING;
statusMessage(problem.getMessage(), type);
} else {
switch (getStatusMode()) {
case EditorStatus.CURSOR_LINE_ERROR:
case EditorStatus.CURSOR_LINE_WARNING:
statusEmpty();
break;
}
}
}
@@ -2797,16 +2688,13 @@ public class JavaEditor extends Editor {
* the error button at the bottom of the PDE
*/
public void updateErrorToggle(boolean hasErrors) {
footer.setNotification(errorTable.getParent(), //errorTableScrollPane,
JavaMode.errorCheckEnabled &&
hasErrors);
footer.setNotification(errorTable.getParent(), hasErrors);
// String title = Language.text("editor.footer.errors");
// if (JavaMode.errorCheckEnabled && errorCheckerService.hasErrors()) {
// if (hasErrors) {
// title += "*";
// }
// ((JTabbedPane) footer).setTitleAt(ERROR_TAB_INDEX, title);
//// btnShowErrors.updateMarker(JavaMode.errorCheckEnabled &&
//// errorCheckerService.hasErrors(),
//// btnShowErrors.updateMarker(hasErrors,
//// errorBar.errorColor);
}
@@ -2875,8 +2763,7 @@ public class JavaEditor extends Editor {
jmode.loadPreferences();
Messages.log("Applying prefs");
// trigger it once to refresh UI
errorCheckerService.handlePreferencesChange();
setProblemList(Collections.emptyList());
sketchChanged();
}
}

View File

@@ -117,33 +117,6 @@ public class JavaMode extends Mode {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/*
public Runner handleRun(Sketch sketch,
RunnerListener listener) throws SketchException {
final JavaEditor editor = (JavaEditor) listener;
editor.errorCheckerService.quickErrorCheck();
// if (enableTweak) {
// enableTweak = false;
// return handleTweak(sketch, listener, false);
// } else {
return handleLaunch(sketch, listener, false);
// }
}
public Runner handlePresent(Sketch sketch,
RunnerListener listener) throws SketchException {
final JavaEditor editor = (JavaEditor) listener;
editor.errorCheckerService.quickErrorCheck();
// if (enableTweak) {
// enableTweak = false;
// return handleTweak(sketch, listener, true);
// } else {
return handleLaunch(sketch, listener, true);
// }
}
*/
/** Handles the standard Java "Run" or "Present" */
public Runner handleLaunch(Sketch sketch, RunnerListener listener,
@@ -174,7 +147,6 @@ public class JavaMode extends Mode {
RunnerListener listener) throws SketchException {
// final boolean present) throws SketchException {
final JavaEditor editor = (JavaEditor) listener;
// editor.errorCheckerService.quickErrorCheck(); // done in prepareRun()
if (isSketchModified(sketch)) {
editor.deactivateRun();
@@ -334,7 +306,7 @@ public class JavaMode extends Mode {
// }
static public volatile boolean errorCheckEnabled = true;
// static public volatile boolean errorCheckEnabled = true;
static public volatile boolean warningsEnabled = true;
static public volatile boolean codeCompletionsEnabled = true;
static public volatile boolean debugOutputEnabled = false;
@@ -343,7 +315,7 @@ public class JavaMode extends Mode {
static public volatile boolean autoSavePromptEnabled = true;
static public volatile boolean defaultAutoSaveEnabled = true;
static public volatile boolean ccTriggerEnabled = false;
// static public volatile boolean importSuggestEnabled = true;
static public volatile boolean importSuggestEnabled = true;
static public int autoSaveInterval = 3; //in minutes
@@ -352,7 +324,7 @@ public class JavaMode extends Mode {
*/
volatile public static int codeCompletionTriggerLength = 1;
static public final String prefErrorCheck = "pdex.errorCheckEnabled";
// static public final String prefErrorCheck = "pdex.errorCheckEnabled";
static public final String prefWarnings = "pdex.warningsEnabled";
static public final String prefDebugOP = "pdex.dbgOutput";
static public final String prefErrorLogs = "pdex.writeErrorLogs";
@@ -377,7 +349,7 @@ public class JavaMode extends Mode {
public void loadPreferences() {
Messages.log("Load PDEX prefs");
ensurePrefsExist();
errorCheckEnabled = Preferences.getBoolean(prefErrorCheck);
// errorCheckEnabled = Preferences.getBoolean(prefErrorCheck);
warningsEnabled = Preferences.getBoolean(prefWarnings);
codeCompletionsEnabled = Preferences.getBoolean(COMPLETION_PREF);
// DEBUG = Preferences.getBoolean(prefDebugOP);
@@ -388,14 +360,14 @@ public class JavaMode extends Mode {
autoSavePromptEnabled = Preferences.getBoolean(prefAutoSavePrompt);
defaultAutoSaveEnabled = Preferences.getBoolean(prefDefaultAutoSave);
ccTriggerEnabled = Preferences.getBoolean(COMPLETION_TRIGGER_PREF);
// importSuggestEnabled = Preferences.getBoolean(prefImportSuggestEnabled);
importSuggestEnabled = Preferences.getBoolean(SUGGEST_IMPORTS_PREF);
loadSuggestionsMap();
}
public void savePreferences() {
Messages.log("Saving PDEX prefs");
Preferences.setBoolean(prefErrorCheck, errorCheckEnabled);
// Preferences.setBoolean(prefErrorCheck, errorCheckEnabled);
Preferences.setBoolean(prefWarnings, warningsEnabled);
Preferences.setBoolean(COMPLETION_PREF, codeCompletionsEnabled);
// Preferences.setBoolean(prefDebugOP, DEBUG);
@@ -406,7 +378,7 @@ public class JavaMode extends Mode {
Preferences.setBoolean(prefAutoSavePrompt, autoSavePromptEnabled);
Preferences.setBoolean(prefDefaultAutoSave, defaultAutoSaveEnabled);
Preferences.setBoolean(COMPLETION_TRIGGER_PREF, ccTriggerEnabled);
// Preferences.setBoolean(prefImportSuggestEnabled, importSuggestEnabled);
Preferences.setBoolean(SUGGEST_IMPORTS_PREF, importSuggestEnabled);
}
public void loadSuggestionsMap() {
@@ -453,8 +425,8 @@ public class JavaMode extends Mode {
public void ensurePrefsExist() {
//TODO: Need to do a better job of managing prefs. Think lists.
if (Preferences.get(prefErrorCheck) == null)
Preferences.setBoolean(prefErrorCheck, errorCheckEnabled);
// if (Preferences.get(prefErrorCheck) == null)
// Preferences.setBoolean(prefErrorCheck, errorCheckEnabled);
if (Preferences.get(prefWarnings) == null)
Preferences.setBoolean(prefWarnings, warningsEnabled);
if (Preferences.get(COMPLETION_PREF) == null)
@@ -475,8 +447,8 @@ public class JavaMode extends Mode {
Preferences.setBoolean(prefDefaultAutoSave, defaultAutoSaveEnabled);
if (Preferences.get(COMPLETION_TRIGGER_PREF) == null)
Preferences.setBoolean(COMPLETION_TRIGGER_PREF, ccTriggerEnabled);
// if (Preferences.get(prefImportSuggestEnabled) == null)
// Preferences.setBoolean(prefImportSuggestEnabled, importSuggestEnabled);
if (Preferences.get(SUGGEST_IMPORTS_PREF) == null)
Preferences.setBoolean(SUGGEST_IMPORTS_PREF, importSuggestEnabled);
}

View File

@@ -61,6 +61,7 @@ import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import processing.app.Messages;
import processing.mode.java.JavaMode;
import com.google.classpath.ClassPath;
import com.google.classpath.RegExpResourceFilter;
@@ -687,8 +688,6 @@ public class CompletionGenerator {
return null;
}
//PreprocessedSketch ps = ecs.requestResult();
if (className.indexOf('.') >= 0) {
// Figure out what is package and what is class
String[] parts = className.split("\\.");
@@ -1319,6 +1318,41 @@ public class CompletionGenerator {
}
protected static boolean ignorableSuggestionImport(PreprocessedSketch ps, String impName) {
String impNameLc = impName.toLowerCase();
List<ImportStatement> programImports = ps.programImports;
List<ImportStatement> codeFolderImports = ps.codeFolderImports;
boolean isImported = Stream
.concat(programImports.stream(), codeFolderImports.stream())
.anyMatch(impS -> {
String packageNameLc = impS.getPackageName().toLowerCase();
return impNameLc.startsWith(packageNameLc);
});
if (isImported) return false;
final String include = "include";
final String exclude = "exclude";
if (impName.startsWith("processing")) {
if (JavaMode.suggestionsMap.containsKey(include) && JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
} else if (JavaMode.suggestionsMap.containsKey(exclude) && JavaMode.suggestionsMap.get(exclude).contains(impName)) {
return true;
}
} else if (impName.startsWith("java")) {
if (JavaMode.suggestionsMap.containsKey(include) && JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
}
}
return true;
}
/**
* A wrapper for java.lang.reflect types.
* Will have to see if the usage turns out to be internal only here or not
@@ -1850,7 +1884,7 @@ public class CompletionGenerator {
matchedClass2 = matchedClass2.replace('/', '.'); //package name
String matchedClass = matchedClass2.substring(0, matchedClass2.length() - 6);
int d = matchedClass.lastIndexOf('.');
if (!ErrorCheckerService.ignorableSuggestionImport(ps, matchedClass)) {
if (!ignorableSuggestionImport(ps, matchedClass)) {
matchedClass = matchedClass.substring(d + 1); //class name
// display package name in grey
String html = "<html>" + matchedClass + " : <font color=#777777>" +

View File

@@ -322,7 +322,7 @@ public class JavaTextArea extends JEditTextArea {
int codeIndex = editor.getSketch().getCodeIndex(editor.getCurrentTab());
int lineStartOffset = editor.getTextArea().getLineStartOffset(caretLineIndex);
editor.getErrorChecker().acceptWhenDone(ps -> {
editor.getPreprocessingService().whenDone(ps -> {
int lineNumber = ps.tabOffsetToJavaLine(codeIndex, lineStartOffset);
String phrase = null;

View File

@@ -342,7 +342,7 @@ public class JavaTextAreaPainter extends TextAreaPainter
/**
* Sets ErrorCheckerService and loads theme for TextAreaPainter(XQMode)
* Loads theme for TextAreaPainter(XQMode)
*/
public void setMode(Mode mode) {
errorUnderlineColor = mode.getColor("editor.error.underline.color");
@@ -385,96 +385,6 @@ public class JavaTextAreaPainter extends TextAreaPainter
}
/*
@Override
public String getToolTipText(MouseEvent event) {
if (!getJavaEditor().hasJavaTabs()) {
int off = textArea.xyToOffset(event.getX(), event.getY());
if (off < 0) {
setToolTipText(null);
return super.getToolTipText(event);
}
int line = textArea.getLineOfOffset(off);
if (line < 0) {
setToolTipText(null);
return super.getToolTipText(event);
}
String s = textArea.getLineText(line);
if (s == null || s.isEmpty()) {
setToolTipText(null);
return super.getToolTipText(event);
} else {
int x = textArea.xToOffset(line, event.getX()), x2 = x + 1, x1 = x - 1;
int xLS = off - textArea.getLineStartNonWhiteSpaceOffset(line);
if (x < 0 || x >= s.length()) {
setToolTipText(null);
return super.getToolTipText(event);
}
String word = s.charAt(x) + "";
if (s.charAt(x) == ' ') {
setToolTipText(null);
return super.getToolTipText(event);
}
if (!(Character.isLetterOrDigit(s.charAt(x)) ||
s.charAt(x) == '_' || s.charAt(x) == '$' || s.charAt(x) == '{' ||
s.charAt(x) == '}')) {
setToolTipText(null);
return super.getToolTipText(event);
}
int i = 0;
while (true) {
i++;
if (x1 >= 0 && x1 < s.length()) {
if (Character.isLetter(s.charAt(x1)) || s.charAt(x1) == '_') {
word = s.charAt(x1--) + word;
xLS--;
} else
x1 = -1;
} else
x1 = -1;
if (x2 >= 0 && x2 < s.length()) {
if (Character.isLetterOrDigit(s.charAt(x2)) || s.charAt(x2) == '_'
|| s.charAt(x2) == '$')
word = word + s.charAt(x2++);
else
x2 = -1;
} else
x2 = -1;
if (x1 < 0 && x2 < 0)
break;
if (i > 200) {
// time out!
// System.err.println("Whoopsy! :P");
break;
}
}
if (Character.isDigit(word.charAt(0))) {
setToolTipText(null);
return super.getToolTipText(event);
}
ASTGenerator ast = getJavaEditor().getErrorChecker().getASTGenerator();
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;
}
}
}
}
// Used when there are Java tabs, but also the fall-through case from above
// setToolTipText(null);
return super.getToolTipText(event);
}
*/
// TweakMode code
protected int horizontalAdjustment = 0;

View File

@@ -1,5 +1,9 @@
package processing.mode.java.pdex;
import com.google.classpath.ClassPath;
import com.google.classpath.RegExpResourceFilter;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -27,7 +31,12 @@ import java.util.Comparator;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.swing.BorderFactory;
@@ -48,10 +57,12 @@ import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import processing.app.Messages;
import processing.app.Preferences;
import processing.app.Sketch;
import processing.app.ui.EditorStatus;
import processing.app.ui.Toolkit;
import processing.mode.java.JavaEditor;
import processing.mode.java.JavaMode;
import processing.mode.java.pdex.PreprocessedSketch.SketchInterval;
import static processing.mode.java.pdex.ASTUtils.*;
@@ -62,24 +73,28 @@ public class PDEX {
private boolean enabled = true;
private ErrorChecker errorChecker;
private ShowUsage showUsage;
private Rename rename;
private DebugTree debugTree;
private JavaEditor editor;
private ErrorCheckerService ecs;
private PreprocessingService pps;
public PDEX(JavaEditor editor, ErrorCheckerService ecs) {
public PDEX(JavaEditor editor, PreprocessingService pps) {
this.editor = editor;
this.ecs = ecs;
this.pps = pps;
this.enabled = !editor.hasJavaTabs();
showUsage = new ShowUsage(editor, ecs);
errorChecker = new ErrorChecker(editor, pps);
showUsage = new ShowUsage(editor, pps);
rename = new Rename(editor);
if (SHOW_DEBUG_TREE) {
debugTree = new DebugTree(editor, ecs);
debugTree = new DebugTree(editor, pps);
}
}
@@ -87,21 +102,21 @@ public class PDEX {
public void handleShowUsage(int tabIndex, int startTabOffset, int stopTabOffset) {
Messages.log("* handleShowUsage");
if (!enabled) return; // show usage disabled if java tabs
ecs.acceptWhenDone(ps -> showUsage.findUsageAndUpdateTree(ps, tabIndex, startTabOffset, stopTabOffset));
pps.whenDone(ps -> showUsage.findUsageAndUpdateTree(ps, tabIndex, startTabOffset, stopTabOffset));
}
public void handleRename(int tabIndex, int startTabOffset, int stopTabOffset) {
Messages.log("* handleRename");
if (!enabled) return; // refactoring disabled w/ java tabs
ecs.acceptWhenDone(ps -> rename.handleRename(ps, tabIndex, startTabOffset, stopTabOffset));
pps.whenDone(ps -> rename.handleRename(ps, tabIndex, startTabOffset, stopTabOffset));
}
public void handleCtrlClick(int tabIndex, int offset) {
Messages.log("* handleCtrlClick");
if (!enabled) return; // disabled w/ java tabs
ecs.acceptWhenDone(ps -> handleCtrlClick(ps, tabIndex, offset));
pps.whenDone(ps -> handleCtrlClick(ps, tabIndex, offset));
}
@@ -113,7 +128,13 @@ public class PDEX {
}
public void notifySketchChanged() {
errorChecker.notifySketchChanged();
}
public void dispose() {
errorChecker.dispose();
showUsage.dispose();
rename.dispose();
if (debugTree != null) {
@@ -178,16 +199,16 @@ public class PDEX {
final JTree tree;
final JavaEditor editor;
final ErrorCheckerService ecs;
final PreprocessingService pps;
final Consumer<PreprocessedSketch> reloadListener;
IBinding binding;
ShowUsage(JavaEditor editor, ErrorCheckerService ecs) {
ShowUsage(JavaEditor editor, PreprocessingService pps) {
this.editor = editor;
this.ecs = ecs;
this.pps = pps;
reloadListener = this::reloadShowUsage;
@@ -201,12 +222,12 @@ public class PDEX {
// Delete references to ASTNodes so that whole AST can be GC'd
binding = null;
tree.setModel(null);
ecs.unregisterDoneListener(reloadListener);
pps.unregisterListener(reloadListener);
}
@Override
public void componentShown(ComponentEvent e) {
ecs.registerDoneListener(reloadListener);
pps.registerListener(reloadListener);
}
});
window.setSize(300, 400);
@@ -689,7 +710,7 @@ public class PDEX {
final Consumer<PreprocessedSketch> updateListener;
DebugTree(JavaEditor editor, ErrorCheckerService ecs) {
DebugTree(JavaEditor editor, PreprocessingService pps) {
updateListener = this::buildAndUpdateTree;
window = new JDialog(editor);
@@ -713,7 +734,7 @@ public class PDEX {
window.addComponentListener(new ComponentAdapter() {
@Override
public void componentHidden(ComponentEvent e) {
ecs.unregisterDoneListener(updateListener);
pps.unregisterListener(updateListener);
tree.setModel(null);
}
});
@@ -723,8 +744,8 @@ public class PDEX {
JScrollPane sp = new JScrollPane();
sp.setViewportView(tree);
window.add(sp);
ecs.acceptWhenDone(updateListener);
ecs.registerDoneListener(updateListener);
pps.whenDone(updateListener);
pps.registerListener(updateListener);
tree.addTreeSelectionListener(e -> {
@@ -735,7 +756,7 @@ public class PDEX {
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (tnode.getUserObject() instanceof ASTNode) {
ASTNode node = (ASTNode) tnode.getUserObject();
ecs.acceptWhenDone(ps -> {
pps.whenDone(ps -> {
SketchInterval si = ps.mapJavaToSketch(node);
EventQueue.invokeLater(() -> {
editor.highlight(si.tabIndex, si.startTabOffset, si.stopTabOffset);
@@ -797,4 +818,134 @@ public class PDEX {
}
private static class ErrorChecker {
// Delay delivering error check result after last sketch change #2677
private final static long DELAY_BEFORE_UPDATE = 650;
private ScheduledExecutorService scheduler;
private volatile ScheduledFuture<?> scheduledUiUpdate = null;
private volatile long nextUiUpdate = 0;
private final Consumer<PreprocessedSketch> errorHandlerListener = this::handleSketchProblems;
private JavaEditor editor;
public ErrorChecker(JavaEditor editor, PreprocessingService pps) {
this.editor = editor;
scheduler = Executors.newSingleThreadScheduledExecutor();
pps.registerListener(errorHandlerListener);
}
public void notifySketchChanged() {
nextUiUpdate = System.currentTimeMillis() + DELAY_BEFORE_UPDATE;
}
public void dispose() {
if (scheduler != null) {
scheduler.shutdownNow();
}
}
private void handleSketchProblems(PreprocessedSketch ps) {
// Process problems
final List<Problem> problems = ps.problems.stream()
// Filter Warnings if they are not enabled
.filter(iproblem -> !(iproblem.isWarning() && !JavaMode.warningsEnabled))
// Hide a useless error which is produced when a line ends with
// an identifier without a semicolon. "Missing a semicolon" is
// also produced and is preferred over this one.
// (Syntax error, insert ":: IdentifierOrNew" to complete Expression)
// See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=405780
.filter(iproblem -> !iproblem.getMessage()
.contains("Syntax error, insert \":: IdentifierOrNew\""))
// Transform into our Problems
.map(iproblem -> {
int start = iproblem.getSourceStart();
int stop = iproblem.getSourceEnd() + 1; // make it exclusive
SketchInterval in = ps.mapJavaToSketch(start, stop);
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
Problem p = new Problem(iproblem, in.tabIndex, line);
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
return p;
})
.collect(Collectors.toList());
// Handle import suggestions
if (JavaMode.importSuggestEnabled) {
Map<String, List<Problem>> undefinedTypeProblems = problems.stream()
// Get only problems with undefined types/names
.filter(p -> {
int id = p.getIProblem().getID();
return id == IProblem.UndefinedType ||
id == IProblem.UndefinedName ||
id == IProblem.UnresolvedVariable;
})
// Group problems by the missing type/name
.collect(Collectors.groupingBy(p -> p.getIProblem().getArguments()[0]));
if (!undefinedTypeProblems.isEmpty()) {
final ClassPath cp = ps.searchClassPath;
// Get suggestions for each missing type, update the problems
undefinedTypeProblems.entrySet().stream()
.forEach(entry -> {
String missingClass = entry.getKey();
List<Problem> affectedProblems = entry.getValue();
String[] suggestions = getImportSuggestions(cp, missingClass);
affectedProblems.forEach(p -> p.setImportSuggestions(suggestions));
});
}
}
if (scheduledUiUpdate != null) {
scheduledUiUpdate.cancel(true);
}
// Update UI after a delay. See #2677
long delay = nextUiUpdate - System.currentTimeMillis();
Runnable uiUpdater = () -> {
if (nextUiUpdate > 0 && System.currentTimeMillis() >= nextUiUpdate) {
EventQueue.invokeLater(() -> editor.setProblemList(problems));
}
};
scheduledUiUpdate = scheduler.schedule(uiUpdater, delay,
TimeUnit.MILLISECONDS);
}
public static String[] getImportSuggestions(ClassPath cp, String className) {
RegExpResourceFilter regf = new RegExpResourceFilter(
Pattern.compile(".*"),
Pattern.compile("(.*\\$)?" + className + "\\.class",
Pattern.CASE_INSENSITIVE));
String[] resources = cp.findResources("", regf);
return Arrays.stream(resources)
// remove ".class" suffix
.map(res -> res.substring(0, res.length() - 6))
// replace path separators with dots
.map(res -> res.replace('/', '.'))
// replace inner class separators with dots
.map(res -> res.replace('$', '.'))
// sort, prioritize clases from java. package
.sorted((o1, o2) -> {
// put java.* first, should be prioritized more
boolean o1StartsWithJava = o1.startsWith("java");
boolean o2StartsWithJava = o2.startsWith("java");
if (o1StartsWithJava != o2StartsWithJava) {
if (o1StartsWithJava) return -1;
return 1;
}
return o1.compareTo(o2);
})
.toArray(String[]::new);
}
}
}

View File

@@ -20,11 +20,8 @@ along with this program; if not, write to the Free Software Foundation, Inc.
package processing.mode.java.pdex;
import com.google.classpath.ClassPath;
import com.google.classpath.ClassPathFactory;
import com.google.classpath.RegExpResourceFilter;
import java.awt.EventQueue;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
@@ -41,21 +38,12 @@ import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.IProblem;
@@ -65,7 +53,6 @@ import org.eclipse.jdt.core.dom.CompilationUnit;
import processing.app.Library;
import processing.app.Messages;
import processing.app.Preferences;
import processing.app.Sketch;
import processing.app.SketchCode;
import processing.app.SketchException;
@@ -74,7 +61,6 @@ import processing.data.IntList;
import processing.data.StringList;
import processing.mode.java.JavaEditor;
import processing.mode.java.JavaMode;
import processing.mode.java.pdex.PreprocessedSketch.SketchInterval;
import processing.mode.java.pdex.TextTransform.OffsetMapper;
import processing.mode.java.preproc.PdePreprocessor;
import processing.mode.java.preproc.PdePreprocessor.Mode;
@@ -84,39 +70,23 @@ import processing.mode.java.preproc.PdePreprocessor.Mode;
* The main error checking service
*/
@SuppressWarnings("unchecked")
public class ErrorCheckerService {
public class PreprocessingService {
protected final JavaEditor editor;
/** The amazing eclipse ast parser */
protected final ASTParser parser = ASTParser.newParser(AST.JLS8);
/** Class path factory for ASTGenerator */
protected final ClassPathFactory classPathFactory = new ClassPathFactory();
private final ClassPathFactory classPathFactory = new ClassPathFactory();
/**
* Used to indirectly stop the Error Checker Thread
*/
private volatile boolean running;
/**
* Error checking doesn't happen before this interval has ellapsed since the
* last request() call.
*/
private final static long errorCheckInterval = 650;
private Thread errorCheckerThread;
private final Thread preprocessingThread;
private final BlockingQueue<Boolean> requestQueue = new ArrayBlockingQueue<>(1);
private ScheduledExecutorService scheduler;
private volatile ScheduledFuture<?> scheduledUiUpdate = null;
private volatile long nextUiUpdate = 0;
private final Object requestLock = new Object();
private boolean needsCheck = false;
private AtomicBoolean codeFolderChanged = new AtomicBoolean(true);
private AtomicBoolean librariesChanged = new AtomicBoolean(true);
private final AtomicBoolean codeFolderChanged = new AtomicBoolean(true);
private final AtomicBoolean librariesChanged = new AtomicBoolean(true);
private volatile boolean running;
private CompletableFuture<PreprocessedSketch> preprocessingTask = new CompletableFuture<>();
private CompletableFuture<?> lastCallback =
@@ -124,75 +94,58 @@ public class ErrorCheckerService {
complete(null); // initialization block
}};
private final Consumer<PreprocessedSketch> errorHandlerListener = this::handleSketchProblems;
private volatile boolean isEnabled = true;
private volatile boolean isContinuousCheckEnabled = true;
public ErrorCheckerService(JavaEditor editor) {
public PreprocessingService(JavaEditor editor) {
this.editor = editor;
isEnabled = !editor.hasJavaTabs();
isContinuousCheckEnabled = JavaMode.errorCheckEnabled;
registerDoneListener(errorHandlerListener);
preprocessingThread = new Thread(this::mainLoop, "ECS");
preprocessingThread.start();
}
private void mainLoop() {
running = true;
PreprocessedSketch prevResult = null;
Messages.log("PPS: Hi!");
while (running) {
try {
try {
requestQueue.take(); // blocking until check requested
requestQueue.take(); // blocking until requested
} catch (InterruptedException e) {
running = false;
break;
}
Messages.log("Starting preprocessing");
Messages.log("PPS: Starting");
prevResult = preprocessSketch(prevResult);
synchronized (requestLock) {
if (requestQueue.isEmpty()) {
Messages.log("Completing preprocessing");
Messages.log("PPS: Completed");
preprocessingTask.complete(prevResult);
}
}
} catch (Exception e) {
Messages.loge("problem in error checker loop", e);
Messages.loge("problem in preprocessor service loop", e);
}
}
Messages.log("PPS: Bye!");
}
public void start() {
scheduler = Executors.newSingleThreadScheduledExecutor();
errorCheckerThread = new Thread(this::mainLoop, "ECS");
errorCheckerThread.start();
}
public void stop() {
public void dispose() {
cancel();
running = false;
if (errorCheckerThread != null) {
running = false;
errorCheckerThread.interrupt();
}
if (scheduler != null) {
scheduler.shutdownNow();
}
preprocessingThread.interrupt();
}
public void cancel() {
requestQueue.clear();
nextUiUpdate = 0;
if (scheduledUiUpdate != null) {
scheduledUiUpdate.cancel(true);
}
}
@@ -202,35 +155,28 @@ public class ErrorCheckerService {
if (preprocessingTask.isDone()) {
preprocessingTask = new CompletableFuture<>();
// Register callback which executes all listeners
registerCallback(this::fireDoneListeners);
}
if (isContinuousCheckEnabled) {
// Continuous check enabled, request
nextUiUpdate = System.currentTimeMillis() + errorCheckInterval;
requestQueue.offer(Boolean.TRUE);
} else {
// Continuous check not enabled, take note
needsCheck = true;
whenDone(this::fireListeners);
}
requestQueue.offer(Boolean.TRUE);
}
}
public void notifyLibrariesChanged() {
Messages.log("PPS: notified libraries changed");
librariesChanged.set(true);
Messages.log("Notify libraries changed");
notifySketchChanged();
}
public void notifyCodeFolderChanged() {
Messages.log("PPS: snotified code folder changed");
codeFolderChanged.set(true);
Messages.log("Notify code folder changed");
notifySketchChanged();
}
private void registerCallback(Consumer<PreprocessedSketch> callback) {
public void whenDone(Consumer<PreprocessedSketch> callback) {
if (!isEnabled) return;
synchronized (requestLock) {
lastCallback = preprocessingTask
@@ -238,49 +184,36 @@ public class ErrorCheckerService {
.thenAcceptBothAsync(lastCallback, (ps, a) -> callback.accept(ps))
// Make sure exception in callback won't cancel whole callback chain
.handleAsync((res, e) -> {
if (e != null) Messages.loge("problem during preprocessing callback", e);
if (e != null) Messages.loge("exception in preprocessing callback", e);
return res;
});
}
}
public void acceptWhenDone(Consumer<PreprocessedSketch> callback) {
if (!isEnabled) return;
synchronized (requestLock) {
// Continuous check not enabled, request check now
if (needsCheck && !isContinuousCheckEnabled) {
needsCheck = false;
requestQueue.offer(Boolean.TRUE);
}
registerCallback(callback);
}
}
/// LISTENERS ----------------------------------------------------------------
private Set<Consumer<PreprocessedSketch>> doneListeners = new CopyOnWriteArraySet<>();
private Set<Consumer<PreprocessedSketch>> listeners = new CopyOnWriteArraySet<>();
public void registerDoneListener(Consumer<PreprocessedSketch> listener) {
if (listener != null) doneListeners.add(listener);
public void registerListener(Consumer<PreprocessedSketch> listener) {
if (listener != null) listeners.add(listener);
}
public void unregisterDoneListener(Consumer<PreprocessedSketch> listener) {
doneListeners.remove(listener);
public void unregisterListener(Consumer<PreprocessedSketch> listener) {
listeners.remove(listener);
}
private void fireDoneListeners(PreprocessedSketch ps) {
for (Consumer<PreprocessedSketch> listener : doneListeners) {
private void fireListeners(PreprocessedSketch ps) {
for (Consumer<PreprocessedSketch> listener : listeners) {
try {
listener.accept(ps);
} catch (Exception e) {
Messages.loge("error when firing ecs listener", e);
Messages.loge("error when firing preprocessing listener", e);
}
}
}
@@ -289,30 +222,6 @@ public class ErrorCheckerService {
/// --------------------------------------------------------------------------
public void addDocumentListener(Document doc) {
if (doc != null) doc.addDocumentListener(sketchChangedListener);
}
protected final DocumentListener sketchChangedListener = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
notifySketchChanged();
}
@Override
public void removeUpdate(DocumentEvent e) {
notifySketchChanged();
}
@Override
public void changedUpdate(DocumentEvent e) {
notifySketchChanged();
}
};
private PreprocessedSketch preprocessSketch(PreprocessedSketch prevResult) {
boolean firstCheck = prevResult == null;
@@ -505,77 +414,6 @@ public class ErrorCheckerService {
}
private void handleSketchProblems(PreprocessedSketch ps) {
// Process problems
final List<Problem> problems = ps.problems.stream()
// Filter Warnings if they are not enabled
.filter(iproblem -> !(iproblem.isWarning() && !JavaMode.warningsEnabled))
// Hide a useless error which is produced when a line ends with
// an identifier without a semicolon. "Missing a semicolon" is
// also produced and is preferred over this one.
// (Syntax error, insert ":: IdentifierOrNew" to complete Expression)
// See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=405780
.filter(iproblem -> !iproblem.getMessage()
.contains("Syntax error, insert \":: IdentifierOrNew\""))
// Transform into our Problems
.map(iproblem -> {
int start = iproblem.getSourceStart();
int stop = iproblem.getSourceEnd() + 1; // make it exclusive
SketchInterval in = ps.mapJavaToSketch(start, stop);
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
Problem p = new Problem(iproblem, in.tabIndex, line);
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
return p;
})
.collect(Collectors.toList());
// Handle import suggestions
if (Preferences.getBoolean(JavaMode.SUGGEST_IMPORTS_PREF)) {
Map<String, List<Problem>> undefinedTypeProblems = problems.stream()
// Get only problems with undefined types/names
.filter(p -> {
int id = p.getIProblem().getID();
return id == IProblem.UndefinedType ||
id == IProblem.UndefinedName ||
id == IProblem.UnresolvedVariable;
})
// Group problems by the missing type/name
.collect(Collectors.groupingBy(p -> p.getIProblem().getArguments()[0]));
if (!undefinedTypeProblems.isEmpty()) {
final ClassPath cp = ps.searchClassPath;
// Get suggestions for each missing type, update the problems
undefinedTypeProblems.entrySet().stream()
.forEach(entry -> {
String missingClass = entry.getKey();
List<Problem> affectedProblems = entry.getValue();
String[] suggestions = getImportSuggestions(cp, missingClass);
affectedProblems.forEach(p -> p.setImportSuggestions(suggestions));
});
}
}
if (scheduledUiUpdate != null) {
scheduledUiUpdate.cancel(true);
}
// Update UI after a delay. See #2677
long delay = nextUiUpdate - System.currentTimeMillis();
Runnable uiUpdater = () -> {
if (nextUiUpdate > 0 && System.currentTimeMillis() >= nextUiUpdate) {
EventQueue.invokeLater(() -> {
if (isContinuousCheckEnabled) {
editor.setProblemList(problems);
}
});
}
};
scheduledUiUpdate = scheduler.schedule(uiUpdater, delay,
TimeUnit.MILLISECONDS);
}
/// IMPORTS -----------------------------------------------------------------
private List<ImportStatement> coreAndDefaultImports;
@@ -608,6 +446,22 @@ public class ErrorCheckerService {
}
private static boolean checkIfImportsChanged(List<ImportStatement> prevImports,
List<ImportStatement> imports) {
if (imports.size() != prevImports.size()) {
return true;
} else {
int count = imports.size();
for (int i = 0; i < count; i++) {
if (!imports.get(i).isSameAs(prevImports.get(i))) {
return true;
}
}
}
return false;
}
/// CLASSPATHS ---------------------------------------------------------------
@@ -621,8 +475,8 @@ public class ErrorCheckerService {
private List<String> codeFolderClassPath;
private List<String> searchLibraryClassPath;
private List<String> sketchLibraryClassPath;
private List<String> searchLibraryClassPath;
private static List<String> buildCodeFolderClassPath(Sketch sketch) {
@@ -670,6 +524,7 @@ public class ErrorCheckerService {
return sanitizeClassPath(classPath.toString());
}
private static List<String> buildSearchLibraryClassPath(JavaMode mode) {
StringBuilder classPath = new StringBuilder();
@@ -735,36 +590,7 @@ public class ErrorCheckerService {
public static String[] getImportSuggestions(ClassPath cp, String className) {
RegExpResourceFilter regf = new RegExpResourceFilter(
Pattern.compile(".*"),
Pattern.compile("(.*\\$)?" + className + "\\.class",
Pattern.CASE_INSENSITIVE));
String[] resources = cp.findResources("", regf);
return Arrays.stream(resources)
// remove ".class" suffix
.map(res -> res.substring(0, res.length() - 6))
// replace path separators with dots
.map(res -> res.replace('/', '.'))
// replace inner class separators with dots
.map(res -> res.replace('$', '.'))
// sort, prioritize clases from java. package
.sorted((o1, o2) -> {
// put java.* first, should be prioritized more
boolean o1StartsWithJava = o1.startsWith("java");
boolean o2StartsWithJava = o2.startsWith("java");
if (o1StartsWithJava != o2StartsWithJava) {
if (o1StartsWithJava) return -1;
return 1;
}
return o1.compareTo(o2);
})
.toArray(String[]::new);
}
protected static CompilationUnit makeAST(ASTParser parser,
private static CompilationUnit makeAST(ASTParser parser,
char[] source,
Map<String, String> options) {
parser.setSource(source);
@@ -776,7 +602,7 @@ public class ErrorCheckerService {
}
protected static CompilationUnit makeASTWithBindings(ASTParser parser,
private static CompilationUnit makeASTWithBindings(ASTParser parser,
char[] source,
Map<String, String> options,
String className,
@@ -796,47 +622,12 @@ public class ErrorCheckerService {
/**
* Ignore processing packages, java.*.*. etc.
*/
protected boolean ignorableImport(String packageName) {
private boolean ignorableImport(String packageName) {
return (packageName.startsWith("java.") ||
packageName.startsWith("javax."));
}
protected static boolean ignorableSuggestionImport(PreprocessedSketch ps, String impName) {
String impNameLc = impName.toLowerCase();
List<ImportStatement> programImports = ps.programImports;
List<ImportStatement> codeFolderImports = ps.codeFolderImports;
boolean isImported = Stream
.concat(programImports.stream(), codeFolderImports.stream())
.anyMatch(impS -> {
String packageNameLc = impS.getPackageName().toLowerCase();
return impNameLc.startsWith(packageNameLc);
});
if (isImported) return false;
final String include = "include";
final String exclude = "exclude";
if (impName.startsWith("processing")) {
if (JavaMode.suggestionsMap.containsKey(include) && JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
} else if (JavaMode.suggestionsMap.containsKey(exclude) && JavaMode.suggestionsMap.get(exclude).contains(impName)) {
return true;
}
} else if (impName.startsWith("java")) {
if (JavaMode.suggestionsMap.containsKey(include) && JavaMode.suggestionsMap.get(include).contains(impName)) {
return false;
}
}
return true;
}
static private final Map<String, String> COMPILER_OPTIONS;
static {
Map<String, String> compilerOptions = new HashMap<>();
@@ -879,37 +670,6 @@ public class ErrorCheckerService {
}
/**
* Checks if import statements in the sketch have changed. If they have,
* compiler classpath needs to be updated.
*/
protected static boolean checkIfImportsChanged(List<ImportStatement> prevImports,
List<ImportStatement> imports) {
if (imports.size() != prevImports.size()) {
return true;
} else {
int count = imports.size();
for (int i = 0; i < count; i++) {
if (!imports.get(i).isSameAs(prevImports.get(i))) {
return true;
}
}
}
return false;
}
public void handlePreferencesChange() {
isContinuousCheckEnabled = JavaMode.errorCheckEnabled;
if (isContinuousCheckEnabled) {
Messages.log(editor.getSketch().getName() + " Error Checker enabled.");
notifySketchChanged();
} else {
Messages.log(editor.getSketch().getName() + " Error Checker disabled.");
}
}
public void handleHasJavaTabsChange(boolean hasJavaTabs) {
isEnabled = !hasJavaTabs;
if (isEnabled) {