Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [xtext-dev] context dependend formatting

Hi,

you are right, I'm just playing around with an xml/xhtml-syntax plus svg and mathml (workflow produces tex, fop with formulas and vector graphics in the pdf result).
In eclipse the formatting in HTML-/XML-Editor isn't very usefull for that purpose. The XML-Editor is much better, but inline elements has linewraps on start and end tags too. Because I've tested Xtext in the area of DSLs (a very enthusiastic experience, you did a very good job), I tried too use it as XML-Editor with usefull word-wrapping and so on. This in combination with Sven's example semanticcoloring would be the perfect solution. I know, that this is not a very common application for Xtext, but it's a very easy way to build a flexible editor.
So to keep the grammar quite simple I needed a way to dynamically format an instance of the grammar. And this is the node model, not the semantic model - I think. Not the grammar model and it's semantic, but the start tag with the name of and inline or block element e.g. i or div. So linewraps are desired or not.
The problem, that node models are not there in any case can be solved. In this case use the default behaviour, so just use the semantic model.
Just not to write too much: the solution with FormattingConfig and it's rules are getting more and more complex (e.g. RC1). So the alternative for individual formatting in the context of the node model is a second usefull but optional way - just for the user to explicite formatting in the editor, because no semantical changes are desired.
You could declare a second set of functions with an additional parameter, the AbstractNode. That woud result in the default
behaviour

    @Override
    protected void addLineEntry(EObject grammarElement, String value, boolean isHidden) throws IOException {
      return addLineEntry(grammarElement, value, isHidden, null);
    }

    @Override
    protected void addLineEntry(EObject grammarElement, String value, boolean isHidden, AbstractNode node) throws IOException {
      ...
    }

To complete my sample, here is my testing grammar. I works very well for wrapping and so on, but it's quick and dirty. (' ' | '-' | '¬' )+ is preserved for formatting, ( '\t' | '\r' | '\n' )+ ignored.

grammar org.sample.lang.Xcl hidden(WS)

generate xcl "http://www.sample.org/lang/Xcl"
import "http://www.eclipse.org/emf/2002/Ecore" as ecore

Model:
    (decl=PI)? WS? root=Element;

PI:
    '<?' target=Tag ( white+=Wsp attributes+=Attribute)* white+=Wsp? '?>';

Element:
     ( ( start=Start text+=Text? (content+=(Element | Entity | CData | Comment) text+=Text? )* stop=Stop) | start=Closed );

Start:
    '<' start=Tag (white+=Wsp attributes+=Attribute)* white+=Wsp? '>' ;

Closed returns Start:
    '<' start=Tag (white+=Wsp attributes+=Attribute)* white+=Wsp? '/>' ;

Stop hidden():
    '</' stop=Tag '>';

Attribute hidden():
    attr=TG '=' content=Att;

Entity hidden():
    '&' entity=Tag ';';

CData:
  '<![CDATA[' Data="" ']]>' ;

Comment:
  '<!--' Data="" '-->';

Data:
    ('"' | "'" | ' ' | TG | '=' | ';' | '-')*;

Text:
    white+=TextWhite | (white+=TextWhite? token+=TextChars ( white+=TextWhite token+=TextChars)* white+=TextWhite?) ;

TextChars:
    ('"' | "'" | TG | '=' | ';')+;

TextWhite:
    (' ' | '-' | '¬' )+;

Att:
  '"' ( "'" | AttChars )* '"' |
  "'" ( '"' | AttChars )* "'" ;

AttChars:
     ' ' | TG | '=' | '&' | ';' | '-' ;

Wsp:
    (' ');

Tag:
    (TG | '-')+;

terminal WS: ( '\t' | '\r' | '\n' )+ ;

terminal TG :
    (!( '"' | "'" | '=' | '<' | '>' | '&' | ';' | ' ' | '-' | '¬' | '\t' | '\r' | '\n'  ))+ ;


And for completeness the formatting rules in M7:

    @Override
    protected void configureFormatting(FormattingConfig c) {
        org.xscl.lang.services.XclGrammarAccess f = (org.xscl.lang.services.XclGrammarAccess) getGrammarAccess();

        c.setWhitespaceRule(f.getWSRule());
        c.setIndentationSpace("\t");
       
        c.setIndentation(f.getStartAccess().getLessThanSignKeyword_0(), f.getStopAccess().getLessThanSignSolidusKeyword_0());
        c.setIndentation(f.getClosedAccess().getLessThanSignKeyword_0(), f.getClosedAccess().getSolidusGreaterThanSignKeyword_4());
        //c.setIndentation(f.getStartAccess().getWhiteAssignment_2_0(), f.getElementAccess().getAlternatives_1());

        c.setLinewrap(2).after(f.getPIAccess().getQuestionMarkGreaterThanSignKeyword_4());
        c.setLinewrap().before(f.getStartAccess().getLessThanSignKeyword_0());
        c.setLinewrap().before(f.getStopAccess().getLessThanSignSolidusKeyword_0());
       
        c.setLinewrap().after(f.getStartAccess().getGreaterThanSignKeyword_4());
        c.setLinewrap().after(f.getClosedAccess().getSolidusGreaterThanSignKeyword_4());
        c.setLinewrap().after(f.getStopAccess().getGreaterThanSignKeyword_2());

        c.setNoSpace().around(f.getStartAccess().getStartAssignment_1());
        c.setNoSpace().around(f.getStartAccess().getLessThanSignKeyword_0());
        c.setNoSpace().around(f.getStartAccess().getAttributesAssignment_2_1());
        c.setNoSpace().around(f.getStartAccess().getWhiteAssignment_2_0());
        c.setNoSpace().around(f.getStartAccess().getWhiteAssignment_3());
       
        c.setNoLinewrap().around(f.getStartAccess().getStartAssignment_1());
        c.setNoLinewrap().before(f.getStartAccess().getWhiteAssignment_2_0());
        c.setNoLinewrap().before(f.getStartAccess().getWhiteAssignment_3());

        c.setNoSpace().around(f.getClosedAccess().getStartAssignment_1());
        c.setNoSpace().around(f.getClosedAccess().getLessThanSignKeyword_0());
        c.setNoSpace().around(f.getClosedAccess().getAttributesAssignment_2_1());
        c.setNoSpace().around(f.getClosedAccess().getWhiteAssignment_2_0());
        c.setNoSpace().around(f.getClosedAccess().getWhiteAssignment_3());
       
        c.setNoLinewrap().around(f.getClosedAccess().getStartAssignment_1());
        c.setNoLinewrap().before(f.getClosedAccess().getWhiteAssignment_2_0());
        c.setNoLinewrap().before(f.getClosedAccess().getWhiteAssignment_3());

        c.setNoSpace().around(f.getStopAccess().getStopAssignment_1());
        c.setNoSpace().around(f.getStopAccess().getLessThanSignSolidusKeyword_0());
        c.setNoSpace().around(f.getStopAccess().getGreaterThanSignKeyword_2());
       
        c.setNoLinewrap().around(f.getStopAccess().getStopAssignment_1());

        c.setNoSpace().around(f.getStartAccess().getGreaterThanSignKeyword_4());
        c.setNoSpace().around(f.getElementAccess().getTextAssignment_0_1());
        c.setNoSpace().around(f.getElementAccess().getTextAssignment_0_2_1());

        c.setNoSpace().around(f.getAttributeAccess().getAttrAssignment_0());
        c.setNoSpace().around(f.getAttributeAccess().getContentAssignment_2());

        c.setNoLinewrap().around(f.getAttributeAccess().getEqualsSignKeyword_1());

        c.setNoSpace().around(f.getPIAccess().getTargetAssignment_1());
        c.setNoSpace().around(f.getPIAccess().getQuestionMarkGreaterThanSignKeyword_4());
        c.setNoSpace().around(f.getPIAccess().getAttributesAssignment_2_1());
        c.setNoSpace().around(f.getPIAccess().getWhiteAssignment_2_0());
        c.setNoSpace().around(f.getPIAccess().getWhiteAssignment_3());
       
        c.setNoLinewrap().before(f.getPIAccess().getWhiteAssignment_2_0());
        c.setNoLinewrap().before(f.getPIAccess().getWhiteAssignment_3());

        c.setNoSpace().around(f.getEntityAccess().getEntityAssignment_1());
        c.setNoSpace().around(f.getEntityAccess().getAmpersandKeyword_0());
        c.setNoSpace().around(f.getEntityAccess().getSemicolonKeyword_2());

        c.setNoLinewrap().around(f.getEntityAccess().getEntityAssignment_1());
        c.setNoLinewrap().around(f.getEntityAccess().getAmpersandKeyword_0());
        c.setNoLinewrap().around(f.getEntityAccess().getSemicolonKeyword_2());

        c.setNoSpace().around(f.getTextAccess().getWhiteAssignment_0());
        c.setNoSpace().around(f.getTextAccess().getWhiteAssignment_1_0());
        c.setNoSpace().around(f.getTextAccess().getTokenAssignment_1_1());
        c.setNoSpace().around(f.getTextAccess().getWhiteAssignment_1_2_0());
        c.setNoSpace().around(f.getTextAccess().getTokenAssignment_1_2_1());
        c.setNoSpace().around(f.getTextAccess().getWhiteAssignment_1_3());
       
        c.setNoLinewrap().before(f.getTextAccess().getWhiteAssignment_0());
        c.setNoLinewrap().before(f.getTextAccess().getWhiteAssignment_1_0());
        c.setNoLinewrap().before(f.getTextAccess().getWhiteAssignment_1_2_0());
        c.setNoLinewrap().before(f.getTextAccess().getWhiteAssignment_1_3());
    }

and the current check for xml

    @Check
    public void checkTypeNameStartsWithCapital(Element type) {
        if (!type.getStart().getStart().equals(type.getStop().getStop())) {
            warning("Start and Stop must be equal", XclPackage.ELEMENT);
        }
    }


cheers,
Uwe



2010/5/18 Moritz Eysholdt <moritz.eysholdt@xxxxxxxxx>
Hi,

I agree that it would be valuable for the formatter to have the current node from the node model available. The current semantic EObject would also be useful. Please note, that the formatter is not only called by the DefaultNodeModelFormatter but also by the ParseTreeConstructor. If a semantic model has been constructed manually or loaded from XMI (i.e. it has not been created by Xtext's parser), there is no node model at all. This is the main reason why the current formatter can not rely on the node model.

You didn't say what your implementation is supposed to do...I can't tell by the code you've sent since I don't know you grammar. However, the < /> remind me of XML...

I've spent a lot of work on the formatter during the last two weeks, so make sure you have a look at Xtext RC1. It will be released today.

Some of the new capabilities are:
- The engine which matches formatting instructions (the thing that is called by collectLocators(...)) to the grammar elements has gotten much more powerful. It is now possible to assign formatting instructions to Groups, Alternatives, UnorderedGroups, ParserRules, DatatypeRules and TerminalRules. For me this sounds like what you called "context sensitive formatting". The context is the position within the grammar, including which parser rules have been entered. In case the formatting needs more context than give by the grammar (e.g. EAttribute-Values, EObjects, Lists-sizes from your semantic model) - that's what we call semantic formatting.
- The formatter can now preserve linebreaks, if needed. See FormattingConfig.setLinewrap(int minWraps, int defaultWraps, int maxWraps)
- The formatter can now insert arbitrary spaces at defined positions FormattingConfig.setSpace(String).

cheers,
 Moritz


...

Back to the top