Continuing where we left off last, today we are ready to write some code in our action. We need to choose a file and then invoke the importer logic on the file and the model.

File Chooser

Since MPS builds upon the IDEA Platform which builds upon Swing, we can use the usual Swing components as well as the platform-provided facilities. I prefer to use classes from the platform when available because they often provide a more polished look-and-feel. In this case the platform provides a class called FileChooser for opening a “choose file” dialog, documented here.


To invoke the FileChooser we will need to pass to it the current project instance. There are in fact several different project classes in MPS, one coming from IDEA (com.intellij.openapi.project.Project) and another one coming from MPS (jetbrains.mps.project.MPSProject). In fact, there are other project classes in MPS but we will not need to concern ourselves with them in this tutorial.

To obtain an instance of the IDEA project we add a context parameter of type Project to our action. I like to call it ideaProject to disambiguate it from a MPS project.

Here is what the FileChooser invocation looks like:

VirtualFile chosenFile = FileChooser.chooseFile(
  FileChooserDescriptorFactory.createSingleFileDescriptor("json"), this.ideaProject, null);

Virtual file system

The result we get from the dialog is a VirtualFile object. Virtual files are abstractions provided by the IDEA platform’s virtual file system (VFS). We are not going to use VFS in our code though, so we will escape the abstraction immediately by calling getPath() on it.

Next, we need to make sure that we are not calling the file chooser while holding a model lock.

Model access: reading, writing, and commanding

All model access in MPS takes place under a read-write lock. The rules are documented in the MPS help. In our case, the importer modifies the model so it will need to be invoked from within a command.

By default, an action’s execute block is wrapped in a command automatically and we don’t have to think of this. On the other hand, UI calls are disallowed when under a read-write lock to prevent UI freezes and deadlocks. This forces us to manage model locking explicitly in our code so we need to disable the automatic command by changing required access from command to none.

If we now need to execute a piece of code in a command, we do it explicitly by using the command statement from jetbrains.mps.lang.access language that was helpfully added to the model for us by the wizard that created the plugin solution.

We need to pass a repository to the command, which we can obtain from the current MPS project, which we get from the context like the IDEA project above.

Show error messages as balloons

Our importer logic can throw exceptions that we should show to the user. If we don’t do anything and simply rethrow the exception, it will cause a flashing exclamation mark to appear in the bottom right corner. If we catch the exception we can use a plain Swing message box to show it, or, even better, a notification. I have written previously about notifications here.

The complete code

Here is what the action looks like after all the modifications:

Next: adding the action to a menu

The action works now but can still only be invoked via the Find Action popup (Ctrl/Cmd+Shift+A). In the next post we will add it to a menu.