mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
[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:
parent
fe2d242e2e
commit
4296349f3e
3 changed files with 109 additions and 17 deletions
|
@ -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();
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<?>)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue