Scope "come from" returns false for all children when in substitution menu

I'm using MPS 2017.2.2 (mbeddr 2017.2.0). I'm trying to use the scope language "come from" along with a Filter by concept scope helper method to limit what the user sees when they bring up the substitution menu (aka completion menu) via Cntrl-Space.

It appears that getScope() gets called twice:

1. First when user Cntrl-Space to bring up the substitution menu.

2. Secondly, after the user picks a reference from the substitution menu.

What I'm seeing is "come from" always returns false on the first getScope call but works as expected on the second getScope call.


Below is the getScope override for my SetOutputAction concept. The SetOutpAction has two children: output and expr.

public Scope getScope(concept<> kind, node<> child)
  overrides ScopeProvider.getScope {
  message info "kind: " + String.valueOf(kind) + ", child: " + String.valueOf(child), <no project>, <no throwable>;
  if (kind.isExactly(ISignal)) {
    message info String.valueOf(come from expr), <no project>, <no throwable>;
    message info String.valueOf(come from output), <no project>, <no throwable>;
    if (come from expr) {
      return new FilteringByConceptScope(parent scope, concept/IReadableSignal/);
    } else {
      return new FilteringByConceptScope(parent scope, concept/IWriteableSignal/);
  return null;

 Below is the message log showing the two calls.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    

Is this expected behaviour?                                                



Yes, this is expected if the "expr" child is null, when you press Control+Space. The "come from" construct returns the child node, on which the completion menu is tone shown. If the child node does not exist, "come from" returns null.



Yes, "expr" is an Expression concept which is null when SetOutputAction is first created (output is as well) so I now see scope isn't meant to be used the way I'm trying to use it.

The Expression is meant to have IReadableSignals only. Is the best way is to create a new type of expression say OutputActionExpression (that extends Expression) and then override getScope() on that? Then I just return filter concept scope on IReadableSignals?


The getScope() method should be defined on ScopeProviders - the nodes that have the possible reference targets as their children. In your case that would be the concepts that hold definitions of IReadableSignals. Just like classes in BaseLanguage are ScopeProviders for methods, so that method calls can refer to them.

The fact that the reference that the user is trying to set is null or not should make no change to the scope of the reference.

You most likely need to create your own concept extending Expression to represent a reference to IReadableSignal. Overriding getScope() on that concept, however, has no reason, since no IReadableSignals are defined as children on nodes of that new concept, so there's nothing to contribute to the scope on this level.

Maybe the "Creating a scope for InputFieldReference" part of the Calculator tutorial ( can help you build your scopes.


I did read the link you specified and also read the Scope section in the MPS User's guide. I was wrong in suggesting OutputActionExpression as that would still be a child and thus can't provide scope for itself so I understand what you are saying there.

I have successfully made my root concept, App, a scope provider as desired. I was trying to setup SetOutputAction (which is an indirect child of the App root concept) as a scope provider to filter the scope from App to provide different scopes to it's two children: IWritableSignal targets for "output" and IReadableSignal targets - as well as still allowing other expression like addition, subtraction, etc - for "expr".

"output" is a smart reference but problem is I can't tell which child the code completion menu is originating from since both children are null to begin with so I can't customize the scope for the "expr" child.



All right, I better understand your case now, I think.

Typically you would be able to distinguish the two kinds of requests by the concept that they are looking for. Are both children looking for the same concept (I guess ISignal)? Maybe you could use a check like "kind.isExactly(IReadSignal)" as a test?



They are, but one child needs to see only IWritableSignal and the other child needs to see only IReadableSignal. Both IWritableSignal and IReadableSignal extends ISignal.

The first child (that's the output child) implements WritableSignalRef which is a smart reference to IWriteableSignal  so technically the scope doesn't need to be filtered for it as it will filter the root concepts scope. But the second child (that's the expr child) would need the scope filtered to show only IReadable signals.



Then I think all you need to do is to set in the Constraints the scope for the second kind of reference needs to be set to "inherited for IReadableSignal".


Unfortunately that's not an option as that would only allow IReadableSignal types in the code completion menu (I want other expressions such as addition, etc to appear as well).

What I ended up doing was adding an intermediate concept with Expression as a child of the intermediate concept and then set the child "expr" type to that intermediate concept. That guarantee's that the expr child is not null and thus I can use the "come from" language concept to inquire if the scope request came from the "expr" child.

Nice thing is that MPS is smart enough to automatically populate this intermediate concept when the user adds the "setoutput" concept (maybe because it only has one child or no editor?) so it's transparent to the user.


I'm glad you found a solution. This one should serve you well.

My proposed solution was more in-line with the Calculator tutorial - the InputFieldReference, which is an expression, with inherited scope for InputField seems to match perfectly your need with IReadableSignalRef with inherited scope for IReadableSignal. The inherited scope only impacts the possible targets for IReadableSignalRef, it has no effect on allowing other kinds of Expressions in the action child. So you will still be able to use addition and others.


Please sign in to leave a comment.