2.6 Quick-Fixes and Context Actions (R7)

Skip to end of metadata
Go to start of metadata

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 in ArrangeContextAction() 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 to RichText.

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 more BulbGroup elements in a Groups collection.
  • Each group has one or more BulbMenuItem elements in a MenuItems collection.
  • Each BulbMenuItem can itself have a Submenu property of type BulbMenu.

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 or ContextActionBase respectively.
  • If you really need to have a separate class for a bulb action, inherit from BulbActionBase. This class implements IBulbAction and has a lot of useful plumbing necessary for modifying documents.