Generic support for commenting out nodes in MPS
MPS provides a universal way to comment out nodes in models. In previous versions this functionality had to be implemented in all languages separately, either through node attributes or dedicated “comment” nodes. Since MPS 3.3 the information about a node being commented out is stored in the model in a generic way. The smodel language ignores commented out nodes by default so that your queries do not have to filter out commented out nodes explicitly.
Additionally, actions have been created to comment/uncomment out a node by hitting "Control/Cmd + /".
How to use it
You can select any node in MPS, except roots, and press Control/Cmd + “/”. That node will be commented out. Let’s watch some examples:
The node that you select or point the cursor at will get commented out. Every single non-root node can be commented out - irrespective of whether it occupies a whole line, several lines, or whether it is nested deeply in an expression-like hierarchy.
If you comment out a node, it will be physically removed from its place in the model. If the commented out node occupied a requested child link, an empty cell is provided so that the user can fill in a new child value.
In BaseLanguage, for example, this gives you possibilities beyond what the Java parsers allows. You can comment out an IfStatement’s condition:
a method parameter:
or a variable type:
To give another example, the editor definition language allows you to comment out an editor cell, for example:
Smart commenting out
The comment and uncomment actions have some intelligence in them in order to decide, which node to comment/uncomment.
- if a node or a set of nodes is selected, this node/nodes will be commented out/uncommented
- if no node is selected, the editor attempts to comment out/uncomment the current "line" - to achieve this a search starts in the node under caret to identify the closest ancestor vertical collection and an ancestor of the node under caret that is a member of this vertical collection will be commented out/uncommented
How does it work in the model
When a node is commented out, it is placed as a child (wrapped) in a special “child attribute”, called BaseCommentAttribute. Then the instance of this attribute is attached to the commented node’s link in the former parent of the commented node. A ChildAttribute is same as the LinkAttribute concept, except that ChildAttribute gets attached to aggregation links. So the commented nodes are not stored as the usual children, and they won’t appear in queries like node.children, node.descendants, etc.
The MPS editor knows about comments and it will draw children as well as the commented out nodes, in this role.
Querying for commented nodes
The smodel language gives you options to query for the commented nodes. You use the same syntax that works for any attributes, only that the comment attribute allows for parametrization by the containment link. For example, if a node has a child collection named commands, querying whether any of the commands children has been commented out would look like:
By default every commented node is drawn surrounded by /* */. You can override the visual appearance of a commented out node by defining a custom commented editor for the concept. Just define the usual editor with the hint “comment”:
The style of the editor should be changed so that the user can easily visually distinguish commented code.
You can either re-use the pre-defined Comment style, which uses a gray color with italics style, or you may create your own style for commented out nodes.
Note: The children of the commented node should be drawn with their usual editor so you need to remove the comment hint in child cells:
The next applicable editor cell gives you a more convenient way to customize the look of commented out nodes - you may address several concepts in a hierarchy with a single customized comment editor.
The next applicable editor cell simply removes the comment hint and redirects the request to find the original editor of the concept (IfStatement). This avoids the need for repetition of the editor definition. You may further simplify the task, if you define a single editor bound to the comment hint for a common super-concept - this way all sub-concepts will get the customized comment editor.
Commenting out/uncommenting nodes from code
The CommentUtil class from jetbrains.mps.editor.runtime can be used to comment out and uncomment nodes from code, such as actions, intentions or key maps. This gives you options to further customize the behavior of commented out nodes.
The CellAction_CommentOrUncomment and its inheritors class come from the same package. They give you the way to simply comment the node and restore the selection or uncomment the node if it is currently commented.
The Comment editor action
The response to the comment/uncomment action can also be customized on the node level. You can set the handler for COMMENT action in the cell's action map:
For example, if we want to prevent the user from commenting out conditions in the robot's Kaja While statement, we attach the above action map to the While editor's cell representing the condition:
Since the COMMENT action is customized it will do what is indicated.
The action will work only if the condition node is selected.
Since we create the CellAction_CommentOrUncommentNode with the node as the parameter, where the node is the While statement, the action will process the While statement:
1) If it is not commented, the action will comment it out.
2) If it is commented out, the action will uncomment it.
Thus the commenting of the condition will be disabled.
Migrating away from your custom commenting out functionality
In versions prior to MPS 3.3 language authors had to implement the comment out functionality themselves for each language individually. In MPS 3.3 the custom functionality may be redundant and should be replaced by the generic functionality provided by MPS, perhaps with some customization as described above. The existing usages of the old custom commenting-out functionality should be migrated to the generic version, which should be done in several steps:
- Your old concepts used for commenting out should be deprecated
- Your keyboard shortcuts, actions and intentions for commenting out/uncommenting should be deprecated or removed
- You may wish to customize the look of commented out nodes by defining custom editors attached to the "comment" editor hint (as described above)
- You may also wish to disable the generic comment out functionality on some editor cells (as described above)
- You may need to provide a migration that will automatically translate usages of your old custom commented nodes in user code into nodes commented in the generic way. This can be done either fully manually or with MPS assistance.
MPS can create a Migration for you, provided you indicate, which concepts represent the old custom comments using the IOldCommentAnnotation and IOldCommentContainer. Since there were two typical ways to create custom comments in the past, there need to be two interface:
- IOldCommentAnnotation - should be implemented by the NodeAttribute that indicates a node is commented out, if attributes were used annotate commented out nodes
- IOldCommentContainer - in case commented nodes were represented by a dedicated concept, such as SingleLineComment, these dedicated concepts should be marked with this concept interface
These marker concept interfaces come from jetbrains.mps.lang.core, so this language needs to extended by your language in order to use them. Once annotated, the generic comment-out functionality will be ceased on the nodes of these concepts in favor of the old custom comment out functionality.
Additionally, the old commenting-out concepts will have warnings reported on them - Old comment container should be migrated or Old comment annotation should be migrated. The quick-fixes for these warnings will create the necessary migrations to convert your old custom commenting-out scheme into the generic one painlessly. Just trigger the quick fixes, check the generated migrations and then migrate your projects.
Fully manual migration
You may create the migration fully manually. Typically all that your migration needs to do is to find all nodes being commented out in the old custom way, uncomment them and call CommentUtil.comment() on each node to get it commented out in the new way. The CommentUtil class comes from jetbrains.mps.editor.runtime.
The generic comment out functionality marks commented out nodes with the BaseCommentAttribute annotation that is attached to the parent of the commented-out node, holds the original role of the commented out node and comes from jetbrains.mps.lang.core, so this language needs to be used in models that contain commented out nodes. An automatic migration should add such language dependency to all altered models. You may take inspiration from The ReplaceSingleLineCommentsWithGenericComments migration, which migrated SingleLineComment nodes in BaseLanguage: