mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
[formatter] made mechanism for finding text regions more powerful
Signed-off-by: Moritz Eysholdt <moritz.eysholdt@itemis.de>
This commit is contained in:
parent
aecc2d679c
commit
ad6ba6d606
35 changed files with 1366 additions and 726 deletions
|
@ -29,6 +29,7 @@ import org.eclipse.xtext.formatting2.internal.TextReplacerContext;
|
|||
import org.eclipse.xtext.formatting2.internal.TextReplacerMerger;
|
||||
import org.eclipse.xtext.formatting2.internal.WhitespaceReplacer;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IComment;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionExtensions;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegionPart;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
|
@ -126,9 +127,9 @@ import com.google.common.collect.Lists;
|
|||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The methods {@code regionForFeature()} and {@code regionForKeyword} are extension methods:
|
||||
* {@link ITextRegionAccess#regionForFeature(EObject, EStructuralFeature)} and
|
||||
* {@link ITextRegionAccess#regionForKeyword(EObject, String)}. They return an {@link ISemanticRegion}.
|
||||
* The methods {@code regionForFeature()} and {@code regionForKeyword} are extension methods: {link
|
||||
* ITextRegionAccess#regionForFeature(EObject, EStructuralFeature)} and {link
|
||||
* ITextRegionAccess#regionForKeyword(EObject, String)}. They return an {@link ISemanticRegion}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
|
@ -163,7 +164,7 @@ public abstract class AbstractFormatter2 implements IFormatter2 {
|
|||
* Offer subclasses access to the methods from {@link ITextRegionAccess} as extension methods.
|
||||
*/
|
||||
@Extension
|
||||
protected ITextRegionAccess regionAccess;
|
||||
protected ITextRegionExtensions textRegionExtensions;
|
||||
|
||||
private FormatterRequest request = null;
|
||||
|
||||
|
@ -297,17 +298,21 @@ public abstract class AbstractFormatter2 implements IFormatter2 {
|
|||
return request;
|
||||
}
|
||||
|
||||
public ITextRegionAccess getTextRegionAccess() {
|
||||
return request.getTextRegionAccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite this method to initialize member fields before {@link #format(Object, IFormattableDocument)} is called.
|
||||
*/
|
||||
protected void initalize(FormatterRequest request) {
|
||||
this.request = request;
|
||||
this.regionAccess = request.getTextRegionAccess();
|
||||
this.textRegionExtensions = request.getTextRegionAccess().getExtensions();
|
||||
}
|
||||
|
||||
protected List<ITextReplacement> postProcess(IFormattableDocument document, List<ITextReplacement> replacements) {
|
||||
List<ITextSegment> expected = Lists.newArrayList();
|
||||
IHiddenRegion current = regionAccess.regionForRootEObject().getPreviousHiddenRegion();
|
||||
IHiddenRegion current = getTextRegionAccess().regionForRootEObject().getPreviousHiddenRegion();
|
||||
while (current != null) {
|
||||
if (current.isUndefined())
|
||||
expected.addAll(current.getMergedSpaces());
|
||||
|
@ -339,7 +344,7 @@ public abstract class AbstractFormatter2 implements IFormatter2 {
|
|||
*/
|
||||
protected void reset() {
|
||||
this.request = null;
|
||||
this.regionAccess = null;
|
||||
this.textRegionExtensions = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,17 +22,26 @@ import com.google.common.collect.Lists;
|
|||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* <p>A request tells the formatter what and how to format.</p>
|
||||
* <p>
|
||||
* A request tells the formatter what and how to format.
|
||||
* </p>
|
||||
*
|
||||
* <p>When invoking the formatter, the request is passed into {@link IFormatter2#format(FormatterRequest)}.</p>
|
||||
* <p>
|
||||
* When invoking the formatter, the request is passed into {@link IFormatter2#format(FormatterRequest)}.
|
||||
* </p>
|
||||
*
|
||||
* <p>A request carries information about:<p>
|
||||
* <p>
|
||||
* A request carries information about:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>The {@link #textRegionAccess} which allows to obtain the to-be-formatted semantic model with text regions.</li>
|
||||
* <li>{@link #preferences Preferences} with keys from e.g. {@link FormatterPreferenceKeys}.</li>
|
||||
* <li>{@link #regions} that describe how to restrict the text regions for which {@link ITextReplacement replacements} are produced.</li>
|
||||
* <li>An option to {@link #allowIdentityEdits()} which will disable automated suppression of text replacements that do not cause changes.</li>
|
||||
* <li>A setting for green-field formatting ({@link #formatUndefinedHiddenRegionsOnly}): only format regions that have no whitespace information yet.</li>
|
||||
* <li>{@link #regions} that describe how to restrict the text regions for which {@link ITextReplacement replacements}
|
||||
* are produced.</li>
|
||||
* <li>An option to {@link #allowIdentityEdits()} which will disable automated suppression of text replacements that do
|
||||
* not cause changes.</li>
|
||||
* <li>A setting for green-field formatting ({@link #formatUndefinedHiddenRegionsOnly}): only format regions that have
|
||||
* no whitespace information yet.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
|
@ -41,8 +50,8 @@ import com.google.common.collect.Maps;
|
|||
public class FormatterRequest {
|
||||
|
||||
/**
|
||||
* Restrict the formatter to produce {@link ITextReplacement replacements} inside the specified regions only. If no regions are
|
||||
* specified, the whole document is formatted.
|
||||
* Restrict the formatter to produce {@link ITextReplacement replacements} inside the specified regions only. If no
|
||||
* regions are specified, the whole document is formatted.
|
||||
*/
|
||||
private Collection<ITextRegion> regions = Lists.newArrayList();
|
||||
|
||||
|
@ -82,9 +91,8 @@ public class FormatterRequest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #textRegionAccess}. If the region has syntax errors and no explicit
|
||||
* {@link ExceptionAcceptor} is configured yet, the {@link ExceptionAcceptor#IGNORING ignoring acceptor}
|
||||
* will be configured.
|
||||
* Sets the {@link #textRegionAccess}. If the region has syntax errors and no explicit {@link ExceptionAcceptor} is
|
||||
* configured yet, the {@link ExceptionAcceptor#IGNORING ignoring acceptor} will be configured.
|
||||
*/
|
||||
public FormatterRequest setTextRegionAccess(ITextRegionAccess tokens) {
|
||||
if (tokens.hasSyntaxError() && this.exceptionHandler == null)
|
||||
|
@ -94,9 +102,9 @@ public class FormatterRequest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Allow the formatter to produce {@link ITextReplacement replacements} that replace regions with text equal to the text of the
|
||||
* region. Since these replacements do not cause text changes, one usually doens't want to have them in a
|
||||
* production environment. However, they are useful to test if a formatter considers all significant regions, e.g.
|
||||
* Allow the formatter to produce {@link ITextReplacement replacements} that replace regions with text equal to the
|
||||
* text of the region. Since these replacements do not cause text changes, one usually doens't want to have them in
|
||||
* a production environment. However, they are useful to test if a formatter considers all significant regions, e.g.
|
||||
* all {@link IHiddenRegion hidden regions}.
|
||||
*/
|
||||
private boolean allowIdentityEdits;
|
||||
|
@ -139,14 +147,14 @@ public class FormatterRequest {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@link IHiddenRegion Hidden regions} are considered undefined when their whitespace/comments are unknown. This happens for
|
||||
* regions that emerged between programmatically created (not parsed!) model elements.
|
||||
* {@link IHiddenRegion Hidden regions} are considered undefined when their whitespace/comments are unknown. This
|
||||
* happens for regions that emerged between programmatically created (not parsed!) model elements.
|
||||
*
|
||||
* Enable this options if, for example, you serialize a model after applying a quick fix, refactoring or have it
|
||||
* edited in a graphical editor and you want to keep the whitespace-changes to a minimum.
|
||||
*/
|
||||
private boolean formatUndefinedHiddenRegionsOnly;
|
||||
|
||||
|
||||
/**
|
||||
* @see #formatUndefinedHiddenRegionsOnly
|
||||
*/
|
||||
|
@ -163,16 +171,26 @@ public class FormatterRequest {
|
|||
}
|
||||
|
||||
/**
|
||||
* <p>Exceptions that occur during formatting are passed to this handler. The handler may choose to throw them, log
|
||||
* them, or ignore them. Formatting continues, unless the handler throws an exception.</p>
|
||||
* <p>
|
||||
* Exceptions that occur during formatting are passed to this handler. The handler may choose to throw them, log
|
||||
* them, or ignore them. Formatting continues, unless the handler throws an exception.
|
||||
* </p>
|
||||
*
|
||||
* <p>Logging exceptions and continuing formatting is the default behavior.</p>
|
||||
* <p>
|
||||
* Logging exceptions and continuing formatting is the default behavior.
|
||||
* </p>
|
||||
*
|
||||
* <p>Throwing exceptions is useful in unit tests.</p>
|
||||
* <p>
|
||||
* Throwing exceptions is useful in unit tests.
|
||||
* </p>
|
||||
*
|
||||
* <p>Ignoring exceptions is useful when formatting a document with syntax errors.</p>
|
||||
* <p>
|
||||
* Ignoring exceptions is useful when formatting a document with syntax errors.
|
||||
* </p>
|
||||
*
|
||||
* <p>Defaults to the {@link ExceptionAcceptor#LOGGING Logging Acceptor}</p>
|
||||
* <p>
|
||||
* Defaults to the {@link ExceptionAcceptor#LOGGING Logging Acceptor}
|
||||
* </p>
|
||||
*
|
||||
* @see ExceptionAcceptor#LOGGING
|
||||
* @see ExceptionAcceptor#THROWING
|
||||
|
|
|
@ -12,11 +12,13 @@ import java.util.Collections;
|
|||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.emf.common.util.TreeIterator;
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.emf.ecore.util.EcoreUtil;
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.AbstractRule;
|
||||
import org.eclipse.xtext.EcoreUtil2;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IAstRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IComment;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
|
||||
|
@ -120,11 +122,14 @@ public class TextRegionAccessToString {
|
|||
Multimap<IHiddenRegion, IEObjectRegion> hiddens = LinkedListMultimap.create();
|
||||
List<String> errors = Lists.newArrayList();
|
||||
ITextRegionAccess access = list.get(0).getTextRegionAccess();
|
||||
List<IEObjectRegion> objects = access.regionsForAllEObjects();
|
||||
for (IEObjectRegion obj : objects) {
|
||||
TreeIterator<EObject> all = EcoreUtil2.eAll(access.regionForRootEObject().getSemanticElement());
|
||||
while (all.hasNext()) {
|
||||
EObject element = all.next();
|
||||
IEObjectRegion obj = access.regionForEObject(element);
|
||||
if (obj == null)
|
||||
continue;
|
||||
IHiddenRegion previous = obj.getPreviousHiddenRegion();
|
||||
IHiddenRegion next = obj.getNextHiddenRegion();
|
||||
EObject element = obj.getSemanticElement();
|
||||
if (previous == null)
|
||||
errors.add("ERROR: " + EmfFormatter.objPath(element) + " has no leading HiddenRegion.");
|
||||
else
|
||||
|
|
|
@ -66,7 +66,7 @@ public class TextRegionsInTextToString {
|
|||
ITextRegionAccess access = getTextRegionAccess();
|
||||
if (access != null) {
|
||||
ITextSegment impactRegion = TextRegions.merge(this.items);
|
||||
List<ILineRegion> expandToLines = access.expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
|
||||
List<ILineRegion> expandToLines = TextRegions.expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
|
||||
return TextRegions.merge(expandToLines);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.formatting2.debug;
|
||||
|
||||
import static org.eclipse.xtext.formatting2.regionaccess.internal.TextRegions.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ILineRegion;
|
||||
|
@ -14,7 +16,6 @@ import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
|
|||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionRewriter;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextReplacement;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.internal.TextRegions;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
|
@ -81,9 +82,9 @@ public class TextRegionsWithTitleToString {
|
|||
List<ITextSegment> segments = Lists.newArrayList();
|
||||
for (Item item : items)
|
||||
segments.add(item.getRegion());
|
||||
ITextSegment impactRegion = TextRegions.merge(segments);
|
||||
List<ILineRegion> expandToLines = access.expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
|
||||
return TextRegions.merge(expandToLines);
|
||||
ITextSegment impactRegion = merge(segments);
|
||||
List<ILineRegion> expandToLines = expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
|
||||
return merge(expandToLines);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -87,8 +87,11 @@ public abstract class FormattableDocument implements IFormattableDocument {
|
|||
@Override
|
||||
public <T extends EObject> T append(T owner, Procedure1<? super IHiddenRegionFormatter> after) {
|
||||
if (owner != null) {
|
||||
IHiddenRegion gap = getTextRegionAccess().trailingHiddenRegion(owner);
|
||||
set(gap, after);
|
||||
IEObjectRegion region = getTextRegionAccess().regionForEObject(owner);
|
||||
if (region != null) {
|
||||
IHiddenRegion gap = region.getNextHiddenRegion();
|
||||
set(gap, after);
|
||||
}
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
@ -170,10 +173,8 @@ public abstract class FormattableDocument implements IFormattableDocument {
|
|||
|
||||
@Override
|
||||
public void formatConditionally(EObject owner, ISubFormatter... formatters) {
|
||||
ITextRegionAccess access = getTextRegionAccess();
|
||||
int offset = access.leadingHiddenRegion(owner).getEndOffset();
|
||||
int length = access.trailingHiddenRegion(owner).getOffset() - offset;
|
||||
formatConditionally(offset, length, formatters);
|
||||
IEObjectRegion region = getTextRegionAccess().regionForEObject(owner);
|
||||
formatConditionally(region.getOffset(), region.getLength(), formatters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -220,8 +221,11 @@ public abstract class FormattableDocument implements IFormattableDocument {
|
|||
@Override
|
||||
public <T extends EObject> T prepend(T owner, Procedure1<? super IHiddenRegionFormatter> before) {
|
||||
if (owner != null) {
|
||||
IHiddenRegion gap = getTextRegionAccess().leadingHiddenRegion(owner);
|
||||
set(gap, before);
|
||||
IEObjectRegion region = getTextRegionAccess().regionForEObject(owner);
|
||||
if (region != null) {
|
||||
IHiddenRegion gap = region.getPreviousHiddenRegion();
|
||||
set(gap, before);
|
||||
}
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,15 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.formatting2.regionaccess;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public interface IEObjectRegion extends ISequentialRegion, IAstRegion {
|
||||
ISemanticRegionsFinder getAllRegionsFor();
|
||||
|
||||
List<ISemanticRegion> getSemanticLeafRegions();
|
||||
Iterable<ISemanticRegion> getAllSemanticRegions();
|
||||
|
||||
ISemanticRegionsFinder getRegionFor();
|
||||
|
||||
Iterable<ISemanticRegion> getSemanticRegions();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.AbstractRule;
|
||||
import org.eclipse.xtext.Assignment;
|
||||
import org.eclipse.xtext.CrossReference;
|
||||
import org.eclipse.xtext.Keyword;
|
||||
import org.eclipse.xtext.RuleCall;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public interface ISemanticRegionFinder {
|
||||
|
||||
ISemanticRegion assignment(Assignment assignment);
|
||||
|
||||
ISemanticRegion crossRef(CrossReference crossReference);
|
||||
|
||||
ISemanticRegion element(AbstractElement element);
|
||||
|
||||
ISemanticRegion feature(EStructuralFeature feature);
|
||||
|
||||
ISemanticRegion keyword(Keyword keyword);
|
||||
|
||||
ISemanticRegion keyword(String keyword);
|
||||
|
||||
ISemanticRegion ruleCall(RuleCall ruleCall);
|
||||
|
||||
ISemanticRegion ruleCallTo(AbstractRule rule);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.AbstractRule;
|
||||
import org.eclipse.xtext.Assignment;
|
||||
import org.eclipse.xtext.CrossReference;
|
||||
import org.eclipse.xtext.Keyword;
|
||||
import org.eclipse.xtext.RuleCall;
|
||||
import org.eclipse.xtext.xbase.lib.Pair;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public interface ISemanticRegionsFinder extends ISemanticRegionFinder {
|
||||
|
||||
List<ISemanticRegion> assignments(Assignment... assignments);
|
||||
|
||||
List<ISemanticRegion> crossRefs(CrossReference... crossReferences);
|
||||
|
||||
List<ISemanticRegion> elements(AbstractElement... elements);
|
||||
|
||||
List<ISemanticRegion> features(EStructuralFeature... features);
|
||||
|
||||
List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(Keyword kw1, Keyword kw2);
|
||||
|
||||
List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(String kw1, String kw2);
|
||||
|
||||
List<ISemanticRegion> keywords(Keyword... keywords);
|
||||
|
||||
List<ISemanticRegion> keywords(String... keywords);
|
||||
|
||||
List<ISemanticRegion> ruleCalls(RuleCall... ruleCalls);
|
||||
|
||||
List<ISemanticRegion> ruleCallsTo(AbstractRule... rules);
|
||||
|
||||
}
|
|
@ -8,15 +8,23 @@
|
|||
package org.eclipse.xtext.formatting2.regionaccess;
|
||||
|
||||
/**
|
||||
* <p>Common interface for {@link IHiddenRegion} and {@link ISemanticRegion}.</p>
|
||||
* <p>
|
||||
* Common interface for {@link IHiddenRegion} and {@link ISemanticRegion}.
|
||||
* </p>
|
||||
*
|
||||
* <p>{@link IHiddenRegion} and {@link ISemanticRegion} are arranged strictly alternating in a linked list. This interface
|
||||
* provides the method to navigate that list.</p>
|
||||
* <p>
|
||||
* {@link IHiddenRegion} and {@link ISemanticRegion} are arranged strictly alternating in a linked list. This interface
|
||||
* provides the method to navigate that list.
|
||||
* </p>
|
||||
*
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public interface ISequentialRegion extends ITextSegment {
|
||||
|
||||
ISemanticRegionFinder immediatelyFollowing();
|
||||
|
||||
ISemanticRegionFinder immediatelyPreceding();
|
||||
|
||||
IHiddenRegion getNextHiddenRegion();
|
||||
|
||||
ISemanticRegion getNextSemanticRegion();
|
||||
|
|
|
@ -7,16 +7,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.formatting2.regionaccess;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import org.eclipse.xtext.resource.XtextResource;
|
||||
|
||||
|
@ -69,13 +60,7 @@ 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.
|
||||
*/
|
||||
EObject getInvokingGrammarElement(EObject obj);
|
||||
ITextRegionExtensions getExtensions();
|
||||
|
||||
/**
|
||||
* @return The {@link XtextResource} that backs the document this class provides access to.
|
||||
|
@ -84,133 +69,18 @@ public interface ITextRegionAccess {
|
|||
|
||||
ITextRegionRewriter getRewriter();
|
||||
|
||||
/**
|
||||
* @return true, if one or more parse error occurred when parsing the document.
|
||||
*/
|
||||
boolean hasSyntaxError();
|
||||
ILineRegion regionForLineAtOffset(int offset);
|
||||
|
||||
/**
|
||||
* @return true, if an parse error occurred during parsing of the provided EObject or any of its children.
|
||||
*/
|
||||
boolean hasSyntaxError(EObject object);
|
||||
|
||||
/**
|
||||
* @return the semantic region representing the 'keyword' that immediately follows the EObject or null.
|
||||
*/
|
||||
ISemanticRegion immediatelyFollowingKeyword(EObject owner, String keyword);
|
||||
|
||||
/**
|
||||
* @return the semantic region representing the 'keyword' that immediately follows the 'region' or null.
|
||||
*/
|
||||
ISemanticRegion immediatelyFollowingKeyword(ISequentialRegion region, String keyword);
|
||||
|
||||
/**
|
||||
* @return the semantic region for a keyword located right before the provided EObject. May be null if there is no
|
||||
* preceding semantic region or the preceding semantic region does not represent a keyword.
|
||||
*/
|
||||
ISemanticRegion immediatelyPrecedingKeyword(EObject owner);
|
||||
|
||||
/**
|
||||
* @return the semantic region for a keyword located right before the provided EObject. May be null if there is no
|
||||
* preceding semantic region or the preceding semantic region does not represent the provided keyword.
|
||||
*/
|
||||
ISemanticRegion immediatelyPrecedingKeyword(EObject owner, String keyword);
|
||||
|
||||
/**
|
||||
* @return the semantic region for a keyword located right before the provided region. May be null if there is no
|
||||
* preceding semantic region or the preceding semantic region does not represent a keyword.
|
||||
*/
|
||||
ISemanticRegion immediatelyPrecedingKeyword(ISequentialRegion region);
|
||||
|
||||
/**
|
||||
* @return the semantic region for a keyword located right before the provided region. May be null if there is no
|
||||
* preceding semantic region or the preceding semantic region does not represent the provided keyword.
|
||||
*/
|
||||
ISemanticRegion immediatelyPrecedingKeyword(ISequentialRegion region, String keyword);
|
||||
|
||||
/**
|
||||
* @return true, if the EObject's text range contains a line-wrap ("\n"). The EObject's text range reaches from the
|
||||
* beginning of its first semantic region to the end of its last semantic region.
|
||||
*/
|
||||
boolean isMultiline(EObject object);
|
||||
|
||||
/**
|
||||
* @return the {@link IHiddenRegion} that precedes the EObject's first {@link ISemanticRegion}.
|
||||
*
|
||||
* @see #trailingHiddenRegion(EObject)
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
IEObjectRegion regionForEObject(EObject object);
|
||||
|
||||
/**
|
||||
* @return returns the first {@link ISemanticRegion} that represents the value of {@code owner.eGet(feature)}. May
|
||||
* be null.
|
||||
*/
|
||||
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);
|
||||
|
||||
ISemanticRegion regionForKeyword(EObject owner, Keyword keyword);
|
||||
ITextSegment regionForDocument();
|
||||
|
||||
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);
|
||||
|
||||
ISemanticRegion regionForRuleCall(EObject owner, RuleCall ruleCall);
|
||||
|
||||
ISemanticRegion regionForCrossRef(EObject owner, CrossReference crossRef);
|
||||
|
||||
List<ISemanticRegion> regionsForRuleCalls(EObject owner, RuleCall... ruleCalls);
|
||||
|
||||
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.
|
||||
*/
|
||||
List<ISemanticRegion> regionsForKeywords(EObject owner, String... keywords);
|
||||
|
||||
List<ISemanticRegion> regionsForKeywords(EObject owner, Keyword... keywords);
|
||||
|
||||
List<ISemanticRegion> regionsForCrossRefs(EObject owner, CrossReference... crossRefs);
|
||||
|
||||
/**
|
||||
* @return All {@link ISemanticRegion semantic regions} that represent a RuleCall to one of the provided
|
||||
* AbstractRules and directly belong to the provided 'EObject'. RuleCalls of child-EObjects are not
|
||||
* considered. May be null.
|
||||
*/
|
||||
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()
|
||||
boolean hasSyntaxError();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.xtext.Action;
|
||||
import org.eclipse.xtext.ParserRule;
|
||||
import org.eclipse.xtext.RuleCall;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public interface ITextRegionExtensions {
|
||||
|
||||
ISemanticRegionsFinder allRegionsFor(EObject object);
|
||||
|
||||
Iterable<ISemanticRegion> allSemanticRegions(EObject object);
|
||||
|
||||
ITextRegionAccess getTextRegionAccess();
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
EObject grammarElement(EObject obj);
|
||||
|
||||
ISemanticRegionFinder immediatelyFollowing(EObject owner);
|
||||
|
||||
ISemanticRegionFinder immediatelyPreceding(EObject owner);
|
||||
|
||||
/**
|
||||
* @return true, if the EObject's text range contains a line-wrap ("\n"). The EObject's text range reaches from the
|
||||
* beginning of its first semantic region to the end of its last semantic region.
|
||||
*/
|
||||
boolean isMultiline(EObject object);
|
||||
|
||||
/**
|
||||
* @return the {@link IHiddenRegion} that follows after the EObject's last {@link ISemanticRegion}.
|
||||
*
|
||||
* @see #previousHiddenRegion(EObject)
|
||||
*/
|
||||
IHiddenRegion nextHiddenRegion(EObject owner);
|
||||
|
||||
/**
|
||||
* @return the {@link IHiddenRegion} that precedes the EObject's first {@link ISemanticRegion}.
|
||||
*
|
||||
* @see #nextHiddenRegion(EObject)
|
||||
*/
|
||||
IHiddenRegion previousHiddenRegion(EObject owner);
|
||||
|
||||
ISemanticRegionsFinder regionFor(EObject object);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
Iterable<ISemanticRegion> semanticRegions(EObject object);
|
||||
|
||||
}
|
|
@ -28,11 +28,7 @@ public class TextRegionAccessBuilder {
|
|||
}
|
||||
|
||||
public ISequenceAcceptor forSequence(EObject ctx, EObject root) {
|
||||
return this.fromSequencer = createTextRegionAccessBuildingSequencer().withRoot(ctx, root);
|
||||
}
|
||||
|
||||
private TextRegionAccessBuildingSequencer createTextRegionAccessBuildingSequencer() {
|
||||
return new TextRegionAccessBuildingSequencer();
|
||||
return this.fromSequencer = new TextRegionAccessBuildingSequencer().withRoot(ctx, root);
|
||||
}
|
||||
|
||||
public ITextRegionAccess create() {
|
||||
|
|
|
@ -13,6 +13,8 @@ import org.eclipse.emf.ecore.EObject;
|
|||
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionFinder;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
@ -78,7 +80,7 @@ public abstract class AbstractEObjectRegion extends AbstractTextSegment implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> getSemanticLeafRegions() {
|
||||
public List<ISemanticRegion> getSemanticRegions() {
|
||||
return semanticRegions;
|
||||
}
|
||||
|
||||
|
@ -91,6 +93,33 @@ public abstract class AbstractEObjectRegion extends AbstractTextSegment implemen
|
|||
return nextHidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyFollowing() {
|
||||
return new SemanticRegionMatcher(getNextSemanticRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyPreceding() {
|
||||
return new SemanticRegionMatcher(getPreviousSemanticRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionsFinder getRegionFor() {
|
||||
return new SemanticRegionInIterableFinder(semanticRegions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ISemanticRegion> getAllSemanticRegions() {
|
||||
ISemanticRegion first = previousHidden.getNextSemanticRegion();
|
||||
ISemanticRegion last = nextHidden.getPreviousSemanticRegion();
|
||||
return new SemanticRegionIterable(first, last);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionsFinder getAllRegionsFor() {
|
||||
return new SemanticRegionInIterableFinder(getAllSemanticRegions());
|
||||
}
|
||||
|
||||
protected void setGrammarElement(EObject grammarElement) {
|
||||
this.grammarElement = grammarElement;
|
||||
}
|
||||
|
@ -106,4 +135,5 @@ public abstract class AbstractEObjectRegion extends AbstractTextSegment implemen
|
|||
protected void setTrailingHiddenRegion(IHiddenRegion trailing) {
|
||||
this.nextHidden = trailing;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,6 +15,7 @@ import org.eclipse.xtext.formatting2.regionaccess.IComment;
|
|||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegionPart;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionFinder;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IWhitespace;
|
||||
|
@ -151,4 +152,14 @@ public abstract class AbstractHiddenRegion extends AbstractTextSegment implement
|
|||
public String toString() {
|
||||
return new TextRegionAccessToString().withOrigin(this).hightlightOrigin().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyFollowing() {
|
||||
return new SemanticRegionMatcher(getNextSemanticRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyPreceding() {
|
||||
return new SemanticRegionMatcher(getPreviousSemanticRegion());
|
||||
}
|
||||
}
|
|
@ -7,117 +7,88 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.formatting2.regionaccess.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
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;
|
||||
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;
|
||||
import org.eclipse.xtext.formatting2.debug.TextRegionAccessToString;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ILineRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionFinder;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionExtensions;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public abstract class AbstractRegionAccess implements ITextRegionAccess {
|
||||
public abstract class AbstractRegionAccess implements ITextRegionAccess, ITextRegionExtensions {
|
||||
|
||||
@Override
|
||||
public EObject getInvokingGrammarElement(EObject obj) {
|
||||
public ITextRegionExtensions getExtensions() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ISemanticRegion> allSemanticRegions(EObject object) {
|
||||
AbstractEObjectRegion region = regionForEObject(object);
|
||||
if (region == null)
|
||||
return Collections.emptyList();
|
||||
return region.getAllSemanticRegions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ISemanticRegion> semanticRegions(EObject object) {
|
||||
AbstractEObjectRegion region = regionForEObject(object);
|
||||
if (region == null)
|
||||
return Collections.emptyList();
|
||||
return region.getSemanticRegions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionsFinder allRegionsFor(EObject object) {
|
||||
AbstractEObjectRegion region = regionForEObject(object);
|
||||
if (region == null)
|
||||
return SemanticRegionNullFinder.INSTANCE;
|
||||
return region.getAllRegionsFor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EObject grammarElement(EObject obj) {
|
||||
AbstractEObjectRegion tokens = regionForEObject(obj);
|
||||
if (tokens == null)
|
||||
return null;
|
||||
return tokens.getGrammarElement();
|
||||
}
|
||||
|
||||
protected abstract String getText();
|
||||
|
||||
@Override
|
||||
public TextRegionRewriter getRewriter() {
|
||||
return new TextRegionRewriter(this);
|
||||
}
|
||||
|
||||
protected abstract String getText();
|
||||
|
||||
@Override
|
||||
public ISemanticRegion immediatelyFollowingKeyword(EObject owner, String keyword) {
|
||||
IHiddenRegion trailingHiddenRegion = trailingHiddenRegion(owner);
|
||||
if (trailingHiddenRegion == null)
|
||||
return null;
|
||||
ISemanticRegion lastRegion = trailingHiddenRegion.getPreviousSemanticRegion();
|
||||
ISemanticRegion result = immediatelyFollowingKeyword(lastRegion, keyword);
|
||||
return result;
|
||||
public ITextRegionAccess getTextRegionAccess() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion immediatelyFollowingKeyword(ISequentialRegion region, String keyword) {
|
||||
if (region == null)
|
||||
return null;
|
||||
ISemanticRegion nextregion = region.getNextSemanticRegion();
|
||||
if (nextregion == null)
|
||||
return null;
|
||||
EObject grammarElement = nextregion.getGrammarElement();
|
||||
if (grammarElement instanceof Keyword && ((Keyword) grammarElement).getValue().equals(keyword))
|
||||
return nextregion;
|
||||
return null;
|
||||
public ISemanticRegionFinder immediatelyFollowing(EObject owner) {
|
||||
AbstractEObjectRegion region = regionForEObject(owner);
|
||||
if (region != null)
|
||||
return region.immediatelyFollowing();
|
||||
return SemanticRegionNullFinder.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion immediatelyPrecedingKeyword(EObject owner) {
|
||||
ISemanticRegion firstRegion = leadingHiddenRegion(owner).getNextSemanticRegion();
|
||||
ISemanticRegion result = immediatelyPrecedingKeyword(firstRegion);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion immediatelyPrecedingKeyword(EObject owner, String keyword) {
|
||||
ISemanticRegion firstRegion = leadingHiddenRegion(owner).getNextSemanticRegion();
|
||||
ISemanticRegion result = immediatelyPrecedingKeyword(firstRegion, keyword);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion immediatelyPrecedingKeyword(ISequentialRegion token) {
|
||||
if (token == null)
|
||||
return null;
|
||||
ISemanticRegion previousRegion = token.getPreviousSemanticRegion();
|
||||
if (previousRegion == null)
|
||||
return null;
|
||||
EObject grammarElement = previousRegion.getGrammarElement();
|
||||
if (grammarElement instanceof Keyword)
|
||||
return previousRegion;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion immediatelyPrecedingKeyword(ISequentialRegion token, String keyword) {
|
||||
if (token == null)
|
||||
return null;
|
||||
ISemanticRegion previousRegion = token.getPreviousSemanticRegion();
|
||||
if (previousRegion == null)
|
||||
return null;
|
||||
EObject grammarElement = previousRegion.getGrammarElement();
|
||||
if (grammarElement instanceof Keyword && ((Keyword) grammarElement).getValue().equals(keyword))
|
||||
return previousRegion;
|
||||
return null;
|
||||
public ISemanticRegionFinder immediatelyPreceding(EObject owner) {
|
||||
AbstractEObjectRegion region = regionForEObject(owner);
|
||||
if (region != null)
|
||||
return region.immediatelyPreceding();
|
||||
return SemanticRegionNullFinder.INSTANCE;
|
||||
}
|
||||
|
||||
protected Map<? extends EObject, ? extends AbstractEObjectRegion> initMap() {
|
||||
|
@ -129,23 +100,11 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess {
|
|||
AbstractEObjectRegion tokens = regionForEObject(object);
|
||||
if (tokens == null)
|
||||
return false;
|
||||
ISemanticRegion current = tokens.getLeadingHiddenRegion().getNextSemanticRegion();
|
||||
ISemanticRegion last = tokens.getTrailingHiddenRegion().getPreviousSemanticRegion();
|
||||
while (current != null) {
|
||||
if (current.isMultiline())
|
||||
return true;
|
||||
if (current == last)
|
||||
return false;
|
||||
IHiddenRegion next = current.getNextHiddenRegion();
|
||||
if (next.isMultiline())
|
||||
return true;
|
||||
current = current.getNextSemanticRegion();
|
||||
}
|
||||
return false;
|
||||
return tokens.isMultiline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IHiddenRegion leadingHiddenRegion(EObject owner) {
|
||||
public IHiddenRegion previousHiddenRegion(EObject owner) {
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return null;
|
||||
|
@ -153,202 +112,19 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess {
|
|||
}
|
||||
|
||||
@Override
|
||||
public abstract AbstractEObjectRegion regionForEObject(EObject obj);
|
||||
|
||||
@Override
|
||||
public ISemanticRegion regionForFeature(EObject owner, EStructuralFeature feat) {
|
||||
assertNoContainment(feat);
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return null;
|
||||
for (ISemanticRegion region : tokens.getSemanticLeafRegions()) {
|
||||
Assignment assignment = GrammarUtil.containingAssignment(region.getGrammarElement());
|
||||
if (assignment != null && assignment.getFeature().equals(feat.getName()))
|
||||
return region;
|
||||
}
|
||||
return null;
|
||||
public ISemanticRegionsFinder regionFor(EObject object) {
|
||||
AbstractEObjectRegion region = regionForEObject(object);
|
||||
if (region != null)
|
||||
return region.getRegionFor();
|
||||
else
|
||||
return SemanticRegionNullFinder.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion regionForCrossRef(EObject owner, CrossReference crossRef) {
|
||||
return regionForRuleCall(owner, (RuleCall) crossRef.getTerminal());
|
||||
}
|
||||
public abstract AbstractEObjectRegion regionForEObject(EObject object);
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> regionsForCrossRefs(EObject owner, CrossReference... crossRefs) {
|
||||
List<RuleCall> 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.");
|
||||
}
|
||||
|
||||
@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 Collections.emptyList();
|
||||
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);
|
||||
if (tokens == null)
|
||||
return null;
|
||||
for (ISemanticRegion region : tokens.getSemanticLeafRegions()) {
|
||||
EObject element = region.getGrammarElement();
|
||||
if (element instanceof Keyword) {
|
||||
Keyword kw = (Keyword) element;
|
||||
if (kw.getValue().equals(keyword))
|
||||
return region;
|
||||
}
|
||||
}
|
||||
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);
|
||||
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 Collections.emptyList();
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion regionForRuleCallTo(EObject owner, AbstractRule rule) {
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return null;
|
||||
for (ISemanticRegion token : tokens.getSemanticLeafRegions()) {
|
||||
EObject element = token.getGrammarElement();
|
||||
if (element instanceof RuleCall) {
|
||||
RuleCall rc = (RuleCall) element;
|
||||
if (rc.getRule() == rule)
|
||||
return token;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> regionsForKeywords(EObject owner, String... keywords) {
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return Collections.emptyList();
|
||||
Collection<String> kwSet = keywords.length <= 1 ? Arrays.asList(keywords) : Sets.newHashSet(keywords);
|
||||
List<ISemanticRegion> result = Lists.newArrayList();
|
||||
for (ISemanticRegion token : tokens.getSemanticLeafRegions())
|
||||
if (token.getGrammarElement() instanceof Keyword) {
|
||||
Keyword kw = (Keyword) token.getGrammarElement();
|
||||
if (kwSet.contains(kw.getValue()))
|
||||
result.add(token);
|
||||
}
|
||||
return ImmutableList.copyOf(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> regionsForKeywords(EObject owner, Keyword... keywords) {
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return Collections.emptyList();
|
||||
List<ISemanticRegion> 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
|
||||
public List<ISemanticRegion> regionsForRuleCallsTo(EObject owner, AbstractRule... rule) {
|
||||
HashSet<AbstractRule> set = Sets.newHashSet(rule);
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return Collections.emptyList();
|
||||
List<ISemanticRegion> result = Lists.newArrayList();
|
||||
for (ISemanticRegion region : tokens.getSemanticLeafRegions()) {
|
||||
EObject element = region.getGrammarElement();
|
||||
if (element instanceof RuleCall) {
|
||||
RuleCall rc = (RuleCall) element;
|
||||
if (set.contains(rc.getRule()))
|
||||
result.add(region);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new TextRegionAccessToString().withRegionAccess(this).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IHiddenRegion trailingHiddenRegion(EObject owner) {
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return null;
|
||||
return tokens.getTrailingHiddenRegion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILineRegion lineForOffset(int offset) {
|
||||
public ILineRegion regionForLineAtOffset(int offset) {
|
||||
String text = getText();
|
||||
if (offset < 0 || offset > text.length())
|
||||
return null;
|
||||
|
@ -365,19 +141,21 @@ public abstract class AbstractRegionAccess implements ITextRegionAccess {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<ILineRegion> expandToLines(ITextSegment segment, int leadingLinesToAdd, int trailingLinesToAdd) {
|
||||
List<ILineRegion> lines = Lists.newArrayList(segment.getLineRegions());
|
||||
for (int i = 1; i < leadingLinesToAdd; i++) {
|
||||
ILineRegion line = lines.get(0).getPreviousLine();
|
||||
if (line != null)
|
||||
lines.add(0, line);
|
||||
}
|
||||
for (int i = 1; i < trailingLinesToAdd; i++) {
|
||||
ILineRegion line = lines.get(lines.size() - 1).getNextLine();
|
||||
if (line != null)
|
||||
lines.add(line);
|
||||
}
|
||||
return lines;
|
||||
public ITextSegment regionForOffset(int offset, int length) {
|
||||
return new TextSegment(this, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new TextRegionAccessToString().withRegionAccess(this).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IHiddenRegion nextHiddenRegion(EObject owner) {
|
||||
AbstractEObjectRegion tokens = regionForEObject(owner);
|
||||
if (tokens == null)
|
||||
return null;
|
||||
return tokens.getTrailingHiddenRegion();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
/*******************************************************************************
|
||||
* 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 java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.ecore.EAttribute;
|
||||
import org.eclipse.emf.ecore.EClass;
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.EReference;
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.AbstractRule;
|
||||
import org.eclipse.xtext.Assignment;
|
||||
import org.eclipse.xtext.CompoundElement;
|
||||
import org.eclipse.xtext.CrossReference;
|
||||
import org.eclipse.xtext.GrammarUtil;
|
||||
import org.eclipse.xtext.Keyword;
|
||||
import org.eclipse.xtext.RuleCall;
|
||||
import org.eclipse.xtext.XtextPackage;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
|
||||
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
|
||||
import org.eclipse.xtext.xbase.lib.Pair;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public abstract class AbstractSemanticRegionsFinder implements ISemanticRegionsFinder {
|
||||
|
||||
protected static class FeaturePredicate implements Predicate<ISemanticRegion> {
|
||||
|
||||
private final String name;
|
||||
private final EClass type;
|
||||
|
||||
public FeaturePredicate(EStructuralFeature feature) {
|
||||
this.name = feature.getName();
|
||||
this.type = feature.getEContainingClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ISemanticRegion input) {
|
||||
EObject element = input.getGrammarElement();
|
||||
Assignment assignment = GrammarUtil.containingAssignment(element);
|
||||
if (assignment == null || !name.equals(assignment.getFeature()))
|
||||
return false;
|
||||
EObject semanticElement = input.getSemanticElement();
|
||||
return type.isInstance(semanticElement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class GrammarElementPredicate implements Predicate<ISemanticRegion> {
|
||||
private final EObject grammarElement;
|
||||
|
||||
public GrammarElementPredicate(EObject grammarElement) {
|
||||
super();
|
||||
this.grammarElement = grammarElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ISemanticRegion input) {
|
||||
return input.getGrammarElement() == grammarElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String string = new GrammarElementTitleSwitch().showRule().showQualified().doSwitch(grammarElement);
|
||||
return "Predicate[" + string + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class GrammarElementsPredicate implements Predicate<ISemanticRegion> {
|
||||
private final Set<? extends EObject> grammarElements;
|
||||
|
||||
public GrammarElementsPredicate(Set<? extends EObject> grammarElements) {
|
||||
super();
|
||||
this.grammarElements = grammarElements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ISemanticRegion input) {
|
||||
return grammarElements.contains(input.getGrammarElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> strings = Lists.newArrayList();
|
||||
GrammarElementTitleSwitch toString = new GrammarElementTitleSwitch().showRule().showQualified();
|
||||
for (EObject ele : grammarElements)
|
||||
strings.add(toString.doSwitch(ele));
|
||||
return "Predicate[" + Joiner.on(", ").join(strings) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
protected static class KeywordPredicate implements Predicate<ISemanticRegion> {
|
||||
private final String keyword;
|
||||
|
||||
public KeywordPredicate(String keyword) {
|
||||
super();
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ISemanticRegion input) {
|
||||
EObject element = input.getGrammarElement();
|
||||
return element instanceof Keyword && keyword.equals(((Keyword) element).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Predicate[" + keyword + "]";
|
||||
}
|
||||
}
|
||||
|
||||
protected static class KeywordsPredicate implements Predicate<ISemanticRegion> {
|
||||
private final Set<String> keywords;
|
||||
|
||||
public KeywordsPredicate(Set<String> keywords) {
|
||||
super();
|
||||
this.keywords = keywords;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ISemanticRegion input) {
|
||||
EObject element = input.getGrammarElement();
|
||||
return element instanceof Keyword && keywords.contains(((Keyword) element).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Predicate[" + Joiner.on(", ").join(keywords) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
protected static class RulePredicate implements Predicate<ISemanticRegion> {
|
||||
private final AbstractRule rule;
|
||||
|
||||
public RulePredicate(AbstractRule rule) {
|
||||
super();
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ISemanticRegion input) {
|
||||
EObject element = input.getGrammarElement();
|
||||
return element instanceof RuleCall && ((RuleCall) element).getRule() == rule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Predicate[" + rule.getName() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
protected static class RulesPredicate implements Predicate<ISemanticRegion> {
|
||||
private final Set<AbstractRule> rules;
|
||||
|
||||
public RulesPredicate(Set<AbstractRule> rules) {
|
||||
super();
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ISemanticRegion input) {
|
||||
EObject element = input.getGrammarElement();
|
||||
return element instanceof RuleCall && rules.contains(((RuleCall) element).getRule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> strings = Lists.newArrayList();
|
||||
for (AbstractRule rule : rules)
|
||||
strings.add(rule.getName());
|
||||
return "Predicate[" + Joiner.on(", ").join(strings) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertNoContainment(EStructuralFeature feat) {
|
||||
if (!(feat instanceof EAttribute) && !(feat instanceof EReference && !((EReference) feat).isContainment()))
|
||||
throw new IllegalStateException("Only EAttributes and CrossReferences allowed.");
|
||||
}
|
||||
|
||||
protected void assertNoEObjectRule(AbstractRule rule) {
|
||||
if (GrammarUtil.isEObjectRule(rule))
|
||||
throw new IllegalStateException("Only Enum, Datatype and Terminal Rule Calls allowed.");
|
||||
}
|
||||
|
||||
protected void assertNoEObjectRuleCall(RuleCall ruleCall) {
|
||||
assertNoEObjectRule(ruleCall.getRule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion assignment(Assignment assignment) {
|
||||
return findFirst(createPredicate(assignment));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> assignments(Assignment... assignments) {
|
||||
return findAll(createPredicate(assignments));
|
||||
}
|
||||
|
||||
protected void collectMatchableElements(AbstractElement ele, Collection<AbstractElement> result) {
|
||||
switch (ele.eClass().getClassifierID()) {
|
||||
case XtextPackage.RULE_CALL:
|
||||
assertNoEObjectRuleCall((RuleCall) ele);
|
||||
case XtextPackage.KEYWORD:
|
||||
result.add(ele);
|
||||
break;
|
||||
case XtextPackage.CROSS_REFERENCE:
|
||||
collectMatchableElements(((CrossReference) ele).getTerminal(), result);
|
||||
break;
|
||||
case XtextPackage.ASSIGNMENT:
|
||||
collectMatchableElements(((Assignment) ele).getTerminal(), result);
|
||||
break;
|
||||
case XtextPackage.ALTERNATIVES:
|
||||
case XtextPackage.GROUP:
|
||||
case XtextPackage.UNORDERED_GROUP:
|
||||
for (AbstractElement child : ((CompoundElement) ele).getElements())
|
||||
collectMatchableElements(child, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected Predicate<ISemanticRegion> createPredicate(AbstractElement ele) {
|
||||
switch (ele.eClass().getClassifierID()) {
|
||||
case XtextPackage.RULE_CALL:
|
||||
assertNoEObjectRuleCall((RuleCall) ele);
|
||||
case XtextPackage.KEYWORD:
|
||||
return new GrammarElementPredicate(ele);
|
||||
}
|
||||
return createPredicate(new AbstractElement[] { ele });
|
||||
}
|
||||
|
||||
protected Predicate<ISemanticRegion> createPredicate(AbstractElement... ele) {
|
||||
Set<AbstractElement> result = Sets.newHashSet();
|
||||
for (AbstractElement e : ele)
|
||||
collectMatchableElements(e, result);
|
||||
switch (result.size()) {
|
||||
case 0:
|
||||
return Predicates.alwaysFalse();
|
||||
case 1:
|
||||
return new GrammarElementPredicate(result.iterator().next());
|
||||
default:
|
||||
return new GrammarElementsPredicate(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion crossRef(CrossReference crossReference) {
|
||||
return findFirst(createPredicate(crossReference));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> crossRefs(CrossReference... crossReferences) {
|
||||
return findAll(createPredicate(crossReferences));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion element(AbstractElement element) {
|
||||
return findFirst(createPredicate(element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> elements(AbstractElement... elements) {
|
||||
return findAll(createPredicate(elements));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion feature(EStructuralFeature feature) {
|
||||
assertNoContainment(feature);
|
||||
return findFirst(new FeaturePredicate(feature));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> features(EStructuralFeature... features) {
|
||||
Set<Predicate<ISemanticRegion>> predicates = Sets.newHashSet();
|
||||
for (int i = 0; i < features.length; i++) {
|
||||
EStructuralFeature feat = features[i];
|
||||
assertNoContainment(feat);
|
||||
predicates.add(new FeaturePredicate(feat));
|
||||
}
|
||||
return findAll(Predicates.or(predicates));
|
||||
}
|
||||
|
||||
protected abstract ImmutableList<ISemanticRegion> findAll(Predicate<ISemanticRegion> predicate);
|
||||
|
||||
protected abstract ISemanticRegion findFirst(Predicate<ISemanticRegion> predicate);
|
||||
|
||||
@Override
|
||||
public ISemanticRegion keyword(Keyword keyword) {
|
||||
return findFirst(createPredicate(keyword));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion keyword(String keyword) {
|
||||
return findFirst(new KeywordPredicate(keyword));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(Keyword kw1, Keyword kw2) {
|
||||
Preconditions.checkNotNull(kw1);
|
||||
Preconditions.checkNotNull(kw2);
|
||||
Preconditions.checkArgument(kw1 != kw2);
|
||||
Predicate<ISemanticRegion> p1 = createPredicate(kw1);
|
||||
Predicate<ISemanticRegion> p2 = createPredicate(kw2);
|
||||
List<ISemanticRegion> all = findAll(Predicates.or(p1, p2));
|
||||
Builder<Pair<ISemanticRegion, ISemanticRegion>> result = ImmutableList.builder();
|
||||
LinkedList<ISemanticRegion> stack = new LinkedList<ISemanticRegion>();
|
||||
for (ISemanticRegion region : all) {
|
||||
if (p1.apply(region))
|
||||
stack.push(region);
|
||||
else if (!stack.isEmpty())
|
||||
result.add(Pair.of(stack.pop(), region));
|
||||
}
|
||||
return result.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(String kw1, String kw2) {
|
||||
Preconditions.checkNotNull(kw1);
|
||||
Preconditions.checkNotNull(kw2);
|
||||
Preconditions.checkArgument(!kw1.equals(kw2));
|
||||
Predicate<ISemanticRegion> p1 = new KeywordPredicate(kw1);
|
||||
Predicate<ISemanticRegion> p2 = new KeywordPredicate(kw2);
|
||||
List<ISemanticRegion> all = findAll(Predicates.or(p1, p2));
|
||||
Builder<Pair<ISemanticRegion, ISemanticRegion>> result = ImmutableList.builder();
|
||||
LinkedList<ISemanticRegion> stack = new LinkedList<ISemanticRegion>();
|
||||
for (ISemanticRegion region : all) {
|
||||
if (p1.apply(region))
|
||||
stack.push(region);
|
||||
else {
|
||||
AbstractRule regionRule = GrammarUtil.containingRule(region.getGrammarElement());
|
||||
while (!stack.isEmpty()) {
|
||||
ISemanticRegion candidate = stack.pop();
|
||||
if (region.getSemanticElement() == candidate.getSemanticElement()) {
|
||||
AbstractRule candidateRule = GrammarUtil.containingRule(candidate.getGrammarElement());
|
||||
if (regionRule == candidateRule) {
|
||||
result.add(Pair.of(candidate, region));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> keywords(Keyword... keywords) {
|
||||
return findAll(createPredicate(keywords));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> keywords(String... keywords) {
|
||||
Predicate<ISemanticRegion> predicate;
|
||||
if (keywords.length == 1)
|
||||
predicate = new KeywordPredicate(keywords[0]);
|
||||
else
|
||||
predicate = new KeywordsPredicate(Sets.newHashSet(keywords));
|
||||
return findAll(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion ruleCall(RuleCall ruleCall) {
|
||||
return findFirst(createPredicate(ruleCall));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> ruleCalls(RuleCall... ruleCalls) {
|
||||
return findAll(createPredicate(ruleCalls));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> ruleCallsTo(AbstractRule... rules) {
|
||||
for (int i = 0; i < rules.length; i++)
|
||||
assertNoEObjectRule(rules[i]);
|
||||
Predicate<ISemanticRegion> predicate;
|
||||
if (rules.length == 1)
|
||||
predicate = new RulePredicate(rules[0]);
|
||||
else
|
||||
predicate = new RulesPredicate(Sets.newHashSet(rules));
|
||||
return findAll(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion ruleCallTo(AbstractRule rule) {
|
||||
assertNoEObjectRule(rule);
|
||||
return findFirst(new RulePredicate(rule));
|
||||
}
|
||||
|
||||
}
|
|
@ -67,7 +67,7 @@ public abstract class AbstractTextSegment implements ITextSegment {
|
|||
|
||||
@Override
|
||||
public List<ILineRegion> getLineRegions() {
|
||||
ILineRegion current = getTextRegionAccess().lineForOffset(getOffset());
|
||||
ILineRegion current = getTextRegionAccess().regionForLineAtOffset(getOffset());
|
||||
List<ILineRegion> result = Lists.newArrayList();
|
||||
int endOffset = getEndOffset();
|
||||
while (current != null) {
|
||||
|
|
|
@ -7,18 +7,13 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.formatting2.regionaccess.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment;
|
||||
import org.eclipse.xtext.nodemodel.BidiTreeIterator;
|
||||
import org.eclipse.xtext.nodemodel.INode;
|
||||
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
|
||||
import org.eclipse.xtext.resource.XtextResource;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
|
@ -49,21 +44,6 @@ public class NodeModelBasedRegionAccess extends AbstractRegionAccess {
|
|||
return getResource().getParseResult().getRootNode().getText().substring(offset, offset + length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSyntaxError() {
|
||||
return getResource().getParseResult().hasSyntaxErrors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSyntaxError(EObject object) {
|
||||
BidiTreeIterator<INode> it = NodeModelUtils.getNode(object).getAsTreeIterable().iterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.next().getSyntaxErrorMessage() != null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractEObjectRegion regionForEObject(EObject obj) {
|
||||
return eObjectToTokens.get(obj);
|
||||
|
@ -80,8 +60,8 @@ public class NodeModelBasedRegionAccess extends AbstractRegionAccess {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<IEObjectRegion> regionsForAllEObjects() {
|
||||
return ImmutableList.<IEObjectRegion> copyOf(eObjectToTokens.values());
|
||||
public boolean hasSyntaxError() {
|
||||
return resource.getParseResult().hasSyntaxErrors();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ public class NodeModelBasedRegionAccessBuilder {
|
|||
newHidden.setPrevious(newSemantic);
|
||||
newSemantic.setLeadingHiddenRegion(lastHidden);
|
||||
lastHidden.setNext(newSemantic);
|
||||
eObjectTokens.getSemanticLeafRegions().add(newSemantic);
|
||||
eObjectTokens.getSemanticRegions().add(newSemantic);
|
||||
newSemantic.setEObjectTokens(eObjectTokens);
|
||||
lastHidden = newHidden;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.eclipse.xtext.AbstractElement;
|
|||
import org.eclipse.xtext.CrossReference;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionFinder;
|
||||
import org.eclipse.xtext.nodemodel.INode;
|
||||
|
||||
/**
|
||||
|
@ -19,9 +20,9 @@ import org.eclipse.xtext.nodemodel.INode;
|
|||
*/
|
||||
public class NodeSemanticRegion extends NodeRegion implements ISemanticRegion {
|
||||
|
||||
private NodeEObjectRegion eObjectTokens;
|
||||
private IHiddenRegion leading;
|
||||
private IHiddenRegion trailing;
|
||||
private NodeEObjectRegion eObjectTokens;
|
||||
|
||||
protected NodeSemanticRegion(NodeModelBasedRegionAccess access, INode node) {
|
||||
super(access, node);
|
||||
|
@ -55,6 +56,25 @@ public class NodeSemanticRegion extends NodeRegion implements ISemanticRegion {
|
|||
return leading != null ? leading.getPreviousSemanticRegion() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EObject getSemanticElement() {
|
||||
return eObjectTokens != null ? eObjectTokens.getSemanticElement() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyFollowing() {
|
||||
return new SemanticRegionMatcher(getNextSemanticRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyPreceding() {
|
||||
return new SemanticRegionMatcher(getPreviousSemanticRegion());
|
||||
}
|
||||
|
||||
protected void setEObjectTokens(NodeEObjectRegion eObjectTokens) {
|
||||
this.eObjectTokens = eObjectTokens;
|
||||
}
|
||||
|
||||
protected void setLeadingHiddenRegion(IHiddenRegion leading) {
|
||||
this.leading = leading;
|
||||
}
|
||||
|
@ -62,13 +82,4 @@ public class NodeSemanticRegion extends NodeRegion implements ISemanticRegion {
|
|||
protected void setTrailingHiddenRegion(IHiddenRegion trailing) {
|
||||
this.trailing = trailing;
|
||||
}
|
||||
|
||||
protected void setEObjectTokens(NodeEObjectRegion eObjectTokens) {
|
||||
this.eObjectTokens = eObjectTokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EObject getSemanticElement() {
|
||||
return eObjectTokens != null ? eObjectTokens.getSemanticElement() : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*******************************************************************************
|
||||
* 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 org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public class SemanticRegionInIterableFinder extends AbstractSemanticRegionsFinder {
|
||||
|
||||
private final Iterable<ISemanticRegion> regions;
|
||||
|
||||
public SemanticRegionInIterableFinder(Iterable<ISemanticRegion> regions) {
|
||||
super();
|
||||
this.regions = regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<ISemanticRegion> findAll(Predicate<ISemanticRegion> predicate) {
|
||||
Builder<ISemanticRegion> builder = ImmutableList.builder();
|
||||
for (ISemanticRegion region : regions)
|
||||
if (predicate.apply(region))
|
||||
builder.add(region);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ISemanticRegion findFirst(Predicate<ISemanticRegion> predicate) {
|
||||
for (ISemanticRegion region : regions)
|
||||
if (predicate.apply(region))
|
||||
return region;
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*******************************************************************************
|
||||
* 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 java.util.Iterator;
|
||||
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public class SemanticRegionIterable implements Iterable<ISemanticRegion> {
|
||||
|
||||
private final ISemanticRegion first;
|
||||
private final ISemanticRegion last;
|
||||
|
||||
public SemanticRegionIterable(ISemanticRegion first, ISemanticRegion last) {
|
||||
super();
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ISemanticRegion> iterator() {
|
||||
return new AbstractIterator<ISemanticRegion>() {
|
||||
private ISemanticRegion next = first;
|
||||
|
||||
@Override
|
||||
protected ISemanticRegion computeNext() {
|
||||
if (next == null)
|
||||
return endOfData();
|
||||
ISemanticRegion result = next;
|
||||
next = next.getNextSemanticRegion();
|
||||
if (result == last)
|
||||
next = null;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*******************************************************************************
|
||||
* 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 org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public class SemanticRegionMatcher extends AbstractSemanticRegionsFinder {
|
||||
|
||||
private final ISemanticRegion region;
|
||||
|
||||
public SemanticRegionMatcher(ISemanticRegion region) {
|
||||
super();
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<ISemanticRegion> findAll(Predicate<ISemanticRegion> predicate) {
|
||||
ISemanticRegion element = findFirst(predicate);
|
||||
if (element == null)
|
||||
return ImmutableList.of();
|
||||
else
|
||||
return ImmutableList.of(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ISemanticRegion findFirst(Predicate<ISemanticRegion> predicate) {
|
||||
if (predicate.apply(region))
|
||||
return region;
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/*******************************************************************************
|
||||
* 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 java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.AbstractRule;
|
||||
import org.eclipse.xtext.Assignment;
|
||||
import org.eclipse.xtext.CrossReference;
|
||||
import org.eclipse.xtext.Keyword;
|
||||
import org.eclipse.xtext.RuleCall;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
|
||||
import org.eclipse.xtext.xbase.lib.Pair;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
public enum SemanticRegionNullFinder implements ISemanticRegionsFinder {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public ISemanticRegion assignment(Assignment assignment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> assignments(Assignment... assignments) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion crossRef(CrossReference crossReference) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> crossRefs(CrossReference... crossReferences) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion element(AbstractElement element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> elements(AbstractElement... elements) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion feature(EStructuralFeature feature) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> features(EStructuralFeature... features) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion keyword(Keyword keyword) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion keyword(String keyword) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(Keyword kw1, Keyword kw2) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(String kw1, String kw2) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> keywords(Keyword... keywords) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> keywords(String... keywords) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion ruleCall(RuleCall ruleCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> ruleCalls(RuleCall... ruleCalls) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ISemanticRegion> ruleCallsTo(AbstractRule... rules) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegion ruleCallTo(AbstractRule rule) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.formatting2.regionaccess.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
|
@ -15,7 +14,6 @@ import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
|
|||
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment;
|
||||
import org.eclipse.xtext.resource.XtextResource;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
|
@ -52,16 +50,6 @@ public class StringBasedRegionAccess extends AbstractRegionAccess {
|
|||
return string.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSyntaxError() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSyntaxError(EObject object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITextSegment regionForDocument() {
|
||||
return new TextSegment(this, 0, string.length());
|
||||
|
@ -87,8 +75,8 @@ public class StringBasedRegionAccess extends AbstractRegionAccess {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<IEObjectRegion> regionsForAllEObjects() {
|
||||
return ImmutableList.<IEObjectRegion> copyOf(eObjectToTokens.values());
|
||||
public boolean hasSyntaxError() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,5 +19,4 @@ public class StringEObjectRegion extends AbstractEObjectRegion {
|
|||
this.setGrammarElement(grammarElement);
|
||||
this.setSemantcElement(semanticElement);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@ import org.eclipse.emf.ecore.EObject;
|
|||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionFinder;
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
|
@ -59,6 +60,16 @@ public class StringSemanticRegion extends StringRegion implements ISemanticRegio
|
|||
return semanticElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyFollowing() {
|
||||
return new SemanticRegionMatcher(getNextSemanticRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISemanticRegionFinder immediatelyPreceding() {
|
||||
return new SemanticRegionMatcher(getPreviousSemanticRegion());
|
||||
}
|
||||
|
||||
protected void setLeadingHiddenRegion(IHiddenRegion leading) {
|
||||
this.leading = leading;
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ public class TextRegionAccessBuildingSequencer implements ISequenceAcceptor {
|
|||
last.setPrevious(semantic);
|
||||
semantic.setTrailingHiddenRegion(last);
|
||||
if (tokens != null) {
|
||||
tokens.getSemanticLeafRegions().add(semantic);
|
||||
tokens.getSemanticRegions().add(semantic);
|
||||
tokens.setTrailingHiddenRegion(last);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Comparator;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ILineRegion;
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment;
|
||||
import org.eclipse.xtext.util.ITextRegion;
|
||||
|
||||
|
@ -87,4 +88,19 @@ public class TextRegions {
|
|||
return new TextSegment(first.getTextRegionAccess(), minOffset, maxEndOffset - minOffset);
|
||||
}
|
||||
|
||||
public static List<ILineRegion> expandToLines(ITextSegment segment, int leadingLinesToAdd, int trailingLinesToAdd) {
|
||||
List<ILineRegion> lines = Lists.newArrayList(segment.getLineRegions());
|
||||
for (int i = 1; i < leadingLinesToAdd; i++) {
|
||||
ILineRegion line = lines.get(0).getPreviousLine();
|
||||
if (line != null)
|
||||
lines.add(0, line);
|
||||
}
|
||||
for (int i = 1; i < trailingLinesToAdd; i++) {
|
||||
ILineRegion line = lines.get(lines.size() - 1).getNextLine();
|
||||
if (line != null)
|
||||
lines.add(line);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class FormattableDocumentTest {
|
|||
idlist a
|
||||
'''
|
||||
formatter = [ IDList model, extension regions, extension document |
|
||||
model.regionForKeyword("idlist").append[oneSpace]
|
||||
model.regionFor.keyword("idlist").append[oneSpace]
|
||||
]
|
||||
expectation = '''
|
||||
idlist a
|
||||
|
@ -50,7 +50,7 @@ class FormattableDocumentTest {
|
|||
idlist aaa bbb ccc ddd eee fff
|
||||
'''
|
||||
formatter = [ IDList model, extension regions, extension document |
|
||||
model.regionsForRuleCallsTo(IDRule).forEach[prepend[autowrap; oneSpace]]
|
||||
model.regionFor.ruleCallsTo(IDRule).forEach[prepend[autowrap; oneSpace]]
|
||||
]
|
||||
expectation = '''
|
||||
idlist aaa
|
||||
|
@ -69,11 +69,11 @@ class FormattableDocumentTest {
|
|||
kwlist kw1 kw2 kw3 kw4
|
||||
'''
|
||||
formatter = [ KWList model, extension regions, extension document |
|
||||
model.regionForKeyword("kwlist").append[autowrap(6); oneSpace]
|
||||
model.regionForKeyword("kw1").append[autowrap; oneSpace]
|
||||
model.regionForKeyword("kw2").append[autowrap; oneSpace]
|
||||
model.regionForKeyword("kw3").append[autowrap; oneSpace]
|
||||
model.regionForKeyword("kw4").append[autowrap; newLine]
|
||||
model.regionFor.keyword("kwlist").append[autowrap(6); oneSpace]
|
||||
model.regionFor.keyword("kw1").append[autowrap; oneSpace]
|
||||
model.regionFor.keyword("kw2").append[autowrap; oneSpace]
|
||||
model.regionFor.keyword("kw3").append[autowrap; oneSpace]
|
||||
model.regionFor.keyword("kw4").append[autowrap; newLine]
|
||||
]
|
||||
expectation = '''
|
||||
kwlist
|
||||
|
@ -95,12 +95,12 @@ class FormattableDocumentTest {
|
|||
model.formatConditionally(
|
||||
[ doc |
|
||||
val extension fits = doc.requireFitsInLine
|
||||
model.regionForKeyword("kwlist").append[oneSpace]
|
||||
model.regionForKeyword("kw1").append[oneSpace]
|
||||
model.regionFor.keyword("kwlist").append[oneSpace]
|
||||
model.regionFor.keyword("kw1").append[oneSpace]
|
||||
],
|
||||
[ extension doc |
|
||||
model.regionForKeyword("kwlist").append[newLine]
|
||||
model.regionForKeyword("kw1").append[newLine]
|
||||
model.regionFor.keyword("kwlist").append[newLine]
|
||||
model.regionFor.keyword("kw1").append[newLine]
|
||||
])
|
||||
]
|
||||
expectation = '''
|
||||
|
@ -123,12 +123,12 @@ class FormattableDocumentTest {
|
|||
model.formatConditionally(
|
||||
[ doc |
|
||||
val extension fits = doc.requireFitsInLine
|
||||
model.regionForKeyword("kwlist").append[oneSpace]
|
||||
model.regionForKeyword("kw1").append[oneSpace]
|
||||
model.regionFor.keyword("kwlist").append[oneSpace]
|
||||
model.regionFor.keyword("kw1").append[oneSpace]
|
||||
],
|
||||
[ extension doc |
|
||||
model.regionForKeyword("kwlist").append[newLine]
|
||||
model.regionForKeyword("kw1").append[newLine]
|
||||
model.regionFor.keyword("kwlist").append[newLine]
|
||||
model.regionFor.keyword("kw1").append[newLine]
|
||||
])
|
||||
]
|
||||
expectation = '''
|
||||
|
|
|
@ -52,7 +52,7 @@ class FormatterSerializerIntegrationTest {
|
|||
|
||||
static class Formatter extends AbstractFormatter2 {
|
||||
def dispatch format(IDList model, extension IFormattableDocument document) {
|
||||
model.regionForKeyword("idlist").append[space = " "]
|
||||
model.regionFor.keyword("idlist").append[space = " "]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.eclipse.xtext.formatting2.IFormattableDocument
|
|||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess
|
||||
import org.eclipse.xtext.junit4.formatter.FormatterTestRequest
|
||||
import org.eclipse.xtext.junit4.formatter.FormatterTester
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionExtensions
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
|
@ -53,8 +54,8 @@ class GenericFormatterTestRequest extends FormatterTestRequest {
|
|||
@Accessors
|
||||
abstract class GenericFormatter<T extends EObject> extends AbstractFormatter2 {
|
||||
def dispatch format(EObject obj, IFormattableDocument document) {
|
||||
format(obj as T, request.textRegionAccess, document)
|
||||
format(obj as T, request.textRegionAccess.extensions, document)
|
||||
}
|
||||
|
||||
def protected abstract void format(T model, ITextRegionAccess regionAccess, IFormattableDocument document)
|
||||
def protected abstract void format(T model, ITextRegionExtensions regionAccess, IFormattableDocument document)
|
||||
}
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* 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 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
|
||||
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 com.google.common.collect.ImmutableList
|
||||
|
||||
/**
|
||||
* @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
|
||||
@Inject extension RegionAccessTestLanguageGrammarAccess
|
||||
|
||||
@Test def void regionForFeatureAttribute() {
|
||||
val mixed = '''6 (foo)'''.parseAs(Mixed)
|
||||
val access = mixed.toAccess
|
||||
val actual = access.regionForFeature(mixed, MIXED__NAME)
|
||||
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)
|
||||
val actuals = access.regionsForFeatures(mixed.child, MIXED__REF)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForFeatureContainmentReference() {
|
||||
val mixed = '''6 (foo) action'''.parseAs(AssignedAction)
|
||||
val access = mixed.toAccess
|
||||
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 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.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 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.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 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)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
try {
|
||||
access.regionsForRuleCalls(mixed, mixedAccess.eobjMixedParserRuleCall_2_2_1_1_0)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@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 <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
|
||||
}
|
||||
|
||||
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<?>)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*******************************************************************************
|
||||
* 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 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
|
||||
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 com.google.common.collect.ImmutableList
|
||||
import org.eclipse.xtext.formatting2.regionaccess.internal.regionaccesstestlanguage.Expression
|
||||
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion
|
||||
|
||||
/**
|
||||
* @author Moritz Eysholdt - Initial contribution and API
|
||||
*/
|
||||
@RunWith(XtextRunner)
|
||||
@InjectWith(RegionAccessTestLanguageInjectorProvider)
|
||||
class SemanticRegionFinderTest {
|
||||
@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 finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.feature(MIXED__NAME)
|
||||
val actuals = finder.features(MIXED__NAME)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForFeatureCrossReference() {
|
||||
val mixed = '''6 (ref foo) action (foo) end'''.parseAs(AssignedAction)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed.child)
|
||||
val actual = finder.regionFor.feature(MIXED__REF)
|
||||
val actuals = finder.regionFor.features(MIXED__REF)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForFeatureContainmentReference() {
|
||||
val mixed = '''6 (foo) action'''.parseAs(AssignedAction)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
try {
|
||||
finder.feature(ASSIGNED_ACTION__CHILD)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
try {
|
||||
finder.features(ASSIGNED_ACTION__CHILD)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallUnassignedTerminal() {
|
||||
val mixed = '''6 (unassigned foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCall(mixedAccess.IDTerminalRuleCall_1_1_0)
|
||||
val actuals = finder.ruleCalls(mixedAccess.IDTerminalRuleCall_1_1_0)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallToUnassignedTerminal() {
|
||||
val mixed = '''6 (unassigned foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCallTo(IDRule)
|
||||
val actuals = finder.ruleCallsTo(IDRule)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallUnassignedDataType() {
|
||||
val mixed = '''6 (unassigned datatype foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCall(mixedAccess.datatypeParserRuleCall_1_1_1)
|
||||
val actuals = finder.ruleCalls(mixedAccess.datatypeParserRuleCall_1_1_1)
|
||||
assertEquals("datatype foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallToUnassignedDataType() {
|
||||
val mixed = '''6 (unassigned datatype foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCallTo(datatypeRule)
|
||||
val actuals = finder.ruleCallsTo(datatypeRule)
|
||||
assertEquals("datatype foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallAssignedTerminal() {
|
||||
val mixed = '''6 (foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCall(mixedAccess.nameIDTerminalRuleCall_2_2_0_0)
|
||||
val actuals = finder.ruleCall(mixedAccess.nameIDTerminalRuleCall_2_2_0_0)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallToAssignedTerminal() {
|
||||
val mixed = '''6 (foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCallTo(IDRule)
|
||||
val actuals = finder.ruleCallTo(IDRule)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallAssignedDataType() {
|
||||
val mixed = '''6 (datatype foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCall(mixedAccess.datatypeDatatypeParserRuleCall_2_2_2_0)
|
||||
val actuals = finder.ruleCall(mixedAccess.datatypeDatatypeParserRuleCall_2_2_2_0)
|
||||
assertEquals("datatype foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallToAssignedDataType() {
|
||||
val mixed = '''6 (datatype foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.ruleCallTo(datatypeRule)
|
||||
val actuals = finder.ruleCallTo(datatypeRule)
|
||||
assertEquals("datatype foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallCrossReference() {
|
||||
val mixed = '''6 (ref foo) action (foo) end'''.parseAs(AssignedAction)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed.child).regionFor
|
||||
val actual = finder.ruleCall(mixedAccess.refMixedIDTerminalRuleCall_2_2_3_1_0_1)
|
||||
val actuals = finder.ruleCalls(mixedAccess.refMixedIDTerminalRuleCall_2_2_3_1_0_1)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallToCrossReference() {
|
||||
val mixed = '''6 (ref foo) action (foo) end'''.parseAs(AssignedAction)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed.child).regionFor
|
||||
val actual = finder.ruleCallTo(IDRule)
|
||||
val actuals = finder.ruleCallsTo(IDRule)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallEObjectParserRule() {
|
||||
val mixed = '''6 (child (foo))'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
try {
|
||||
finder.ruleCall(mixedAccess.eobjMixedParserRuleCall_2_2_1_1_0)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
try {
|
||||
finder.ruleCalls(mixedAccess.eobjMixedParserRuleCall_2_2_1_1_0)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test def void regionForRuleCallToEObjectParserRule() {
|
||||
val mixed = '''6 (child (foo))'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
try {
|
||||
finder.ruleCallTo(mixedRule)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
try {
|
||||
finder.ruleCallsTo(mixedRule)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test def void regionForKeywordString() {
|
||||
val mixed = '''6 (foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.keyword("(")
|
||||
val actuals = finder.keywords("(")
|
||||
assertEquals("(", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForKeyword() {
|
||||
val mixed = '''6 (foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.keyword(mixedAccess.leftParenthesisKeyword_0)
|
||||
val actuals = finder.keywords(mixedAccess.leftParenthesisKeyword_0)
|
||||
assertEquals("(", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForCrossReference() {
|
||||
val mixed = '''6 (ref foo) action (foo) end'''.parseAs(AssignedAction)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed.child).regionFor
|
||||
val actual = finder.crossRef(mixedAccess.refMixedCrossReference_2_2_3_1_0)
|
||||
val actuals = finder.crossRefs(mixedAccess.refMixedCrossReference_2_2_3_1_0)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForAssignment() {
|
||||
val mixed = '''6 (foo)'''.parseAs(Mixed)
|
||||
val finder = mixed.toAccess.regionForEObject(mixed).regionFor
|
||||
val actual = finder.assignment(mixedAccess.nameAssignment_2_2_0)
|
||||
val actuals = finder.assignments(mixedAccess.nameAssignment_2_2_0)
|
||||
assertEquals("foo", actual, actuals)
|
||||
}
|
||||
|
||||
@Test def void regionForKeywordPairs() {
|
||||
val extension rule = parenthesizedAccess
|
||||
val expr = '''5 (foo)'''.parseAs(Expression)
|
||||
val finder = expr.toAccess.regionForEObject(expr).regionFor
|
||||
val actual1 = finder.keywordPairs("(", ")").pairsToString
|
||||
val actual2 = finder.keywordPairs(leftParenthesisKeyword_0, rightParenthesisKeyword_2).pairsToString
|
||||
val expected = "(foo)"
|
||||
assertEquals(expected, actual1)
|
||||
assertEquals(expected, actual2)
|
||||
}
|
||||
|
||||
@Test def void regionForKeywordPairs2() {
|
||||
val extension rule = parenthesizedAccess
|
||||
val expr = '''5 (a + ((b) + c) + d)'''.parseAs(Expression)
|
||||
val finder = expr.toAccess.regionForRootEObject.allRegionsFor
|
||||
val actual1 = finder.keywordPairs("(", ")").pairsToString
|
||||
val actual2 = finder.keywordPairs(leftParenthesisKeyword_0, rightParenthesisKeyword_2).pairsToString
|
||||
val expected = "(b); ((b) + c); (a + ((b) + c) + d)"
|
||||
assertEquals(expected, actual1)
|
||||
assertEquals(expected, actual2)
|
||||
}
|
||||
|
||||
def private String pairsToString(Iterable<Pair<ISemanticRegion, ISemanticRegion>> pairs) {
|
||||
pairs.map[key.merge(value).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
|
||||
}
|
||||
|
||||
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