Annotations (Or Attributes) in MPS

What Are Annotations

Sometimes it's needed to mark a node with some another node, without declaring a different role for the marker. Consider, for example, some comments which can be placed nearby any arbitrary node, or something like template macros which are used in generator templates. Such a comment or such a macro serves as an annotation or an attribute which contains some additional information about a node but does not change its structure. In MPS, now you can declare concepts the instances of which can serve as such markers.

Note: currently the template macros are not implemented via attributes. It's for us to do soon.

Besides the properties, children and references, a MPS node can have so-called attributes or annotations. One can say if node's children are contained within the node and node's referents are linked to it, then node's attributes are 'sticked' to its side. There are three main kinds of attributes or annotations: ones which are sticked to a whole node, ones which annotate a property (sticked to a property name), and ones which are sticked to a particular referent role. A node which serves as an attribute must have a concept derived from one of the three following concepts: AttributeConcept, PropertyAttributeConcept or LinkAttributeConcept. These three concepts are defined in a language jetbrains.mps.annotations.

To get or set an attribute programmaticaly, there are several methods of the class SNode. Namely,

Defining Annotations

When you want to define a kind of annotation, create a concept and derive it from AttributeConcept, PropertyAttributeConcept or LinkAttributeConcept. Then just declare properties, children, referents, etc. just like in any ordinary concept. To enable users of your language giving attributes to nodes you should define some actions (keymaps, right transforms, etc.) which execution implements the intended behaviour. For example, imagine you have a concept MyComment which extends AttributeConcept and contains a single string property 'text'. Then you may create a keymap which executes on, say, 'Ctrl-Alt-C', and contains such a code:

    public void execute(KeyEvent keyEvent, final EditorContext context) {
      SNode node = context.getContextCell().getSNode();
      if (node.hasAttribute()) {
        if (node.getAttribute() instanceof MyComment){
          node.setAttribute(null);
        }
      } else {
        MyComment comment = new MyComment(node.getModel());
        comment.setText("<new text>");
        node.setAttribute(comment);
      }
    }
  
This keymap will add a comment to a node, if the node does not have any attributes, otherwise it will remove a comment from the node if comment exists, and it will not affect another kinds of attributes.

Attributes Appearance: Designing An Editor

When a node has an attribute it's attributes' editor which is shown when you edit a node, not the node's own. The same with property and link attributes: the editors for attributes are shown instead of cells for link targets and property values.

But, you almost always want to see the original node, too. For that purpose, an editor declaration for an attribute should have a meta-cell reresenting the original node.

If your attribute is an attribute for a property or a link, you can use two special kinds of meta-cells: link attribute cell and property attribute cell. You may use them in editors for link attributes and property attributes, respectively. Such a meta-cell represents that very cell for link/propety, instead of which an attribute editor is shown.

If your attribute is an attribute for a whole node, you should use just a ref.node meta-cell with a link role 'attributedNode'. For example, an editor declaration for the imaginary concept MyComment may consist of two meta-cells: a property meta-cell for the 'text' property and a ref.node meta-cell for the attributed node.