microsoft/vscode

No way to ensure an extension is prioritized as a rename provider in a language

Opened this issue · 4 comments

We are implementing rename functionality and have found some guidance about how to ensure our extension "wins". This is based on the fact that rename takes the results from the first extension to return anything, the scoring implementation, and finally how the sorting handles when there is a score tie.

Unfortunately, the option for depending on the built-in extension doesn't work for us. The Angular Language Service provides features for both html and typescript languages. We've found that if the first document opened is an html file, the built in typescript extension does not register its providers, but our extension does. Then when a TS file is opened, the TS extension registers as a rename provider, meaning it will "win" the registration time tie breaker. The result is that renames will not work correctly from TS files.

We haven't found any additional guidance in existing issues or documentation on how to approach this. I guess I would categorize this as a feature request to make the provider registry for renaming more configurable.

Yes - there is no good way to achieve this. You continue the funky-path, e.g your extension could simply activate the TS/JS extension but that's all from "hacker land". I also don't know a good solution to this one. These the options

  • let the user choose - dynamically or via setting
  • add a setting to TS to disable its rename feature
  • NOT an option: allow extension A to mute extension B

Hi @jrieken, thanks for the quick response! I like the idea of letting the user choose which extension takes precedence, either dynamically or via a setting.

As for what we can do at the moment, while it feels very "hacker land", it's at least explicit and predictable behavior if it can be made to work. I'm still having a bit of trouble figuring that part out though.

In our extension, before we start our LanguageClient we try to activate the TS/JS extension with const ext = vscode.extensions.getExtension('vscode.typescript-language-features'); and await ext?.activate();. This doesn't seem to have the desired effect though.
If the first file opened was an HTML file, I still see the "Initializing TS/JS language features" notification the first time a TS file is open and renames from TS files are still handled by the built in extension. I would assume this means I wasn't able to get the TS/JS extension to register providers before ours (or prevent it from registering again when the first TS file is open).

We've also wondered investigating if re-registering ourselves as a rename provider after the first TS file is opened could do the trick but haven't tried it out yet.

I have a very hacker way to achieve this.
Assuming there are only two extensions, the TS/JS extension and angular extension, and the TS/JS extension win, If I make the TS/JS extension reject the rename request by writing a ts server plugin, the vscode will ask the next extension which will be the angular extension.
But if there is another extension, I think this cannot make sure the angular extension always wins.

I ran into the same behavior when registering a rename provider for typescript. I would argue for having a solution where both rename providers (our own and typescript's) would be useable at the same time.

My situation is that when a user renames an 'x' then we have to rename a corresponding 'y' too or things will break later. In typescript this was neatly done by me finding 'y' when there was an attempt to rename an 'x', executing the vscode.executeDocumentRenameProvider for both 'x' and 'y' with their new names but not returning anything in my provider during this time so that I got results from typescripts own provider, merge the edits from these results and return the merge from my provider.

I learned today that this isn't exactly supported, hopefully it will be in the future because being able to reuse typescripts provider saves a lot of work.