146
Moritz Eysholdt, itemis AG XTEXT’S NEW FORMATTER API

Xtext's new Formatter API

Embed Size (px)

Citation preview

Page 1: Xtext's new Formatter API

Moritz Eysholdt, itemis AG

XTEXT’S NEW FORMATTER API

Page 2: Xtext's new Formatter API

AGENDA• motivation

• use cases

• ITextRegionAccess

• IFormattableDocument

• example formatter

• testing

Page 3: Xtext's new Formatter API

DEFINITION

Page 4: Xtext's new Formatter API

DEFINITION

“improve readability and emphasize structure of a document

without changing its meaning”

Page 5: Xtext's new Formatter API

DEFINITION

“improve readability and emphasize structure of a document

without changing its meaning”

“prettify spaces, line wraps, tabs and indentation without changing the AST”

Page 6: Xtext's new Formatter API

…if it does change the AST, it’s probably a clean-up action or refactoring

Page 7: Xtext's new Formatter API

MOTIVATION

Page 8: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 9: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

With Formatter: CTRL+SHIFT+F

MOTIVATION

Page 10: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

With Formatter: CTRL+SHIFT+F

Without Formatter: →→⇒⇒__↓ →→⇒⇒⇒⇒__↓ ¶ ↑↑⇒↓↓⇒⇒¶

MOTIVATION

Page 11: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 12: Xtext's new Formatter API

bad formatting… - obscures the code’s structure - unnecessarily consumes brain

cycles to filter out anomalies

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 13: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 14: Xtext's new Formatter API

• whitespace-only changes in diffs are annoying

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 15: Xtext's new Formatter API

• whitespace-only changes in diffs are annoying

• merge conflicts because of whitespace-only super annoying

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 16: Xtext's new Formatter API

• 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

Page 17: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 18: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 19: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 20: Xtext's new Formatter API

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 21: Xtext's new Formatter API

wrong indentation! proper formatting would have

increased the chances of spotting this bug!

I’m lazy

readability

diff & merge

bug prevention

MOTIVATION

Page 22: Xtext's new Formatter API

USE CASES

Page 23: Xtext's new Formatter API

on demand

serialization

semantic quickfix

USE CASES

Page 24: Xtext's new Formatter API

on demand

serialization

semantic quickfix

CTRL+SHIFT+F….and via context menu

USE CASES

Page 25: Xtext's new Formatter API

on demand

serialization

semantic quickfix

USE CASES

Page 26: Xtext's new Formatter API

on demand

serialization

semantic quickfix

Use an Xtext grammar to convert an EMF model to text. Formatter contributes indentation, newlines, space, etc.

USE CASES

Page 27: Xtext's new Formatter API

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

Page 28: Xtext's new Formatter API

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])); } });}

Page 29: Xtext's new Formatter API

ARCHITECTURE

Page 30: Xtext's new Formatter API

messy

formatter

pretty

Page 31: Xtext's new Formatter API

messy

formatter

pretty

developer

creates

developer creates formatter, NoEngine!

Page 32: Xtext's new Formatter API

messy

formatter

pretty

developer

creates

text

model(AST)

text-regions(CST)

in

in

in

Input: Text, AST and CST

Page 33: Xtext's new Formatter API

messy

formatter

pretty

developer

creates

text

model(AST)

text-regions(CST)

in

in

in

text replacements

apply

Output: TextReplacements

Page 34: Xtext's new Formatter API

messy

formatter

pretty

grammar

developer

creates

text

model(AST)

text-regions(CST)

in

in

in

text replacements

apply

references

access to grammar

Page 35: Xtext's new Formatter API

fragment = formatting2.Formatter2Fragment auto-inject {}

fragment = formatting.FormatterFragment auto-inject {}

GeneratorFragment for MWE2

Page 36: Xtext's new Formatter API

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 “;";

Page 37: Xtext's new Formatter API

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

Page 38: Xtext's new Formatter API

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

Page 39: Xtext's new Formatter API

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 “;";

Page 40: Xtext's new Formatter API

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 “;";

Page 41: Xtext's new Formatter API

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 “;";

Page 42: Xtext's new Formatter API

TEXT REGIONS

Page 43: Xtext's new Formatter API

text:StringDocument

getText():String

offset:intlength:int

TextRegion1 *

TextRegions… …represent a substring …can overlap …can be empty

Page 44: Xtext's new Formatter API

regionForOffset(int offset, int length)regionForDocument()

resource:XtextResource

ITextRegionAccess

regionForEObject(EObject object)regionForRootEObject()

the document. created from node

model or via serializer

Page 45: Xtext's new Formatter API

getText():String

offset : intlength: int

ITextRegion

regionForOffset(int offset, int length)regionForDocument()

resource:XtextResource

ITextRegionAccess

regionForEObject(EObject object)regionForRootEObject()

returns

regions for anything

Page 46: Xtext's new Formatter API

non-hidden token 0..n hidden tokens

ISequentialRegion

getText():String

offset : intlength: 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

Page 47: Xtext's new Formatter API

whitespace token comment token

ISequentialRegion

getText():String

offset : intlength: 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

Page 48: Xtext's new Formatter API

eObject:EObjectgrammarElement:EObject

IEObjectRegion ISequentialRegion

getText():String

offset : intlength: 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

leadingtrailing

for AST element

Page 49: Xtext's new Formatter API

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

Page 50: Xtext's new Formatter API

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

/* Copyright */machine my.X;!// First Statestate

1. Take Document

1.

Page 51: Xtext's new Formatter API

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

1. Take Document 2. Parse it using Grammar

1.

2.

Page 52: Xtext's new Formatter API

class StatesFormatter extends AbstractFormatter2 {! def dispatch void format(Model model, extension IFormattableDocument document) { println(textRegionAccess.toString()) }}

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

/* Copyright */machine my.X;!// First Statestate

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.

Page 53: Xtext's new Formatter API

class StatesFormatter extends AbstractFormatter2 {! def dispatch void format(Model model, extension IFormattableDocument document) { println(textRegionAccess) }}

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

/* Copyright */machine my.X;!// First Statestate

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.

Page 54: Xtext's new Formatter API

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

Page 55: Xtext's new Formatter API

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

Page 56: Xtext's new Formatter API

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

Page 57: Xtext's new Formatter API

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

keyword

datatype

Page 58: Xtext's new Formatter API

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

empty

first region in document

last region in document

1 part

2 parts

Page 59: Xtext's new Formatter API

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

Page 60: Xtext's new Formatter API

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

Page 61: Xtext's new Formatter API

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

begin

end

grammarElement

path

Page 62: Xtext's new Formatter API

Columns: 1:offset 2:length 3:kind 4: text 5:grammarElementKind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion! 0 H "/* Copyright */" Comment:TerminalRule'ML_COMMENT' 16 "\n" Whitespace:TerminalRule'WS' B Model'my.X' Model16 7 S "machine" Model:'machine'23 1 H " " Whitespace:TerminalRule'WS'24 4 S "my.X" Model:name=QualifiedName28 0 H28 1 S ";" Model:';'29 H "\n\n" Whitespace:TerminalRule'WS' 17 "// First State\n" 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' Model51 0 H

/* Copyright */machine my.X;!// First Statestate

Model: "machine" name=QualifiedName ";" elements+=State*;!State: {State} ‘state';!QualifiedName: ID ('.' ID)*;

eObject:EObjectgrammarElement:EObject

IEObjectRegion

undefined:boolean

IHiddenRegion

grammarElement:EObject

ISemanticRegion nextprevious

previousnext

grammarElement:EObject

IHiddenRegionPart

children

parts

IWhitespace IComment

leadingtrailing

Page 63: Xtext's new Formatter API

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 “;";

Page 64: Xtext's new Formatter API

FIND SEMANTIC REGIONS

// in local eObject only eObject.regionFortextRegionAccess.regionForEObject(eObject).regionFor!// in eObject or any of its childreneObject.allRegionsFortextRegionAccess.regionForEObject(eObject).allRegionsFor!// in whole documenttextRegionAccess.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("(", ")")

{

Page 65: Xtext's new Formatter API

FIND SEMANTIC REGIONS

// in local eObject only eObject.regionFortextRegionAccess.regionForEObject(eObject).regionFor!// in eObject or any of its childreneObject.allRegionsFortextRegionAccess.regionForEObject(eObject).allRegionsFor!// in whole documenttextRegionAccess.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

Page 66: Xtext's new Formatter API

FIND SEMANTIC REGIONS

// in local eObject only eObject.regionFortextRegionAccess.regionForEObject(eObject).regionFor!// in eObject or any of its childreneObject.allRegionsFortextRegionAccess.regionForEObject(eObject).allRegionsFor!// in whole documenttextRegionAccess.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

Page 67: Xtext's new Formatter API

FIND SEMANTIC REGIONS

// in local eObject only eObject.regionFortextRegionAccess.regionForEObject(eObject).regionFor!// in eObject or any of its childreneObject.allRegionsFortextRegionAccess.regionForEObject(eObject).allRegionsFor!// in whole documenttextRegionAccess.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)

Page 68: Xtext's new Formatter API

FIND SEMANTIC REGIONS

// in local eObject only eObject.regionFortextRegionAccess.regionForEObject(eObject).regionFor!// in eObject or any of its childreneObject.allRegionsFortextRegionAccess.regionForEObject(eObject).allRegionsFor!// in whole documenttextRegionAccess.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)

Page 69: Xtext's new Formatter API

FIND SEMANTIC REGIONS

// in local eObject only eObject.regionFortextRegionAccess.regionForEObject(eObject).regionFor!// in eObject or any of its childreneObject.allRegionsFortextRegionAccess.regionForEObject(eObject).allRegionsFor!// in whole documenttextRegionAccess.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)

Page 70: Xtext's new Formatter API

TEXT REGION RECAP

Page 71: Xtext's new Formatter API

TEXT REGION RECAP• ITextRegionAccess (for Xtend, ITextRegionExtensions)

Page 72: Xtext's new Formatter API

TEXT REGION RECAP• ITextRegionAccess (for Xtend, ITextRegionExtensions)

• linked list of strictly alternating ISemanticRegion and IHiddenRegion

Page 73: Xtext's new Formatter API

TEXT REGION RECAP• ITextRegionAccess (for Xtend, ITextRegionExtensions)

• linked list of strictly alternating ISemanticRegion and IHiddenRegion

• IHiddenRegion can be empty

Page 74: Xtext's new Formatter API

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

Page 75: Xtext's new Formatter API

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

Page 76: Xtext's new Formatter API

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!

Page 77: Xtext's new Formatter API

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)

Page 78: Xtext's new Formatter API

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)

Page 79: Xtext's new Formatter API

FORMATTABLE DOCUMENT

Page 80: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!! // ...}

API: specify formatting information for hidden regions

Implementation: store text replacers for text regions

Page 81: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!! // ...}

API: specify formatting information for hidden regions

Implementation: store text replacers for text regions

Page 82: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

specify for one or two

offset : intlength: int

IHiddenRegion

autowrap()setPriority(int priority)indent()newLine()setNewLines(int newLines)setNewLines(int min, int default, int max)noSpace()oneSpace()setSpace(String space)

IHiddenRegionFormatter

Page 83: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

specify for one or two

offset : intlength: 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] }}

Page 84: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

specify for one or two

offset : intlength: 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

Page 85: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

specify for one or two

offset : intlength: 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

Page 86: Xtext's new Formatter API

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 IHiddenRegionFormatter IHiddenRegion

Page 87: Xtext's new Formatter API

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

Page 88: Xtext's new Formatter API

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

Page 89: Xtext's new Formatter API

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

Page 90: Xtext's new Formatter API

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

Page 91: Xtext's new Formatter API

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

Page 92: Xtext's new Formatter API

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 two IHiddenRegionFormatter IHiddenRegion

Page 93: Xtext's new Formatter API

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 two IHiddenRegionFormatter IHiddenRegion

IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion

Page 94: Xtext's new Formatter API

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 two IHiddenRegionFormatter IHiddenRegion

IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion

Page 95: Xtext's new Formatter API

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 two IHiddenRegionFormatter IHiddenRegion

IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion

Page 96: Xtext's new Formatter API

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 two IHiddenRegionFormatter IHiddenRegion

IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion

Page 97: Xtext's new Formatter API

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 two IHiddenRegionFormatter IHiddenRegion

IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion ISemanitcRegion IHiddenRegion

Page 98: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

Page 99: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

Why specify formatting for two hidden regions at once?

Page 100: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

Why specify formatting for two hidden regions at once?- indent() spans from first to second hidden region!

Page 101: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

Why specify formatting for two hidden regions at once?- indent() spans from first to second hidden region!- convenience

Page 102: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!!! // ...}

Why specify formatting for two hidden regions at once?- indent() spans from first to second hidden region!- convenience

Page 103: Xtext's new Formatter API

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?

Page 104: Xtext's new Formatter API

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

Page 105: Xtext's new Formatter API

----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------machine my.X;state<19| >foo<23| >actions 1, 2, 3;<43|>end!--------------------------------------------------------------------------------19 1 " ": HiddenRegionReplacer: space=' ';indentInc=123 2 "\n ": HiddenRegionReplacer: newLine=143 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';

Page 106: Xtext's new Formatter API

----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------machine my.X;state<19| >foo<23| >actions 1, 2, 3;<43|>end!--------------------------------------------------------------------------------19 1 " ": HiddenRegionReplacer: space=' ';indentInc=123 2 "\n ": HiddenRegionReplacer: newLine=143 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';

Page 107: Xtext's new Formatter API

----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------machine my.X;state<19| >foo<23| >actions 1, 2, 3;<43|>end!--------------------------------------------------------------------------------19 1 " ": HiddenRegionReplacer: space=' ';indentInc=123 2 "\n ": HiddenRegionReplacer: newLine=143 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';

Page 108: Xtext's new Formatter API

----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------machine my.X;state<19| >foo<23| >actions 1, 2, 3;<43|>end!--------------------------------------------------------------------------------19 1 " ": HiddenRegionReplacer: space=' ';indentInc=123 2 "\n ": HiddenRegionReplacer: newLine=143 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';

Page 109: Xtext's new Formatter API

----------- RootDocument with ITextReplacers (syntax: <offset|text>) -----------machine my.X;state<19| >foo<23| >actions 1, 2, 3;<43|>end!--------------------------------------------------------------------------------19 1 " ": HiddenRegionReplacer: space=' ';indentInc=123 2 "\n ": HiddenRegionReplacer: newLine=143 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';

Page 110: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!! // ...}

API: specify formatting information for hidden regions

Implementation: store text replacers for text regions

Page 111: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!! // ...}

offset : intlength: intnewText: String

ITextReplacementautowrap()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!

Page 112: Xtext's new Formatter API

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

Page 113: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!}

creates TextReplacements

createReplacements(ITextReplacerContext context): ITextReplacerContext

ITextReplacer

offset : intlength: intnewText: 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

Page 114: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!}

specific for TextRegion

createReplacements(ITextReplacerContext context): ITextReplacerContext

ITextReplacer

offset : intlength: intnewText: 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 : intlength: int

ITextRegion

1

1

creates

Page 115: Xtext's new Formatter API

interface IFormattableDocument {!!!!!!!!!!!! !!!!!!!!!!!!!}

createReplacements(ITextReplacerContext context): ITextReplacerContext

ITextReplacer

offset : intlength: intnewText: 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(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

context collects replacements

Page 116: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

Page 117: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

sorted

Page 118: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

invoke in sorted order

Page 119: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

in/out context

Page 120: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

return replacements

Page 121: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

create replacement

Page 122: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

add to context

Page 123: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

return cloned context

Page 124: Xtext's new Formatter API

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 : intlength: intnewText: String

ITextReplacementHiddenRegionReplacer

getIndentation(): intwithIndentation(int indent): ITextReplacerContextgetParent(): ITextReplacerContextaddReplacement(ITextReplacement repl)getAllReplacements():List<ITextReplacement>

ITextReplacerContext

uses

offset : intlength: int

ITextRegion

1

1

creates

collects

Page 125: Xtext's new Formatter API

EXAMPLES

Page 126: Xtext's new Formatter API

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';

Page 127: Xtext's new Formatter API

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';

Page 128: Xtext's new Formatter API

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

Page 129: Xtext's new Formatter API

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

Page 130: Xtext's new Formatter API

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

Page 131: Xtext's new Formatter API

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

Page 132: Xtext's new Formatter API

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';

Page 133: Xtext's new Formatter API

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

Page 134: Xtext's new Formatter API

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';

Page 135: Xtext's new Formatter API

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';

Page 136: Xtext's new Formatter API

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';

Page 137: Xtext's new Formatter API

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';

Page 138: Xtext's new Formatter API

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

Page 139: Xtext's new Formatter API

Table-based formattingstate 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';

Page 140: Xtext's new Formatter API

Table-based formattingstate 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';

Page 141: Xtext's new Formatter API

Table-based formattingstate 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';

Page 142: Xtext's new Formatter API

TESTS

Page 143: Xtext's new Formatter API

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" } } ''' ]}

Page 144: Xtext's new Formatter API

THE OLD FORMATTER

• no access to text, Node Model or AST

• not enhanceable engine

• will be deprecated

Page 145: Xtext's new Formatter API

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

Page 146: Xtext's new Formatter API

Questions?