Child pages
  • Plugin
Skip to end of metadata
Go to start of metadata

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.

Tutorial

Icon

A quick and simple tutorial by Federico Tomassetti on how to create action and show it in a context menu is available here:
http://www.federico-tomassetti.it/tutorial-how-to-add-an-action-to-the-jetbrains-metaprogramming-system/

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.

Note

Icon

Use isApplicable only if you don't modify presentation manually. This will work in isApplicable, but update method is  more suitable place for complex presentation manipulations.



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.

Note

Icon

In update/build blocks use add statement to add group members.


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.

Note

Icon
  • You shouldn't care about group creation order and modifications order - this statement is fully declarative.
  • If A is added into B, B into C, C will contain A

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.

Note

Icon
  • For now, shortcuts for an action A work only in those places, where A can be accessed from popup menu.
  • For now, shortcuts will not work if the action is added using group with update contents.
  • We plan to fix it later by providing an ability of explicit specifying the places where the action can be called using its shortcuts.

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.

Be careful

Icon

This operation can't currently be used in dispose() method

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.

Note

Icon

Only primitive types and non-abstract classes can be used as types of persistent fields. If you want to store some complex data, create a persistent field of type org.jdom.Element (do not forget to import model org.jdom), annotate it with com.intellij.util.xmlb.annotations.Tag and serialize/deserialize your data manually in after read / before write


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.

Hint

Icon

uiLanguage components can be used here

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.

Be careful

Icon

This operation can't currently be used in dispose() method

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.

Previous Next

  • No labels