SlideShare a Scribd company logo
1 of 146
Download to read offline
Moritz Eysholdt, itemis AG
XTEXT’S NEW
FORMATTER API
AGENDA
• motivation
• use cases
• ITextRegionAccess
• IFormattableDocument
• example formatter
• testing
DEFINITION
DEFINITION
“improve readability and emphasize structure
of a document
without changing its meaning”
DEFINITION
“improve readability and emphasize structure
of a document
without changing its meaning”
“prettify spaces, line wraps, tabs and indentation
without changing the AST”
…if it does change the AST,
it’s probably a clean-up action or refactoring
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
With Formatter:
CTRL+SHIFT+F
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
With Formatter:
CTRL+SHIFT+F
Without Formatter:
→→ __↓
→→ __↓
¶
↑↑ ↓↓ ¶
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
bad formatting…
- obscures the code’s structure
- unnecessarily consumes brain
cycles to filter out anomalies
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
• whitespace-only changes in diffs
are annoying
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
• whitespace-only changes in diffs
are annoying
• merge conflicts because of
whitespace-only super annoying
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
• 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
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
wrong indentation!
proper formatting would have
increased the chances of
spotting this bug!
I’m lazy
readability
diff & merge
bug prevention
MOTIVATION
USE CASES
on demand
serialization
semantic
quickfix
USE CASES
on demand
serialization
semantic
quickfix
CTRL+SHIFT+F
….and via context menu
USE CASES
on demand
serialization
semantic
quickfix
USE CASES
on demand
serialization
semantic
quickfix
Use an Xtext grammar to convert an
EMF model to text.
Formatter contributes indentation,
newlines, space, etc.
USE CASES
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
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]));	
}	
});	
}
ARCHITECTURE
messy
formatter
pretty
messy
formatter
pretty
developer
creates
developer creates formatter, NoEngine!
messy
formatter
pretty
developer
creates
text
model
(AST)
text-
regions
(CST)
in
in
in
Input: Text, AST and CST
messy
formatter
pretty
developer
creates
text
model
(AST)
text-
regions
(CST)
in
in
in
text
replacements
apply
Output: TextReplacements
messy
formatter
pretty
grammar
developer
creates
text
model
(AST)
text-
regions
(CST)
in
in
in
text
replacements
apply
references
access to grammar
fragment = formatting2.Formatter2Fragment auto-inject {}
fragment = formatting.FormatterFragment auto-inject {}
GeneratorFragment for MWE2
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 “;";
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 “;";
extends AbstractFormatter2
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 “;";
dispatch over AST elements
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
Model:	
	 "machine" name=QualifiedName “;";
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
Model:	
	 "machine" name=QualifiedName “;";
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 “;";
TEXT REGIONS
text:String
Document
getText():String
offset:int
length:int
TextRegion1 *
TextRegions…
…represent a substring
…can overlap
…can be empty
regionForOffset(int offset, int length)
regionForDocument()
resource:XtextResource
ITextRegionAccess
regionForEObject(EObject object)
regionForRootEObject()
the document.	

created from node
model or via serializer
getText():String
offset : int
length: int
ITextRegion
regionForOffset(int offset, int length)
regionForDocument()
resource:XtextResource
ITextRegionAccess
regionForEObject(EObject object)
regionForRootEObject()
returns
regions for anything
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
whitespace token comment token
ISequentialRegion
getText():String
offset : int
length: int
ITextRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPartparts
IWhitespace IComment
regionForOffset(int offset, int length)
regionForDocument()
resource:XtextResource
ITextRegionAccess
regionForEObject(EObject object)
regionForRootEObject()
returns
eObject:EObject
grammarElement:EObject
IEObjectRegion ISequentialRegion
getText():String
offset : int
length: int
ITextRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
regionForOffset(int offset, int length)
regionForDocument()
resource:XtextResource
ITextRegionAccess
regionForEObject(EObject object)
regionForRootEObject()
returns
returns
leading
trailing
for AST element
eObject:EObject
grammarElement:EObject
IEObjectRegion
undefined:boolean
IHiddenRegion
grammarElement:EObject
ISemanticRegion nextprevious
previousnext
grammarElement:EObject
IHiddenRegionPart
children
parts
IWhitespace IComment
leading
trailing
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
1. Take Document
1.
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
1.
2.
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.
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.
/* 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
/* 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
/* 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
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
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
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
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
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
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
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 “;";
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("(", ")")
{
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
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
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)
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)
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)
TEXT REGION RECAP
TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
• linked list of strictly alternating ISemanticRegion and
IHiddenRegion
TEXT REGION RECAP
• ITextRegionAccess (for Xtend, ITextRegionExtensions)
• linked list of strictly alternating ISemanticRegion and
IHiddenRegion
• IHiddenRegion can be empty
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
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
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!
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)
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)
FORMATTABLE
DOCUMENT
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
API:
specify formatting information for hidden regions
Implementation:
store text replacers for text regions
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
API:
specify formatting information for hidden regions
Implementation:
store text replacers for text regions
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify
for one
or two
offset : int
length: int
IHiddenRegion
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify
for one
or two
offset : int
length: int
IHiddenRegion
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
class StatesFormatter extends AbstractFormatter2 {	
!
	 def dispatch void format(Model model, extension IFormattableDocument document) {	
	 	 model.regionFor.keyword(";").prepend[noSpace]	
	 }	
}
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify
for one
or two
offset : int
length: int
IHiddenRegion
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
space, newLines, indent, autowrap
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify
for one
or two
offset : int
length: int
IHiddenRegion
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
IHiddenRegionPart
IWhitespace IComment
1
*
remember the
strictly alternating
list of
hidden/semantic regions
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for provided hidden region	
	 def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init)	
!
	 // append after the provided semantic region	
	 def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init)	
	 def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init)	
!
	 // prepend before the provided semantic region	
	 def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init)	
	 def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for oneIHiddenRegionFormatter IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for provided hidden region	
	 def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init)	
!
	 // append after the provided semantic region	
	 def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init)	
	 def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init)	
!
	 // prepend before the provided semantic region	
	 def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init)	
	 def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for one
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
IHiddenRegionFormatter IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for provided hidden region	
	 def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init)	
!
	 // append after the provided semantic region	
	 def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init)	
	 def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init)	
!
	 // prepend before the provided semantic region	
	 def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init)	
	 def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for one
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
IHiddenRegionFormatter IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for provided hidden region	
	 def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init)	
!
	 // append after the provided semantic region	
	 def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init)	
	 def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init)	
!
	 // prepend before the provided semantic region	
	 def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init)	
	 def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for one
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
IHiddenRegionFormatter IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for provided hidden region	
	 def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init)	
!
	 // append after the provided semantic region	
	 def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init)	
	 def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init)	
!
	 // prepend before the provided semantic region	
	 def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init)	
	 def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for one
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
IHiddenRegionFormatter IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for provided hidden region	
	 def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init)	
!
	 // append after the provided semantic region	
	 def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init)	
	 def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init)	
!
	 // prepend before the provided semantic region	
	 def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init)	
	 def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for one
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
IHiddenRegionFormatter IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for both provided hidden regions	
	 def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init)	
!
	 // before and after provided semantic region	
	 def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init)	
	 def surround(EObject owner, (IHiddenRegionFormatter)=>void init)	
!
	 // set for two inwards-bound hidden regions	
	 def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init)	
	 def interior(EObject object, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for twoIHiddenRegionFormatter IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for both provided hidden regions	
	 def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init)	
!
	 // before and after provided semantic region	
	 def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init)	
	 def surround(EObject owner, (IHiddenRegionFormatter)=>void init)	
!
	 // set for two inwards-bound hidden regions	
	 def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init)	
	 def interior(EObject object, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for twoIHiddenRegionFormatter IHiddenRegion
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for both provided hidden regions	
	 def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init)	
!
	 // before and after provided semantic region	
	 def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init)	
	 def surround(EObject owner, (IHiddenRegionFormatter)=>void init)	
!
	 // set for two inwards-bound hidden regions	
	 def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init)	
	 def interior(EObject object, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for twoIHiddenRegionFormatter IHiddenRegion
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for both provided hidden regions	
	 def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init)	
!
	 // before and after provided semantic region	
	 def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init)	
	 def surround(EObject owner, (IHiddenRegionFormatter)=>void init)	
!
	 // set for two inwards-bound hidden regions	
	 def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init)	
	 def interior(EObject object, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for twoIHiddenRegionFormatter IHiddenRegion
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for both provided hidden regions	
	 def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init)	
!
	 // before and after provided semantic region	
	 def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init)	
	 def surround(EObject owner, (IHiddenRegionFormatter)=>void init)	
!
	 // set for two inwards-bound hidden regions	
	 def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init)	
	 def interior(EObject object, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for twoIHiddenRegionFormatter IHiddenRegion
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
	 // set for both provided hidden regions	
	 def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init)	
!
	 // before and after provided semantic region	
	 def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init)	
	 def surround(EObject owner, (IHiddenRegionFormatter)=>void init)	
!
	 // set for two inwards-bound hidden regions	
	 def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init)	
	 def interior(EObject object, (IHiddenRegionFormatter)=>void init)	
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
specify for twoIHiddenRegionFormatter IHiddenRegion
IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
Why specify formatting for two hidden regions at once?
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
Why specify formatting for two hidden regions at once?
- indent() spans from first to second hidden region!
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
Why specify formatting for two hidden regions at once?
- indent() spans from first to second hidden region!
- convenience
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
Why specify formatting for two hidden regions at once?
- indent() spans from first to second hidden region!
- convenience
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
Why specify formatting for two hidden regions at once?
- indent() spans from first to second hidden region!
- convenience
Why not increaseIndent() and decreaseIndent() individually?
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
Why specify formatting for two hidden regions at once?
- indent() spans from first to second hidden region!
- convenience
Why not increaseIndent() and decreaseIndent() individually?
- likely to not be applied symmetrically due to bugs, exceptions
----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------	
machine my.X;	
state<19| >foo<23|	
	 >actions 1, 2, 3;<43|	
>end	
!
--------------------------------------------------------------------------------	
19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1	
23 2 "n	 ": HiddenRegionReplacer: newLine=1	
43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.interior[indent]	
	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 state.regionFor.keyword("end").prepend[newLine]	
	 println(document.toString)	
}
State:	
	 {State} 'state' name=ID	
	 ('actions' actions+=INT ("," actions+=INT)* ";")?	
	 'end';
----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------	
machine my.X;	
state<19| >foo<23|	
	 >actions 1, 2, 3;<43|	
>end	
!
--------------------------------------------------------------------------------	
19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1	
23 2 "n	 ": HiddenRegionReplacer: newLine=1	
43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.interior[indent]	
	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 state.regionFor.keyword("end").prepend[newLine]	
	 println(document.toString)	
}
State:	
	 {State} 'state' name=ID	
	 ('actions' actions+=INT ("," actions+=INT)* ";")?	
	 'end';
----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------	
machine my.X;	
state<19| >foo<23|	
	 >actions 1, 2, 3;<43|	
>end	
!
--------------------------------------------------------------------------------	
19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1	
23 2 "n	 ": HiddenRegionReplacer: newLine=1	
43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.interior[indent]	
	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 state.regionFor.keyword("end").prepend[newLine]	
	 println(document.toString)	
}
State:	
	 {State} 'state' name=ID	
	 ('actions' actions+=INT ("," actions+=INT)* ";")?	
	 'end';
----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------	
machine my.X;	
state<19| >foo<23|	
	 >actions 1, 2, 3;<43|	
>end	
!
--------------------------------------------------------------------------------	
19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1	
23 2 "n	 ": HiddenRegionReplacer: newLine=1	
43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.interior[indent]	
	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 state.regionFor.keyword("end").prepend[newLine]	
	 println(document.toString)	
}
State:	
	 {State} 'state' name=ID	
	 ('actions' actions+=INT ("," actions+=INT)* ";")?	
	 'end';
----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------	
machine my.X;	
state<19| >foo<23|	
	 >actions 1, 2, 3;<43|	
>end	
!
--------------------------------------------------------------------------------	
19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1	
23 2 "n	 ": HiddenRegionReplacer: newLine=1	
43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.interior[indent]	
	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 state.regionFor.keyword("end").prepend[newLine]	
	 println(document.toString)	
}
State:	
	 {State} 'state' name=ID	
	 ('actions' actions+=INT ("," actions+=INT)* ";")?	
	 'end';
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
API:
specify formatting information for hidden regions
Implementation:
store text replacers for text regions
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
	 // ...	
}
offset : int
length: int
newText: String
ITextReplacement
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
( )*?
Recap: The formatter outputs text replacements!
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
HiddenRegionReplacer
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
configures
configures a
HiddenRegionReplacer
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
}
creates TextReplacements
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
configures
creates
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
}
specific for TextRegion
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
configures
offset : int
length: int
ITextRegion
1
1
creates
interface IFormattableDocument {	
!
!
!
!
!
!
!
!
!
!
!
!
	 	
!
!
!
!
!
!
!
!
!
!
!
!
!
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
autowrap()
setPriority(int priority)
indent()
newLine()
setNewLines(int newLines)
setNewLines(int min, int default, int max)
noSpace()
oneSpace()
setSpace(String space)
IHiddenRegionFormatter
configures
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
context collects
replacements
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
sorted
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
invoke in	

sorted order
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
in/out	

context
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
return	

replacements
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
create
replacement
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
add to 	

context
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
return cloned	

context
class FormattableDocument {	
!
	 Map<ITextRegion, ITextReplacer> replacers;	
!
	 def List<ITextReplacement> renderToTextReplacements() {	
	 	 val List<ITextReplacer> sorted = sortByOffset(replacers)	
	 	 var ITextReplacerContext context = createTextReplacerContext()	
	 	 for (ITextReplacer replacer : sorted) {	
	 	 	 context = replacer.createReplacements(context)	
	 	 }	
	 	 return context.getAllReplacements()	
	 }	
}	
!
!
!
!
!
!
class MyReplacer implements ITextReplacer {	
!
	 override ITextReplacerContext createReplacements(ITextReplacerContext context) {	
	 	 val ITextReplacement replacement = region.replaceWith(" ")	
	 	 context.addReplacement(replacement)	
	 	 val int oldIndentation = context.getIndentation()	
	 	 return context.withIndentation(oldIndentation + 1)	
	 }	
}
createReplacements(ITextReplacerContext context): ITextReplacerContext
ITextReplacer
offset : int
length: int
newText: String
ITextReplacementHiddenRegionReplacer
getIndentation(): int
withIndentation(int indent): ITextReplacerContext
getParent(): ITextReplacerContext
addReplacement(ITextReplacement repl)
getAllReplacements():List<ITextReplacement>
ITextReplacerContext
uses
offset : int
length: int
ITextRegion
1
1
creates
collects
EXAMPLES
state foo	
	 actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 	
	 10, 11, 12, 13; 	
end
AutoWrap
def dispatch void format(State state, extension IFormattableDocument document) {	
	 for (k : state.regionFor.keywords(“,")) {	
	 	 k.prepend[noSpace].append[oneSpace; autowrap]	
	 }	
}
State:	
	 'state' name=ID	
	 	 'actions' actions+=INT ("," actions+=INT)* ";"	
	 'end';
state foo	
	 actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 	
	 10, 11, 12, 13; 	
end
AutoWrap
def dispatch void format(State state, extension IFormattableDocument document) {	
	 for (k : state.regionFor.keywords(“,")) {	
	 	 k.prepend[noSpace].append[oneSpace; autowrap]	
	 }	
}
State:	
	 'state' name=ID	
	 	 'actions' actions+=INT ("," actions+=INT)* ";"	
	 'end';
def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 val IAutowrapFormatter af = [ region, wrapped, doc |	
	 	 	 val first = (region as IWhitespace).hiddenRegion	
	 	 	 val last = state.regionFor.keyword(";").previousHiddenRegion	
	 	 	 doc.set(first, last, [indent])	
	 	 ]	
	 	 for (k : state.regionFor.keywords(",")) {	
	 	 	 k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af]	
	 	 }	
}
AutoWrap with Handler
State:	
	 'state' name=ID	
	 	 'actions' actions+=INT ("," actions+=INT)* ";"	
	 'end';
state foo	
	 actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 	
	 	 10, 11, 12, 13; 	
end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 val IAutowrapFormatter af = [ region, wrapped, doc |	
	 	 	 val first = (region as IWhitespace).hiddenRegion	
	 	 	 val last = state.regionFor.keyword(";").previousHiddenRegion	
	 	 	 doc.set(first, last, [indent])	
	 	 ]	
	 	 for (k : state.regionFor.keywords(",")) {	
	 	 	 k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af]	
	 	 }	
}
AutoWrap with Handler
State:	
	 'state' name=ID	
	 	 'actions' actions+=INT ("," actions+=INT)* ";"	
	 'end';
state foo	
	 actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 	
	 	 10, 11, 12, 13; 	
end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 val IAutowrapFormatter af = [ region, wrapped, doc |	
	 	 	 val first = (region as IWhitespace).hiddenRegion	
	 	 	 val last = state.regionFor.keyword(";").previousHiddenRegion	
	 	 	 doc.set(first, last, [indent])	
	 	 ]	
	 	 for (k : state.regionFor.keywords(",")) {	
	 	 	 k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af]	
	 	 }	
}
AutoWrap with Handler
State:	
	 'state' name=ID	
	 	 'actions' actions+=INT ("," actions+=INT)* ";"	
	 'end';
state foo	
	 actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 	
	 	 10, 11, 12, 13; 	
end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 val IAutowrapFormatter af = [ region, wrapped, doc |	
	 	 	 val first = (region as IWhitespace).hiddenRegion	
	 	 	 val last = state.regionFor.keyword(";").previousHiddenRegion	
	 	 	 doc.set(first, last, [indent])	
	 	 ]	
	 	 for (k : state.regionFor.keywords(",")) {	
	 	 	 k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af]	
	 	 }	
}
AutoWrap with Handler
State:	
	 'state' name=ID	
	 	 'actions' actions+=INT ("," actions+=INT)* ";"	
	 'end';
state foo	
	 actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 	
	 	 10, 11, 12, 13; 	
end
Conditional Formatting state bar	
	 1 => foo;	
	 2 => foo;	
	 3 => foo;	
	 4 => foo;	
	 5 => foo;	
end
state bar 1 => foo; 2 => foo; 3 => foo; end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.formatConditionally(	
	 	 [ doc |	
	 	 	 val extension slDoc = doc.requireFitsInLine	
	 	 	 state.regionFor.feature(STATE__NAME).surround[oneSpace]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[oneSpace]	
	 	 	 }	
	 	 ],	
	 	 [ extension doc |	
	 	 	 state.interior[indent]	
	 	 	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[newLine]	
	 	 	 }	
	 	 ]	
	 )	
}
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
Conditional Formatting state bar	
	 1 => foo;	
	 2 => foo;	
	 3 => foo;	
	 4 => foo;	
	 5 => foo;	
end
state bar 1 => foo; 2 => foo; 3 => foo; end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.formatConditionally(	
	 	 [ doc |	
	 	 	 val extension slDoc = doc.requireFitsInLine	
	 	 	 state.regionFor.feature(STATE__NAME).surround[oneSpace]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[oneSpace]	
	 	 	 }	
	 	 ],	
	 	 [ extension doc |	
	 	 	 state.interior[indent]	
	 	 	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[newLine]	
	 	 	 }	
	 	 ]	
	 )	
}
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
XOR
Conditional Formatting state bar	
	 1 => foo;	
	 2 => foo;	
	 3 => foo;	
	 4 => foo;	
	 5 => foo;	
end
state bar 1 => foo; 2 => foo; 3 => foo; end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.formatConditionally(	
	 	 [ doc |	
	 	 	 val extension slDoc = doc.requireFitsInLine	
	 	 	 state.regionFor.feature(STATE__NAME).surround[oneSpace]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[oneSpace]	
	 	 	 }	
	 	 ],	
	 	 [ extension doc |	
	 	 	 state.interior[indent]	
	 	 	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[newLine]	
	 	 	 }	
	 	 ]	
	 )	
}
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
Conditional Formatting state bar	
	 1 => foo;	
	 2 => foo;	
	 3 => foo;	
	 4 => foo;	
	 5 => foo;	
end
state bar 1 => foo; 2 => foo; 3 => foo; end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.formatConditionally(	
	 	 [ doc |	
	 	 	 val extension slDoc = doc.requireFitsInLine	
	 	 	 state.regionFor.feature(STATE__NAME).surround[oneSpace]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[oneSpace]	
	 	 	 }	
	 	 ],	
	 	 [ extension doc |	
	 	 	 state.interior[indent]	
	 	 	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[newLine]	
	 	 	 }	
	 	 ]	
	 )	
}
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
Conditional Formatting state bar	
	 1 => foo;	
	 2 => foo;	
	 3 => foo;	
	 4 => foo;	
	 5 => foo;	
end
state bar 1 => foo; 2 => foo; 3 => foo; end
def dispatch void format(State state, extension IFormattableDocument document) {	
	 state.formatConditionally(	
	 	 [ doc |	
	 	 	 val extension slDoc = doc.requireFitsInLine	
	 	 	 state.regionFor.feature(STATE__NAME).surround[oneSpace]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[oneSpace]	
	 	 	 }	
	 	 ],	
	 	 [ extension doc |	
	 	 	 state.interior[indent]	
	 	 	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[newLine]	
	 	 	 }	
	 	 ]	
	 )	
}
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
Pattern-Aware formatting
	 def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 if (state.regionFor.keyword("end").previousHiddenRegion.isMultiline) {	
	 	 	 state.interior[indent]	
	 	 	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[newLine]	
	 	 	 }	
	 	 } else {	
	 	 	 state.regionFor.feature(STATE__NAME).surround[oneSpace]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[oneSpace]	
	 	 	 }	
	 	 }	
	 }
state bar	
	 1 => foo;	
end
state bar 1 => foo; end
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
Pattern-Aware formatting
	 def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 if (state.regionFor.keyword("end").previousHiddenRegion.isMultiline) {	
	 	 	 state.interior[indent]	
	 	 	 state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[newLine]	
	 	 	 }	
	 	 } else {	
	 	 	 state.regionFor.feature(STATE__NAME).surround[oneSpace]	
	 	 	 for (t : state.transitions) {	
	 	 	 	 t.format.append[oneSpace]	
	 	 	 }	
	 	 }	
	 }
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
state bar	
	 1 => foo;	
end
state bar 1 => foo; end
Table-based formatting
state baz	
	 1 => foo;	
	 12 => foo;	
	 123 => foo;	
end
	 def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 state.interior[indent]	
	 	 val width = state.transitions.map[regionFor.feature(TRANSITION__EVENT).length].max + 1;	
	 	 for (t : state.transitions) {	
	 	 	 val region = t.regionFor.feature(TRANSITION__EVENT)	
	 	 	 region.append[space = Strings.repeat(" ", width - region.length)]	
	 	 	 t.regionFor.keyword(";").prepend[noSpace].append[newLine]	
	 	 }	
	 }
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
Table-based formatting
state baz	
	 1 => foo;	
	 12 => foo;	
	 123 => foo;	
end
	 def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 state.interior[indent]	
	 	 val width = state.transitions.map[regionFor.feature(TRANSITION__EVENT).length].max + 1;	
	 	 for (t : state.transitions) {	
	 	 	 val region = t.regionFor.feature(TRANSITION__EVENT)	
	 	 	 region.append[space = Strings.repeat(" ", width - region.length)]	
	 	 	 t.regionFor.keyword(";").prepend[noSpace].append[newLine]	
	 	 }	
	 }
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
Table-based formatting
state baz	
	 1 => foo;	
	 12 => foo;	
	 123 => foo;	
end
	 def dispatch void format(State state, extension IFormattableDocument document) {	
	 	 state.interior[indent]	
	 	 val width = state.transitions.map[regionFor.feature(TRANSITION__EVENT).length].max + 1;	
	 	 for (t : state.transitions) {	
	 	 	 val region = t.regionFor.feature(TRANSITION__EVENT)	
	 	 	 region.append[space = Strings.repeat(" ", width - region.length)]	
	 	 	 t.regionFor.keyword(";").prepend[noSpace].append[newLine]	
	 	 }	
	 }
State:	
	 'state' name=ID	
	 transitions+=Transition*	
	 'end';
TESTS
org.eclipse.xtext.junit4.formatter.FormatterTester
@Test def void example1() {	
	 assertFormatted[	
	 	 toBeFormatted = '''	
	 	 	 entity Foo {	
	 	 	 	 propertyName:String	
	 	 	 	
	 	 	 	 op name() {	
	 	 	 	 }	
	 	 	 }	
	 	 '''	
	 ]	
} @Test def void example2() {	
	 assertFormatted[	
	 	 expectation = '''	
	 	 	 entity Foo {	
	 	 	 	 op foo():String {	
	 	 	 	 	 "xx"	
	 	 	 	 }	
	 	 	 }	
	 	 '''	
	 	 toBeFormatted = '''	
	 	 	 entity Foo { op foo ( ) : String { "xx" } }	
	 	 '''	
	 ]	
}
THE OLD FORMATTER
• no access to text, Node Model or AST
• not enhanceable engine
• will be deprecated
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
Questions?

More Related Content

What's hot

What's hot (20)

DTD
DTDDTD
DTD
 
Php operators
Php operatorsPhp operators
Php operators
 
Javascript validating form
Javascript validating formJavascript validating form
Javascript validating form
 
TypeScript: Basic Features and Compilation Guide
TypeScript: Basic Features and Compilation GuideTypeScript: Basic Features and Compilation Guide
TypeScript: Basic Features and Compilation Guide
 
Dom
DomDom
Dom
 
Javascript Basic
Javascript BasicJavascript Basic
Javascript Basic
 
Introduction To HTML
Introduction To HTMLIntroduction To HTML
Introduction To HTML
 
Typescript Fundamentals
Typescript FundamentalsTypescript Fundamentals
Typescript Fundamentals
 
Html ppt
Html pptHtml ppt
Html ppt
 
Javascript conditional statements
Javascript conditional statementsJavascript conditional statements
Javascript conditional statements
 
The New JavaScript: ES6
The New JavaScript: ES6The New JavaScript: ES6
The New JavaScript: ES6
 
XML, DTD & XSD Overview
XML, DTD & XSD OverviewXML, DTD & XSD Overview
XML, DTD & XSD Overview
 
File Uploading in PHP
File Uploading in PHPFile Uploading in PHP
File Uploading in PHP
 
Document Object Model
Document Object ModelDocument Object Model
Document Object Model
 
Web html table tags
Web html  table tagsWeb html  table tags
Web html table tags
 
JavaScript & Dom Manipulation
JavaScript & Dom ManipulationJavaScript & Dom Manipulation
JavaScript & Dom Manipulation
 
How to Make HTML and CSS Files
How to Make HTML and CSS FilesHow to Make HTML and CSS Files
How to Make HTML and CSS Files
 
Php forms
Php formsPhp forms
Php forms
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentation
 
Directives in asp.net
Directives in asp.netDirectives in asp.net
Directives in asp.net
 

Viewers also liked

Executable specifications for xtext
Executable specifications for xtextExecutable specifications for xtext
Executable specifications for xtext
meysholdt
 

Viewers also liked (20)

Future of Xtext
Future of XtextFuture of Xtext
Future of Xtext
 
Xtext Best Practices
Xtext Best PracticesXtext Best Practices
Xtext Best Practices
 
Extending the Xbase Typesystem
Extending the Xbase TypesystemExtending the Xbase Typesystem
Extending the Xbase Typesystem
 
Codegeneration Goodies
Codegeneration GoodiesCodegeneration Goodies
Codegeneration Goodies
 
Scoping
ScopingScoping
Scoping
 
Pragmatic DSL Design with Xtext, Xbase and Xtend 2
Pragmatic DSL Design with Xtext, Xbase and Xtend 2Pragmatic DSL Design with Xtext, Xbase and Xtend 2
Pragmatic DSL Design with Xtext, Xbase and Xtend 2
 
Language Engineering With Xtext
Language Engineering With XtextLanguage Engineering With Xtext
Language Engineering With Xtext
 
Building a Python IDE with Xtext
Building a Python IDE with XtextBuilding a Python IDE with Xtext
Building a Python IDE with Xtext
 
Scoping Tips and Tricks
Scoping Tips and TricksScoping Tips and Tricks
Scoping Tips and Tricks
 
Building Your Own DSL with Xtext
Building Your Own DSL with XtextBuilding Your Own DSL with Xtext
Building Your Own DSL with Xtext
 
Executable specifications for xtext
Executable specifications for xtextExecutable specifications for xtext
Executable specifications for xtext
 
Java DSLs with Xtext
Java DSLs with XtextJava DSLs with Xtext
Java DSLs with Xtext
 
Introduction to Xbase
Introduction to XbaseIntroduction to Xbase
Introduction to Xbase
 
Turning Ideas Into Code Faster
Turning Ideas Into Code FasterTurning Ideas Into Code Faster
Turning Ideas Into Code Faster
 
Textual Modeling Framework Xtext
Textual Modeling Framework XtextTextual Modeling Framework Xtext
Textual Modeling Framework Xtext
 
Parsing Expression With Xtext
Parsing Expression With XtextParsing Expression With Xtext
Parsing Expression With Xtext
 
Xbase - Implementing Domain-Specific Languages for Java
Xbase - Implementing Domain-Specific Languages for JavaXbase - Implementing Domain-Specific Languages for Java
Xbase - Implementing Domain-Specific Languages for Java
 
Code Generation With Xtend
Code Generation With XtendCode Generation With Xtend
Code Generation With Xtend
 
Responsiveness
ResponsivenessResponsiveness
Responsiveness
 
XRobots
XRobotsXRobots
XRobots
 

Similar to Xtext's new Formatter API

Csharp4 strings and_regular_expressions
Csharp4 strings and_regular_expressionsCsharp4 strings and_regular_expressions
Csharp4 strings and_regular_expressions
Abed Bukhari
 
C,c++ interview q&a
C,c++ interview q&aC,c++ interview q&a
C,c++ interview q&a
Kumaran K
 
An Overview Of Python With Functional Programming
An Overview Of Python With Functional ProgrammingAn Overview Of Python With Functional Programming
An Overview Of Python With Functional Programming
Adam Getchell
 
TI1220 Lecture 14: Domain-Specific Languages
TI1220 Lecture 14: Domain-Specific LanguagesTI1220 Lecture 14: Domain-Specific Languages
TI1220 Lecture 14: Domain-Specific Languages
Eelco Visser
 

Similar to Xtext's new Formatter API (20)

Hadoop MapReduce framework - Module 3
Hadoop MapReduce framework - Module 3Hadoop MapReduce framework - Module 3
Hadoop MapReduce framework - Module 3
 
Csharp4 strings and_regular_expressions
Csharp4 strings and_regular_expressionsCsharp4 strings and_regular_expressions
Csharp4 strings and_regular_expressions
 
Modern C++
Modern C++Modern C++
Modern C++
 
Compass Framework
Compass FrameworkCompass Framework
Compass Framework
 
Unit v
Unit vUnit v
Unit v
 
Opensource gis development - part 5
Opensource gis development - part 5Opensource gis development - part 5
Opensource gis development - part 5
 
Spark with Elasticsearch
Spark with ElasticsearchSpark with Elasticsearch
Spark with Elasticsearch
 
Metaprogramming
MetaprogrammingMetaprogramming
Metaprogramming
 
Model-Driven Software Development - Language Workbenches & Syntax Definition
Model-Driven Software Development - Language Workbenches & Syntax DefinitionModel-Driven Software Development - Language Workbenches & Syntax Definition
Model-Driven Software Development - Language Workbenches & Syntax Definition
 
Patterns in Python
Patterns in PythonPatterns in Python
Patterns in Python
 
Choose'10: Ralf Laemmel - Dealing Confortably with the Confusion of Tongues
Choose'10: Ralf Laemmel - Dealing Confortably with the Confusion of TonguesChoose'10: Ralf Laemmel - Dealing Confortably with the Confusion of Tongues
Choose'10: Ralf Laemmel - Dealing Confortably with the Confusion of Tongues
 
Intro to Core Data
Intro to Core DataIntro to Core Data
Intro to Core Data
 
C,c++ interview q&a
C,c++ interview q&aC,c++ interview q&a
C,c++ interview q&a
 
WEB DEVELOPMENT
WEB DEVELOPMENTWEB DEVELOPMENT
WEB DEVELOPMENT
 
Allura - an Open Source MongoDB Based Document Oriented SourceForge
Allura - an Open Source MongoDB Based Document Oriented SourceForgeAllura - an Open Source MongoDB Based Document Oriented SourceForge
Allura - an Open Source MongoDB Based Document Oriented SourceForge
 
xml2tex at TUG 2014
xml2tex at TUG 2014xml2tex at TUG 2014
xml2tex at TUG 2014
 
An Overview Of Python With Functional Programming
An Overview Of Python With Functional ProgrammingAn Overview Of Python With Functional Programming
An Overview Of Python With Functional Programming
 
Latex workshop: Essentials and Practices
Latex workshop: Essentials and PracticesLatex workshop: Essentials and Practices
Latex workshop: Essentials and Practices
 
Echtzeitapplikationen mit Elixir und GraphQL
Echtzeitapplikationen mit Elixir und GraphQLEchtzeitapplikationen mit Elixir und GraphQL
Echtzeitapplikationen mit Elixir und GraphQL
 
TI1220 Lecture 14: Domain-Specific Languages
TI1220 Lecture 14: Domain-Specific LanguagesTI1220 Lecture 14: Domain-Specific Languages
TI1220 Lecture 14: Domain-Specific Languages
 

Xtext's new Formatter API

  • 1. Moritz Eysholdt, itemis AG XTEXT’S NEW FORMATTER API
  • 2. AGENDA • motivation • use cases • ITextRegionAccess • IFormattableDocument • example formatter • testing
  • 4. DEFINITION “improve readability and emphasize structure of a document without changing its meaning”
  • 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
  • 8. I’m lazy readability diff & merge bug prevention MOTIVATION
  • 9. I’m lazy readability diff & merge bug prevention With Formatter: CTRL+SHIFT+F MOTIVATION
  • 10. I’m lazy readability diff & merge bug prevention With Formatter: CTRL+SHIFT+F Without Formatter: →→ __↓ →→ __↓ ¶ ↑↑ ↓↓ ¶ MOTIVATION
  • 11. I’m lazy readability diff & merge bug prevention MOTIVATION
  • 12. bad formatting… - obscures the code’s structure - unnecessarily consumes brain cycles to filter out anomalies I’m lazy readability diff & merge bug prevention MOTIVATION
  • 13. I’m lazy readability diff & merge bug prevention MOTIVATION
  • 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
  • 17. I’m lazy readability diff & merge bug prevention MOTIVATION
  • 18. I’m lazy readability diff & merge bug prevention MOTIVATION
  • 19. I’m lazy readability diff & merge bug prevention MOTIVATION
  • 20. 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
  • 26. on demand serialization semantic quickfix Use an Xtext grammar to convert an EMF model to text. Formatter contributes indentation, newlines, space, etc. USE CASES
  • 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])); } }); }
  • 35. fragment = formatting2.Formatter2Fragment auto-inject {} fragment = formatting.FormatterFragment auto-inject {} GeneratorFragment for MWE2
  • 36. 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 “;";
  • 37. 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 “;"; extends AbstractFormatter2
  • 38. 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 “;"; dispatch over AST elements
  • 39. 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 Model: "machine" name=QualifiedName “;";
  • 40. 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 Model: "machine" name=QualifiedName “;";
  • 41. 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 “;";
  • 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
  • 47. whitespace token comment token ISequentialRegion getText():String offset : int length: int ITextRegion undefined:boolean IHiddenRegion grammarElement:EObject ISemanticRegion nextprevious previousnext grammarElement:EObject IHiddenRegionPartparts IWhitespace IComment regionForOffset(int offset, int length) regionForDocument() resource:XtextResource ITextRegionAccess regionForEObject(EObject object) regionForRootEObject() returns
  • 48. eObject:EObject grammarElement:EObject IEObjectRegion ISequentialRegion getText():String offset : int length: int ITextRegion undefined:boolean IHiddenRegion grammarElement:EObject ISemanticRegion nextprevious previousnext grammarElement:EObject IHiddenRegionPart children parts IWhitespace IComment regionForOffset(int offset, int length) regionForDocument() resource:XtextResource ITextRegionAccess regionForEObject(EObject object) regionForRootEObject() returns returns leading trailing for AST element
  • 51. 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 1. 2.
  • 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)
  • 71. TEXT REGION RECAP • ITextRegionAccess (for Xtend, ITextRegionExtensions)
  • 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)
  • 80. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } API: specify formatting information for hidden regions Implementation: store text replacers for text regions
  • 81. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } API: specify formatting information for hidden regions Implementation: store text replacers for text regions
  • 82. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one or two offset : int length: int IHiddenRegion autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter
  • 83. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one or two offset : int length: int IHiddenRegion autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter class StatesFormatter extends AbstractFormatter2 { ! def dispatch void format(Model model, extension IFormattableDocument document) { model.regionFor.keyword(";").prepend[noSpace] } }
  • 84. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one or two offset : int length: int IHiddenRegion autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter space, newLines, indent, autowrap
  • 85. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one or two offset : int length: int IHiddenRegion autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion IHiddenRegionPart IWhitespace IComment 1 * remember the strictly alternating list of hidden/semantic regions
  • 86. interface IFormattableDocument { ! ! ! ! ! ! ! // set for provided hidden region def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init) ! // append after the provided semantic region def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init) def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init) ! // prepend before the provided semantic region def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init) def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for oneIHiddenRegionFormatter IHiddenRegion
  • 87. interface IFormattableDocument { ! ! ! ! ! ! ! // set for provided hidden region def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init) ! // append after the provided semantic region def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init) def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init) ! // prepend before the provided semantic region def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init) def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion IHiddenRegionFormatter IHiddenRegion
  • 88. interface IFormattableDocument { ! ! ! ! ! ! ! // set for provided hidden region def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init) ! // append after the provided semantic region def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init) def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init) ! // prepend before the provided semantic region def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init) def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion IHiddenRegionFormatter IHiddenRegion
  • 89. interface IFormattableDocument { ! ! ! ! ! ! ! // set for provided hidden region def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init) ! // append after the provided semantic region def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init) def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init) ! // prepend before the provided semantic region def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init) def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion IHiddenRegionFormatter IHiddenRegion
  • 90. interface IFormattableDocument { ! ! ! ! ! ! ! // set for provided hidden region def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init) ! // append after the provided semantic region def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init) def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init) ! // prepend before the provided semantic region def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init) def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion IHiddenRegionFormatter IHiddenRegion
  • 91. interface IFormattableDocument { ! ! ! ! ! ! ! // set for provided hidden region def set(IHiddenRegion hiddenRegion, (IHiddenRegionFormatter)=>void init) ! // append after the provided semantic region def append(ISemanticRegion appendAfter, (IHiddenRegionFormatter)=>void init) def append(EObject appendAfter, (IHiddenRegionFormatter)=>void init) ! // prepend before the provided semantic region def prepend(ISemanticRegion prependBefore, (IHiddenRegionFormatter)=>void init) def prepend(EObject prependBefore, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for one IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion IHiddenRegionFormatter IHiddenRegion
  • 92. interface IFormattableDocument { ! ! ! ! ! ! ! // set for both provided hidden regions def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init) ! // before and after provided semantic region def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init) def surround(EObject owner, (IHiddenRegionFormatter)=>void init) ! // set for two inwards-bound hidden regions def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init) def interior(EObject object, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for twoIHiddenRegionFormatter IHiddenRegion
  • 93. interface IFormattableDocument { ! ! ! ! ! ! ! // set for both provided hidden regions def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init) ! // before and after provided semantic region def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init) def surround(EObject owner, (IHiddenRegionFormatter)=>void init) ! // set for two inwards-bound hidden regions def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init) def interior(EObject object, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for twoIHiddenRegionFormatter IHiddenRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
  • 94. interface IFormattableDocument { ! ! ! ! ! ! ! // set for both provided hidden regions def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init) ! // before and after provided semantic region def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init) def surround(EObject owner, (IHiddenRegionFormatter)=>void init) ! // set for two inwards-bound hidden regions def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init) def interior(EObject object, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for twoIHiddenRegionFormatter IHiddenRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
  • 95. interface IFormattableDocument { ! ! ! ! ! ! ! // set for both provided hidden regions def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init) ! // before and after provided semantic region def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init) def surround(EObject owner, (IHiddenRegionFormatter)=>void init) ! // set for two inwards-bound hidden regions def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init) def interior(EObject object, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for twoIHiddenRegionFormatter IHiddenRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
  • 96. interface IFormattableDocument { ! ! ! ! ! ! ! // set for both provided hidden regions def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init) ! // before and after provided semantic region def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init) def surround(EObject owner, (IHiddenRegionFormatter)=>void init) ! // set for two inwards-bound hidden regions def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init) def interior(EObject object, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for twoIHiddenRegionFormatter IHiddenRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
  • 97. interface IFormattableDocument { ! ! ! ! ! ! ! // set for both provided hidden regions def set(IHiddenRegion r1, IHiddenRegion r2, (IHiddenRegionFormatter)=>void init) ! // before and after provided semantic region def surround(ISemanticRegion semanticRegion, (IHiddenRegionFormatter)=>void init) def surround(EObject owner, (IHiddenRegionFormatter)=>void init) ! // set for two inwards-bound hidden regions def interior(ISemanticRegion r1, ISemanticRegion r2, (IHiddenRegionFormatter)=>void init) def interior(EObject object, (IHiddenRegionFormatter)=>void init) ! ! ! ! ! ! ! ! ! ! ! // ... } specify for twoIHiddenRegionFormatter IHiddenRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion
  • 98. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... }
  • 99. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } Why specify formatting for two hidden regions at once?
  • 100. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } Why specify formatting for two hidden regions at once? - indent() spans from first to second hidden region!
  • 101. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } Why specify formatting for two hidden regions at once? - indent() spans from first to second hidden region! - convenience
  • 102. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } Why specify formatting for two hidden regions at once? - indent() spans from first to second hidden region! - convenience
  • 103. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } Why specify formatting for two hidden regions at once? - indent() spans from first to second hidden region! - convenience Why not increaseIndent() and decreaseIndent() individually?
  • 104. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } Why specify formatting for two hidden regions at once? - indent() spans from first to second hidden region! - convenience Why not increaseIndent() and decreaseIndent() individually? - likely to not be applied symmetrically due to bugs, exceptions
  • 105. ----------- RootDocument with ITextReplacers (syntax: <offset|text>) ----------- machine my.X; state<19| >foo<23| >actions 1, 2, 3;<43| >end ! -------------------------------------------------------------------------------- 19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1 23 2 "n ": HiddenRegionReplacer: newLine=1 43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1 def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] state.regionFor.keyword("end").prepend[newLine] println(document.toString) } State: {State} 'state' name=ID ('actions' actions+=INT ("," actions+=INT)* ";")? 'end';
  • 106. ----------- RootDocument with ITextReplacers (syntax: <offset|text>) ----------- machine my.X; state<19| >foo<23| >actions 1, 2, 3;<43| >end ! -------------------------------------------------------------------------------- 19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1 23 2 "n ": HiddenRegionReplacer: newLine=1 43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1 def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] state.regionFor.keyword("end").prepend[newLine] println(document.toString) } State: {State} 'state' name=ID ('actions' actions+=INT ("," actions+=INT)* ";")? 'end';
  • 107. ----------- RootDocument with ITextReplacers (syntax: <offset|text>) ----------- machine my.X; state<19| >foo<23| >actions 1, 2, 3;<43| >end ! -------------------------------------------------------------------------------- 19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1 23 2 "n ": HiddenRegionReplacer: newLine=1 43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1 def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] state.regionFor.keyword("end").prepend[newLine] println(document.toString) } State: {State} 'state' name=ID ('actions' actions+=INT ("," actions+=INT)* ";")? 'end';
  • 108. ----------- RootDocument with ITextReplacers (syntax: <offset|text>) ----------- machine my.X; state<19| >foo<23| >actions 1, 2, 3;<43| >end ! -------------------------------------------------------------------------------- 19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1 23 2 "n ": HiddenRegionReplacer: newLine=1 43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1 def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] state.regionFor.keyword("end").prepend[newLine] println(document.toString) } State: {State} 'state' name=ID ('actions' actions+=INT ("," actions+=INT)* ";")? 'end';
  • 109. ----------- RootDocument with ITextReplacers (syntax: <offset|text>) ----------- machine my.X; state<19| >foo<23| >actions 1, 2, 3;<43| >end ! -------------------------------------------------------------------------------- 19 1 " ": HiddenRegionReplacer: space=' ';indentInc=1 23 2 "n ": HiddenRegionReplacer: newLine=1 43 1 "n": HiddenRegionReplacer: newLine=1;indentDec=1 def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] state.regionFor.keyword("end").prepend[newLine] println(document.toString) } State: {State} 'state' name=ID ('actions' actions+=INT ("," actions+=INT)* ";")? 'end';
  • 110. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } API: specify formatting information for hidden regions Implementation: store text replacers for text regions
  • 111. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! // ... } offset : int length: int newText: String ITextReplacement autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter ( )*? Recap: The formatter outputs text replacements!
  • 112. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer HiddenRegionReplacer autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter configures configures a HiddenRegionReplacer
  • 113. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! } creates TextReplacements createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter configures creates
  • 114. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! } specific for TextRegion createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter configures offset : int length: int ITextRegion 1 1 creates
  • 115. interface IFormattableDocument { ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer autowrap() setPriority(int priority) indent() newLine() setNewLines(int newLines) setNewLines(int min, int default, int max) noSpace() oneSpace() setSpace(String space) IHiddenRegionFormatter configures getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects context collects replacements
  • 116. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects
  • 117. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects sorted
  • 118. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects invoke in sorted order
  • 119. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects in/out context
  • 120. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects return replacements
  • 121. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects create replacement
  • 122. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects add to context
  • 123. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects return cloned context
  • 124. class FormattableDocument { ! Map<ITextRegion, ITextReplacer> replacers; ! def List<ITextReplacement> renderToTextReplacements() { val List<ITextReplacer> sorted = sortByOffset(replacers) var ITextReplacerContext context = createTextReplacerContext() for (ITextReplacer replacer : sorted) { context = replacer.createReplacements(context) } return context.getAllReplacements() } } ! ! ! ! ! ! class MyReplacer implements ITextReplacer { ! override ITextReplacerContext createReplacements(ITextReplacerContext context) { val ITextReplacement replacement = region.replaceWith(" ") context.addReplacement(replacement) val int oldIndentation = context.getIndentation() return context.withIndentation(oldIndentation + 1) } } createReplacements(ITextReplacerContext context): ITextReplacerContext ITextReplacer offset : int length: int newText: String ITextReplacementHiddenRegionReplacer getIndentation(): int withIndentation(int indent): ITextReplacerContext getParent(): ITextReplacerContext addReplacement(ITextReplacement repl) getAllReplacements():List<ITextReplacement> ITextReplacerContext uses offset : int length: int ITextRegion 1 1 creates collects
  • 126. state foo actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; end AutoWrap def dispatch void format(State state, extension IFormattableDocument document) { for (k : state.regionFor.keywords(“,")) { k.prepend[noSpace].append[oneSpace; autowrap] } } State: 'state' name=ID 'actions' actions+=INT ("," actions+=INT)* ";" 'end';
  • 127. state foo actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; end AutoWrap def dispatch void format(State state, extension IFormattableDocument document) { for (k : state.regionFor.keywords(“,")) { k.prepend[noSpace].append[oneSpace; autowrap] } } State: 'state' name=ID 'actions' actions+=INT ("," actions+=INT)* ";" 'end';
  • 128. def dispatch void format(State state, extension IFormattableDocument document) { val IAutowrapFormatter af = [ region, wrapped, doc | val first = (region as IWhitespace).hiddenRegion val last = state.regionFor.keyword(";").previousHiddenRegion doc.set(first, last, [indent]) ] for (k : state.regionFor.keywords(",")) { k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af] } } AutoWrap with Handler State: 'state' name=ID 'actions' actions+=INT ("," actions+=INT)* ";" 'end'; state foo actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; end
  • 129. def dispatch void format(State state, extension IFormattableDocument document) { val IAutowrapFormatter af = [ region, wrapped, doc | val first = (region as IWhitespace).hiddenRegion val last = state.regionFor.keyword(";").previousHiddenRegion doc.set(first, last, [indent]) ] for (k : state.regionFor.keywords(",")) { k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af] } } AutoWrap with Handler State: 'state' name=ID 'actions' actions+=INT ("," actions+=INT)* ";" 'end'; state foo actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; end
  • 130. def dispatch void format(State state, extension IFormattableDocument document) { val IAutowrapFormatter af = [ region, wrapped, doc | val first = (region as IWhitespace).hiddenRegion val last = state.regionFor.keyword(";").previousHiddenRegion doc.set(first, last, [indent]) ] for (k : state.regionFor.keywords(",")) { k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af] } } AutoWrap with Handler State: 'state' name=ID 'actions' actions+=INT ("," actions+=INT)* ";" 'end'; state foo actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; end
  • 131. def dispatch void format(State state, extension IFormattableDocument document) { val IAutowrapFormatter af = [ region, wrapped, doc | val first = (region as IWhitespace).hiddenRegion val last = state.regionFor.keyword(";").previousHiddenRegion doc.set(first, last, [indent]) ] for (k : state.regionFor.keywords(",")) { k.prepend[noSpace].append[oneSpace; autowrap; onAutowrap = af] } } AutoWrap with Handler State: 'state' name=ID 'actions' actions+=INT ("," actions+=INT)* ";" 'end'; state foo actions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; end
  • 132. Conditional Formatting state bar 1 => foo; 2 => foo; 3 => foo; 4 => foo; 5 => foo; end state bar 1 => foo; 2 => foo; 3 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.formatConditionally( [ doc | val extension slDoc = doc.requireFitsInLine state.regionFor.feature(STATE__NAME).surround[oneSpace] for (t : state.transitions) { t.format.append[oneSpace] } ], [ extension doc | state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] for (t : state.transitions) { t.format.append[newLine] } ] ) } State: 'state' name=ID transitions+=Transition* 'end';
  • 133. Conditional Formatting state bar 1 => foo; 2 => foo; 3 => foo; 4 => foo; 5 => foo; end state bar 1 => foo; 2 => foo; 3 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.formatConditionally( [ doc | val extension slDoc = doc.requireFitsInLine state.regionFor.feature(STATE__NAME).surround[oneSpace] for (t : state.transitions) { t.format.append[oneSpace] } ], [ extension doc | state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] for (t : state.transitions) { t.format.append[newLine] } ] ) } State: 'state' name=ID transitions+=Transition* 'end'; XOR
  • 134. Conditional Formatting state bar 1 => foo; 2 => foo; 3 => foo; 4 => foo; 5 => foo; end state bar 1 => foo; 2 => foo; 3 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.formatConditionally( [ doc | val extension slDoc = doc.requireFitsInLine state.regionFor.feature(STATE__NAME).surround[oneSpace] for (t : state.transitions) { t.format.append[oneSpace] } ], [ extension doc | state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] for (t : state.transitions) { t.format.append[newLine] } ] ) } State: 'state' name=ID transitions+=Transition* 'end';
  • 135. Conditional Formatting state bar 1 => foo; 2 => foo; 3 => foo; 4 => foo; 5 => foo; end state bar 1 => foo; 2 => foo; 3 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.formatConditionally( [ doc | val extension slDoc = doc.requireFitsInLine state.regionFor.feature(STATE__NAME).surround[oneSpace] for (t : state.transitions) { t.format.append[oneSpace] } ], [ extension doc | state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] for (t : state.transitions) { t.format.append[newLine] } ] ) } State: 'state' name=ID transitions+=Transition* 'end';
  • 136. Conditional Formatting state bar 1 => foo; 2 => foo; 3 => foo; 4 => foo; 5 => foo; end state bar 1 => foo; 2 => foo; 3 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.formatConditionally( [ doc | val extension slDoc = doc.requireFitsInLine state.regionFor.feature(STATE__NAME).surround[oneSpace] for (t : state.transitions) { t.format.append[oneSpace] } ], [ extension doc | state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] for (t : state.transitions) { t.format.append[newLine] } ] ) } State: 'state' name=ID transitions+=Transition* 'end';
  • 137. Pattern-Aware formatting def dispatch void format(State state, extension IFormattableDocument document) { if (state.regionFor.keyword("end").previousHiddenRegion.isMultiline) { state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] for (t : state.transitions) { t.format.append[newLine] } } else { state.regionFor.feature(STATE__NAME).surround[oneSpace] for (t : state.transitions) { t.format.append[oneSpace] } } } state bar 1 => foo; end state bar 1 => foo; end State: 'state' name=ID transitions+=Transition* 'end';
  • 138. Pattern-Aware formatting def dispatch void format(State state, extension IFormattableDocument document) { if (state.regionFor.keyword("end").previousHiddenRegion.isMultiline) { state.interior[indent] state.regionFor.feature(STATE__NAME).prepend[oneSpace].append[newLine] for (t : state.transitions) { t.format.append[newLine] } } else { state.regionFor.feature(STATE__NAME).surround[oneSpace] for (t : state.transitions) { t.format.append[oneSpace] } } } State: 'state' name=ID transitions+=Transition* 'end'; state bar 1 => foo; end state bar 1 => foo; end
  • 139. Table-based formatting state baz 1 => foo; 12 => foo; 123 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] val width = state.transitions.map[regionFor.feature(TRANSITION__EVENT).length].max + 1; for (t : state.transitions) { val region = t.regionFor.feature(TRANSITION__EVENT) region.append[space = Strings.repeat(" ", width - region.length)] t.regionFor.keyword(";").prepend[noSpace].append[newLine] } } State: 'state' name=ID transitions+=Transition* 'end';
  • 140. Table-based formatting state baz 1 => foo; 12 => foo; 123 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] val width = state.transitions.map[regionFor.feature(TRANSITION__EVENT).length].max + 1; for (t : state.transitions) { val region = t.regionFor.feature(TRANSITION__EVENT) region.append[space = Strings.repeat(" ", width - region.length)] t.regionFor.keyword(";").prepend[noSpace].append[newLine] } } State: 'state' name=ID transitions+=Transition* 'end';
  • 141. Table-based formatting state baz 1 => foo; 12 => foo; 123 => foo; end def dispatch void format(State state, extension IFormattableDocument document) { state.interior[indent] val width = state.transitions.map[regionFor.feature(TRANSITION__EVENT).length].max + 1; for (t : state.transitions) { val region = t.regionFor.feature(TRANSITION__EVENT) region.append[space = Strings.repeat(" ", width - region.length)] t.regionFor.keyword(";").prepend[noSpace].append[newLine] } } State: 'state' name=ID transitions+=Transition* 'end';
  • 142. TESTS
  • 143. org.eclipse.xtext.junit4.formatter.FormatterTester @Test def void example1() { assertFormatted[ toBeFormatted = ''' entity Foo { propertyName:String op name() { } } ''' ] } @Test def void example2() { assertFormatted[ expectation = ''' entity Foo { op foo():String { "xx" } } ''' toBeFormatted = ''' entity Foo { op foo ( ) : String { "xx" } } ''' ] }
  • 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