[regionAccess] implemented regionsForFeatures()

Signed-off-by: Moritz Eysholdt <moritz.eysholdt@itemis.de>
This commit is contained in:
Moritz Eysholdt 2015-04-21 15:03:08 +02:00
parent 303287508f
commit fe2d242e2e
3 changed files with 139 additions and 18 deletions

View file

@ -67,6 +67,8 @@ import org.eclipse.xtext.resource.XtextResource;
*/
public interface ITextRegionAccess {
List<ILineRegion> expandToLines(ITextSegment segment, int leadingLinesToAdd, int trailingLinesToAdd);
/**
* @return the {@link RuleCall} or the assigned {@link Action} that led to the construction of this EObject. For the
* model's root element, the {@link ParserRule} is returned.
@ -78,6 +80,8 @@ public interface ITextRegionAccess {
*/
XtextResource getResource();
ITextRegionRewriter getRewriter();
/**
* @return true, if one or more parse error occurred when parsing the document.
*/
@ -135,6 +139,10 @@ public interface ITextRegionAccess {
*/
IHiddenRegion leadingHiddenRegion(EObject owner);
ILineRegion lineForOffset(int offset);
ITextSegment regionForDocument();
/**
* @return a text region that reaches from the beginning of its first semantic region to the end of its last
* semantic region.
@ -147,18 +155,29 @@ public interface ITextRegionAccess {
*/
ISemanticRegion regionForFeature(EObject owner, EStructuralFeature feature);
/**
* no containment; returned order same as syntactic oder
*/
List<ISemanticRegion> regionsForFeatures(EObject owner, EStructuralFeature... features);
/**
* @return the first {@link ISemanticRegion} that represent 'keyword' and directly belongs to the provided
* 'EObject'. Keywords of child-EObjects are not considered. May be null.
*/
ISemanticRegion regionForKeyword(EObject owner, String keyword);
ITextSegment regionForOffset(int offset, int length);
IEObjectRegion regionForRootEObject();
/**
* @return the first {@link ISemanticRegion} that represent a RuleCall to the provided AbstractRule and directly
* belongs to the provided 'EObject'. RuleCalls of child-EObjects are not considered. May be null.
*/
ISemanticRegion regionForRuleCallTo(EObject owner, AbstractRule rule);
List<IEObjectRegion> regionsForAllEObjects();
/**
* @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.
@ -172,26 +191,12 @@ public interface ITextRegionAccess {
*/
List<ISemanticRegion> regionsForRuleCallsTo(EObject owner, AbstractRule... rule);
String textForOffset(int offset, int length);
/**
* @return the {@link IHiddenRegion} that follows after the EObject's last {@link ISemanticRegion}.
*
* @see #leadingHiddenRegion(EObject)
*/
IHiddenRegion trailingHiddenRegion(EObject owner); // rename to nextHiddenRegion; do same with leading()
ITextRegionRewriter getRewriter();
IEObjectRegion regionForRootEObject();
ITextSegment regionForDocument();
ITextSegment regionForOffset(int offset, int length);
ILineRegion lineForOffset(int offset);
String textForOffset(int offset, int length);
List<ILineRegion> expandToLines(ITextSegment segment, int leadingLinesToAdd, int trailingLinesToAdd);
List<IEObjectRegion> regionsForAllEObjects();
}

View file

@ -13,6 +13,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
@ -31,6 +32,7 @@ import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion;
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@ -154,8 +156,7 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess {
@Override
public ISemanticRegion regionForFeature(EObject owner, EStructuralFeature feat) {
if (!(feat instanceof EAttribute) && !(feat instanceof EReference && !((EReference) feat).isContainment()))
throw new IllegalStateException("Only EAttributes and CrossReferences allowed.");
assertNoContainment(feat);
AbstractEObjectRegion tokens = regionForEObject(owner);
if (tokens == null)
return null;
@ -167,6 +168,31 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess {
return null;
}
protected void assertNoContainment(EStructuralFeature feat) {
if (!(feat instanceof EAttribute) && !(feat instanceof EReference && !((EReference) feat).isContainment()))
throw new IllegalStateException("Only EAttributes and CrossReferences allowed.");
}
@Override
public List<ISemanticRegion> regionsForFeatures(EObject owner, EStructuralFeature... features) {
Set<String> names = Sets.newHashSet();
for (int i = 0; i < features.length; i++) {
EStructuralFeature feat = features[i];
assertNoContainment(feat);
names.add(feat.getName());
}
AbstractEObjectRegion tokens = regionForEObject(owner);
if (tokens == null)
return null;
List<ISemanticRegion> result = Lists.newArrayList();
for (ISemanticRegion region : tokens.getSemanticLeafRegions()) {
Assignment assignment = GrammarUtil.containingAssignment(region.getGrammarElement());
if (assignment != null && names.contains(assignment.getFeature()))
result.add(region);
}
return ImmutableList.copyOf(result);
}
@Override
public ISemanticRegion regionForKeyword(EObject owner, String keyword) {
AbstractEObjectRegion tokens = regionForEObject(owner);

View file

@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.formatting2.regionaccess.internal
import com.google.inject.Inject
import com.google.inject.Provider
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess
import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder
import org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.Mixed
import org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.Root
import org.eclipse.xtext.junit4.InjectWith
import org.eclipse.xtext.junit4.XtextRunner
import org.eclipse.xtext.junit4.util.ParseHelper
import org.eclipse.xtext.junit4.validation.ValidationTestHelper
import org.eclipse.xtext.resource.XtextResource
import org.junit.Test
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
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@RunWith(XtextRunner)
@InjectWith(RegionAccessTestLanguageInjectorProvider)
class RegionAccessAccessTest {
@Inject extension ParseHelper<Root> parseHelper
@Inject Provider<TextRegionAccessBuilder> textRegionAccessBuilder
@Inject extension ValidationTestHelper validationTestHelper
@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)
}
@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)
}
@Test(expected=IllegalStateException) 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)
}
@Test def void regionsForFeaturesAttribute() {
val mixed = '''6 (foo)'''.parseAs(Mixed)
val access = mixed.toAccess
val actual = access.regionsForFeatures(mixed, MIXED__NAME)
assertEquals("foo", actual.map[text].join)
}
@Test def void regionsForFeaturesCrossReference() {
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)
}
@Test(expected=IllegalStateException) def void regionsForFeaturesContainmentReference() {
val mixed = '''6 (foo) action'''.parseAs(AssignedAction)
val access = mixed.toAccess
val actual = access.regionsForFeatures(mixed, ASSIGNED_ACTION__CHILD)
assertEquals("foo", actual.map[text].join)
}
def private <T extends EObject> parseAs(CharSequence seq, Class<T> cls) {
val result = seq.parse
result.assertNoErrors
return cls.cast(result)
}
def private ITextRegionAccess toAccess(EObject obj) {
return textRegionAccessBuilder.get.forNodeModel(obj.eResource as XtextResource).create
}
}