X

Geertjan's Blog

  • August 26, 2007

How to Write a Groovy Editor (Part 3)

Geertjan Wielenga
Product Manager
First, a thousand words:

So, here, apart from some obvious syntax coloring, there are two other interesting things going on. First, there's a code fold for classes:

And also for the body of classes:

I found that it is easier to create code folds before creating syntax colors. Syntax coloring can be extremely tricky, because the order in which you define TOKEN declarations is important. When the IDE parses the NBS file, it reads from top to bottom and so if the text in the editor applies to more than one TOKEN declaration, one TOKEN declaration's color may end up overwriting another TOKEN declaration's color. So, to take that factor out of the equation, it makes sense to first make sure that the grammar rules make sense. If you have a grammar rule for a class definition, there's no better way of assessing whether it is defined correctly than to assign a code fold to it, as below:

FOLD:ClassDeclaration: {
expand_type_action_name:"LBL_ExpandComments";
collapse_type_action_name:"LBL_CollapseComments";
fold_display_name:"Groovy class";
}

(Note that the 'expand_type_action_name' and the 'collapse_type_action_name' resolve to labels that appear on two menu items that magically appear in the editor's popup menu, for expanding and collapsing the editor's code folds.)

Another issue that's problematic is coming to a stable set of grammar rules in the first place. The "hit and miss" approach I used in the first part of this series cannot be the ultimate solution. Better to have the GroovyRecognizer.html and GroovyLexer.html close by. The absolute purist approach, which would be to stick religiously to these two HTML documents, is not likely to work either. For one thing, it's hard to figure out exactly how to translate these definitions to Schliemann. I had a very good start, thanks to some work that Jim Clarke did some months ago, but even that (i.e., generated draft NBS from the above HTML documents) isn't the final answer. Some common sense, I guess, is the bridge between the "hit and miss" (i.e., starting with another NBS and tuning it to your own needs) and the purist (i.e., be religious and do not deviate from the official ANTLR definition) approach.

Also note that I have the Navigator working, for the first time for Groovy. Not perfect yet, but a clear start. As an example, this is how the class ends up in the Navigator:

NAVIGATOR:ClassDeclaration: {
display_name: org.netbeans.modules.mygroovyeditor.Groovy.className;
icon: "/org/netbeans/modules/languages/resources/class.gif";
isLeaf: "false";
}

The above should be put in the Groovy.nbs file. (By the way, the 'isLeaf' key doesn't seem to make a difference, currently, whether you put true or false, but I think it does need to be there anyway.) And then here, in my Grovvy.java class, I have the className method:

public static String className(SyntaxContext context) {
ASTPath path = context.getASTPath();
ASTItem functionClause = (ASTNode) path.getLeaf();
ASTItem name = null;
for (ASTItem item : functionClause.getChildren()) {
name = item;
}
if (name != null) {
String nameStr = ((ASTNode) name).getAsText();
return "Class: " + nameStr.substring(0, nameStr.indexOf("{"));
}
return "?";
}

The string manipulation is a bit messy, I'm pretty sure it should be a cleaner solution than that, but it gets the job done.

But the coloring is a long way from done. I'm only just beginning to understand how the grammar rules work. In between, I'm working on the hyperlinks in the Output window, which appear there when Groovy returns error messages when a script is run. Some of the error messages are convenient in that they include the line number where the error occurred. It is then very easy to create the hyperlink such that the user can click it to jump to the erroneous line. Other errors, especially those coming from javax.script.ScriptException, give no indication of where the error took place. This is a pretty big problem, one that may not be possible to solve. Adding annotations to the other error lines, i.e., those that can be identified, will be easy. Just the same old annotation approach as done for all editors, just attach the annotation to the line and you're done, like so:

It's pretty cool that Schliemann gives lots of room for incorporating the standard NetBeans API classes. (By the way, the 'Groovy error' message in the screenshot above, which pops up when the mouse hovers over the red error mark, is a bit truncated, that's not how it is in real life, just how my screenshot ended up.)

Well, that's where things are right now. Way too soon to release all this in any form, but if things continue in this way, maybe by the end of the week. The grammar rules are going to continue being a problem, though. To get an absolutely reliable editor may take quite some time in tweaking and so on. And then there's also code completion to look at, of course...

A final happy picture, of some SwingBuilder code:

Join the discussion

Comments ( 10 )
  • Alex Kotchnev Sunday, August 26, 2007

    As usual, I'm looking at all of this beautiful syntax highlighting for Groovy, and I'm totally drooling over it.. Can't wait for you to (hopefully) release this into a beautiful NB 6.0 NBM that I can pop into my shiny NetBeans, and be able to use it..

    Btw, just in case that you care (and I thought you would, considering that you're presenting on Grails eXchange), I started working on a Grails-Tapestry plugin, I put the first blog post (there should be more to come) at http://akochnev.blogspot.com/2007/08/grails-tapestry-grapestry-part-1-of-n.html


  • Ranganath.S Monday, August 27, 2007

    hi geertjan,

    is this plugin available for download?

    Thank you


  • Geertjan Monday, August 27, 2007

    Raganath, no... not yet. Too early, too many things to fix, improve, round off, and finetune. But there's enough instructions for you to build it yourself. :-)

    Alex, interesting stuff, about Tapestry and Grails. I'd be interested to see how you'd do the same, but using the Tapestry plugin for NetBeans -- i.e., what additional manual steps you'd need to take after that, thus indicating what kind of Grails support the IDE should need. Watch this space for the announcement of the release of my Groovy plugin for 6.0... sometime. Until then, I'll keep blogging about the parts I'm working on.


  • Alex Kotchnev Monday, August 27, 2007

    I do use the Tapestry NetBeans plugin to develop the page specs, etc. Currently, the Tapestry plugin is not much help in implementing the page classes in Groovy, as a matter of fact, implementing the page classes in Java is easier (from a usability perspective), since Java has code & annotation completion going for it. On the Groovy/Grails side though, the data access through GORM is just a dream come true.

    A couple of weeks/months ago, you did blog about some work happening on a Grails project type, I think a combination of that, the Tapestry plugin, and Groovy syntax highlighting (and eventually code completion) would do wonders.


  • Robert Monday, August 27, 2007

    I am also looking forward to this being released. Very nice work.


  • Geertjan Monday, August 27, 2007

    Alex, thanks for the info -- good to know. Watch this space also for further news on Grails...

    Robert -- me too and thanks!


  • Bjoern Lietz Tuesday, August 28, 2007

    Well, Well pretty good stuff... But at least one thing needs clarication. Token Literals. F.I. why this declarations are wrong :

    TOKEN:string: (

    ["\\""]|["'"]

    ([\^ "\\"" "\\n" "\\r"] |

    ("\\\\"<-(I am gettin error here) ["r" "n" "t" "\\\\" "\\'" "\\""])

    )\*

    ["\\""]|["'"]

    )

    TOKEN:char: (

    "\\'"

    ( [\^"\\'" "\\n" "\\r"] |

    ("\\\\"<-(I am gettin error here) ["r" "n" "t" "\\\\" "\\'" "\\""])

    )

    "\\'"

    )

    An other example :

    My Float-Literal regular expression :

    ((['0'-'9']['0'-'9' '_']\*)?\\.['0'-'9' '_']+(['e' 'E']['+' '-']?['0'-'9' '_']+)?)/(['0'-'9']['0'-'9' '_']\*(['e' 'E']['+' '-']?['0'-'9' '_']+)?)

    Seems to be not tranlateable into Schliemann.

    the \\. ->Backslash DOT in there means a literal period. I don't know how Schliemann regex syntax escapes special characters but the backslash is pretty universal ...

    I have defined the grammar within one day but describing TOKEN Literals in Schliemann regex; is driving me crazy! Where to find help ?


  • Bjoern Lietz Tuesday, August 28, 2007

    Add on Info regarding my Float Literal TOKEN: question ;

    The Schliemann grammar defination :

    # INT/FLOAT LITERAL

    IntLiteral = Decimal | Binary | Octal |

    Hexadecimal;

    Decimal = DecimalDigit (DecimalDigit | "_")\*;

    DecimalDigit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";

    Binary = "0" ("b" | "B") (BinaryDigit | "_") (BinaryDigit | "_")\*;

    BinaryDigit = "0" | "1";

    Octal = "0" ("c" | "C") (OctalDigit | "_") (OctalDigit | "_")\*;

    OctalDigit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7";

    Hexadecimal = "0" ("x" | "X") (HexDigit | "_") (HexDigit | "_")\*;

    HexDigit =

    "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

    "A" | "a" | "B" | "b" | "C" | "c" | "D" | "d" | "E" | "e" | "F" | "f";

    FloatLiteral =

    [DecimalDigit (DecimalDigit | "_")\*] "." (DecimalDigit | "_") (DecimalDigit | "_")\* [Exponent]

    DecimalDigit (DecimalDigit | '_')\* [Exponent];

    Exponent = ("e" | "E")["+" | "-"] (DecimalDigit | "_") (DecimalDigit | "_")\*;


  • Hanz Wednesday, August 29, 2007

    Hi Bjoern,

    it looks like its not bug in your Token Literal, but its bug in Schliemann parser. I have created an issue for it:

    http://www.netbeans.org/issues/show_bug.cgi?id=114113

    thanks.


  • Les Stroud Thursday, September 13, 2007

    I don't suppose you would consider making it public at this early stage so others can help push it to completion, would you? :)


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha