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,6 +170,11 @@ 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 ) {
@@ -179,13 +185,16 @@ private void parse(final String errorLog) {
179185 abort ("Code selection unknown: Erros are not highlighted in the Editor" );
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+
189198 errorLines = new TreeSet <>();
190199 final StringTokenizer tokenizer = new StringTokenizer (errorLog , "\n " );
191200 if (isJava ) {
@@ -206,34 +215,65 @@ private void parse(final String errorLog) {
206215 }
207216
208217 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 ) {
218+
219+ if ( // Elimination of some false positives. TODO: Make this Regex
220+ lineText .indexOf (":classloader:" ) > -1 // ruby
221+ || lineText .indexOf (".org.python." ) > -1 // python
222+ || lineText .indexOf (".codehaus.groovy." ) > -1 // groovy
223+ || lineText .indexOf (".tools.nsc." ) > -1 // scala
224+ || lineText .indexOf ("at bsh." ) > -1 // beanshel
225+ || lineText .indexOf ("$Recompilation$" ) > -1 // javascript
226+ ) {//
214227 return ;
215228 }
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- }
229+
230+ final int extensionIdx = extensionIdx (lineText );
231+ final int lineIdx = lineText .toLowerCase ().indexOf ("line" );
232+ if (lineIdx < 0 && extensionIdx < 0 && filenameIdx (lineText ) < 0 )
233+ return ;
234+
235+ extractLineIndicesFromFilteredTextLines (lineText , errorLines );
236+ }
237+
238+ private void extractLineIndicesFromFilteredTextLines (final String lineText , final Collection <Integer > errorLines ) {
239+ // System.out.println("Section being matched: " + lineText);
240+ final Pattern pattern = Pattern .compile (":(\\ d+)|line\\ D*(\\ d+)" , Pattern .CASE_INSENSITIVE );
241+ final Matcher matcher = pattern .matcher (lineText );
242+
243+ if (matcher .find ()) {
244+ try {
245+ final String firstGroup = matcher .group (1 );
246+ final String lastGroup = matcher .group (matcher .groupCount ());
247+ final String group = (firstGroup == null ) ? lastGroup : firstGroup ;
248+ // System.out.println("firstGroup: " + firstGroup);
249+ // System.out.println("lastGroup: " + lastGroup);
250+
251+ final int lineNumber = Integer .valueOf (group .trim ());
252+ if (lineNumber > 0 )
253+ errorLines .add (lineNumber - 1 + lineOffset ); // store 0-based indices
254+ } catch (final NumberFormatException e ) {
255+ e .printStackTrace ();
233256 }
234257 }
235258 }
236259
260+ private int extensionIdx (final String line ) {
261+ int dotIndex = -1 ;
262+ for (final String extension : editorPane .getCurrentLanguage ().getExtensions ()) {
263+ dotIndex = line .indexOf ("." + extension );
264+ if (dotIndex > -1 )
265+ return dotIndex ;
266+ }
267+ return -1 ;
268+ }
269+
270+ private int filenameIdx (final String line ) {
271+ int index = line .indexOf (editorPane .getFileName ());
272+ if (index == -1 )
273+ index = (line .indexOf (" Script" )); // unsaved file, etc.
274+ return index ;
275+ }
276+
237277 private void parseJava (final String filename , final String line , final Collection <Integer > errorLines ) {
238278 int colon = line .indexOf (filename );
239279 if (colon <= 0 )
@@ -256,7 +296,7 @@ private void parseJava(final String filename, final String line, final Collectio
256296 private void abort (final String msg ) {
257297 if (writer != null ) {
258298 String finalMsg = "[WARNING] " + msg + "\n " ;
259- finalMsg += "[WARNING] Error line(s) below may not match line numbers in the editor\n " ;
299+ finalMsg += "[WARNING] Error line(s) below may not match line numbers in the editor\n " ;
260300 writer .textArea .insert (finalMsg , lengthOfJTextAreaWriter );
261301 }
262302 errorLines = null ;
@@ -289,21 +329,34 @@ public ParseResult parse(final RSyntaxDocument doc, final String style) {
289329 if (isEnabled () && !SyntaxConstants .SYNTAX_STYLE_NONE .equals (style )) {
290330 errorLines .forEach (line -> {
291331 result .addNotice (new ErrorNotice (this , line ));
292- if (highlightAbnoxiously ) {
332+ });
333+ if (highlightAbnoxiously ) {
334+ final Color c = highlightColor ();
335+ errorLines .forEach (line -> {
293336 try {
294- editorPane .addLineHighlight (line , COLOR );
337+ editorPane .addLineHighlight (line , c );
295338 } catch (final BadLocationException ignored ) {
296339 // do nothing
297340 }
298- }
299- });
341+ });
342+ }
300343 }
301344 return result ;
302345
303346 }
304347
305348 }
306349
350+ private Color highlightColor () {
351+ // https://stackoverflow.com/a/29576746
352+ final Color c1 = editorPane .getCurrentLineHighlightColor ();
353+ final Color c2 = (editorPane .getBackground () == null ) ? COLOR : editorPane .getBackground ();
354+ final int r = (int ) Math .sqrt ( (Math .pow (c1 .getRed (), 2 ) + Math .pow (c2 .getRed (), 2 )) / 2 );
355+ final int g = (int ) Math .sqrt ( (Math .pow (c1 .getGreen (), 2 ) + Math .pow (c2 .getGreen (), 2 )) / 2 );
356+ final int b = (int ) Math .sqrt ( (Math .pow (c1 .getBlue (), 2 ) + Math .pow (c2 .getGreen (), 2 )) / 2 );
357+ return new Color (r , g , b , c1 .getAlpha ());
358+ }
359+
307360 class ErrorNotice extends DefaultParserNotice {
308361 public ErrorNotice (final Parser parser , final int line ) {
309362 super (parser , "Run Error: Line " + (line + 1 ), line );
@@ -314,4 +367,20 @@ public ErrorNotice(final Parser parser, final int line) {
314367
315368 }
316369
370+ public static void main (final String [] args ) throws Exception {
371+ // poor man's test for REGEX filtering
372+ final String groovy = " at Script1.run(Script1.groovy:51)" ;
373+ final String python = "File \" New_.py\" , line 51, in <module>" ;
374+ final String ruby = "<main> at Batch_Convert.rb:51" ;
375+ final String scala = " at line number 51 at column number 18" ;
376+ final String beanshell = "or class name: Systesm : at Line: 51 : in file: " ;
377+ final String javascript = " at jdk.nashorn.internal.scripts.Script$15$Greeting.:program(Greeting.js:51)" ;
378+ Arrays .asList (groovy , python , ruby , scala , beanshell , javascript ).forEach (lang -> {
379+ final ErrorParser parser = new ErrorParser (new EditorPane ());
380+ final TreeSet <Integer > errorLines = new TreeSet <>();
381+ parser .extractLineIndicesFromFilteredTextLines (lang , errorLines );
382+ assert (errorLines .first () == 50 );
383+ System .out .println ((errorLines .first () == 50 ) + ": <<" + lang + ">> " );
384+ });
385+ }
317386}
0 commit comments