For editing model contents, we might use a grammar that differs from the one used for model serialization.
A typical use-case may allow to change the order of features in order to allow only a subset of them to be modified.
As an example, assume the following Xtext grammar snippet:
grammar org.eclipse.xtext.example.fowlerdsl.Statemachine with org.eclipse.xtext.common.Terminals
generate statemachine "http://www.eclipse.org/xtext/example/fowlerdsl/Statemachine"
Statemachine :
{Statemachine}
('events'
events+=Event+
'end')?
// ...
;
Event:
name=ID code=INT? ('[' guard=Guard ']')?
;
// ...
In our editor, we want the end-user to edit only the name
and guard
features of Event
. This is not possible with the given grammar, as code
is placed between them.
To solve this, we create a new language:
grammar org.eclipse.xtext.example.fowlerdsl.InlineEdit with org.eclipse.xtext.example.fowlerdsl.Statemachine
import "http://www.eclipse.org/xtext/example/fowlerdsl/Statemachine"
import "http://www.eclipse.org/emf/2002/Ecore" as ecore
InlineStatemachine returns Statemachine: (1)
Statemachine
;
//@Override (2)
Event:
name=ID ('[' guard=Guard ']')? code=INT?
;
-
We have to have a root rule, because Xtext uses the first rule as entry rule. We just forward to the original root rule.
-
Newer Xtext version know the
@Override
annotation to redefine a rule.
This creates a grammar (for the identical metamodel) that serializes features name
and guard
adjacent to each other, so we can limit the editor to them.
The editing grammar must fulfill the following criteria:
-
based on identical metamodel
-
has same root element
-
contains rules for all semantic elements also covered by the original grammar (either inherited or self-implemented)
-
must serialize correctly from a model without any previous textual representation
We might need to fix serialization issues in this approach.
If we experience serialization issues, namely keywords get merged resulting in invalid syntax, we can use a workaround provided by com.altran.general.integration.xtextsirius.runtime
plug-in.
Typical symptoms of this issue include invalid auto-completion suggestions in the editor and exceptions on committing the changed elements.
To fix this, register the following classes to the editing language:
public class InlineEditRuntimeModule extends org.eclipse.xtext.example.fowlerdsl.AbstractInlineEditRuntimeModule {
public Class<? extends IHiddenTokenSequencer> bindIHiddenTokenSequencer() {
return com.altran.general.integration.xtextsirius.runtime.serializer.ForceWhitespaceBetweenKeywordsHiddenTokenSequencer.class;
}
public Class<? extends TextRegionAccessBuilder> bindTextRegionAccessBuilder() {
return com.altran.general.integration.xtextsirius.runtime.serializer.ForceWhitespaceBetweenKeywordsTextRegionAccessBuilder.class;
}
}
Especially if we ignored nested features, we might need to adjust the serializer to be able to provide a suitable adjusted grammar.
Assume the following Xtext grammar snippet defining an UML-like Association, to be displayed as edge:
Association:
name=ID
target=TypeRef
;
// also used at lots of other places
TypeRef:
lowerBound=INT '..' upperBound=INT type=[Type]
;
Note that TypeRef.type
is mandatory.
Example model:
wheels 1..4 RubberWheel
We want to ignore target.type
for Association
(as described) and use the following adjusted grammar:
Association:
name=ID
target=AssociationTypeRef
;
AssociationTypeRef returns TypeRef:
lowerBound=INT '..' upperBound=INT
;
// also used at lots of other places
TypeRef:
lowerBound=INT '..' upperBound=INT type=[Type]
;
This won’t work, because the Xtext serializer refuses to serialize a model if it cannot find a grammar rule for a mandatory feature.
We solve this by registering a special org.eclipse.xtext.serializer.sequencer.TransientValueService
to our adjusted grammar:
public class InlineEditRuntimeModule extends org.eclipse.xtext.example.fowlerdsl.AbstractInlineEditRuntimeModule {
public Class<? extends ITransientValueService> bindSerializerTransientValueService() {
return com.altran.general.integration.xtextsirius.runtime.ignoredfeature.IgnoredFeatureTransientValueService.class;
}
}
Sirius has its own session management, including the contained Ecore resources. We might want to limit the Xtext editor’s global scope to these resources.
A special org.eclipse.xtext.scoping.IGlobalScopeProvider
achieves this:
public class InlineEditRuntimeModule extends org.eclipse.xtext.example.fowlerdsl.AbstractInlineEditRuntimeModule {
public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
return com.altran.general.integration.xtextsirius.runtime.resource.XtextSiriusResourceSetGlobalScopeProvider.class;
}
}
Caution
|
This copies the contents of all Ecore resources from a Sirius session when showing an Xtext/Sirius editor. Depending on the number and size of the resources, the processing and memory performance might be significant. |
EMF’s default validator EObjectValidator
raises an issue if a list contains more than one element with the same EKey.
However, the error message is not always helpful, and it’s assigned to the complete container.
We provide an additional validator com.altran.general.integration.xtextsirius.runtime.validator.DuplicateEKeyValidatorFast
to fix the described limitations.