3333import java .awt .Rectangle ;
3434import java .io .PrintWriter ;
3535import java .io .StringWriter ;
36+ import java .util .Arrays ;
3637import java .util .Collection ;
3738import java .util .StringTokenizer ;
3839import java .util .TreeSet ;
5455
5556public class ErrorParser {
5657
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 ;
5960 /* 0-base indices of Editor's lines that have errored */
6061 private TreeSet <Integer > errorLines ;
6162 /* When running selected code errored lines map to Editor through this offset */
@@ -169,23 +170,34 @@ private void gotoLine(final int lineNumber) {
169170 }
170171
171172 private void parse (final String errorLog ) {
173+
174+ final ScriptLanguage lang = editorPane .getCurrentLanguage ();
175+ if (lang == null )
176+ return ;
177+
172178 // Do nothing if disabled, or if only selected text was evaluated in the
173179 // script but we don't know where in the document such selection occurred
174180 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 );
176182 return ;
177183 }
178184 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 );
180186 return ;
181187 }
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 ());
186190 final String fileName = editorPane .getFileName ();
187191 if (isJava && fileName == null )
188192 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+
189201 errorLines = new TreeSet <>();
190202 final StringTokenizer tokenizer = new StringTokenizer (errorLog , "\n " );
191203 if (isJava ) {
@@ -197,7 +209,9 @@ private void parse(final String errorLog) {
197209 parseNonJava (tokenizer .nextToken (), errorLines );
198210 }
199211 }
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 ()) {
201215 notifyingParser = new ErrorStripNotifyingParser ();
202216 editorPane .addParser (notifyingParser );
203217 editorPane .forceReparsing (notifyingParser );
@@ -206,34 +220,66 @@ private void parse(final String errorLog) {
206220 }
207221
208222 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+ ) {//
214233 return ;
215234 }
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 ();
233262 }
234263 }
235264 }
236265
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+
237283 private void parseJava (final String filename , final String line , final Collection <Integer > errorLines ) {
238284 int colon = line .indexOf (filename );
239285 if (colon <= 0 )
@@ -253,10 +299,11 @@ private void parseJava(final String filename, final String line, final Collectio
253299 }
254300 }
255301
256- private void abort (final String msg ) {
302+ private void abort (final String msg , final boolean offsetNotice ) {
257303 if (writer != null ) {
258304 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 " ;
260307 writer .textArea .insert (finalMsg , lengthOfJTextAreaWriter );
261308 }
262309 errorLines = null ;
@@ -289,21 +336,34 @@ public ParseResult parse(final RSyntaxDocument doc, final String style) {
289336 if (isEnabled () && !SyntaxConstants .SYNTAX_STYLE_NONE .equals (style )) {
290337 errorLines .forEach (line -> {
291338 result .addNotice (new ErrorNotice (this , line ));
292- if (highlightAbnoxiously ) {
339+ });
340+ if (highlightAbnoxiously ) {
341+ final Color c = highlightColor ();
342+ errorLines .forEach (line -> {
293343 try {
294- editorPane .addLineHighlight (line , COLOR );
344+ editorPane .addLineHighlight (line , c );
295345 } catch (final BadLocationException ignored ) {
296346 // do nothing
297347 }
298- }
299- });
348+ });
349+ }
300350 }
301351 return result ;
302352
303353 }
304354
305355 }
306356
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+
307367 class ErrorNotice extends DefaultParserNotice {
308368 public ErrorNotice (final Parser parser , final int line ) {
309369 super (parser , "Run Error: Line " + (line + 1 ), line );
@@ -314,4 +374,21 @@ public ErrorNotice(final Parser parser, final int line) {
314374
315375 }
316376
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+ }
317394}
0 commit comments