mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	feat(lsp): add buf_detach_client
This allows the user to detach an active buffer from the language client. If no clients remain attached to a buffer, the on_lines callback is used to cancel nvim_buf_attach.
This commit is contained in:
		@@ -1112,9 +1112,9 @@ local text_document_did_change_handler
 | 
			
		||||
do
 | 
			
		||||
  text_document_did_change_handler = function(_, bufnr, changedtick, firstline, lastline, new_lastline)
 | 
			
		||||
 | 
			
		||||
    -- Don't do anything if there are no clients attached.
 | 
			
		||||
    -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
 | 
			
		||||
    if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
 | 
			
		||||
      return
 | 
			
		||||
      return true
 | 
			
		||||
    end
 | 
			
		||||
    util.buf_versions[bufnr] = changedtick
 | 
			
		||||
    local compute_change_and_notify = changetracking.prepare(bufnr, firstline, lastline, new_lastline)
 | 
			
		||||
@@ -1220,6 +1220,50 @@ function lsp.buf_attach_client(bufnr, client_id)
 | 
			
		||||
  return true
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Detaches client from the specified buffer.
 | 
			
		||||
--- Note: While the server is notified that the text document (buffer)
 | 
			
		||||
--- was closed, it is still able to send notifications should it ignore this notification.
 | 
			
		||||
---
 | 
			
		||||
---@param bufnr number Buffer handle, or 0 for current
 | 
			
		||||
---@param client_id number Client id
 | 
			
		||||
function lsp.buf_detach_client(bufnr, client_id)
 | 
			
		||||
  validate {
 | 
			
		||||
    bufnr     = {bufnr, 'n', true};
 | 
			
		||||
    client_id = {client_id, 'n'};
 | 
			
		||||
  }
 | 
			
		||||
  bufnr = resolve_bufnr(bufnr)
 | 
			
		||||
 | 
			
		||||
  local client = lsp.get_client_by_id(client_id)
 | 
			
		||||
  if not client or not client.attached_buffers[bufnr] then
 | 
			
		||||
    vim.notify(
 | 
			
		||||
      string.format('Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', client_id, bufnr)
 | 
			
		||||
    )
 | 
			
		||||
    return
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  changetracking.reset_buf(client, bufnr)
 | 
			
		||||
 | 
			
		||||
  if client.resolved_capabilities.text_document_open_close then
 | 
			
		||||
    local uri = vim.uri_from_bufnr(bufnr)
 | 
			
		||||
    local params = { textDocument = { uri = uri; } }
 | 
			
		||||
    client.notify('textDocument/didClose', params)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  client.attached_buffers[bufnr] = nil
 | 
			
		||||
  util.buf_versions[bufnr] = nil
 | 
			
		||||
 | 
			
		||||
  all_buffer_active_clients[bufnr][client_id] = nil
 | 
			
		||||
  if #vim.tbl_keys(all_buffer_active_clients[bufnr]) == 0 then
 | 
			
		||||
    all_buffer_active_clients[bufnr] = nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  local namespace = vim.lsp.diagnostic.get_namespace(client_id)
 | 
			
		||||
  vim.diagnostic.reset(namespace, bufnr)
 | 
			
		||||
 | 
			
		||||
  vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id))
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Checks if a buffer is attached for a particular client.
 | 
			
		||||
---
 | 
			
		||||
---@param bufnr (number) Buffer handle, or 0 for current
 | 
			
		||||
 
 | 
			
		||||
@@ -301,6 +301,43 @@ describe('LSP', function()
 | 
			
		||||
      }
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('should detach buffer in response to nvim_buf_detach', function()
 | 
			
		||||
      local expected_handlers = {
 | 
			
		||||
        {NIL, {}, {method="shutdown", client_id=1}};
 | 
			
		||||
        {NIL, {}, {method="finish", client_id=1}};
 | 
			
		||||
      }
 | 
			
		||||
      local client
 | 
			
		||||
      test_rpc_server {
 | 
			
		||||
        test_name = "basic_finish";
 | 
			
		||||
        on_setup = function()
 | 
			
		||||
          exec_lua [[
 | 
			
		||||
            BUFFER = vim.api.nvim_create_buf(false, true)
 | 
			
		||||
          ]]
 | 
			
		||||
          eq(true, exec_lua("return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)"))
 | 
			
		||||
          eq(true, exec_lua("return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)"))
 | 
			
		||||
          exec_lua [[
 | 
			
		||||
            vim.api.nvim_command(BUFFER.."bwipeout")
 | 
			
		||||
          ]]
 | 
			
		||||
        end;
 | 
			
		||||
        on_init = function(_client)
 | 
			
		||||
          client = _client
 | 
			
		||||
          client.notify('finish')
 | 
			
		||||
        end;
 | 
			
		||||
        on_exit = function(code, signal)
 | 
			
		||||
          eq(0, code, "exit code", fake_lsp_logfile)
 | 
			
		||||
          eq(0, signal, "exit signal", fake_lsp_logfile)
 | 
			
		||||
        end;
 | 
			
		||||
        on_handler = function(err, result, ctx)
 | 
			
		||||
          eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
 | 
			
		||||
          if ctx.method == 'finish' then
 | 
			
		||||
            exec_lua("return lsp.buf_detach_client(BUFFER, TEST_RPC_CLIENT_ID)")
 | 
			
		||||
            eq(false, exec_lua("return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)"))
 | 
			
		||||
            client.stop()
 | 
			
		||||
          end
 | 
			
		||||
        end;
 | 
			
		||||
      }
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('client should return settings via workspace/configuration handler', function()
 | 
			
		||||
      local expected_handlers = {
 | 
			
		||||
        {NIL, {}, {method="shutdown", client_id=1}};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user