From 28879f131e8881b399b83e8e5fe21cf1f5975c82 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 31 Jan 2021 17:12:43 -0800 Subject: [PATCH] Add support for 24x7 completion and omni-completion --- autoload/handlers.vim | 53 +++++++++++++++++++++++------ autoload/lsp.vim | 58 ++++++++++++++++++++++++++------ autoload/lspserver.vim | 2 ++ doc/lsp.txt | 76 +++++++++++++++++++++++++----------------- plugin/lsp.vim | 9 +++++ 5 files changed, 147 insertions(+), 51 deletions(-) diff --git a/autoload/handlers.vim b/autoload/handlers.vim index 944e51b..d644c99 100644 --- a/autoload/handlers.vim +++ b/autoload/handlers.vim @@ -32,14 +32,9 @@ def s:processInitializeReply(lspserver: dict, req: dict, reply: dicthas_key('completionProvider') + if g:LSP_24x7_Complete && caps->has_key('completionProvider') var triggers = caps.completionProvider.triggerCharacters - for ch in triggers - exe 'inoremap ' .. ch .. ' ' .. ch .. "" - endfor - lspserver.completionTriggerChars = - caps.completionProvider.triggerCharacters + lspserver.completionTriggerChars = triggers endif @@ -106,7 +101,7 @@ def s:processSignaturehelpReply(lspserver: dict, req: dict, reply: dic startcol = text->stridx(label) endif endif - var popupID = text->popup_atcursor({}) + var popupID = text->popup_atcursor({moved: 'any'}) prop_type_add('signature', {bufnr: popupID->winbufnr(), highlight: 'LineNr'}) if hllen > 0 prop_add(1, startcol + 1, {bufnr: popupID->winbufnr(), length: hllen, type: 'signature'}) @@ -162,6 +157,7 @@ def s:processCompletionReply(lspserver: dict, req: dict, reply: dict> = [] for item in items var d: dict = {} if item->has_key('textEdit') && item.textEdit->has_key('newText') @@ -188,10 +184,47 @@ def s:processCompletionReply(lspserver: dict, req: dict, reply: dictadd(d) + completeItems->add(d) endfor - lspserver.completePending = false + if g:LSP_24x7_Complete + if completeItems->empty() + # no matches + return + endif + + if completeItems->len() == 1 + && matchstr(getline('.'), completeItems[0].word .. '\>') != '' + # only one complete match. No need to show the completion popup + return + endif + + # Find the start column for the completion. If any of the entries + # returned by the LSP server has a starting position, then use that. + var start_col: number = 0 + for item in items + if item->has_key('textEdit') + start_col = item.textEdit.range.start.character + 1 + break + endif + endfor + + # LSP server didn't return a starting position for completion, search + # backwards from the current cursor position for a non-keyword character. + if start_col == 0 + var line: string = getline('.') + var start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\k' + start -= 1 + endwhile + start_col = start + 1 + endif + + complete(start_col, completeItems) + else + lspserver.completeItems = completeItems + lspserver.completePending = false + endif enddef # process the 'textDocument/hover' reply from the LSP server diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 0e04d61..4bd3d78 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -268,16 +268,24 @@ def lsp#addFile(bnr: number): void endif lspserver.textdocDidOpen(bnr, ftype) - # Display hover information - autocmd CursorHold call s:LspHover() # file saved notification handler autocmd BufWritePost call s:lspSavedFile() # add a listener to track changes to this buffer listener_add(function('lsp#bufchange_listener'), bnr) - setbufvar(bnr, '&completeopt', 'menuone,popup,noinsert,noselect') - setbufvar(bnr, '&omnifunc', 'lsp#omniFunc') - setbufvar(bnr, '&completepopup', 'border:off') + + # set options for insert mode completion + if g:LSP_24x7_Complete + setbufvar(bnr, '&completeopt', 'menuone,popup,noinsert,noselect') + setbufvar(bnr, '&completepopup', 'border:off') + # autocmd for 24x7 insert mode completion + autocmd TextChangedI call lsp#complete() + # in insert mode stops completion and inserts a + inoremap pumvisible() ? "\\" : "\" + else + setbufvar(bnr, '&omnifunc', 'lsp#omniFunc') + endif + setbufvar(bnr, '&balloonexpr', 'LspDiagExpr()') exe 'autocmd InsertLeave call lsp#leftInsertMode()' @@ -524,6 +532,39 @@ def lsp#jumpToDiag(which: string): void WarnMsg('Error: No more diagnostics found') enddef +# Insert mode completion handler. Used when 24x7 completion is enabled +# (default). +def lsp#complete() + var cur_col: number = col('.') + var line: string = getline('.') + + if cur_col == 0 || line->empty() + return + endif + + var ftype: string = &filetype + var lspserver: dict = s:lspGetServer(ftype) + if lspserver->empty() || !lspserver.running || lspserver.caps->empty() + return + endif + + # If the character before the cursor is not a keyword character or is not + # one of the LSP completion trigger characters, then do nothing. + if line[cur_col - 2] !~ '\k' + if lspserver.completionTriggerChars->index(line[cur_col - 2]) == -1 + return + endif + endif + + # first send all the changes in the current buffer to the LSP server + listener_flush() + + # initiate a request to LSP server to get list of completions + lspserver.getCompletion() + + return +enddef + # omni complete handler def lsp#omniFunc(findstart: number, base: string): any var ftype: string = &filetype @@ -572,17 +613,14 @@ enddef # Display the hover message from the LSP server for the current cursor # location -def LspHover() +def lsp#hover() var ftype = &filetype if ftype == '' return endif var lspserver: dict = s:lspGetServer(ftype) - if lspserver->empty() - return - endif - if !lspserver.running + if lspserver->empty() || !lspserver.running return endif diff --git a/autoload/lspserver.vim b/autoload/lspserver.vim index 922c0b5..1c089d8 100644 --- a/autoload/lspserver.vim +++ b/autoload/lspserver.vim @@ -58,6 +58,7 @@ def s:startServer(lspserver: dict): number lspserver.nextID = 1 lspserver.requests = {} lspserver.completePending = false + lspserver.completionTriggerChars = [] lspserver.workspaceFolders = [getcwd()] var job = job_start(cmd, opts) @@ -791,6 +792,7 @@ export def NewLspServer(path: string, args: list): dict caps: {}, requests: {}, completePending: false, + completionTriggerChars: [], diagsMap: {}, workspaceSymbolPopup: 0, workspaceSymbolQuery: '' diff --git a/doc/lsp.txt b/doc/lsp.txt index 2c0bb05..3aee08e 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -2,7 +2,7 @@ Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) For Vim version 8.2.2342 and above -Last change: Jan 20, 2021 +Last change: Jan 31, 2021 ============================================================================== *lsp-license* @@ -173,14 +173,14 @@ accepts a list of LSP servers with the above information. ============================================================================== 5. Commands *lsp-commands* - |:LspShowServers| + *:LspShowServers* :LspShowServers Displays the list of registered LSP servers and their status. The LSP servers are registered using the lsp#addServer() function. The output shows the Vim file type, the corresponding LSP server status and the path to the LSP server executable. - |:LspGotoDefinition| + *:LspGotoDefinition* :LspGotoDefinition Jumps to the definition of the symbol under the cursor. If the file is already present in a window, then jumps to that window. Otherwise, opens the file @@ -189,68 +189,68 @@ accepts a list of LSP servers with the above information. Also the |``| mark is set to the position before the jump. - |:LspGotoDefinition| + *:LspGotoDefinition* :LspGotoDeclaration Jumps to the declaration of the symbol under the cursor. The behavior of this command is similar to the |:LspGotoDefinition| command. - |:LspGotoTypeDef| + *:LspGotoTypeDef* :LspGotoTypeDef Jumps to the type definition of the symbol under the cursor. The behavior of this command is similar to the |:LspGotoDefinition| command. Note that not all the LSP servers support this feature. - |:LspGotoImpl| + *:LspGotoImpl* :LspGotoImpl Jumps to the implementation of the symbol under the cursor. The behavior of this command is similar to the |:LspGotoDefinition| command. Note that not all the LSP servers support this feature. - |:LspShowSignature| + *:LspShowSignature* :LspShowSignature Displays the signature of the symbol (e.g. a function or method) before the cursor in a popup. The popup is also automatically displayed in insert mode after entering a symbol name followed by a separator (e.g. a opening parenthesis). - |:LspDiagShow| + *:LspDiagShow* :LspDiagShow Creates a new location list with the diagnostics messages (if any) from the LSP server for the current file and opens the location list window. You can use the Vim location list commands to browse the list. - |:LspDiagFirst| + *:LspDiagFirst* :LspDiagFirst Jumps to the location of the first diagnostic message for the current file. - |:LspDiagNext| + *:LspDiagNext* :LspDiagNext Jumps to the location of the diagnostic message after the current cursor position. - |:LspDiagPrev| + *:LspDiagPrev* :LspDiagPrev Jumps to the location of the diagnostic message before the current cursor position. - |:LspDiagCurrent| + *:LspDiagCurrent* :LspDiagCurrent Displays the diagnostic message (if any) for the current line. - |:LspShowReferences| + *:LspShowReferences* :LspShowReferences Creates a new location list with the list of locations where the symbol under the cursor is referenced and opens the location window. - |:LspHighlight| + *:LspHighlight* :LspHighlight Highlights all the matches for the symbol under cursor. The text, read and write references to the symbol are highlighted using Search, DiffChange and DiffDelete highlight groups respectively. - |:LspHighlightClear| + *:LspHighlightClear* :LspHighlightClear Clears all the symbol matches highlighted by the |:LspHighlight| command. - |:LspOutline| + *:LspOutline* :LspOutline Opens a vertically split window with the list of symbols defined in the current file. The current symbol is highlighted. The symbols are grouped by @@ -262,27 +262,27 @@ accepts a list of LSP servers with the above information. the symbols in the new file. Folds are created in the outline window for the various group of symbols. - |:LspFormat| + *:LspFormat* :LspFormat Format the current file using the LSP server. :{range}LspFormat Format the specified range of lines in the current file using the LSP server. - |:LspCalledBy| + *:LspCalledBy* :LspCalledBy Display the list of symbols called by the current symbol. (NOT IMPLEMENTED YET). - |:LspCalling| + *:LspCalling* :LspCalling Display the list of symbols calling the current symbol (NOT IMPLEMENTED YET). - |:LspRename| + *:LspRename* :LspRename Rename the current symbol. You will be prompted to enter the new name for the symbol. You can press or enter an empty string in the prompt to cancel the operation. - |:LspCodeAction| + *:LspCodeAction* :LspCodeAction Apply the code action supplied by the LSP server to the diagnostic in the current line. This works only if there is a diagnostic message for the current line. @@ -291,7 +291,7 @@ accepts a list of LSP servers with the above information. prompted to select one of the actions supplied by the LSP server. - |:LspSymbolSearch| + *:LspSymbolSearch* :LspSymbolSearch Perform a workspace wide search for the symbol . A popup window is opened with the list of matching symbols. You can enter a few characters to narrow @@ -325,31 +325,45 @@ accepts a list of LSP servers with the above information. You can enter a new search pattern to do a workspace wide symbol search. - |:LspSelectionRange| + *:LspHover* +:LspHover Show the documentation for the symbol under the cursor + in a popup window. + + *:LspSelectionRange* :LspSelectionRange Visually select the current symbol range. - |:LspFold| + *:LspFold* :LspFold Fold the current file. - |:LspWorkspaceAddFolder| + *:LspWorkspaceAddFolder* :LspWorkspaceAddFolder {folder} Add a folder to the workspace - |:LspWorkspaceRemoveFolder| + *:LspWorkspaceRemoveFolder* :LspWorkspaceRemoveFolder {folder} Remove a folder from the workspace - |:LspWorkspaceListFolders| + *:LspWorkspaceListFolders* :LspWorkspaceListFolders Show the list of folders in the workspace. ============================================================================== 6. Insert mode completion -The LSP plugin sets the 'omnifunc' option for the buffers which have a -registered LSP server. To complete a symbol in insert mode, you can press -CTRL-X CTRL-O to invoke completion using the items suggested by the LSP -server. +By default, in insert mode, the LSP plugin automatically displays the matches +for the symbol under the cursor in an insert-completion popup menu. You can +use the keys described in |popupmenu-keys| with this menu. + +To disable the auto-compeltion, you can set the LSP_24x7_Complete variable +to v:false in your .vimrc file: > + + let g:LSP_24x7_Complete=v:false +< +If this variable is set, then the LSP plugin doesn't automatically start +completion in insert mode and instead supports omni-completion (|compl-omni|). +It sets the 'omnifunc' option for the buffers which have a registered LSP +server. To complete a symbol in insert mode manually, you can press CTRL-X +CTRL-O to invoke completion using the items suggested by the LSP server. ============================================================================== diff --git a/plugin/lsp.vim b/plugin/lsp.vim index d1af460..dbfd632 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -5,6 +5,12 @@ if v:version < 802 || !has('patch-8.2.2342') finish endif +" Perform completion in insert mode automatically. Otherwise use +" omni-complete. +if !exists('g:LSP_24x7_Complete') + let g:LSP_24x7_Complete = v:true +endif + augroup LSPAutoCmds au! autocmd BufNewFile,BufReadPost * @@ -40,8 +46,11 @@ command! -nargs=0 -bar LspCalling call lsp#outgoingCalls() command! -nargs=0 -bar LspRename call lsp#rename() command! -nargs=0 -bar LspCodeAction call lsp#codeAction() command! -nargs=? -bar LspSymbolSearch call lsp#symbolSearch() +command! -nargs=0 -bar LspHover call lsp#hover() command! -nargs=0 -bar LspSelectionRange call lsp#selectionRange() command! -nargs=0 -bar LspFold call lsp#foldDocument() command! -nargs=0 -bar LspWorkspaceListFolders call lsp#listWorkspaceFolders() command! -nargs=1 -bar -complete=dir LspWorkspaceAddFolder call lsp#addWorkspaceFolder() command! -nargs=1 -bar -complete=dir LspWorkspaceRemoveFolder call lsp#removeWorkspaceFolder() + +" vim: shiftwidth=2 softtabstop=2 -- 2.48.1