From ad6ba6d6066d2f5efbc615b2dbf99f129997f8fc Mon Sep 17 00:00:00 2001
From: Moritz Eysholdt
Date: Thu, 23 Apr 2015 13:45:56 +0200
Subject: [PATCH] [formatter] made mechanism for finding text regions more
powerful
Signed-off-by: Moritz Eysholdt
---
.../xtext/formatting2/AbstractFormatter2.java | 19 +-
.../xtext/formatting2/FormatterRequest.java | 64 ++-
.../debug/TextRegionAccessToString.java | 11 +-
.../debug/TextRegionsInTextToString.java | 2 +-
.../debug/TextRegionsWithTitleToString.java | 9 +-
.../internal/FormattableDocument.java | 20 +-
.../regionaccess/IEObjectRegion.java | 9 +-
.../regionaccess/ISemanticRegionFinder.java | 38 ++
.../regionaccess/ISemanticRegionsFinder.java | 46 ++
.../regionaccess/ISequentialRegion.java | 14 +-
.../regionaccess/ITextRegionAccess.java | 140 +-----
.../regionaccess/ITextRegionExtensions.java | 67 +++
.../regionaccess/TextRegionAccessBuilder.java | 6 +-
.../internal/AbstractEObjectRegion.java | 32 +-
.../internal/AbstractHiddenRegion.java | 11 +
.../internal/AbstractRegionAccess.java | 368 ++++------------
.../AbstractSemanticRegionsFinder.java | 410 ++++++++++++++++++
.../internal/AbstractTextSegment.java | 2 +-
.../internal/NodeModelBasedRegionAccess.java | 24 +-
.../NodeModelBasedRegionAccessBuilder.java | 2 +-
.../internal/NodeSemanticRegion.java | 31 +-
.../SemanticRegionInIterableFinder.java | 44 ++
.../internal/SemanticRegionIterable.java | 47 ++
.../internal/SemanticRegionMatcher.java | 42 ++
.../internal/SemanticRegionNullFinder.java | 120 +++++
.../internal/StringBasedRegionAccess.java | 16 +-
.../internal/StringEObjectRegion.java | 1 -
.../internal/StringSemanticRegion.java | 11 +
.../TextRegionAccessBuildingSequencer.java | 2 +-
.../regionaccess/internal/TextRegions.java | 16 +
.../internal/FormattableDocumentTest.xtend | 30 +-
.../FormatterSerializerIntegrationTest.xtend | 2 +-
.../internal/GenericFormatterTester.xtend | 5 +-
.../internal/RegionAccessAccessTest.xtend | 170 --------
.../internal/SemanticRegionFinderTest.xtend | 261 +++++++++++
35 files changed, 1366 insertions(+), 726 deletions(-)
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionFinder.java
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionsFinder.java
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionExtensions.java
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractSemanticRegionsFinder.java
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionInIterableFinder.java
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionIterable.java
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionMatcher.java
create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionNullFinder.java
delete mode 100644 tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend
create mode 100644 tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionFinderTest.xtend
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractFormatter2.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractFormatter2.java
index a35badc3b..b739f05f7 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractFormatter2.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/AbstractFormatter2.java
@@ -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;
*
*
*
- * 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}.
*
*
*
@@ -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 postProcess(IFormattableDocument document, List replacements) {
List 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;
}
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/FormatterRequest.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/FormatterRequest.java
index 2c09185ea..80e335457 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/FormatterRequest.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/FormatterRequest.java
@@ -22,17 +22,26 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
- * A request tells the formatter what and how to format.
+ *
+ * A request tells the formatter what and how to format.
+ *
*
- * When invoking the formatter, the request is passed into {@link IFormatter2#format(FormatterRequest)}.
+ *
+ * When invoking the formatter, the request is passed into {@link IFormatter2#format(FormatterRequest)}.
+ *
*
- * A request carries information about:
+ *
+ * A request carries information about:
+ *
*
* - The {@link #textRegionAccess} which allows to obtain the to-be-formatted semantic model with text regions.
* - {@link #preferences Preferences} with keys from e.g. {@link FormatterPreferenceKeys}.
- * - {@link #regions} that describe how to restrict the text regions for which {@link ITextReplacement replacements} are produced.
- * - An option to {@link #allowIdentityEdits()} which will disable automated suppression of text replacements that do not cause changes.
- * - A setting for green-field formatting ({@link #formatUndefinedHiddenRegionsOnly}): only format regions that have no whitespace information yet.
+ * - {@link #regions} that describe how to restrict the text regions for which {@link ITextReplacement replacements}
+ * are produced.
+ * - An option to {@link #allowIdentityEdits()} which will disable automated suppression of text replacements that do
+ * not cause changes.
+ * - A setting for green-field formatting ({@link #formatUndefinedHiddenRegionsOnly}): only format regions that have
+ * no whitespace information yet.
*
*
* @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 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 {
}
/**
- * 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.
+ *
+ * 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.
+ *
*
- * Logging exceptions and continuing formatting is the default behavior.
+ *
+ * Logging exceptions and continuing formatting is the default behavior.
+ *
*
- * Throwing exceptions is useful in unit tests.
+ *
+ * Throwing exceptions is useful in unit tests.
+ *
*
- * Ignoring exceptions is useful when formatting a document with syntax errors.
+ *
+ * Ignoring exceptions is useful when formatting a document with syntax errors.
+ *
*
- * Defaults to the {@link ExceptionAcceptor#LOGGING Logging Acceptor}
+ *
+ * Defaults to the {@link ExceptionAcceptor#LOGGING Logging Acceptor}
+ *
*
* @see ExceptionAcceptor#LOGGING
* @see ExceptionAcceptor#THROWING
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionAccessToString.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionAccessToString.java
index 708673a2d..6b01223ea 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionAccessToString.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionAccessToString.java
@@ -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 hiddens = LinkedListMultimap.create();
List errors = Lists.newArrayList();
ITextRegionAccess access = list.get(0).getTextRegionAccess();
- List objects = access.regionsForAllEObjects();
- for (IEObjectRegion obj : objects) {
+ TreeIterator 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
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsInTextToString.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsInTextToString.java
index 4a7b0df32..557413128 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsInTextToString.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsInTextToString.java
@@ -66,7 +66,7 @@ public class TextRegionsInTextToString {
ITextRegionAccess access = getTextRegionAccess();
if (access != null) {
ITextSegment impactRegion = TextRegions.merge(this.items);
- List expandToLines = access.expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
+ List expandToLines = TextRegions.expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
return TextRegions.merge(expandToLines);
}
return null;
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsWithTitleToString.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsWithTitleToString.java
index 56ab64db5..b49d756c2 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsWithTitleToString.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/debug/TextRegionsWithTitleToString.java
@@ -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 segments = Lists.newArrayList();
for (Item item : items)
segments.add(item.getRegion());
- ITextSegment impactRegion = TextRegions.merge(segments);
- List expandToLines = access.expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
- return TextRegions.merge(expandToLines);
+ ITextSegment impactRegion = merge(segments);
+ List expandToLines = expandToLines(impactRegion, getLeadingLines(), getTrailingLines());
+ return merge(expandToLines);
}
return null;
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/internal/FormattableDocument.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/internal/FormattableDocument.java
index 7ba939d1e..c2bad9052 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/internal/FormattableDocument.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/internal/FormattableDocument.java
@@ -87,8 +87,11 @@ public abstract class FormattableDocument implements IFormattableDocument {
@Override
public 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 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;
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/IEObjectRegion.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/IEObjectRegion.java
index 0ceada12e..c55fab6ce 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/IEObjectRegion.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/IEObjectRegion.java
@@ -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 getSemanticLeafRegions();
+ Iterable getAllSemanticRegions();
+
+ ISemanticRegionsFinder getRegionFor();
+
+ Iterable getSemanticRegions();
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionFinder.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionFinder.java
new file mode 100644
index 000000000..8057bbebc
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionFinder.java
@@ -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);
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionsFinder.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionsFinder.java
new file mode 100644
index 000000000..4a8da7d66
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISemanticRegionsFinder.java
@@ -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 assignments(Assignment... assignments);
+
+ List crossRefs(CrossReference... crossReferences);
+
+ List elements(AbstractElement... elements);
+
+ List features(EStructuralFeature... features);
+
+ List> keywordPairs(Keyword kw1, Keyword kw2);
+
+ List> keywordPairs(String kw1, String kw2);
+
+ List keywords(Keyword... keywords);
+
+ List keywords(String... keywords);
+
+ List ruleCalls(RuleCall... ruleCalls);
+
+ List ruleCallsTo(AbstractRule... rules);
+
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISequentialRegion.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISequentialRegion.java
index eeb9f7d17..1bc9e3bdf 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISequentialRegion.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ISequentialRegion.java
@@ -8,15 +8,23 @@
package org.eclipse.xtext.formatting2.regionaccess;
/**
- * Common interface for {@link IHiddenRegion} and {@link ISemanticRegion}.
+ *
+ * Common interface for {@link IHiddenRegion} and {@link ISemanticRegion}.
+ *
*
- * {@link IHiddenRegion} and {@link ISemanticRegion} are arranged strictly alternating in a linked list. This interface
- * provides the method to navigate that list.
+ *
+ * {@link IHiddenRegion} and {@link ISemanticRegion} are arranged strictly alternating in a linked list. This interface
+ * provides the method to navigate that list.
+ *
*
* @author Moritz Eysholdt - Initial contribution and API
*/
public interface ISequentialRegion extends ITextSegment {
+ ISemanticRegionFinder immediatelyFollowing();
+
+ ISemanticRegionFinder immediatelyPreceding();
+
IHiddenRegion getNextHiddenRegion();
ISemanticRegion getNextSemanticRegion();
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java
index 30b2efe16..4c68ce1de 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionAccess.java
@@ -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 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 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 regionsForRuleCalls(EObject owner, RuleCall... ruleCalls);
-
- List 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 regionsForKeywords(EObject owner, String... keywords);
-
- List regionsForKeywords(EObject owner, Keyword... keywords);
-
- List regionsForCrossRefs(EObject owner, CrossReference... crossRefs);
-
- /**
- * @return All {@link ISemanticRegion semantic regions} that represent a RuleCall to one of the provided
- * AbstractRules and directly belong to the provided 'EObject'. RuleCalls of child-EObjects are not
- * considered. May be null.
- */
- List 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();
+
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionExtensions.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionExtensions.java
new file mode 100644
index 000000000..f308db604
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/ITextRegionExtensions.java
@@ -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 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 semanticRegions(EObject object);
+
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/TextRegionAccessBuilder.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/TextRegionAccessBuilder.java
index 698fa15a1..b788ac289 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/TextRegionAccessBuilder.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/TextRegionAccessBuilder.java
@@ -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() {
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractEObjectRegion.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractEObjectRegion.java
index 32e7b3166..7c36b202f 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractEObjectRegion.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractEObjectRegion.java
@@ -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 getSemanticLeafRegions() {
+ public List 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 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;
}
+
}
\ No newline at end of file
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractHiddenRegion.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractHiddenRegion.java
index a0f76e8c4..e8c60d6d0 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractHiddenRegion.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractHiddenRegion.java
@@ -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());
+ }
}
\ No newline at end of file
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java
index dffdade92..a7ac3d461 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractRegionAccess.java
@@ -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 allSemanticRegions(EObject object) {
+ AbstractEObjectRegion region = regionForEObject(object);
+ if (region == null)
+ return Collections.emptyList();
+ return region.getAllSemanticRegions();
+ }
+
+ @Override
+ public Iterable 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 regionsForCrossRefs(EObject owner, CrossReference... crossRefs) {
- List rcs = Lists.newArrayList();
- for (int i = 0; i < crossRefs.length; i++)
- rcs.add((RuleCall) crossRefs[i].getTerminal());
- return regionsForRuleCalls(owner, rcs.toArray(new RuleCall[rcs.size()]));
- }
-
- protected void assertNoContainment(EStructuralFeature feat) {
- if (!(feat instanceof EAttribute) && !(feat instanceof EReference && !((EReference) feat).isContainment()))
- throw new IllegalStateException("Only EAttributes and CrossReferences allowed.");
- }
-
- @Override
- public List regionsForFeatures(EObject owner, EStructuralFeature... features) {
- Set 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 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 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 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 regionsForKeywords(EObject owner, String... keywords) {
- AbstractEObjectRegion tokens = regionForEObject(owner);
- if (tokens == null)
- return Collections.emptyList();
- Collection kwSet = keywords.length <= 1 ? Arrays.asList(keywords) : Sets.newHashSet(keywords);
- List 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 regionsForKeywords(EObject owner, Keyword... keywords) {
- AbstractEObjectRegion tokens = regionForEObject(owner);
- if (tokens == null)
- return Collections.emptyList();
- List result = Lists.newArrayList();
- for (ISemanticRegion region : tokens.getSemanticLeafRegions())
- for (int i = 0; i < keywords.length; i++)
- if (region.getGrammarElement() == keywords[i])
- result.add(region);
- return ImmutableList.copyOf(result);
- }
-
- @Override
- public List regionsForRuleCallsTo(EObject owner, AbstractRule... rule) {
- HashSet set = Sets.newHashSet(rule);
- AbstractEObjectRegion tokens = regionForEObject(owner);
- if (tokens == null)
- return Collections.emptyList();
- List 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 expandToLines(ITextSegment segment, int leadingLinesToAdd, int trailingLinesToAdd) {
- List 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();
}
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractSemanticRegionsFinder.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractSemanticRegionsFinder.java
new file mode 100644
index 000000000..31c9f18b8
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractSemanticRegionsFinder.java
@@ -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 {
+
+ 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 {
+ 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 {
+ 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 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 {
+ 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 {
+ private final Set keywords;
+
+ public KeywordsPredicate(Set 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 {
+ 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 {
+ private final Set rules;
+
+ public RulesPredicate(Set 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 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 assignments(Assignment... assignments) {
+ return findAll(createPredicate(assignments));
+ }
+
+ protected void collectMatchableElements(AbstractElement ele, Collection 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 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 createPredicate(AbstractElement... ele) {
+ Set 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 crossRefs(CrossReference... crossReferences) {
+ return findAll(createPredicate(crossReferences));
+ }
+
+ @Override
+ public ISemanticRegion element(AbstractElement element) {
+ return findFirst(createPredicate(element));
+ }
+
+ @Override
+ public List elements(AbstractElement... elements) {
+ return findAll(createPredicate(elements));
+ }
+
+ @Override
+ public ISemanticRegion feature(EStructuralFeature feature) {
+ assertNoContainment(feature);
+ return findFirst(new FeaturePredicate(feature));
+ }
+
+ @Override
+ public List features(EStructuralFeature... features) {
+ Set> 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 findAll(Predicate predicate);
+
+ protected abstract ISemanticRegion findFirst(Predicate 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> keywordPairs(Keyword kw1, Keyword kw2) {
+ Preconditions.checkNotNull(kw1);
+ Preconditions.checkNotNull(kw2);
+ Preconditions.checkArgument(kw1 != kw2);
+ Predicate p1 = createPredicate(kw1);
+ Predicate p2 = createPredicate(kw2);
+ List all = findAll(Predicates.or(p1, p2));
+ Builder> result = ImmutableList.builder();
+ LinkedList stack = new LinkedList();
+ 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> keywordPairs(String kw1, String kw2) {
+ Preconditions.checkNotNull(kw1);
+ Preconditions.checkNotNull(kw2);
+ Preconditions.checkArgument(!kw1.equals(kw2));
+ Predicate p1 = new KeywordPredicate(kw1);
+ Predicate p2 = new KeywordPredicate(kw2);
+ List all = findAll(Predicates.or(p1, p2));
+ Builder> result = ImmutableList.builder();
+ LinkedList stack = new LinkedList();
+ 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 keywords(Keyword... keywords) {
+ return findAll(createPredicate(keywords));
+ }
+
+ @Override
+ public List keywords(String... keywords) {
+ Predicate 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 ruleCalls(RuleCall... ruleCalls) {
+ return findAll(createPredicate(ruleCalls));
+ }
+
+ @Override
+ public List ruleCallsTo(AbstractRule... rules) {
+ for (int i = 0; i < rules.length; i++)
+ assertNoEObjectRule(rules[i]);
+ Predicate 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));
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractTextSegment.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractTextSegment.java
index e477edb55..186e89cb9 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractTextSegment.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/AbstractTextSegment.java
@@ -67,7 +67,7 @@ public abstract class AbstractTextSegment implements ITextSegment {
@Override
public List getLineRegions() {
- ILineRegion current = getTextRegionAccess().lineForOffset(getOffset());
+ ILineRegion current = getTextRegionAccess().regionForLineAtOffset(getOffset());
List result = Lists.newArrayList();
int endOffset = getEndOffset();
while (current != null) {
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccess.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccess.java
index 48472840a..497608b2b 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccess.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccess.java
@@ -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 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 regionsForAllEObjects() {
- return ImmutableList. copyOf(eObjectToTokens.values());
+ public boolean hasSyntaxError() {
+ return resource.getParseResult().hasSyntaxErrors();
}
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccessBuilder.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccessBuilder.java
index 2205148eb..0900fc262 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccessBuilder.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeModelBasedRegionAccessBuilder.java
@@ -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;
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeSemanticRegion.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeSemanticRegion.java
index bb70aed69..58c49fb14 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeSemanticRegion.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/NodeSemanticRegion.java
@@ -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;
- }
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionInIterableFinder.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionInIterableFinder.java
new file mode 100644
index 000000000..3587ff9ab
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionInIterableFinder.java
@@ -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 regions;
+
+ public SemanticRegionInIterableFinder(Iterable regions) {
+ super();
+ this.regions = regions;
+ }
+
+ @Override
+ protected ImmutableList findAll(Predicate predicate) {
+ Builder builder = ImmutableList.builder();
+ for (ISemanticRegion region : regions)
+ if (predicate.apply(region))
+ builder.add(region);
+ return builder.build();
+ }
+
+ @Override
+ protected ISemanticRegion findFirst(Predicate predicate) {
+ for (ISemanticRegion region : regions)
+ if (predicate.apply(region))
+ return region;
+ return null;
+ }
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionIterable.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionIterable.java
new file mode 100644
index 000000000..4c3bb9349
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionIterable.java
@@ -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 {
+
+ private final ISemanticRegion first;
+ private final ISemanticRegion last;
+
+ public SemanticRegionIterable(ISemanticRegion first, ISemanticRegion last) {
+ super();
+ this.first = first;
+ this.last = last;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new AbstractIterator() {
+ 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;
+ }
+ };
+ }
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionMatcher.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionMatcher.java
new file mode 100644
index 000000000..2521d8dca
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionMatcher.java
@@ -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 findAll(Predicate predicate) {
+ ISemanticRegion element = findFirst(predicate);
+ if (element == null)
+ return ImmutableList.of();
+ else
+ return ImmutableList.of(element);
+ }
+
+ @Override
+ protected ISemanticRegion findFirst(Predicate predicate) {
+ if (predicate.apply(region))
+ return region;
+ return null;
+ }
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionNullFinder.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionNullFinder.java
new file mode 100644
index 000000000..59a0290a9
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionNullFinder.java
@@ -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 assignments(Assignment... assignments) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public ISemanticRegion crossRef(CrossReference crossReference) {
+ return null;
+ }
+
+ @Override
+ public List crossRefs(CrossReference... crossReferences) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public ISemanticRegion element(AbstractElement element) {
+ return null;
+ }
+
+ @Override
+ public List elements(AbstractElement... elements) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public ISemanticRegion feature(EStructuralFeature feature) {
+ return null;
+ }
+
+ @Override
+ public List 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> keywordPairs(Keyword kw1, Keyword kw2) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List> keywordPairs(String kw1, String kw2) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List keywords(Keyword... keywords) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List keywords(String... keywords) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public ISemanticRegion ruleCall(RuleCall ruleCall) {
+ return null;
+ }
+
+ @Override
+ public List ruleCalls(RuleCall... ruleCalls) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List ruleCallsTo(AbstractRule... rules) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public ISemanticRegion ruleCallTo(AbstractRule rule) {
+ return null;
+ }
+
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringBasedRegionAccess.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringBasedRegionAccess.java
index 2444bf0ad..b3d0757cb 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringBasedRegionAccess.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringBasedRegionAccess.java
@@ -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 regionsForAllEObjects() {
- return ImmutableList. copyOf(eObjectToTokens.values());
+ public boolean hasSyntaxError() {
+ return false;
}
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringEObjectRegion.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringEObjectRegion.java
index 053202d53..ff43decea 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringEObjectRegion.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringEObjectRegion.java
@@ -19,5 +19,4 @@ public class StringEObjectRegion extends AbstractEObjectRegion {
this.setGrammarElement(grammarElement);
this.setSemantcElement(semanticElement);
}
-
}
\ No newline at end of file
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringSemanticRegion.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringSemanticRegion.java
index f59bdd416..51966761b 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringSemanticRegion.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/StringSemanticRegion.java
@@ -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;
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegionAccessBuildingSequencer.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegionAccessBuildingSequencer.java
index a49efe655..e3f82df5f 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegionAccessBuildingSequencer.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegionAccessBuildingSequencer.java
@@ -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);
}
}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegions.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegions.java
index b8c1ef700..f50bf9d6a 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegions.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/formatting2/regionaccess/internal/TextRegions.java
@@ -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 expandToLines(ITextSegment segment, int leadingLinesToAdd, int trailingLinesToAdd) {
+ List 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;
+ }
+
}
diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormattableDocumentTest.xtend b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormattableDocumentTest.xtend
index bc03ca26d..8535c552f 100644
--- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormattableDocumentTest.xtend
+++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormattableDocumentTest.xtend
@@ -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 = '''
diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormatterSerializerIntegrationTest.xtend b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormatterSerializerIntegrationTest.xtend
index 67bcba0e0..181b43ef9 100644
--- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormatterSerializerIntegrationTest.xtend
+++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/FormatterSerializerIntegrationTest.xtend
@@ -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 = " "]
}
}
diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/GenericFormatterTester.xtend b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/GenericFormatterTester.xtend
index d3897ff87..dcfd8c411 100644
--- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/GenericFormatterTester.xtend
+++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/internal/GenericFormatterTester.xtend
@@ -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 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)
}
diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend
deleted file mode 100644
index 23e0bdeb1..000000000
--- a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/RegionAccessAccessTest.xtend
+++ /dev/null
@@ -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 parseHelper
- @Inject Provider 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 parseAs(CharSequence seq, Class 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>)
- }
-}
\ No newline at end of file
diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionFinderTest.xtend b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionFinderTest.xtend
new file mode 100644
index 000000000..66f20a415
--- /dev/null
+++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/formatting2/regionaccess/internal/SemanticRegionFinderTest.xtend
@@ -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 parseHelper
+ @Inject Provider 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> pairs) {
+ pairs.map[key.merge(value).text].join("; ")
+ }
+
+ def private parseAs(CharSequence seq, Class 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>)
+ }
+}
\ No newline at end of file