]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Support for detecting the workspace root directory
authorYegappan Lakshmanan <yegappan@yahoo.com>
Fri, 7 Apr 2023 21:40:08 +0000 (14:40 -0700)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Fri, 7 Apr 2023 21:40:08 +0000 (14:40 -0700)
autoload/lsp/lsp.vim
autoload/lsp/lspserver.vim
autoload/lsp/util.vim
doc/lsp.txt

index f4f6f013394d74a23d4aaf24fbae9fe7d6907309..020c07a3e3487a0de0f5dc315d2867a85b4025a8 100644 (file)
@@ -371,7 +371,7 @@ export def AddFile(bnr: number): void
     if !lspInitializedOnce
       LspInitOnce()
     endif
-    lspserver.startServer()
+    lspserver.startServer(bnr)
   endif
   buf.BufLspServerSet(bnr, lspserver)
 
@@ -438,7 +438,7 @@ export def RestartServer()
   endfor
 
   # Start the server again
-  lspserver.startServer()
+  lspserver.startServer(bufnr(''))
 
   AddBuffersToLsp(ftype)
 enddef
@@ -514,10 +514,15 @@ export def AddServer(serverList: list<dict<any>>)
       server.workspaceConfig = {}
     endif
 
+    if !server->has_key('rootSearch') || server.rootSearch->type() != v:t_list
+      server.rootSearch = []
+    endif
+
     var lspserver: dict<any> = lserver.NewLspServer(server.name, server.path,
                                                    args, server.syncInit,
                                                    initializationOptions,
                                                    server.workspaceConfig,
+                                                   server.rootSearch,
                                                    customNotificationHandlers,
                                                    server.debug)
 
index 01df4347f6d211dc6c43d84444ebba1d77612989..dbaacea217bc3e56825bef4c72d42b2724bd4d25 100644 (file)
@@ -46,7 +46,7 @@ enddef
 
 # Start a LSP server
 #
-def StartServer(lspserver: dict<any>): number
+def StartServer(lspserver: dict<any>, bnr: number): number
   if lspserver.running
     util.WarnMsg('LSP server for is already running')
     return 0
@@ -71,7 +71,7 @@ def StartServer(lspserver: dict<any>): number
   lspserver.completionLazyDoc = false
   lspserver.completionTriggerChars = []
   lspserver.signaturePopup = -1
-  lspserver.workspaceFolders = [getcwd()]
+  lspserver.workspaceFolders = [bnr->bufname()->fnamemodify(':p:h')]
 
   var job = cmd->job_start(opts)
   if job->job_status() == 'fail'
@@ -85,7 +85,7 @@ def StartServer(lspserver: dict<any>): number
   lspserver.job = job
   lspserver.running = true
 
-  lspserver.initServer()
+  lspserver.initServer(bnr)
 
   return 0
 enddef
@@ -143,7 +143,7 @@ enddef
 
 # Request: 'initialize'
 # Param: InitializeParams
-def InitServer(lspserver: dict<any>)
+def InitServer(lspserver: dict<any>, bnr: number)
   # interface 'InitializeParams'
   var initparams: dict<any> = {}
   initparams.processId = getpid()
@@ -151,12 +151,23 @@ def InitServer(lspserver: dict<any>)
        name: 'Vim',
        version: v:versionlong->string(),
       }
-  var curdir: string = getcwd()
-  initparams.rootPath = curdir
-  initparams.rootUri = util.LspFileToUri(curdir)
+
+  # Compute the rootpath (based on the directory of the buffer)
+  var bufDir = bnr->bufname()->fnamemodify(':p:h')
+  var rootPath = ''
+  var rootSearchFiles = lspserver.rootSearchFiles
+  if !rootSearchFiles->empty()
+    rootPath = util.FindNearestRootDir(bufDir, rootSearchFiles)
+  endif
+  if rootPath == ''
+    rootPath = bufDir
+  endif
+  var rootUri = util.LspFileToUri(rootPath)
+  initparams.rootPath = rootPath
+  initparams.rootUri = rootUri
   initparams.workspaceFolders = [{
-       name: curdir->fnamemodify(':t'),
-       uri: util.LspFileToUri(curdir)
+       name: rootPath->fnamemodify(':t'),
+       uri: rootUri
      }]
   initparams.trace = 'off'
   initparams.capabilities = capabilities.GetClientCaps()
@@ -1384,6 +1395,7 @@ enddef
 export def NewLspServer(name_arg: string, path_arg: string, args: list<string>,
                        isSync: bool, initializationOptions: any,
                        workspaceConfig: dict<any>,
+                       rootSearchFiles: list<any>,
                        customNotificationHandlers: dict<func>,
                        debug_arg: bool): dict<any>
   var lspserver: dict<any> = {
@@ -1400,6 +1412,7 @@ export def NewLspServer(name_arg: string, path_arg: string, args: list<string>,
     nextID: 1,
     caps: {},
     requests: {},
+    rootSearchFiles: rootSearchFiles,
     omniCompletePending: false,
     completionTriggerChars: [],
     signaturePopup: -1,
index a51718bb846d39b276ee7636bebb1ce82c1120aa..cb1798e610f34ad46b0a933dceb32c0c968a13cf 100644 (file)
@@ -226,4 +226,45 @@ export def Indexof(list: list<any>, CallbackFn: func(number, any): bool): number
   return -1
 enddef
 
+# Find the nearest root directory containing a file or directory name from the
+# list of names in 'files' starting with the directory 'startDir'.
+# Based on a similar implementation in the vim-lsp plugin.
+# Searches upwards starting with the directory 'startDir'.
+# If a file name ends with '/' or '\', then it is a directory name, otherwise
+# it is a file name.
+# Returns '' if none of the file and directory names in 'files' can be found
+# in one of the parent directories.
+export def FindNearestRootDir(startDir: string, files: list<any>): string
+  var foundDirs: dict<bool> = {}
+
+  for file in files
+    if file->type() != v:t_string || file == ''
+      continue
+    endif
+    var isDir = file[-1 : ] ==# '/' || file[-1 : ] ==# '\'
+    var relPath: string
+    if isDir
+      relPath = finddir(file, $'{startDir};')
+    else
+      relPath = findfile(file, $'{startDir};')
+    endif
+    if relPath->empty()
+      continue
+    endif
+    var rootDir = relPath->fnamemodify(isDir ? ':p:h:h' : ':p:h')
+    foundDirs[rootDir] = true
+  endfor
+  if foundDirs->empty()
+    return ''
+  endif
+
+  # Sort the directory names by length
+  var sortedList: list<string> = foundDirs->keys()->sort((a, b) => {
+    return b->len() - a->len()
+  })
+
+  # choose the longest matching path (the nearest directory from 'startDir')
+  return sortedList[0]
+enddef
+
 # vim: tabstop=8 shiftwidth=2 softtabstop=2
index d837fc1ffcd276acfc47f611185464384d3f4e32..1aa9086e2bfa9328a1b332fdc66280583929bf00 100644 (file)
@@ -2,7 +2,7 @@
 
 Author: Yegappan Lakshmanan  (yegappan AT yahoo DOT com)
 For Vim version 9.0 and above
-Last change: April 2, 2023
+Last change: April 7, 2023
 
 ==============================================================================
                                                *lsp-license*
@@ -259,6 +259,18 @@ To add a language server, the following information is needed:
                        this notification.  This configuration is also used to
                        respond to the "workspace/configuration" request
                        message from the language server.
+                                               *lsp-cfg-rootSearch*
+       rootSearch      (Optional) a List of file and directory names used to
+                       locate the root path or uri of the workspace.  The
+                       directory names in "rootSearch" must end in "/" or
+                       "\".  Each file and directory name in "rootSearch" is
+                       searched upwards in all the parent directories.  If
+                       multiple directories are found, then the directory
+                       closest to the directory of the current buffer is used
+                       as the workspace root.  If this parameter is not
+                       specified or the files are not found, then the
+                       directory of the current buffer is used as the
+                       workspace root.
 
 Aditionally the following configurations can be made:
 
@@ -297,6 +309,7 @@ server and to set the language server options.  For example: >
 
     let lspServers = [
                \     #{
+               \        name: 'clangd',
                \        filetype: ['c', 'cpp'],
                \        path: '/usr/local/bin/clangd',
                \        args: ['--background-index']