Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/tools/gopls: provide a way to run ExecuteCommand features in one step #40438

Open
leitzler opened this issue Jul 27, 2020 · 3 comments
Open

x/tools/gopls: provide a way to run ExecuteCommand features in one step #40438

leitzler opened this issue Jul 27, 2020 · 3 comments
Labels

Comments

@leitzler
Copy link
Contributor

@leitzler leitzler commented Jul 27, 2020

What version of Go are you using (go version)?

golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a
golang.org/x/tools/gopls v0.0.0-20200721032237-77f530d86f9a

I was adding fillstruct support to govim and they way gopls currently works is that it require a CodeAction/ExecuteCommand/ApplyEdit dance.

From a user point of view I see fillstruct as a direct request, initiated when the user already have the cursor placed on a non-filled struct identifier and the sole intention is to fill it.

It would be nice to be able to call ExecuteCommand from the client w/o a CodeAction request in before, but as discussed on Slack the different commands (and their arguments) aren't stable enough to be made public.
Example of a fillstruct command as returned by CodeAction:

Command:     &protocol.Command{
    Title:     "Fill foo with default values",
    Command:   "fill_struct",
    Arguments: {
        "{\"uri\":\"file:///private/var/folders/xm/ls208xd174v95pgd20rn_qbh0000gn/T/tmp.lmXRD9mP/main.go\",\"range\":{\"start\":{\"line\":7,\"character\":6},\"end\":{\"line\":7,\"character\":6}}}",
    },
},

As long as there is only one single (fillstruct) Command in the response, govim can automatically do another roundtrip and call ExecuteCommand with that Command. But if there are more than one Command in the CodaAction response (or the command isn't a fillstruct) there is currently no good way for the client to tell them apart.

Neither Command name (e.g. "fill_struct") or Arguments are specified anywhere so the client cannot know if/which command that belong to the request. Title is a dynamic string that contain the struct identifier. Using that field would require string matching (and unique struct identifiers on each line).

AFAIK other commands, such as "Extract variable" or "Extract function", are falls into the same category of user-invoked commands with a clear intent.

From my point of view gopls needs the following to be able to provide one step commands (I could obviously have missed other/better ways).

  • Use explicit kinds for all CodeActions with Commands, like refactor.rewrite.fillstruct instead of just the base kind refactor.rewrite so that it is possible to request a single kind of code action commands.
  • Allow a more narrow range as part of the CodeAction request (see CL244519). fillstruct operates on the whole line regardless of range specified in the CodeAction parameters.
@gopherbot
Copy link

@gopherbot gopherbot commented Jul 27, 2020

Change https://golang.org/cl/244519 mentions this issue: internal/lsp: allow narrower scope for convenience CodeActions

@stamblerre
Copy link
Contributor

@stamblerre stamblerre commented Jul 27, 2020

Use explicit kinds for all CodeActions with Commands, like refactor.rewrite.fillstruct instead of just the base kind refactor.rewrite so that it is possible to request a single kind of code action commands.

We do still need to look into how these code action kinds work, and maybe we will use them. Still, I think that a client executing commands directly will always be prone to errors and incompatibilities, at least until the protocol has a way to tell the client which arguments should be provided to which commands.

I'm not sure if this is something that the protocol will support, but microsoft/language-server-protocol#642 and microsoft/language-server-protocol#430 are relevant. It may be worth filing an issue for this specifically, as I'm sure that every language has to deal with this issue.

Unless something changes, the only "correct" way to call fillstruct will be to first call codeAction, as that's the only correct way of getting the command and checking if it's available to call.

gopherbot pushed a commit to golang/tools that referenced this issue Jul 27, 2020
Code actions that apply convenience fixes were filtered by the start
line so it wasn't possible to narrow the scope to a specific range.

This change allows clients to send a specific range (or cursor position)
to filter all fixes where the range doesn't intersect with the provided
range. It also widens the diagnostic returned by fillstruct analysis.

The idea is to provide a way to narrow the scope without breaking
clients that do want to ask for code actions using the entire line.

Updates golang/go#40438

Change-Id: Ifd984a092a4a3bf0b3a2a5426d3e65023ba4eebc
Reviewed-on: https://go-review.googlesource.com/c/tools/+/244519
Run-TryBot: Pontus Leitzler <leitzler@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
@leitzler
Copy link
Contributor Author

@leitzler leitzler commented Jul 28, 2020

We do still need to look into how these code action kinds work, and maybe we will use them. Still, I think that a client executing commands directly will always be prone to errors and incompatibilities, at least until the protocol has a way to tell the client which arguments should be provided to which commands.

Yes, the purpose here would be to provide a way for the client to ask for fillstruct (or any of the others) explicitly in the CodeAction request.

I think that would be needed in the case of refactor.extract already, and refactor.rewrite if/when other rewrites than fillstruct is added.

If the user want to extract to function they call CodeAction with only set to refactor.extract today and gets two CodeAction responses, one for "Extract to variable" and one for "Extract to function". They can't be told apart w/o looking at the Title.

That's why I suggest adding refactor.extract.function in the CodeAction request so the response can be used to call ExecuteCommand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.