After discussing the pros and cons of generic and custom empty lines yesterday, let’s have a look at the steps required to implement custom empty lines. I provide them in the form of a checklist, similar to another language design pattern that I described earlier, Inline definitions.

To make the following text less abstract and easier to follow, I will use an example where we have a StatementList concept that contains a multiple child statements of concept Statement, i.e. statements: Statement[0..n]. Assuming that Statement is abstract and we want to add an EmptyStatement to represent an empty line, here is what we have to do:

  1. Create EmptyStatement concept.
    • Structure: EmptyStatement extends Statement.
    • Editor:
      • EmptyStatement_Editor consists of a single empty <constant> cell with editable : true style. Making the cell editable will let the user convert (substitute) the empty statement with a non-empty one by typing.
      • EmptyStatement_SubstituteMenu, the default substitution menu for EmptyStatement, is defined and left empty. This will cause EmptyStatement to be excluded from completion menus.
  2. StatementList_Editor: the %statements% collection is modified to include an element factory that returns new node<EmptyStatement>. This will tell MPS to create a new EmptyStatement node when Enter is pressed, instead of creating the abstract Statement.
  3. (Optional.) In StatementList_Behavior it may be useful to add a behavior method that filters out empty statements:
    public sequence<node<Statement>> nonEmptyStatements() {
        this.statements.where({~it => !it.isInstanceOf(EmptyStatement); });
    }
    

And that’s all there is to it.

Some more improvements:

  • Empty lines are good places for context assistant menu placeholders, so if you use context assistant in your language, consider adding a placeholder after the empty constant cell. This is illustrated in the robot_Kaja MPS sample.
  • If you use mbeddr platform, it provides interface com.mbeddr.core.base.structure.IEmpty that can be used as a marker for empty lines. It also defines an empty default substitution menu so you don’t have to create one yourself if you extend the interface.