Skip to content

feat: discovery-first tool resolution (make Mason optional)#21

Open
charliie-dev wants to merge 5 commits into
mainfrom
feat/discovery-first-tooling
Open

feat: discovery-first tool resolution (make Mason optional)#21
charliie-dev wants to merge 5 commits into
mainfrom
feat/discovery-first-tooling

Conversation

@charliie-dev

@charliie-dev charliie-dev commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Feasibility POC — RFC ayamir/nvimdots#1293

Implements the "separate tool discovery from tool installation" idea from this comment: Mason becomes one installer backend, not a hard requirement. On FreeBSD/NixOS/etc. — where Mason's prebuilt binaries don't run — system-provided tools are used directly, with no manual symlinking and no startup notification spam.

No new toggle, no OS detection. Behaviour is driven purely by availability.

Resolution model (per tool)

0. unrecognized name ─────────────────► "fix your config" guidance (never installs)
1. binary on $PATH (system / Mason) ──► configure now, same tick
2. Mason ships a package ─────────────► install in background, configure on completion
3. self-validating local config ──────► configure now; its error() joins the warning
4. neither ───────────────────────────► aggregate into ONE "please install" warning

Applied to LSP, DAP, formatters, and linters through one shared loop (modules/utils/tools.lua). Everything resolvable without an install registers synchronously (lazy-load trigger replay still works); only installs defer.

Changes

Area File What
Helper modules/utils/tools.lua (new) Shared resolution loop: $PATH + Mason-bin-dir probe, install-then-configure tracking, bin→package reverse lookup, per-subsystem warning aggregator (one message, not N) with a configurable deadline (settings.tool_install_timeout)
LSP mason-lspconfig.lua, lsp.lua Cascade resolver driven by lsp_deps; external_lsp_deps merged into lsp_deps; function-cmd servers (e.g. jsonls) probed via the Mason package's declared bins; off-$PATH cmds rewritten to absolute paths before vim.lsp.enable() (covers Mason PATH = "skip")
Formatter/Linter conform.lua, nvim-lint.lua, mason.lua Deps resolved against each plugin's own runtime registry (the ground truth), Mason package reverse-looked-up by binary; mason.lua's ensure_installed loop removed (UI-only now)
DAP dap/init.lua, dap/clients/* Self-driven over dap_deps; mason-nvim-dap's private mappings guarded, with an adapter_meta ground-truth fallback (delvedlv, pythondebugpy-adapter); client configs self-validate and resolve binaries lazily on the spawn path, so remote attach works without local binaries

settings.lua

  • lsp_deps: absorbs nil_ls, nixd, and shuck — one list, every entry is wanted. Package-less servers (nixd, shuck) are enabled from their $PATH binary; when absent they join the aggregated warning (the fix is provisioning via home-manager/mise, not Mason)
  • external_lsp_deps: removed
  • formatter_deps / linter_deps: entries are now the subsystem's tool names, matching lsp_deps semantics (cmakelangcmake_format, golangci-lintgolangcilint); the Mason package name is derived, not configured
  • tool_install_timeout (new): deadline before the aggregated warning is flushed despite unsettled installs

Verification

  • CI: stylua + selene + nix flake check, green.
  • Headless smoke: config loads and attaches lua_ls via the new resolver, no errors.
  • Copilot review loop run to convergence (all threads resolved, latest round: no new comments).

Known POC limitations

  • DAP $PATH detection leans on the Mason package's declared binaries (nvim-dap has no uniform command registry); adapters outside adapter_meta/mason-nvim-dap mappings need a client config.
  • Not yet tested on an actual BSD/NixOS host — that's the next feasibility step.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a discovery-first tool resolution model so that tools already available on $PATH (system-provided or Mason-provided) are used directly, while Mason becomes an optional installer backend rather than a hard runtime gate. This supports environments like NixOS/FreeBSD where Mason-installed binaries may be unavailable or undesirable.

Changes:

  • Add shared helpers for $PATH probing, Mason spec.bin extraction, and aggregated “missing tools” warnings.
  • Rework LSP and DAP setup to be driven by desired dependency lists with discovery-first resolution and deferred installs.
  • Update Mason bootstrap for formatters/linters to only install when the tool isn’t already on $PATH, aggregating missing-tool warnings.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lua/modules/utils/tools.lua New helper module for executable discovery + aggregated missing-tool warnings.
lua/modules/configs/tool/dap/init.lua Switch DAP setup to discovery-first resolution instead of mason-nvim-dap’s installed-only handler gating.
lua/modules/configs/completion/servers/shuck.lua Update server comment to reflect the new discovery-first resolution model.
lua/modules/configs/completion/mason.lua Only install formatter/linter packages when not already available on $PATH; aggregate missing warnings.
lua/modules/configs/completion/mason-lspconfig.lua Drive LSP setup via discovery-first resolution + deferred installs, using mason-lspconfig mappings.
lua/modules/configs/completion/lsp.lua Remove external_lsp_deps wiring; rely on centralized discovery-first LSP setup.
lua/core/settings.lua Collapse external/system LSP list into unified lsp_deps list with discovery-first semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lua/modules/configs/completion/mason-lspconfig.lua Outdated
Comment thread lua/core/settings.lua Outdated
Comment thread lua/modules/configs/tool/dap/init.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread lua/modules/configs/completion/mason-lspconfig.lua Outdated
Comment thread lua/modules/configs/tool/dap/init.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/configs/tool/dap/init.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread lua/core/settings.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread lua/modules/utils/tools.lua Outdated
Comment thread lua/modules/configs/tool/dap/init.lua Outdated
Comment thread lua/modules/configs/tool/dap/init.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Comment thread lua/modules/configs/completion/mason-lspconfig.lua Outdated
Comment thread lua/modules/configs/completion/mason.lua Outdated
Comment thread lua/core/settings.lua Outdated
Comment thread lua/modules/utils/tools.lua Outdated
Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/utils/tools.lua

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread lua/modules/configs/tool/dap/clients/python.lua Outdated
Comment thread lua/modules/configs/tool/dap/clients/delve.lua
Comment thread lua/modules/configs/completion/mason.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread lua/modules/utils/tools.lua Outdated
Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread lua/modules/configs/completion/mason-lspconfig.lua Outdated
Comment thread lua/modules/configs/tool/dap/init.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Comment thread lua/modules/utils/tools.lua
Comment thread lua/modules/configs/tool/dap/clients/python.lua Outdated
Comment thread lua/modules/configs/tool/dap/clients/python.lua
Comment thread lua/modules/configs/tool/dap/clients/delve.lua

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Comment thread lua/modules/utils/tools.lua
Comment thread lua/modules/configs/tool/dap/clients/python.lua Outdated
Comment thread lua/modules/configs/tool/dap/clients/python.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Comment thread lua/modules/utils/tools.lua
Comment thread lua/modules/configs/completion/mason.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/configs/completion/mason.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/configs/tool/dap/init.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread lua/modules/utils/tools.lua
Comment thread lua/modules/configs/completion/mason-lspconfig.lua

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

lua/modules/configs/completion/mason-lspconfig.lua:79

  • mason_lsp_handler always requires the repo preset module (completion.servers.<name>) even when a user override exists and is a function. Some preset modules do non-trivial work at load (e.g. vim.fn.expand in lua_ls.lua), so this adds avoidable overhead and can surface preset-load errors that the user override wouldn’t otherwise hit. Load the preset lazily only when needed (no user override, or when merging table-based overrides).
		local modules = server_modules(lsp_name)
		local ok, custom_handler = pcall(require, modules[1])
		local default_ok, default_handler = pcall(require, modules[2])
		-- Use preset if there is no user definition
		if not ok then
			ok, custom_handler = default_ok, default_handler
		end

Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Comment thread lua/modules/utils/tools.lua Outdated
Comment thread lua/modules/utils/tools.lua
Comment thread lua/modules/utils/tools.lua
Comment thread lua/modules/configs/completion/mason-lspconfig.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment thread lua/modules/configs/completion/mason-lspconfig.lua Outdated
Comment thread lua/modules/configs/completion/conform.lua Outdated
Comment thread lua/modules/configs/completion/nvim-lint.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comment thread lua/modules/utils/tools.lua Outdated
Comment thread lua/modules/plugins/tool.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comment thread lua/core/settings.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment thread lua/modules/configs/tool/dap/init.lua
Comment thread lua/modules/configs/completion/mason-lspconfig.lua
Comment thread lua/core/settings.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comment thread lua/core/settings.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread lua/modules/configs/completion/mason-lspconfig.lua
Comment thread lua/modules/utils/tools.lua Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Shared resolution loop for LSP/DAP/formatter/linter deps: probe $PATH and
Mason's bin dir first, fall back to a Mason install, and aggregate every
unresolved tool into one warning with a configurable deadline
(settings.tool_install_timeout). Teach selene about package.searchpath,
which module_path uses.
Fold external_lsp_deps into a single lsp_deps list resolved via the shared
loop: probe manual specs and lspconfig cmds for binaries, install via Mason
when a package exists, rewrite off-$PATH cmds before enabling, and route
typos to the unknown-name bucket. Registration stays synchronous; only
installs defer.
conform.nvim and nvim-lint resolve their own deps against their runtime
registries (the ground truth), reverse-looking-up Mason packages by binary;
mason.lua's ensure_installed loop is gone. nvim-lint's probe defers off the
first BufReadPost and re-lints after late installs; conform stays synchronous
so the off-$PATH rewrite lands before the first format-on-save.
Adapters resolve via the shared loop, with mason-nvim-dap's mappings guarded
as private internals and an adapter_meta ground-truth fallback (delve -> dlv,
python -> debugpy-adapter). Client configs self-validate and resolve their
binaries lazily on the spawn path, so remote attach works without them.
The built-in v2 entry's fileMatch overlaps the v3 schema added via extra,
and schemastore's replace can't rename v2 -> v3.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants