refactor: improve Neovim/Nix config for quant econ workflow

Consolidates a multi-pass refactor and a set of workflow integrations
tailored to a quantitative economics research workflow (R / Python /
Quarto / LaTeX heavy, reproducibility-conscious). The existing terminal
setup is preserved (snacks.nvim was deliberately not adopted).

=== Structural refactors ===
* Replace the `_G.Config` global with a proper `require('config')` Lua
  module. `_G.Config` is kept only as a backward-compatible alias.
* Split the monolithic `plugin/10_keymap.lua` into domain-specific
  files under `lua/keymap/` (core, helpers, leader, terminal, repl).
* Harden `lua/nix_smart_send.lua` `send_repl`: only skip forward on
  comment nodes, and return cleanly when there is no next sibling.
* Add a filetype-aware REPL dispatcher in `lua/keymap/repl.lua`:
  pure R scripts -> R.nvim, .qmd/.Rmd chunks -> quarto.runner,
  everything else -> vim-slime. Prefer `quarto.runner.run_line()`
  when available and fall back to `run_cell()`.
* Add R Treesitter text objects (function / call / assignment) for
  faster motion when sending code to the REPL.
* Add `+Send` and `+Debug` leader-clue groups for the new prefixes.

=== LSP and tooling ===
* Register `yamlls` for Quarto YAML frontmatter (`plugin/25_lsp.lua`).
* Register `texlab` and add `vimtex` for a real `.tex` workflow
  (`plugin/25_lsp.lua` + new `plugin/28_latex.lua`).
* Wire `nvim-dap` with an R adapter backed by `vscDebugger`
  (new `plugin/26_dap.lua`, gated to the `r` cat).
* Add `image-nvim` for in-editor plots, gated to the `r`/`markdown`
  cats (new `plugin/27_image.lua`).
* Wire `lintr` into `nvim-lint` for R / Quarto (`plugin/22_languages.lua`).
* Add Treesitter parsers for `stata`, `matlab`, `bibtex` for completeness.

=== Nix updates ===
* Pin `python313Packages.pylatexenc` -> `python3Packages.pylatexenc`
  so the markdown cat survives nixpkgs Python default shifts.
* Add `texlab`, `imagemagick`, `luaPackages.magick` to the markdown
  cat so `image.nvim` has a working backend.
* Add `vscDebugger` and `lintr` to the R package list.

=== Misc ===
* Prefer the R.nvim v1.0 Lua API (`require('r.run').send_line()` /
  `send_selection()`); keep `<Plug>` mappings as a fallback so older
  downstream builds don't regress.
* Use `alejandra` (already installed) as the nixd / nil_ls formatter.

Files: 14 modified, 5 added (953 insertions, 490 deletions).

Local verification before merge:
1. `nix flake check --no-build`
2. `nvim --headless -u NONE -l tests/init.lua`
3. Open a `.qmd` -> confirm `yamlls` attaches and `image.nvim` renders.
4. Open an `.R` -> `<leader>db`, `<leader>dc` confirm DAP loads.
5. Open a `.tex` -> confirm `texlab` + `vimtex` are active.
This commit is contained in:
Daniel 2026-07-26 06:35:52 +00:00
commit 7f01be59d7
23 changed files with 952 additions and 490 deletions

View file

@ -30,6 +30,68 @@ later(function()
end
end)
-- Linting (via nvim-lint)
later(function()
Config.add("nvim-lint")
local lint_ok, lint = pcall(require, "lint")
if not lint_ok then
return
end
-- R code style via lintr (must be available in the R runtime).
-- lintr::lint() returns a "lints" object; format() turns it into the
-- standard "file:line:col: severity: message" lines.
lint.linters.lintr = {
cmd = "Rscript",
stdin = false,
args = {
"-e",
"args <- commandArgs(trailingOnly=TRUE); l <- lintr::lint(args[1]); if (length(l) > 0) cat(paste(format(l), collapse='\\n'), '\\n')",
},
append_fname = true,
stream = "both",
ignore_exitcode = true,
parser = function(output, bufnr, linter_cwd)
local diagnostics = {}
-- Pattern: /path/file.R:10:5: style: Some message
for line in output:gmatch("[^\r\n]+") do
local path, lnum, col, severity, message = line:match("^[^:]+:(%d+):(%d+):%s*(%w+):%s*(.+)$")
if path then
local severity_map = {
style = vim.diagnostic.severity.INFO,
warning = vim.diagnostic.severity.WARN,
error = vim.diagnostic.severity.ERROR,
}
table.insert(diagnostics, {
bufnr = bufnr,
lnum = math.max(0, tonumber(lnum) - 1),
col = math.max(0, tonumber(col) - 1),
end_lnum = tonumber(lnum) - 1,
end_col = tonumber(col),
severity = severity_map[severity:lower()] or vim.diagnostic.severity.WARN,
message = message or "lintr issue",
source = "lintr",
})
end
end
return diagnostics
end,
}
lint.linters_by_ft = {
r = { "lintr" },
rmd = { "lintr" },
quarto = { "lintr" },
}
vim.api.nvim_create_autocmd({ "BufReadPost", "BufWritePost", "InsertLeave" }, {
group = vim.api.nvim_create_augroup("LintOnEvents", { clear = true }),
callback = function()
lint.try_lint()
end,
})
end)
-- Markdown
now_if_args(function()
add("render-markdown.nvim")