[465003] fixed 'ITextRegionAccess cannot handle duplicate terminal rule'

https://bugs.eclipse.org/bugs/show_bug.cgi?id=465003

Signed-off-by: Moritz Eysholdt <moritz.eysholdt@itemis.de>
This commit is contained in:
Moritz Eysholdt 2015-04-21 15:49:13 +02:00
parent fe2d242e2e
commit 4296349f3e
3 changed files with 109 additions and 17 deletions

View file

@ -147,7 +147,7 @@ public interface ITextRegionAccess {
* @return a text region that reaches from the beginning of its first semantic region to the end of its last
* semantic region.
*/
IEObjectRegion regionForEObject(EObject object); // TODO: should be semantic region?
IEObjectRegion regionForEObject(EObject object);
/**
* @return returns the first {@link ISemanticRegion} that represents the value of {@code owner.eGet(feature)}. May
@ -176,6 +176,10 @@ public interface ITextRegionAccess {
*/
ISemanticRegion regionForRuleCallTo(EObject owner, AbstractRule rule);
ISemanticRegion regionForRuleCall(EObject owner, RuleCall ruleCall);
List<ISemanticRegion> regionsForRuleCalls(EObject owner, RuleCall... ruleCalls);
List<IEObjectRegion> regionsForAllEObjects();
/**

View file

@ -209,6 +209,38 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess {
return null;
}
@Override
public ISemanticRegion regionForRuleCall(EObject owner, RuleCall ruleCall) {
assertNoEObjectRuleCall(ruleCall);
AbstractEObjectRegion tokens = regionForEObject(owner);
if (tokens == null)
return null;
for (ISemanticRegion region : tokens.getSemanticLeafRegions())
if (region.getGrammarElement() == ruleCall)
return region;
return null;
}
@Override
public List<ISemanticRegion> regionsForRuleCalls(EObject owner, RuleCall... ruleCalls) {
for (int i = 0; i < ruleCalls.length; i++)
assertNoEObjectRuleCall(ruleCalls[i]);
AbstractEObjectRegion tokens = regionForEObject(owner);
if (tokens == null)
return null;
List<ISemanticRegion> result = Lists.newArrayList();
for (ISemanticRegion region : tokens.getSemanticLeafRegions())
for (int i = 0; i < ruleCalls.length; i++)
if (region.getGrammarElement() == ruleCalls[i])
result.add(region);
return ImmutableList.copyOf(result);
}
protected void assertNoEObjectRuleCall(RuleCall ruleCall) {
if (GrammarUtil.isEObjectRuleCall(ruleCall))
throw new IllegalStateException("Only Enum, Datatype and Terminal Rule Calls allowed.");
}
@Override
public ITextSegment regionForOffset(int offset, int length) {
return new TextSegment(this, offset, length);

View file

@ -9,11 +9,15 @@ package org.eclipse.xtext.formatting2.regionaccess.internal
import com.google.inject.Inject
import com.google.inject.Provider
import java.util.Collection
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment
import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder
import org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.AssignedAction
import org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.Mixed
import org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.Root
import org.eclipse.xtext.formatting2.regionaccess.internal.services.RegionAccessTestLanguageGrammarAccess
import org.eclipse.xtext.junit4.InjectWith
import org.eclipse.xtext.junit4.XtextRunner
import org.eclipse.xtext.junit4.util.ParseHelper
@ -24,7 +28,7 @@ import org.junit.runner.RunWith
import static org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.RegionaccesstestlanguagePackage.Literals.*
import static org.junit.Assert.*
import org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.AssignedAction
import com.google.common.collect.ImmutableList
/**
* @author Moritz Eysholdt - Initial contribution and API
@ -35,47 +39,92 @@ class RegionAccessAccessTest {
@Inject extension ParseHelper<Root> parseHelper
@Inject Provider<TextRegionAccessBuilder> textRegionAccessBuilder
@Inject extension ValidationTestHelper validationTestHelper
@Inject extension RegionAccessTestLanguageGrammarAccess
@Test def void regionForFeatureAttribute() {
val mixed = '''6 (foo)'''.parseAs(Mixed)
val access = mixed.toAccess
val actual = access.regionForFeature(mixed, MIXED__NAME)
assertEquals("foo", actual.text)
val actuals = access.regionsForFeatures(mixed, MIXED__NAME)
assertEquals("foo", actual, actuals)
}
@Test def void regionForFeatureCrossReference() {
val mixed = '''6 (ref foo) action (foo) end'''.parseAs(AssignedAction)
val access = mixed.toAccess
val actual = access.regionForFeature(mixed.child, MIXED__REF)
assertEquals("foo", actual.text)
val actuals = access.regionsForFeatures(mixed.child, MIXED__REF)
assertEquals("foo", actual, actuals)
}
@Test(expected=IllegalStateException) def void regionForFeatureContainmentReference() {
def void regionForFeatureContainmentReference() {
val mixed = '''6 (foo) action'''.parseAs(AssignedAction)
val access = mixed.toAccess
val actual = access.regionForFeature(mixed, ASSIGNED_ACTION__CHILD)
assertEquals("foo", actual.text)
try {
access.regionForFeature(mixed, ASSIGNED_ACTION__CHILD)
fail()
} catch (IllegalStateException e) {
}
try {
access.regionsForFeatures(mixed, ASSIGNED_ACTION__CHILD)
fail()
} catch (IllegalStateException e) {
}
}
@Test def void regionsForFeaturesAttribute() {
@Test def void regionForRuleCallUnassignedTerminal() {
val mixed = '''6 (unassigned foo)'''.parseAs(Mixed)
val access = mixed.toAccess
val actual = access.regionForRuleCall(mixed, mixedAccess.IDTerminalRuleCall_1_1_0)
val actuals = access.regionsForRuleCalls(mixed, mixedAccess.IDTerminalRuleCall_1_1_0)
assertEquals("foo", actual, actuals)
}
@Test def void regionForRuleCallUnassignedDataType() {
val mixed = '''6 (unassigned datatype foo)'''.parseAs(Mixed)
val access = mixed.toAccess
val actual = access.regionForRuleCall(mixed, mixedAccess.datatypeParserRuleCall_1_1_1)
val actuals = access.regionsForRuleCalls(mixed, mixedAccess.datatypeParserRuleCall_1_1_1)
assertEquals("datatype foo", actual, actuals)
}
@Test def void regionForRuleCallAssignedTerminal() {
val mixed = '''6 (foo)'''.parseAs(Mixed)
val access = mixed.toAccess
val actual = access.regionsForFeatures(mixed, MIXED__NAME)
assertEquals("foo", actual.map[text].join)
val actual = access.regionForRuleCall(mixed, mixedAccess.nameIDTerminalRuleCall_2_2_0_0)
val actuals = access.regionForRuleCall(mixed, mixedAccess.nameIDTerminalRuleCall_2_2_0_0)
assertEquals("foo", actual, actuals)
}
@Test def void regionsForFeaturesCrossReference() {
@Test def void regionForRuleCallAssignedDataType() {
val mixed = '''6 (datatype foo)'''.parseAs(Mixed)
val access = mixed.toAccess
val actual = access.regionForRuleCall(mixed, mixedAccess.datatypeDatatypeParserRuleCall_2_2_2_0)
val actuals = access.regionForRuleCall(mixed, mixedAccess.datatypeDatatypeParserRuleCall_2_2_2_0)
assertEquals("datatype foo", actual, actuals)
}
@Test def void regionForRuleCallCrossReference() {
val mixed = '''6 (ref foo) action (foo) end'''.parseAs(AssignedAction)
val access = mixed.toAccess
val actual = access.regionsForFeatures(mixed.child, MIXED__REF)
assertEquals("foo", actual.map[text].join)
val actual = access.regionForRuleCall(mixed.child, mixedAccess.refMixedIDTerminalRuleCall_2_2_3_1_0_1)
val actuals = access.regionsForRuleCalls(mixed.child, mixedAccess.refMixedIDTerminalRuleCall_2_2_3_1_0_1)
assertEquals("foo", actual, actuals)
}
@Test(expected=IllegalStateException) def void regionsForFeaturesContainmentReference() {
val mixed = '''6 (foo) action'''.parseAs(AssignedAction)
def void regionForRuleCallEObjectParserRule() {
val mixed = '''6 (child foo)'''.parseAs(Mixed)
val access = mixed.toAccess
val actual = access.regionsForFeatures(mixed, ASSIGNED_ACTION__CHILD)
assertEquals("foo", actual.map[text].join)
try {
access.regionForRuleCall(mixed, mixedAccess.eobjMixedParserRuleCall_2_2_1_1_0)
fail()
} catch (IllegalStateException e) {
}
try {
access.regionsForRuleCalls(mixed, mixedAccess.eobjMixedParserRuleCall_2_2_1_1_0)
fail()
} catch (IllegalStateException e) {
}
}
def private <T extends EObject> parseAs(CharSequence seq, Class<T> cls) {
@ -87,4 +136,11 @@ class RegionAccessAccessTest {
def private ITextRegionAccess toAccess(EObject obj) {
return textRegionAccessBuilder.get.forNodeModel(obj.eResource as XtextResource).create
}
def private void assertEquals(String expected, ITextSegment single, Collection<? extends ITextSegment> regions) {
assertEquals(expected, single.text)
assertEquals(1, regions.size)
assertEquals(expected, regions.head.text)
assertTrue(regions instanceof ImmutableList<?>)
}
}