Some Actions + typechecking stop working in reference-based editors


I encountered that some actions do not work when using reference-based editors. Imagine we have the following concept and editors: 




Then, we should be able to edit the class's members either from a MyRefToClass or the ClassConcept node. For instance:

 ClassConcept editor window:

MyRefToClass editor window:

However, when I want to create a DotExpression from within the MyRefToClass editor the dotExpression side transformation is not called (in the ClassConcept editor everything works just fine):

I checked whether transformations do not work in general. But I can add the static keyword, for instance, to a method, which transforms the declaration. I can go to the ClassConcept editor component and add the dot. However, in the MyRefToClass editor the code completion menu is also not available then.

Moreover, the type checker indicators (those curved lines) are not shown.

Is this desired behavior or a bug?




Official comment

This all boils down to the type-system not calculating the types.

You are taking the editor to an extreme here by editing nodes from different roots and this case is not yet supported by the MPS type-system. The current editor contract mandates that only nodes with a common root can be edited within a single editor.

The typechecker is bound to the editor and the root (context) that the editor correspond to, which allows it to perform an efficient incremental calculation of types. This context is kept in the editor and is bound to the root being edited. Thus, when faced with a node coming from some other root (not in the current context), the typechecker simply returns null as its type.

Thanks for the feedback and detailed explanation! I already opened an issue in youtrack:

The problem is that this functionality is crucial to our project. For instance, one can open the class directly, but also using this other root node, which maintains a reference to a certain class (+ some additional information necessary for the projection). The developer then can move fluidly between the views and even having them side by side. Except for DotExpressions and AutoCompletion in DotExpressions the projections works already quite nicely. 

Thus, at least having a workaround would be great (i.e. just DotExpressions + AutoCompletion). Do you have any ideas? I already thought about using node attributes, but this would get very complicated, because we leverage the editor root with its additional information during creation of StatementLists. Moreover, I don't know how to handle the ambiguity introduced by multiple attributes in this context. In other words, which attribute we are currently working in.

And another naive question: would it be possible to bind the typechecker to model roots instead of editor roots?

Thanks a lot!


Thanks again for the hint. It helped me to workaround the problem. This is currently just a simple hack, but good enough for our proof of concept.

I extended public SNode getNode() {...} in, which returns the current root node. Instead of just returning myRootNode, I checked myRootNode's concept and if its MyRefToClass, I return the referenced node (i.e., the class).

public SNode getNode() {
if (SNodeOperations.isInstanceOf(myRootNode, MetaAdapterFactory.getConcept(0x5f8cf3a359fa48d7L, 0x8ec0d1097b3dfb6bL, 0x6d26c76a87f2650bL, "RefEditor_TestLanguage.structure.MyRefToClass"))) {
return SLinkOperations.getTarget(myRootNode, MetaAdapterFactory.getReferenceLink(0x5f8cf3a359fa48d7L, 0x8ec0d1097b3dfb6bL, 0x6d26c76a87f2650bL, 0x6d26c76a87f2650cL, "class"));
return myRootNode;

Sure it is a dirty hack, and thus it would be nice to have support for this issue in the future.



We found a more maintainable solution using the EditorComponentCreateListener–that is, the issue is fixed.

project plugin ReferenceBasedEditor {                                                                                               
private EditorComponentCreationListener myEditorComponentCreationListener;

init(project)->void {
this.myEditorComponentCreationListener = new EditorComponentCreationListener(project) {
public void editorComponentCreate(EditorComponent editorComponent) {
if (editorComponent.getEditedNode().isInstanceOfConcept(concept/Ref_ClassConcept/)) {
((node<Ref_ClassConcept >) editorComponent.getEditedNode().theClassToWhichIRefer,
public void editorComponentDisposed(EditorComponent editorComponent) {
<no statements>

dispose(project)->void {

Please sign in to leave a comment.