# process the 'textDocument/hover' reply from the LSP server
# Result: Hover | null
-export def HoverReply(lspserver: dict<any>, _: any, reply: dict<any>): void
- if !util.SanitizeReply('textDocument/hover', reply)
+export def HoverReply(lspserver: dict<any>, hoverResult: any): void
+ if hoverResult->empty()
return
endif
var hoverText: list<string>
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
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
# 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
# LSP server standard output handler
def Output_cb(lspserver: dict<any>, 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
completionItem: {
documentationFormat: ['plaintext', 'markdown'],
resolveSupport: {properties: ['detail', 'documentation']},
- snippetSupport: true
+ snippetSupport: false
},
completionItemKind: {valueSet: range(1, 25)}
},
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
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
return {}
enddef
+# LSP server asynchronous RPC callback
+def AsyncRpcCb(lspserver: dict<any>, method: string, RpcCb: func, chan: channel, reply: dict<any>)
+ 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<any>, method: string, params: any, Cbfunc: func): number
var req = {}
req.method = method
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<any>
+ 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
# 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<any>, bnr: number): 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"
# process the 'textDocument/signatureHelp' reply from the LSP server and
# display the symbol signature help.
# Result: SignatureHelp | null
-export def SignatureHelp(lspserver: dict<any>, _: any, reply: dict<any>): void
- if !util.SanitizeReply('textDocument/signatureHelp', reply)
- return
- endif
-
- var sighelp: dict<any> = reply.result
+export def SignatureHelp(lspserver: dict<any>, sighelp: any): void
if sighelp->empty()
CloseSignaturePopup(lspserver)
return
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
}]}, 't')
enddef
-export def SanitizeReply(reqmsg: string, reply: dict<any>): 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
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:
options.OptionsSet(opts)
enddef
-def g:LspServerTraceEnable()
- lsp.EnableServerTrace()
-enddef
-
def g:LspAddServer(serverList: list<dict<any>>)
lsp.AddServer(serverList)
enddef
endif
enddef
+# Command line completion function for the LspSetTrace command.
+def LspServerDebugComplete(arglead: string, cmdline: string, cursorpos: number): list<string>
+ 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('<abuf>')->str2nr())
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(<q-args>)
+command! -nargs=1 -complete=customlist,LspServerTraceComplete -bar LspServerTrace lsp.ServerTraceSet(<q-args>)
+command! -nargs=1 -complete=customlist,LspServerDebugComplete -bar LspServerDebug lsp.ServerDebug(<q-args>)
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)