HowTo -- Adding additional Tools (aka Views)

Skip to end of metadata
Go to start of metadata

Adding additional Tools (aka Views)

This document describes how to build a new Tool (For Eclipse
users, a Tool in MPS is like a View in Eclipse) for MPS. This can serve as an
example to build arbitrary additional tools into MPS. This text emphasizes to
aspects of Tool development: How to add a new tool to a language and the menu
system, and how to synchronize the view with whatever is currently edited.

In all other ways tools are just Swing UI programs. So for implementing
sophisticated views, one needs Java Swing experience. In this tutorial we will
not focus too much on the intricacies of building a tree view with Swing — an
endeavour that is surprisingly non-trivial!

The Outline Tool itself

The MPS plugin language supports the definition of new Tools. Create a new
instance of Tool and set the name. You can also give it a different
caption and an icon. A default location (bottom, left, right, top) can also be
defined.

We now add three fields to the Tool. The first one is used to remember the
project, the second one connects the tool to MPS's message bus, and the third
one remember the ToolSynchronizer}. The {{ToolSynchronizer updates the
outline view in case the active editor changes. The message bus is IDEA
platform's infrastructure for event distribution. We use it for getting notified
of selection changes in the currently active editor. More on these two later.

We are then ready to implement the three main methods of a Tool: init,
dispose} and {{getComponent. Here is the code (with comments, please
read them!)

An Action to Open the Tool

Actions are commands that live in the MPS UI. We have to add an action to open
the outline view. Actions live in the plugins aspect of a language. Actions can
define keyboard shortcuts, captions and icons, as expected. They also declare
action parameters. These define which context needs to be available to be able
to execute the action. This determines the presence of the action in the menu,
and supports delivering that context into the action itself. In our case the
context includes the currently opened project as well as the currently opened
file editor.

In the execute method of the action we create and open the Outline tool.
The setOutline method is an ordinary method that lives it the Outline Tool
itseld. It simply stores the editor that's passed in.

An Action Group

Menu items are added via groups. To be able to add the Open Outline Action to
the menu system, we have to define a new group. The group defines its contents
(the action, plus a two separators) and it determines where in the
menu it should go. Groups live in the plugin aspect as well.

Managing the Tool Lifecycle

The tool must play nicely with the rest of MPS. It has to listen for a number of
events and react properly. There are two listeners dedicated to this task. The
EditorActivationListener tracks which of the potentially many open editors
is currently active, since the outline view has to show the outline for whatever
editor is currently used by the user. It is also responsible to hooking up
further listeners that deal with the selection status and model changes (see
below). The ModelLifecycleListener tracks lifecycle events for the model
that is edited by the currently active editor. The model may be replaced while
it is edited, for example, by a revert changes VCS operation.

Editor Activation and Focus

The EditorActivationListener tracks which of the potentially many open
editors is currently active. It is instantiated and hooked up to the MPS message
bus by the tool as it is created by the following code (already shown above):

In its constructor, it remember the outline tree. Then it sets up a new outline
tree selection listener that listens to selection changes made by the user
in the tree itself.

It then sets up a listener to keep track of the model lifecycle (load, unload,
replace) and it hooks up with the events collector (both explained below).

The EditorActivationListener} implement {{FileEditorManagerListener, so
it has to implement the following three methods:

In our case we are interested in the selectionChanged event, since we'll
have to clean up and hook up all kinds of other listeners. Here is the
implementation.

The cleanupOldEditor method removes existing listeners from the old, now
inactive editor:

The next method hooks up the infrastructure to the newly selected editor and its
underlying model. Notice how we use SNodePointer whenever we keep
references to nodes. This acts as a proxy and deals with resolving the node in
case of a model is replaced and contains a "weak reference" to the actual node,
so it can be garbage collected if the model is unloaded. This is important to
avoid memory leaks!

Tracking the Model Lifecycle

The ModelLifecycleListener} extends the {{SModelAdapter class provided
by MPS. We are intereted in the model replacement and hence overload the
modelReplaced method. It is called whenever the currenly held model is
replaced by a new one, e.g. during a VCS rever operation.

In the implementation, we create a new new tree model for the same root. While
this code looks a bit nonsensical, note that we use an SNodePointer
internally which automatically re-resolves the proxied node in the new model.
We also add ourselves as a listener to the new model's descriptor to be notified
of subseqent model replacement events.

Synchronizing Node Selections

Tracking Editor Selection

This one updates the selection (and expansion status) of the tree as the
selected node in the currently active editor changes. We have already made sure
(above) that the outline view's tree is synchronized with the currently active
editor.

The class extends MPS' SingularSelectionListenerAdapter because we are
only interested in single node selections. We overwrite the
selectionChangedTo method in the following way:

Tracking Changes in the Model Structure

The tree structure in the outline view has to be updated if nodes are added,
changed (renamed), moved or deleted in the editor. Tree change events can be
quite granular, and to avoid overhead, MPS collects them into batches related to
more coarse-grained commands. By using MPS' EventsCollector base class, we
can get notified when a significant batch of events has happened, and then
inspect the event list for those that we are interested in using a visitor.

The ModelChangeListener performs this task. To do so, we have to implement
the eventsHappened method. We get a list of events, and use a an inner
class that extends SModelEventVisitorAdapter to visit the events and react
to those that we are interested in.

The ModelChangeVisitor inner class, which acts as the visitor to notify
the tree, overrides visitPropertyEvent to find out about nodes whose
properties have changed in the current batch. It then notifies all the listeners
of the tree model.

It also overwrites visitChildEvent to get notified of child
additions/deletions of nodes. Except that the API in JTree is a bit
annoying, the following commented code should be clear about what it does:

The way back: Tracking Tree Selection

Tracking a JTree's selection happens by implementing Swing's
TreeSelectionListener} and overwriting it's {{valueChanged method the
following way:

Swing's Artifacts: Tree Model and Renderer

In this section I want to describe a couple of interesting MPS-specific aspects
of the implementation of the Swing artifacts.

Tree Cell Renderer

The tree cell renderer is responsible for rendering the cells in the tree. It
uses the getPresentation} method on the nodes, and the {{IconManager to
get (a cached version of) the icon for the respective node.

Tree Model

The tree model is interesting since we include only those children in the tree
node whose concept has been annotated with the
ShowConceptInOutlineAttribute attribute. It is stored in the
storeInOutline role. In the tree model, we have to filter the children of
a node for the presence of this attribute. Here is the respective helper method:

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.