Rory Becker - DevExpress CodeRush Blog

January 2014 - Posts

  • TemplateExpand with CollapsedRegions – How does it work?

    Disclaimer (Yes again)

    Yup, some people still don’t like regions. If that’s you, then you might not be that interested in how a region-appreciative plugin works.
    We’re not here to discuss if they’re good or bad. We’re here to show you how the plugin from my previous post does what it does.

    Overview

    There are only 2 parts to this plugin.

    1. Expand the Template
    2. Collapse Regions

    Expand the Template

    If you’re looking to write an action which needs to be able to expand templates as part of it’s remit, then there really is no better way to get this done, than to call the original command which already takes care of this.

    To call another command, you’ll want code like this:

    ExecuteTemplateExpand

    Locate Regions to Collapse

    So the next issue is which regions to collapse. You see there might already be regions in the document before your template is expanded, and you don’t necessarily want to collapse those.

    Ideally we would like to know the sourceRange into which the template expanded. The TemplateExpand command does not provide this information out of the box, so we’ll have to work it out ourselves. I picked what seemed at the time, like the simplest approach possible.

    I calculate the LinesFromStart and LinesFromEnd. (ie the number of lines from these points to the line the caret is on.)

    CapturePositionalInformation

    …then these values can be used (together with the size of the post-expansion document) to calculate the size and position of the region into which the template expanded.

    CalculateTemplateExpandRange

    Collapse the Regions

    Once we have expanded our template, and have a range of code to process, it should be a simple enough procedure to iterate through RegionDirectives in this range and collapse each of them in turn.

    CollapseRegions

    Except that it’s unfortunately not that simple.

    A Problem of Timing

    As indicated, the problem is one of timing.

    You see VS is a multi-threaded application. This allows it to delegate certain non-essential tasks to background threads. These task are assumed to be less vital than some others and thus will take something of a back seat if other more processor intensive work is being done.

    So what does this mean for us? Well it turns out that fully hydrating new regions added to code is considered a less important task.

    You can definitely understand that point of view. The 500ms delay between adding a region and it being considered a real region is nothing right. I bet most of you have never even noticed that small delay between moving off the line after creating a region and the point when VS recognises it as a region and adds outlining to it.

    However as indicated this does provide us with a small issue.

    If we run our code as suggested so far, we’ll hit a situation where the instruction to collapse a detected region is ignored, because there isn’t yet any sort of outlining associated with the region(s) in question.

    So what to do instead?

    Well the simplest solution is just to wait until the relevant information is available. I’d prefer to do this via event, but at the time of writing the code, I couldn’t find one that would alert me at the proper time.

    So I created a DelayedRunner class (main code below) whose purpose is to repeatedly run a passed func<bool> until such time as that function returns true.

    I added a parameter to specify a delay between each execution, and another to provide a maximum length of time, after which the DelayedRunner would turn itself off and essentially give up.

    RunUntilSuccess

    This required that I alter the code of CollapseRegions so that it returned a bool indicating it’s level of success.

    Note: If no regions are detected in the range, the routine returns success immediately.

    CollapseRegionsWithSuccess

    Finally this delayed execution is executed using the following code:

    ExecuteDelayedRegionCollapse

    Limitations

    The multithreaded nature of this command means that it is not possible to wrap this up in a singe UnitOfWork and add it to the undo stack. Therefore when attempting to undo the effects of this command, the user will have to invoke Ctrl+Z twice to achieve the desired results.

    As always the code for this plugin is available on github and the latest release is also available as a VSIX

  • New CodeRush Plugin – TemplateExpandWithCollapsedRegions

    Disclaimer

    Yes I know. Regions are bad. Regions are awful. Regions should be shot repeatedly until dead. Try to bear in mind that not everyone views everything the same way, and openness to alternatives is a good thing. For the purposes of this blog post, I’m going to ignore the fact that some people see regions as a bad thing. The customer asked for this functionality. Hopefully it will be useful. For the benefit of the customer in question, I’m going to try to keep them relatively anonymous :)

    No regions were harmed in the making of this plugin. Sorry.

    Customer Request (Paraphrased for security reasons)

    I love CodeRush Templates. Is there a way to have CodeRush expand a template, but leave regions that it generates collapsed?

    Short answer: No, there’s nothing built in. Sorry.

    Longer Answer: That sounds like a challenge. Let me see what I can do.

    TemplateExpand with Collapsed Regions

    When you expand a template in CodeRush, your space (or sometimes Tab) key is firing our TemplateExpand command. This in turn invokes all sorts of clever magic and emits text and other things into your editor, with considerably less effort on your part, than if you’d had to type it all yourself.

    This new plugin provides a single new (alternative) command TemplateExpandWithCollapsedRegions.

    To use it, simply bind it to a shortcut of your choice. In testing I have bound it, as I usually do, to Ctrl+T. You can of course pick anything you like. You may even decide you like it so much that you want to replace the original TemplateExpand command. If that’s the case then you’ll need to locate the existing binding on the shortcuts page, and alter the command from TemplateExpand to TemplateExpandWithCollapsedRegions.

    Once you have assigned this command to a shortcut you can use it just like the original command.

    This latest version of this plugin can be found on github.
    Feel free to download, install it and see what you think Smile

  • Extract Method and Inline literals – How does it work?

    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.

  • Extract literals from selection.
  • Extract remaining code to new method.
  • Inline literals back into the call to the new method.
  • 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:

    EMIL-TypeInRangeFilter

    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.

    EMIL-GetPrimitivesMethod

    Having found these literals, we need to place our caret within their name, or better yet, select them entirely…

    EMIL-SelectRange

    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:

    EMIL-ExecuteRefactoringMethod

    Using this helper method, we make the call to the ‘Introduce Local’ refactoring

    EMIL-CallIntroduceLocalRefactoring

    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…

    ReparseActiveDocument

    … 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:

    EMIL-InitializedVarFilter

    This IElementFilter looks for IElements whose type is InitializedVariable and also match a given name

    As before, we use our new IElementFilter like this

    EMIL-GetInitializedVarsMethod

    Iterating through our InitializedVars we can move each above the selection using:

    EMIL-MoveInitializedVar

    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)

    EMIL-SelectSelectRange

    … and then Extract the Method using the helper function we created earlier.

    EMIL-CallExtractMethod

    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

    EMIL-SelectInitializedVariable

    Then Execute the Inline Temp refactoring

    EMIL-CallInlineTempRefactoring

    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.

  • New CodeRush Plugin – Extract Method and Inline literals

    You know what it’s like. Every now and then you spot that you’re performing the same sequence of tasks over and over again, and you find yourself wishing that there was a way to automate what you’re doing.

    Well you’re not alone. Just recently I had one such situation.

    The pattern in question is a relatively simple one.

    It usually starts with a method which itself calls several other methods.

    ExtractAndInlineStart

    In the middle of that code, you realise there are a few lines which you’d like to be able to reuse a little easier.

    Naturally you reach for your trusty Extract Method refactoring. Highlight the code and extract away.

    However what you’re left with isn’t quite what you want.

    ExtractAndInlineSimple 

    What you really want is to have those literal values passed in as parameters.

    Your ultimate goal is more like:

    ExtractAndInlineImplemented

    There are 2 ways to go about this with CodeRush.

    Option 1 – Method First

    In this first case, you…

    - Extract your Target Code as shown in the first example. (Extract Method x 1)
    - Promote each literal to a parameter of the new method. (Promote Parameter x L)

    For a hypothetical method with 3 parameters, this will result in 4 operations. (3 x Promote Parameter + 1 x Extract Method)

    Option 2 – Params First (and Last)

    This second version is more complicated, but suits the way some people think a little better.

    - Extract each of the literal values in your Target Code (Introduce Local x L)
    - Move the resultant locals up above the portion of code you’re looking to extract. (Copy and Paste x L)
    - Extract your Target Code (Extract Method x 1)
    - Re-Inline the literals you created in the first step. (Inline Temp x L)

    For the same hypothetical method (with 3 params), this solution requires 10 operations. (3 x Introduce Local + 3 Copy\Paste + 1 Extract Method + 3 Inline Temp)

    Each option suits different people’s approach, but both are a few more steps more than I’d like to have to perform

    It’s lucky then that CodeRush is so Extensible Open-mouthed smile

    Enter a new plugin

    The new plugin provides an additional refactoring (Extract Method And Inline Temp) which sits in the usual smart Tag menu, and makes itself available alongside Extract Method.

    All that’s required is a single click, and CodeRush will whisk you passed all of those tedious (and potentially error ridden) manual steps.

    ExtractAndInlineRefactoring

    As usual, this project is Now available on github with full source code and VSIX Install.

    In a future post I’ll walk you through how this plugin works.

    For now, just look for the latest release, and away you go Smile

LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, ASP.NET, WinForms, HTML5 or Windows 10, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2017 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners