As you may have noticed, ReSharper features background analysis of your code. In fact, at any one time there are several different analyzers running at different stages, all combining to provide the most complete assessment of your code.
When writing a plug-in, you will most likely be implementing your own daemon stage - a class decorated with the
DaemonStage attribute that implements the
The interface is fairly simple, and has two members:
CreateProcess()method is the method where a daemon stage process is created. A daemon stage process is the process that is created on a per-document basis to analyze documents and provide information about them.
NeedsErrorStripe()method is used to determine whether or not analysese from this daemon stage contribute to the markers on the right-hand stripe, as well as to the list of document errors. There are only three possible values from the
ErrorStripeRequestenumeration that can be returned here.
Daemon Stage Process
Now let's talk about a daemon stage process. This also happens to be a class that implements the
IDaemonStageProcess interface. Typically, this class is passed the daemon stage so that it can get, e.g., the solution or current file from that variable.
IDaemonStageProcess interface has two members:
- A property called
DaemonProcesswhich returns the corresponding daemon process. Typically you would return the value that you passed into the constructor.
- A method called
Execute()where the actual analysis happens.
Let's talk about the Execute method in more detail. As I mentioned before, you may wish to start by getting at the AST of the code you're analysing. You use the reference to the daemon process to get at the necessary files:
Now that you have the code structure you want to analyse, you spin up one of the element processors on it. An element processor is a class implementing an interface such as
IRecursiveElementProcessor, that lets you walk around all the tree nodes in a particular block of code. In the case of a recursive element processor, it has the following methods:
InteriorShouldBeProcessed()determines whether the interior of the provided ITreeNode should be processed. If it returns true, the processor will drill down into the inner structures. If it returns false, the processor will stop at this level.
ProcessBeforeInterior()lets you process the tree node before you've gone into its descendants.
ProcessAfterInterior()lets you process the tree node after you've gone through its descendants.
Now, the end result of all the work done by the recursive element processor is a set of highlighting. A highlighting is essentially information about a piece of code that has to be highlighted (underlined with a wavy line), as well as shown in the marker bar and possibly in the document errors. Highlightings also happen to be the points on which QuickFixes work.
Highlightings all implement the IHighlighting interface and are decorated with one of the highlighting attributes related to the type of highlighting it is. There are two types of highlightings available:
StaticSeverityHighlightingattribute is used to indicate a highlighting that never goes away. This means that when you set it, it will be shown.
ConfigurableSeverityHighlightinglets the user configure if and when this highlighting is shown. As a consequence, it has context actions to let you disable it temporarily or edit its settings.
There are several pieces of information that your own highlighting class can provide. At a minimum, you can specify:
- The severity of the highlighting. This is defined in the severity attribute.
- The tooltip for the highlighting when you move the caret over it in the editor.
- The tooltip for the marker bar.
So - coming back to the daemon stage process - you have invoked the element processor (or several) and got a set of highlightings. The last thing to do is to commit the results using the commiter that is provided as a parameter to the Execute() method:
And that's it - your daemon is ready to go!