langium
langium copied to clipboard
Formatter shows cyclic behaviour
I am implementing a code formatter for a language that has a number of constructs with code regions enclosed in keywords, in a nested fashion, for example a minimal program could be something like:
BEGIN_NAMESPACE
myNamespace
BEGIN_STRUCT
myStruct
END_STRUCT
END_NAMESPACE
I would like to have a free line before the opening BEGIN_STRUCT keyword and if there already is one or more than one, I wish to keep those lines, but when I implement it according to the documentation (at least to my understanding of it) and apply the auto format multiple times, the formatting changes back and forth, adding and removing the desired line.
Langium version: current development (3.0 candidate)
Steps To Reproduce
For example just add this to the domain-model.langium grammar:
Structure:
'BEGIN_STRUCT'
name=QualifiedName
'END_STRUCT';
NameSpace:
'BEGIN_NAMESPACE'
name=QualifiedName
(elements+=AbstractElement)*
'END_NAMESPACE';
and register the two as AbstractElement:
AbstractElement:
PackageDeclaration | Type | Structure | NameSpace;
then add this to the format function of the DomainModelFormatter:
} else if (ast.isStructure(node)) {
const formatter = this.getNodeFormatter(node);
const structOpen = formatter.keyword('BEGIN_STRUCT');
const structClose = formatter.keyword('END_STRUCT');
formatter.interior(structOpen, structClose).prepend(Formatting.indent());
structOpen.prepend(Formatting.newLines(2, {allowMore: true}));
structClose.prepend(Formatting.newLine());
} else if (ast.isNameSpace(node)) {
const formatter = this.getNodeFormatter(node);
const nsOpen = formatter.keyword('BEGIN_NAMESPACE');
const nsClose = formatter.keyword('END_NAMESPACE');
formatter.interior(nsOpen, nsClose).prepend(Formatting.indent());
nsClose.prepend(Formatting.newLine());
}
Then apply Format Document
repeatedly to a document with the minimal program I wrote above.
The current behavior
The line before BEGIN_STRUCT alternatingly appears and is removed. More than one line is initially removed, after that same behaviour: alternate between empty line and no empty line before BEGIN_STRUCT.
The expected behavior
Depending on whether there already is a free line it should get added or simply left in the formatted code. Multiple applications of the format should not change anything after the first time. More than one free line should also be respected and not removed.