In our last post we have created a simple importer that we could invoke from the MPS Console. Today we’ll start implementing a way for our users to invoke the importer.

A common way of extending MPS is via plugins that provide actions. An action is a piece of code that can be invoked via a menu, such as the main menu or a context menu, or a key combination. In the context of MPS it is important to distinguish these so called plugin actions from editor actions which are invoked by the MPS editor to manipulate the node being edited. Both types of actions can be provided by the language developer but plugin actions can perform any task and have access to the entire IDE, whereas editor actions only have access to the current editor and the node being edited.

To launch the importer our action will need two pieces of input from the user: the source file and the target model. Our plan will be to take the target model from the context and use the built-in file chooser dialog to ask for the source file.

Plugin solution

Whereas we were free to put our Java code (almost) anywhere we like, we are more constrained with plugins and actions. They must reside in a specially configured solution in a specially named model. MPS provides a wizard that can create both the solution and the model for us, found in the context menu of the project (New → Plugin Solution):

The wizard will perform three tasks for us:

  1. Create a solution and set its Solution Kind set to Other. You can also manually set the solution kind on the Java tab in the solution properties dialog:

  2. Create a model named <solution-name>.plugin with a few useful languages already imported for use.

  3. Create a root node of concept StandalonePluginDescriptor.

These three arcane bits of configuration are required for MPS to load actions and other plugin elements from the solution. More details about MPS classloading and solution kinds can be found on a helpful Wiki page of the mbeddr project.

Creating an action

We can now create our first action in the model (right click on the model → New → j.m.lang.plugin → Action). Let’s call it ImportLocationsFromJson and set the caption to “Import Locations from JSON…”. The ellipsis at the end is a convention to indicate that the action will show a dialog.

As a quick check that things so far have been configured properly, after we build the action we should be able to use the “Find Action” popup (Ctrl/Cmd+Shift+A) to look it up:

You can even try invoking it from that popup (press Enter): it should do nothing at all (including throwing no errors).

NOTE: Actions are documented in the JetBrains MPS Help and I suggest you read the documentation to gain a better understanding of what we are going to do.

Getting the target model: context parameters

Our action will be placed in a context menu of a model. It can determine the model that it is invoked on via a context parameter with key MODEL. To use this key we need to import the model that contains MPSCommonDataKeys class, jetbrains.mps.ide.actions@java_stub:

If MPS does not give the name to the parameter automatically, name it model. Leave it required.

Adding a required parameter serves two purposes: we will be able to use it in the execute block, and the action will be disabled if invoked from a context where no model is present. If you now try looking up the action again via Cmd/Ctrl+Shift+A, you will find that the action is disabled if the editor is focused and enabled if the focus is on a model in the Logical View (project tree).

"Import Locations..." action is disabled when the focus is currently in the editor

Next: code!

Having the model means we have one half of our inputs without writing any code yet. In our next part we will have no such luxury and will get our hands dirty with Java code to let the user pick the input file and perform the action. Meanwhile, the code is on GitHub and I’m eager to hear your reactions and thoughts about the tutorial so far.