mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-15 08:18:55 +00:00
[#1626] Use node model to generate comments with original format.
Signed-off-by: Arne Deutsch <Arne.Deutsch@itemis.de>
This commit is contained in:
parent
b34bbe849a
commit
8ab3672c97
3 changed files with 160 additions and 57 deletions
|
@ -70,19 +70,20 @@ public class GrammarAccessExtensions2Test {
|
||||||
" | '?:';\n";
|
" | '?:';\n";
|
||||||
String expected =
|
String expected =
|
||||||
"//OpOther:" + NL +
|
"//OpOther:" + NL +
|
||||||
"// '->'" + NL +
|
"// '->'" + NL +
|
||||||
"// | '..<'" + NL +
|
"// | '..<'" + NL +
|
||||||
"// | '>' '..'" + NL +
|
"// | '>' '..'" + NL +
|
||||||
"// | '..'" + NL +
|
"// | '..'" + NL +
|
||||||
"// | '=>'" + NL +
|
"// | '=>'" + NL +
|
||||||
"// | '>' (=> ('>' '>') | '>') | '<' (=> ('<' '<') | '<' | '=>') | '<>'" + NL +
|
"// | '>' (=>('>' '>') | '>')" + NL +
|
||||||
"// | '?:';";
|
"// | '<' (=>('<' '<') | '<' | '=>')" + NL +
|
||||||
|
"// | '<>'" + NL +
|
||||||
|
"// | '?:';";
|
||||||
firstRuleIsConvertedTo(grammar, expected);
|
firstRuleIsConvertedTo(grammar, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGrammarFragmentToString2() throws Exception {
|
public void testGrammarFragmentToString2() throws Exception {
|
||||||
String NL = System.lineSeparator();
|
|
||||||
String grammar =
|
String grammar =
|
||||||
"grammar org.xtext.example.mydsl.MyDsl hidden (ML_COMMENT)\n" +
|
"grammar org.xtext.example.mydsl.MyDsl hidden (ML_COMMENT)\n" +
|
||||||
"import 'http://www.eclipse.org/emf/2002/Ecore' as ecore\n" +
|
"import 'http://www.eclipse.org/emf/2002/Ecore' as ecore\n" +
|
||||||
|
@ -91,8 +92,7 @@ public class GrammarAccessExtensions2Test {
|
||||||
"terminal ML_COMMENT: '/*'(!'*')->'*/';\n" +
|
"terminal ML_COMMENT: '/*'(!'*')->'*/';\n" +
|
||||||
"terminal ID: '^'?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;";
|
"terminal ID: '^'?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;";
|
||||||
String expected =
|
String expected =
|
||||||
"//terminal ML_COMMENT:" + NL
|
"//terminal ML_COMMENT: '/*'(!'*')->'*/';";
|
||||||
+ "// '/*' !'*'->'*/';";
|
|
||||||
secondRuleIsConvertedTo(grammar, expected);
|
secondRuleIsConvertedTo(grammar, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.xtext.xtext.generator.grammarAccess
|
package org.eclipse.xtext.xtext.generator.grammarAccess
|
||||||
|
|
||||||
import com.google.common.collect.Maps
|
|
||||||
import com.google.inject.Binder
|
import com.google.inject.Binder
|
||||||
import com.google.inject.Guice
|
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.List
|
import java.util.List
|
||||||
|
@ -29,16 +27,17 @@ import org.eclipse.xtext.Grammar
|
||||||
import org.eclipse.xtext.GrammarUtil
|
import org.eclipse.xtext.GrammarUtil
|
||||||
import org.eclipse.xtext.Group
|
import org.eclipse.xtext.Group
|
||||||
import org.eclipse.xtext.Keyword
|
import org.eclipse.xtext.Keyword
|
||||||
|
import org.eclipse.xtext.Parameter
|
||||||
import org.eclipse.xtext.ParserRule
|
import org.eclipse.xtext.ParserRule
|
||||||
import org.eclipse.xtext.RuleCall
|
import org.eclipse.xtext.RuleCall
|
||||||
import org.eclipse.xtext.TypeRef
|
import org.eclipse.xtext.TypeRef
|
||||||
import org.eclipse.xtext.UnorderedGroup
|
import org.eclipse.xtext.UnorderedGroup
|
||||||
import org.eclipse.xtext.XtextRuntimeModule
|
import org.eclipse.xtext.XtextRuntimeModule
|
||||||
import org.eclipse.xtext.formatting.ILineSeparatorInformation
|
import org.eclipse.xtext.formatting.ILineSeparatorInformation
|
||||||
|
import org.eclipse.xtext.nodemodel.util.NodeModelUtils
|
||||||
import org.eclipse.xtext.resource.SaveOptions
|
import org.eclipse.xtext.resource.SaveOptions
|
||||||
import org.eclipse.xtext.serializer.ISerializer
|
import org.eclipse.xtext.serializer.ISerializer
|
||||||
import org.eclipse.xtext.xtext.RuleNames
|
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.XtextGeneratorNaming
|
||||||
import org.eclipse.xtext.xtext.generator.model.TypeReference
|
import org.eclipse.xtext.xtext.generator.model.TypeReference
|
||||||
import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil
|
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.EcoreUtil2.*
|
||||||
import static extension org.eclipse.xtext.GrammarUtil.*
|
import static extension org.eclipse.xtext.GrammarUtil.*
|
||||||
import static extension org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil.*
|
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
|
* This API can be used by other templates to generate code
|
||||||
|
@ -66,11 +66,9 @@ class GrammarAccessExtensions {
|
||||||
"\t" -> "tab",
|
"\t" -> "tab",
|
||||||
"\\" -> "backslash"
|
"\\" -> "backslash"
|
||||||
};
|
};
|
||||||
val Map<String, ISerializer> xtextSerializerByLineDelimiter = Maps.newHashMapWithExpectedSize(2)
|
|
||||||
|
|
||||||
@Inject CodeConfig codeConfig
|
|
||||||
|
|
||||||
@Inject extension XtextGeneratorNaming
|
@Inject extension XtextGeneratorNaming
|
||||||
|
@Inject CodeConfig codeConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the GrammarAccess implementation for a grammar.
|
* Returns a reference to the GrammarAccess implementation for a grammar.
|
||||||
|
@ -396,8 +394,59 @@ class GrammarAccessExtensions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def String grammarFragmentToString(EObject ele, String prefix) {
|
def String grammarFragmentToString(EObject object, String prefix) {
|
||||||
return serializer.grammarFragmentToString(ele, 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.length; n++)
|
||||||
|
lines.set(n, prefix + lines.get(n).replaceAll("\t", " ").substring(commonWhitespace.length))
|
||||||
|
// generate result by joining lines
|
||||||
|
return lines.join(codeConfig.lineDelimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def isBlank(String line) {
|
||||||
|
return line.trim.empty
|
||||||
|
}
|
||||||
|
|
||||||
|
private def String commonLeadingWhitespace(List<String> 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
|
@FinalFieldsConstructor
|
||||||
protected static class LineSeparatorModule extends XtextRuntimeModule {
|
protected static class LineSeparatorModule extends XtextRuntimeModule {
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,10 @@
|
||||||
package org.eclipse.xtext.xtext.generator.grammarAccess;
|
package org.eclipse.xtext.xtext.generator.grammarAccess;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.Guice;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Injector;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -45,10 +43,13 @@ import org.eclipse.xtext.TypeRef;
|
||||||
import org.eclipse.xtext.UnorderedGroup;
|
import org.eclipse.xtext.UnorderedGroup;
|
||||||
import org.eclipse.xtext.XtextRuntimeModule;
|
import org.eclipse.xtext.XtextRuntimeModule;
|
||||||
import org.eclipse.xtext.formatting.ILineSeparatorInformation;
|
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.resource.SaveOptions;
|
||||||
import org.eclipse.xtext.serializer.ISerializer;
|
import org.eclipse.xtext.serializer.ISerializer;
|
||||||
import org.eclipse.xtext.service.CompoundModule;
|
import org.eclipse.xtext.service.CompoundModule;
|
||||||
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
|
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.Exceptions;
|
||||||
import org.eclipse.xtext.xbase.lib.Extension;
|
import org.eclipse.xtext.xbase.lib.Extension;
|
||||||
import org.eclipse.xtext.xbase.lib.Functions.Function1;
|
import org.eclipse.xtext.xbase.lib.Functions.Function1;
|
||||||
|
@ -93,15 +94,13 @@ public class GrammarAccessExtensions {
|
||||||
|
|
||||||
private static Map<String, String> SPECIAL_CHARS = Collections.<String, String>unmodifiableMap(CollectionLiterals.<String, String>newHashMap(Pair.<String, String>of("\b", "backspace"), Pair.<String, String>of("\f", "formFeed"), Pair.<String, String>of("\n", "lineFeed"), Pair.<String, String>of("\r", "carriageReturn"), Pair.<String, String>of("\t", "tab"), Pair.<String, String>of("\\", "backslash")));
|
private static Map<String, String> SPECIAL_CHARS = Collections.<String, String>unmodifiableMap(CollectionLiterals.<String, String>newHashMap(Pair.<String, String>of("\b", "backspace"), Pair.<String, String>of("\f", "formFeed"), Pair.<String, String>of("\n", "lineFeed"), Pair.<String, String>of("\r", "carriageReturn"), Pair.<String, String>of("\t", "tab"), Pair.<String, String>of("\\", "backslash")));
|
||||||
|
|
||||||
private final Map<String, ISerializer> xtextSerializerByLineDelimiter = Maps.<String, ISerializer>newHashMapWithExpectedSize(2);
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private CodeConfig codeConfig;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Extension
|
@Extension
|
||||||
private XtextGeneratorNaming _xtextGeneratorNaming;
|
private XtextGeneratorNaming _xtextGeneratorNaming;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CodeConfig codeConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the GrammarAccess implementation for a grammar.
|
* Returns a reference to the GrammarAccess implementation for a grammar.
|
||||||
*/
|
*/
|
||||||
|
@ -600,8 +599,91 @@ public class GrammarAccessExtensions {
|
||||||
return _switchResult;
|
return _switchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String grammarFragmentToString(final EObject ele, final String prefix) {
|
public String grammarFragmentToString(final EObject object, final String prefix) {
|
||||||
return GrammarAccessExtensions.grammarFragmentToString(this.getSerializer(), ele, 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<String>)Conversions.doWrapArray(_converted_lines)).subList(first, (last + 1)), String.class));
|
||||||
|
final String[] _converted_lines_1 = (String[])lines;
|
||||||
|
int _size = ((List<String>)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<String>)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<String> 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;
|
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.<ISerializer>getInstance(ISerializer.class);
|
|
||||||
this.xtextSerializerByLineDelimiter.put(delimiter, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String grammarElementIdentifier(final EObject it) {
|
public String grammarElementIdentifier(final EObject it) {
|
||||||
if (it instanceof AbstractElement) {
|
if (it instanceof AbstractElement) {
|
||||||
return _grammarElementIdentifier((AbstractElement)it);
|
return _grammarElementIdentifier((AbstractElement)it);
|
||||||
|
|
Loading…
Reference in a new issue