Conditionally detatch an LSP client from the current buffer

I use the Zettelkasten program zk for note taking. It ships with its own LSP to power some nice features. However, I also run the Marksman LSP, as the zk LSP only runs within zk notebooks, and isn’t as fully featured as Marksman.

Running multiple LSPs can lead to confusing behaviour when their functionality clashes. Usually it’s fine, nvim-lsp just executes the command with the first client in the list (I assume). But there have been times for me when it wasn’t fine.

Another reason may be that you want to exclusively use the formatting capabilities of a certain LSP within a certain project.

Solution

Each LSP has some default methods and fields. Here are the nvim-lsp docs.

We can create some logic in the on_init method to check if an LSP is attached, and act based on the result.

I’m using Mason for installing, setting up and managing LSPs.


-- ... more code

		require("mason-lspconfig").setup_handlers({
			function(server_name)
				lspconfig[server_name].setup({
					on_attach = -- ... code out of scope
				})
			end,
			["marksman"] = function()
				require("lspconfig").marksman.setup({
					on_attach = -- ... code out of scope
					-- if zk lsp is attached, we are working in a zk notebook
					-- so detach marksman.
					on_init = function(client)
						local clients = vim.lsp.get_active_clients()
						for _, c in ipairs(clients) do
							if c.name == "zk" then
								vim.lsp.buf_detach_client(0, client.id)
							end
						end
					end,
				})
			end,
      })