5. DEFINITION
“improve readability and emphasize structure
of a document
without changing its meaning”
“prettify spaces, line wraps, tabs and indentation
without changing the AST”
6. …if it does change the AST,
it’s probably a clean-up action or refactoring
14. • whitespace-only changes in diffs
are annoying
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
15. • whitespace-only changes in diffs
are annoying
• merge conflicts because of
whitespace-only super annoying
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
16. • whitespace-only changes in diffs
are annoying
• merge conflicts because of
whitespace-only super annoying
• only commit formatted code!
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
21. wrong indentation!
proper formatting would have
increased the chances of
spotting this bug!
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
27. on demand
serialization
semantic
quickfix
Use an Xtext grammar to convert an
EMF model to text.
Formatter contributes indentation,
newlines, space, etc.
Serializer Use Cases:
- non-textual (e.g. graphical) editors
- model migration
USE CASES
28. on demand
serialization
semantic
quickfix
USE CASES
@Fix(IssueCodes.INVALID_FEATURE_NAME)
public void fixName(final Issue issue, IssueResolutionAcceptor acceptor) {
acceptor.accept(issue, "Uncapitalize name", "", "",
new ISemanticModification() {
@Override
public void apply(EObject element, IModificationContext context) {
((Feature) element).setName(toFirstLower(issue.getData()[0]));
}
});
}
44. regionForOffset(int offset, int length)
regionForDocument()
resource:XtextResource
ITextRegionAccess
regionForEObject(EObject object)
regionForRootEObject()
the document.
created from node
model or via serializer
45. getText():String
offset : int
length: int
ITextRegion
regionForOffset(int offset, int length)
regionForDocument()
resource:XtextResource
ITextRegionAccess
regionForEObject(EObject object)
regionForRootEObject()
returns
regions for anything
46. non-hidden token 0..n hidden tokens
ISequentialRegion
getText():String
offset : int
length: int
ITextRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
regionForOffset(int offset, int length)
regionForDocument()
resource:XtextResource
ITextRegionAccess
regionForEObject(EObject object)
regionForRootEObject()
returns
strictly alternating
linked list of
hidden/semantic regions
52. class StatesFormatter extends AbstractFormatter2 {
!
def dispatch void format(Model model, extension IFormattableDocument document) {
println(textRegionAccess.toString())
}
}
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
1. Take Document
2. Parse it using Grammar
3. Dump it via textRegionAccess.toString()
1.
2.
3.
53. class StatesFormatter extends AbstractFormatter2 {
!
def dispatch void format(Model model, extension IFormattableDocument document) {
println(textRegionAccess)
}
}
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
1. Take Document
2. Parse it using Grammar
3. Dump it via textRegionAccess.toString()
4. See what we get (next slide)
1.
2.
3.
54. /* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
55. /* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
56. /* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
57. Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
keyword
datatype
58. Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
empty
first region in document
last region in document
1 part
2 parts
59. Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
60. Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
61. Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
begin
end
grammarElement
path
62. Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement
Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion
!
0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT'
16 "n" Whitespace:TerminalRule'WS'
B Model'my.X' Model
16 7 S "machine" Model:'machine'
23 1 H " " Whitespace:TerminalRule'WS'
24 4 S "my.X" Model:name=QualifiedName
28 0 H
28 1 S ";" Model:';'
29 H "nn" Whitespace:TerminalRule'WS'
17 "// First Staten" Comment:TerminalRule'SL_COMMENT'
B State Model:elements+=State path:Model'my.X'/elements[0]
46 5 S "state" State:'state'
E State Model:elements+=State path:Model'my.X'/elements[0]
E Model'my.X' Model
51 0 H
/* Copyright */
machine my.X;
!
// First State
state
Model:
"machine"
name=QualifiedName
";"
elements+=State*;
!
State:
{State} ‘state';
!
QualifiedName:
ID ('.' ID)*;
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
63. 1. start with the EObject
2. find desired semantic text region using ITextRegionAccess
3. register formatting information on IFormattableDocument
(Recap)
import org.eclipse.xtext.formatting2.AbstractFormatter2
!
class StatesFormatter extends AbstractFormatter2 {
!
def dispatch void format(Model model, extension IFormattableDocument document) {
model.regionFor.keyword(";").prepend[noSpace]
}
}
Model:
"machine" name=QualifiedName “;";
64. FIND SEMANTIC REGIONS
// in local eObject only
eObject.regionFor
textRegionAccess.regionForEObject(eObject).regionFor
!
// in eObject or any of its children
eObject.allRegionsFor
textRegionAccess.regionForEObject(eObject).allRegionsFor
!
// in whole document
textRegionAccess.regionForRootEObject.allRegionsFor
}
// first matched semantic region
.keyword(",")
.feature(StatesPackage.Literals.STATE__NAME)
.keyword(actionsKeyword_2_1_0)
.assignment(actionsAssignment_2_1_1)
.ruleCall(actionsINTTerminalRuleCall_2_1_1_0)
!
// all matched semantic region
.keywords(",")
.features(StatesPackage.Literals.STATE__NAME)
.keywords(actionsKeyword_2_1_0)
.assignments(actionsAssignment_2_1_1)
.ruleCalls(actionsINTTerminalRuleCall_2_1_1_0)
!
.keywordPairs("(", ")")
{
65. FIND SEMANTIC REGIONS
// in local eObject only
eObject.regionFor
textRegionAccess.regionForEObject(eObject).regionFor
!
// in eObject or any of its children
eObject.allRegionsFor
textRegionAccess.regionForEObject(eObject).allRegionsFor
!
// in whole document
textRegionAccess.regionForRootEObject.allRegionsFor
}
// first matched semantic region
.keyword(",")
.feature(StatesPackage.Literals.STATE__NAME)
.keyword(actionsKeyword_2_1_0)
.assignment(actionsAssignment_2_1_1)
.ruleCall(actionsINTTerminalRuleCall_2_1_1_0)
!
// all matched semantic region
.keywords(",")
.features(StatesPackage.Literals.STATE__NAME)
.keywords(actionsKeyword_2_1_0)
.assignments(actionsAssignment_2_1_1)
.ruleCalls(actionsINTTerminalRuleCall_2_1_1_0)
!
.keywordPairs("(", ")")
{ITextRegionAccess
66. FIND SEMANTIC REGIONS
// in local eObject only
eObject.regionFor
textRegionAccess.regionForEObject(eObject).regionFor
!
// in eObject or any of its children
eObject.allRegionsFor
textRegionAccess.regionForEObject(eObject).allRegionsFor
!
// in whole document
textRegionAccess.regionForRootEObject.allRegionsFor
}
// first matched semantic region
.keyword(",")
.feature(StatesPackage.Literals.STATE__NAME)
.keyword(actionsKeyword_2_1_0)
.assignment(actionsAssignment_2_1_1)
.ruleCall(actionsINTTerminalRuleCall_2_1_1_0)
!
// all matched semantic region
.keywords(",")
.features(StatesPackage.Literals.STATE__NAME)
.keywords(actionsKeyword_2_1_0)
.assignments(actionsAssignment_2_1_1)
.ruleCalls(actionsINTTerminalRuleCall_2_1_1_0)
!
.keywordPairs("(", ")")
{ITextRegionExtensions
67. FIND SEMANTIC REGIONS
// in local eObject only
eObject.regionFor
textRegionAccess.regionForEObject(eObject).regionFor
!
// in eObject or any of its children
eObject.allRegionsFor
textRegionAccess.regionForEObject(eObject).allRegionsFor
!
// in whole document
textRegionAccess.regionForRootEObject.allRegionsFor
}
// first matched semantic region
.keyword(",")
.feature(StatesPackage.Literals.STATE__NAME)
.keyword(actionsKeyword_2_1_0)
.assignment(actionsAssignment_2_1_1)
.ruleCall(actionsINTTerminalRuleCall_2_1_1_0)
!
// all matched semantic region
.keywords(",")
.features(StatesPackage.Literals.STATE__NAME)
.keywords(actionsKeyword_2_1_0)
.assignments(actionsAssignment_2_1_1)
.ruleCalls(actionsINTTerminalRuleCall_2_1_1_0)
!
.keywordPairs("(", ")")
{Keywords (String)
68. FIND SEMANTIC REGIONS
// in local eObject only
eObject.regionFor
textRegionAccess.regionForEObject(eObject).regionFor
!
// in eObject or any of its children
eObject.allRegionsFor
textRegionAccess.regionForEObject(eObject).allRegionsFor
!
// in whole document
textRegionAccess.regionForRootEObject.allRegionsFor
}
// first matched semantic region
.keyword(",")
.feature(StatesPackage.Literals.STATE__NAME)
.keyword(actionsKeyword_2_1_0)
.assignment(actionsAssignment_2_1_1)
.ruleCall(actionsINTTerminalRuleCall_2_1_1_0)
!
// all matched semantic region
.keywords(",")
.features(StatesPackage.Literals.STATE__NAME)
.keywords(actionsKeyword_2_1_0)
.assignments(actionsAssignment_2_1_1)
.ruleCalls(actionsINTTerminalRuleCall_2_1_1_0)
!
.keywordPairs("(", ")")
{EMF EStructuralFeature (EAttribute, EReference)
69. FIND SEMANTIC REGIONS
// in local eObject only
eObject.regionFor
textRegionAccess.regionForEObject(eObject).regionFor
!
// in eObject or any of its children
eObject.allRegionsFor
textRegionAccess.regionForEObject(eObject).allRegionsFor
!
// in whole document
textRegionAccess.regionForRootEObject.allRegionsFor
}
// first matched semantic region
.keyword(",")
.feature(StatesPackage.Literals.STATE__NAME)
.keyword(actionsKeyword_2_1_0)
.assignment(actionsAssignment_2_1_1)
.ruleCall(actionsINTTerminalRuleCall_2_1_1_0)
!
// all matched semantic region
.keywords(",")
.features(StatesPackage.Literals.STATE__NAME)
.keywords(actionsKeyword_2_1_0)
.assignments(actionsAssignment_2_1_1)
.ruleCalls(actionsINTTerminalRuleCall_2_1_1_0)
!
.keywordPairs("(", ")")
{Grammar Element (from MyLanguageGrammarAccess)
72. TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
• linked list of strictly alternating ISemanticRegion and
IHiddenRegion
73. TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
• linked list of strictly alternating ISemanticRegion and
IHiddenRegion
• IHiddenRegion can be empty
74. TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
• linked list of strictly alternating ISemanticRegion and
IHiddenRegion
• IHiddenRegion can be empty
• IHiddenRegion contains parts, e.g. IComment and
IWhitespace
75. TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
• linked list of strictly alternating ISemanticRegion and
IHiddenRegion
• IHiddenRegion can be empty
• IHiddenRegion contains parts, e.g. IComment and
IWhitespace
• IEObjectRegion for elements from AST
76. TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
• linked list of strictly alternating ISemanticRegion and
IHiddenRegion
• IHiddenRegion can be empty
• IHiddenRegion contains parts, e.g. IComment and
IWhitespace
• IEObjectRegion for elements from AST
• use toString() during debugging!
77. import org.eclipse.xtext.formatting2.AbstractFormatter2
!
class StatesFormatter extends AbstractFormatter2 {
!
def dispatch void format(Model model, extension IFormattableDocument document) {
model.regionFor.keyword(";").prepend[noSpace]
}
}
1. start with the EObject
2. find desired semantic text region using ITextRegionAccess
3. register formatting information on IFormattableDocument
Model:
"machine" name=QualifiedName “;"; (Recap)
78. import org.eclipse.xtext.formatting2.AbstractFormatter2
!
class StatesFormatter extends AbstractFormatter2 {
!
def dispatch void format(Model model, extension IFormattableDocument document) {
model.regionFor.keyword(";").prepend[noSpace]
}
}
1. start with the EObject
2. find desired semantic text region using ITextRegionAccess
3. register formatting information on IFormattableDocument
Model:
"machine" name=QualifiedName “;"; (Recap)
144. THE OLD FORMATTER
• no access to text, Node Model or AST
• not enhanceable engine
• will be deprecated
145. RELEASES
• Alpha release as part of Xtext 2.8.0, 2.8.1, 2.8.3
• Beta release as of Xtext 2.9-beta
• stable (public API) release (hopefully) with Xtext 2.9