- To make MPS treat some special model as a documentation aspect (that is our new custom aspect), an aspect declaration should be created in the documentation language. To do so, we create a plugin aspect in the language and import the customAspect language.
- Create an aspect declaration in the plugin model of the language and fill in its fields. This tells MPS that this language can be used to implement a new custom aspect for other languages.
- After making/rebuilding the documentation language, it's already possible to create a documentation aspect in the sample language and create a doc concept in it.
- Now, we should move to the language runtime in order to specify the functionality of the new aspect as it should work inside MPS. In our example, let's create an interface that would be able to retrieve and return the documentation for a chosen concept. To do so, we create a runtime solution, add it as a runtime module of our documentation language and create an interface in it. Note that the runtime class must implement the ILanguageAspect interface. To satisfy our needs, the method must take a concept as a parameter and return a string with the corresponding documentation text.
- In the generator for the documentation language we now need to have an implementation of the interface from above generated. A conditional root rule and the following template will do the trick and generate the documentation descriptor class:
The condition ensures that the rule only triggers for the models of your custom aspect, i.e. in our case models that hold the documentation definitions (jetbrains.mps.samples.customAspect.sampleLanguage.documentation).
The useful feature here is the concept switch construction, which allows you to ignore the concept implementation details. It simply loops through all documented concepts (the LOOP macro) and for each such concept creates a matching case (exactly ->$[ConceptDocumentation]) that returns a string value obtained from the associated ConceptDocumentation.
- So we have an interface and an implementation class. Now, we need to tie them together - we have to generate the part of the LanguageRuntime class, which will instantiate our concept, i.e. whenever the documentation aspect is required, it will return the DocumentationDescriptor class. To understand how the following works, look at how the class Language.java is generated (see Language class in model j.m.lang.descriptor.generator.template.main). The descriptor instantiation is done by a template switch called InstantiateAspectDescriptor, which we have to extend in our new aspect language so that it works with one more aspect model:
Note: as we have extended a template switch from another generator, we should also add an "extends" dependency to the documentation's language generator
Essentially, we're adding a check for the DocumentationAspectDescriptor interface to the generated Language class and return a fresh instance of the DocumentationDescriptor, if the requested aspectClass is our custom aspect interface.
- The only thing left is using our new aspect. For that purpose, an action needs to be created that will show documentation for a concept of a node under cursor on demand:
The jetbrains.mps.ide.actions@java_stub model must be imported in order to be able to specify the context parameters. The action must be created as part of a (newly created) plugin solution (more on plugin solutions at Plugin) with a StandalonePluginDescriptor and hooked into the menu through an ActionGroupDeclaration:
This way the IDE Code menu will be enhanced.
- Let's now try it out! Rebuild the project, create or open a node of the DocumentedConcept concept in the sandbox solution and invoke the Show Documentation action from the Code menu: