(nvim) nvim v0.12 migration
This commit is contained in:
parent
f0db0ada98
commit
7b2eb8c525
51 changed files with 1629 additions and 1627 deletions
29
nvim/lsp/bashls.lua
Normal file
29
nvim/lsp/bashls.lua
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---@brief
|
||||
---
|
||||
--- https://github.com/bash-lsp/bash-language-server
|
||||
---
|
||||
--- `bash-language-server` can be installed via `npm`:
|
||||
--- ```sh
|
||||
--- npm i -g bash-language-server
|
||||
--- ```
|
||||
---
|
||||
--- Language server for bash, written using tree sitter in typescript.
|
||||
|
||||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "bash-language-server", "start" },
|
||||
settings = {
|
||||
bashIde = {
|
||||
-- Glob pattern for finding and parsing shell script files in the workspace.
|
||||
-- Used by the background analysis features across files.
|
||||
|
||||
-- Prevent recursive scanning which will cause issues when opening a file
|
||||
-- directly in the home directory (e.g. ~/foo.sh).
|
||||
--
|
||||
-- Default upstream pattern is "**/*@(.sh|.inc|.bash|.command)".
|
||||
globPattern = vim.env.GLOB_PATTERN or "*@(.sh|.inc|.bash|.command)",
|
||||
},
|
||||
},
|
||||
filetypes = { "bash", "sh" },
|
||||
root_markers = { ".git" },
|
||||
}
|
||||
51
nvim/lsp/css_variables.lua
Normal file
51
nvim/lsp/css_variables.lua
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---@brief
|
||||
---
|
||||
--- https://github.com/vunguyentuan/vscode-css-variables/tree/master/packages/css-variables-language-server
|
||||
---
|
||||
--- CSS variables autocompletion and go-to-definition
|
||||
---
|
||||
--- `css-variables-language-server` can be installed via `npm`:
|
||||
---
|
||||
--- ```sh
|
||||
--- npm i -g css-variables-language-server
|
||||
--- ```
|
||||
|
||||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "css-variables-language-server", "--stdio" },
|
||||
filetypes = { "css", "scss", "less" },
|
||||
|
||||
-- Taken from lsp/ts_ls.lua to handle simple projects and monorepos.
|
||||
root_dir = function(bufnr, on_dir)
|
||||
local root_markers = { "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "bun.lockb", "bun.lock" }
|
||||
-- Give the root markers equal priority by wrapping them in a table
|
||||
root_markers = vim.fn.has("nvim-0.11.3") == 1 and { root_markers, { ".git" } }
|
||||
or vim.list_extend(root_markers, { ".git" })
|
||||
-- We fallback to the current working directory if no project root is found
|
||||
local project_root = vim.fs.root(bufnr, root_markers) or vim.fn.getcwd()
|
||||
|
||||
on_dir(project_root)
|
||||
end,
|
||||
|
||||
-- Same as inlined defaults that don't seem to work without hardcoding them in the lua config
|
||||
-- https://github.com/vunguyentuan/vscode-css-variables/blob/763a564df763f17aceb5f3d6070e0b444a2f47ff/packages/css-variables-language-server/src/CSSVariableManager.ts#L31-L50
|
||||
settings = {
|
||||
cssVariables = {
|
||||
lookupFiles = { "**/*.less", "**/*.scss", "**/*.sass", "**/*.css" },
|
||||
blacklistFolders = {
|
||||
"**/.cache",
|
||||
"**/.DS_Store",
|
||||
"**/.git",
|
||||
"**/.hg",
|
||||
"**/.next",
|
||||
"**/.svn",
|
||||
"**/bower_components",
|
||||
"**/CVS",
|
||||
"**/dist",
|
||||
"**/node_modules",
|
||||
"**/tests",
|
||||
"**/tmp",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
15
nvim/lsp/cssls.lua
Normal file
15
nvim/lsp/cssls.lua
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||
capabilities.textDocument.completion.completionItem.snippetSupport = true
|
||||
|
||||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "vscode-css-language-server", "--stdio" },
|
||||
filetypes = { "css", "scss", "less" },
|
||||
root_markers = { "package.json", ".git" },
|
||||
capabilities = capabilities,
|
||||
settings = {
|
||||
css = { validate = true },
|
||||
scss = { validate = true },
|
||||
less = { validate = true },
|
||||
},
|
||||
}
|
||||
99
nvim/lsp/gopls.lua
Normal file
99
nvim/lsp/gopls.lua
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
---@brief
|
||||
---
|
||||
--- https://github.com/golang/tools/tree/master/gopls
|
||||
---
|
||||
--- Google's lsp server for golang.
|
||||
|
||||
--- @class go_dir_custom_args
|
||||
---
|
||||
--- @field envvar_id string
|
||||
---
|
||||
--- @field custom_subdir string?
|
||||
|
||||
local mod_cache = nil
|
||||
local std_lib = nil
|
||||
|
||||
---@param custom_args go_dir_custom_args
|
||||
---@param on_complete fun(dir: string | nil)
|
||||
local function identify_go_dir(custom_args, on_complete)
|
||||
local cmd = { "go", "env", custom_args.envvar_id }
|
||||
vim.system(cmd, { text = true }, function(output)
|
||||
local res = vim.trim(output.stdout or "")
|
||||
if output.code == 0 and res ~= "" then
|
||||
if custom_args.custom_subdir and custom_args.custom_subdir ~= "" then
|
||||
res = res .. custom_args.custom_subdir
|
||||
end
|
||||
on_complete(res)
|
||||
else
|
||||
vim.schedule(function()
|
||||
vim.notify(
|
||||
("[gopls] identify " .. custom_args.envvar_id .. " dir cmd failed with code %d: %s\n%s"):format(
|
||||
output.code,
|
||||
vim.inspect(cmd),
|
||||
output.stderr
|
||||
)
|
||||
)
|
||||
end)
|
||||
on_complete(nil)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---@return string?
|
||||
local function get_std_lib_dir()
|
||||
if std_lib and std_lib ~= "" then
|
||||
return std_lib
|
||||
end
|
||||
|
||||
identify_go_dir({ envvar_id = "GOROOT", custom_subdir = "/src" }, function(dir)
|
||||
if dir then
|
||||
std_lib = dir
|
||||
end
|
||||
end)
|
||||
return std_lib
|
||||
end
|
||||
|
||||
---@return string?
|
||||
local function get_mod_cache_dir()
|
||||
if mod_cache and mod_cache ~= "" then
|
||||
return mod_cache
|
||||
end
|
||||
|
||||
identify_go_dir({ envvar_id = "GOMODCACHE" }, function(dir)
|
||||
if dir then
|
||||
mod_cache = dir
|
||||
end
|
||||
end)
|
||||
return mod_cache
|
||||
end
|
||||
|
||||
---@param fname string
|
||||
---@return string?
|
||||
local function get_root_dir(fname)
|
||||
if mod_cache and fname:sub(1, #mod_cache) == mod_cache then
|
||||
local clients = vim.lsp.get_clients({ name = "gopls" })
|
||||
if #clients > 0 then
|
||||
return clients[#clients].config.root_dir
|
||||
end
|
||||
end
|
||||
if std_lib and fname:sub(1, #std_lib) == std_lib then
|
||||
local clients = vim.lsp.get_clients({ name = "gopls" })
|
||||
if #clients > 0 then
|
||||
return clients[#clients].config.root_dir
|
||||
end
|
||||
end
|
||||
return vim.fs.root(fname, "go.work") or vim.fs.root(fname, "go.mod") or vim.fs.root(fname, ".git")
|
||||
end
|
||||
|
||||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "gopls" },
|
||||
filetypes = { "go", "gomod", "gowork", "gotmpl" },
|
||||
root_dir = function(bufnr, on_dir)
|
||||
local fname = vim.api.nvim_buf_get_name(bufnr)
|
||||
get_mod_cache_dir()
|
||||
get_std_lib_dir()
|
||||
-- see: https://github.com/neovim/nvim-lspconfig/issues/804
|
||||
on_dir(get_root_dir(fname))
|
||||
end,
|
||||
}
|
||||
16
nvim/lsp/html.lua
Normal file
16
nvim/lsp/html.lua
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||
capabilities.textDocument.completion.completionItem.snippetSupport = true
|
||||
|
||||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "vscode-html-language-server", "--stdio" },
|
||||
filetypes = { "html", "templ" },
|
||||
root_markers = { "package.json", ".git" },
|
||||
capabilities = capabilities,
|
||||
init_options = {
|
||||
provideFormatter = true,
|
||||
embeddedLanguages = { css = true, javascript = true },
|
||||
configurationSection = { "html", "css", "javascript" },
|
||||
},
|
||||
settings = {},
|
||||
}
|
||||
33
nvim/lsp/lua_ls.lua
Normal file
33
nvim/lsp/lua_ls.lua
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "lua-language-server" },
|
||||
filetypes = { "lua" },
|
||||
root_markers = {
|
||||
".luarc.json",
|
||||
".luarc.jsonc",
|
||||
".luacheckrc",
|
||||
".stylua.toml",
|
||||
"stylua.toml",
|
||||
"selene.toml",
|
||||
"selene.yml",
|
||||
".git",
|
||||
},
|
||||
settings = {
|
||||
Lua = {
|
||||
runtime = {
|
||||
version = "Lua 5.4",
|
||||
},
|
||||
completion = {
|
||||
enable = true,
|
||||
},
|
||||
diagnostics = {
|
||||
enable = true,
|
||||
globals = { "vim" },
|
||||
},
|
||||
workspace = {
|
||||
library = { vim.env.VIMRUNTIME },
|
||||
checkThirdParty = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
195
nvim/lsp/rust-analyzer.lua
Normal file
195
nvim/lsp/rust-analyzer.lua
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
---@brief
|
||||
---
|
||||
--- https://github.com/rust-lang/rust-analyzer
|
||||
---
|
||||
--- rust-analyzer (aka rls 2.0), a language server for Rust
|
||||
---
|
||||
---
|
||||
--- See [docs](https://rust-analyzer.github.io/book/configuration.html) for extra settings. The settings can be used like this:
|
||||
--- ```lua
|
||||
--- vim.lsp.config('rust_analyzer', {
|
||||
--- settings = {
|
||||
--- ['rust-analyzer'] = {
|
||||
--- diagnostics = {
|
||||
--- enable = false;
|
||||
--- }
|
||||
--- }
|
||||
--- }
|
||||
--- })
|
||||
--- ```
|
||||
---
|
||||
--- Note: do not set `init_options` for this LS config, it will be automatically populated by the contents of settings["rust-analyzer"] per
|
||||
--- https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26.
|
||||
|
||||
local function reload_workspace(bufnr)
|
||||
local clients = vim.lsp.get_clients({ bufnr = bufnr, name = "rust_analyzer" })
|
||||
for _, client in ipairs(clients) do
|
||||
vim.notify("Reloading Cargo Workspace")
|
||||
---@diagnostic disable-next-line:param-type-mismatch
|
||||
client:request("rust-analyzer/reloadWorkspace", nil, function(err)
|
||||
if err then
|
||||
error(tostring(err))
|
||||
end
|
||||
vim.notify("Cargo workspace reloaded")
|
||||
end, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function user_sysroot_src()
|
||||
return vim.tbl_get(vim.lsp.config["rust_analyzer"], "settings", "rust-analyzer", "cargo", "sysrootSrc")
|
||||
end
|
||||
|
||||
local function default_sysroot_src()
|
||||
local sysroot = vim.tbl_get(vim.lsp.config["rust_analyzer"], "settings", "rust-analyzer", "cargo", "sysroot")
|
||||
if not sysroot then
|
||||
local rustc = os.getenv("RUSTC") or "rustc"
|
||||
local result = vim.system({ rustc, "--print", "sysroot" }, { text = true }):wait()
|
||||
|
||||
local stdout = result.stdout
|
||||
if result.code == 0 and stdout then
|
||||
if string.sub(stdout, #stdout) == "\n" then
|
||||
if #stdout > 1 then
|
||||
sysroot = string.sub(stdout, 1, #stdout - 1)
|
||||
else
|
||||
sysroot = ""
|
||||
end
|
||||
else
|
||||
sysroot = stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return sysroot and vim.fs.joinpath(sysroot, "lib/rustlib/src/rust/library") or nil
|
||||
end
|
||||
|
||||
local function is_library(fname)
|
||||
local user_home = vim.fs.normalize(vim.env.HOME)
|
||||
local cargo_home = os.getenv("CARGO_HOME") or user_home .. "/.cargo"
|
||||
local registry = cargo_home .. "/registry/src"
|
||||
local git_registry = cargo_home .. "/git/checkouts"
|
||||
|
||||
local rustup_home = os.getenv("RUSTUP_HOME") or user_home .. "/.rustup"
|
||||
local toolchains = rustup_home .. "/toolchains"
|
||||
|
||||
local sysroot_src = user_sysroot_src() or default_sysroot_src()
|
||||
|
||||
for _, item in ipairs({ toolchains, registry, git_registry, sysroot_src }) do
|
||||
if item and vim.fs.relpath(item, fname) then
|
||||
local clients = vim.lsp.get_clients({ name = "rust_analyzer" })
|
||||
return #clients > 0 and clients[#clients].config.root_dir or nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "rust-analyzer" },
|
||||
filetypes = { "rust" },
|
||||
root_dir = function(bufnr, on_dir)
|
||||
local fname = vim.api.nvim_buf_get_name(bufnr)
|
||||
local reused_dir = is_library(fname)
|
||||
if reused_dir then
|
||||
on_dir(reused_dir)
|
||||
return
|
||||
end
|
||||
|
||||
local cargo_crate_dir = vim.fs.root(fname, { "Cargo.toml" })
|
||||
local cargo_workspace_root
|
||||
|
||||
if cargo_crate_dir == nil then
|
||||
on_dir(
|
||||
vim.fs.root(fname, { "rust-project.json" })
|
||||
or vim.fs.dirname(vim.fs.find(".git", { path = fname, upward = true })[1])
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local cmd = {
|
||||
"cargo",
|
||||
"metadata",
|
||||
"--no-deps",
|
||||
"--format-version",
|
||||
"1",
|
||||
"--manifest-path",
|
||||
cargo_crate_dir .. "/Cargo.toml",
|
||||
}
|
||||
|
||||
vim.system(cmd, { text = true }, function(output)
|
||||
if output.code == 0 then
|
||||
if output.stdout then
|
||||
local result = vim.json.decode(output.stdout)
|
||||
if result["workspace_root"] then
|
||||
cargo_workspace_root = vim.fs.normalize(result["workspace_root"])
|
||||
end
|
||||
end
|
||||
|
||||
on_dir(cargo_workspace_root or cargo_crate_dir)
|
||||
else
|
||||
vim.schedule(function()
|
||||
vim.notify(
|
||||
("[rust_analyzer] cmd failed with code %d: %s\n%s"):format(output.code, cmd, output.stderr)
|
||||
)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end,
|
||||
capabilities = {
|
||||
experimental = {
|
||||
serverStatusNotification = true,
|
||||
commands = {
|
||||
commands = {
|
||||
"rust-analyzer.showReferences",
|
||||
"rust-analyzer.runSingle",
|
||||
"rust-analyzer.debugSingle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
---@type lspconfig.settings.rust_analyzer
|
||||
settings = {
|
||||
["rust-analyzer"] = {
|
||||
lens = {
|
||||
debug = { enable = true },
|
||||
enable = true,
|
||||
implementations = { enable = true },
|
||||
references = {
|
||||
adt = { enable = true },
|
||||
enumVariant = { enable = true },
|
||||
method = { enable = true },
|
||||
trait = { enable = true },
|
||||
},
|
||||
run = { enable = true },
|
||||
updateTest = { enable = true },
|
||||
},
|
||||
},
|
||||
},
|
||||
before_init = function(init_params, config)
|
||||
-- See https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26
|
||||
if config.settings and config.settings["rust-analyzer"] then
|
||||
init_params.initializationOptions = config.settings["rust-analyzer"]
|
||||
end
|
||||
---@param command table{ title: string, command: string, arguments: any[] }
|
||||
vim.lsp.commands["rust-analyzer.runSingle"] = function(command)
|
||||
local r = command.arguments[1]
|
||||
local cmd = { "cargo", unpack(r.args.cargoArgs) }
|
||||
if r.args.executableArgs and #r.args.executableArgs > 0 then
|
||||
vim.list_extend(cmd, { "--", unpack(r.args.executableArgs) })
|
||||
end
|
||||
|
||||
local proc = vim.system(cmd, { cwd = r.args.cwd, env = r.args.environment })
|
||||
|
||||
local result = proc:wait()
|
||||
|
||||
if result.code == 0 then
|
||||
vim.notify(result.stdout, vim.log.levels.INFO)
|
||||
else
|
||||
vim.notify(result.stderr, vim.log.levels.ERROR)
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_attach = function(_, bufnr)
|
||||
vim.api.nvim_buf_create_user_command(bufnr, "LspCargoReload", function()
|
||||
reload_workspace(bufnr)
|
||||
end, { desc = "Reload current cargo workspace" })
|
||||
end,
|
||||
}
|
||||
36
nvim/lsp/tailwindcss.lua
Normal file
36
nvim/lsp/tailwindcss.lua
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "tailwindcss-language-server", "--stdio" },
|
||||
filetypes = { "html", "css", "scss", "javascriptreact", "typescriptreact", "templ" },
|
||||
root_markers = {
|
||||
"tailwind.config.js",
|
||||
"tailwind.config.cjs",
|
||||
"tailwind.config.mjs",
|
||||
"tailwind.config.ts",
|
||||
"postcss.config.js",
|
||||
"package.json",
|
||||
".git",
|
||||
},
|
||||
init_options = {
|
||||
userLanguages = {
|
||||
templ = "html",
|
||||
},
|
||||
},
|
||||
settings = {
|
||||
tailwindCSS = {
|
||||
validate = true,
|
||||
lint = {
|
||||
cssConflict = "warning",
|
||||
invalidApply = "error",
|
||||
invalidConfigPath = "error",
|
||||
invalidScreen = "error",
|
||||
invalidTailwindDirective = "error",
|
||||
invalidVariant = "error",
|
||||
recommendedVariantOrder = "warning",
|
||||
},
|
||||
includeLanguages = {
|
||||
templ = "html",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
6
nvim/lsp/templ.lua
Normal file
6
nvim/lsp/templ.lua
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---@type vim.lsp.Config
|
||||
return {
|
||||
cmd = { "templ", "lsp" },
|
||||
filetypes = { "templ" },
|
||||
root_markers = { "go.mod", "go.work", ".git" },
|
||||
}
|
||||
199
nvim/lsp/ts_ls.lua
Normal file
199
nvim/lsp/ts_ls.lua
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
---@brief
|
||||
---
|
||||
--- https://github.com/typescript-language-server/typescript-language-server
|
||||
---
|
||||
--- `ts_ls`, aka `typescript-language-server`, is a Language Server Protocol implementation for TypeScript wrapping `tsserver`. Note that `ts_ls` is not `tsserver`.
|
||||
---
|
||||
--- `typescript-language-server` depends on `typescript`. Both packages can be installed via `npm`:
|
||||
--- ```sh
|
||||
--- npm install -g typescript typescript-language-server
|
||||
--- ```
|
||||
---
|
||||
--- To configure typescript language server, add a
|
||||
--- [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) or
|
||||
--- [`jsconfig.json`](https://code.visualstudio.com/docs/languages/jsconfig) to the root of your
|
||||
--- project.
|
||||
---
|
||||
--- Here's an example that disables type checking in JavaScript files.
|
||||
---
|
||||
--- ```json
|
||||
--- {
|
||||
--- "compilerOptions": {
|
||||
--- "module": "commonjs",
|
||||
--- "target": "es6",
|
||||
--- "checkJs": false
|
||||
--- },
|
||||
--- "exclude": [
|
||||
--- "node_modules"
|
||||
--- ]
|
||||
--- }
|
||||
--- ```
|
||||
---
|
||||
--- Use the `:LspTypescriptSourceAction` command to see "whole file" ("source") code-actions such as:
|
||||
--- - organize imports
|
||||
--- - remove unused code
|
||||
---
|
||||
--- Use the `:LspTypescriptGoToSourceDefinition` command to navigate to the source definition of a symbol (e.g., jump to the original implementation instead of type definitions).
|
||||
---
|
||||
--- ### Monorepo support
|
||||
---
|
||||
--- `ts_ls` supports monorepos by default. It will automatically find the `tsconfig.json` or `jsconfig.json` corresponding to the package you are working on.
|
||||
--- This works without the need of spawning multiple instances of `ts_ls`, saving memory.
|
||||
---
|
||||
--- It is recommended to use the same version of TypeScript in all packages, and therefore have it available in your workspace root. The location of the TypeScript binary will be determined automatically, but only once.
|
||||
---
|
||||
--- Some care must be taken here to correctly infer whether a file is part of a Deno program, or a TS program that
|
||||
--- expects to run in Node or Web Browsers. This supports having a Deno module using the denols LSP as a part of a
|
||||
--- mostly-not-Deno monorepo. We do this by finding the nearest package manager lock file, and the nearest deno.json
|
||||
--- or deno.jsonc.
|
||||
---
|
||||
--- Example:
|
||||
---
|
||||
--- ```
|
||||
--- project-root
|
||||
--- +-- node_modules/...
|
||||
--- +-- package-lock.json
|
||||
--- +-- package.json
|
||||
--- +-- packages
|
||||
--- +-- deno-module
|
||||
--- | +-- deno.json
|
||||
--- | +-- package.json <-- It's normal for Deno projects to have package.json files!
|
||||
--- | +-- src
|
||||
--- | +-- index.ts <-- this is a Deno file
|
||||
--- +-- node-module
|
||||
--- +-- package.json
|
||||
--- +-- src
|
||||
--- +-- index.ts <-- a non-Deno file (ie, should use ts_ls or tsgols)
|
||||
--- ```
|
||||
---
|
||||
--- From the file being edited, we walk up to find the nearest package manager lockfile. This is PROJECT ROOT.
|
||||
--- From the file being edited, find the nearest deno.json or deno.jsonc. This is DENO ROOT.
|
||||
--- From the file being edited, find the nearest deno.lock. This is DENO LOCK ROOT
|
||||
--- If DENO LOCK ROOT is found, and PROJECT ROOT is missing or shorter, then this is a deno file, and we abort.
|
||||
--- If DENO ROOT is found, and it's longer than or equal to PROJECT ROOT, then this is a Deno file, and we abort.
|
||||
--- Otherwise, attach at PROJECT ROOT, or the cwd if not found.
|
||||
|
||||
---@type vim.lsp.Config
|
||||
return {
|
||||
init_options = { hostInfo = "neovim" },
|
||||
cmd = function(dispatchers, config)
|
||||
local cmd = "typescript-language-server"
|
||||
if (config or {}).root_dir then
|
||||
local local_cmd = vim.fs.joinpath(config.root_dir, "node_modules/.bin", cmd)
|
||||
if vim.fn.executable(local_cmd) == 1 then
|
||||
cmd = local_cmd
|
||||
end
|
||||
end
|
||||
return vim.lsp.rpc.start({ cmd, "--stdio" }, dispatchers)
|
||||
end,
|
||||
filetypes = {
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
},
|
||||
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 root_markers = { "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "bun.lockb", "bun.lock" }
|
||||
-- Give the root markers equal priority by wrapping them in a table
|
||||
root_markers = vim.fn.has("nvim-0.11.3") == 1 and { root_markers, { ".git" } }
|
||||
or vim.list_extend(root_markers, { ".git" })
|
||||
-- exclude deno
|
||||
local deno_root = vim.fs.root(bufnr, { "deno.json", "deno.jsonc" })
|
||||
local deno_lock_root = vim.fs.root(bufnr, { "deno.lock" })
|
||||
local project_root = vim.fs.root(bufnr, root_markers)
|
||||
if deno_lock_root and (not project_root or #deno_lock_root > #project_root) then
|
||||
-- deno lock is closer than package manager lock, abort
|
||||
return
|
||||
end
|
||||
if deno_root and (not project_root or #deno_root >= #project_root) then
|
||||
-- deno config is closer than or equal to package manager lock, abort
|
||||
return
|
||||
end
|
||||
-- project is standard TS, not deno
|
||||
-- We fallback to the current working directory if no project root is found
|
||||
on_dir(project_root or vim.fn.getcwd())
|
||||
end,
|
||||
handlers = {
|
||||
-- handle rename request for certain code actions like extracting functions / types
|
||||
["_typescript.rename"] = function(_, result, ctx)
|
||||
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
||||
vim.lsp.util.show_document({
|
||||
uri = result.textDocument.uri,
|
||||
range = {
|
||||
start = result.position,
|
||||
["end"] = result.position,
|
||||
},
|
||||
}, client.offset_encoding)
|
||||
vim.lsp.buf.rename()
|
||||
return vim.NIL
|
||||
end,
|
||||
},
|
||||
commands = {
|
||||
["editor.action.showReferences"] = function(command, ctx)
|
||||
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
||||
local file_uri, position, references = unpack(command.arguments)
|
||||
|
||||
local quickfix_items = vim.lsp.util.locations_to_items(references --[[@as any]], client.offset_encoding)
|
||||
vim.fn.setqflist({}, " ", {
|
||||
title = command.title,
|
||||
items = quickfix_items,
|
||||
context = {
|
||||
command = command,
|
||||
bufnr = ctx.bufnr,
|
||||
},
|
||||
})
|
||||
|
||||
vim.lsp.util.show_document({
|
||||
uri = file_uri --[[@as string]],
|
||||
range = {
|
||||
start = position --[[@as lsp.Position]],
|
||||
["end"] = position --[[@as lsp.Position]],
|
||||
},
|
||||
}, client.offset_encoding)
|
||||
---@diagnostic enable: assign-type-mismatch
|
||||
|
||||
vim.cmd("botright copen")
|
||||
end,
|
||||
},
|
||||
on_attach = function(client, bufnr)
|
||||
-- ts_ls provides `source.*` code actions that apply to the whole file. These only appear in
|
||||
-- `vim.lsp.buf.code_action()` if specified in `context.only`.
|
||||
vim.api.nvim_buf_create_user_command(bufnr, "LspTypescriptSourceAction", function()
|
||||
local source_actions = vim.tbl_filter(function(action)
|
||||
return vim.startswith(action, "source.")
|
||||
end, client.server_capabilities.codeActionProvider.codeActionKinds)
|
||||
|
||||
vim.lsp.buf.code_action({
|
||||
context = {
|
||||
only = source_actions,
|
||||
diagnostics = {},
|
||||
},
|
||||
})
|
||||
end, {})
|
||||
|
||||
-- Go to source definition command
|
||||
vim.api.nvim_buf_create_user_command(bufnr, "LspTypescriptGoToSourceDefinition", function()
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
local params = vim.lsp.util.make_position_params(win, client.offset_encoding)
|
||||
client:exec_cmd({
|
||||
command = "_typescript.goToSourceDefinition",
|
||||
title = "Go to source definition",
|
||||
arguments = { params.textDocument.uri, params.position },
|
||||
}, { bufnr = bufnr }, function(err, result)
|
||||
if err then
|
||||
vim.notify("Go to source definition failed: " .. err.message, vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
if not result or vim.tbl_isempty(result) then
|
||||
vim.notify("No source definition found", vim.log.levels.INFO)
|
||||
return
|
||||
end
|
||||
vim.lsp.util.show_document(result[1], client.offset_encoding, { focus = true })
|
||||
end)
|
||||
end, { desc = "Go to source definition" })
|
||||
end,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue