mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-15 16:28:56 +00:00
Merge pull request #328 from eclipse/msp_templateNodes
Fixed inconsistencies between TemplateNode and Xtend templates
This commit is contained in:
commit
ecc8da5c4f
12 changed files with 581 additions and 158 deletions
|
@ -51,15 +51,17 @@ class GeneratorNodeTest {
|
|||
var node = root.trace.append('Hallo').appendNewLine
|
||||
node.indent
|
||||
node.append('noindent').appendNewLine
|
||||
node.append(new IndentNode(' ', true, true))
|
||||
node.append('noindent').appendNewLine
|
||||
|
||||
val processor = new GeneratorNodeProcessor
|
||||
Assert.assertEquals('''
|
||||
Hallo
|
||||
noindent
|
||||
noindent
|
||||
'''.toString, processor.process(node).toString)
|
||||
}
|
||||
|
||||
|
||||
@Test def void testTemplateProcessing() {
|
||||
val root = loc(0)
|
||||
val node = root.trace
|
||||
|
@ -89,20 +91,20 @@ class GeneratorNodeTest {
|
|||
}'''.toString, result.traceRegion.toString)
|
||||
}
|
||||
|
||||
def StringConcatenationClient someCodeGen(int n) '''
|
||||
private def StringConcatenationClient someCodeGen(int n) '''
|
||||
«FOR i : 0..<n»
|
||||
before «loc(10+i).trace.append('Hello')» after
|
||||
«someCodeGen(n-1)»
|
||||
«ENDFOR»
|
||||
'''
|
||||
def String someCodeGen_noTrace(int n) '''
|
||||
private def String someCodeGen_noTrace(int n) '''
|
||||
«FOR i : 0..<n»
|
||||
before «'Hello'» after
|
||||
«someCodeGen_noTrace(n-1)»
|
||||
«ENDFOR»
|
||||
'''
|
||||
|
||||
def loc(int idx) {
|
||||
private def loc(int idx) {
|
||||
new LocationData(idx, 100-idx, 0, 0, new SourceRelativeURI('foo/mymodel.dsl'))
|
||||
}
|
||||
|
||||
|
@ -146,4 +148,49 @@ class GeneratorNodeTest {
|
|||
'''.toString, processor.process(node).toString)
|
||||
}
|
||||
|
||||
@Test def void testIndentVariants() {
|
||||
val node = new CompositeGeneratorNode
|
||||
node.doIndent(false, false)
|
||||
node.doIndent(true, false)
|
||||
node.doIndent(false, true)
|
||||
node.doIndent(true, true)
|
||||
|
||||
val processor = new GeneratorNodeProcessor
|
||||
Assert.assertEquals('''
|
||||
// indentImmediately: false, indentEmptyLines: false
|
||||
a
|
||||
|
||||
bc
|
||||
|
||||
d
|
||||
// indentImmediately: true, indentEmptyLines: false
|
||||
a
|
||||
|
||||
b c
|
||||
|
||||
d
|
||||
// indentImmediately: false, indentEmptyLines: true
|
||||
a
|
||||
|
||||
bc
|
||||
|
||||
d
|
||||
// indentImmediately: true, indentEmptyLines: true
|
||||
a
|
||||
|
||||
b c
|
||||
|
||||
d
|
||||
'''.toString, processor.process(node).toString)
|
||||
}
|
||||
|
||||
private def void doIndent(CompositeGeneratorNode parent, boolean indentImmediately, boolean indentEmptyLines) {
|
||||
parent.append('// indentImmediately: ').append(indentImmediately)
|
||||
parent.append(', indentEmptyLines: ').append(indentEmptyLines).appendNewLine
|
||||
parent.append(new IndentNode(' ', indentImmediately, indentEmptyLines).append('a').appendNewLine.appendNewLine.append('b'))
|
||||
parent.append(new IndentNode(' ', indentImmediately, indentEmptyLines).append('c')).appendNewLine
|
||||
parent.append(new IndentNode(' ', indentImmediately, indentEmptyLines).appendNewLine)
|
||||
parent.append('d').append(new IndentNode(' ', indentImmediately, indentEmptyLines).appendNewLine)
|
||||
}
|
||||
|
||||
}
|
|
@ -60,13 +60,6 @@ class TemplateNodeTest {
|
|||
''')
|
||||
}
|
||||
|
||||
@Test def void testSeparatorLoop() {
|
||||
val strings = #['a', 'b', 'c']
|
||||
assertEquals('''
|
||||
«FOR s : strings SEPARATOR ', '»"«s»"«ENDFOR»
|
||||
''')
|
||||
}
|
||||
|
||||
private def other() '''
|
||||
foo «"dfdf" + 23» bar
|
||||
'''
|
||||
|
@ -77,7 +70,56 @@ class TemplateNodeTest {
|
|||
«other()»
|
||||
'''
|
||||
|
||||
def void assertEquals(StringConcatenationClient c) {
|
||||
@Test def void testSeparatorLoop() {
|
||||
val strings = #['a', 'b', 'c']
|
||||
assertEquals('''
|
||||
«FOR s : strings SEPARATOR ', '»"«s»"«ENDFOR»
|
||||
''')
|
||||
}
|
||||
|
||||
@Test def void testIndentedIf() {
|
||||
val condition = true
|
||||
val string = 'foo'
|
||||
assertEquals('''
|
||||
Very wise:
|
||||
«IF condition»
|
||||
who «string» do
|
||||
«ENDIF»
|
||||
''')
|
||||
}
|
||||
|
||||
@Test def void testIndentedFor() {
|
||||
val list = #['foo', 'bar']
|
||||
assertEquals('''
|
||||
Very wise:
|
||||
«FOR s : list» «s»«ENDFOR»
|
||||
''')
|
||||
}
|
||||
|
||||
@Test def void testIndentedTemplate() {
|
||||
val StringConcatenationClient template = '''
|
||||
sometimes foo
|
||||
|
||||
and sometimes bar
|
||||
'''
|
||||
assertEquals('''
|
||||
Very wise:
|
||||
«template»
|
||||
''')
|
||||
}
|
||||
|
||||
@Test def void testIfNotEmpty() {
|
||||
val StringConcatenationClient template = '''
|
||||
«''»
|
||||
foo
|
||||
'''
|
||||
assertEquals('''
|
||||
Very wise:
|
||||
«template»
|
||||
''')
|
||||
}
|
||||
|
||||
protected def void assertEquals(StringConcatenationClient c) {
|
||||
val ext = new GeneratorNodeExtensions()
|
||||
val processor = new GeneratorNodeProcessor()
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.eclipse.xtext.generator.trace.SourceRelativeURI;
|
|||
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
|
||||
import org.eclipse.xtext.generator.trace.node.GeneratorNodeExtensions;
|
||||
import org.eclipse.xtext.generator.trace.node.GeneratorNodeProcessor;
|
||||
import org.eclipse.xtext.generator.trace.node.IndentNode;
|
||||
import org.eclipse.xtext.xbase.lib.ExclusiveRange;
|
||||
import org.eclipse.xtext.xbase.lib.Extension;
|
||||
import org.junit.Assert;
|
||||
|
@ -73,12 +74,17 @@ public class GeneratorNodeTest {
|
|||
CompositeGeneratorNode node = this.exts.appendNewLine(this.exts.append(this.exts.trace(root), "Hallo"));
|
||||
this.exts.indent(node);
|
||||
this.exts.appendNewLine(this.exts.append(node, "noindent"));
|
||||
IndentNode _indentNode = new IndentNode(" ", true, true);
|
||||
this.exts.append(node, _indentNode);
|
||||
this.exts.appendNewLine(this.exts.append(node, "noindent"));
|
||||
final GeneratorNodeProcessor processor = new GeneratorNodeProcessor();
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("Hallo");
|
||||
_builder.newLine();
|
||||
_builder.append("noindent");
|
||||
_builder.newLine();
|
||||
_builder.append("noindent");
|
||||
_builder.newLine();
|
||||
Assert.assertEquals(_builder.toString(), processor.process(node).toString());
|
||||
}
|
||||
|
||||
|
@ -146,7 +152,7 @@ public class GeneratorNodeTest {
|
|||
Assert.assertEquals(_builder.toString(), result.getTraceRegion().toString());
|
||||
}
|
||||
|
||||
public StringConcatenationClient someCodeGen(final int n) {
|
||||
private StringConcatenationClient someCodeGen(final int n) {
|
||||
StringConcatenationClient _client = new StringConcatenationClient() {
|
||||
@Override
|
||||
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
|
||||
|
@ -169,7 +175,7 @@ public class GeneratorNodeTest {
|
|||
return _client;
|
||||
}
|
||||
|
||||
public String someCodeGen_noTrace(final int n) {
|
||||
private String someCodeGen_noTrace(final int n) {
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
{
|
||||
ExclusiveRange _doubleDotLessThan = new ExclusiveRange(0, n, true);
|
||||
|
@ -187,7 +193,7 @@ public class GeneratorNodeTest {
|
|||
return _builder.toString();
|
||||
}
|
||||
|
||||
public LocationData loc(final int idx) {
|
||||
private LocationData loc(final int idx) {
|
||||
SourceRelativeURI _sourceRelativeURI = new SourceRelativeURI("foo/mymodel.dsl");
|
||||
return new LocationData(idx, (100 - idx), 0, 0, _sourceRelativeURI);
|
||||
}
|
||||
|
@ -266,4 +272,77 @@ public class GeneratorNodeTest {
|
|||
_builder_3.newLine();
|
||||
Assert.assertEquals(_builder_3.toString(), processor.process(node).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndentVariants() {
|
||||
final CompositeGeneratorNode node = new CompositeGeneratorNode();
|
||||
this.doIndent(node, false, false);
|
||||
this.doIndent(node, true, false);
|
||||
this.doIndent(node, false, true);
|
||||
this.doIndent(node, true, true);
|
||||
final GeneratorNodeProcessor processor = new GeneratorNodeProcessor();
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("// indentImmediately: false, indentEmptyLines: false");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("a");
|
||||
_builder.newLine();
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("bc");
|
||||
_builder.newLine();
|
||||
_builder.newLine();
|
||||
_builder.append("d");
|
||||
_builder.newLine();
|
||||
_builder.append("// indentImmediately: true, indentEmptyLines: false");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("a");
|
||||
_builder.newLine();
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("b c");
|
||||
_builder.newLine();
|
||||
_builder.newLine();
|
||||
_builder.append("d ");
|
||||
_builder.newLine();
|
||||
_builder.append("// indentImmediately: false, indentEmptyLines: true");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("a");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("bc");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.newLine();
|
||||
_builder.append("d");
|
||||
_builder.newLine();
|
||||
_builder.append("// indentImmediately: true, indentEmptyLines: true");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("a");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.append("b c");
|
||||
_builder.newLine();
|
||||
_builder.append(" ");
|
||||
_builder.newLine();
|
||||
_builder.append("d ");
|
||||
_builder.newLine();
|
||||
Assert.assertEquals(_builder.toString(), processor.process(node).toString());
|
||||
}
|
||||
|
||||
private void doIndent(final CompositeGeneratorNode parent, final boolean indentImmediately, final boolean indentEmptyLines) {
|
||||
this.exts.append(this.exts.append(parent, "// indentImmediately: "), Boolean.valueOf(indentImmediately));
|
||||
this.exts.appendNewLine(this.exts.append(this.exts.append(parent, ", indentEmptyLines: "), Boolean.valueOf(indentEmptyLines)));
|
||||
this.exts.append(parent, this.exts.append(this.exts.appendNewLine(this.exts.appendNewLine(this.exts.append(new IndentNode(" ", indentImmediately, indentEmptyLines), "a"))), "b"));
|
||||
this.exts.appendNewLine(this.exts.append(parent, this.exts.append(new IndentNode(" ", indentImmediately, indentEmptyLines), "c")));
|
||||
this.exts.append(parent, this.exts.appendNewLine(new IndentNode(" ", indentImmediately, indentEmptyLines)));
|
||||
this.exts.append(this.exts.append(parent, "d"), this.exts.appendNewLine(new IndentNode(" ", indentImmediately, indentEmptyLines)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,6 +125,28 @@ public class TemplateNodeTest {
|
|||
this.assertEquals(_client);
|
||||
}
|
||||
|
||||
private CharSequence other() {
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("foo ");
|
||||
_builder.append(("dfdf" + Integer.valueOf(23)));
|
||||
_builder.append(" bar");
|
||||
_builder.newLineIfNotEmpty();
|
||||
return _builder;
|
||||
}
|
||||
|
||||
private String multiLineString() {
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("test ");
|
||||
_builder.newLine();
|
||||
_builder.append("bar");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
CharSequence _other = this.other();
|
||||
_builder.append(_other, "\t");
|
||||
_builder.newLineIfNotEmpty();
|
||||
return _builder.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeparatorLoop() {
|
||||
final List<String> strings = Collections.<String>unmodifiableList(CollectionLiterals.<String>newArrayList("a", "b", "c"));
|
||||
|
@ -150,29 +172,101 @@ public class TemplateNodeTest {
|
|||
this.assertEquals(_client);
|
||||
}
|
||||
|
||||
private CharSequence other() {
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("foo ");
|
||||
_builder.append(("dfdf" + Integer.valueOf(23)));
|
||||
_builder.append(" bar");
|
||||
_builder.newLineIfNotEmpty();
|
||||
return _builder;
|
||||
@Test
|
||||
public void testIndentedIf() {
|
||||
final boolean condition = true;
|
||||
final String string = "foo";
|
||||
StringConcatenationClient _client = new StringConcatenationClient() {
|
||||
@Override
|
||||
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
|
||||
_builder.append("Very wise:");
|
||||
_builder.newLine();
|
||||
{
|
||||
if (condition) {
|
||||
_builder.append("\t");
|
||||
_builder.append("who ");
|
||||
_builder.append(string, "\t");
|
||||
_builder.append(" do");
|
||||
_builder.newLineIfNotEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
this.assertEquals(_client);
|
||||
}
|
||||
|
||||
private String multiLineString() {
|
||||
StringConcatenation _builder = new StringConcatenation();
|
||||
_builder.append("test ");
|
||||
_builder.newLine();
|
||||
_builder.append("bar");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
CharSequence _other = this.other();
|
||||
_builder.append(_other, "\t");
|
||||
_builder.newLineIfNotEmpty();
|
||||
return _builder.toString();
|
||||
@Test
|
||||
public void testIndentedFor() {
|
||||
final List<String> list = Collections.<String>unmodifiableList(CollectionLiterals.<String>newArrayList("foo", "bar"));
|
||||
StringConcatenationClient _client = new StringConcatenationClient() {
|
||||
@Override
|
||||
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
|
||||
_builder.append("Very wise:");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
{
|
||||
for(final String s : list) {
|
||||
_builder.append("\t");
|
||||
_builder.append(s, "\t");
|
||||
}
|
||||
}
|
||||
_builder.newLineIfNotEmpty();
|
||||
}
|
||||
};
|
||||
this.assertEquals(_client);
|
||||
}
|
||||
|
||||
public void assertEquals(final StringConcatenationClient c) {
|
||||
@Test
|
||||
public void testIndentedTemplate() {
|
||||
StringConcatenationClient _client = new StringConcatenationClient() {
|
||||
@Override
|
||||
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
|
||||
_builder.append("sometimes foo");
|
||||
_builder.newLine();
|
||||
_builder.newLine();
|
||||
_builder.append("and sometimes bar");
|
||||
_builder.newLine();
|
||||
}
|
||||
};
|
||||
final StringConcatenationClient template = _client;
|
||||
StringConcatenationClient _client_1 = new StringConcatenationClient() {
|
||||
@Override
|
||||
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
|
||||
_builder.append("Very wise:");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
_builder.append(template, "\t");
|
||||
_builder.newLineIfNotEmpty();
|
||||
}
|
||||
};
|
||||
this.assertEquals(_client_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIfNotEmpty() {
|
||||
StringConcatenationClient _client = new StringConcatenationClient() {
|
||||
@Override
|
||||
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
|
||||
_builder.newLineIfNotEmpty();
|
||||
_builder.append("foo");
|
||||
_builder.newLine();
|
||||
}
|
||||
};
|
||||
final StringConcatenationClient template = _client;
|
||||
StringConcatenationClient _client_1 = new StringConcatenationClient() {
|
||||
@Override
|
||||
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
|
||||
_builder.append("Very wise:");
|
||||
_builder.newLine();
|
||||
_builder.append("\t");
|
||||
_builder.append(template, "\t");
|
||||
_builder.newLineIfNotEmpty();
|
||||
}
|
||||
};
|
||||
this.assertEquals(_client_1);
|
||||
}
|
||||
|
||||
protected void assertEquals(final StringConcatenationClient c) {
|
||||
final GeneratorNodeExtensions ext = new GeneratorNodeExtensions();
|
||||
final GeneratorNodeProcessor processor = new GeneratorNodeProcessor();
|
||||
final CompositeGeneratorNode root = new CompositeGeneratorNode();
|
||||
|
|
|
@ -41,7 +41,7 @@ class GeneratorNodeProcessor {
|
|||
|
||||
@Accessors protected static class Context {
|
||||
List<StringBuilder> lines
|
||||
Deque<String> currentIndents
|
||||
Deque<IndentNode> currentIndents
|
||||
boolean pendingIndent
|
||||
AbstractTraceRegion currentRegion = null
|
||||
|
||||
|
@ -52,7 +52,7 @@ class GeneratorNodeProcessor {
|
|||
def int contentLength() {
|
||||
val contentLength = lines.fold(0) [ $0 + $1.length ]
|
||||
if (pendingIndent) {
|
||||
return contentLength + currentIndents.fold(0) [ $0 + $1.length ]
|
||||
return contentLength + currentIndents.fold(0) [ $0 + $1.indentationString.length ]
|
||||
} else {
|
||||
return contentLength
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class GeneratorNodeProcessor {
|
|||
def Result process(IGeneratorNode root) {
|
||||
val ctx = new Context => [
|
||||
lines = newArrayList(new StringBuilder)
|
||||
currentIndents = new ArrayDeque<String>()
|
||||
currentIndents = new ArrayDeque
|
||||
pendingIndent = true
|
||||
]
|
||||
doProcess(root, ctx)
|
||||
|
@ -74,74 +74,104 @@ class GeneratorNodeProcessor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Indent nodes apply indentation between newline and content of its children.
|
||||
* An indent node prepends indentation to each line of its children.
|
||||
*/
|
||||
protected def dispatch void doProcess(IndentNode node, Context ctx) {
|
||||
// do nothing if the indent node is empty
|
||||
if (node.children.empty) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
ctx.currentIndents.push(node.indentationString)
|
||||
ctx.pendingIndent = true
|
||||
doProcessChildren(node, ctx)
|
||||
} finally {
|
||||
ctx.currentIndents.pop
|
||||
if (node._hasContent(ctx)) {
|
||||
if (node.indentImmediately && !ctx.pendingIndent) {
|
||||
ctx.currentLine.append(node.indentationString)
|
||||
}
|
||||
try {
|
||||
ctx.currentIndents.push(node)
|
||||
doProcessChildren(node, ctx)
|
||||
} finally {
|
||||
ctx.currentIndents.pop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected def dispatch void doProcess(NewLineNode node, Context ctx) {
|
||||
val trimmedLine = ctx.currentLine.toString.trim
|
||||
if (node.ifNotEmpty && trimmedLine.empty) {
|
||||
if (node.ifNotEmpty && !ctx.currentLine.hasNonWhitespace) {
|
||||
ctx.lines.set(ctx.currentLineNumber, new StringBuilder)
|
||||
return
|
||||
} else {
|
||||
if (ctx.pendingIndent)
|
||||
handlePendingIndent(ctx, true)
|
||||
ctx.currentLine.append(node.lineDelimiter)
|
||||
ctx.lines.add(new StringBuilder)
|
||||
}
|
||||
ctx.currentLine.append(node.lineDelimiter)
|
||||
ctx.lines.add(new StringBuilder)
|
||||
ctx.pendingIndent = true
|
||||
}
|
||||
|
||||
protected def dispatch void doProcess(TextNode node, Context ctx) {
|
||||
val txt = node.text.toString
|
||||
if (txt.empty) {
|
||||
return
|
||||
if (node._hasContent(ctx)) {
|
||||
if (ctx.pendingIndent)
|
||||
handlePendingIndent(ctx, false)
|
||||
ctx.currentLine.append(node.text)
|
||||
}
|
||||
if (ctx.pendingIndent) {
|
||||
val indentString = new StringBuilder
|
||||
for (indentationString : ctx.currentIndents) {
|
||||
indentString.append(indentationString)
|
||||
}
|
||||
}
|
||||
|
||||
protected def void handlePendingIndent(Context ctx, boolean endOfLine) {
|
||||
val indentString = new StringBuilder
|
||||
for (indentNode : ctx.currentIndents) {
|
||||
if (indentNode.indentEmptyLines || !endOfLine)
|
||||
indentString.append(indentNode.indentationString)
|
||||
}
|
||||
if (indentString.length > 0) {
|
||||
ctx.currentLine.insert(0, indentString)
|
||||
ctx.pendingIndent = false
|
||||
}
|
||||
ctx.currentLine.append(node.text)
|
||||
ctx.pendingIndent = false
|
||||
}
|
||||
|
||||
protected def dispatch void doProcess(TraceNode node, Context ctx) {
|
||||
if (node._hasContent(ctx)) {
|
||||
val beforeRegion = ctx.currentRegion
|
||||
val newRegion = new CompletableTraceRegion(false, node.sourceLocation, beforeRegion)
|
||||
val offset = ctx.contentLength
|
||||
val startLineNumber = ctx.currentLineNumber
|
||||
try {
|
||||
ctx.currentRegion = newRegion
|
||||
doProcessChildren(node, ctx)
|
||||
} finally {
|
||||
if (beforeRegion !== null)
|
||||
ctx.currentRegion = beforeRegion
|
||||
newRegion.complete(offset, ctx.contentLength - offset, startLineNumber, ctx.currentLineNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected def dispatch void doProcess(CompositeGeneratorNode node, Context ctx) {
|
||||
doProcessChildren(node, ctx)
|
||||
}
|
||||
|
||||
protected def dispatch void doProcess(TraceNode node, Context ctx) {
|
||||
val beforeRegion = ctx.currentRegion
|
||||
val newRegion = new CompletableTraceRegion(false, node.sourceLocation, beforeRegion)
|
||||
val offset = ctx.contentLength
|
||||
val startLineNumber = ctx.currentLineNumber
|
||||
try {
|
||||
ctx.currentRegion = newRegion
|
||||
doProcessChildren(node, ctx)
|
||||
} finally {
|
||||
if (beforeRegion !== null)
|
||||
ctx.currentRegion = beforeRegion
|
||||
newRegion.complete(offset, ctx.contentLength - offset, startLineNumber, ctx.currentLineNumber)
|
||||
}
|
||||
}
|
||||
|
||||
protected def void doProcessChildren(CompositeGeneratorNode node, Context ctx) {
|
||||
for (child : node.children) {
|
||||
doProcess(child, ctx)
|
||||
}
|
||||
}
|
||||
protected def dispatch boolean hasContent(CompositeGeneratorNode node, Context ctx) {
|
||||
node.children.exists[hasContent(ctx)]
|
||||
}
|
||||
|
||||
protected def dispatch boolean hasContent(NewLineNode node, Context ctx) {
|
||||
!(node.ifNotEmpty && ctx.currentLine.length == 0)
|
||||
}
|
||||
|
||||
protected def dispatch boolean hasContent(TextNode node, Context ctx) {
|
||||
!node.text.nullOrEmpty
|
||||
}
|
||||
|
||||
protected static def boolean hasNonWhitespace(CharSequence s) {
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
if (!Character.isWhitespace(s.charAt(i)))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected static def boolean isNullOrEmpty(CharSequence s) {
|
||||
s === null || s.length == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to avoid multi-pass processing, when constructing a trace region tree.
|
||||
*
|
||||
|
|
|
@ -10,8 +10,7 @@ package org.eclipse.xtext.generator.trace.node
|
|||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
|
||||
/**
|
||||
* An indent node prepends the indentation string to each line that is generated through its children
|
||||
* (including the first line).
|
||||
* An indent node prepends the indentation string to each line that is generated through its children.
|
||||
*
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
|
@ -20,8 +19,27 @@ class IndentNode extends CompositeGeneratorNode {
|
|||
|
||||
String indentationString
|
||||
|
||||
/**
|
||||
* When this is set to {@code true}, the indentation is always inserted in the first line, otherwise it is
|
||||
* inserted only if the first line has no text preceding this node.
|
||||
*/
|
||||
boolean indentImmediately
|
||||
|
||||
/**
|
||||
* When this is set to {@code true}, all lines are indented, otherwise only lines with text content are indented.
|
||||
*/
|
||||
boolean indentEmptyLines
|
||||
|
||||
new(String indentationString) {
|
||||
this(indentationString, true, false)
|
||||
}
|
||||
|
||||
new(String indentationString, boolean indentImmediately, boolean indentEmptyLines) {
|
||||
if (indentationString === null)
|
||||
throw new NullPointerException
|
||||
this.indentationString = indentationString
|
||||
this.indentImmediately = indentImmediately
|
||||
this.indentEmptyLines = indentEmptyLines
|
||||
}
|
||||
|
||||
}
|
|
@ -20,13 +20,18 @@ class NewLineNode implements IGeneratorNode {
|
|||
|
||||
String lineDelimiter
|
||||
|
||||
/**
|
||||
* When this is set to {@code true}, the preceding line is removed if it contains only whitespace.
|
||||
*/
|
||||
boolean ifNotEmpty
|
||||
|
||||
new(String lineDelimiter) {
|
||||
this.lineDelimiter = lineDelimiter
|
||||
this(lineDelimiter, false)
|
||||
}
|
||||
|
||||
new(String lineDelimiter, boolean ifNotEmpty) {
|
||||
if (lineDelimiter === null)
|
||||
throw new NullPointerException
|
||||
this.lineDelimiter = lineDelimiter
|
||||
this.ifNotEmpty = ifNotEmpty
|
||||
}
|
||||
|
|
|
@ -33,14 +33,8 @@ class TemplateNode extends CompositeGeneratorNode implements TargetStringConcate
|
|||
override append(Object object, String indentation) {
|
||||
if (indentation.length > 0) {
|
||||
val before = currentParent
|
||||
// The first line of an indented template is prepended with an explicit indentation string.
|
||||
// We need to revert this because this case is already handled by the GeneratorNodeProcessor.
|
||||
val lastChild = before.children.last
|
||||
if (lastChild instanceof TextNode && (lastChild as TextNode).text == indentation) {
|
||||
before.children.remove(before.children.size - 1)
|
||||
}
|
||||
try {
|
||||
currentParent = new IndentNode(indentation)
|
||||
currentParent = new IndentNode(indentation, false, true)
|
||||
before.children += currentParent
|
||||
append(object)
|
||||
} finally {
|
||||
|
@ -94,7 +88,7 @@ class TemplateNode extends CompositeGeneratorNode implements TargetStringConcate
|
|||
for (var i = currentParent.children.size - 1; i >= 0; i--) {
|
||||
val node = currentParent.children.get(i)
|
||||
if (node instanceof TextNode) {
|
||||
if (node.text.toString.trim.length === 0) {
|
||||
if (!node.text.hasContent) {
|
||||
currentParent.children.remove(i)
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +96,14 @@ class TemplateNode extends CompositeGeneratorNode implements TargetStringConcate
|
|||
append(object, indentation)
|
||||
}
|
||||
|
||||
protected static def boolean hasContent(CharSequence s) {
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
if (!Character.isWhitespace(s.charAt(i)))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override newLine() {
|
||||
this.nodeFactory.appendNewLine(currentParent)
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.xtext.util.ITextRegion;
|
|||
import org.eclipse.xtext.util.ITextRegionWithLineInformation;
|
||||
import org.eclipse.xtext.util.TextRegionWithLineInformation;
|
||||
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
|
||||
import org.eclipse.xtext.xbase.lib.Functions.Function1;
|
||||
import org.eclipse.xtext.xbase.lib.Functions.Function2;
|
||||
import org.eclipse.xtext.xbase.lib.IterableExtensions;
|
||||
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
|
||||
|
@ -127,7 +128,7 @@ public class GeneratorNodeProcessor {
|
|||
protected static class Context {
|
||||
private List<StringBuilder> lines;
|
||||
|
||||
private Deque<String> currentIndents;
|
||||
private Deque<IndentNode> currentIndents;
|
||||
|
||||
private boolean pendingIndent;
|
||||
|
||||
|
@ -144,11 +145,11 @@ public class GeneratorNodeProcessor {
|
|||
};
|
||||
final Integer contentLength = IterableExtensions.<StringBuilder, Integer>fold(this.lines, Integer.valueOf(0), _function);
|
||||
if (this.pendingIndent) {
|
||||
final Function2<Integer, String, Integer> _function_1 = (Integer $0, String $1) -> {
|
||||
int _length = $1.length();
|
||||
final Function2<Integer, IndentNode, Integer> _function_1 = (Integer $0, IndentNode $1) -> {
|
||||
int _length = $1.getIndentationString().length();
|
||||
return Integer.valueOf((($0).intValue() + _length));
|
||||
};
|
||||
Integer _fold = IterableExtensions.<String, Integer>fold(this.currentIndents, Integer.valueOf(0), _function_1);
|
||||
Integer _fold = IterableExtensions.<IndentNode, Integer>fold(this.currentIndents, Integer.valueOf(0), _function_1);
|
||||
return ((contentLength).intValue() + (_fold).intValue());
|
||||
} else {
|
||||
return (contentLength).intValue();
|
||||
|
@ -170,11 +171,11 @@ public class GeneratorNodeProcessor {
|
|||
}
|
||||
|
||||
@Pure
|
||||
public Deque<String> getCurrentIndents() {
|
||||
public Deque<IndentNode> getCurrentIndents() {
|
||||
return this.currentIndents;
|
||||
}
|
||||
|
||||
public void setCurrentIndents(final Deque<String> currentIndents) {
|
||||
public void setCurrentIndents(final Deque<IndentNode> currentIndents) {
|
||||
this.currentIndents = currentIndents;
|
||||
}
|
||||
|
||||
|
@ -274,7 +275,7 @@ public class GeneratorNodeProcessor {
|
|||
final Procedure1<GeneratorNodeProcessor.Context> _function = (GeneratorNodeProcessor.Context it) -> {
|
||||
StringBuilder _stringBuilder = new StringBuilder();
|
||||
it.lines = CollectionLiterals.<StringBuilder>newArrayList(_stringBuilder);
|
||||
ArrayDeque<String> _arrayDeque = new ArrayDeque<String>();
|
||||
ArrayDeque<IndentNode> _arrayDeque = new ArrayDeque<IndentNode>();
|
||||
it.currentIndents = _arrayDeque;
|
||||
it.pendingIndent = true;
|
||||
};
|
||||
|
@ -285,76 +286,90 @@ public class GeneratorNodeProcessor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Indent nodes apply indentation between newline and content of its children.
|
||||
* An indent node prepends indentation to each line of its children.
|
||||
*/
|
||||
protected void _doProcess(final IndentNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
boolean _isEmpty = node.getChildren().isEmpty();
|
||||
if (_isEmpty) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ctx.currentIndents.push(node.getIndentationString());
|
||||
ctx.pendingIndent = true;
|
||||
this.doProcessChildren(node, ctx);
|
||||
} finally {
|
||||
ctx.currentIndents.pop();
|
||||
boolean __hasContent = this._hasContent(node, ctx);
|
||||
if (__hasContent) {
|
||||
if ((node.isIndentImmediately() && (!ctx.pendingIndent))) {
|
||||
ctx.currentLine().append(node.getIndentationString());
|
||||
}
|
||||
try {
|
||||
ctx.currentIndents.push(node);
|
||||
this.doProcessChildren(node, ctx);
|
||||
} finally {
|
||||
ctx.currentIndents.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void _doProcess(final NewLineNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
final String trimmedLine = ctx.currentLine().toString().trim();
|
||||
if ((node.isIfNotEmpty() && trimmedLine.isEmpty())) {
|
||||
if ((node.isIfNotEmpty() && (!GeneratorNodeProcessor.hasNonWhitespace(ctx.currentLine())))) {
|
||||
int _currentLineNumber = ctx.currentLineNumber();
|
||||
StringBuilder _stringBuilder = new StringBuilder();
|
||||
ctx.lines.set(_currentLineNumber, _stringBuilder);
|
||||
return;
|
||||
} else {
|
||||
if (ctx.pendingIndent) {
|
||||
this.handlePendingIndent(ctx, true);
|
||||
}
|
||||
ctx.currentLine().append(node.getLineDelimiter());
|
||||
StringBuilder _stringBuilder_1 = new StringBuilder();
|
||||
ctx.lines.add(_stringBuilder_1);
|
||||
}
|
||||
ctx.currentLine().append(node.getLineDelimiter());
|
||||
StringBuilder _stringBuilder_1 = new StringBuilder();
|
||||
ctx.lines.add(_stringBuilder_1);
|
||||
ctx.pendingIndent = true;
|
||||
}
|
||||
|
||||
protected void _doProcess(final TextNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
final String txt = node.getText().toString();
|
||||
boolean _isEmpty = txt.isEmpty();
|
||||
if (_isEmpty) {
|
||||
return;
|
||||
}
|
||||
if (ctx.pendingIndent) {
|
||||
final StringBuilder indentString = new StringBuilder();
|
||||
for (final String indentationString : ctx.currentIndents) {
|
||||
indentString.append(indentationString);
|
||||
boolean __hasContent = this._hasContent(node, ctx);
|
||||
if (__hasContent) {
|
||||
if (ctx.pendingIndent) {
|
||||
this.handlePendingIndent(ctx, false);
|
||||
}
|
||||
ctx.currentLine().append(node.getText());
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePendingIndent(final GeneratorNodeProcessor.Context ctx, final boolean endOfLine) {
|
||||
final StringBuilder indentString = new StringBuilder();
|
||||
for (final IndentNode indentNode : ctx.currentIndents) {
|
||||
if ((indentNode.isIndentEmptyLines() || (!endOfLine))) {
|
||||
indentString.append(indentNode.getIndentationString());
|
||||
}
|
||||
}
|
||||
int _length = indentString.length();
|
||||
boolean _greaterThan = (_length > 0);
|
||||
if (_greaterThan) {
|
||||
ctx.currentLine().insert(0, indentString);
|
||||
}
|
||||
ctx.pendingIndent = false;
|
||||
}
|
||||
|
||||
protected void _doProcess(final TraceNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
boolean __hasContent = this._hasContent(node, ctx);
|
||||
if (__hasContent) {
|
||||
final AbstractTraceRegion beforeRegion = ctx.currentRegion;
|
||||
ILocationData _sourceLocation = node.getSourceLocation();
|
||||
final GeneratorNodeProcessor.CompletableTraceRegion newRegion = new GeneratorNodeProcessor.CompletableTraceRegion(false, _sourceLocation, beforeRegion);
|
||||
final int offset = ctx.contentLength();
|
||||
final int startLineNumber = ctx.currentLineNumber();
|
||||
try {
|
||||
ctx.currentRegion = newRegion;
|
||||
this.doProcessChildren(node, ctx);
|
||||
} finally {
|
||||
if ((beforeRegion != null)) {
|
||||
ctx.currentRegion = beforeRegion;
|
||||
}
|
||||
int _contentLength = ctx.contentLength();
|
||||
int _minus = (_contentLength - offset);
|
||||
newRegion.complete(offset, _minus, startLineNumber, ctx.currentLineNumber());
|
||||
}
|
||||
ctx.currentLine().insert(0, indentString);
|
||||
ctx.pendingIndent = false;
|
||||
}
|
||||
ctx.currentLine().append(node.getText());
|
||||
}
|
||||
|
||||
protected void _doProcess(final CompositeGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
this.doProcessChildren(node, ctx);
|
||||
}
|
||||
|
||||
protected void _doProcess(final TraceNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
final AbstractTraceRegion beforeRegion = ctx.currentRegion;
|
||||
ILocationData _sourceLocation = node.getSourceLocation();
|
||||
final GeneratorNodeProcessor.CompletableTraceRegion newRegion = new GeneratorNodeProcessor.CompletableTraceRegion(false, _sourceLocation, beforeRegion);
|
||||
final int offset = ctx.contentLength();
|
||||
final int startLineNumber = ctx.currentLineNumber();
|
||||
try {
|
||||
ctx.currentRegion = newRegion;
|
||||
this.doProcessChildren(node, ctx);
|
||||
} finally {
|
||||
if ((beforeRegion != null)) {
|
||||
ctx.currentRegion = beforeRegion;
|
||||
}
|
||||
int _contentLength = ctx.contentLength();
|
||||
int _minus = (_contentLength - offset);
|
||||
newRegion.complete(offset, _minus, startLineNumber, ctx.currentLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
protected void doProcessChildren(final CompositeGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
List<IGeneratorNode> _children = node.getChildren();
|
||||
for (final IGeneratorNode child : _children) {
|
||||
|
@ -362,6 +377,37 @@ public class GeneratorNodeProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean _hasContent(final CompositeGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
final Function1<IGeneratorNode, Boolean> _function = (IGeneratorNode it) -> {
|
||||
return Boolean.valueOf(this.hasContent(it, ctx));
|
||||
};
|
||||
return IterableExtensions.<IGeneratorNode>exists(node.getChildren(), _function);
|
||||
}
|
||||
|
||||
protected boolean _hasContent(final NewLineNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
return (!(node.isIfNotEmpty() && (ctx.currentLine().length() == 0)));
|
||||
}
|
||||
|
||||
protected boolean _hasContent(final TextNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
boolean _isNullOrEmpty = GeneratorNodeProcessor.isNullOrEmpty(node.getText());
|
||||
return (!_isNullOrEmpty);
|
||||
}
|
||||
|
||||
protected static boolean hasNonWhitespace(final CharSequence s) {
|
||||
for (int i = 0; (i < s.length()); i++) {
|
||||
boolean _isWhitespace = Character.isWhitespace(s.charAt(i));
|
||||
boolean _not = (!_isWhitespace);
|
||||
if (_not) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static boolean isNullOrEmpty(final CharSequence s) {
|
||||
return ((s == null) || (s.length() == 0));
|
||||
}
|
||||
|
||||
protected void doProcess(final IGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
if (node instanceof IndentNode) {
|
||||
_doProcess((IndentNode)node, ctx);
|
||||
|
@ -383,4 +429,17 @@ public class GeneratorNodeProcessor {
|
|||
Arrays.<Object>asList(node, ctx).toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasContent(final IGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
|
||||
if (node instanceof CompositeGeneratorNode) {
|
||||
return _hasContent((CompositeGeneratorNode)node, ctx);
|
||||
} else if (node instanceof NewLineNode) {
|
||||
return _hasContent((NewLineNode)node, ctx);
|
||||
} else if (node instanceof TextNode) {
|
||||
return _hasContent((TextNode)node, ctx);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled parameter types: " +
|
||||
Arrays.<Object>asList(node, ctx).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,7 @@ import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
|
|||
import org.eclipse.xtext.xbase.lib.Pure;
|
||||
|
||||
/**
|
||||
* An indent node prepends the indentation string to each line that is generated through its children
|
||||
* (including the first line).
|
||||
* An indent node prepends the indentation string to each line that is generated through its children.
|
||||
*
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
|
@ -22,8 +21,28 @@ import org.eclipse.xtext.xbase.lib.Pure;
|
|||
public class IndentNode extends CompositeGeneratorNode {
|
||||
private String indentationString;
|
||||
|
||||
/**
|
||||
* When this is set to {@code true}, the indentation is always inserted in the first line, otherwise it is
|
||||
* inserted only if the first line has no text preceding this node.
|
||||
*/
|
||||
private boolean indentImmediately;
|
||||
|
||||
/**
|
||||
* When this is set to {@code true}, all lines are indented, otherwise only lines with text content are indented.
|
||||
*/
|
||||
private boolean indentEmptyLines;
|
||||
|
||||
public IndentNode(final String indentationString) {
|
||||
this(indentationString, true, false);
|
||||
}
|
||||
|
||||
public IndentNode(final String indentationString, final boolean indentImmediately, final boolean indentEmptyLines) {
|
||||
if ((indentationString == null)) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.indentationString = indentationString;
|
||||
this.indentImmediately = indentImmediately;
|
||||
this.indentEmptyLines = indentEmptyLines;
|
||||
}
|
||||
|
||||
@Pure
|
||||
|
@ -34,4 +53,22 @@ public class IndentNode extends CompositeGeneratorNode {
|
|||
public void setIndentationString(final String indentationString) {
|
||||
this.indentationString = indentationString;
|
||||
}
|
||||
|
||||
@Pure
|
||||
public boolean isIndentImmediately() {
|
||||
return this.indentImmediately;
|
||||
}
|
||||
|
||||
public void setIndentImmediately(final boolean indentImmediately) {
|
||||
this.indentImmediately = indentImmediately;
|
||||
}
|
||||
|
||||
@Pure
|
||||
public boolean isIndentEmptyLines() {
|
||||
return this.indentEmptyLines;
|
||||
}
|
||||
|
||||
public void setIndentEmptyLines(final boolean indentEmptyLines) {
|
||||
this.indentEmptyLines = indentEmptyLines;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,13 +23,19 @@ import org.eclipse.xtext.xbase.lib.Pure;
|
|||
public class NewLineNode implements IGeneratorNode {
|
||||
private String lineDelimiter;
|
||||
|
||||
/**
|
||||
* When this is set to {@code true}, the preceding line is removed if it contains only whitespace.
|
||||
*/
|
||||
private boolean ifNotEmpty;
|
||||
|
||||
public NewLineNode(final String lineDelimiter) {
|
||||
this.lineDelimiter = lineDelimiter;
|
||||
this(lineDelimiter, false);
|
||||
}
|
||||
|
||||
public NewLineNode(final String lineDelimiter, final boolean ifNotEmpty) {
|
||||
if ((lineDelimiter == null)) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.lineDelimiter = lineDelimiter;
|
||||
this.ifNotEmpty = ifNotEmpty;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
package org.eclipse.xtext.generator.trace.node;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.util.Collections;
|
||||
|
@ -50,14 +49,8 @@ public class TemplateNode extends CompositeGeneratorNode implements StringConcat
|
|||
boolean _greaterThan = (_length > 0);
|
||||
if (_greaterThan) {
|
||||
final CompositeGeneratorNode before = this.currentParent;
|
||||
final IGeneratorNode lastChild = IterableExtensions.<IGeneratorNode>last(before.getChildren());
|
||||
if (((lastChild instanceof TextNode) && Objects.equal(((TextNode) lastChild).getText(), indentation))) {
|
||||
int _size = before.getChildren().size();
|
||||
int _minus = (_size - 1);
|
||||
before.getChildren().remove(_minus);
|
||||
}
|
||||
try {
|
||||
IndentNode _indentNode = new IndentNode(indentation);
|
||||
IndentNode _indentNode = new IndentNode(indentation, false, true);
|
||||
this.currentParent = _indentNode;
|
||||
List<IGeneratorNode> _children = before.getChildren();
|
||||
_children.add(this.currentParent);
|
||||
|
@ -137,9 +130,9 @@ public class TemplateNode extends CompositeGeneratorNode implements StringConcat
|
|||
{
|
||||
final IGeneratorNode node = this.currentParent.getChildren().get(i);
|
||||
if ((node instanceof TextNode)) {
|
||||
int _length = ((TextNode)node).getText().toString().trim().length();
|
||||
boolean _tripleEquals = (_length == 0);
|
||||
if (_tripleEquals) {
|
||||
boolean _hasContent = TemplateNode.hasContent(((TextNode)node).getText());
|
||||
boolean _not = (!_hasContent);
|
||||
if (_not) {
|
||||
this.currentParent.getChildren().remove(i);
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +141,17 @@ public class TemplateNode extends CompositeGeneratorNode implements StringConcat
|
|||
this.append(object, indentation);
|
||||
}
|
||||
|
||||
protected static boolean hasContent(final CharSequence s) {
|
||||
for (int i = 0; (i < s.length()); i++) {
|
||||
boolean _isWhitespace = Character.isWhitespace(s.charAt(i));
|
||||
boolean _not = (!_isWhitespace);
|
||||
if (_not) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newLine() {
|
||||
this.nodeFactory.appendNewLine(this.currentParent);
|
||||
|
|
Loading…
Reference in a new issue