4

I want to select code, e.g.

doSomething(myEnum.firstThing)
doSomethingElse(myEnum.firstThing)
evenMoreStuff(myEnum.firstThing)

and convert it to a function

GroupCommonStuf(myEnum.firstThing)

which would look like:

GroupCommonStuff(myEnum id)
{
  doSomething(id)
  doSomethingElse(id)
  evenMoreStuff(id)
}

Preferably it would catch any code that is:

doSomething(myEnum.secondThing)
doSomethingElse(myEnum.secondThing)
evenMoreStuff(myEnum.secondThing)

and convert it to

GroupCommonStuf(myEnum.secondThing)

etc.

I have ReSharper installed. But the extract method in ReSharper seems to only extract the code block I select. It doesn't propagate it other "similar code", which I am sure Eclipse does for C++ and Java.

I assume I could also achieve this through regular expressions, but I am not regular expression enlightened yet. How can I do this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chrispepper1989
  • 2,100
  • 2
  • 23
  • 48
  • Cool idea. I can't see how this would work without parsing the code, unless you're just doing functions - even then I can see pitfalls. Once you create the syntax tree - maybe then you could do some sort of expanding search with some sort of minimum threshold. In my head I'm designing a compiler - so good look with this. ;-) – Daniel Gimenez Oct 31 '14 at 13:00
  • Unless I am remembering through rose tinted glasses I could swear this was possible in eclipse... I suppose I could write a parser....tempting :p – chrispepper1989 Oct 31 '14 at 13:12
  • @DanielGimenez Thanks to "search with pattern" parser wasn't needed :) perhaps the 3 steps below could be rolled into one by leveraging: https://www.jetbrains.com/resharper/features/open_api.html – chrispepper1989 Oct 31 '14 at 14:12

1 Answers1

4

This is possible in three steps using three useful ReSharper tools:

  • Extract To Method
  • Introduce Parameter
  • Search With Pattern

Possible alternatives without ReSharper are at the bottom


  1. Select one offending code block, Extract To Method

    (Ctrl + R, M)*** or right clickRefactorExtractMethod)

    Result:

    GroupCommonStuf()
    {
        doSomething(myEnum.firstThing)
        doSomethingElse(myEnum.firstThing)
        evenMoreStuff(myEnum.firstThing)
    
    }
    
  2. Select one "myEnum.firstThing" within the function, Introduce Parameter (Ctrl + R, P) or right clickRefactorIntroduce parameter

    The wizard is pretty self explanatory :)

    Result:

    GroupCommonStuf(myEnum type)
    {
        doSomething(type)
        doSomethingElse(type)
        evenMoreStuff(type)
    }
    
  3. ReSharper → FindSearch With Pattern (click replace tab)

    OR Select an untouched offending code block, Right click* → Find Similar Code

  4. Click Add PlaceHolder→ "Type" = Arguments → "Name"=enumeration

  5. Type the following:

    Enter image description here

    Result:

    Using the above pattern script code blocks that match:

    doSomething(myEnum.firstThing);
    doSomethingElse(myEnum.firstThing);
    evenMoreStuff(myEnum.firstThing);
    

    will be replaced with

    GroupCommonStuf(myEnum.firstThing)
    

    The same for any variation of the enum :).

Some Explanation On What's Going On Here

In short, ReSharper is finding any arguments between ( ) and storing them in the variable "enumeration" (the name doesn't actually matter). Then inserting that string of arguments anywhere the variable appears in the replace block.

There are different placeholders that do different things and it's important to use the right one. The "type" placeholder even supports regular expressions which makes it incredibly powerful.

You could simply type $enumeration$ in this case because the default placeholder is "arguments". However I would advise getting in the habit of using "add placeholder" as it will be clearer when you do more complex patterns. It's important that you know it's not just "put strings here into variable".

To explain this better, consider this example, say you had the following code dotted around:

... = GetGraphic(Graphics.First).ShapeArray[index].ShapeColour
... = GetGraphic(Graphics.Second).ShapeArray[index].ShapeSize
... = GetGraphic(Graphics.First).ShapeArray[index].ShapeSize

And you had decided to replace all of these with code more akin to

... = GetShapeColour(Graphics.First, index);
... = GetShapeSize(Graphics.Second, index);
... = GetShapeSize(Graphics.First, index);

By using the correct place holders you can do this with one search and replace:

enter image description here

  • $args$ (an argument placeholder) takes care of moving whatever the parameters is for you, in this case its just one, but it could be several.
  • $variableOfArray$ (A type place holder, with regex set to Shape*) takes care of moving the ShapeColour, ShapeSize "variable type" names

If you had used "argument placeholder" for "variableOfArray" Resharper would have said it couldn't find any occurrences of that pattern because there was no such thing as GetGraphic( .. ).ShapeArray[index].( .. ).


Possible Work-arounds without ReSharper:

Extract To Method → Extract Method Refactoring

Introduce Parameter → "find replace" with/without Promote Variable To Parameter

Search With Pattern → This is clearly the hard one to me there are three options that I wouldn't particularly want to explore and my guess would be would generally require more time then it would to do the changes manually:

  • VERY clever regular expression (this could be annoying within Visual Studio itself)
  • Bash scripts with clever sed (slight improvement on just regexing within Visual Studio, but still hard)
  • Writing your own parser???When all else fails...to the code!
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chrispepper1989
  • 2,100
  • 2
  • 23
  • 48