Xtext's new Formatter API

M
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?
1 von 146

Recomendados

Lightweight Xtext Editors as SWT Widgets von
Lightweight Xtext Editors as SWT WidgetsLightweight Xtext Editors as SWT Widgets
Lightweight Xtext Editors as SWT Widgetsmeysholdt
3.7K views43 Folien
Serializing EMF models with Xtext von
Serializing EMF models with XtextSerializing EMF models with Xtext
Serializing EMF models with Xtextmeysholdt
5.6K views34 Folien
Deep dive into Xtext scoping local and global scopes explained von
Deep dive into Xtext scoping local and global scopes explainedDeep dive into Xtext scoping local and global scopes explained
Deep dive into Xtext scoping local and global scopes explainedHolger Schill
3.4K views74 Folien
Using Xcore with Xtext von
Using Xcore with XtextUsing Xcore with Xtext
Using Xcore with XtextHolger Schill
4.5K views79 Folien
Xtext beyond the defaults - how to tackle performance problems von
Xtext beyond the defaults -  how to tackle performance problemsXtext beyond the defaults -  how to tackle performance problems
Xtext beyond the defaults - how to tackle performance problemsHolger Schill
2.4K views130 Folien
Javascript Basic von
Javascript BasicJavascript Basic
Javascript BasicKang-min Liu
1.8K views36 Folien

Más contenido relacionado

Was ist angesagt?

Java 8 features von
Java 8 featuresJava 8 features
Java 8 featuresMaýur Chourasiya
194 views42 Folien
The Xtext Grammar Language von
The Xtext Grammar LanguageThe Xtext Grammar Language
The Xtext Grammar LanguageDr. Jan Köhnlein
5K views32 Folien
Extending the Xbase Typesystem von
Extending the Xbase TypesystemExtending the Xbase Typesystem
Extending the Xbase TypesystemSebastian Zarnekow
2.2K views33 Folien
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.] von
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]Jaeman An
547 views61 Folien
C# Basics von
C# BasicsC# Basics
C# BasicsSunil OS
481.5K views43 Folien
OOP in C++ von
OOP in C++OOP in C++
OOP in C++ppd1961
6.5K views194 Folien

Was ist angesagt?(20)

Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.] von Jaeman An
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
Jaeman An547 views
C# Basics von Sunil OS
C# BasicsC# Basics
C# Basics
Sunil OS481.5K views
OOP in C++ von ppd1961
OOP in C++OOP in C++
OOP in C++
ppd19616.5K views
Php7 extensions workshop von julien pauli
Php7 extensions workshopPhp7 extensions workshop
Php7 extensions workshop
julien pauli3.8K views
Building blocks of Angular von Knoldus Inc.
Building blocks of AngularBuilding blocks of Angular
Building blocks of Angular
Knoldus Inc.554 views
Test-Driven Development of Xtext DSLs von meysholdt
Test-Driven Development  of Xtext DSLsTest-Driven Development  of Xtext DSLs
Test-Driven Development of Xtext DSLs
meysholdt5.1K views
Idiomatic Kotlin von intelliyole
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlin
intelliyole7.4K views
Php tutorial(w3schools) von Arjun Shanka
Php tutorial(w3schools)Php tutorial(w3schools)
Php tutorial(w3schools)
Arjun Shanka92.6K views
C# conventions & good practices von Tan Tran
C# conventions & good practicesC# conventions & good practices
C# conventions & good practices
Tan Tran15.9K views
Array in c language von home
Array in c languageArray in c language
Array in c language
home42K views
OPcache の最適化器の今 von y-uti
OPcache の最適化器の今OPcache の最適化器の今
OPcache の最適化器の今
y-uti10.2K views
7 rules of simple and maintainable code von Geshan Manandhar
7 rules of simple and maintainable code7 rules of simple and maintainable code
7 rules of simple and maintainable code
Geshan Manandhar2.7K views

Destacado

Future of Xtext von
Future of XtextFuture of Xtext
Future of XtextSven Efftinge
5.2K views28 Folien
Xtext Best Practices von
Xtext Best PracticesXtext Best Practices
Xtext Best PracticesSebastian Zarnekow
6.3K views54 Folien
Codegeneration Goodies von
Codegeneration GoodiesCodegeneration Goodies
Codegeneration Goodiesmeysholdt
2.2K views78 Folien
Scoping von
ScopingScoping
ScopingDr. Jan Köhnlein
2.1K views69 Folien
Pragmatic DSL Design with Xtext, Xbase and Xtend 2 von
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 2Dr. Jan Köhnlein
6.5K views92 Folien
Language Engineering With Xtext von
Language Engineering With XtextLanguage Engineering With Xtext
Language Engineering With XtextSven Efftinge
968 views19 Folien

Destacado(20)

Codegeneration Goodies von meysholdt
Codegeneration GoodiesCodegeneration Goodies
Codegeneration Goodies
meysholdt2.2K views
Pragmatic DSL Design with Xtext, Xbase and Xtend 2 von Dr. Jan Köhnlein
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
Dr. Jan Köhnlein6.5K views
Language Engineering With Xtext von Sven Efftinge
Language Engineering With XtextLanguage Engineering With Xtext
Language Engineering With Xtext
Sven Efftinge968 views
Executable specifications for xtext von meysholdt
Executable specifications for xtextExecutable specifications for xtext
Executable specifications for xtext
meysholdt12.8K views
Turning Ideas Into Code Faster von meysholdt
Turning Ideas Into Code FasterTurning Ideas Into Code Faster
Turning Ideas Into Code Faster
meysholdt1.4K views
Parsing Expression With Xtext von Sven Efftinge
Parsing Expression With XtextParsing Expression With Xtext
Parsing Expression With Xtext
Sven Efftinge10K views
Xbase - Implementing Domain-Specific Languages for Java von meysholdt
Xbase - Implementing Domain-Specific Languages for JavaXbase - Implementing Domain-Specific Languages for Java
Xbase - Implementing Domain-Specific Languages for Java
meysholdt4.4K views

Similar a Xtext's new Formatter API

Hadoop MapReduce framework - Module 3 von
Hadoop MapReduce framework - Module 3Hadoop MapReduce framework - Module 3
Hadoop MapReduce framework - Module 3Rohit Agrawal
459 views24 Folien
Csharp4 strings and_regular_expressions von
Csharp4 strings and_regular_expressionsCsharp4 strings and_regular_expressions
Csharp4 strings and_regular_expressionsAbed Bukhari
441 views20 Folien
Modern C++ von
Modern C++Modern C++
Modern C++Richard Thomson
1.7K views25 Folien
Compass Framework von
Compass FrameworkCompass Framework
Compass FrameworkLukas Vlcek
813 views10 Folien
Unit v von
Unit vUnit v
Unit vsnehaarao19
311 views35 Folien
Opensource gis development - part 5 von
Opensource gis development - part 5Opensource gis development - part 5
Opensource gis development - part 5Andrea Antonello
1.4K views40 Folien

Similar a Xtext's new Formatter API(20)

Hadoop MapReduce framework - Module 3 von Rohit Agrawal
Hadoop MapReduce framework - Module 3Hadoop MapReduce framework - Module 3
Hadoop MapReduce framework - Module 3
Rohit Agrawal459 views
Csharp4 strings and_regular_expressions von Abed Bukhari
Csharp4 strings and_regular_expressionsCsharp4 strings and_regular_expressions
Csharp4 strings and_regular_expressions
Abed Bukhari441 views
Opensource gis development - part 5 von Andrea Antonello
Opensource gis development - part 5Opensource gis development - part 5
Opensource gis development - part 5
Andrea Antonello1.4K views
Spark with Elasticsearch von Holden Karau
Spark with ElasticsearchSpark with Elasticsearch
Spark with Elasticsearch
Holden Karau1.4K views
Case Study On System Requirement Modeling von Laura Scott
Case Study On System Requirement ModelingCase Study On System Requirement Modeling
Case Study On System Requirement Modeling
Laura Scott3 views
Model-Driven Software Development - Language Workbenches & Syntax Definition von Eelco Visser
Model-Driven Software Development - Language Workbenches & Syntax DefinitionModel-Driven Software Development - Language Workbenches & Syntax Definition
Model-Driven Software Development - Language Workbenches & Syntax Definition
Eelco Visser1.2K views
Patterns in Python von dn
Patterns in PythonPatterns in Python
Patterns in Python
dn 3.3K views
Choose'10: Ralf Laemmel - Dealing Confortably with the Confusion of Tongues von CHOOSE
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
CHOOSE 788 views
C,c++ interview q&a von Kumaran K
C,c++ interview q&aC,c++ interview q&a
C,c++ interview q&a
Kumaran K858 views
Allura - an Open Source MongoDB Based Document Oriented SourceForge von Rick Copeland
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
Rick Copeland2.9K views
An Overview Of Python With Functional Programming von Adam Getchell
An Overview Of Python With Functional ProgrammingAn Overview Of Python With Functional Programming
An Overview Of Python With Functional Programming
Adam Getchell656 views
Latex workshop: Essentials and Practices von Mohamed Alrshah
Latex workshop: Essentials and PracticesLatex workshop: Essentials and Practices
Latex workshop: Essentials and Practices
Mohamed Alrshah1.5K views
Echtzeitapplikationen mit Elixir und GraphQL von Moritz Flucht
Echtzeitapplikationen mit Elixir und GraphQLEchtzeitapplikationen mit Elixir und GraphQL
Echtzeitapplikationen mit Elixir und GraphQL
Moritz Flucht409 views

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