From: Roberto Castagnola Date: Sun, 23 Oct 2022 00:48:45 +0000 (+0200) Subject: Lazy documentation for selected completion item X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=e735efdaa836e6efcb30a6cfbfde053894334344;p=vim-lsp.git Lazy documentation for selected completion item --- diff --git a/autoload/lsp/handlers.vim b/autoload/lsp/handlers.vim index 73838fd..49637fd 100644 --- a/autoload/lsp/handlers.vim +++ b/autoload/lsp/handlers.vim @@ -27,6 +27,8 @@ def ProcessInitializeReply(lspserver: dict, req: dict, reply: dicthas_key('completionProvider') var triggers = caps.completionProvider.triggerCharacters lspserver.completionTriggerChars = triggers + lspserver.completionLazyDoc = lspserver.caps.completionProvider->has_key('resolveProvider') + && lspserver.caps.completionProvider.resolveProvider endif # send a "initialized" notification to server @@ -112,20 +114,25 @@ def ProcessCompletionReply(lspserver: dict, req: dict, reply: dicthas_key('kind') # namespace CompletionItemKind # map LSP kind to complete-item-kind d.kind = LspCompleteItemKindChar(item.kind) endif - if item->has_key('detail') - d.menu = item.detail - endif - if item->has_key('documentation') - if item.documentation->type() == v:t_string && item.documentation != '' - d.info = item.documentation - elseif item.documentation->type() == v:t_dict - && item.documentation.value->type() == v:t_string - d.info = item.documentation.value + if lspserver.completionLazyDoc + d.info = 'Lazy doc' + else + if item->has_key('detail') + d.menu = item.detail + endif + if item->has_key('documentation') + if item.documentation->type() == v:t_string && item.documentation != '' + d.info = item.documentation + elseif item.documentation->type() == v:t_dict + && item.documentation.value->type() == v:t_string + d.info = item.documentation.value + endif endif endif d.user_data = item @@ -182,6 +189,80 @@ def ProcessCompletionReply(lspserver: dict, req: dict, reply: dict, req: dict, reply: dict): void + if reply.result->empty() + return + endif + + # check if completion item is still selected + var cInfo = complete_info() + if cInfo->empty() + || !cInfo.pum_visible + || cInfo.selected == -1 + || cInfo.items[cInfo.selected].user_data.label != reply.result.label + return + endif + + var infoText: list + var infoKind: string + + if reply.result->has_key('detail') + infoText->extend([reply.result.detail]) + endif + + if reply.result->has_key('documentation') + if !infoText->empty() + infoText->extend(['- - -']) + endif + if reply.result.documentation->type() == v:t_dict + # MarkupContent + if reply.result.documentation.kind == 'plaintext' + infoText->extend(reply.result.documentation.value->split("\n")) + infoKind = 'text' + elseif reply.result.documentation.kind == 'markdown' + infoText->extend(reply.result.documentation.value->split("\n")) + infoKind = 'markdown' + else + util.ErrMsg($'Error: Unsupported documentation type ({reply.result.documentation.kind})') + return + endif + elseif reply.result.documentation->type() == v:t_string + infoText->extend(reply.result.documentation->split("\n")) + else + util.ErrMsg($'Error: Unsupported documentation ({reply.result.documentation})') + return + endif + endif + + if infoText->empty() + return + endif + + # check if completion item is changed in meantime + cInfo = complete_info() + if cInfo->empty() + || !cInfo.pum_visible + || cInfo.selected == -1 + || cInfo.items[cInfo.selected].user_data.label != reply.result.label + return + endif + + var id = popup_findinfo() + if id > 0 + var bufnr = id->winbufnr() + infoKind->setbufvar(bufnr, '&ft') + if infoKind == 'markdown' + 3->setwinvar(id, '&cole') + else + 0->setwinvar(id, '&cole') + endif + id->popup_settext(infoText) + id->popup_show() + endif +enddef + # process the 'textDocument/hover' reply from the LSP server # Result: Hover | null def ProcessHoverReply(lspserver: dict, req: dict, reply: dict): void @@ -425,6 +506,7 @@ export def ProcessReply(lspserver: dict, req: dict, reply: dict): 'initialize': ProcessInitializeReply, 'textDocument/signatureHelp': ProcessSignaturehelpReply, 'textDocument/completion': ProcessCompletionReply, + 'completionItem/resolve': ProcessResolveReply, 'textDocument/hover': ProcessHoverReply, 'textDocument/documentHighlight': ProcessDocHighlightReply, 'textDocument/documentSymbol': ProcessDocSymbolReply, diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index 80dc971..4a0025a 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -254,8 +254,13 @@ def BufferInit(bnr: number): void # set options for insert mode completion if opt.lspOptions.autoComplete - setbufvar(bnr, '&completeopt', 'menuone,popup,noinsert,noselect') - setbufvar(bnr, '&completepopup', 'border:off') + if lspserver.completionLazyDoc + setbufvar(bnr, '&completeopt', 'menuone,popuphidden,noinsert,noselect') + setbufvar(bnr, '&completepopup', 'width:80,highlight:Pmenu,align:menu,border:off') + else + setbufvar(bnr, '&completeopt', 'menuone,popup,noinsert,noselect') + setbufvar(bnr, '&completepopup', 'border:off') + endif # in insert mode stops completion and inserts a if !opt.lspOptions.noNewlineInCompletion inoremap pumvisible() ? "\\" : "\" @@ -279,6 +284,9 @@ def BufferInit(bnr: number): void if opt.lspOptions.autoComplete # Trigger 24x7 insert mode completion when text is changed exe $'autocmd TextChangedI call LspComplete()' + if lspserver.completionLazyDoc + exe $'autocmd CompleteChanged call LspResolve()' + endif endif # Update the diagnostics when insert mode is stopped @@ -590,6 +598,19 @@ def g:LspComplete() return enddef +# Lazy complete documentation handler +def g:LspResolve() + var lspserver: dict = CurbufGetServerChecked() + if lspserver->empty() + return + endif + + var item = v:event.completed_item + if item->has_key('user_data') + lspserver.resolveCompletion(item.user_data) + endif +enddef + # omni complete handler def g:LspOmniFunc(findstart: number, base: string): any var lspserver: dict = CurbufGetServerChecked() diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index c420471..61248fc 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -56,6 +56,7 @@ def StartServer(lspserver: dict): number lspserver.nextID = 1 lspserver.requests = {} lspserver.omniCompletePending = false + lspserver.completionLazyDoc = false lspserver.completionTriggerChars = [] lspserver.signaturePopup = -1 lspserver.workspaceFolders = [getcwd()] @@ -94,6 +95,7 @@ def InitServer(lspserver: dict) completion: { completionItem: { documentationFormat: ['plaintext', 'markdown'], + resolveSupport: {properties: ['detail', 'documentation']}, snippetSupport: false }, completionItemKind: {valueSet: range(1, 25)} @@ -445,6 +447,30 @@ def GetCompletion(lspserver: dict, triggerKind_arg: number, triggerChar: st endif enddef +# Get lazy properties for a completion item. +# Request: "completionItem/resolve" +# Param: CompletionItem +def ResolveCompletion(lspserver: dict, item: dict): void + # Check whether LSP server supports completion item resolve + if !lspserver.caps->has_key('completionProvider') + || !lspserver.caps.completionProvider->has_key('resolveProvider') + || !lspserver.caps.completionProvider.resolveProvider + util.ErrMsg("Error: LSP server does not support completion item resolve") + return + endif + + var req = lspserver.createRequest('completionItem/resolve') + + # interface CompletionItem + req.params = item + + lspserver.sendMessage(req) + if exists('g:LSPTest') && g:LSPTest + # When running LSP tests, make this a synchronous call + lspserver.waitForResponse(req) + endif +enddef + # Jump to or peek a symbol location. # # Send 'msg' to a LSP server and process the reply. 'msg' is one of the @@ -1136,6 +1162,7 @@ export def NewLspServer(path: string, args: list, isSync: bool, initiali textdocDidChange: function(TextdocDidChange, [lspserver]), sendInitializedNotif: function(SendInitializedNotif, [lspserver]), getCompletion: function(GetCompletion, [lspserver]), + resolveCompletion: function(ResolveCompletion, [lspserver]), gotoDefinition: function(GotoDefinition, [lspserver]), switchSourceHeader: function(SwitchSourceHeader, [lspserver]), gotoDeclaration: function(GotoDeclaration, [lspserver]),