mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(lsp): check if buffer was detached in on_init callback (#28942)
Co-authored-by: Jongwook Choi <wookayin@gmail.com>
(cherry picked from commit af200c10cf)
Co-authored-by: Ilia Choly <ilia.choly@gmail.com>
			
			
This commit is contained in:
		![41898282+github-actions[bot]@users.noreply.github.com](/assets/img/avatar_default.png) github-actions[bot]
					github-actions[bot]
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							10a16c1311
						
					
				
				
					commit
					bdd5871dc5
				
			| @@ -612,7 +612,10 @@ function Client:initialize() | |||||||
|     self:_run_callbacks(self._on_init_cbs, lsp.client_errors.ON_INIT_CALLBACK_ERROR, self, result) |     self:_run_callbacks(self._on_init_cbs, lsp.client_errors.ON_INIT_CALLBACK_ERROR, self, result) | ||||||
|  |  | ||||||
|     for buf in pairs(reattach_bufs) do |     for buf in pairs(reattach_bufs) do | ||||||
|       self:_on_attach(buf) |       -- The buffer may have been detached in the on_init callback. | ||||||
|  |       if self.attached_buffers[buf] then | ||||||
|  |         self:_on_attach(buf) | ||||||
|  |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     log.info( |     log.info( | ||||||
|   | |||||||
| @@ -70,8 +70,8 @@ before_each(function() | |||||||
|         inlayHintProvider = true, |         inlayHintProvider = true, | ||||||
|       }, |       }, | ||||||
|       handlers = { |       handlers = { | ||||||
|         ['textDocument/inlayHint'] = function() |         ['textDocument/inlayHint'] = function(_, _, callback) | ||||||
|           return vim.json.decode(response) |           callback(nil, vim.json.decode(response)) | ||||||
|         end, |         end, | ||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
| @@ -106,8 +106,8 @@ describe('vim.lsp.inlay_hint', function() | |||||||
|           inlayHintProvider = true, |           inlayHintProvider = true, | ||||||
|         }, |         }, | ||||||
|         handlers = { |         handlers = { | ||||||
|           ['textDocument/inlayHint'] = function() |           ['textDocument/inlayHint'] = function(_, _, callback) | ||||||
|             return {} |             callback(nil, {}) | ||||||
|           end, |           end, | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
| @@ -206,8 +206,8 @@ describe('vim.lsp.inlay_hint', function() | |||||||
|             inlayHintProvider = true, |             inlayHintProvider = true, | ||||||
|           }, |           }, | ||||||
|           handlers = { |           handlers = { | ||||||
|             ['textDocument/inlayHint'] = function() |             ['textDocument/inlayHint'] = function(_, _, callback) | ||||||
|               return { expected2 } |               callback(nil, { expected2 }) | ||||||
|             end, |             end, | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
|   | |||||||
| @@ -95,11 +95,11 @@ describe('semantic token highlighting', function() | |||||||
|             }, |             }, | ||||||
|           }, |           }, | ||||||
|           handlers = { |           handlers = { | ||||||
|             ['textDocument/semanticTokens/full'] = function() |             ['textDocument/semanticTokens/full'] = function(_, _, callback) | ||||||
|               return vim.fn.json_decode(response) |               callback(nil, vim.fn.json_decode(response)) | ||||||
|             end, |             end, | ||||||
|             ['textDocument/semanticTokens/full/delta'] = function() |             ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) | ||||||
|               return vim.fn.json_decode(edit_response) |               callback(nil, vim.fn.json_decode(edit_response)) | ||||||
|             end, |             end, | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
| @@ -503,11 +503,11 @@ describe('semantic token highlighting', function() | |||||||
|             }, |             }, | ||||||
|           }, |           }, | ||||||
|           handlers = { |           handlers = { | ||||||
|             ['textDocument/semanticTokens/full'] = function() |             ['textDocument/semanticTokens/full'] = function(_, _, callback) | ||||||
|               return nil |               callback(nil, nil) | ||||||
|             end, |             end, | ||||||
|             ['textDocument/semanticTokens/full/delta'] = function() |             ['textDocument/semanticTokens/full/delta'] = function() | ||||||
|               return nil |               callback(nil, nil) | ||||||
|             end, |             end, | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
| @@ -551,11 +551,11 @@ describe('semantic token highlighting', function() | |||||||
|             }, |             }, | ||||||
|           }, |           }, | ||||||
|           handlers = { |           handlers = { | ||||||
|             ['textDocument/semanticTokens/full'] = function() |             ['textDocument/semanticTokens/full'] = function(_, _, callback) | ||||||
|               return vim.fn.json_decode(response) |               callback(nil, vim.fn.json_decode(response)) | ||||||
|             end, |             end, | ||||||
|             ['textDocument/semanticTokens/full/delta'] = function() |             ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) | ||||||
|               return vim.fn.json_decode(edit_response) |               callback(nil, vim.fn.json_decode(edit_response)) | ||||||
|             end, |             end, | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
| @@ -1018,8 +1018,8 @@ b = "as"]], | |||||||
|               }, |               }, | ||||||
|             }, |             }, | ||||||
|             handlers = { |             handlers = { | ||||||
|               ['textDocument/semanticTokens/full'] = function() |               ['textDocument/semanticTokens/full'] = function(_, _, callback) | ||||||
|                 return vim.fn.json_decode(resp) |                 callback(nil, vim.fn.json_decode(resp)) | ||||||
|               end, |               end, | ||||||
|             } |             } | ||||||
|           }) |           }) | ||||||
| @@ -1404,11 +1404,11 @@ int main() | |||||||
|               }, |               }, | ||||||
|             }, |             }, | ||||||
|             handlers = { |             handlers = { | ||||||
|               ['textDocument/semanticTokens/full'] = function() |               ['textDocument/semanticTokens/full'] = function(_, _, callback) | ||||||
|                 return vim.fn.json_decode(resp1) |                 callback(nil, vim.fn.json_decode(resp1)) | ||||||
|               end, |               end, | ||||||
|               ['textDocument/semanticTokens/full/delta'] = function() |               ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) | ||||||
|                 return vim.fn.json_decode(resp2) |                 callback(nil, vim.fn.json_decode(resp2)) | ||||||
|               end, |               end, | ||||||
|             } |             } | ||||||
|           }) |           }) | ||||||
|   | |||||||
| @@ -39,8 +39,7 @@ M.create_server_definition = [[ | |||||||
|         }) |         }) | ||||||
|         local handler = handlers[method] |         local handler = handlers[method] | ||||||
|         if handler then |         if handler then | ||||||
|           local response, err = handler(method, params) |           handler(method, params, callback) | ||||||
|           callback(err, response) |  | ||||||
|         elseif method == 'initialize' then |         elseif method == 'initialize' then | ||||||
|           callback(nil, { |           callback(nil, { | ||||||
|             capabilities = opts.capabilities or {} |             capabilities = opts.capabilities or {} | ||||||
|   | |||||||
| @@ -501,6 +501,35 @@ describe('LSP', function() | |||||||
|       eq(true, result.detach_called) |       eq(true, result.detach_called) | ||||||
|     end) |     end) | ||||||
|  |  | ||||||
|  |     it('should not re-attach buffer if it was deleted in on_init #28575', function() | ||||||
|  |       clear() | ||||||
|  |       exec_lua(create_server_definition) | ||||||
|  |       exec_lua([[ | ||||||
|  |         local server = _create_server({ | ||||||
|  |           handlers = { | ||||||
|  |             initialize = function(method, params, callback) | ||||||
|  |               vim.schedule(function() | ||||||
|  |                 callback(nil, { capabilities = {} }) | ||||||
|  |               end) | ||||||
|  |             end | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         local bufnr = vim.api.nvim_create_buf(false, true) | ||||||
|  |         local on_init_called = false | ||||||
|  |         local client_id = vim.lsp.start({ | ||||||
|  |           name = 'detach-dummy', | ||||||
|  |           cmd = server.cmd, | ||||||
|  |           on_init = function() | ||||||
|  |             vim.api.nvim_buf_delete(bufnr, {}) | ||||||
|  |             on_init_called = true | ||||||
|  |           end | ||||||
|  |         }) | ||||||
|  |         vim.lsp.buf_attach_client(bufnr, client_id) | ||||||
|  |         local ok = vim.wait(1000, function() return on_init_called end) | ||||||
|  |         assert(ok, "on_init was not called") | ||||||
|  |       ]]) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|     it('client should return settings via workspace/configuration handler', function() |     it('client should return settings via workspace/configuration handler', function() | ||||||
|       local expected_handlers = { |       local expected_handlers = { | ||||||
|         { NIL, {}, { method = 'shutdown', client_id = 1 } }, |         { NIL, {}, { method = 'shutdown', client_id = 1 } }, | ||||||
| @@ -670,7 +699,7 @@ describe('LSP', function() | |||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           handlers = { |           handlers = { | ||||||
|             ['textDocument/willSaveWaitUntil'] = function() |             ['textDocument/willSaveWaitUntil'] = function(_, _, callback) | ||||||
|               local text_edit = { |               local text_edit = { | ||||||
|                 range = { |                 range = { | ||||||
|                   start = { line = 0, character = 0 }, |                   start = { line = 0, character = 0 }, | ||||||
| @@ -678,7 +707,7 @@ describe('LSP', function() | |||||||
|                 }, |                 }, | ||||||
|                 newText = 'Hello' |                 newText = 'Hello' | ||||||
|               } |               } | ||||||
|               return { text_edit, } |               callback(nil, { text_edit, }) | ||||||
|             end |             end | ||||||
|           }, |           }, | ||||||
|         }) |         }) | ||||||
| @@ -4144,8 +4173,8 @@ describe('LSP', function() | |||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           handlers = { |           handlers = { | ||||||
|             ["textDocument/codeAction"] = function() |             ["textDocument/codeAction"] = function(_, _, callback) | ||||||
|               return { |               callback(nil, { | ||||||
|                 { |                 { | ||||||
|                   title = "Code Action 1", |                   title = "Code Action 1", | ||||||
|                   command = { |                   command = { | ||||||
| @@ -4153,10 +4182,10 @@ describe('LSP', function() | |||||||
|                     command = "command:1", |                     command = "command:1", | ||||||
|                   } |                   } | ||||||
|                 } |                 } | ||||||
|               } |               }) | ||||||
|             end, |             end, | ||||||
|             ["codeAction/resolve"] = function() |             ["codeAction/resolve"] = function(_, _, callback) | ||||||
|               return nil, "resolve failed" |               callback("resolve failed", nil) | ||||||
|             end, |             end, | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
| @@ -4344,7 +4373,7 @@ describe('LSP', function() | |||||||
|               }, |               }, | ||||||
|             }, |             }, | ||||||
|             handlers = { |             handlers = { | ||||||
|               ["textDocument/codeLens"] = function(method, params) |               ["textDocument/codeLens"] = function(method, params, callback) | ||||||
|                 local lenses = { |                 local lenses = { | ||||||
|                   { |                   { | ||||||
|                     range = { |                     range = { | ||||||
| @@ -4357,7 +4386,7 @@ describe('LSP', function() | |||||||
|                     }, |                     }, | ||||||
|                   }, |                   }, | ||||||
|                 } |                 } | ||||||
|                 return lenses |                 callback(nil, lenses) | ||||||
|               end, |               end, | ||||||
|             } |             } | ||||||
|           }) |           }) | ||||||
| @@ -4665,13 +4694,13 @@ describe('LSP', function() | |||||||
|           }, |           }, | ||||||
|           handlers = { |           handlers = { | ||||||
|             ---@return lsp.Location[] |             ---@return lsp.Location[] | ||||||
|             ['textDocument/definition'] = function() |             ['textDocument/definition'] = function(_, _, callback) | ||||||
|               return { _G.mock_locations[1] } |               callback(nil, { _G.mock_locations[1] }) | ||||||
|             end, |             end, | ||||||
|             ---@return lsp.WorkspaceSymbol[] |             ---@return lsp.WorkspaceSymbol[] | ||||||
|             ['workspace/symbol'] = function(_, request) |             ['workspace/symbol'] = function(_, request, callback) | ||||||
|               assert(request.query == 'foobar') |               assert(request.query == 'foobar') | ||||||
|               return { |               callback(nil, { | ||||||
|                 { |                 { | ||||||
|                   name = 'foobar', |                   name = 'foobar', | ||||||
|                   kind = 13, ---@type lsp.SymbolKind |                   kind = 13, ---@type lsp.SymbolKind | ||||||
| @@ -4682,7 +4711,7 @@ describe('LSP', function() | |||||||
|                   kind = 12, ---@type lsp.SymbolKind |                   kind = 12, ---@type lsp.SymbolKind | ||||||
|                   location = _G.mock_locations[2], |                   location = _G.mock_locations[2], | ||||||
|                 } |                 } | ||||||
|               } |               }) | ||||||
|             end, |             end, | ||||||
|           }, |           }, | ||||||
|         }) |         }) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user