Implement custom editor behavior

Hi fellow MPS enthusiasts,

I am currently trying to implement a toy-language in order to familiarize myself with MPS. At this point I am hitting the wall concerning a specific editor behavior that I am trying to implement.
The context is as follows:

  • Let's assume I have concepts A, B, C which implement the INamedConcept interface.
  • concept A has [0..n] children of some concept B.
  • context C holds a [1] reference to an instance of concept B called refToB.

Now, the editor of concept C should allow me to do the following:
There is a cell for the refToB reference on which I can invoke the completion menu via CTRL+SPACE. This menu provides me with all possible target instances of concept B for this reference. However, the menu should not simply list all concept B instances but instead should list them as Parent.Child, i.e. NameOfParentInstanceA.NameOfChildInstanceB
Once I select any of the given options the cell should show NameOfParentInstanceA.NameOfChildInstanceB as well and not just the name of the referenced instance of concept B.

I tried to implement this with the standard cell models including custom cells but couldn't really make it work. I was also considering editor actions but to no avail.

I would be grateful for any leads.
Cheers Simon
8 comments
1. Use the constraints aspect of C the define the look of Bs in the completion menu:
link {b} 
  referent set handler:<none> 
  scope: 
    <default> 
  presentation : 
    (exists, referenceNode, contextNode, containingLink, linkTarget, operationContext, enclosingNode, model, position, contextRole, parameterNode, inEditor, smartReference, visible)->string { 
      parameterNode.parent : INamedConcept.name + ":" + parameterNode.name; 
    } 
;
prettyPrint();

2. Define a behaviour method on B to create a full name:
public string fullName() { 
  this.parent : INamedConcept.name + "->" + this.name; 
}       
prettyPrint();

BTW, you could be calling this method from 1. to reuse the functionality

3. Change the editor of C to use the fullName() method:
[- C { name } ( % b % -> * R/O model access * ) -]

Inspector tool window for the R/O model access cell:
...
(editorContext, node)->string { 
  node.fullName(); 
}
prettyPrint();
0
Hi Vaclav,

thanks. That worked like a charm. This was also a good and simple intro to behavior methods and how to use them.
0
Indeed :)
I may actually try to distill this into a short demo.
0
Hi Vaclav,

great idea. Well, I would like to extend my example from above with the following feature:
The completion menu for refToB shall exclude all those possible reference targets that have already been referenced elsewhere.

So let's assume I have something like:
A a1
    b1
    b2

A a2
    b3

C c1
    a1.b1

If I would now add C c2 the completion menu for refToB of c2 should only offer a1.b2 and a2.b3 since a1.b1 has already been referenced.

Can I extend the constraint aspect of C somehow or do I have to solve this via Typesystem?
0
Hi Simon,

for this you'll need to define the scopes for the reference. There are several ways to do so, each suitable for different needs. They are all described in the second part of the Calculator tutorial (https://www.jetbrains.com/mps/docs/tutorial.html#Creating_a_scope_for_InputFieldReference).

For this particular case I'd perhaps go with "reference" scope:

link {b} 
  referent set handler:<none> 
  scope: 
    (exists, referenceNode, contextNode, containingLink, linkTarget, operationContext, enclosingNode, model, position, contextRole)->Scope { 
      sequence<node<B>> bs = model.nodes(B).where({~b => model.nodes(C).all({~c => c.b :ne: b; }); }); 
      new ListScope(bs) { 
        public string getName(node<> child) { 
          child : B.name; 
        } 
      }; 
    } 
  presentation : 
    (exists, referenceNode, contextNode, containingLink, linkTarget, operationContext, enclosingNode, model, position, contextRole, parameterNode, inEditor, smartReference, visible)->string { 
      parameterNode.parent : INamedConcept.name + ":" + parameterNode.name; 
    } 
;
prettyPrint();

Vaclav
0
Hi Vaclav,

got it. This approach works to restrict the menu items. However once I select one item from the list the editor accepts it but indicates an error saying for instance "Error: reference b1 (refToB) is out of search scope". Where does this come from?
0
Well, we need to be smarter about listing the allowed "bs":
sequence<node<B>> bs = model.nodes(B).where({~b => model.nodes(C).where({~c => c :ne: contextNode; }).all({~c => c.b :ne: b; }); });
prettyPrint();
Clearly, the B that the current C refers to should also be in scope.
0
Alright, good point.
Well, everything came together nicely. Thanks for your help and discussion on this issue.
0

Please sign in to leave a comment.