From 695c930a1ecf1b6db40b158b9da10ccc5e4a333a Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Sat, 26 Sep 2015 08:34:12 -0400 Subject: [PATCH 1/4] Always null check result of extractTypeInfo() --- .../mode/java/pdex/ASTGenerator.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/java/src/processing/mode/java/pdex/ASTGenerator.java b/java/src/processing/mode/java/pdex/ASTGenerator.java index 1e209c694..2296a1ec6 100644 --- a/java/src/processing/mode/java/pdex/ASTGenerator.java +++ b/java/src/processing/mode/java/pdex/ASTGenerator.java @@ -2793,6 +2793,10 @@ public class ASTGenerator { SimpleType stp = extracTypeInfo(findDeclaration((qn.getQualifier()))); // log(qn.getQualifier() + "->" + qn.getName()); + if (stp == null) { + return null; + } + declaringClass = findDeclaration(stp.getName()); // log("QN decl class: " + getNodeAsString(declaringClass)); @@ -2810,16 +2814,15 @@ public class ASTGenerator { // .toString())); SimpleType stp = extracTypeInfo(findDeclaration((qnn.getQualifier()))); - if (stp != null) { - declaringClass = findDeclaration(stp.getName()); - constrains.clear(); - constrains.add(ASTNode.TYPE_DECLARATION); - constrains.add(ASTNode.FIELD_DECLARATION); - return definedIn(declaringClass, qnn.getName().toString(), - constrains, null); - } else { + if (stp == null) { return null; } + declaringClass = findDeclaration(stp.getName()); + constrains.clear(); + constrains.add(ASTNode.TYPE_DECLARATION); + constrains.add(ASTNode.FIELD_DECLARATION); + return definedIn(declaringClass, qnn.getName().toString(), + constrains, null); } } } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE) { @@ -3010,6 +3013,11 @@ public class ASTGenerator { // .toString())); SimpleType stp = extracTypeInfo(findDeclaration2((qnn.getQualifier()), alternateParent)); + + if (stp == null) { + return null; + } + // log(qnn.getQualifier() + "->" + qnn.getName()); declaringClass = findDeclaration2(stp.getName(), alternateParent); From d083dcd0e4e6271a1fc7427e4e625c8eb60c99e1 Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Sat, 26 Sep 2015 10:03:20 -0400 Subject: [PATCH 2/4] Make error checker play nice when switching tabs --- app/src/processing/app/ui/ErrorTable.java | 16 ++-- java/src/processing/mode/java/JavaEditor.java | 12 ++- .../mode/java/pdex/ASTGenerator.java | 2 + .../mode/java/pdex/ErrorCheckerService.java | 86 +++++++++---------- .../mode/java/pdex/JavaTextArea.java | 8 +- 5 files changed, 64 insertions(+), 60 deletions(-) diff --git a/app/src/processing/app/ui/ErrorTable.java b/app/src/processing/app/ui/ErrorTable.java index 415b8451b..347ba2128 100644 --- a/app/src/processing/app/ui/ErrorTable.java +++ b/app/src/processing/app/ui/ErrorTable.java @@ -106,14 +106,16 @@ public class ErrorTable extends JTable { synchronized public void mouseClicked(MouseEvent e) { try { int row = ((ErrorTable) e.getSource()).getSelectedRow(); - Object data = getModel().getValueAt(row, DATA_COLUMN); - int clickCount = e.getClickCount(); - if (clickCount == 1) { - editor.errorTableClick(data); - } else if (clickCount > 1) { - editor.errorTableDoubleClick(data); + if (row >= 0 && row < getRowCount()) { + Object data = getModel().getValueAt(row, DATA_COLUMN); + int clickCount = e.getClickCount(); + if (clickCount == 1) { + editor.errorTableClick(data); + } else if (clickCount > 1) { + editor.errorTableDoubleClick(data); + } +// editor.getErrorChecker().scrollToErrorLine(row); } -// editor.getErrorChecker().scrollToErrorLine(row); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 09c2c3baf..1d15991ca 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -2324,6 +2324,7 @@ public class JavaEditor extends Editor { */ @Override public void setCode(SketchCode code) { + //System.out.println("tab switch: " + code.getFileName()); // set the new document in the textarea, etc. need to do this first super.setCode(code); @@ -2355,6 +2356,13 @@ public class JavaEditor extends Editor { if (getDebugger() != null && getDebugger().isStarted()) { getDebugger().startTrackingLineChanges(); } + if (errorCheckerService != null) { + if (errorColumn != null) { + getErrorPoints().clear(); + statusEmpty(); + } + errorCheckerService.request(); + } } @@ -2654,10 +2662,10 @@ public class JavaEditor extends Editor { * Handle whether the tiny red error indicator is shown near * the error button at the bottom of the PDE */ - public void updateErrorToggle() { + public void updateErrorToggle(boolean hasErrors) { footer.setNotification(errorTable.getParent(), //errorTableScrollPane, JavaMode.errorCheckEnabled && - errorCheckerService.hasErrors()); + hasErrors); // String title = Language.text("editor.footer.errors"); // if (JavaMode.errorCheckEnabled && errorCheckerService.hasErrors()) { // title += "*"; diff --git a/java/src/processing/mode/java/pdex/ASTGenerator.java b/java/src/processing/mode/java/pdex/ASTGenerator.java index 2296a1ec6..f20c51edc 100644 --- a/java/src/processing/mode/java/pdex/ASTGenerator.java +++ b/java/src/processing/mode/java/pdex/ASTGenerator.java @@ -1927,6 +1927,8 @@ public class ASTGenerator { @Override public void valueChanged(TreeSelectionEvent e) { Messages.log(e.toString()); + + // TODO: this should already run on EDT so why the SwingWorker? SwingWorker worker = new SwingWorker() { @Override diff --git a/java/src/processing/mode/java/pdex/ErrorCheckerService.java b/java/src/processing/mode/java/pdex/ErrorCheckerService.java index 2797a4a29..696e3f51a 100644 --- a/java/src/processing/mode/java/pdex/ErrorCheckerService.java +++ b/java/src/processing/mode/java/pdex/ErrorCheckerService.java @@ -26,6 +26,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -232,8 +233,6 @@ public class ErrorCheckerService { defaultImportsOffset = pdePrepoc.getCoreImports().length + pdePrepoc.getDefaultImports().length + 1; astGenerator = new ASTGenerator(this); - lastCodeCheckResult.syntaxErrors = false; - lastCodeCheckResult.containsErrors = false; errorMsgSimplifier = new ErrorMessageSimplifier(); tempErrorLog = new TreeMap<>(); sketchChangedListener = new SketchChangedListener(); @@ -256,26 +255,6 @@ public class ErrorCheckerService { Executors.newSingleThreadScheduledExecutor(); volatile ScheduledFuture scheduledUiUpdate = null; volatile long nextUiUpdate = 0; - private Runnable uiUpdater = new Runnable() { - @Override - public void run() { - if (nextUiUpdate > 0 && - System.currentTimeMillis() >= nextUiUpdate) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - calcPdeOffsetsForProbList(); - updateErrorTable(); - editor.updateErrorBar(lastCodeCheckResult.problems); - editor.getTextArea().repaint(); - updatePaintedThingys(); - editor.updateErrorToggle(); - updateSketchCodeListeners(); - } - }); - } - } - }; private Runnable mainLoop = new Runnable() { @Override @@ -284,7 +263,7 @@ public class ErrorCheckerService { lastCodeCheckResult = checkCode(); - if (!hasSyntaxErrors()) { + if (!lastCodeCheckResult.syntaxErrors) { // editor.showProblemListView(Language.text("editor.footer.console")); editor.showConsole(); } @@ -326,6 +305,27 @@ public class ErrorCheckerService { } // Update UI after a delay. See #2677 long delay = nextUiUpdate - System.currentTimeMillis(); + Runnable uiUpdater = new Runnable() { + final CodeCheckResult result = lastCodeCheckResult; + + @Override + public void run() { + if (nextUiUpdate > 0 && + System.currentTimeMillis() >= nextUiUpdate) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + calcPdeOffsetsForProbList(result); + updateErrorTable(result.problems); + editor.updateErrorBar(result.problems); + editor.getTextArea().repaint(); + editor.updateErrorToggle(result.containsErrors); + updateSketchCodeListeners(); + } + }); + } + } + }; scheduledUiUpdate = scheduler.schedule(uiUpdater, delay, TimeUnit.MILLISECONDS); } @@ -399,6 +399,9 @@ public class ErrorCheckerService { protected void checkForMissingImports() { + // Atomic access + CodeCheckResult lastCodeCheckResult = this.lastCodeCheckResult; + if (Preferences.getBoolean(JavaMode.SUGGEST_IMPORTS_PREF)) { for (Problem p : lastCodeCheckResult.problems) { if(p.getIProblem().getID() == IProblem.UndefinedType) { @@ -617,11 +620,11 @@ public class ErrorCheckerService { /** * Calculates PDE Offsets from Java Offsets for Problems */ - private void calcPdeOffsetsForProbList() { + private void calcPdeOffsetsForProbList(CodeCheckResult codeCheckResult) { try { PlainDocument javaSource = new PlainDocument(); - javaSource.insertString(0, lastCodeCheckResult.sourceCode, null); + javaSource.insertString(0, codeCheckResult.sourceCode, null); // Code in pde tabs stored as PlainDocument List pdeTabs = new ArrayList<>(); for (SketchCode sc : editor.getSketch().getCode()) { @@ -635,16 +638,16 @@ public class ErrorCheckerService { } int pkgNameOffset = ("package " + className + ";\n").length(); // package name is added only during compile check - if (lastCodeCheckResult.sourceCodeOffset == 0) { + if (codeCheckResult.sourceCodeOffset == 0) { pkgNameOffset = 0; } - for (Problem p : lastCodeCheckResult.problems) { + for (Problem p : codeCheckResult.problems) { int prbStart = p.getIProblem().getSourceStart() - pkgNameOffset; int prbEnd = p.getIProblem().getSourceEnd() - pkgNameOffset; int javaLineNumber = p.getSourceLineNumber() - 1; // not sure if this is necessary [fry 150808] - javaLineNumber -= lastCodeCheckResult.sourceCodeOffset; + javaLineNumber -= codeCheckResult.sourceCodeOffset; // errors on the first line were setting this to -1 [fry 150808] if (javaLineNumber < 0) { javaLineNumber = 0; @@ -871,7 +874,7 @@ public class ErrorCheckerService { /** * Updates the error table in the Error Window. */ - public void updateErrorTable() { + public void updateErrorTable(List problems) { try { ErrorTable table = editor.getErrorTable(); table.clearRows(); @@ -880,7 +883,7 @@ public class ErrorCheckerService { // int index = 0; // for (int i = 0; i < problemsList.size(); i++) { Sketch sketch = editor.getSketch(); - for (Problem p : lastCodeCheckResult.problems) { + for (Problem p : problems) { String message = p.getMessage(); if (Preferences.getBoolean(JavaMode.SUGGEST_IMPORTS_PREF)) { if (p.getIProblem().getID() == IProblem.UndefinedType) { @@ -926,21 +929,6 @@ public class ErrorCheckerService { } - // TODO: why is this here and not in event handler? - /** Repaints the textarea if required */ - private void updatePaintedThingys() { -// currentTab = editor.getSketch().getCodeIndex(editor.getSketch().getCurrentCode()); - currentTab = editor.getSketch().getCurrentCodeIndex(); - //log("Tab changed " + currentTab + " LT " + lastTab); - if (currentTab != lastTab) { - request(); - lastTab = currentTab; - editor.getTextArea().repaint(); - editor.statusEmpty(); - } - } - - /** * Updates editor status bar, depending on whether the caret is on an error * line or not @@ -960,7 +948,8 @@ public class ErrorCheckerService { editor.statusMessage(errorMarker.getProblem().getMessage(), EditorStatus.COMPILER_ERROR); } - return; + } else { + editor.statusEmpty(); // No error, clear the status } } @@ -1335,6 +1324,9 @@ public class ErrorCheckerService { return; } + // Atomic access + CodeCheckResult lastCodeCheckResult = this.lastCodeCheckResult; + if (errorIndex < lastCodeCheckResult.problems.size() && errorIndex >= 0) { Problem p = lastCodeCheckResult.problems.get(errorIndex); scrollToErrorLine(p); @@ -1555,7 +1547,7 @@ public class ErrorCheckerService { //editor.clearErrorPoints(); editor.getErrorPoints().clear(); lastCodeCheckResult.problems.clear(); - updateErrorTable(); + updateErrorTable(Collections.emptyList()); updateEditorStatus(); editor.getTextArea().repaint(); editor.repaintErrorBar(); diff --git a/java/src/processing/mode/java/pdex/JavaTextArea.java b/java/src/processing/mode/java/pdex/JavaTextArea.java index 43c02fefe..dfe5c8377 100644 --- a/java/src/processing/mode/java/pdex/JavaTextArea.java +++ b/java/src/processing/mode/java/pdex/JavaTextArea.java @@ -443,7 +443,7 @@ public class JavaTextArea extends JEditTextArea { @Override protected Void doInBackground() throws Exception { Messages.log("phrase parse start"); - phrase = parsePhrase(text, caretLinePosition); + phrase = parsePhrase(text); Messages.log("phrase: " + phrase); if (phrase == null) return null; @@ -504,7 +504,7 @@ public class JavaTextArea extends JEditTextArea { suggestionWorker.execute(); } - protected static String parsePhrase(String lineText, int caretLinePosition) { + protected static String parsePhrase(final String lineText) { boolean overloading = false; @@ -535,7 +535,7 @@ public class JavaTextArea extends JEditTextArea { } } - final int currentCharIndex = caretLinePosition - 1; + final int currentCharIndex = lineText.length() - 1; { // Check if the caret is in the comment int commentStart = lineText.indexOf("//", 0); @@ -658,7 +658,7 @@ public class JavaTextArea extends JEditTextArea { position++; // Extract phrase - String phrase = lineText.substring(position, caretLinePosition).trim(); + String phrase = lineText.substring(position, lineText.length()).trim(); Messages.log(phrase); if (phrase.length() == 0 || Character.isDigit(phrase.charAt(0))) { From 15d14375ca4f80f464f30e13fc3dc8b8c4a67a0b Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Sat, 26 Sep 2015 10:08:52 -0400 Subject: [PATCH 3/4] Set selection mode for error table --- app/src/processing/app/ui/ErrorTable.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/processing/app/ui/ErrorTable.java b/app/src/processing/app/ui/ErrorTable.java index 347ba2128..a38216409 100644 --- a/app/src/processing/app/ui/ErrorTable.java +++ b/app/src/processing/app/ui/ErrorTable.java @@ -29,6 +29,7 @@ import java.awt.event.MouseEvent; import javax.swing.JLabel; import javax.swing.JTable; +import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.ToolTipManager; import javax.swing.table.DefaultTableModel; @@ -79,6 +80,8 @@ public class ErrorTable extends JTable { public ErrorTable(final Editor editor) { super(new DefaultTableModel(columnNames, 0)); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + this.editor = editor; JTableHeader header = getTableHeader(); From 5ab6cd5fc4d625a7ffc51455274f0707158473fd Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Sat, 26 Sep 2015 10:11:11 -0400 Subject: [PATCH 4/4] Prevent AIOOBE in error table If the error checker updates the table between the click and invocation of the click event listener, selection will be cleared and click index will be -1. Checking if the index is valid before using it. --- app/src/processing/app/ui/ErrorTable.java | 36 +++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/src/processing/app/ui/ErrorTable.java b/app/src/processing/app/ui/ErrorTable.java index a38216409..9dc7df067 100644 --- a/app/src/processing/app/ui/ErrorTable.java +++ b/app/src/processing/app/ui/ErrorTable.java @@ -105,25 +105,25 @@ public class ErrorTable extends JTable { // } addMouseListener(new MouseAdapter() { - @Override - synchronized public void mouseClicked(MouseEvent e) { - try { - int row = ((ErrorTable) e.getSource()).getSelectedRow(); - if (row >= 0 && row < getRowCount()) { - Object data = getModel().getValueAt(row, DATA_COLUMN); - int clickCount = e.getClickCount(); - if (clickCount == 1) { - editor.errorTableClick(data); - } else if (clickCount > 1) { - editor.errorTableDoubleClick(data); - } + @Override + synchronized public void mouseClicked(MouseEvent e) { + try { + int row = ((ErrorTable) e.getSource()).getSelectedRow(); + if (row >= 0 && row < getRowCount()) { + Object data = getModel().getValueAt(row, DATA_COLUMN); + int clickCount = e.getClickCount(); + if (clickCount == 1) { + editor.errorTableClick(data); + } else if (clickCount > 1) { + editor.errorTableDoubleClick(data); + } // editor.getErrorChecker().scrollToErrorLine(row); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); /* addMouseMotionListener(new MouseMotionAdapter() {