-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Description
Commit 8ad2d8d introduced changes that restrict mentioned language servers from attaching to files outside the workspace directory. While this behavior makes sense for some servers, it creates inconveniences for JavaScript/TypeScript development.
Problem:
- General JS/TS files: Developers often edit standalone JS/TS files not bound to any workspace or project. The current restriction prevents language servers from attaching to these files, reducing functionality (e.g., linting, autocompletion, and diagnostics).
- ESLint/Biome: These servers do not support single files and should only attach when configuration files (e.g., .eslintrc, biome.json) are present in the directory, even if the directory is not a formal workspace.
- Svelte: Unlike JS/TS, the Svelte language server should likely only attach to files within a Svelte project, as standalone Svelte files are rare and less practical.
Expected Behavior:
- JS/TS Language Servers: Should attach to any JS/TS file, regardless of workspace context, to support standalone file editing.
- ESLint/Biome: Should attach to files when configuration files are present in the directory, even if it’s not a workspace.
- Svelte Language Server: Should only attach to files within a Svelte project.
Current Behavior:
- All mentioned servers are restricted from attaching to files outside the workspace directory,
Possible solutions
example of current eslint config
root_dir = function(bufnr, on_dir)
-- The project root is where the LSP can be started from
-- As stated in the documentation above, this LSP supports monorepos and simple projects.
-- We select then from the project root, which is identified by the presence of a package
-- manager lock file.
local project_root_markers = { 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb', 'bun.lock' }
local project_root = vim.fs.root(bufnr, project_root_markers)
if not project_root then
return nil
end
-- We know that the buffer is using ESLint if it has a config file
-- in its directory tree.
--
-- Eslint used to support package.json files as config files, but it doesn't anymore.
-- We keep this for backward compatibility.
local filename = vim.api.nvim_buf_get_name(bufnr)
local eslint_config_files_with_package_json =
util.insert_package_json(eslint_config_files, 'eslintConfig', filename)
local is_buffer_using_eslint = vim.fs.find(eslint_config_files_with_package_json, {
path = filename,
type = 'file',
limit = 1,
upward = true,
stop = vim.fs.dirname(project_root),
})[1]
if not is_buffer_using_eslint then
return nil
end
on_dir(project_root)
end,
For eslint/biome servers, the root_dir
function should:
- Return if neither a root project marker nor a config file is found.
- Call
on_dir(config_file_directory)
if only a config file is found (and no root project marker exists).
For ts_ls/tsgo/vtsls, the root_dir
function should call on_dir(nil)
if there is no project_root
to allow these language servers to attach to standalone files, as suggested here by @justinmk.
Previous description
Summary
ts_ls server don't attach to javascript or typescript files when there are no root marker files in directory (ex. package-lock.json)
Steps to reproduce
- create minimal neovim config file.
Minimal init.lua
vim.o.clipboard = 'unnamedplus'
vim.o.softtabstop = 2
vim.o.shiftwidth = 2
vim.o.expandtab = true
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
-- Make sure to setup `mapleader` and `maplocalleader` before
-- loading lazy.nvim so that mappings are correct.
-- This is also a good place to setup other settings (vim.opt)
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- Setup lazy.nvim
require("lazy").setup({
spec = {
"neovim/nvim-lspconfig",
config = function()
vim.lsp.enable("ts_ls")
end,
},
-- Configure any other settings here. See the documentation for more details.
-- colorscheme that will be used when installing plugins.
install = { colorscheme = { "habamax" } },
-- automatically check for plugin updates
checker = { enabled = true },
})
- install localy ts_ls server
npm install -g typescript typescript-language-server
. - run neovim with minimal config
nvim -u /path/to/minimal_init.lua
. - start editing index.js file
:e index.js<CR>
. - open lsp info
:LspInfo<CR>
. ts_ls is not an active lsp. - start editing index.ts file
:e index.js<CR>
- open lsp info
:LspInfo<CR>
. ts_ls is not an active lsp. - run
npm init -y
. - repeat steps from 4 to 7.
- create package-lock.json file.
- repeat steps from 4 to 7. now ts_ls attached to both index.js and index.ts files
Expected behavior
ts_ls server should always be attached to any javascript or typescript files regardless where these files are located
Current behavior
ts_ls server don't attach to javascript or typescript files when there are no root marker files in directory (ex. package-lock.json)
LspInfo
index.js LspInfo
- LSP log level : WARN
- Log path: /home/user/.local/state/nvim/lsp.log
- Log size: 0 KB
vim.lsp: Active Clients ~
- No active clients
vim.lsp: Enabled Configurations ~
- ts_ls:
- cmd: { "typescript-language-server", "--stdio" }
- commands: {
["editor.action.showReferences"] = <function 1>
}
- filetypes: javascript, javascriptreact, javascript.jsx, typescript, typescriptreact, typescript.tsx
- handlers: {
["_typescript.rename"] = <function 1>
}
- init_options: {
hostInfo = "neovim"
}
- on_attach: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:110>
- root_dir: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:56>
vim.lsp: File Watcher ~
- file watching "(workspace/didChangeWatchedFiles)" disabled on all clients
vim.lsp: Position Encodings ~
- No active clients
index.ts LspInfo
- LSP log level : WARN
- Log path: /home/user/.local/state/nvim/lsp.log
- Log size: 0 KB
vim.lsp: Active Clients ~
- No active clients
vim.lsp: Enabled Configurations ~
- ts_ls:
- cmd: { "typescript-language-server", "--stdio" }
- commands: {
["editor.action.showReferences"] = <function 1>
}
- filetypes: javascript, javascriptreact, javascript.jsx, typescript, typescriptreact, typescript.tsx
- handlers: {
["_typescript.rename"] = <function 1>
}
- init_options: {
hostInfo = "neovim"
}
- on_attach: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:110>
- root_dir: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:56>
vim.lsp: File Watcher ~
- file watching "(workspace/didChangeWatchedFiles)" disabled on all clients
vim.lsp: Position Encodings ~
- No active clients
index.js LspInfo with package-lock.json file in directory
- LSP log level : WARN
- Log path: /home/user/.local/state/nvim/lsp.log
- Log size: 0 KB
vim.lsp: Active Clients ~
- ts_ls (id: 1)
- Version: ? (no serverInfo.version response)
- Root directory: /tmp/nvim
- Command: { "typescript-language-server", "--stdio" }
- Settings: {}
- Attached buffers: 1
vim.lsp: Enabled Configurations ~
- ts_ls:
- cmd: { "typescript-language-server", "--stdio" }
- commands: {
["editor.action.showReferences"] = <function 1>
}
- filetypes: javascript, javascriptreact, javascript.jsx, typescript, typescriptreact, typescript.tsx
- handlers: {
["_typescript.rename"] = <function 1>
}
- init_options: {
hostInfo = "neovim"
}
- on_attach: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:110>
- root_dir: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:56>
vim.lsp: File Watcher ~
- file watching "(workspace/didChangeWatchedFiles)" disabled on all clients
vim.lsp: Position Encodings ~
- No buffers contain mixed position encodings
index.ts LspInfo with package-lock.json file in directory
- LSP log level : WARN
- Log path: /home/user/.local/state/nvim/lsp.log
- Log size: 0 KB
vim.lsp: Active Clients ~
- ts_ls (id: 1)
- Version: ? (no serverInfo.version response)
- Root directory: /tmp/nvim
- Command: { "typescript-language-server", "--stdio" }
- Settings: {}
- Attached buffers: 1, 4
vim.lsp: Enabled Configurations ~
- ts_ls:
- cmd: { "typescript-language-server", "--stdio" }
- commands: {
["editor.action.showReferences"] = <function 1>
}
- filetypes: javascript, javascriptreact, javascript.jsx, typescript, typescriptreact, typescript.tsx
- handlers: {
["_typescript.rename"] = <function 1>
}
- init_options: {
hostInfo = "neovim"
}
- on_attach: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:110>
- root_dir: <function @/home/user/.local/share/nvim/lazy/nvim-lspconfig/lsp/ts_ls.lua:56>
vim.lsp: File Watcher ~
- file watching "(workspace/didChangeWatchedFiles)" disabled on all clients
vim.lsp: Position Encodings ~
- No buffers contain mixed position encodings
Investigation
By using git bisect I found that this Commit 8ad2d8d introduced changes that broke server ability to attach to any javascript or typescript files outside of root directory which contains root marker files (ex. package-lock.json)