From 7dc4339d7f1e1ea8001f6bc5d23e711ab432fd8f Mon Sep 17 00:00:00 2001 From: Moritz Eysholdt Date: Tue, 21 Apr 2015 16:15:39 +0200 Subject: [PATCH] [464867] fixed 'ITextRegionAccess cannot handle duplicate keywords' https://bugs.eclipse.org/bugs/show_bug.cgi?id=464867 Signed-off-by: Moritz Eysholdt --- .../regionaccess/ITextRegionAccess.java | 12 ++++- .../internal/AbstractRegionAccess.java | 44 +++++++++++++++++-- .../internal/RegionAccessAccessTest.xtend | 30 +++++++++++-- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java index 56bde2d60..30b2efe16 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java @@ -13,6 +13,8 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.Action; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.Keyword; import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.nodemodel.ILeafNode; @@ -166,6 +168,8 @@ public interface ITextRegionAccess { */ ISemanticRegion regionForKeyword(EObject owner, String keyword); + ISemanticRegion regionForKeyword(EObject owner, Keyword keyword); + ITextSegment regionForOffset(int offset, int length); IEObjectRegion regionForRootEObject(); @@ -178,6 +182,8 @@ public interface ITextRegionAccess { ISemanticRegion regionForRuleCall(EObject owner, RuleCall ruleCall); + ISemanticRegion regionForCrossRef(EObject owner, CrossReference crossRef); + List regionsForRuleCalls(EObject owner, RuleCall... ruleCalls); List regionsForAllEObjects(); @@ -186,7 +192,11 @@ public interface ITextRegionAccess { * @return All {@link ISemanticRegion semantic regions} that represent one of the provided 'keyword's and directly * belong to the provided 'EObject'. Keywords of child-EObjects are not considered. */ - List regionsForKeywords(EObject owner, String... string); + List regionsForKeywords(EObject owner, String... keywords); + + List regionsForKeywords(EObject owner, Keyword... keywords); + + List regionsForCrossRefs(EObject owner, CrossReference... crossRefs); /** * @return All {@link ISemanticRegion semantic regions} that represent a RuleCall to one of the provided diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java index d9bfd4d61..dffdade92 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java @@ -21,6 +21,7 @@ import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; @@ -168,6 +169,19 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess { return null; } + @Override + public ISemanticRegion regionForCrossRef(EObject owner, CrossReference crossRef) { + return regionForRuleCall(owner, (RuleCall) crossRef.getTerminal()); + } + + @Override + public List regionsForCrossRefs(EObject owner, CrossReference... crossRefs) { + List rcs = Lists.newArrayList(); + for (int i = 0; i < crossRefs.length; i++) + rcs.add((RuleCall) crossRefs[i].getTerminal()); + return regionsForRuleCalls(owner, rcs.toArray(new RuleCall[rcs.size()])); + } + protected void assertNoContainment(EStructuralFeature feat) { if (!(feat instanceof EAttribute) && !(feat instanceof EReference && !((EReference) feat).isContainment())) throw new IllegalStateException("Only EAttributes and CrossReferences allowed."); @@ -183,7 +197,7 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess { } AbstractEObjectRegion tokens = regionForEObject(owner); if (tokens == null) - return null; + return Collections.emptyList(); List result = Lists.newArrayList(); for (ISemanticRegion region : tokens.getSemanticLeafRegions()) { Assignment assignment = GrammarUtil.containingAssignment(region.getGrammarElement()); @@ -209,6 +223,17 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess { return null; } + @Override + public ISemanticRegion regionForKeyword(EObject owner, Keyword keyword) { + AbstractEObjectRegion tokens = regionForEObject(owner); + if (tokens == null) + return null; + for (ISemanticRegion region : tokens.getSemanticLeafRegions()) + if (region.getGrammarElement() == keyword) + return region; + return null; + } + @Override public ISemanticRegion regionForRuleCall(EObject owner, RuleCall ruleCall) { assertNoEObjectRuleCall(ruleCall); @@ -227,7 +252,7 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess { assertNoEObjectRuleCall(ruleCalls[i]); AbstractEObjectRegion tokens = regionForEObject(owner); if (tokens == null) - return null; + return Collections.emptyList(); List result = Lists.newArrayList(); for (ISemanticRegion region : tokens.getSemanticLeafRegions()) for (int i = 0; i < ruleCalls.length; i++) @@ -275,7 +300,20 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess { if (kwSet.contains(kw.getValue())) result.add(token); } - return result; + return ImmutableList.copyOf(result); + } + + @Override + public List regionsForKeywords(EObject owner, Keyword... keywords) { + AbstractEObjectRegion tokens = regionForEObject(owner); + if (tokens == null) + return Collections.emptyList(); + List result = Lists.newArrayList(); + for (ISemanticRegion region : tokens.getSemanticLeafRegions()) + for (int i = 0; i < keywords.length; i++) + if (region.getGrammarElement() == keywords[i]) + result.add(region); + return ImmutableList.copyOf(result); } @Override diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend index f2e381553..23e0bdeb1 100644 --- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend +++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend @@ -57,7 +57,7 @@ class RegionAccessAccessTest { assertEquals("foo", actual, actuals) } - def void regionForFeatureContainmentReference() { + @Test def void regionForFeatureContainmentReference() { val mixed = '''6 (foo) action'''.parseAs(AssignedAction) val access = mixed.toAccess try { @@ -112,8 +112,8 @@ class RegionAccessAccessTest { assertEquals("foo", actual, actuals) } - def void regionForRuleCallEObjectParserRule() { - val mixed = '''6 (child foo)'''.parseAs(Mixed) + @Test def void regionForRuleCallEObjectParserRule() { + val mixed = '''6 (child (foo))'''.parseAs(Mixed) val access = mixed.toAccess try { access.regionForRuleCall(mixed, mixedAccess.eobjMixedParserRuleCall_2_2_1_1_0) @@ -127,6 +127,30 @@ class RegionAccessAccessTest { } } + @Test def void regionForKeywordString() { + val mixed = '''6 (foo)'''.parseAs(Mixed) + val access = mixed.toAccess + val actual = access.regionForKeyword(mixed, "(") + val actuals = access.regionsForKeywords(mixed, "(") + assertEquals("(", actual, actuals) + } + + @Test def void regionForKeyword() { + val mixed = '''6 (foo)'''.parseAs(Mixed) + val access = mixed.toAccess + val actual = access.regionForKeyword(mixed, mixedAccess.leftParenthesisKeyword_0) + val actuals = access.regionsForKeywords(mixed, mixedAccess.leftParenthesisKeyword_0) + assertEquals("(", actual, actuals) + } + + @Test def void regionForCrossReference() { + val mixed = '''6 (ref foo) action (foo) end'''.parseAs(AssignedAction) + val access = mixed.toAccess + val actual = access.regionForCrossRef(mixed.child, mixedAccess.refMixedCrossReference_2_2_3_1_0) + val actuals = access.regionsForCrossRefs(mixed.child, mixedAccess.refMixedCrossReference_2_2_3_1_0) + assertEquals("foo", actual, actuals) + } + def private parseAs(CharSequence seq, Class cls) { val result = seq.parse result.assertNoErrors