This is an archive of the discontinued LLVM Phabricator instance.

[clangd][WIP] Integrate the refactoring actions into clangd
Needs ReviewPublic

Authored by arphaman on Oct 18 2017, 7:15 AM.

Details

Summary

This WIP patch provides a sample implementation of an integration of Clang's refactoring actions from libToolingRefactor into clangd.

In terms of protocol support, the patch adds:

  • Support for the Command & CommandArgument structures.
  • Support for the workspace/executeCommand command.

Note that the rename is not supported as an editor command since LSP has another entry in the protocol for it.

The integration with the refactoring library is done through the RefactoringEditorClient class that's implemented in a parent revision. Right now the test checks that the initial version of the "extract function" refactoring can be performed.

Diff Detail

Repository
rL LLVM

Event Timeline

arphaman created this revision.Oct 18 2017, 7:15 AM

I think that the clangd editor plugin will have to be modified as well, but I haven't looked into that yet.

ioeric added a subscriber: ioeric.Oct 18 2017, 8:00 AM
ilya-biryukov added inline comments.Oct 23 2017, 12:43 AM
clangd/ClangdServer.cpp
500

Maybe initialize RefactoringClient in constructor?

514

s/getCommands//

526

NIT: remove empty line.

clangd/Protocol.cpp
992

Maybe use llvm::Optional<CommandArgument> instead of CommandArgument?
If the loop body below won't get executed (i.e., Params is empty), we'll be returning uninitialized local variable.

clangd/Protocol.h
540

Could we make ArgumentKind, union members and default constructor private (makeDocumentID , makeSelectionRange and unparse would be the only way to construct CommandArgument)?

This would give an interface that's much harder to misuse.

sammccall edited edge metadata.Oct 23 2017, 9:24 AM

As noted on the other patch, I'm not sure EditorCommands is a useful abstraction.

This shows up one of the problems: clangd is mostly decoupled from the actual behavior/semantics of the commands by the EditorCommands abstraction. However LSP doesn't say what the structure of ExecuteCommandParams.arguments is - it depends on the individual command. But clangd needs to parse those arguments out of the JSON! So we're left with all the EditorCommands needing to take the same set of arguments, which then get hardcoded in clangd.

Coupling clangd more closely to the refactorings seems simpler and more flexible - what would we be losing?

Other than the EditorCommands question, implementation looks fine!

clangd/Protocol.h
535

Why a union-of-unions instead of AlignedCharArrayUnion<Range, TextDocumentIdentifier>?

ilya-biryukov edited edge metadata.Oct 25 2017, 2:06 AM
ilya-biryukov added a subscriber: malaperle.

There's another patch (D39276) that tries to add workspace/executeCommand for a slightly different use-case.
Could we take the code for parsing/handling workspace/executeCommand from this patch and extract it into a separate change so that these two patches can be reviewed independently?
I expect that the only addition the other patch needs (D39276) is to add a new kind of CommandArgument.

rwols added a subscriber: rwols.Oct 25 2017, 2:19 PM

There's another patch (D39276) that tries to add workspace/executeCommand for a slightly different use-case.
Could we take the code for parsing/handling workspace/executeCommand from this patch and extract it into a separate change so that these two patches can be reviewed independently?

@arphaman unless you're far down this path already, I'd actually prefer we land that patch soon and merge this one into it:

  • It's got a pretty narrow scope (1 command, not much logic), and will be ready soon. It's almost actually the isolated patch that @ilya-biryukov describes. This patch is both more general in scope and is stacked on top of other nontrivial changes, so will probably take longer.
  • It makes a couple of different decisions that I think would aid the design here, will leave detailed comments.

But if people prefer, we can certainly tackle command parsing separately.

clangd/Protocol.cpp
990

Here we're trying to parse the args independent of what the command is. I'm not sure this is a path we want to go down:

  • there's no guarantee that args are even Objects, for a start!
  • we're detecting the type based on field names "range" and "doc", which don't seem that unlikely to be used in different ways by different commands. In that case, we've painted ourselves into a corner.
  • I'm uncomfortable with the idea that if we encounter an unknown command with unknown args, we're going to throw them at "parse" anyway and... probably get None back? (I'd hope our parsers don't crash, but d0k's fuzzing shows otherwise I think)

If we parse the arg list with some small per-command logic, we can still easily share the code that handles common arg types like a selection.

clangd/Protocol.h
533

vector<union> is a good literal translation of any[], but it's not that convenient for the code that's consuming these args, nor is it convenient to implement (basically because LLVM lacks a good variant, I think).

D39276 hints at a different approach that I think is worth a look: basically we try to fit the args into a set of typed "slots" which are just Optional<T> fields on EditorCommand. For each command, we can decide how to parse the args into these slots, and which slots we're going to look at. We can share slots for common attributes (selection), but some of them will probably be specialized. (If size is a concern, we can use unique_ptr instead of Optional).

This does mean making some assumptions (relative order of different args isn't significant, have to choose between "single" and "list" for each slot) but they seem pretty safe/flexible.

There's another patch (D39276) that tries to add workspace/executeCommand for a slightly different use-case.
Could we take the code for parsing/handling workspace/executeCommand from this patch and extract it into a separate change so that these two patches can be reviewed independently?

@arphaman unless you're far down this path already, I'd actually prefer we land that patch soon and merge this one into it:

  • It's got a pretty narrow scope (1 command, not much logic), and will be ready soon. It's almost actually the isolated patch that @ilya-biryukov describes. This patch is both more general in scope and is stacked on top of other nontrivial changes, so will probably take longer.
  • It makes a couple of different decisions that I think would aid the design here, will leave detailed comments.

But if people prefer, we can certainly tackle command parsing separately.

Ok, I will wait until that patch lands.

hokein added a subscriber: hokein.Nov 3 2017, 9:35 AM