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";
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String, ISerializer> 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.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
|
||||
protected static class LineSeparatorModule extends XtextRuntimeModule {
|
||||
|
||||
|
|
|
@ -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<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
|
||||
@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<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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (it instanceof AbstractElement) {
|
||||
return _grammarElementIdentifier((AbstractElement)it);
|
||||
|
|
Loading…
Reference in a new issue