Repository navigation
Expand file tree
/
Copy pathlsp.lua
More file actions
369 lines (351 loc) · 11.7 KB
/
Copy pathlsp.lua
File metadata and controls
369 lines (351 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
-- package manager by configurations
local mason_lspconfig = require("mason-lspconfig")
mason_lspconfig.setup({
ensure_installed = {
"lua_ls",
"rust_analyzer",
"jsonls",
"ruff",
"basedpyright",
"eslint",
"ts_ls",
"yamlls",
"clangd",
},
-- mason auto-enables every installed server by default. Exclude the Python
-- type checkers so they aren't force-enabled behind our backs — the
-- conditional vim.lsp.enable() below picks exactly one (pyrefly when the
-- venv provides it, else basedpyright).
automatic_enable = { exclude = { "basedpyright", "pyrefly" } },
})
-- -- despite changes to lsp in 0.11, this is still needed.
-- mason_lspconfig.setup_handlers {
-- -- The first entry (without a key) will be the default handler
-- -- and will be called for each installed server that doesn't have
-- -- a dedicated handler.
-- function(server_name) -- default handler (optional)
-- require("lspconfig")[server_name].setup {}
-- end,
--
-- }
-- LSP attach and capabilities. blink.cmp supplies the completion capabilities
-- (replaces cmp_nvim_lsp.default_capabilities); get_lsp_capabilities() already
-- merges Neovim's defaults.
local lsp_capabilities = require("blink.cmp").get_lsp_capabilities()
-- Load nvim-lspconfig for the side effect: its bundled lsp/<name>.lua configs
-- (cmd, root_markers, filetypes) land on the runtimepath so vim.lsp.enable()
-- can pick them up. Path joining now uses native vim.fs.joinpath — the old
-- lspconfig.util.path helper is deprecated.
require("lspconfig")
-- Locate a Python virtualenv even when it isn't activated: prefer an explicit
-- $VIRTUAL_ENV, otherwise walk up from the current file (or cwd) for a `.venv`
-- directory (the uv / common layout). This is why pyrefly used to stay off —
-- launching nvim without activating the venv hid the project-local pyrefly.
local function find_venv()
if vim.env.VIRTUAL_ENV and vim.env.VIRTUAL_ENV ~= "" then
return vim.env.VIRTUAL_ENV
end
local from = vim.fn.expand("%:p:h")
if from == "" then
from = vim.fn.getcwd()
end
return vim.fs.find(".venv", { path = from, upward = true, type = "directory" })[1]
end
local function get_python_path()
local venv = find_venv()
if venv then
return vim.fs.joinpath(venv, "bin", "python")
end
-- Fallback to system Python.
return vim.fn.exepath("python3") or vim.fn.exepath("python") or "python"
end
-- Hover/signature/float borders are set globally via `vim.o.winborder` in
-- init.lua (the old `vim.lsp.handlers[...] = vim.lsp.with(...)` API is deprecated).
vim.lsp.config["lua_ls"] = {
capabilities = lsp_capabilities,
filetypes = { "lua" },
settings = {
Lua = {
workspace = { checkThirdParty = false },
telemetry = { enable = false },
diagnostics = { globals = { "vim" } }, -- Get the language server to recognize the `vim` global
completion = {
callSnippets = "Both", -- "Disable", "Replace"
displayContext = 6,
},
-- hint.enable -> hint = { enable … }
hint = {
enable = true,
arrayIndex = "Enable",
setType = true,
},
},
},
}
vim.lsp.config["yamlls"] = {
capabilities = lsp_capabilities,
filetypes = { "yaml", "yaml.docker-compose", "yaml.gitlab" },
settings = {
-- redhat.telemetry is a top-level yamlls setting, NOT under `yaml`.
-- It previously sat at array index [1] inside `yaml`, making that
-- table a mixed list+dict — which nvim_exec_autocmds can't serialize
-- for the LspNotify autocmd ("Cannot convert given Lua table").
redhat = {
telemetry = {
enabled = false,
},
},
yaml = {
-- Use SchemaStore.nvim's catalog instead of yamlls' built-in fetcher
-- (schemaStore disabled so the two don't both inject schemas).
schemaStore = { enable = false, url = "" },
schemas = require("schemastore").yaml.schemas(),
filetypes = { "yaml", "yaml.docker-compose", "yaml.gitlab", "yml" },
single_file_support = true,
},
},
}
vim.lsp.config["jsonls"] = {
capabilities = lsp_capabilities,
filetypes = { "json", "jsonc" },
settings = {
json = {
-- Schema catalog from SchemaStore.nvim (package.json, tsconfig, etc).
schemas = require("schemastore").json.schemas(),
validate = { enable = true },
},
},
}
-- ts_ls: `filetypes` and `init_options` are top-level config keys (init_options
-- is sent at `initialize` — that's where the @vue/typescript-plugin must go, or
-- it never loads). inlayHints/format live under settings.typescript /
-- settings.javascript, each prefixed `includeInlay*` — the old flat layout under
-- `settings` was silently ignored by ts_ls.
-- https://github.com/typescript-language-server/typescript-language-server/blob/master/docs/configuration.md
local ts_inlay_hints = {
includeInlayParameterNameHints = "all",
includeInlayParameterNameHintsWhenArgumentMatchesName = true,
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
includeInlayPropertyDeclarationTypeHints = true,
includeInlayFunctionLikeReturnTypeHints = true,
includeInlayEnumMemberValueHints = true,
}
local ts_format = {
baseIndentSize = 2,
indentSize = 1,
indentStyle = "Smart",
insertSpaceAfterCommaDelimiter = true,
insertSpaceAfterConstructor = true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions = true,
insertSpaceAfterKeywordsInControlFlowStatements = true,
semicolons = "Insert",
}
vim.lsp.config["ts_ls"] = {
capabilities = lsp_capabilities,
filetypes = {
"javascript",
"javascriptreact",
"javascript.jsx",
"typescript",
"typescriptreact",
"typescript.tsx",
"vue",
},
init_options = {
plugins = {
{
name = "@vue/typescript-plugin",
location = "/usr/local/lib/node_modules/@vue/typescript-plugin",
languages = { "javascript", "typescript", "vue" },
},
},
hostInfo = "neovim",
},
settings = {
typescript = { inlayHints = ts_inlay_hints, format = ts_format },
javascript = { inlayHints = ts_inlay_hints, format = ts_format },
},
}
vim.lsp.config["eslint"] = {
capabilities = lsp_capabilities,
settings = {
bin = "eslint", -- or `eslint_d`
-- useFlatConfig is omitted: current eslint-lsp auto-detects flat config
-- (eslint.config.js), which is the ESLint 9 default. Force it only to
-- pin legacy .eslintrc behaviour.
code_actions = {
enable = true,
apply_on_save = {
enable = true,
types = { "directive", "problem", "suggestion", "layout" },
},
disable_rule_comment = {
enable = true,
location = "separate_line", -- or `same_line`
},
},
diagnostics = {
enable = true,
report_unused_disable_directives = false,
run_on = "type", -- or `save`
},
completion = {
enable = true,
},
},
}
vim.lsp.config["ruff"] = {
-- disabled code actions for ruff since we want to use pyright
capabilities = lsp_capabilities,
filetypes = { "python" },
settings = {
code_actions = {
enable = false,
},
},
}
-- vim.api.nvim_create_user_command(
-- 'Ruff',
-- function()
-- vim.lsp.buf.code_action {
-- context = {
-- only = { 'source.fixAll.ruff' }
-- },
-- apply = true,
-- }
-- vim.lsp.buf.format { async = true }
-- end,
-- { desc = "Reformat python with ruff" }
-- )
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("lsp_attach_disable_ruff_hover", { clear = true }),
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
if client == nil then
return
end
if client.name == "ruff" then
-- Disable hover in favor of Pyright
client.server_capabilities.hoverProvider = false
end
end,
desc = "LSP: Disable hover capability from Ruff",
})
vim.lsp.config["basedpyright"] = {
capabilities = lsp_capabilities,
filetypes = { "python", "jupyter", "ipynb", "py", "pyc" },
before_init = function(_, config)
config.settings.python.pythonPath = get_python_path()
end,
settings = {
basedpyright = {
-- Using Ruff's import organizer
disableOrganizeImports = true,
-- https://docs.basedpyright.com/v1.21.0/configuration/language-server-settings/
analysis = {
autoImportCompletions = true,
-- Ignore all files for analysis to exclusively use Ruff for linting
--ignore = { '*' },
userFileIndexingLimit = 5000,
autoIndent = true,
autoSearchPaths = true,
autoFormatStrings = true,
diagnosticMode = "openFilesOnly",
inlayHints = {
functionReturnTypes = true,
genericTypes = true,
pytestParameters = true,
callArgumentNames = true,
},
typeCheckingMode = "standard",
useLibraryCodeForTypes = true,
},
},
-- Kept (empty): before_init writes settings.python.pythonPath into it.
python = {},
},
}
-- Resolve a binary from the project virtualenv when it actually provides it,
-- else fall back to PATH. Mirrors get_python_path()'s venv-first assumption.
local function get_venv_bin(name)
local venv = find_venv()
if venv then
local candidate = vim.fs.joinpath(venv, "bin", name)
if vim.fn.executable(candidate) == 1 then
return candidate
end
end
local found = vim.fn.exepath(name)
return found ~= "" and found or name
end
-- Resolved once at startup (from the launch-time venv/cwd), reused for both the
-- pyrefly cmd and the enable-gate below.
local pyrefly_bin = get_venv_bin("pyrefly")
-- pyrefly — fast Rust-based Python type checker. filetypes/root_markers come
-- from nvim-lspconfig's bundled config; we point cmd at the project venv's
-- pyrefly (installed per-project, e.g. `uv add --dev pyrefly`) and add
-- completion caps + inlay hints.
-- NOTE: overlaps with basedpyright (both type-check Python) — expect duplicate
-- diagnostics until one is disabled.
vim.lsp.config["pyrefly"] = {
cmd = { pyrefly_bin, "lsp" },
capabilities = lsp_capabilities,
-- pyrefly reads inlay-hint config from workspace/configuration (i.e. nvim's
-- `settings`), under a top-level `analysis.inlayHints`. variableTypes /
-- functionReturnTypes default true; callArgumentNames defaults "off".
settings = {
analysis = {
inlayHints = {
variableTypes = true,
functionReturnTypes = true,
callArgumentNames = "all", -- "all" | "partial" | "off"
pytestParameters = true,
},
},
},
}
-- Python type checker: pyrefly is the default and is always enabled.
-- basedpyright runs ADDITIONALLY only when a project explicitly configures it
-- (a pyrightconfig.json, or a [tool.basedpyright]/[tool.pyright] table in
-- pyproject.toml). When both run, expect overlapping diagnostics/hover.
local function basedpyright_configured()
local root = vim.fn.getcwd()
if vim.fn.filereadable(root .. "/pyrightconfig.json") == 1 then
return true
end
local pyproject = root .. "/pyproject.toml"
if vim.fn.filereadable(pyproject) == 1 then
local content = table.concat(vim.fn.readfile(pyproject), "\n")
if content:match("%[tool%.basedpyright%]") or content:match("%[tool%.pyright%]") then
return true
end
end
return false
end
local servers = { "lua_ls", "ruff", "ts_ls", "jsonls", "yamlls", "eslint", "clangd" }
-- pyrefly is the default Python type checker — enable it whenever its binary is
-- resolvable. If it isn't, warn once instead of failing silently per-buffer
-- (lspconfig's pyrefly config notifies on every failed spawn otherwise).
if vim.fn.executable(pyrefly_bin) == 1 then
table.insert(servers, "pyrefly")
else
vim.schedule(function()
vim.notify(
("pyrefly not found (%s) — Python type-checking via pyrefly is off. Install it, e.g. `uv add --dev pyrefly`."):format(
pyrefly_bin
),
vim.log.levels.WARN
)
end)
end
if basedpyright_configured() then
table.insert(servers, "basedpyright")
end
vim.lsp.enable(servers)
-- Format-on-save is owned by conform.nvim (lua/plugins/formatting/conform.lua),
-- which runs the attached LSP formatter via lsp_format = "last". The old
-- bespoke BufWritePre autocmd lived here.
vim.api.nvim_create_autocmd("LspAttach", {
callback = require("config.mappings.lsp").on_attach,
})