autoload/lsp/codeaction.vim | 80 +++++++++++++++++++++++++++--------------------------- autoload/lsp/lsp.vim | 4 ++-- autoload/lsp/lspserver.vim | 4 ++-- autoload/lsp/util.vim | 12 ++++++++++++ doc/lsp.txt | 14 ++++++++++---- plugin/lsp.vim | 2 +- test/unit_tests.vim | 47 +++++++++++++++++++++++++++++++++++++++-------- diff --git a/autoload/lsp/codeaction.vim b/autoload/lsp/codeaction.vim index 5a34d908c7c2d8303434200c4ecd2b0d123875da..229523039a59fe73108d1885ed29e397a9f08517 100644 --- a/autoload/lsp/codeaction.vim +++ b/autoload/lsp/codeaction.vim @@ -43,7 +43,7 @@ DoCommand(lspserver, selAction) endif enddef -export def ApplyCodeAction(lspserver: dict, actions: list>): void +export def ApplyCodeAction(lspserver: dict, actions: list>, query: string): void if actions->empty() # no action can be performed util.WarnMsg('No code action is available') @@ -61,46 +61,46 @@ endfor var choice: number - if exists('g:LSPTest') && g:LSPTest && exists('g:LSPTest_CodeActionChoice') - # Running the LSP unit-tests. Instead of prompting the user, use the - # choice set in LSPTest_CodeActionChoice. - choice = g:LSPTest_CodeActionChoice - else - if opt.lspOptions.usePopupInCodeAction - # Use a popup menu to show the code action - popup_create(text, { - pos: 'botleft', - line: 'cursor-1', - col: 'cursor', - zindex: 1000, - cursorline: 1, - mapping: 0, - wrap: 0, - title: 'Code action', - callback: (_, result) => { - # Invalid item selected or closed the popup - if result <= 0 || result > text->len() - return - endif + if query =~ '^\d\+' # digit + choice = str2nr(query) + elseif query =~ '^/' # regex + choice = 1 + util.Indexof(actions, (i, a) => a.title =~ query[1 : ]) + elseif query != '' # literal string + choice = 1 + util.Indexof(actions, (i, a) => a.title[0 : query->len() - 1] == query) + elseif opt.lspOptions.usePopupInCodeAction + # Use a popup menu to show the code action + popup_create(text, { + pos: 'botleft', + line: 'cursor-1', + col: 'cursor', + zindex: 1000, + cursorline: 1, + mapping: 0, + wrap: 0, + title: 'Code action', + callback: (_, result) => { + # Invalid item selected or closed the popup + if result <= 0 || result > text->len() + return + endif - # Do the code action - HandleCodeAction(lspserver, actions[result - 1]) - }, - filter: (winid, key) => { - if key == 'h' || key == 'l' - winid->popup_close(-1) - elseif key->str2nr() > 0 - # assume less than 10 entries are present - winid->popup_close(key->str2nr()) - else - return popup_filter_menu(winid, key) - endif - return 1 - }, - }) - else - choice = inputlist(["Code action:"] + text) - endif + # Do the code action + HandleCodeAction(lspserver, actions[result - 1]) + }, + filter: (winid, key) => { + if key == 'h' || key == 'l' + winid->popup_close(-1) + elseif key->str2nr() > 0 + # assume less than 10 entries are present + winid->popup_close(key->str2nr()) + else + return popup_filter_menu(winid, key) + endif + return 1 + }, + }) + else + choice = inputlist(["Code action:"] + text) endif if choice < 1 || choice > text->len() diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index e42b12a12cd354be40e40c224ccd89296cab0be2..24f41bb6dbaea429a5a1f90f3423fd11057d4ab0 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -707,14 +707,14 @@ enddef # Perform a code action # Uses LSP "textDocument/codeAction" request -export def CodeAction(line1: number, line2: number) +export def CodeAction(line1: number, line2: number, query: string) var lspserver: dict = buf.CurbufGetServerChecked() if lspserver->empty() return endif var fname: string = @% - lspserver.codeAction(fname, line1, line2) + lspserver.codeAction(fname, line1, line2, query) enddef # Perform a workspace wide symbol lookup diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index 16d98380013aea065742b806e47983cba9abb75e..0f6385b919ab2a8171626b0e5a587a20a9457ed3 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -1335,7 +1335,7 @@ # Request: "textDocument/codeAction" # Param: CodeActionParams def CodeAction(lspserver: dict, fname_arg: string, line1: number, - line2: number) + line2: number, query: string) # Check whether LSP server supports code action operation if !lspserver.isCodeActionProvider util.ErrMsg("Error: LSP server does not support code action operation") @@ -1368,7 +1368,7 @@ util.WarnMsg('No code action is available') return endif - codeaction.ApplyCodeAction(lspserver, reply.result) + codeaction.ApplyCodeAction(lspserver, reply.result, query) enddef # List project-wide symbols matching query string diff --git a/autoload/lsp/util.vim b/autoload/lsp/util.vim index 11b160655c7344e7f40b2f675b0256f1ef06835c..091c8d221dfff36249ef2ba0896318a38ffee8a3 100644 --- a/autoload/lsp/util.vim +++ b/autoload/lsp/util.vim @@ -206,4 +206,16 @@ setcursorcharpos(location.targetSelectionRange.start.line + 1, location.targetSelectionRange.start.character + 1) enddef +# 'indexof' is to new to use it, use this instead. +export def Indexof(list: list, CallbackFn: func(number, any): bool): number + var ix = 0 + for val in list + if CallbackFn(ix, val) + return ix + endif + ix += 1 + endfor + return -1 +enddef + # vim: tabstop=8 shiftwidth=2 softtabstop=2 diff --git a/doc/lsp.txt b/doc/lsp.txt index f25181de71680a476a9409eeb778650fe9ebafad..0cecbe75adf154e6b119841e2a2b0dd91ce30d68 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -330,13 +330,19 @@ A description of the various commands provided by this plugin is below. You can map these commands to keys and make it easier to invoke them. *:LspCodeAction* -:LspCodeAction Apply the code action supplied by the language server +:LspCodeAction [query] Apply the code action supplied by the language server to the diagnostic in the current line. This works only if there is a diagnostic message for the current line. You can use the |:LspDiagCurrent| command to display - the diagnostic for the current line. You will be - prompted to select one of the actions supplied by the - language server. + the diagnostic for the current line. + + When [query] is given the code action starting with + [query] will be applied. [query] can be a regexp + pattern, or a digit corresponding to the index of the + code actions in the created prompt. + + When [query] is not given you will be prompted to select + one of the actions supplied by the language server. *:LspDiagCurrent* :LspDiagCurrent Displays the diagnostic message (if any) for the diff --git a/plugin/lsp.vim b/plugin/lsp.vim index c983f56cf0bf8da2a2ee24b6076d3aa7fea8d71b..8bf146b8b2244f77be7a0f010d03ac7b549e2223 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -80,7 +80,7 @@ # This takes some time. # autocmd VimLeavePre * call lsp.StopAllServers() # LSP commands -command! -nargs=0 -bar -range LspCodeAction lsp.CodeAction(, ) +command! -nargs=? -bar -range LspCodeAction lsp.CodeAction(, , ) command! -nargs=0 -bar LspDiagCurrent lsp.LspShowCurrentDiag() command! -nargs=0 -bar LspDiagFirst lsp.JumpToDiag('first') command! -nargs=0 -bar LspDiagHighlightDisable lsp.DiagHighlightDisable() diff --git a/test/unit_tests.vim b/test/unit_tests.vim index dd16bfde1259391b11bf47e9860bd7b73a514b2c..777649a6d8c48c7b6a257c0253219fae2bc7d090 100644 --- a/test/unit_tests.vim +++ b/test/unit_tests.vim @@ -343,26 +343,57 @@ setline(1, lines) sleep 1 cursor(4, 1) redraw! - g:LSPTest_CodeActionChoice = 1 - :LspCodeAction + :LspCodeAction 1 assert_equal("\tcount = 20;", getline(4)) setline(4, "\tcount = 20:") cursor(4, 1) sleep 500m - g:LSPTest_CodeActionChoice = 0 - :LspCodeAction + :LspCodeAction 0 assert_equal("\tcount = 20:", getline(4)) - g:LSPTest_CodeActionChoice = 2 cursor(4, 1) - :LspCodeAction + :LspCodeAction 2 assert_equal("\tcount = 20:", getline(4)) - g:LSPTest_CodeActionChoice = 1 cursor(4, 1) - :LspCodeAction + :LspCodeAction 1 assert_equal("\tcount = 20;", getline(4)) + bw! + + # pattern and string prefix + silent! edit Xtest.c + sleep 200m + var lines2: list =<< trim END + void testFunc() + { + int count; + if (count = 1) { + } + } + END + setline(1, lines2) + sleep 1 + cursor(4, 1) + redraw! + :LspCodeAction use + assert_equal("\tif (count == 1) {", getline(4)) + + setline(4, "\tif (count = 1) {") + cursor(4, 1) + sleep 500m + :LspCodeAction /paren + assert_equal("\tif ((count = 1)) {", getline(4)) + + setline(4, "\tif (count = 1) {") + cursor(4, 1) + sleep 500m + :LspCodeAction NON_EXISTING_PREFIX + assert_equal("\tif (count = 1) {", getline(4)) + + cursor(4, 1) + :LspCodeAction /NON_EXISTING_REGEX + assert_equal("\tif (count = 1) {", getline(4)) bw! # empty file