endfunction
let lspServers = [
\ #{
+ \ name: 'clangd',
\ filetype: ['c', 'cpp'],
\ path: '/usr/local/bin/clangd',
\ args: ['--background-index']
\ },
\ #{
+ \ name: 'typescriptlang',
\ filetype: ['javascript', 'typescript'],
\ path: '/usr/local/bin/typescript-language-server',
\ args: ['--stdio'],
\ }
\ },
\ #{
+ \ name: 'bashlang',
\ filetype: 'sh',
\ path: '/usr/local/bin/bash-language-server',
\ args: ['start']
\ },
\ #{
+ \ name: 'vimlang',
\ filetype: 'vim',
\ path: '/usr/local/bin/vim-language-server',
\ args: ['--stdio']
\ },
\ #{
+ \ name: 'golang',
\ filetype: ['go', 'gomod'],
\ path: '/usr/local/bin/gopls',
\ args: ['serve'],
\ syncInit: v:true
\ },
\ #{
+ \ name: 'rustlang',
\ filetype: ['rust'],
\ path: '/usr/local/bin/rust-analyzer',
\ args: [],
\ syncInit: v:true
\ },
\ #{
+ \ name: 'pylang',
\ filetype: ['python'],
\ path: '/usr/local/bin/pyls',
\ args: []
\ },
\ #{
+ \ name: 'fortranls',
\ filetype: ['fortran'],
\ path: '/usr/local/bin/fortls',
\ args: ['--nthreads=1', '--use_signature_help', '--hover_signature']
\ },
\ #{
+ \ name: 'phplang',
\ filetype: ['php'],
\ path: '/usr/local/bin/intelephense',
\ args: ['--stdio'],
```viml
let lspServers = [
\ #{
+ \ name: 'clang',
\ filetype: ['c', 'cpp'],
\ path: '/usr/local/bin/clangd',
\ args: ['--background-index']
mtype = msgType[reply.params.type]
endif
- util.TraceLog(false, $'{strftime("%m/%d/%y %T")}: [{mtype}]: {reply.params.message}')
+ lspserver.traceLog($'{strftime("%m/%d/%y %T")}: [{mtype}]: {reply.params.message}')
enddef
# process unsupported notification messages
import './options.vim' as opt
# Util used to compute the hoverText from textDocument/hover reply
-def GetHoverText(hoverResult: any): list<any>
+def GetHoverText(lspserver: dict<any>, hoverResult: any): list<any>
if hoverResult->empty()
return ['', '']
endif
return [hoverResult.contents.value->split("\n"), 'lspgfm']
endif
- util.TraceLog(
- true,
+ lspserver.errorLog(
$'{strftime("%m/%d/%y %T")}: Unsupported hover contents kind ({hoverResult.contents.kind})'
)
return ['', '']
return [hoverText, 'lspgfm']
endif
- util.TraceLog(
- true,
+ lspserver.errorLog(
$'{strftime("%m/%d/%y %T")}: Unsupported hover reply ({hoverResult})'
)
return ['', '']
# process the 'textDocument/hover' reply from the LSP server
# Result: Hover | null
export def HoverReply(lspserver: dict<any>, hoverResult: any): void
- var [ hoverText, hoverKind ] = GetHoverText(hoverResult)
+ var [hoverText, hoverKind] = GetHoverText(lspserver, hoverResult)
# Nothing to show
if hoverText->empty()
return
endif
+ var lspserver: dict<any> = buf.CurbufGetServer()
+ if lspserver->empty()
+ return
+ endif
+
if arg ==? 'on'
- util.ClearTraceLogs()
- util.ServerTrace(true)
+ util.ClearTraceLogs(lspserver.logfile)
+ util.ClearTraceLogs(lspserver.errfile)
+ lspserver.debug = true
elseif arg ==? 'off'
- util.ServerTrace(false)
+ lspserver.debug = false
elseif arg ==? 'messages'
- util.ServerMessagesShow(false)
+ util.ServerMessagesShow(lspserver.logfile)
else
- util.ServerMessagesShow(true)
+ util.ServerMessagesShow(lspserver.errfile)
endif
enddef
return
endif
args = server.args
- else
-
endif
var initializationOptions: dict<any> = {}
server.syncInit = v:false
endif
- var lspserver: dict<any> = lserver.NewLspServer(server.path,
- args,
- server.syncInit,
+ if !server->has_key('name') || server.name->type() != v:t_string
+ || server.name == ''
+ # Use the executable name (without the extension) as the language server
+ # name.
+ server.name = server.path->fnamemodify(':t:r')
+ endif
+
+ if !server->has_key('debug') || server.debug->type() != v:t_bool
+ server.debug = false
+ endif
+
+ var lspserver: dict<any> = lserver.NewLspServer(server.name, server.path,
+ args, server.syncInit,
initializationOptions,
- customNotificationHandlers)
+ customNotificationHandlers,
+ server.debug)
var ftypes = server.filetype
if ftypes->type() == v:t_string
- lspserver.name = ftypes->substitute('\w\+', '\L\u\0', '')
AddServerForFiltype(lspserver, ftypes, server.omnicompl)
elseif ftypes->type() == v:t_list
- lspserver.name = ftypes[0]->substitute('\w\+', '\L\u\0', '')
for ftype in ftypes
AddServerForFiltype(lspserver, ftype, server.omnicompl)
endfor
# LSP server standard output handler
def Output_cb(lspserver: dict<any>, chan: channel, msg: any): void
- util.TraceLog(false, $'{strftime("%m/%d/%y %T")}: Received {msg->string()}')
+ lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Received {msg->string()}')
lspserver.data = msg
lspserver.processMessages()
enddef
# LSP server error output handler
-def Error_cb(lspserver: dict<any>, chan: channel, emsg: string,): void
- util.TraceLog(true, emsg)
+def Error_cb(lspserver: dict<any>, chan: channel, emsg: string): void
+ lspserver.errorLog(emsg)
enddef
# LSP server exit callback
lspserver.sendNotification('$/setTrace', params)
enddef
+# Log a debug message to the LSP server debug file
+def TraceLog(lspserver: dict<any>, msg: string)
+ if lspserver.debug
+ util.TraceLog(lspserver.logfile, false, msg)
+ endif
+enddef
+
+# Log an error message to the LSP server error file
+def ErrorLog(lspserver: dict<any>, errmsg: string)
+ if lspserver.debug
+ util.TraceLog(lspserver.errfile, true, errmsg)
+ endif
+enddef
+
# Return the next id for a LSP server request message
def NextReqID(lspserver: dict<any>): number
var id = lspserver.nextID
endif
job->ch_sendexpr(content)
if content->has_key('id')
- util.TraceLog(false, $'{strftime("%m/%d/%y %T")}: Sent {content->string()}')
+ lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Sent {content->string()}')
endif
enddef
return {}
endif
- util.TraceLog(false, $'{strftime("%m/%d/%y %T")}: Sent {req->string()}')
+ lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Sent {req->string()}')
# Do the synchronous RPC call
var reply = job->ch_evalexpr(req)
- util.TraceLog(false, $'{strftime("%m/%d/%y %T")}: Received {reply->string()}')
+ lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Received {reply->string()}')
if reply->has_key('result')
# successful reply
# LSP server asynchronous RPC callback
def AsyncRpcCb(lspserver: dict<any>, method: string, RpcCb: func, chan: channel, reply: dict<any>)
- util.TraceLog(false, $'{strftime("%m/%d/%y %T")}: Received {reply->string()}')
+ lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Received {reply->string()}')
if reply->empty()
return
return -1
endif
- util.TraceLog(false, $'{strftime("%m/%d/%y %T")}: Sent {req->string()}')
+ lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Sent {req->string()}')
# Do the asynchronous RPC call
var Fn = function('AsyncRpcCb', [lspserver, method, Cbfunc])
return symbol.TagFunc(lspserver, taglocations, pat)
enddef
-export def NewLspServer(path: string, args: list<string>, isSync: bool, initializationOptions: any, customNotificationHandlers: dict<func>): dict<any>
+export def NewLspServer(name_arg: string, path_arg: string, args: list<string>,
+ isSync: bool, initializationOptions: any,
+ customNotificationHandlers: dict<func>,
+ debug_arg: bool): dict<any>
var lspserver: dict<any> = {
- path: path,
+ name: name_arg,
+ path: path_arg,
args: args,
syncInit: isSync,
initializationOptions: initializationOptions,
peekSymbolPopup: -1,
peekSymbolFilePopup: -1,
callHierarchyType: '',
- selection: {}
+ selection: {},
+ debug: debug_arg
}
+ lspserver.logfile = $'lsp-{lspserver.name}.log'
+ lspserver.errfile = $'lsp-{lspserver.name}.err'
+
# Add the LSP server functions
lspserver->extend({
startServer: function(StartServer, [lspserver]),
shutdownServer: function(ShutdownServer, [lspserver]),
exitServer: function(ExitServer, [lspserver]),
setTrace: function(SetTrace, [lspserver]),
+ traceLog: function(TraceLog, [lspserver]),
+ errorLog: function(ErrorLog, [lspserver]),
nextReqID: function(NextReqID, [lspserver]),
createRequest: function(CreateRequest, [lspserver]),
createResponse: function(CreateResponse, [lspserver]),
else
lsp_log_dir = $TEMP .. '\\'
endif
-var lsp_server_trace: bool = false
-
-# Enable or disable LSP server trace messages
-export def ServerTrace(trace_enable: bool)
- lsp_server_trace = trace_enable
-enddef
# Log a message from the LSP server. stderr is true for logging messages
# from the standard error and false for stdout.
-export def TraceLog(stderr: bool, msg: string)
- if !lsp_server_trace
- return
- endif
+export def TraceLog(fname: string, stderr: bool, msg: string)
if stderr
- writefile(msg->split("\n"), $'{lsp_log_dir}lsp-server.err', 'a')
+ writefile(msg->split("\n"), $'{lsp_log_dir}{fname}', 'a')
else
- writefile([msg], $'{lsp_log_dir}lsp-server.out', 'a')
+ writefile([msg], $'{lsp_log_dir}{fname}', 'a')
endif
enddef
# Empty out the LSP server trace logs
-export def ClearTraceLogs()
- if !lsp_server_trace
- return
- endif
- writefile([], $'{lsp_log_dir}lsp-server.out')
- writefile([], $'{lsp_log_dir}lsp-server.err')
+export def ClearTraceLogs(fname: string)
+ writefile([], fname)
enddef
-# Open the LSP server debug messages file. If errors is true, then open the
-# error messages file.
-export def ServerMessagesShow(errors: bool = false)
- var fname: string
- if errors
- fname = $'{lsp_log_dir}lsp-server.err'
- else
- fname = $'{lsp_log_dir}lsp-server.out'
+# Open the LSP server debug messages file.
+export def ServerMessagesShow(fname: string)
+ var fullname = $'{lsp_log_dir}{fname}'
+ if !filereadable(fullname)
+ WarnMsg($'File {fullname} is not found')
+ return
endif
- if filereadable(fname)
- var wid = fname->bufwinid()
- if wid == -1
- exe $'split {fname}'
- else
- win_gotoid(wid)
- endif
- setlocal autoread
- setlocal nomodified
- setlocal nomodifiable
+ var wid = fullname->bufwinid()
+ if wid == -1
+ exe $'split {fullname}'
+ else
+ win_gotoid(wid)
endif
+ setlocal autoread
+ setlocal bufhidden=wipe
+ setlocal nomodified
+ setlocal nomodifiable
enddef
# Parse a LSP Location or LocationLink type and return a List with two items.
==============================================================================
4. Configuration *lsp-configuration*
- *LspAddServer* *g:LspAddServer*
+ *LspAddServer()* *g:LspAddServer()*
To use the plugin features with a particular file type(s), you need to first
register a language server for that file type(s).
let lspServers = [
\ #{
+ \ name: 'typescriptls',
\ filetype: ['javascript', 'typescript'],
\ path: '/usr/local/bin/typescript-language-server',
\ args: ['--stdio']
\ },
\ #{
+ \ name: 'pythonls',
\ filetype: 'python',
\ path: '/usr/local/bin/pyls',
\ args: ['--check-parent-process', '-v']
let lspServers = [
\ #{
+ \ name: 'clangd',
\ filetype: ['c', 'cpp'],
\ path: '/usr/local/bin/clangd',
\ args: ['--background-index']
\ },
\ #{
+ \ name: 'golang',
\ filetype: ['go', 'gomod', 'gohtmltmpl', 'gotexttmpl'],
\ path: '/path/to/.go/bin/gopls',
\ args: [],
\ syncInit: v:true,
\ },
\ #{
+ \ name: 'rustls',
\ filetype: ['rust'],
\ path: '/path/to/.cargo/bin/rust-analyzer',
\ args: [],
\ syncInit: v:true,
\ },
\ #{
+ \ name: 'bashls',
\ filetype: 'sh',
\ path: '/usr/local/bin/bash-language-server',
\ args: ['start']
\ },
\ #{
+ \ name: 'vimls',
\ filetype: ['vim'],
\ path: '/usr/local/bin/vim-language-server',
\ args: ['--stdio']
\ },
\ #{
+ \ name: 'phpls',
\ filetype: ['php'],
\ path: '/usr/local/bin/intelephense',
\ args: ['--stdio'],
<
To add a language server, the following information is needed:
+ *lsp-cfg-name*
+ name (Optional) name of the language server. Can by any
+ string. Used in LSP messages and log files.
*lsp-cfg-path*
path complete path to the language server executable
(without any arguments).
or useful for initialization. Those can be provided in
this dictionary and if present will be transmitted to
the lsp server.
+ *lsp-cfg-debug*
+ debug (Optional) log the messages printed by this language
+ server in stdout and stderr to a file. Useful for
+ debugging a language server. By default the
+ messages are not logged. See |lsp-debug| for more
+ information.
Aditionally the following configurations can be made:
- *lsp-cfg-customNotificationHandlers*
+ *lsp-cfg-customNotificationHandlers*
customNotificationHandlers
(Optional) some lsp servers (e.g.
typescript-language-server) will send additional
and standard error.
By default, the language server messages are not
logged. On a Unix-like system, when enabled, these
- messages are logged to the /tmp/lsp-server.out and
- /tmp/lsp-server.err file respectively. On MS-Windows,
- the %TEMP%/lsp-server.out and %TEMP%/lsp-server.err%
- files are used. See |lsp-debug| for more information.
+ messages are logged to the /tmp/lsp-<server-name>.log
+ and /tmp/lsp-<server-name>.err file respectively. On
+ MS-Windows, the %TEMP%/lsp-<server-name>.log and
+ %TEMP%/lsp-<server-name>.err% files are used. See
+ |lsp-debug| for more information.
*:LspServerRestart*
:LspServerRestart Restart (stop and then start) the language server for
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: >
+enables the logging of the messages from the language server for the current
+buffer: >
:LspServerDebug on
<
This command also clears the log files. The following command disables the
-logging of the messages: >
+logging of the messages from the language server for the current buffer: >
:LspServerDebug off
<
-By default, the messages are not logged.
+By default, the messages are not logged. Another method to enable the debug
+is to set the "debug" field to v:true when adding a language server
+using |LspAddServer()|.
The messages printed by the language 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.
+lsp-<server-name>.log file and the messages printed in the stderr are logged
+to the lsp-<server-name>.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 following command opens the file containing the messages printed by the
language server in the stdout: >
these options when registering the language 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: >
+use the :LspServerTrace command to set the server trace value: >
:LspServerTrace { off | messages | verbose }
<