diff options
author | Tomáš Janoušek <tomi@nomi.cz> | 2021-10-07 04:29:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-07 11:29:00 +0900 |
commit | efa563826d3dd20c4dbd5cbb6b2bdf2f46ac286e (patch) | |
tree | e7f4a983ace5ffeecf4b4ea18039727e1ca231f5 | |
parent | f9deee0e416f626d8597e3e6ea8e89172e5aaa78 (diff) | |
download | ale-efa563826d3dd20c4dbd5cbb6b2bdf2f46ac286e.zip |
codefix: Fix code actions that return Command[] directly instead of CodeAction[] (#3929)
According to
https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_codeAction,
the response to textDocument/codeAction is:
(Command | CodeAction)[] | null
and the code only handled the case where it was a CodeAction that either
specified an edit or a command, but didn't handle a direct Command.
Note that the specification also says that both can be specified and
then the edit is applied first, then the command. Furthermore, there
seems to be some hacky code handling arguments directly, which I suspect
is non-standard and only works with a specific LSP server that happens
to pass the edits in the arguments unmodified.
-rw-r--r-- | autoload/ale/codefix.vim | 8 | ||||
-rw-r--r-- | test/test_codefix.vader | 22 |
2 files changed, 19 insertions, 11 deletions
diff --git a/autoload/ale/codefix.vim b/autoload/ale/codefix.vim index 09ee34cb..853ee4e8 100644 --- a/autoload/ale/codefix.vim +++ b/autoload/ale/codefix.vim @@ -202,6 +202,14 @@ function! ale#codefix#ApplyLSPCodeAction(data, item) abort \) let l:request_id = ale#lsp#Send(a:data.connection_id, l:message) + elseif has_key(a:item, 'command') && has_key(a:item, 'arguments') + \&& type(a:item.command) == v:t_string + let l:message = ale#lsp#message#ExecuteCommand( + \ a:item.command, + \ a:item.arguments, + \) + + let l:request_id = ale#lsp#Send(a:data.connection_id, l:message) elseif has_key(a:item, 'edit') || has_key(a:item, 'arguments') if has_key(a:item, 'edit') let l:topass = a:item.edit diff --git a/test/test_codefix.vader b/test/test_codefix.vader index a9fff89d..88a3676c 100644 --- a/test/test_codefix.vader +++ b/test/test_codefix.vader @@ -444,16 +444,6 @@ Execute("workspace/applyEdit" from LSP should be handled): \ [{'description': 'applyEdit', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 28, 'line': 8}, 'newText': ', Config', 'start': {'offset': 28, 'line': 8}}, {'end': {'offset': 13, 'line': 97}, 'newText': 'await newFunction(redis, imageKey, cover, config);', 'start': {'offset': 3, 'line': 95}}, {'end': {'offset': 3, 'line': 100}, 'newText': '^@async function newFunction(redis: IRedis, imageKey: string, cover: Buffer, config: Config) {^@ try {^@ await redis.set(imageKey, cover, ''ex'', parseInt(config.coverKeyTTL, 10));^@ }^@ catch { }^@}^@', 'start': {'offset': 3, 'line': 100}}]}]}], \ g:code_actions -Execute(Code Actions from LSP should be handled with user input if there are more than one action): - call ale#codefix#SetMap({2: {}}) - call ale#codefix#HandleLSPResponse(1, - \ {'id': 2, 'jsonrpc': '2.0', 'result': [{'title': 'fake for testing'}, {'arguments': [{'documentChanges': [{'edits': [{'range': {'end': {'character': 31, 'line': 2}, 'start': {'character': 31, 'line': 2}}, 'newText': ', createVideo'}], 'textDocument': {'uri': 'file:///foo/bar/file.ts', 'version': 1}}]}], 'title': 'Add ''createVideo'' to existing import declaration from "./video"', 'command': '_typescript.applyWorkspaceEdit'}]}) - - AssertEqual g:handle_code_action_called, 1 - AssertEqual - \ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 32, 'line': 3}, 'newText': ', createVideo', 'start': {'offset': 32, 'line': 3}}]}]}], - \ g:code_actions - Execute(Code Actions from LSP should be handled when returned with documentChanges): call ale#codefix#SetMap({2: {}}) call ale#codefix#HandleLSPResponse(1, @@ -464,7 +454,7 @@ Execute(Code Actions from LSP should be handled when returned with documentChang \ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/test.py', 'textChanges': [{'end': {'offset': 1, 'line': 1}, 'newText': 'def func_bomdjnxh():^@ a = 1return a^@^@^@', 'start': {'offset': 1, 'line': 1}}, {'end': {'offset': 10, 'line': 2}, 'newText': 'func_bomdjnxh()^@', 'start': {'offset': 9, 'line': 2}}]}]}], \ g:code_actions -Execute(LSP Code Actions handles command responses): +Execute(LSP Code Actions handles CodeAction responses): call ale#codefix#SetMap({3: { \ 'connection_id': 0, \}}) @@ -475,6 +465,16 @@ Execute(LSP Code Actions handles command responses): \ [[0, 'workspace/executeCommand', {'arguments': [{'file': '/foo/bar/file.ts', 'action': 'function_scope_1', 'endOffset': 0, 'refactor': 'Extract Symbol', 'endLine': 68, 'startLine': 65, 'startOffset': 1}], 'command': '_typescript.applyRefactoring'}]], \ g:message_list +Execute(LSP Code Actions handles Command responses): + call ale#codefix#SetMap({2: { + \ 'connection_id': 2, + \}}) + call ale#codefix#HandleLSPResponse(1, + \ {'id': 2, 'jsonrpc': '2.0', 'result': [{'title': 'fake for testing'}, {'arguments': [{'documentChanges': [{'edits': [{'range': {'end': {'character': 31, 'line': 2}, 'start': {'character': 31, 'line': 2}}, 'newText': ', createVideo'}], 'textDocument': {'uri': 'file:///foo/bar/file.ts', 'version': 1}}]}], 'title': 'Add ''createVideo'' to existing import declaration from "./video"', 'command': '_typescript.applyWorkspaceEdit'}]}) + + AssertEqual + \ [[0, 'workspace/executeCommand', {'arguments': [{'documentChanges': [{'edits': [{'range': {'end': {'character': 31, 'line': 2}, 'start': {'character': 31, 'line': 2}}, 'newText': ', createVideo'}], 'textDocument': {'uri': 'file:///foo/bar/file.ts', 'version': 1}}]}], 'command': '_typescript.applyWorkspaceEdit'}]], + \ g:message_list Execute(Prints message when LSP code action returns no results): call ale#codefix#SetMap({3: {}}) |