33
33
import java .awt .Rectangle ;
34
34
import java .io .PrintWriter ;
35
35
import java .io .StringWriter ;
36
+ import java .util .Arrays ;
36
37
import java .util .Collection ;
37
38
import java .util .StringTokenizer ;
38
39
import java .util .TreeSet ;
54
55
55
56
public class ErrorParser {
56
57
57
- /* Color for ErrorStrip marks + line highlights. May not work for all themes */
58
- private static final Color COLOR = new Color ( 255 , 99 , 71 , 128 ) ;
58
+ /* Color for ErrorStrip marks and fallback taint for line highlights */
59
+ private static final Color COLOR = Color . RED ;
59
60
/* 0-base indices of Editor's lines that have errored */
60
61
private TreeSet <Integer > errorLines ;
61
62
/* When running selected code errored lines map to Editor through this offset */
@@ -169,23 +170,34 @@ private void gotoLine(final int lineNumber) {
169
170
}
170
171
171
172
private void parse (final String errorLog ) {
173
+
174
+ final ScriptLanguage lang = editorPane .getCurrentLanguage ();
175
+ if (lang == null )
176
+ return ;
177
+
172
178
// Do nothing if disabled, or if only selected text was evaluated in the
173
179
// script but we don't know where in the document such selection occurred
174
180
if (!enabled ) {
175
- abort ("Execution errors are not highlighted when auto-imports are active" );
181
+ abort ("Execution errors are not highlighted when auto-imports are active" , true );
176
182
return ;
177
183
}
178
184
if (lineOffset == -1 ) {
179
- abort ("Code selection unknown: Erros are not highlighted in the Editor" );
185
+ abort ("Code selection unknown: Erros are not highlighted in the Editor" , true );
180
186
return ;
181
187
}
182
- final ScriptLanguage lang = editorPane .getCurrentLanguage ();
183
- if (lang == null )
184
- return ;
185
- final boolean isJava = lang .getLanguageName ().equals ("Java" );
188
+
189
+ final boolean isJava = "Java" .equals (lang .getLanguageName ());
186
190
final String fileName = editorPane .getFileName ();
187
191
if (isJava && fileName == null )
188
192
return ;
193
+
194
+ // HACK scala code seems to always be pre-pended by some 10 lines of code(!?).
195
+ if ("Scala" .equals (lang .getLanguageName ()))
196
+ lineOffset += 10 ;
197
+ // HACK and R by one (!?)
198
+ else if ("R" .equals (lang .getLanguageName ()))
199
+ lineOffset += 1 ;
200
+
189
201
errorLines = new TreeSet <>();
190
202
final StringTokenizer tokenizer = new StringTokenizer (errorLog , "\n " );
191
203
if (isJava ) {
@@ -197,7 +209,9 @@ private void parse(final String errorLog) {
197
209
parseNonJava (tokenizer .nextToken (), errorLines );
198
210
}
199
211
}
200
- if (!errorLines .isEmpty ()) {
212
+ if (errorLines .isEmpty () && "IJ1 Macro" .equals (lang .getLanguageName ())) {
213
+ abort ("Execution errors handled by the Macro Interpreter. Use the Interpreter's Debug option for error tracking" , false );
214
+ } else if (!errorLines .isEmpty ()) {
201
215
notifyingParser = new ErrorStripNotifyingParser ();
202
216
editorPane .addParser (notifyingParser );
203
217
editorPane .forceReparsing (notifyingParser );
@@ -206,34 +220,66 @@ private void parse(final String errorLog) {
206
220
}
207
221
208
222
private void parseNonJava (final String lineText , final Collection <Integer > errorLines ) {
209
- // Rationale: line errors of interpretative scripts will mention both the
210
- // extension
211
- // of the script and the term "line "
212
- final int tokenIndex = lineText .indexOf ("line " );
213
- if (tokenIndex < 0 ) {
223
+
224
+ if ( // Elimination of some false positives. TODO: Make this Regex
225
+ lineText .indexOf (":classloader:" ) > -1 // ruby
226
+ || lineText .indexOf (".org.python." ) > -1 // python
227
+ || lineText .indexOf (".codehaus.groovy." ) > -1 // groovy
228
+ || lineText .indexOf (".tools.nsc." ) > -1 // scala
229
+ || lineText .indexOf ("at bsh." ) > -1 // beanshel
230
+ || lineText .indexOf ("$Recompilation$" ) > -1 // javascript
231
+ || lineText .indexOf ("at org.renjin." ) > -1 // R: The only thing useful in traces are Syntax errors!?
232
+ ) {//
214
233
return ;
215
234
}
216
- // System.out.println("Parsing candidate: " + lineText);
217
- for (final String extension : editorPane .getCurrentLanguage ().getExtensions ()) {
218
- final int dotIndex = lineText .indexOf ("." + extension );
219
- if (dotIndex < 0 )
220
- continue ;
221
- final Pattern pattern = Pattern .compile ("\\ d+" );
222
- // System.out.println("section being matched: " + lineText.substring(tokenIndex));
223
- final Matcher matcher = pattern .matcher (lineText .substring (tokenIndex ));
224
- if (matcher .find ()) {
225
- try {
226
- final int lineNumber = Integer .valueOf (matcher .group ());
227
- if (lineNumber > 0 )
228
- errorLines .add (lineNumber - 1 + lineOffset ); // store 0-based indices
229
- // System.out.println("line No (zero-based): " + (lineNumber - 1));
230
- } catch (final NumberFormatException e ) {
231
- // ignore
232
- }
235
+
236
+ final int extensionIdx = extensionIdx (lineText );
237
+ final int lineIdx = lineText .toLowerCase ().indexOf ("line" );
238
+ if (lineIdx < 0 && extensionIdx < 0 && filenameIdx (lineText ) < 0 )
239
+ return ;
240
+
241
+ extractLineIndicesFromFilteredTextLines (lineText , errorLines );
242
+ }
243
+
244
+ private void extractLineIndicesFromFilteredTextLines (final String lineText , final Collection <Integer > errorLines ) {
245
+ // System.out.println(" Section being matched: " + lineText);
246
+ final Pattern pattern = Pattern .compile (":(\\ d+)|line\\ D*(\\ d+)" , Pattern .CASE_INSENSITIVE );
247
+ final Matcher matcher = pattern .matcher (lineText );
248
+
249
+ if (matcher .find ()) {
250
+ try {
251
+ final String firstGroup = matcher .group (1 );
252
+ final String lastGroup = matcher .group (matcher .groupCount ());
253
+ final String group = (firstGroup == null ) ? lastGroup : firstGroup ;
254
+ // System.out.println(" firstGroup: " + firstGroup);
255
+ // System.out.println(" lastGroup: " + lastGroup);
256
+
257
+ final int lineNumber = Integer .valueOf (group .trim ());
258
+ if (lineNumber > 0 )
259
+ errorLines .add (lineNumber - 1 + lineOffset); // store 0-based indices
260
+ } catch (final NumberFormatException e ) {
261
+ e .printStackTrace ();
233
262
}
234
263
}
235
264
}
236
265
266
+ private int extensionIdx (final String line ) {
267
+ int dotIndex = -1 ;
268
+ for (final String extension : editorPane .getCurrentLanguage ().getExtensions ()) {
269
+ dotIndex = line .indexOf ("." + extension );
270
+ if (dotIndex > -1 )
271
+ return dotIndex ;
272
+ }
273
+ return -1 ;
274
+ }
275
+
276
+ private int filenameIdx (final String line ) {
277
+ int index = line .indexOf (editorPane .getFileName ());
278
+ if (index == -1 )
279
+ index = (line .indexOf (" Script" )); // unsaved file, etc.
280
+ return index ;
281
+ }
282
+
237
283
private void parseJava (final String filename , final String line , final Collection <Integer > errorLines ) {
238
284
int colon = line .indexOf (filename );
239
285
if (colon <= 0 )
@@ -253,10 +299,11 @@ private void parseJava(final String filename, final String line, final Collectio
253
299
}
254
300
}
255
301
256
- private void abort (final String msg ) {
302
+ private void abort (final String msg , final boolean offsetNotice ) {
257
303
if (writer != null ) {
258
304
String finalMsg = "[WARNING] " + msg + "\n " ;
259
- finalMsg += "[WARNING] Error line(s) below may not match line numbers in the editor\n " ;
305
+ if (offsetNotice )
306
+ finalMsg += "[WARNING] Error line(s) below may not match line numbers in the editor\n " ;
260
307
writer .textArea .insert (finalMsg , lengthOfJTextAreaWriter );
261
308
}
262
309
errorLines = null ;
@@ -289,21 +336,34 @@ public ParseResult parse(final RSyntaxDocument doc, final String style) {
289
336
if (isEnabled () && !SyntaxConstants .SYNTAX_STYLE_NONE .equals (style )) {
290
337
errorLines .forEach (line -> {
291
338
result .addNotice (new ErrorNotice (this , line ));
292
- if (highlightAbnoxiously ) {
339
+ });
340
+ if (highlightAbnoxiously ) {
341
+ final Color c = highlightColor ();
342
+ errorLines .forEach (line -> {
293
343
try {
294
- editorPane .addLineHighlight (line , COLOR );
344
+ editorPane .addLineHighlight (line , c );
295
345
} catch (final BadLocationException ignored ) {
296
346
// do nothing
297
347
}
298
- }
299
- });
348
+ });
349
+ }
300
350
}
301
351
return result ;
302
352
303
353
}
304
354
305
355
}
306
356
357
+ private Color highlightColor () {
358
+ // https://stackoverflow.com/a/29576746
359
+ final Color c1 = editorPane .getCurrentLineHighlightColor ();
360
+ final Color c2 = (editorPane .getBackground () == null ) ? COLOR : editorPane .getBackground ();
361
+ final int r = (int ) Math .sqrt ( (Math .pow (c1 .getRed (), 2 ) + Math .pow (c2 .getRed (), 2 )) / 2 );
362
+ final int g = (int ) Math .sqrt ( (Math .pow (c1 .getGreen (), 2 ) + Math .pow (c2 .getGreen (), 2 )) / 2 );
363
+ final int b = (int ) Math .sqrt ( (Math .pow (c1 .getBlue (), 2 ) + Math .pow (c2 .getGreen (), 2 )) / 2 );
364
+ return new Color (r , g , b , c1 .getAlpha ());
365
+ }
366
+
307
367
class ErrorNotice extends DefaultParserNotice {
308
368
public ErrorNotice (final Parser parser , final int line ) {
309
369
super (parser , "Run Error: Line " + (line + 1 ), line );
@@ -314,4 +374,21 @@ public ErrorNotice(final Parser parser, final int line) {
314
374
315
375
}
316
376
377
+ public static void main (final String [] args ) throws Exception {
378
+ // poor man's test for REGEX filtering
379
+ final String groovy = " at Script1.run(Script1.groovy:51)" ;
380
+ final String python = "File \" New_.py\" , line 51, in <module>" ;
381
+ final String ruby = "<main> at Batch_Convert.rb:51" ;
382
+ final String scala = " at line number 51 at column number 18" ;
383
+ final String beanshell = "or class name: Dummy : at Line: 51 : in file: " ;
384
+ final String javascript = " at jdk.nashorn.internal.scripts.Script$15$Greeting.:program(Greeting.js:51)" ;
385
+ final String r = "org.renjin.parser.ParseException: Syntax error at line 51 char 2: syntax error, unexpected ',', expecting '\\ n' or ';'" ;
386
+ Arrays .asList (groovy , python , ruby , scala , beanshell , javascript , r ).forEach (lang -> {
387
+ final ErrorParser parser = new ErrorParser (new EditorPane ());
388
+ final TreeSet <Integer > errorLines = new TreeSet <>();
389
+ parser .extractLineIndicesFromFilteredTextLines (lang , errorLines );
390
+ assert (errorLines .first () == 50 );
391
+ System .out .println ((errorLines .first () == 50 ) + ": " + lang );
392
+ });
393
+ }
317
394
}
0 commit comments