diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/generator/GrammarAccessExtensions2Test.java b/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/generator/GrammarAccessExtensions2Test.java index 15444e733..ad5c8043a 100644 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/generator/GrammarAccessExtensions2Test.java +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/generator/GrammarAccessExtensions2Test.java @@ -70,19 +70,20 @@ public class GrammarAccessExtensions2Test { " | '?:';\n"; String expected = "//OpOther:" + NL + - "// '->'" + NL + - "// | '..<'" + NL + - "// | '>' '..'" + NL + - "// | '..'" + NL + - "// | '=>'" + NL + - "// | '>' (=> ('>' '>') | '>') | '<' (=> ('<' '<') | '<' | '=>') | '<>'" + NL + - "// | '?:';"; + "// '->'" + NL + + "// | '..<'" + NL + + "// | '>' '..'" + NL + + "// | '..'" + NL + + "// | '=>'" + NL + + "// | '>' (=>('>' '>') | '>')" + NL + + "// | '<' (=>('<' '<') | '<' | '=>')" + NL + + "// | '<>'" + NL + + "// | '?:';"; firstRuleIsConvertedTo(grammar, expected); } @Test public void testGrammarFragmentToString2() throws Exception { - String NL = System.lineSeparator(); String grammar = "grammar org.xtext.example.mydsl.MyDsl hidden (ML_COMMENT)\n" + "import 'http://www.eclipse.org/emf/2002/Ecore' as ecore\n" + @@ -91,8 +92,7 @@ public class GrammarAccessExtensions2Test { "terminal ML_COMMENT: '/*'(!'*')->'*/';\n" + "terminal ID: '^'?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;"; String expected = - "//terminal ML_COMMENT:" + NL - + "// '/*' !'*'->'*/';"; + "//terminal ML_COMMENT: '/*'(!'*')->'*/';"; secondRuleIsConvertedTo(grammar, expected); } diff --git a/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.xtend b/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.xtend index ba96d8ae5..4667931c0 100644 --- a/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.xtend +++ b/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.xtend @@ -8,9 +8,7 @@ *******************************************************************************/ package org.eclipse.xtext.xtext.generator.grammarAccess -import com.google.common.collect.Maps import com.google.inject.Binder -import com.google.inject.Guice import com.google.inject.Inject import java.util.ArrayList import java.util.List @@ -29,16 +27,17 @@ import org.eclipse.xtext.Grammar import org.eclipse.xtext.GrammarUtil import org.eclipse.xtext.Group import org.eclipse.xtext.Keyword +import org.eclipse.xtext.Parameter import org.eclipse.xtext.ParserRule import org.eclipse.xtext.RuleCall import org.eclipse.xtext.TypeRef import org.eclipse.xtext.UnorderedGroup import org.eclipse.xtext.XtextRuntimeModule import org.eclipse.xtext.formatting.ILineSeparatorInformation +import org.eclipse.xtext.nodemodel.util.NodeModelUtils import org.eclipse.xtext.resource.SaveOptions import org.eclipse.xtext.serializer.ISerializer import org.eclipse.xtext.xtext.RuleNames -import org.eclipse.xtext.xtext.generator.CodeConfig import org.eclipse.xtext.xtext.generator.XtextGeneratorNaming import org.eclipse.xtext.xtext.generator.model.TypeReference import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil @@ -48,7 +47,8 @@ import static extension java.lang.Character.* import static extension org.eclipse.xtext.EcoreUtil2.* import static extension org.eclipse.xtext.GrammarUtil.* import static extension org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil.* -import org.eclipse.xtext.Parameter +import com.google.common.base.Strings +import org.eclipse.xtext.xtext.generator.CodeConfig /** * This API can be used by other templates to generate code @@ -66,11 +66,9 @@ class GrammarAccessExtensions { "\t" -> "tab", "\\" -> "backslash" }; - val Map xtextSerializerByLineDelimiter = Maps.newHashMapWithExpectedSize(2) - - @Inject CodeConfig codeConfig @Inject extension XtextGeneratorNaming + @Inject CodeConfig codeConfig /** * Returns a reference to the GrammarAccess implementation for a grammar. @@ -396,8 +394,59 @@ class GrammarAccessExtensions { } } - def String grammarFragmentToString(EObject ele, String prefix) { - return serializer.grammarFragmentToString(ele, prefix) + def String grammarFragmentToString(EObject object, String prefix) { + val node = NodeModelUtils.findActualNodeFor(object) + if (node === null) { + if (object instanceof RuleCall) + if (object?.rule?.name !== null) + return process(object.rule.name, prefix) + return "" + } else { + return node.text.process(prefix) + } + } + + private def process(String input, String prefix) { + // remove leading and trailing blank lines + var lines = input.split('\\s*(\\r?\\n)') + var first = 0 + while (isBlank(lines.get(first))) + first++ + var last = lines.length - 1 + while (isBlank(lines.get(last))) + last-- + lines = lines.subList(first, last + 1) + // just one line, trim it and be done + if (lines.size() == 1) + return prefix + lines.get(0).trim(); + // remove common whitespace (e.g. leading blanks) and add prefix to each line + val commonWhitespace = commonLeadingWhitespace(lines) + for (var n=0; n lines) { + if(lines.size() < 2) return ""; + // determine common prefix while ignoring blank lines + var current = Strings.repeat(" ", lines.get(0).replaceAll("\t", " ").length()) + for (var i = 0; i < lines.length; i++) { + val next = lines.get(i).replaceAll("\t", " "); + if (!isBlank(next)) + current = Strings.commonPrefix(current, next); + } + // replace blank lines with prefix to handle empty lines gracefully + for (var i = 0; i < lines.length; i++) { + val next = lines.get(i).replaceAll("\t", " "); + if (isBlank(next)) + lines.set(i, current) + } + return current; } /** @@ -546,18 +595,6 @@ class GrammarAccessExtensions { } } - private def ISerializer getSerializer() { - val delimiter = codeConfig.lineDelimiter - var result = xtextSerializerByLineDelimiter.get(delimiter) - if (result !== null) { - return result - } - val injector = Guice.createInjector(new LineSeparatorModule[delimiter]) - result = injector.getInstance(ISerializer) - xtextSerializerByLineDelimiter.put(delimiter, result) - return result; - } - @FinalFieldsConstructor protected static class LineSeparatorModule extends XtextRuntimeModule { diff --git a/org.eclipse.xtext.xtext.generator/xtend-gen/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.java b/org.eclipse.xtext.xtext.generator/xtend-gen/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.java index 6de7e80ac..835c62aeb 100644 --- a/org.eclipse.xtext.xtext.generator/xtend-gen/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.java +++ b/org.eclipse.xtext.xtext.generator/xtend-gen/org/eclipse/xtext/xtext/generator/grammarAccess/GrammarAccessExtensions.java @@ -9,12 +9,10 @@ package org.eclipse.xtext.xtext.generator.grammarAccess; import com.google.common.base.Objects; +import com.google.common.base.Strings; import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; import com.google.inject.Binder; -import com.google.inject.Guice; import com.google.inject.Inject; -import com.google.inject.Injector; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -45,10 +43,13 @@ import org.eclipse.xtext.TypeRef; import org.eclipse.xtext.UnorderedGroup; import org.eclipse.xtext.XtextRuntimeModule; import org.eclipse.xtext.formatting.ILineSeparatorInformation; +import org.eclipse.xtext.nodemodel.ICompositeNode; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.SaveOptions; import org.eclipse.xtext.serializer.ISerializer; import org.eclipse.xtext.service.CompoundModule; import org.eclipse.xtext.xbase.lib.CollectionLiterals; +import org.eclipse.xtext.xbase.lib.Conversions; import org.eclipse.xtext.xbase.lib.Exceptions; import org.eclipse.xtext.xbase.lib.Extension; import org.eclipse.xtext.xbase.lib.Functions.Function1; @@ -93,15 +94,13 @@ public class GrammarAccessExtensions { private static Map SPECIAL_CHARS = Collections.unmodifiableMap(CollectionLiterals.newHashMap(Pair.of("\b", "backspace"), Pair.of("\f", "formFeed"), Pair.of("\n", "lineFeed"), Pair.of("\r", "carriageReturn"), Pair.of("\t", "tab"), Pair.of("\\", "backslash"))); - private final Map xtextSerializerByLineDelimiter = Maps.newHashMapWithExpectedSize(2); - - @Inject - private CodeConfig codeConfig; - @Inject @Extension private XtextGeneratorNaming _xtextGeneratorNaming; + @Inject + private CodeConfig codeConfig; + /** * Returns a reference to the GrammarAccess implementation for a grammar. */ @@ -600,8 +599,91 @@ public class GrammarAccessExtensions { return _switchResult; } - public String grammarFragmentToString(final EObject ele, final String prefix) { - return GrammarAccessExtensions.grammarFragmentToString(this.getSerializer(), ele, prefix); + public String grammarFragmentToString(final EObject object, final String prefix) { + final ICompositeNode node = NodeModelUtils.findActualNodeFor(object); + if ((node == null)) { + if ((object instanceof RuleCall)) { + AbstractRule _rule = null; + if (((RuleCall)object)!=null) { + _rule=((RuleCall)object).getRule(); + } + String _name = null; + if (_rule!=null) { + _name=_rule.getName(); + } + boolean _tripleNotEquals = (_name != null); + if (_tripleNotEquals) { + return this.process(((RuleCall)object).getRule().getName(), prefix); + } + } + return ""; + } else { + return this.process(node.getText(), prefix); + } + } + + private String process(final String input, final String prefix) { + String[] lines = input.split("\\s*(\\r?\\n)"); + int first = 0; + while (this.isBlank(lines[first])) { + first++; + } + int _length = lines.length; + int last = (_length - 1); + while (this.isBlank(lines[last])) { + last--; + } + final String[] _converted_lines = (String[])lines; + lines = ((String[])Conversions.unwrapArray(((List)Conversions.doWrapArray(_converted_lines)).subList(first, (last + 1)), String.class)); + final String[] _converted_lines_1 = (String[])lines; + int _size = ((List)Conversions.doWrapArray(_converted_lines_1)).size(); + boolean _equals = (_size == 1); + if (_equals) { + String _trim = (lines[0]).trim(); + return (prefix + _trim); + } + final String[] _converted_lines_2 = (String[])lines; + final String commonWhitespace = this.commonLeadingWhitespace(((List)Conversions.doWrapArray(_converted_lines_2))); + for (int n = 0; (n < lines.length); n++) { + String _substring = (lines[n]).replaceAll("\t", " ").substring(commonWhitespace.length()); + String _plus = (prefix + _substring); + lines[n] = _plus; + } + final String[] _converted_lines_3 = (String[])lines; + return IterableExtensions.join(((Iterable)Conversions.doWrapArray(_converted_lines_3)), this.codeConfig.getLineDelimiter()); + } + + private boolean isBlank(final String line) { + return line.trim().isEmpty(); + } + + private String commonLeadingWhitespace(final List lines) { + int _size = lines.size(); + boolean _lessThan = (_size < 2); + if (_lessThan) { + return ""; + } + String current = Strings.repeat(" ", lines.get(0).replaceAll("\t", " ").length()); + for (int i = 0; (i < ((Object[])Conversions.unwrapArray(lines, Object.class)).length); i++) { + { + final String next = lines.get(i).replaceAll("\t", " "); + boolean _isBlank = this.isBlank(next); + boolean _not = (!_isBlank); + if (_not) { + current = Strings.commonPrefix(current, next); + } + } + } + for (int i = 0; (i < ((Object[])Conversions.unwrapArray(lines, Object.class)).length); i++) { + { + final String next = lines.get(i).replaceAll("\t", " "); + boolean _isBlank = this.isBlank(next); + if (_isBlank) { + lines.set(i, current); + } + } + } + return current; } /** @@ -862,22 +944,6 @@ public class GrammarAccessExtensions { return _switchResult; } - private ISerializer getSerializer() { - final String delimiter = this.codeConfig.getLineDelimiter(); - ISerializer result = this.xtextSerializerByLineDelimiter.get(delimiter); - if ((result != null)) { - return result; - } - final ILineSeparatorInformation _function = () -> { - return delimiter; - }; - GrammarAccessExtensions.LineSeparatorModule _lineSeparatorModule = new GrammarAccessExtensions.LineSeparatorModule(_function); - final Injector injector = Guice.createInjector(_lineSeparatorModule); - result = injector.getInstance(ISerializer.class); - this.xtextSerializerByLineDelimiter.put(delimiter, result); - return result; - } - public String grammarElementIdentifier(final EObject it) { if (it instanceof AbstractElement) { return _grammarElementIdentifier((AbstractElement)it);