mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	LSP: Feature/add workspace folders (#12638)
* First implementation of workspace folders * Add completion for current directory * Add tracking of workspace folders * Add workspace folder listing * Add checks on adding/removing workspaces * Add appropriate initialization options * Add documentation * Make workspaceFolders available wherever client is
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							2d5dd85eef
						
					
				
				
					commit
					0d83a1c43f
				
			@@ -996,6 +996,19 @@ type_definition()                              *vim.lsp.buf.type_definition()*
 | 
			
		||||
                Jumps to the definition of the type of the symbol under the
 | 
			
		||||
                cursor.
 | 
			
		||||
 | 
			
		||||
add_workspace_folder({path})              *vim.lsp.buf.add_workspace_folder()*
 | 
			
		||||
                Add the folder at path to the workspace folders. If {path} is
 | 
			
		||||
                not provided, the user will be prompted for a path using
 | 
			
		||||
                |input()|.
 | 
			
		||||
 | 
			
		||||
remove_workspace_folder({path})        *vim.lsp.buf.remove_workspace_folder()*
 | 
			
		||||
                Remove the folder at path from the workspace folders. If 
 | 
			
		||||
                {path} is not provided, the user will be prompted for 
 | 
			
		||||
                a path using |input()|.
 | 
			
		||||
 | 
			
		||||
list_workspace_folders()                *vim.lsp.buf.list_workspace_folders()*
 | 
			
		||||
                List all folders in the workspace.
 | 
			
		||||
 | 
			
		||||
workspace_symbol({query})                     *vim.lsp.buf.workspace_symbol()*
 | 
			
		||||
                Lists all symbols in the current workspace in the quickfix
 | 
			
		||||
                window.
 | 
			
		||||
 
 | 
			
		||||
@@ -597,7 +597,10 @@ function lsp.start_client(config)
 | 
			
		||||
      --  -- workspace folder in the user interface.
 | 
			
		||||
      --  name
 | 
			
		||||
      -- }
 | 
			
		||||
      workspaceFolders = nil;
 | 
			
		||||
      workspaceFolders = {{
 | 
			
		||||
        uri = vim.uri_from_fname(config.root_dir);
 | 
			
		||||
        name = string.format("%s", config.root_dir);
 | 
			
		||||
      }};
 | 
			
		||||
    }
 | 
			
		||||
    if config.before_init then
 | 
			
		||||
      -- TODO(ashkan) handle errors here.
 | 
			
		||||
@@ -610,6 +613,7 @@ function lsp.start_client(config)
 | 
			
		||||
      rpc.notify('initialized', {[vim.type_idx]=vim.types.dictionary})
 | 
			
		||||
      client.initialized = true
 | 
			
		||||
      uninitialized_clients[client_id] = nil
 | 
			
		||||
      client.workspaceFolders = initialize_params.workspaceFolders
 | 
			
		||||
      client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities")
 | 
			
		||||
      -- These are the cleaned up capabilities we use for dynamically deciding
 | 
			
		||||
      -- when to send certain events to clients.
 | 
			
		||||
 
 | 
			
		||||
@@ -238,6 +238,61 @@ function M.outgoing_calls()
 | 
			
		||||
  end)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- List workspace folders.
 | 
			
		||||
function M.list_workspace_folders()
 | 
			
		||||
  local workspace_folders = {}
 | 
			
		||||
  for _, client in ipairs(vim.lsp.buf_get_clients()) do
 | 
			
		||||
    for _, folder in ipairs(client.workspaceFolders) do
 | 
			
		||||
      table.insert(workspace_folders, folder.name)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  return workspace_folders
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Add a workspace folder.
 | 
			
		||||
function M.add_workspace_folder(workspace_folder)
 | 
			
		||||
  workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'))
 | 
			
		||||
  vim.api.nvim_command("redraw")
 | 
			
		||||
  if not (workspace_folder and #workspace_folder > 0) then return end
 | 
			
		||||
  if vim.fn.isdirectory(workspace_folder) == 0 then
 | 
			
		||||
    print(workspace_folder, " is not a valid directory")
 | 
			
		||||
    return
 | 
			
		||||
  end
 | 
			
		||||
  local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}})
 | 
			
		||||
  for _, client in ipairs(vim.lsp.buf_get_clients()) do
 | 
			
		||||
    local found = false
 | 
			
		||||
    for _, folder in ipairs(client.workspaceFolders) do
 | 
			
		||||
      if folder.name == workspace_folder then
 | 
			
		||||
        found = true
 | 
			
		||||
        print(workspace_folder, "is already part of this workspace")
 | 
			
		||||
        break
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    if not found then
 | 
			
		||||
      vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params)
 | 
			
		||||
      table.insert(client.workspaceFolders, params.event.added[1])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Remove a workspace folder.
 | 
			
		||||
function M.remove_workspace_folder(workspace_folder)
 | 
			
		||||
  workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'))
 | 
			
		||||
  vim.api.nvim_command("redraw")
 | 
			
		||||
  if not (workspace_folder and #workspace_folder > 0) then return end
 | 
			
		||||
  local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}})
 | 
			
		||||
  for _, client in ipairs(vim.lsp.buf_get_clients()) do
 | 
			
		||||
    for idx, folder in ipairs(client.workspaceFolders) do
 | 
			
		||||
      if folder.name == workspace_folder then
 | 
			
		||||
        vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params)
 | 
			
		||||
        client.workspaceFolders[idx] = nil
 | 
			
		||||
        return
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  print(workspace_folder,  "is not currently part of the workspace")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Lists all symbols in the current workspace in the quickfix window.
 | 
			
		||||
---
 | 
			
		||||
--- The list is filtered against {query}; if the argument is omitted from the
 | 
			
		||||
 
 | 
			
		||||
@@ -716,6 +716,7 @@ function protocol.make_client_capabilities()
 | 
			
		||||
        };
 | 
			
		||||
        hierarchicalWorkspaceSymbolSupport = true;
 | 
			
		||||
      };
 | 
			
		||||
      workspaceFolders = true;
 | 
			
		||||
      applyEdit = true;
 | 
			
		||||
    };
 | 
			
		||||
    callHierarchy = {
 | 
			
		||||
@@ -974,6 +975,28 @@ function protocol.resolve_capabilities(server_capabilities)
 | 
			
		||||
    error("The server sent invalid implementationProvider")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local workspace = server_capabilities.workspace
 | 
			
		||||
  local workspace_properties = {}
 | 
			
		||||
  if workspace == nil or workspace.workspaceFolders == nil then
 | 
			
		||||
    -- Defaults if omitted.
 | 
			
		||||
    workspace_properties = {
 | 
			
		||||
      workspace_folder_properties =  {
 | 
			
		||||
        supported = false;
 | 
			
		||||
        changeNotifications=false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  elseif type(workspace.workspaceFolders) == 'table' then
 | 
			
		||||
    workspace_properties = {
 | 
			
		||||
      workspace_folder_properties = {
 | 
			
		||||
        supported = vim.F.ifnil(workspace.workspaceFolders.supported, false);
 | 
			
		||||
        changeNotifications = vim.F.ifnil(workspace.workspaceFolders.changeNotifications, false);
 | 
			
		||||
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  else
 | 
			
		||||
    error("The server sent invalid workspace")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local signature_help_properties
 | 
			
		||||
  if server_capabilities.signatureHelpProvider == nil then
 | 
			
		||||
    signature_help_properties = {
 | 
			
		||||
@@ -993,6 +1016,7 @@ function protocol.resolve_capabilities(server_capabilities)
 | 
			
		||||
  return vim.tbl_extend("error"
 | 
			
		||||
      , text_document_sync_properties
 | 
			
		||||
      , signature_help_properties
 | 
			
		||||
      , workspace_properties
 | 
			
		||||
      , general_properties
 | 
			
		||||
      )
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -1314,6 +1314,9 @@ function M.make_text_document_params()
 | 
			
		||||
  return { uri = vim.uri_from_bufnr(0) }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function M.make_workspace_params(added, removed)
 | 
			
		||||
  return { event = { added = added; removed = removed; } }
 | 
			
		||||
end
 | 
			
		||||
--- Returns visual width of tabstop.
 | 
			
		||||
---
 | 
			
		||||
--@see |softtabstop|
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user