Code formatting for template languages

Hi,


Anybody knows how to do code formatting for template languages. Any example?
I found the class TemplateLanguageFormattingModelBuilder. But no idea of how to use it.
I tried to register the extended class under the extension lang.formatter. But It didn't work.


Thanks..!!

8 comments
Comment actions Permalink

Hey Amila,

For quick-win formatting, you can use com.intellij.psi.templateLanguages.SimpleTemplateLanguageFormattingModelBuilder.  This will use the formatter of the language you are templating, which is most of the battle.  See the Handlebars plugin for an example (https://github.com/dmarcotte/idea-handlebars/blob/master/META-INF/plugin.xml, https://github.com/dmarcotte/idea-handlebars/blob/master/src/com/dmarcotte/handlebars/format/HbFormattingModelBuilder.java).  I believe that as long as your FileViewProvider implements TemplateLanguageFileViewProvider it should just work (ex: https://github.com/dmarcotte/idea-handlebars/blob/master/src/com/dmarcotte/handlebars/file/HbFileViewProvider.java).

As for TemplateLanguageFormattingModelBuilder, I am currently trying to improve the Handlebars formatting to also format the Handlebars syntax (not just the file it is templating).  TemplateLanguageFormattingModelBuilder seems like the way to do this, but it's proving a bit tough to reverse engineer, and I'm also not seeing any examples of its usage out there.  Anyone have any experience making a full template language formatter?

0
Comment actions Permalink

Hi,

Thank you very much for your help Marcotte. It was really helpfull.
My language structure was like this.

html

<%

javascript

%>

html

I got the  code formatting for html part using SimpleTemplateLanguageFormattingModelBuilder. But could not get the code formatting for
javascript content. Javascript is parsed using IlazyParseableElementType. Any example will be much helpfull.


Thanks ..!!

0
Comment actions Permalink

Glad to help!

And yup, if I get any traction on a proper template formatter, I'll definitely update this thread.

0
Comment actions Permalink

Hi Marcotte,

Your kind concern is really appreciated.

Thanks ..!!

0
Comment actions Permalink

(edited to reflect that this is no longer a work in progress)

Update: I've got a working TemplateLanguageFormattingModelBuilder implemented for the IDEA-Handlebars plugin.  You can check out the work in progress here finished formatter: https://github.com/dmarcotte/idea-handlebars/pull/27/files.  I'll update again when it's polished, but I thought I'd post sooner than later (done!)

Some notes:

  • I first created a standalone (non-templating) formatter to get a feel for how IDEA formatting works (it's a bit wonky, but ultimately really clever once the concepts click).  Check it out here: https://gist.github.com/4039840
  • Instead of trying to get this test formatter to properly ignore the non-Handlebars content, it was developed against test files which only contained Handlebars elements
  • The lexer needed to do a better job of identifying whitespace in order for the formatter to do its job.  It was previously marking whitespace as the responsability of the templated language to lex, but that meant that my formatter couldn't properly ignore it
  • Once that was working pretty well, it was much easier to extend TemplateLanguageFormattingModelBuilder and TemplateLanguageBlock (and figure out what things to override/work around)


Hope that helps.

0
Comment actions Permalink

Almost forgot to note the most important tip:

  • Make sure you debug your plugin with the "-Didea.is.internal=true" VM option added.  Then use "Tools->View PSI Structure of Current File" to get an invaluable view of the Blocks your formatter is creating.
0
Comment actions Permalink

The diff at https://github.com/dmarcotte/idea-handlebars/pull/27/files now represents a fully functioning example of a template formatter.  Hopefully it's a help for anyone trying to implement their own.

0
Comment actions Permalink

I know this is obsolete, but still someone might find it useful. You can check out my plugin hosted on github, where exactly this problem is solved. There are lots of formatter tests in /testData, which could make you quickly decide whether go with it or not. Me and my colleagues have been using this for some time now, and it really does work like charm. This template language basically consists of html, javascript delimited by <% %> markers, custom xml tags and some directives. So this plugin can be used as a guide to create formatter for html + custom stuff, html + javascript or html + javascript + custom stuff. All the code you're interested in is in /src/ool/idea/plugin/editor/format. The code is quite complex, so I'll try to describe just the key posts, which may not be clear from reading it:

  • the lexer always trims javascript tokens and returns white space tokens for white spaces around them, e.g.: '<% var a = 1; %>' consists of five tokens - open block marker, white space, javascript code, white space, close block marker.
  • 'foreign' - from html / js point of view - token insertion is taken special care of in xml / js tree patcher. It is well docummented here in javadoc.
  • I override TemplateXmlBlock / TemplateSyntheticBlock / TemplateXmlTagBlock not just because of spacing settings, but also because of custom xml injected block builder, which...
  • ... wraps foreign blocks (nodes) in OxyTemplateForeignElementWrapper - the same block which javascript formatter uses for wrapping foreign blocks. I'd recomment you to take closer look at buildChildren() method, because this is where indispensable part of logic lies.
  • the OxyTemplateInjectedBlockBuilder builds html / js blocks, while both are handled in totally different way. Html stuff is wrapped by AnotherlanguageBlockWrapper, html tags get TemplateXmlTagBlock, the rest is TemplateXmlBlock. For javascript the range of foreign block is calculated and wrapped by InnerJsBlockWrapper (similar to AnotherLanguageBlockWrapper). Both methods have allowed range as parameter, which cannot be exceeded by child blocks, and both return next node, which is to be processed by original block builder (shiftOriginalNode()).
  • The child blocks are merged (rearranged) in OxyTemplateFormatter#mergeWithTemplateBlocks and InnerJsBlock#getSubBlocks. Both these methods handle child blocks from html / js point of view and basically both do the same thing. For example for '<div><br/><m:foo><br/></m:foo><br/></div>' the child blocks of div would be TemplateXmlTagBlock, OxyTemplateForeignElementWrapper, TemplateXmlTagBlock, another OxyTemplateForeignElementWrapper and another TemplateXmlTagBlock. The range of first foreign wrapper is adjusted to cover the range from its start up to the end of the last one and is replaced by its children, so the result is TemplateXmlTagBlock, MacroTagBlock, TemplateXmlTagBlock.

I've heen trying some experiments with TemplateLanguageFormattingModelBuilder, as Daniel suggests, but I've ran into some problems and limitations. I could only create blocks, which cover ast node, and I could never create a synthetic block. Just feel free to use the code, for html + js you'd just get rid of custom xml / directives related code and it should work well.

0

Please sign in to leave a comment.