Introducing ReSharper 5.0: Structural Search and Replace

Previous posts introducing ReSharper 5.0:

Structural Search and Replace is one of the most powerful yet less explored ReSharper 5.0 features. It allows you to find code that matches a structured template, parts of which may have certain restrictions. For example, you can set the type of the expression you are looking for, or specify the number of arguments. In addition to plainly finding parts of code that match the template, you can instantly replace them using an equally powerful replace template, or even configure ReSharper to inspect your solution for code that matches the search pattern and suggest replacing it using a quick-fix. Simply put, Structural Search and Replace provides you with an opportunity to easily extend built-in ReSharper code inspections with your custom ones.

Smart Search

Let’s look at a specific example. We’ll search for all expressions matching the pattern enumerable.Count() > 0, where enumerable is any expression of type IEnumerable.

If you were to solve this task with Find Usages, you would step through all the calls of the Count() method. This means you would have to go through tens or hundreds of calls, which is tiring and prone to error. Find Text might look slightly more promising, but once you think about it, problems come up too:

  1. The expression may be written with line returns or comments.
  2. The Count() method in your project may be implemented by objects of different types.

Find Text doesn’t cut it either.

Choose ReSharper | Find | Search With Pattern and enter the following pattern in the text box:

The $enumerable$ string will be highlighted red. This is because dollar signs signify placeholders — a placeholder is replaced with any text matching the specified restrictions: the placeholder type and its parameters. In our case, $enumerable$ will match any expression of type IEnumerable. We must define this placeholder by clicking Add Placeholder, selecting Expression and entering “enumerable” in the Name field (the placeholder name without the dollar signs). For Expression Type enter “IEnumerable” (just start typing and ReSharper will suggest suitable options). Make sure to check Or derived type as well.

So, in just a few seconds, without even going near regular expressions we’ve created an effective search pattern. But here’s another feature that’s extra-useful! Note the Match similar constructs check box below the edit field. If you select it, ReSharper will search not only for exact matches but also for constructs that are semantically identical. For example, a > 0 and 0 < a are semantically identical. In our case, you should probably select this check box, because you’re likely to be looking for all the different conditional statements where Count() is compared with zero.

That’s all! Now click Find and see the results.

Smart Replace

A search feature this powerful would not be nearly as useful without a replace feature. You don’t want to just locate all the bad code — you want to replace it with good code! So, go ahead and click Replace in the Search With Pattern dialog. You will get a box to enter the replace pattern. The replace pattern can be any text that is valid for the language you’re using, plus placeholders if you need them. For example, enter the following:

$enumerable$.Any()

And click Replace!

Turn a Pattern into a Highlighting and a Quick-Fix

Now you have a search template and a replace pattern. It makes sense to create a highlighting and a quick-fix based on these patterns. To do so, click Save in the pattern editing dialog, and your pattern will be saved to the Pattern Catalog. This catalog is used to store commonly used patterns, and may turn into a powerful tool for creating your own code inspections.

If you open the catalog (ReSharper | Tools | Pattern Catalog), you can define a tooltip for your pattern, which will pop up in the corresponding quick-fix, as well as the type of highlighting you want to apply:

Once you set these options for your pattern, the highlighting works! Now all code that matches your pattern will be highlighted — on the fly. Also, the appropriate quick-fix will appear as soon as you position the caret on the highlighted expression. Ain’t it cool?!

Search Pattern Examples

This search pattern is designed to simplify expressions:

In this example we used placeholders for the types of the expression and the identifier. We didn’t impose any restrictions on them, but used them in the replace pattern. The only placeholder with restrictions is $seq$ — it’s an IEnumerable.

And here’s a pattern that implements highlighting and a quick-fix for Replace ‘if’ with ‘?:’:

Sharing Patterns

If you have created a useful search and replace pattern that detects and removes a code smell, share your knowledge with colleagues using import/export functionality in the the Pattern Catalog! As an option, leave comments with your patterns, and with your permission, we may include the most interesting patterns in the next version of ReSharper!

UPDATE! A sample Pattern Catalog is now available from the ReSharper web site. Learn details in this blog post.

Author: Alexander Zverev, senior ReSharper developer. Translated from original article (in Russian)

This entry was posted in How-To's, ReSharper Tips&Tricks and tagged , , , . Bookmark the permalink.

24 Responses to Introducing ReSharper 5.0: Structural Search and Replace

  1. Is it possible to get your patterns from the article?

  2. Jon Erickson says:

    @Igal Tabachnik – here are the patterns from the article:

    Use method Any()
    $seq$.Any()
    0]]>

    Replace with ternary operator
    $x$ = $condition$ ? $expr1$ : $expr2$;

  3. Jon Erickson says:

    haha, that didn’t turn out so well, go here for the patterns xml:

    http://codepaste.net/e4m5n3

  4. Oleg Anashkin says:

    Screenshot of patterns catalog has quite a lot of (probably useful) patterns. Can you share all of them, please?

  5. John Long says:

    This is a neat feature, I’ve toyed with it a bit and can see the potential power.
    One thing you need, though, is a way to indicate that a pattern only applies to a particular version of the .NET framework, otherwise you’re going to end up with inaccurate suggestions.

  6. David Martin says:

    From the Pattern Catalogue there is a Search Now button. What is this supposed to do? I followed your example of $enumerable$.Count() > 0 and it works great. However, if I highlight it in the catalogue and click Search Now I get a message stating: “Can not parse pattern”. Is this a bug, or an I doing something wrong?

  7. @John
    You’re probably right. Please submit a feature request to ReSharper issue tracker

    @David
    “Search now” It’s supposed to search for occurrences of a specific search pattern and display them in the “Find Results” tool window.
    The message you’re getting may be a bug but it can display when you’ve not added a placeholder or configured it incorrectly, so please make sure it’s all ok with your placeholder. Btw, if you’re using an older R# build, I recommend that you upgrade to a fresh nightly build from http://confluence.jetbrains.net/display/ReSharper/ReSharper+5.0+Nightly+Builds because SSR has been worked on until recently

  8. @Oleg
    We’re going to include them in R# 5.0 release :)
    If for some reason we don’t, I’ll publish them here.

  9. @David> I had the same problem but I found why :
    When using $x$ in the template, the place holder name must be x and not $x$…
    So remove the $ signs in the place holder names and the placeholders should become blue (instead of red) in your template.

  10. Gik25 says:

    You had the best idea in the history of software best practice, but without providing an online automatized way for sharing pattern you vanify all the effort of single programmers.

    For example I think that accessor for property and field should always be in the following format:

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string _id;

    public string Id
    {
    [DebuggerStepThrough]
    get{return _id;}
    [DebuggerStepThrough]
    set {_id = value;}
    }

    I can do a search and replace pattern but no one else would use it.
    And you could provide similar example by yourself… but just if you create a community.

    Why don’t you premiate the better patterns with a resharper license? I guess you would motivate peoples to do their better.

  11. Richard says:

    From the main ReSharper page:
    “JetBrains plans to ship ReSharper 5 simultaneously with Visual Studio 2010.”

    Is there any update on this? VS2010 shipped today, and although the new tools are nice, I’m already missing R#!

  12. @Richard
    Not much left to wait :) If all goes fine, we’re releasing R# in approx. 4 hours

    BTW, “sim-shipping with Visual Studio” doesn’t mean shipping the same day with VS but within 30 days of VS release.

  13. Joe White says:

    “JetBrains plans to ship ReSharper 5 simultaneously with Visual Studio 2010.” — http://www.jetbrains.com/resharper/index.html

    I’m honestly baffled as to how you could say “simultaneously” — i.e., “at the exact same instant” — and think it’s obvious that you really meant “up to 30 days later”. If you’re redefining the language, shouldn’t you have at least included a translation guide?

    To be fair, I’m happy you’re not in a huge rush to ship — based on the last nightly build, there’s still some stabilization work to be done, especially around structured search and replace, and I’m happy you’re taking the time to get it right. That said, if you guys never actually intended to ship the same day as VS2010, then why did you say that you were going to?

  14. Obfuscator says:

    Hi Joe! Sorry for any confusion. We did intend to ship the same day as VS2010, and we have. Enjoy!

  15. Joe White says:

    Cool. It was odd to have VS out and have no word from R# at first, but since you did ship within a few hours after VS, that no-news period turned out to be no big deal. What really threw me was you stating that “sim-ship” meant “up to 30 days later” as if you expected us to know that. That’s not what those words mean to me — if you do this “sim-shipping” again, you might want to throw in a footnote, just so people aren’t surprised if you do need to use that little bit of extra time you planned for.

    Anyway, I’m looking forward to soon playing with the (hopefully more stable than the last nightly, i.e., able to actually create more than one pattern in the Pattern Catalog without them corrupting each other?) new version for real.

  16. Obfuscator says:

    Hi Joe,
    I feel obliged to clear up the confusion. In this case ‘sim-shipping’ is a Microsoft term and policy, not ours, and it stipulates shipping within 30 days of the launch of Visual Studio.
    In the future, we’ll make sure to specifically mention any delays. We didn’t in this case, because our launch was indeed pretty close to being ‘simultaneous’.
    Again, I hope we have met your expectations and the expectations of other folks out there, and that no one has been misled.
    Cheers,
    Obfuscator

  17. Greg says:

    I upgraded from the RC to the final release and I only have one pattern in my catalog “($t$)”. Should I have more?

  18. @Greg
    Seems that at the end of the day, the patterns did not make it into the release build :(
    We’ll publish them at the website and post a link in the blog. Stay tuned

  19. deerchao says:

    It supports C# only, not VB?

  20. Oleg Anashkin says:

    I have created RSRP-178923 to track “Provide more patterns in Patterns Catalog” request.

  21. Robbe says:

    Jou, great. So far.
    I did already some useful patterns for quick-replace.
    In the first impression I really needed some hints about syntax, but there’s just an editor. OK – I came across.
    Now however I try to match (and reformat) a property, but I can’t get it to work. I’d love to see some hints. This is what I tried:

    public $rettype$ $PropName$
    {
    get
    {
    $dummyStatements$
    }

    set
    {
    $dummyStatements$;
    }
    }

  22. Joe White says:

    @Robbe, a couple of guesses:

    1. I’m not an expert with SSR, but I’m guessing this would only match if the getter and setter both contained the *same* statements, which will never happen. Try making separate placeholders for the getter’s and setter’s dummy statements.

    2. Remove the semicolon from inside your setter. A statement placeholder will already match the semicolon, so adding another one might cause it to only match if you have an extra semicolon in your setter. (“Match similar constructs” might make this a non-issue, I’m not sure. But better to say what you mean, which means leaving off the semicolon.)

  23. Robbe says:

    @Joe White:
    You are so right. That was a mind-bug. The way I have it, now solves the problem, however not completely.
    Here I try to do something like INotifyChanged, however special to our domain.
    [FunctionProperty]
    public $type$ $propid$
    {
    get { $getstmt$ }
    set { $setstmt$ }
    }
    —— replace by ———
    [FunctionProperty(FunctionPropertyCategories.MultiChannel)]
    public $type$ $prop$
    {
    get{ return ($type$)this.GetCurrentChannelParameterValue(“TODO”);}
    set{ if (this.SetCurrentChannelParameterValue(“TODO”, value))
    { this.OnPropertyChanged(“TODO”); }}
    }
    ——–
    That works.
    The downside of this is obviously the “TODO”. I did not manage to get the property identifier replaced at that spot. So “$propid$” or similar kind do not work.
    @all: Does this feature consist of lets say string conversion of identifiers and types?
    Can this be solved?
    But anyways: Great feature – to be more documented and paved.

  24. Robbe says:

    BTW: Three nice Quickfixes from my side
    1. replace obj == otherthing wich objet.Equals
    2/3. Transform between var x = (type)y var x = y as type; if(x!=null)
    (maybe not fully prooven to succeed)
    http://pastebin.com/gXSpMpfb

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">