]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Use server specific log and error files. Add optional name and debug options to...
authorYegappan Lakshmanan <yegappan@yahoo.com>
Tue, 4 Apr 2023 02:49:56 +0000 (19:49 -0700)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Tue, 4 Apr 2023 02:49:56 +0000 (19:49 -0700)
README.md
autoload/lsp/handlers.vim
autoload/lsp/hover.vim
autoload/lsp/lsp.vim
autoload/lsp/lspserver.vim
autoload/lsp/util.vim
doc/lsp.txt

index d5d428a7d61bb490bffcff151abb45031403970b..ebb1726ea8856a464ef7f541f4dc0b2f1b3a8130 100644 (file)
--- a/README.md
+++ b/README.md
@@ -54,11 +54,13 @@ function! TypeScriptCustomNotificationHandler(lspserver, reply) abort
 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'],
@@ -67,38 +69,45 @@ let lspServers = [
        \        }
        \      },
        \     #{
+       \        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'],
@@ -129,6 +138,7 @@ If you used [vim-plug](https://github.com/junegunn/vim-plug) to install the LSP
 ```viml
 let lspServers = [
        \     #{
+       \        name: 'clang',
        \        filetype: ['c', 'cpp'],
        \        path: '/usr/local/bin/clangd',
        \        args: ['--background-index']
index cbeab062512cd0a3e13477bfd591d190a1e734db..c2479e8e3fce1e74cda050cdcf57697f2c4c9805 100644 (file)
@@ -50,7 +50,7 @@ def ProcessLogMsgNotif(lspserver: dict<any>, reply: dict<any>)
     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
index b5dc50f72c3ba7f08c01f3b6a9f83b7077098fb2..c90a4b34ebb4e10d455159f7f341ed7900f8193c 100644 (file)
@@ -6,7 +6,7 @@ import './util.vim'
 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
@@ -22,8 +22,7 @@ def GetHoverText(hoverResult: any): list<any>
       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 ['', '']
@@ -65,8 +64,7 @@ def GetHoverText(hoverResult: any): list<any>
     return [hoverText, 'lspgfm']
   endif
 
-  util.TraceLog(
-    true,
+  lspserver.errorLog(
     $'{strftime("%m/%d/%y %T")}: Unsupported hover reply ({hoverResult})'
   )
   return ['', '']
@@ -75,7 +73,7 @@ enddef
 # 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()
index a9ea16ae37b70879558d31c87616b08a8e353416..65accd3c25e8b5bbbc46dc3e99af9091092a070b 100644 (file)
@@ -60,15 +60,21 @@ export def ServerDebug(arg: string)
     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
 
@@ -471,8 +477,6 @@ export def AddServer(serverList: list<dict<any>>)
         return
       endif
       args = server.args
-    else
-
     endif
 
     var initializationOptions: dict<any> = {}
@@ -494,18 +498,27 @@ export def AddServer(serverList: list<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
index b93d8853639067a8d2ba001bab0fd4f974643080..2a64da1b7f87ea27db08b2e9ce667bafad96db74 100644 (file)
@@ -26,14 +26,14 @@ import './inlayhints.vim'
 
 # 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
@@ -222,6 +222,20 @@ def SetTrace(lspserver: dict<any>, traceVal: string)
   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
@@ -290,7 +304,7 @@ def SendMessage(lspserver: dict<any>, content: dict<any>): void
   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
 
@@ -315,12 +329,12 @@ def Rpc(lspserver: dict<any>, method: string, params: any, handleError: bool = t
     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
@@ -342,7 +356,7 @@ enddef
 
 # 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
@@ -382,7 +396,7 @@ def AsyncRpc(lspserver: dict<any>, method: string, params: any, Cbfunc: func): n
     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])
@@ -1336,9 +1350,13 @@ def TagFunc(lspserver: dict<any>, pat: string, flags: string, info: dict<any>):
   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,
@@ -1361,8 +1379,12 @@ export def NewLspServer(path: string, args: list<string>, isSync: bool, initiali
     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]),
@@ -1371,6 +1393,8 @@ export def NewLspServer(path: string, args: list<string>, isSync: bool, initiali
     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]),
index d2c9e606cc3ce9ab6b7c5c7d61521d37cd441a7d..a51718bb846d39b276ee7636bebb1ce82c1120aa 100644 (file)
@@ -21,55 +21,39 @@ if has('unix')
 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.
index bef44bb80deec57005f807cf7115dac35793d7b4..2869ded45632f38fda8fea374d4a73cb1e18ff81 100644 (file)
@@ -145,7 +145,7 @@ The following commands are provided:
 
 ==============================================================================
 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).
@@ -160,11 +160,13 @@ LSP plugin, the steps are described later in this section: >
 
    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']
@@ -181,33 +183,39 @@ Shell script, Vim script and PHP file types: >
 
    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'],
@@ -221,6 +229,9 @@ Shell script, Vim script and PHP file types: >
 <
 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).
@@ -239,10 +250,16 @@ To add a language server, the following information is needed:
                        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
@@ -702,10 +719,11 @@ can map these commands to keys and make it easier to invoke them.
                                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
@@ -1090,22 +1108,25 @@ or >
 
 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: >
@@ -1129,7 +1150,7 @@ language server documentation for information about this.  You can include
 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 }
 <