Skip to content

Suggestion: "extract variable" and "inline variable" refactorings #3750

Closed

Description

@cmeeren

"Variable" might be the wrong name, but you get the picture.

Given the following code:

<code before>
let bar = foo |> doSomething param1 param2 param3 |> thenSomethingElse
<code after>

I frequently want to hightlight any valid sub-expression, say doSomething param1 param2 param3, and press a keyboard shortcut to get this:

<code before>
let curried = doSomething param1 param2 param3
let bar = foo |> curried |> thenSomethingElse
<code after>

Naturally, upon execution, this refactor will end up in a rename operation on the name of the extracted value so that I can pick one myself.

I realize also that some decision will need to be taken if you try to extract something from a one-liner, e.g.

let foo = somethingToExtract somethingElse

might become

let foo = 
  let bar = somethingToExtract
  bar somethingElse

Furthermore, I frequently have need for the exact opposite: Place the caret on a variable name and use a hypothetical "inline variable" refactoring to inline the definition everywhere it's used.

In sum, these are probably the refactorings I miss the most when working in F#.

Activity

  1. added this to the Unknown milestone on Aug 25, 2018
  2. cartermp commented on Dec 16, 2020

    @cartermp
    Contributor

    I took a dive into what it would take to implement this today. Unfortunately it's highly complex and we just don't have the tools to make it work. Problems encountered:

    1. The F# tools in VS do not have a mapping from a document to a syntax tree and vice/versa. There is no "get syntax tree" operation on a document nor is there an ability to say, "here is the modified tree, now write it to the document". It is possible to work around this, which we do in all tooling, but it takes a lot of code and ends up with a lot of helpers being written.
    2. The F# syntax tree is (1) lossy and (2) lacks key abilities like "find the first ancestor to this node" and "insert a new node before or after this other node". It's just not something the F# compiler service supports as concepts. This makes determining key information difficult (lots of code) and also makes writing new data into a user's document very, very challenging. What would normally be pretty simple - find the syntax node we're adjacent to and insert a new node of a particular kind - now requires manually wiring up the functionality to communicate between our lossy tree's data and an underlying document's text representation. It's error-prone and complicated code that is simply working at the wrong abstraction level.
    3. We don't support the Roslyn syntax tree API, which is used for these refactorings, critically, to mark specific nodes as needing to be renamed. Roslyn's infrastructure will take care of starting a new rename session once the changes to the syntax tree are written with those annotations. So there's no way to trigger the rename after the text changes even if points (1) and (2) were worked around well enough.

    I'm going to keep this open because it's a valuable feature to have, but I'm afraid that there's not much hope in it getting implemented any time soon. To do this in any maintainable and/or extendable way (that we could be confident would be bug-free) would require a very, very large amount of code.

  3. locked and limited conversation to collaborators on Apr 5, 2022
  4. converted this issue into a discussion #12940 on Apr 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions