In order to integrate with MPS IDE, you can use plugin aspect of your language. There are a number of entities that can be used in your language's plugin. This chapter describes all of them.
Actions and action groups
One can add custom actions to any menu in MPS by using action and action group entities.
The action describes one concrete action. Action groups are named lists of actions intended for structuring of actions - adding them to other groups and MPS groups (which represent menus themselves) and combining them into popup menus. You can also create groups with dynamically changing contents.
How to add new actions to existing groups?
In order to add new actions to existing groups, the following should be done:
- actions should be described
- described actions should be composed into groups
- these groups should be added to existing groups (e.g. to predefined MPS groups to add new actions to MPS menus).
Predefined MPS groups are stored in jetbrains.mps.ide.actions model, which is an accessory model to pluginLanguage, so you don't need to import it into your model.
Action structure
Action properties
Name - The name of an action. You can give any name you want, the only obvious constraint is that the names must be unique in scope of the model.
Mnemonic - if mnemonic is specified, the action will be available via alt+mnemonic shortcut when any group that contains this action is displayed. Note that the mnemonic (if specified) must be one of the chars in action's caption. Mnemonic is displayed as an underlined symbol in action's caption.
Execute outside command - all operations with MPS models are executed within commands. Command is an item in the undo list (you don't control it manually, MPS does it for you), so the user can undo changes brought into the model by action's execution. Also, all the code executed in a command, has read-write access to the model but if you show dialogs inside of command, it can cause deadlock, so, if you are not using UI in your action, set this to false, otherwise it should be set to true and control read/write access manually with read action and command statements.
Caption - the string representing an action in menus
Description - this string (if specified) will be displayed in the status bar when this action is active (selected in any menu)
Keystroke - if specified, action will be available via this keystroke even if the containing group is not open (butthis action must be accessible through a chain of menus in current place). The first field of the keystroke editor specifies, which control keys should be pressed, while the second one goes for keycode.
Icon - this icon will be displayed near the action in all menus. You can select the icon file by pressing "..." button. Note that the icon must be placed near your language (because it's stored not as an image, but as a path relative to the language's root)
Construction parameters
Each action can be parameterized at construction time using construction parameters. This can be any data determining action's behavior. Thus, a single action that uses construction parameters can represent multiple different behaviors. To manage actions and handle keymaps MPS needs a unique identifier for each concrete behavior represented by an action. So, toString function was introduced for each construction parameter (can be seen in inspector). For primitive types there is no need to specify this function explicitly - MPS can do it automatically. For more complex parameters, you need to write this function explicitly so that for each concrete behavior of an action there is a different set of values returned from toString() functions.
Enable/disable action control
Is always visible flag - if you want your action to be visible even in disabled state, set this to true, otherwise to false.
Context parameters - specifies which items must be present in current context for the action to be able to execute. They are extracted from the context before any method is executed. There are two types of context parameters - optional and non-optional, this can be set in inspector. If some non-optional parameters were not extracted, the action state is set to disabled and isApplicable/update/execute methods are not executed. If all action parameters were extracted, you can use their values in all action methods. There are 2 types of action parameters - simple and complex action parameters.
- Simple action parameters allow to simply extract all available data from current data context. The data is provided "by key", so you should specify the name and the key in declaration. The type of the parameter will be set automatically.
- Complex action parameters were introduced to perform some frequently used checks and typecasts. Now there are 3 types available for context parameter of this type: node<concept>, nlist<concept>, model.
- node<concept> - currently selected node which is an instance of specified concept. Action won't be enabled if selected node isn't an instance of this concept.
- nlist<concept> - currently selected nodes. It is checked that all nodes are instances of concept (if specified). As with node<concept>, the action won't be enabled if the check fails.
- model - current model
Is Applicable / update - In cooperation with context parameters, this controls enabled/disabled state of the action. The isApplicable method returns the new state of an action, update method is designed to update the state manually. You can also update any of your action's properties (caption, icon etc.) by accessing action's presentation via event.getPresentation(). This method is executed only if all context parameters were extracted from the context.
Execute - this method is executed when the action is performed. It is guaranteed that it is executed only if the action's update method for the same event left the action in active state (or isApplicable returned true) and all required context parameters are present in current context and were filled in.
Methods - in this section you can declare utility methods.
Group structure
Group describes a set of actions and provides the information about how to modify other groups with current group.
Presentation
Name - The name of the group. You can give any name you want, the only obvious constraint is that the names must be unique in scope of the model.
isPopup - if this is true, the group represents a popup menu, otherwise it represents a list of actions.
Caption (popup=true) - string that will be displayed as a name of a popup menu
Mnemonic (popup=true) - if mnemonic is specified, popup menu will be available via alt+mnemonic shortcut when any group that contains it is displayed. Note that the mnemonic (if specified) must be one of the chars in caption. Mnemonic is displayed as an underlined symbol in popum menu caption.
Is invisible when disabled - if set to true, group will not be shown in case it has no enabled actions or is disabled manually in update().
Contents
There are 3 possibilities to describe group contents:
Element list - this is just a static list of actions, groups and labels (see modifications). The available elements are:
- ->name - an anchors. Anchors are used for modifying one group with another. See Add statement section for details.
- <---> - separator
- ActionName[parameters] - an actions.
Build - this alternative should be used in groups, which contents are static, but depend on some initial conditions - the group is builded once and is not updated. Use add statement to add elements inside build block.
Update - this goes for dynamically changing groups. Group is updated every time before it is rendered.
Modifications and labels
Add to <group> at position <position> - this statement adds current group to <group> at the given position. Every group has a <default> position, which tells to add the current group to the end of the target group. Some groups can provide additional positions by adding so-called anchors into themselves. Adding anchors is described in contents section. The anchor itself is invisible and represents a position, in which a group can be added.
actionGroup <...> expression
There is a specific expression available in pluginLanguage to access any registered group - actionGroup<group> expression.
Shortcuts
All the actions added by plugins are visible in Settings->Keymap and Settings->Menus and Toolbars. This means that any user can customize shortcuts for all MPS actions. The shortcut specified in action is the one that will be added to default shortcuts scheme.
Editor Tabs
Look at any concept declaration. Have you noticed the tabs at the bottom of the editor? You are able to add the same functionality to concepts of your language.
What is the meaning of these tabs? The answer is pretty simple - they contain editors for some aspects of "base" node. You can also create corresponding aspect nodes from some of them. Each tab can be either single-tabbed (which means that only one node is displayed in it, e.g. editor tab) or multi-tabbed (if multiple nodes can be created for this aspect of the base node, see Typesystem tab for example).
How the editor for node is created? When you open some node, call it N, MPS tries to find the "base" node for N. If there isn't any base node, MPS just opens the editor for selected node. If the node is found (call it B), MPS opens some tabs for it, containing editors for some subordinate nodes. Then it selects the tab for N and sets the top icon and caption corresponding to B.
When you create tabbed editors, you actually provide rules for: finding base node, finding subordinate nodes and (optionally) algorithms of subordinate nodes creation.
Editor Tab Structure
Name - The name of the rule. You can give any name you want, the only obvious constraint is that the names must be unique in scope of the model.
Main Concept - the concept of base node.
Base Node - this is a rule for searching for "base" node. It should return null if the base node is not found or this TabbedEditor can't be applied.
Tabs
Single-tabbed tab
Caption - the caption of the tab
Empty text - this will be written in this tab's panel if there are no nodes to display in it
getNode - should return the node to edit in this tab
create - if specified, this wil be executed when user asks to create node from this tab
Multi-tabbed tab
Caption - the caption of the tab
Empty text - this will be written in this tab's panel if there are no nodes to display in it
getNodes - should return nodes to edit in this tab
getCaption- should return caption for inner tab for the given node
create - if specified, this wil be executed when user asks to create node from this tab
Tools
Tool is an instrument that has a graphical presentation and aimed to perform some specific tasks. For example, Usages View, Todo Viewer, Model and Module repository Viewers are all tools. MPS has rich UI support for tools - you can move it by drag-and-drop from one edge of the window to another, hide, show and perform many other actions.
Tools are created "per project". They are initialized/disposed on class reloading (after language generation, on "reload all" action etc.)
Tool structure
Name - The name of the tool. You can give any name you want, the only obvious constraint is that the names must be unique in scope of the model.
Caption - this string will be displayed in tool's header and on the tool's button in tools pane
Number - if specified, alt+number becomes a shortcut for showing this tool (f it s available)
Icon - the icon to be displayed on the tool's button. You can select the icon file by pressing "..." button. Note that the icon mustbe placed near your language (because it's stored not as an image, but as a path relative to the language's root)
Init - initialize tool instance here
Dispose - dispose all tool resources here
getComponent - should return a Swing component (instance of a class which extends JComponent) to display inside the tool's window. If you are planning to create tabs in your tool and you are familiar with tools framework in IDEA, it's better to use IDEA's support for tabs. Using this framework greatly improves tabs functionality and UI.
Fields and methods - regular fields and methods, you can use then in your tool and in external code.
Tool operation
We added an operation to simply access a tool in some project. You can access it as project.tool<toolName>, where project is an IDEA Project. Do not forget to import pluginLanguage to use it.
Preferences components
Sometimes you may want to be able to edit and save some settings (e.g. your tools' settings) between MPS startups. We have introduced preferences components for these purposes.
Each preferences component includes a number of preferences pages and a number of persistent fields.Preferences page is a dialog for editing user preferences. They are accessible through File->Settings.
Persistent fields are saved to .iws files when project is closed and restored from them on project open. The saving process uses reflection, so you don't need to care about serialization/deserialization in most cases.
Preferences component structure
name - component name. You can give any name you want, the only obvious constraint is that the names must be unique in scope of the model.
fields - these are persistent fields. They are initialized before after read and pages creation, so their values will be correct in every moment they can be accessed. Default values can be specified for them.
after read / before write - these blocks are used for custom serialization purposes and for applying/collecting preferences, which have no corresponding preferences pages (e.g. tool dimensions)
pages - preferences pages
Preferences page structure
name - the string to be used as a caption in Settings page. The name must be unique within a model.
component - UI component to edit preferences.
icon - the icon to show in Settings window. The size of the icon can be up to 32x32
reset - reset preferences values in UI component when this method is called.
commit - in this method preferences should be collected from the UI component and commited to wherever they are used.
isModified - if this method returns false, commit won't be executed. This is for preferences pages with long-running commit method.
PreferenceComponent expression
We added an expression to simply access a PreferenceComponent in some project. You can access it as project.preferenceComponent<componentName>, where project is an IDEA Project. Do not forget to import pluginLanguage to use it.
Custom plugin parts
Custom plugin parts are custom actions performed on plugin initializing/disposing. They behave exactly like plugins. You can create as many custom plugins for your language as you want. There are two types of custom plugins - project and application custom plugins. The project custom plugin is instantiated once per project, while the application custom plugin is instantiated once per application and therefore it doesn't have a project parameter.
File Generators
File Generators provide an ability of changing the place to generate files to. This can be used, for example, to store application database settings in application's root directory regardless of the model in which they were described.
File Generator structure
generateFile - this method is used to create files. You are provided with contents (String) and some additional information and need to create resulting files.
isDefault / overridesDefault - these two methods are used to better control file generator applicability. Each file generator can be either "default" or "non-default". Defult file generators can be overridden by non-default.
fields, methods, extended class - some additional stuff can be declared here
Generation Listeners
Using generation listeners you can perform custom activities on generation events.
These events are:
- before generation (models) - occures before generation
- on models generated (success,models) - occures just after all models were generated
- after generation(models) - occures after generation has finished
Generate Plugins concept
Note that when one creates the plugin aspect, he doesn't have to explicity write the plugin class, instead of it, he describes only the parts of the plugin itself. The plugin class is generated automatically, but only for plugin aspects of languages. If plugin language is used in a model, the plugin class won't be generated for it automatically. To generate the class, create Generate Plugin node in this model and specify which plugin classes should be generated.