Skip to content

Commit a37d44f

Browse files
committed
feat(settings): resolver-native dep lists, mason as optional backend
Move lsp/formatter/linter/dap dep lists to resolver-native names with a tool_install_timeout knob, and guard the removed external_lsp_deps key with a migration warning. mason.lua drops the bootstrap ensure-install loop (the resolvers own installs) and attaches registry events for mid-session install hand-off; mason-nvim-dap becomes a standalone lazy spec so a provisioned session never pays Mason on the :Dap tick. vim.yml teaches selene package.searchpath.
1 parent d63f939 commit a37d44f

4 files changed

Lines changed: 118 additions & 61 deletions

File tree

lua/core/settings.lua

Lines changed: 92 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ settings["disabled_plugins"] = {}
5757
-- These settings will override the defaults during initialization.
5858
-- Parameters will auto-complete as you type.
5959
-- Example: { sky = "#04A5E5" }
60-
---@type palette[]
60+
---@type palette
61+
---@diagnostic disable-next-line: missing-fields
6162
settings["palette_overwrite"] = {}
6263

6364
-- Set the colorscheme here.
@@ -84,23 +85,19 @@ settings["external_browser"] = "chrome-cli open"
8485
---@type boolean
8586
settings["lsp_inlayhints"] = false
8687

87-
-- LSPs installed outside Mason (e.g. via system package manager).
88-
-- These will be configured but not installed by Mason.
89-
-- Key: lspconfig server name, Value: executable name to check availability.
90-
---@type table<string, string>
91-
settings["external_lsp_deps"] = {
92-
nixd = "nixd",
93-
nil_ls = "nil",
94-
shuck = "shuck", -- shell linter/formatter/LSP (Rust); installed via mise, not Mason
95-
-- dartls = "dart",
96-
}
97-
98-
-- LSPs to install during bootstrap.
88+
-- Language servers to enable, resolved discovery-first at runtime: binary on $PATH is
89+
-- used as-is; else Mason installs it when it ships a package; else an aggregated warning
90+
-- asks you to provision it. Names whose filetypes lspconfig knows resolve on that
91+
-- filetype's FIRST buffer (a late sweep classifies the rest once per session); names
92+
-- with user overrides, repo modules that override filetypes, or no filetype data
93+
-- resolve on the first file open. See `modules.utils.tools` and
94+
-- `completion/mason-lspconfig.lua`.
9995
-- Full list: https://github.com/neovim/nvim-lspconfig/tree/master/lsp
10096
---@type string[]
10197
settings["lsp_deps"] = {
10298
"bashls",
10399
"clangd",
100+
-- "dartls", -- Dart LSP (ships with the Dart SDK)
104101
"dockerls",
105102
"gh_actions_ls",
106103
-- "gitlab_ci_ls",
@@ -111,7 +108,10 @@ settings["lsp_deps"] = {
111108
"lua_ls",
112109
"marksman",
113110
"neocmake",
111+
"nil_ls", -- Nix LSP; the Nix-provisioned $PATH binary is preferred
112+
"nixd", -- Nix LSP (Rust); provisioned from Nix ($PATH)
114113
"ruff",
114+
"shuck", -- shell linter/formatter/LSP (Rust); installed via mise by choice ($PATH wins)
115115
"systemd_lsp",
116116
"terraformls",
117117
"tflint",
@@ -120,39 +120,56 @@ settings["lsp_deps"] = {
120120
"zuban",
121121
}
122122

123-
-- Formatters to install during bootstrap (Mason package names).
124-
-- These are managed by Mason and used by conform.nvim.
123+
-- Formatters to resolve when conform.nvim lazy-loads (first BufWritePre /
124+
-- :Format). conform formatter names, resolved discovery-first like lsp_deps.
125125
---@type string[]
126126
settings["formatter_deps"] = {
127127
"beautysh",
128128
"clang-format",
129-
"cmakelang",
129+
"cmake_format",
130130
"fixjson",
131131
"gofumpt",
132132
"goimports",
133133
"mdsf",
134+
"nixfmt", -- Nix formatter; prefer the $PATH binary (Nix)
134135
"prettier",
135136
"superhtml",
136137
"shellharden",
138+
"statix", -- Nix linter, its `fix` mode doubles as a conform formatter; from Nix ($PATH)
137139
"stylua",
138140
}
139141

140-
-- Linters to install during bootstrap (Mason package names).
141-
-- These are managed by Mason and used by nvim-lint.
142+
-- Linters to resolve discovery-first (nvim-lint linter names). A name mapped
143+
-- to a filetype resolves on that filetype's FIRST matching event after
144+
-- nvim-lint lazy-loads (the resolve-only FileType autocmd or a lint event) —
145+
-- nothing is installed or warned about before such a buffer opens; unmapped
146+
-- names (typos, manual-only linters) get an immediate deferred pass instead.
142147
---@type string[]
143148
settings["linter_deps"] = {
144149
"actionlint",
150+
"deadnix", -- Nix dead-code linter; prefer the $PATH binary (Nix)
145151
"hadolint",
146152
"markdownlint-cli2",
147153
"oxlint",
148154
-- "rumdl", -- markdownlint Rust rewrite; waiting for rule coverage to mature
149-
"golangci-lint",
155+
"golangcilint",
150156
"selene",
151157
"shellcheck",
158+
"shuck", -- shell linter for yaml.github `run:` blocks; installed via mise by choice
159+
"statix", -- Nix linter; prefer the $PATH binary (Nix)
152160
"systemdlint",
161+
"zsh", -- `zsh -n` syntax check via the system shell itself
153162
}
154163

155-
-- Debug Adapter Protocol (DAP) clients to install and configure during bootstrap.
164+
-- Deadline (ms) for background Mason work before the aggregated missing-tool warning
165+
-- flushes anyway. Gates each tracked install (its own window) AND the registry refresh
166+
-- wait; late completions still recover. Missing or non-positive values fall back to
167+
-- the resolver's DEFAULT_TOOL_INSTALL_TIMEOUT_MS in `modules/utils/tools.lua`.
168+
---@type number
169+
settings["tool_install_timeout"] = 300000
170+
171+
-- DAP adapters to enable (mason-nvim-dap adapter names), resolved
172+
-- discovery-first when nvim-dap lazy-loads (first :Dap* command or debug keymap).
156173
-- Supported DAPs: https://github.com/jay-babu/mason-nvim-dap.nvim/blob/main/lua/mason-nvim-dap/mappings/source.lua
157174
---@type string[]
158175
settings["dap_deps"] = {
@@ -242,4 +259,58 @@ settings["dashboard_image"] = {
242259
[[⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⠿⢿⠿⠷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀]],
243260
}
244261

245-
return require("modules.utils").extend_config(settings, "user.settings")
262+
local merged = require("modules.utils").extend_config(settings, "user.settings")
263+
264+
-- Migration guard: the discovery-first refactor removed this key; a stale
265+
-- user/settings.lua would merge it in and feed nothing — its servers would
266+
-- vanish without a word.
267+
if merged.external_lsp_deps ~= nil then
268+
-- The removed setting was a MAP of server name -> executable name, but a
269+
-- stale override can survive in any shape: classify before advising so the
270+
-- guidance never presents numeric indices as keys, never drops entries a
271+
-- half-migrated LIST residue still carries, and always names the final
272+
-- step (deleting the dead key). Neither group suppresses the other.
273+
local string_keys, list_items = {}, {}
274+
if type(merged.external_lsp_deps) == "table" then
275+
for k, v in pairs(merged.external_lsp_deps) do
276+
if type(k) == "string" then
277+
string_keys[#string_keys + 1] = k
278+
elseif type(k) == "number" and type(v) == "string" then
279+
list_items[#list_items + 1] = v
280+
end
281+
end
282+
table.sort(string_keys)
283+
table.sort(list_items)
284+
end
285+
local guidance
286+
if #string_keys > 0 and #list_items > 0 then
287+
guidance = "Move its KEYS ("
288+
.. table.concat(string_keys, ", ")
289+
.. ") AND its list entries ("
290+
.. table.concat(list_items, ", ")
291+
.. ")\n— all of them server names — into `lsp_deps`, then delete `external_lsp_deps`."
292+
elseif #string_keys > 0 then
293+
guidance = "Move its KEYS ("
294+
.. table.concat(string_keys, ", ")
295+
.. ") — the server names, not the\n"
296+
.. "executable values — into `lsp_deps`, then delete `external_lsp_deps`."
297+
elseif #list_items > 0 then
298+
guidance = "It now holds a LIST ("
299+
.. table.concat(list_items, ", ")
300+
.. ") — those are already the\n"
301+
.. "server names; move them into `lsp_deps` and delete `external_lsp_deps`."
302+
else
303+
guidance = "It is empty or not a map — delete `external_lsp_deps` from user/settings.lua."
304+
end
305+
-- (Scheduled: the notifier plugin isn't loaded this early; the default
306+
-- notify still lands in :messages.)
307+
vim.schedule(function()
308+
vim.notify(
309+
"`external_lsp_deps` was removed: non-Mason servers are now discovered\nfrom $PATH. " .. guidance,
310+
vim.log.levels.WARN,
311+
{ title = "core.settings" }
312+
)
313+
end)
314+
end
315+
316+
return merged

lua/modules/configs/completion/mason.lua

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,34 +27,14 @@ M.setup = function()
2727
},
2828
})
2929

30-
-- Ensure formatters and linters are installed (only if mason loaded)
31-
local ok, registry = pcall(require, "mason-registry")
32-
if not ok then
33-
return
34-
end
30+
-- Formatter/linter resolution lives in conform.lua and nvim-lint.lua (against their
31+
-- own registrations); Mason here is UI-only / lazy install fallback.
3532

36-
local settings = require("core.settings")
37-
local ensure_installed = vim.list_extend(vim.deepcopy(settings.formatter_deps), settings.linter_deps)
38-
39-
for _, pkg_name in ipairs(ensure_installed) do
40-
local pkg_ok, pkg = pcall(registry.get_package, pkg_name)
41-
if not pkg_ok then
42-
vim.notify(
43-
string.format("[Mason] Package '%s' not found in registry", pkg_name),
44-
vim.log.levels.WARN,
45-
{ title = "Mason" }
46-
)
47-
elseif not pkg:is_installed() then
48-
pkg:install():once("closed", function()
49-
if not pkg:is_installed() then
50-
vim.notify(
51-
string.format("[Mason] Failed to install '%s'", pkg_name),
52-
vim.log.levels.WARN,
53-
{ title = "Mason" }
54-
)
55-
end
56-
end)
57-
end
33+
-- A user-driven install (:MasonInstall / the :Mason UI) must finish any
34+
-- pending resolver hand-off even when no resolver ever loaded the registry.
35+
local ok, registry = pcall(require, "mason-registry")
36+
if ok then
37+
require("modules.utils.tools").attach_registry_events(registry)
5838
end
5939
end
6040

lua/modules/plugins/tool.lua

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,6 @@ tool["aaronhallaert/advanced-git-search.nvim"] = {
7676
},
7777
}
7878

79-
-- tool["amitds1997/remote-nvim.nvim"] = {
80-
-- lazy = true,
81-
-- version = "*",
82-
-- cmd = { "RemoteStart", "RemoteStop", "RemoteInfo", "RemoteCleanup", "RemoteConfigDel", "RemoteLog" },
83-
-- dependencies = {
84-
-- "nvim-lua/plenary.nvim",
85-
-- "MunifTanjim/nui.nvim",
86-
-- "nvim-telescope/telescope.nvim",
87-
-- },
88-
-- config = true,
89-
-- }
90-
9179
----------------------------------------------------------------------
9280
-- DAP Plugins --
9381
----------------------------------------------------------------------
@@ -106,7 +94,6 @@ tool["mfussenegger/nvim-dap"] = {
10694
},
10795
config = require("tool.dap"),
10896
dependencies = {
109-
{ "jay-babu/mason-nvim-dap.nvim" },
11097
{
11198
"rcarriga/nvim-dap-ui",
11299
dependencies = "nvim-neotest/nvim-nio",
@@ -115,6 +102,15 @@ tool["mfussenegger/nvim-dap"] = {
115102
},
116103
}
117104

105+
-- mason-nvim-dap only supplies the adapter -> package mappings; the DAP resolver
106+
-- degrades to $PATH discovery when it (or mason.nvim) is absent. Deliberately
107+
-- NOT a dependency of nvim-dap: lazy.nvim loads dependencies together with the
108+
-- parent, and a fully provisioned session must not pay Mason on the :Dap*
109+
-- tick — the DAP config requires it on demand (module loader) instead.
110+
tool["jay-babu/mason-nvim-dap.nvim"] = {
111+
lazy = true,
112+
}
113+
118114
tool["trixnz/sops.nvim"] = {
119115
lazy = false,
120116
}

vim.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,13 @@ globals:
66
any: true
77
_debugging:
88
any: true
9+
# LuaJIT ships the Lua 5.2 extension package.searchpath; the lua51 base
10+
# selene std lib doesn't know it.
11+
package.searchpath:
12+
args:
13+
- type: string
14+
- type: string
15+
- type: string
16+
required: false
17+
- type: string
18+
required: false

0 commit comments

Comments
 (0)