This section covers the functionality related to Quick-Fixes and Context Actions that has been augmented for ReSharper 7. If you are porting a R# 6.x or earlier plugin, please consult see 1.8 Migration from ReSharper 6.x (R7).
Introduction
One of the ways that ReSharper offers to correct or modify your code is by using a pop-up menu that shows up on the left-hand side of the screen:
This menu (typically triggered with the Alt+Enter shortcut) contains a set of menu items that plugin writers can provide. There are two separate ReSharper constructs that both can provide items to appear in this menu:
- Context actions are simply possible code modifications that might be applicable in a particular point in code. Context actions typically offer a chance to improve the code without dramatically changing it. CA items are displayed with yellow light bulb icons.
- Quick-fixes are possible modifications that appear associated with a particular highlighting (i.e., a warning or an error). These typically offer a chance to correct a particular problem in your code. Quick-fix items are displayed with red light bulb icons.
In each case, a context action (CA) or quick-fix (QF) can provide zero or more menu entries for the user to act upon. As of ReSharper 7, these entries can also be hierarchical in nature, i.e. one can have nested/submenu items in addition to top-level ones.
Bulb Action
Each item provided by a QF or CA is called a bulb action. A bulb action is any class that implements the IBulbAction
interface. This interface has two members:
Text
--- this property needs to contain the text that is displayed in the menu item.Execute()
--- this method is called when the menu item is chosen.
Whether or not to create separate classes for bulb actions (in addition to QF/CA classes) is a design decision. Typically, if your QF or CA only intends to display one item, it may not be necessary.
In addition to ‘plain text’ offered by IBulbAction
, your bulb action can also provide rich text, i.e., text that has some formatting. For example:
To support it, your bulb action must also implement the IBulbItemRichText
interface. This interface has a property called RichText
that contains a definition of the text shown in the menu, including formatting changes such as bold text or a different text color. Here’s an example:
Check out the TextStyle
class for a range of possible options.
Bulb Menu
A bulb menu is a collection of bulb actions provided by various CAs and QFs, all arranged in a particular list or hierarchy. Bulb menus are created intermally by ReSharper, but they should be populated explicitly in the QFs and CAs via the CreateBulbItems
method.
A bulb menu is represented by the BulbMenu
class. The contents of this class can be modified in several ways. The simplest way, if you want to add just one item to the root level of the menu is to call a method such as ArrangeContextAction()
or ArrangeQuickFix()
. Both of these methods takes one bulb action and puts it at the top level.
A singular call such as ArrangeContextAction()
has a corresponding method ArrangeContextActions()
--- note the s
at the end. This basically takes several bulb items and puts them at the top level.
Note: the bulb menu doesn’t just support adding ordinary QF and CA items but also other items, but also more complicated items such as refactoring quick-fixes. The fundamental difference between these is the icon that is displayed next to the item.
Now let’s talk about the hierarchical aspects of menus. Essentially, a bulb menu keeps information about its structure in a property called Groups
which is an enumeration of BulbGroup
objects. Rather than creating groups, it is recommended that you use the BulbMenu.GetOrCreateGroup()
method to avoid creating duplicate groups. This method takes an Anchor
which relates to the location where the group is placed --- take a look at the static members of AnchorsForBulbMenuGroups
for some well-known anchors.
Having acquired or created a group, you can do one of two things:
- Add a single bulb action using the
AddBulbAction()
method. This is precisely what happens behind the scenes inArrangeContextAction()
and similar methods. - Use the
GetOrCreateSubmenu()
method in order to create a submenu for a particular menu.
Hierarchical Menu (a.k.a. Submenu)
This is where an explanation of submenus is in order. You see, one of the properties of a BulbGroup
is called MenuItems
and it contains, you guessed it, a collection of BulbMenuItem}}s. What happens behind the scenes in the {{GetOrCreateSubmenu()
method is that a new BulbMenuItem
is acquired or created and then added to the collection of existing items.
Both the explicit creation of the BulbMenuItem
and its addition via GetOrCreateSubmenu()
require you to initialize and pass in a BulbMenuItemViewDescription
structure. For this structure, you need to define:
- An anchor (as per bulb groups)
- An icon. You can take one of the existing icons in ReSharper or pass
null
to avoid having an icon altogether. - A rich text definition. Use the
RichText
class or, if you don’t need any formatting, simply cast an ordinary string toRichText
.
You’ll also note that the BulbMenuItem
constructor has two additional parameters. The bulbAction
parameter lets you define a bulb action to execute when this menu item is triggered. The withSubmenu
parameter defines whether you need to have a submenu for this menu item. If you do, it initializes the Submenu
property of the BulbMenuItem
to a new BulbMenu
.
Brief recap of the way things are structured:
- You have a top-level
BulbMenu
that you can items directly to. - A
BulbMenu
has one or moreBulbGroup
elements in aGroups
collection. - Each group has one or more
BulbMenuItem
elements in aMenuItems
collection. - Each
BulbMenuItem
can itself have aSubmenu
property of typeBulbMenu
.
Context Action
As mentioned previously, a context action is meant to offer an opportunity to change code in a particular context. For example, offering to change a number from hexadecimal to decimal should be a context action --- this is a convenience method.
A context action is a class that follows the following rules:
- It implements the
IContextAction
interface - It is decorated with the
ContextAction
attribute - It has a constructor that takes the CA data provider as a parameter
Let’s start with the CA constructor. As mentioned above, it should have one parameter corresponding to a data provider for the context action. A data provider is simply a storage class for various bits of information that are relevant to the context action (i.e., its context). The data provider is a language-specific interface derived from IContextActionDataProvider
.
Here is an example from a C# context action:
Now it’s time to implement the interface members. The first, an IsAvailable()
method is used to check whether this context action is available. If it is, menu items will be added to the pop-up, if not -- they won’t. The IsAvailable()
method is the right place to start using that provider
that we cached. For example, the code below checks that we are on an assignment statement:
The next interface member is the one that populates the bulb menu. The method is defined as
and the idea is that you use the menu
parameter to define the structure of the menu, as described in the previous section. For instance, the implementation can be as simple as
if you’ve only got one bulb action that you want to add.
Quick-Fix
A quick-fix is just like a context action. The only difference is that a quick-fix appears in response to a highlighting. A quick-fix is called this because its purpose is to fix a particular problem, a problem typically found by a daemon and highlighted in code.
The creation of a quick-fix is very similar to the creation of a context action. You need a class that:
- Implements the
IQuickFix
interface - Is decorated with the
QuickFix
attribute - Has at least one constructor with one parameter corresponding to a particular highlighting
The constructor parameter that the quick-fix takes must correspond to the highlighting which corresponds to this quick-fix. As far as interface members are concerned, the situation is identical to that of the context action, with the only difference that the CreateBulbItems()
method takes an extra parameter indicating the Severity
of the associated highlighting.
Note that BulbMenu.ArrangeQuickFixes()
requires a set of pairs of bulb actions and severities. Thus, if you keep an internal list of IBulbAction
elements, you can convert it to a set of pairs using such code as:
Base Classes
In the majority of cases, implementing IBulbAction
, IQuickFix
or IContextAction
is not recommended. Instead, we suggest that you do the following:
- If your QF or CA publishes only a single bulb action, consider inheriting your class from
QuickFixBase
orContextActionBase
respectively. - If you really need to have a separate class for a bulb action, inherit from
BulbActionBase
. This class implementsIBulbAction
and has a lot of useful plumbing necessary for modifying documents.