diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/formatter/XtextFormatterExpected.xtext b/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/formatter/XtextFormatterExpected.xtext index 94856b379e..a9f86ad858 100755 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/formatter/XtextFormatterExpected.xtext +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/formatter/XtextFormatterExpected.xtext @@ -47,4 +47,4 @@ terminal ML_COMMENT2: @Override terminal ID: - super; \ No newline at end of file + super; diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/reconstr/XtextGrammarReconcilationTest.java b/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/reconstr/XtextGrammarReconcilationTest.java index 2590802604..69c1812548 100755 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/reconstr/XtextGrammarReconcilationTest.java +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/parsetree/reconstr/XtextGrammarReconcilationTest.java @@ -43,12 +43,12 @@ public void setUp() throws Exception { // check assertFalse(model.equals(result)); - String expectedModel = LineDelimiters.toPlatform("grammar foo with org.eclipse.xtext.common.Terminals\n\nHONOLULU:\n name=ID;"); + String expectedModel = LineDelimiters.toPlatform("grammar foo with org.eclipse.xtext.common.Terminals\n\nHONOLULU:\n name=ID;\n"); assertEquals(expectedModel, result); } @Test public void testSelf() { - Grammar g = getGrammarAccess().getGrammar(); + Grammar g = load(URI.createURI("classpath:/org/eclipse/xtext/Xtext.xtext")); for (AbstractRule r : g.getRules()) { // AbstractRule r = GrammarUtil.findRuleForName(g, "GrammarID"); // System.out.println("serializing :" + r.getName()); diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/XtextSerializerTest.java b/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/XtextSerializerTest.java index 1d720a2773..d055f077b3 100644 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/XtextSerializerTest.java +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/serializer/XtextSerializerTest.java @@ -8,28 +8,30 @@ *******************************************************************************/ package org.eclipse.xtext.serializer; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.Grammar; import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.Group; -import org.eclipse.xtext.RuleCall; -import org.eclipse.xtext.TerminalRule; import org.eclipse.xtext.XtextStandaloneSetup; -import org.eclipse.xtext.common.services.TerminalsGrammarAccess; -import org.eclipse.xtext.grammarinheritance.services.BaseInheritanceTestLanguageGrammarAccess; -import org.eclipse.xtext.grammarinheritance.services.InheritanceTestLanguageGrammarAccess; +import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.resource.XtextResourceSet; -import org.eclipse.xtext.service.GrammarProvider; -import org.eclipse.xtext.services.XtextGrammarAccess; import org.eclipse.xtext.testing.serializer.SerializerTestHelper; import org.eclipse.xtext.tests.AbstractXtextTests; +import org.eclipse.xtext.util.Pair; +import org.eclipse.xtext.util.Tuples; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; +import com.google.common.collect.Lists; import com.google.inject.Inject; -import com.google.inject.Provider; /** * @author Moritz Eysholdt - Initial contribution and API @@ -38,11 +40,6 @@ public class XtextSerializerTest extends AbstractXtextTests { @Inject private SerializerTestHelper tester; - private Grammar load(URI uri) { - XtextResourceSet rs = new XtextResourceSet(); - return (Grammar) rs.getResource(uri, true).getContents().get(0); - } - @Override public void setUp() throws Exception { super.setUp(); @@ -58,48 +55,14 @@ public void testXtextXtextWithNM() throws Exception { } @Test - @Ignore("To be done") - public void testXtextXtextWithoutNM() throws Exception { + public void testGroup() { Grammar grammar = load(URI.createURI("classpath:/org/eclipse/xtext/Xtext.xtext")); - tester.assertSerializeWithoutNodeModel(grammar); + AbstractRule rule = GrammarUtil.findRuleForName(grammar, "org.eclipse.xtext.Xtext.Grammar"); + Group cGroup_2 = (Group) rule.eContents().get(1).eContents().get(2); + detachNodeModel(grammar); + String string = get(ISerializer.class).serialize(cGroup_2); + Assert.assertEquals("(\"with\" usedGrammars+=[Grammar|GrammarID] (\",\" usedGrammars+=[Grammar|GrammarID])*)?", + string); } - @Test - public void testGroup() { - XtextGrammarAccess grammarAccess = get(XtextGrammarAccess.class); - String string = get(ISerializer.class).serialize(grammarAccess.getGrammarAccess().getGroup_2()); - Assert.assertEquals("(\"with\" usedGrammars+=[Grammar|GrammarID] (\",\" usedGrammars+=[Grammar|GrammarID])*)?", string); - } - - @Test - public void testFQNInSuper_01() { - GrammarProvider grammarProvider = new GrammarProvider("org.eclipse.xtext.grammarinheritance.InheritanceTestLanguage", new Provider() { - @Override - public XtextResourceSet get() { - return XtextSerializerTest.this.get(XtextResourceSet.class); - } - }); - grammarProvider.setClassLoader(getClass().getClassLoader()); - TerminalsGrammarAccess gaTerminals = new TerminalsGrammarAccess(grammarProvider); - BaseInheritanceTestLanguageGrammarAccess gaBaseInheritanceTestLanguage = new BaseInheritanceTestLanguageGrammarAccess(grammarProvider, gaTerminals); - InheritanceTestLanguageGrammarAccess grammarAccess = new InheritanceTestLanguageGrammarAccess(grammarProvider, gaBaseInheritanceTestLanguage, gaTerminals); - String string = get(ISerializer.class).serialize(grammarAccess.getFQNRule().getAlternatives()); - Assert.assertEquals("ID (\".\" ID)*", string); - } - - @Ignore("Serialization does not have the correct context information") - @Test - public void testFQNInSuper_02() { - Grammar grammar = load(URI.createURI("classpath:/org/eclipse/xtext/grammarinheritance/InheritanceTestLanguage.xtext")); - AbstractRule rule = GrammarUtil.findRuleForName(grammar, "FQN"); - Assert.assertNotNull(rule); - Group group = (Group) rule.getAlternatives(); - RuleCall ruleCall = (RuleCall) group.getElements().get(0); - TerminalRule id = (TerminalRule) ruleCall.getRule(); - Assert.assertSame(grammar, GrammarUtil.getGrammar(id)); - String string = get(ISerializer.class).serialize(rule.getAlternatives()); - Assert.assertEquals("ID (\".\" ID)*", string); - // currently wrong result is - Assert.assertEquals("super::ID (\".\" super::ID)*", string); - } } diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/tests/AbstractXtextTests.java b/org.eclipse.xtext.tests/src/org/eclipse/xtext/tests/AbstractXtextTests.java index 51dc1424b5..99645bfe00 100644 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/tests/AbstractXtextTests.java +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/tests/AbstractXtextTests.java @@ -13,12 +13,16 @@ import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.List; +import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource.Diagnostic; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.Constants; +import org.eclipse.xtext.Grammar; import org.eclipse.xtext.IGrammarAccess; import org.eclipse.xtext.ISetup; import org.eclipse.xtext.conversion.IValueConverterService; @@ -42,11 +46,14 @@ import org.eclipse.xtext.testing.serializer.SerializerTestHelper; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.util.LazyStringInputStream; +import org.eclipse.xtext.util.Pair; +import org.eclipse.xtext.util.Tuples; import org.junit.After; import org.junit.Assert; import org.junit.Before; import com.google.common.base.Joiner; +import com.google.common.collect.Lists; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; @@ -390,6 +397,30 @@ protected String readFileIntoString(String filePath) throws IOException { throw new IllegalStateException("May not happen, but helps to suppress false positives in eclipse' control flow analysis."); } + protected Grammar load(URI uri) { + XtextResourceSet rs = new XtextResourceSet(); + return (Grammar) rs.getResource(uri, true).getContents().get(0); + } + + protected List> detachNodeModel(EObject eObject) { + EcoreUtil.resolveAll(eObject); + List> result = Lists.newArrayList(); + Iterator iterator = EcoreUtil.getAllContents(eObject.eResource(), false); + while (iterator.hasNext()) { + EObject object = (EObject) iterator.next(); + Iterator adapters = object.eAdapters().iterator(); + while (adapters.hasNext()) { + Adapter adapter = adapters.next(); + if (adapter instanceof ICompositeNode) { + adapters.remove(); + result.add(Tuples.create(object, (ICompositeNode) adapter)); + break; + } + } + } + return result; + } + public static final class Keys { private static final TypeLiteral> resourceSetLiteral = new TypeLiteral>(){ }; diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/GrammarFlatteningTest.xtend b/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/GrammarFlatteningTest.xtend index 99a61e4a16..72dad93e10 100644 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/GrammarFlatteningTest.xtend +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/GrammarFlatteningTest.xtend @@ -3,7 +3,7 @@ * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. - * + * * SPDX-License-Identifier: EPL-2.0 */ package org.eclipse.xtext.xtext @@ -28,7 +28,7 @@ class GrammarFlatteningTest extends AbstractXtextTests { override Grammar getModel(String model) throws Exception { return getModel(model, false) } - + def Grammar getModel(String model, boolean dropUnreachable) throws Exception { var Grammar grammar = super.getModel(model) as Grammar var RuleNames ruleNames = RuleNames.getRuleNames(grammar, false) @@ -44,10 +44,10 @@ class GrammarFlatteningTest extends AbstractXtextTests { @Test def void test_01() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID; - ''') + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID; + ''') var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -74,17 +74,18 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_02() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID; - terminal ID: super; - ''') + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID; + terminal ID: super; + ''') var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -114,16 +115,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_03() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID | name=ID | name=STRING; - ''') + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID | name=ID | name=STRING; + ''') var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -159,16 +161,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_04() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID child=Rule?; - ''') + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID child=Rule?; + ''') var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -198,16 +201,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_05() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID (child=Rule|child=Rule+)?; - ''') + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID (child=Rule|child=Rule+)?; + ''') var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -237,16 +241,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_06() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID (child=Rule)?; - ''') + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID (child=Rule)?; + ''') var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -276,16 +281,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_07() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID (child=Rule)?; - ''', true) + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID (child=Rule)?; + ''', true) var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -312,16 +318,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } @Test def void test_08() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID =>( ->child=Rule | ->'keyword')?; - ''', true) + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID =>( ->child=Rule | ->'keyword')?; + ''', true) var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -348,16 +355,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_09() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID ->( =>child=Rule | =>'keyword')?; - ''', true) + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID ->( =>child=Rule | =>'keyword')?; + ''', true) var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -384,53 +392,54 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_10() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - ParserRuleParameters: {ParserRuleParameters} - ( '#1' scenario=Scenario1 - | '#2' scenario=Scenario1 - | '#3' scenario=Scenario2 - | '#4' scenario=Scenario2 - | =>('#5' scenario=Scenario2) - | =>('#6' scenario=Scenario2) - | '#7' scenario=Scenario3 - | '#8' scenario=Scenario3 - | '#9' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) - | '#10' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) - | '#11' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) - | '#12' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) - ) - ; - - Scenario1 returns Scenario: - first=ID - | second=ID - ; - - Scenario2 returns Scenario: - first=IdOrKeyword - ; - - Scenario3 returns Scenario: - =>first=IdOrKeyword - | second='keyword' - ; - - Scenario4 returns Scenario: - =>second=IdOrKeyword 'keyword' - ; - - IdOrKeyword: - 'keyword' - | ID - ; - ''', true) + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + ParserRuleParameters: {ParserRuleParameters} + ( '#1' scenario=Scenario1 + | '#2' scenario=Scenario1 + | '#3' scenario=Scenario2 + | '#4' scenario=Scenario2 + | =>('#5' scenario=Scenario2) + | =>('#6' scenario=Scenario2) + | '#7' scenario=Scenario3 + | '#8' scenario=Scenario3 + | '#9' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) + | '#10' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) + | '#11' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) + | '#12' (scenario=Scenario4 | scenario=Scenario2 'keyword'?) + ) + ; + + Scenario1 returns Scenario: + first=ID + | second=ID + ; + + Scenario2 returns Scenario: + first=IdOrKeyword + ; + + Scenario3 returns Scenario: + =>first=IdOrKeyword + | second='keyword' + ; + + Scenario4 returns Scenario: + =>second=IdOrKeyword 'keyword' + ; + + IdOrKeyword: + 'keyword' + | ID + ; + ''', true) var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -491,17 +500,18 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Ignore("Flattened grammar access produces bad grammar?") @Test def void test_11() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: name=ID =>( ->child=Rule | ->('a' 'b'))?; - ''', true) + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: name=ID =>( ->child=Rule | ->('a' 'b'))?; + ''', true) var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -528,16 +538,17 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } - + @Test def void test_12() throws Exception { var Grammar flattened = getModel( ''' - grammar com.foo.bar with org.eclipse.xtext.common.Terminals - generate myPack 'http://myURI' - Rule: =>(name+=ID*); - ''', true) + grammar com.foo.bar with org.eclipse.xtext.common.Terminals + generate myPack 'http://myURI' + Rule: =>(name+=ID*); + ''', true) var String serialized = getSerializer().serialize(flattened) assertEquals(''' grammar com.foo.bar hidden(RULE_WS, RULE_ML_COMMENT, RULE_SL_COMMENT) @@ -564,6 +575,7 @@ class GrammarFlatteningTest extends AbstractXtextTests { " " | "\t" | "\r" | "\n"+; terminal RULE_ANY_OTHER: - .;'''.toString, serialized) + .; + '''.toString, serialized) } } diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.xtend b/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.xtend index 278adfe6fd..d58c7a2b51 100644 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.xtend +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.xtend @@ -40,10 +40,11 @@ class XtextGrammarSerializationTest extends AbstractXtextTests { MyRule : name=ID | name=STRING - | name='name';''' + | name='name'; + ''' doTestSerialization(model, expectedModel) } - + @Test def void testArguments_01() throws Exception { val String model = ''' grammar foo with org.eclipse.xtext.common.Terminals @@ -58,10 +59,11 @@ class XtextGrammarSerializationTest extends AbstractXtextTests { generate mm "http://bar" Rule : - name=ID child=Rule;''' + name=ID child=Rule; + ''' doTestSerialization(model, expectedModel) } - + @Test def void testArguments_02() throws Exception { val String model = ''' grammar foo with org.eclipse.xtext.common.Terminals @@ -86,7 +88,8 @@ class XtextGrammarSerializationTest extends AbstractXtextTests { value3=MyParameterizedRule; MyParameterizedRule : - name=ID child=MyParameterizedRule;''' + name=ID child=MyParameterizedRule; + ''' doTestSerialization(model, expectedModel) } @@ -102,7 +105,8 @@ class XtextGrammarSerializationTest extends AbstractXtextTests { generate mm "http://bar" as fooMM StartRule returns fooMM::T: - name=ID;''' + name=ID; + ''' doTestSerialization(model, expectedModel) } @@ -118,10 +122,11 @@ class XtextGrammarSerializationTest extends AbstractXtextTests { generate mm "http://bar" as fooMM StartRule returns fooMM::T: - (name+=ID)*;''' + (name+=ID)*; + ''' doTestSerialization(model, expectedModel) } - + @Test def void testSerializationSuperCall() throws Exception { val String model = ''' grammar foo with org.eclipse.xtext.common.Terminals @@ -138,7 +143,8 @@ class XtextGrammarSerializationTest extends AbstractXtextTests { name=super::ID value=Terminals::STRING thing=STRING; terminal STRING: - super;''' + super; + ''' doTestSerialization(model, expectedModel) } @@ -175,7 +181,8 @@ class XtextGrammarSerializationTest extends AbstractXtextTests { rule=[xtext::ParserRule]; MyRule returns xtext::ParserRule: - name=ID;''' + name=ID; + ''' doTestSerialization(model, expectedModel) } diff --git a/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/GrammarFlatteningTest.java b/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/GrammarFlatteningTest.java index d3d4f903f2..06326f6673 100644 --- a/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/GrammarFlatteningTest.java +++ b/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/GrammarFlatteningTest.java @@ -112,6 +112,7 @@ public void test_01() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -184,6 +185,7 @@ public void test_02() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -266,6 +268,7 @@ public void test_03() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -336,6 +339,7 @@ public void test_04() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -406,6 +410,7 @@ public void test_05() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -476,6 +481,7 @@ public void test_06() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -540,6 +546,7 @@ public void test_07() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -604,6 +611,7 @@ public void test_08() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -668,6 +676,7 @@ public void test_09() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -727,7 +736,7 @@ public void test_10() throws Exception { _builder.append("\t"); _builder.append(" first=ID"); _builder.newLine(); - _builder.append(" "); + _builder.append("\t "); _builder.append("| second=ID"); _builder.newLine(); _builder.append(";"); @@ -894,6 +903,7 @@ public void test_10() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -959,6 +969,7 @@ public void test_11() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } @@ -1023,6 +1034,7 @@ public void test_12() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append(".;"); + _builder_1.newLine(); Assert.assertEquals(_builder_1.toString(), serialized); } } diff --git a/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.java b/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.java index e17c39b23a..2a9308a475 100644 --- a/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.java +++ b/org.eclipse.xtext.tests/xtend-gen/org/eclipse/xtext/xtext/XtextGrammarSerializationTest.java @@ -65,6 +65,7 @@ public void testParameters() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("| name=\'name\';"); + _builder_1.newLine(); final String expectedModel = _builder_1.toString(); this.doTestSerialization(model, expectedModel); } @@ -95,6 +96,7 @@ public void testArguments_01() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("name=ID child=Rule;"); + _builder_1.newLine(); final String expectedModel = _builder_1.toString(); this.doTestSerialization(model, expectedModel); } @@ -150,6 +152,7 @@ public void testArguments_02() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("name=ID child=MyParameterizedRule;"); + _builder_1.newLine(); final String expectedModel = _builder_1.toString(); this.doTestSerialization(model, expectedModel); } @@ -175,6 +178,7 @@ public void testSimpleSerialization() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("name=ID;"); + _builder_1.newLine(); final String expectedModel = _builder_1.toString(); this.doTestSerialization(model, expectedModel); } @@ -200,6 +204,7 @@ public void testSerializationWithCardinalityOverride() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("(name+=ID)*;"); + _builder_1.newLine(); final String expectedModel = _builder_1.toString(); this.doTestSerialization(model, expectedModel); } @@ -233,6 +238,7 @@ public void testSerializationSuperCall() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("super;"); + _builder_1.newLine(); final String expectedModel = _builder_1.toString(); this.doTestSerialization(model, expectedModel); } @@ -290,6 +296,7 @@ public void testMetamodelRefSerialization() throws Exception { _builder_1.newLine(); _builder_1.append("\t"); _builder_1.append("name=ID;"); + _builder_1.newLine(); final String expectedModel = _builder_1.toString(); this.doTestSerialization(model, expectedModel); } diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/XtextRuntimeModule.java b/org.eclipse.xtext/src/org/eclipse/xtext/XtextRuntimeModule.java index 32a34b5e73..1692975988 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/XtextRuntimeModule.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/XtextRuntimeModule.java @@ -11,6 +11,9 @@ import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.formatting.IFormatter; +import org.eclipse.xtext.formatting2.FormatterPreferenceValuesProvider; +import org.eclipse.xtext.formatting2.FormatterPreferences; +import org.eclipse.xtext.formatting2.IFormatter2; import org.eclipse.xtext.linking.ILinker; import org.eclipse.xtext.linking.ILinkingDiagnosticMessageProvider; import org.eclipse.xtext.linking.ILinkingService; @@ -18,6 +21,7 @@ import org.eclipse.xtext.parser.antlr.IReferableElementsUnloader; import org.eclipse.xtext.parser.antlr.SyntaxErrorMessageProvider; import org.eclipse.xtext.parsetree.reconstr.ITokenSerializer.ICrossReferenceSerializer; +import org.eclipse.xtext.preferences.IPreferenceValuesProvider; import org.eclipse.xtext.parsetree.reconstr.ITransientValueService; import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager; import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy; @@ -39,6 +43,7 @@ import org.eclipse.xtext.xtext.XtextCrossReferenceSerializer; import org.eclipse.xtext.xtext.XtextDiagnosticConverter; import org.eclipse.xtext.xtext.XtextFormatter; +import org.eclipse.xtext.xtext.XtextFormatterJava; import org.eclipse.xtext.xtext.XtextFragmentProvider; import org.eclipse.xtext.xtext.XtextLinkingDiagnosticMessageProvider; import org.eclipse.xtext.xtext.XtextLinkingService; @@ -100,6 +105,15 @@ public Class bindIFormatter() { return XtextFormatter.class; } + public Class bindIFormatter2() { + return XtextFormatterJava.class; + } + + public void configureFormatterPreferences(Binder binder) { + binder.bind(IPreferenceValuesProvider.class).annotatedWith(FormatterPreferences.class) + .to(FormatterPreferenceValuesProvider.class); + } + @Override public Class bindIValueConverterService() { return XtextValueConverters.class; diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractJavaFormatter.java b/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractJavaFormatter.java index 38405b3a0c..4050d61080 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractJavaFormatter.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractJavaFormatter.java @@ -8,6 +8,8 @@ *******************************************************************************/ package org.eclipse.xtext.formatting2; +import java.util.Collections; + import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion; import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion; @@ -15,7 +17,9 @@ import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionFinder; import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder; import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Exceptions; import org.eclipse.xtext.util.PolymorphicDispatcher; +import org.eclipse.xtext.util.PolymorphicDispatcher.ErrorHandler; import com.google.common.annotations.Beta; @@ -82,6 +86,20 @@ public abstract class AbstractJavaFormatter extends AbstractFormatter2 { private PolymorphicDispatcher dispatcher = createPolymorhicDispatcher(); + // Error handler dispatches to default method in case of a missing method in concrete formatter + protected class EObjectErrorHandler implements ErrorHandler { + @Override + public Void handle(Object[] params, Throwable e) { + if (e instanceof NoSuchMethodException) { + if (params.length == 2 && params[0] instanceof EObject && params[1] instanceof IFormattableDocument) { + _format((EObject) params[0], (IFormattableDocument) params[1]); + return null; + } + } + return Exceptions.throwUncheckedException(e); + } + } + // reflective method that calls "_format" methods found in the implementing class. @Override public void format(Object child, IFormattableDocument document) { @@ -103,8 +121,11 @@ public void format(Object child, IFormattableDocument document) { * Override if you like to specify formatting methods in different way then default (using annotations or similar). */ protected PolymorphicDispatcher createPolymorhicDispatcher() { - return PolymorphicDispatcher.createForSingleTarget(m -> "format".equals(m.getName()) - && m.getParameterCount() == 2 && m.getParameterTypes()[1] == IFormattableDocument.class, this); + return new PolymorphicDispatcher(Collections.singletonList(this), + m -> "format".equals(m.getName()) && m.getParameterCount() == 2 + && m.getParameterTypes()[0] != Object.class + && m.getParameterTypes()[1] == IFormattableDocument.class, + new EObjectErrorHandler()); } // implementations that forward the methods of ITextRegionExtensions to simplify formatter code. @@ -152,5 +173,5 @@ protected IEObjectRegion regionForEObject(EObject semanticElement) { protected Iterable semanticRegions(EObject semanticElement) { return textRegionExtensions.semanticRegions(semanticElement); } - + } diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/xtext/XtextFormatterJava.java b/org.eclipse.xtext/src/org/eclipse/xtext/xtext/XtextFormatterJava.java new file mode 100644 index 0000000000..1f4a534bc9 --- /dev/null +++ b/org.eclipse.xtext/src/org/eclipse/xtext/xtext/XtextFormatterJava.java @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2020 itemis AG (http://www.itemis.com) and others. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.xtext.xtext; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.AbstractElement; +import org.eclipse.xtext.AbstractMetamodelDeclaration; +import org.eclipse.xtext.AbstractRule; +import org.eclipse.xtext.Action; +import org.eclipse.xtext.Alternatives; +import org.eclipse.xtext.Annotation; +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CharacterRange; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.EnumRule; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.Group; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.NamedArgument; +import org.eclipse.xtext.NegatedToken; +import org.eclipse.xtext.Parameter; +import org.eclipse.xtext.ParserRule; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.TerminalRule; +import org.eclipse.xtext.TypeRef; +import org.eclipse.xtext.UntilToken; +import org.eclipse.xtext.Wildcard; +import org.eclipse.xtext.formatting2.AbstractJavaFormatter; +import org.eclipse.xtext.formatting2.IFormattableDocument; + +/** + * @author Arne Deutsch - Initial contribution and API + */ +public class XtextFormatterJava extends AbstractJavaFormatter { + + protected void format(Grammar grammar, IFormattableDocument doc) { + doc.prepend(regionFor(grammar).keyword("("), it -> it.noSpace()); + formatParens(grammar, doc); + regionFor(grammar).keywords(",").forEach(s -> doc.prepend(s, it -> it.noSpace())); + regionFor(grammar).keywords(",").forEach(s -> doc.append(s, it -> it.oneSpace())); + boolean first = true; + for (AbstractMetamodelDeclaration decl : grammar.getMetamodelDeclarations()) { + doc.set(previousHiddenRegion(decl), first ? it -> it.setNewLines(2) : it -> it.setNewLines(1)); + first = false; + doc.format(decl); + } + for (AbstractRule rule : grammar.getRules()) { + doc.set(previousHiddenRegion(rule), it -> it.setNewLines(2)); + doc.format(rule); + } + doc.set(nextHiddenRegion(grammar), it -> it.setNewLines(1)); + } + + protected void format(ParserRule rule, IFormattableDocument doc) { + doc.prepend(regionFor(rule).keyword("<"), it -> it.oneSpace()); + doc.append(regionFor(rule).keyword(">"), it -> it.noSpace()); + rule.getParameters().forEach(p -> doc.format(p)); + formatRule(rule, doc); + formatParens(rule, doc); + } + + protected void format(TerminalRule rule, IFormattableDocument doc) { + formatRule(rule, doc); + } + + protected void format(EnumRule rule, IFormattableDocument doc) { + formatRule(rule, doc); + } + + protected void format(Alternatives alternatives, IFormattableDocument doc) { + regionFor(alternatives).keywords("|").forEach(r -> { + doc.surround(r, it -> it.autowrap()); + doc.surround(r, it -> it.setNewLines(0, 0, 1)); + doc.surround(r, it -> it.oneSpace()); + }); + formatParens(alternatives, doc); + formatCardinality(alternatives, doc); + for (AbstractElement element : alternatives.getElements()) + doc.format(element); + } + + protected void format(Assignment assignment, IFormattableDocument doc) { + regionFor(assignment).keywords("=", "+=", "?=").forEach(r -> doc.surround(r, it -> it.noSpace())); + formatCardinality(assignment, doc); + formatParens(assignment, doc); + doc.format(assignment.getTerminal()); + } + + protected void format(Group group, IFormattableDocument doc) { + formatCardinality(group, doc); + formatParens(group, doc); + boolean first = true; + EList elements = group.getElements(); + int size = elements.size(); + int index = 0; + for (AbstractElement element : elements) { + index++; + boolean last = index == size; + if (!first && !last) { + doc.prepend(element, it -> it.setNewLines(0, 0, 1)); + if (!(element instanceof UntilToken) && !(element instanceof Group)) + doc.surround(element, it -> it.oneSpace()); + } else if (!first && last) { + doc.prepend(element, it -> it.setNewLines(0, 0, 1)); + } + first = false; + doc.format(element); + } + } + + protected void format(Wildcard wildcard, IFormattableDocument doc) { + doc.surround(regionFor(wildcard).keyword(":"), it -> it.oneSpace()); + } + + protected void format(CharacterRange range, IFormattableDocument doc) { + doc.surround(regionFor(range).keyword(".."), it -> it.noSpace()); + formatCardinality(range, doc); + doc.format(range.getLeft()); + doc.format(range.getRight()); + } + + protected void format(RuleCall call, IFormattableDocument doc) { + doc.prepend(regionFor(call).keyword("<"), it -> it.noSpace()); + call.getArguments().forEach(a -> doc.format(a)); + } + + protected void format(Keyword keyword, IFormattableDocument doc) { + doc.surround(keyword, it -> it.autowrap()); + formatCardinality(keyword, doc); + } + + protected void format(NegatedToken token, IFormattableDocument doc) { + doc.append(regionFor(token).keyword("!"), it -> it.noSpace()); + formatParens(token, doc); + formatCardinality(token, doc); + doc.format(token.getTerminal()); + } + + protected void format(UntilToken token, IFormattableDocument doc) { + doc.surround(regionFor(token).keyword("->"), it -> it.noSpace()); + doc.format(token.getTerminal()); + } + + protected void format(Action action, IFormattableDocument doc) { + doc.append(regionFor(action).keyword("{"), it -> it.noSpace()); + doc.prepend(regionFor(action).keyword("}"), it -> it.noSpace()); + } + + protected void format(CrossReference ref, IFormattableDocument doc) { + doc.prepend(regionFor(ref).keyword("["), it -> it.autowrap()); + doc.append(regionFor(ref).keyword("["), it -> it.noSpace()); + doc.prepend(regionFor(ref).keyword("]"), it -> it.noSpace()); + doc.append(regionFor(ref).keyword("]"), it -> it.autowrap()); + doc.surround(regionFor(ref).keyword("|"), it -> it.noSpace()); + doc.format(ref.getType()); + } + + protected void format(Parameter param, IFormattableDocument doc) { + doc.surround(param, it -> it.noSpace()); + } + + protected void format(NamedArgument param, IFormattableDocument doc) { + doc.surround(param, it -> it.noSpace()); + regionFor(param).keywords("=").forEach(k -> doc.surround(k, it -> it.noSpace())); + } + + protected void format(TypeRef ref, IFormattableDocument doc) { + doc.surround(regionFor(ref).keyword("::"), it -> it.noSpace()); + } + + protected void format(Annotation annotation, IFormattableDocument doc) { + doc.surround(regionFor(annotation).keyword("@"), it -> it.noSpace()); + doc.append(annotation, it -> it.newLine()); + } + + private void formatRule(AbstractRule rule, IFormattableDocument doc) { + doc.surround(regionFor(rule).keyword("returns"), it -> it.oneSpace()); + doc.prepend(regionFor(rule).keyword(":"), it -> it.noSpace()); + doc.append(regionFor(rule).keyword(":"), it -> it.newLine()); + doc.prepend(regionFor(rule).keyword(";"), it -> it.noSpace()); + doc.append(regionFor(rule).keyword(";"), it -> it.noSpace()); + doc.interior(regionFor(rule).keyword(":"), regionFor(rule).keyword(";"), it -> it.indent()); + rule.getAnnotations().forEach(a -> doc.format(a)); + doc.format(rule.getType()); + doc.format(rule.getAlternatives()); + } + + private void formatParens(EObject element, IFormattableDocument doc) { + doc.prepend(regionFor(element).keyword("("), it -> it.autowrap()); + doc.append(regionFor(element).keyword("("), it -> it.noSpace()); + doc.prepend(regionFor(element).keyword(")"), it -> it.noSpace()); + doc.append(regionFor(element).keyword(")"), it -> it.autowrap()); + } + + private void formatCardinality(EObject element, IFormattableDocument doc) { + regionFor(element).keywords("?", "*", "+").forEach(r -> doc.prepend(r, it -> it.noSpace())); + } + +}