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.
Projects
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.