From a54fa00a4e13ede6c19b03ecfe1a988f415ed094 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Fri, 28 Oct 2022 08:29:11 -0700 Subject: [PATCH] Snipper support is set to true for completion matches (by mistake). Include plugin debug information in the help text. Add debug messages for async RPC calls. Cleanup async RPC APIs --- autoload/lsp/hover.vim | 36 ++++++++++---------- autoload/lsp/lsp.vim | 18 +++++++--- autoload/lsp/lspserver.vim | 69 +++++++++++++++++++++++++++----------- autoload/lsp/signature.vim | 7 +--- autoload/lsp/util.vim | 27 ++------------- doc/lsp.txt | 32 ++++++++++++++++++ plugin/lsp.vim | 17 +++++++--- 7 files changed, 128 insertions(+), 78 deletions(-) diff --git a/autoload/lsp/hover.vim b/autoload/lsp/hover.vim index ab6ab8d..7119aab 100644 --- a/autoload/lsp/hover.vim +++ b/autoload/lsp/hover.vim @@ -7,50 +7,50 @@ import './options.vim' as opt # process the 'textDocument/hover' reply from the LSP server # Result: Hover | null -export def HoverReply(lspserver: dict, _: any, reply: dict): void - if !util.SanitizeReply('textDocument/hover', reply) +export def HoverReply(lspserver: dict, hoverResult: any): void + if hoverResult->empty() return endif var hoverText: list var hoverKind: string - if reply.result.contents->type() == v:t_dict - if reply.result.contents->has_key('kind') + if hoverResult.contents->type() == v:t_dict + if hoverResult.contents->has_key('kind') # MarkupContent - if reply.result.contents.kind == 'plaintext' - hoverText = reply.result.contents.value->split("\n") + if hoverResult.contents.kind == 'plaintext' + hoverText = hoverResult.contents.value->split("\n") hoverKind = 'text' - elseif reply.result.contents.kind == 'markdown' - hoverText = reply.result.contents.value->split("\n") + elseif hoverResult.contents.kind == 'markdown' + hoverText = hoverResult.contents.value->split("\n") hoverKind = 'markdown' else - util.ErrMsg($'Error: Unsupported hover contents type ({reply.result.contents.kind})') + util.ErrMsg($'Error: Unsupported hover contents type ({hoverResult.contents.kind})') return endif - elseif reply.result.contents->has_key('value') + elseif hoverResult.contents->has_key('value') # MarkedString - hoverText = reply.result.contents.value->split("\n") + hoverText = hoverResult.contents.value->split("\n") else - util.ErrMsg($'Error: Unsupported hover contents ({reply.result.contents})') + util.ErrMsg($'Error: Unsupported hover contents ({hoverResult.contents})') return endif - elseif reply.result.contents->type() == v:t_list + elseif hoverResult.contents->type() == v:t_list # interface MarkedString[] - for e in reply.result.contents + for e in hoverResult.contents if e->type() == v:t_string hoverText->extend(e->split("\n")) else hoverText->extend(e.value->split("\n")) endif endfor - elseif reply.result.contents->type() == v:t_string - if reply.result.contents->empty() + elseif hoverResult.contents->type() == v:t_string + if hoverResult.contents->empty() return endif - hoverText->extend(reply.result.contents->split("\n")) + hoverText->extend(hoverResult.contents->split("\n")) else - util.ErrMsg($'Error: Unsupported hover contents ({reply.result.contents})') + util.ErrMsg($'Error: Unsupported hover contents ({hoverResult.contents})') return endif diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index 23ae46a..373ff05 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -92,9 +92,19 @@ def LspOmniComplSet(ftype: string, enabled: bool) ftypeOmniCtrlMap->extend({[ftype]: enabled}) enddef -export def EnableServerTrace() - util.ClearTraceLogs() - util.ServerTrace(true) +# Enable/disable the logging of the language server protocol messages +export def ServerDebug(arg: string) + if arg !=? 'on' && arg !=? 'off' + util.ErrMsg($'Error: Invalid argument ("{arg}") for LSP server debug') + return + endif + + if arg ==? 'on' + util.ClearTraceLogs() + util.ServerTrace(true) + else + util.ServerTrace(false) + endif enddef # Show information about all the LSP servers @@ -491,7 +501,7 @@ enddef # set the LSP server trace level for the current buffer # Params: SetTraceParams -export def SetTraceServer(traceVal: string) +export def ServerTraceSet(traceVal: string) if ['off', 'messages', 'verbose']->index(traceVal) == -1 util.ErrMsg($'Error: Unsupported LSP server trace value {traceVal}') return diff --git a/autoload/lsp/lspserver.vim b/autoload/lsp/lspserver.vim index 0f82878..6ef13f6 100644 --- a/autoload/lsp/lspserver.vim +++ b/autoload/lsp/lspserver.vim @@ -17,7 +17,7 @@ import './callhierarchy.vim' as callhier # LSP server standard output handler def Output_cb(lspserver: dict, chan: channel, msg: any): void - util.TraceLog(false, $'Received: {msg->string()}') + util.TraceLog(false, $'Received [{strftime("%m/%d/%y %T")}]: {msg->string()}') lspserver.data = msg lspserver.processMessages() enddef @@ -99,7 +99,7 @@ def InitServer(lspserver: dict) completionItem: { documentationFormat: ['plaintext', 'markdown'], resolveSupport: {properties: ['detail', 'documentation']}, - snippetSupport: true + snippetSupport: false }, completionItemKind: {valueSet: range(1, 25)} }, @@ -265,7 +265,7 @@ def SendMessage(lspserver: dict, content: dict): void endif ch->ch_sendexpr(content) if content->has_key('id') - util.TraceLog(false, $'Sent: {content->string()}') + util.TraceLog(false, $'Sent [{strftime("%m/%d/%y %T")}]: {content->string()}') endif enddef @@ -283,8 +283,13 @@ def Rpc(lspserver: dict, method: string, params: any): dict return {} endif + util.TraceLog(false, $'Sent [{strftime("%m/%d/%y %T")}]: {req->string()}') + + # Do the synchronous RPC call var reply = ch->ch_evalexpr(req) + util.TraceLog(false, $'Received [{strftime("%m/%d/%y %T")}]: {reply->string()}') + if reply->has_key('result') # successful reply return reply @@ -303,7 +308,31 @@ def Rpc(lspserver: dict, method: string, params: any): dict return {} enddef +# LSP server asynchronous RPC callback +def AsyncRpcCb(lspserver: dict, method: string, RpcCb: func, chan: channel, reply: dict) + util.TraceLog(false, $'Received [{strftime("%m/%d/%y %T")}]: {reply->string()}') + + if reply->empty() + return + endif + + if reply->has_key('error') + # request failed + var emsg: string + emsg = $'{reply.error.message}, code = {reply.error.code}' + if reply.error->has_key('data') + emsg ..= $', data = {reply.error.data->string()}' + endif + util.ErrMsg($'Error(LSP): request {method} failed ({emsg})') + return + endif + + RpcCb(lspserver, reply.result) +enddef + # Send a async RPC request message to the LSP server with a callback function. +# Returns the LSP message id. This id can be used to cancel the RPC request +# (if needed). Returns -1 on error. def AsyncRpc(lspserver: dict, method: string, params: any, Cbfunc: func): number var req = {} req.method = method @@ -316,7 +345,20 @@ def AsyncRpc(lspserver: dict, method: string, params: any, Cbfunc: func): n return -1 endif - var reply = ch->ch_sendexpr(req, {callback: Cbfunc}) + util.TraceLog(false, $'Sent [{strftime("%m/%d/%y %T")}]: {req->string()}') + + # Do the asynchronous RPC call + var Fn = function('AsyncRpcCb', [lspserver, method, Cbfunc]) + + var reply: dict + if get(g:, 'LSPTest') + # When running LSP tests, make this a synchronous RPC call + reply = Rpc(lspserver, method, params) + Fn(test_null_channel(), reply) + else + # Otherwise, make an asynchronous RPC call + reply = ch->ch_sendexpr(req, {callback: Fn}) + endif if reply->empty() return -1 endif @@ -643,14 +685,8 @@ def ShowSignature(lspserver: dict): void # interface SignatureHelpParams # interface TextDocumentPositionParams var params = GetLspTextDocPosition() - if get(g:, 'LSPTest') - # When running LSP tests, make this a synchronous call - var reply = lspserver.rpc('textDocument/signatureHelp', params) - signature.SignatureHelp(lspserver, 0, reply) - else - lspserver.rpc_a('textDocument/signatureHelp', params, - function(signature.SignatureHelp, [lspserver])) - endif + lspserver.rpc_a('textDocument/signatureHelp', params, + signature.SignatureHelp) enddef def DidSaveFile(lspserver: dict, bnr: number): void @@ -682,14 +718,7 @@ def ShowHoverInfo(lspserver: dict): void # interface HoverParams # interface TextDocumentPositionParams var params = GetLspTextDocPosition() - if get(g:, 'LSPTest') - # When running LSP tests, make this a synchronous call - var reply = lspserver.rpc('textDocument/hover', params) - hover.HoverReply(lspserver, 0, reply) - else - lspserver.rpc_a('textDocument/hover', params, - function(hover.HoverReply, [lspserver])) - endif + lspserver.rpc_a('textDocument/hover', params, hover.HoverReply) enddef # Request: "textDocument/references" diff --git a/autoload/lsp/signature.vim b/autoload/lsp/signature.vim index 9acecb8..c5c0c84 100644 --- a/autoload/lsp/signature.vim +++ b/autoload/lsp/signature.vim @@ -41,12 +41,7 @@ enddef # process the 'textDocument/signatureHelp' reply from the LSP server and # display the symbol signature help. # Result: SignatureHelp | null -export def SignatureHelp(lspserver: dict, _: any, reply: dict): void - if !util.SanitizeReply('textDocument/signatureHelp', reply) - return - endif - - var sighelp: dict = reply.result +export def SignatureHelp(lspserver: dict, sighelp: any): void if sighelp->empty() CloseSignaturePopup(lspserver) return diff --git a/autoload/lsp/util.vim b/autoload/lsp/util.vim index a567cbc..25701e3 100644 --- a/autoload/lsp/util.vim +++ b/autoload/lsp/util.vim @@ -35,9 +35,9 @@ export def TraceLog(stderr: bool, msg: string) return endif if stderr - writefile(split(msg, "\n"), $'{lsp_log_dir}lsp_server.err', 'a') + writefile(msg->split("\n"), $'{lsp_log_dir}lsp_server.err', 'a') else - writefile(split(msg, "\n"), $'{lsp_log_dir}lsp_server.out', 'a') + writefile([msg], $'{lsp_log_dir}lsp_server.out', 'a') endif enddef @@ -151,27 +151,4 @@ export def PushCursorToTagStack() }]}, 't') enddef -export def SanitizeReply(reqmsg: string, reply: dict): bool - if reply->empty() - return false - endif - - if reply->has_key('error') - # request failed - var emsg: string - emsg = $'{reply.error.message}, code = {reply.error.code}' - if reply.error->has_key('data') - emsg ..= $', data = {reply.error.data->string()}' - endif - ErrMsg($'Error(LSP): request {reqmsg} failed ({emsg})') - return false - endif - - if reply.result->empty() - return false - endif - - return true -enddef - # vim: tabstop=8 shiftwidth=2 softtabstop=2 diff --git a/doc/lsp.txt b/doc/lsp.txt index c837964..71bbdc6 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -711,4 +711,36 @@ LspDiagsUpdated A |User| autocommand invoked when new server. This is invoked after the LSP client has processed the diagnostics. +============================================================================== +10. Debugging *lsp-debug* + +To debug this plugin, you can log the language server protocol messages sent +and received by the plugin from the language server. The following command +enables the logging of the messages: > + + :LspServerDebug on +< +This command also clears the log files. The following command disables the +logging of the messages: > + + :LspServerDebug off +< +By default, the messages are not logged. + +The messages printed by the LSP server in the stdout are logged to the +lsp_server.out file and the messages printed in the stderr are logged to the +lsp_server.err file. On a Unix-like system, these files are created in the +/tmp directory. On MS-Windows, these files are created in the %TEMP% +directory. + +The language servers typically support command line options to enable debug +messages and to increase the verbosity of the messages. You can refer to the +language server documentation for information about this. You can include +these options when registering the LSP server with this plugin. + +If a language server supports the "$/logTrace" LSP notification, then you can +use the :LspServerTrace command to set the trace value: > + + :LspServerTrace { off | messages | verbose } +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/plugin/lsp.vim b/plugin/lsp.vim index 635328c..c9a15a7 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -14,10 +14,6 @@ def g:LspOptionsSet(opts: dict) options.OptionsSet(opts) enddef -def g:LspServerTraceEnable() - lsp.EnableServerTrace() -enddef - def g:LspAddServer(serverList: list>) lsp.AddServer(serverList) enddef @@ -40,6 +36,16 @@ def LspServerTraceComplete(arglead: string, cmdline: string, cursorpos: number): endif enddef +# Command line completion function for the LspSetTrace command. +def LspServerDebugComplete(arglead: string, cmdline: string, cursorpos: number): list + var l = ['off', 'on'] + if arglead->empty() + return l + else + return filter(l, (_, val) => val =~ arglead) + endif +enddef + augroup LSPAutoCmds au! autocmd BufNewFile,BufReadPost * lsp.AddFile(expand('')->str2nr()) @@ -59,7 +65,8 @@ augroup END command! -nargs=0 -bar LspShowServers lsp.ShowServers() command! -nargs=0 -bar LspShowServerCapabilities lsp.ShowServerCapabilities() command! -nargs=0 -bar LspServerRestart lsp.RestartServer() -command! -nargs=1 -complete=customlist,LspServerTraceComplete -bar LspSetTrace lsp.SetTraceServer() +command! -nargs=1 -complete=customlist,LspServerTraceComplete -bar LspServerTrace lsp.ServerTraceSet() +command! -nargs=1 -complete=customlist,LspServerDebugComplete -bar LspServerDebug lsp.ServerDebug() command! -nargs=0 -bar LspGotoDefinition lsp.GotoDefinition(v:false) command! -nargs=0 -bar LspGotoDeclaration lsp.GotoDeclaration(v:false) command! -nargs=0 -bar LspGotoTypeDef lsp.GotoTypedef(v:false) -- 2.48.1