If you recall in my previous post, I presented a new plugin "CR_ExtractMethodAndInlineLiterals”
In this post I’ll walk you through exactly how it works.
Overview
This plugin does it’s job by automating the execution of existing Refactorings, moving the caret around and executing more Refactorings.
Broadly speaking there are 3 parts to this task.
Extraction of the literals
The first part of extracting the literals is to find them. For this we will use an instance of ElementEnumerable. The ElementEnumerable constructor requires a scope (in which to search) and a filter (to use for testing found items). We can’t pass the current selection as scope (Selection isn’t a LanguageElement). Instead we’ll pass the ActiveFileNode. For the Filter, we’ll pass a custom implementation of an IElementFilter. The bulk of our class (TypeInRangeFilter) is shown here:
IElementFilters are used to test IElements (or sequences of them) against specific criteria. The criteria will vary from implementation to implementation. In this case we have created an IElementFilter which passes elements if they are both of the required type, and their start is within the supplied range.
We use this IElementFilter together with the ElementEnumerable in order to produce a list of PrimitiveExpressions (strings, numbers etc) )within the selected SourceRange.
Having found these literals, we need to place our caret within their name, or better yet, select them entirely…
Once selected, we need to call a refactoring.
However calling a Refactoring from within another refactoring, is a little more complicated that just getting a reference and calling it:
Using this helper method, we make the call to the ‘Introduce Local’ refactoring
At this point it’s worth noting that the refactoring will have changed our code, and naturally enough the Active Document will have to be reparsed.
For this, we need a call to…
… which will cause CodeRush to update it’s internal representation of the code.
Each call to Introduce Local will have caused the the introduction of a new Initialized variable. However each of these will have been placed immediately prior to it’s initial usage. This is less than ideal since since we’d like to exclude these initializations from the extraction. Not to worry, we can move these statements up above the original selection.
Finding these InitializedVariables involves another custom implementation of IElementFilter (InitializedVarFilter).
This time the internals look more like this:
This IElementFilter looks for IElements whose type is InitializedVariable and also match a given name
As before, we use our new IElementFilter like this
Iterating through our InitializedVars we can move each above the selection using:
Extract Method
Next we reselect our original selection. (Messing with code doesn’t always alter your selection, but it can be a good practice to ensure things are the way you want them anyway)
… and then Extract the Method using the helper function we created earlier.
Inline Variables
Our final official step is to inline the Initialized Variables back into the method call.
Once more we can iterate through our previously gathered list of InitializedVariables
Select the range of each in turn
Then Execute the Inline Temp refactoring
Essentially that’s all there is to it.
As usual the full code to this plugin can be found on github.com as well as the VSIX.
Feel free to use these as a reference for your own plugins, or to extend them and contribute changes back again.