4

I'm implementing IDE support for a language using Language Server Protocol.

I want to trigger a rename after extracting a variable into the current scope. That is, I've implemented steps 1 to 2 of the current flow and want to know how to implement 3 and 4

  1. When the user selects an expression a yellow lightbulb shows up. Example: z = 3 + /*selection-start*/5000/*selection-end*/ lightbulb

  2. When the user selects "extract into variable" then a new variable called "placeholder" is created in the current scope and the original expression is assigned to it. Example: placeholder = 5000; z = 3 + placeholderrenaming

  3. The first instance of placeholder is highlighted and the text box for renaming pops up. When the user types "the_new_name" and presses Return then the text is: the_new_name = 5000; z = 3 + the_new_name

renamed

Is it possible to implement this flow with LSP? If so, how? I checked the LSP spec and it sounds like I'm looking for a Command, but I didn't see a built-in Command for renaming

TypeScript's language server has the behavior I'm trying to replicate (implemented around here), but TypeScript doesn't implement language server protocol, so peeking at its source didn't help me. The screenshots above are from the TypeScript plugin built into VSCode

Max Heiber
  • 14,346
  • 12
  • 59
  • 97

2 Answers2

2

It seems as if editors must call for this rename themselves. Servers can send a list of edits as a result of a code action request, however they cannot request input from the user at this time: https://github.com/microsoft/language-server-protocol/issues/1641

amycodes
  • 902
  • 14
  • How does TypeScript do it? Would it be possible with a custom command and some code on the LSP server side to handle it? Alternatively, there's Rust Analyzer's solution, which is to put the cursor on the new variable, which is almost as good. Do you know where I could go to learn how they pulled that off? – Max Heiber Apr 06 '23 at 17:44
  • 1
    TS does many out-of-spec things, so I wouldn't be surprised if this was another one of them. You need custom client logic to do it effectively, by sending a rename request after the codeaction. I'm not sure how else you would do it, at least from the spec. If you wanted custom server logic, it still wouldn't work right because that can't request text from the user (as per the issue). I can go digging in rust analyser now to find it. – amycodes Apr 06 '23 at 20:10
  • 1
    I don't really understand how rust-analyzer is doing it, but it's here: https://github.com/rust-lang/rust-analyzer/blob/625a6f37dec0629e8777a3550f5381618c819a3c/crates/ide-assists/src/handlers/extract_variable.rs#L84-L85 – amycodes Apr 06 '23 at 20:24
  • One minor note for other people finding this: The TypeScript support in VSCode uses a custom protocol, not LSP. There are LSPs for TS, but they use an adapter that wraps tsserver such as https://github.com/typescript-language-server/typescript-language-server. – Max Heiber Apr 07 '23 at 19:50
0

LSP does not support this flow, but it can be done with a custom command. Here's one technique:

  • Include snippet text in the the WorkspaceEdits, like "${0: placeholder}". Snippet syntax is documented here.
  • In the client, when handling code action responses, detect snippet syntax in edits and then use the IDE's APIs for triggering the rename flow. For VSCode, this can be done by triggering a custom command that uses VSCode APIs. I couldn't find any documentation for this, so cargo-culted Rust-Analyzer's solution. (function command(editor: vscode.editor) { ... editor.replace(range, text) ... }`
Max Heiber
  • 14,346
  • 12
  • 59
  • 97