I'm using Neovim's lspconfig plugin to manage LSP servers for my development environment. Whenever I open a TypeScript file, both denols and tsserver attach to the same buffer, causing conflicts.
When I open a TypeScript file in:
- A project with
deno.json, bothdenolsandtsserverattach. - A Node.js project with a
package.json, bothdenolsandtsserverstill attach. - A non-project file or single-file context, both servers attach again.
both servers auto-attach no matter the context, I even remove the lspconfig config completely and they still get attached
Tried this simple config from the Deno setup your environment
local lspconfig = require("lspconfig")
local util = lspconfig.util
lspconfig.denols.setup({
root_dir = util.root_pattern("deno.json", "deno.jsonc"), -- Deno projects only
single_file_support = false,
})
lspconfig.tsserver.setup({
root_dir = util.root_pattern("package.json"), -- Node.js projects only
single_file_support = false,
})
LSP configs active in this buffer (bufnr: 4) ~
- Language client log: ~/.local/state/nvim/lsp.log
- Detected filetype: `typescript`
- 2 client(s) attached to this buffer
- Client: `denols` (id: 1, bufnr: [4])
root directory: /mnt/library/development/projects/obsidian-programming-advices/
filetypes: javascript, javascriptreact, javascript.jsx, typescript, typescriptreact, typescript.tsx
cmd: ~/.local/share/nvim/mason/bin/deno lsp
version: `deno 2.1.5 (stable, release, x86_64-unknown-linux-gnu)`
executable: true
autostart: true
- Client: `ts_ls` (id: 2, bufnr: [4])
root directory: /mnt/library/development/projects/obsidian-programming-advices/
filetypes: javascript, javascriptreact, javascript.jsx, typescript, typescriptreact, typescript.tsx
cmd: ~/.local/share/nvim/mason/bin/typescript-language-server --stdio
version: `4.3.3`
executable: true
autostart: true
I'm using lspconfig and this is my config:
-- Deno Language Server
lspconfig.denols.setup({
capabilities = capabilities,
on_attach = function(client)
-- Enable document formatting for Deno
if client.server_capabilities.documentFormattingProvider then
client.server_capabilities.documentFormattingProvider = true
end
end,
root_dir = lspconfig.util.root_pattern("deno.json", "deno.jsonc"),
settings = {
deno = {
enable = true,
lint = true,
fmt = true,
inlayHints = {
parameterNames = { enabled = "all", suppressWhenArgumentMatchesName = true },
parameterTypes = { enabled = false },
variableTypes = { enabled = false, suppressWhenTypeMatchesName = false },
propertyDeclarationTypes = { enabled = false },
functionLikeReturnTypes = { enabled = true },
enumMemberValues = { enabled = true },
},
},
},
})
-- Ts_ls Language Server
lspconfig.ts_ls.setup({
capabilities = capabilities,
on_attach = on_attach,
root_dir = util.root_pattern("package.json"),
single_file_support = false,
settings = {
typescript = {
format = {
enable = false,
},
},
javascript = {
format = {
enable = false,
},
},
completions = {
completeFunctionCalls = true,
},
},
})
In the end I get around it using this wonky solution:
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
local is_node_project = vim.fn.filereadable("package.json") == 1
local is_deno_project = vim.fn.filereadable("deno.json") == 1
-- Stop `denols` in Node.js projects
if client.name == "denols" and is_node_project then
client.stop()
end
-- Stop `tsserver` in Deno projects
if client.name == "ts_ls" and is_deno_project then
client.stop()
end
end,
})
So I still need a proper solution!
Instead, both denols and ts_ls attached to the same buffer in a non-Deno project, causing conflicts, likely due to improper root directory detection for denols. I updated the configuration to make the root detection stricter for both denols and ts_ls, and verified the behavior using :LspInfo.