mirror of
https://github.com/dwinkler1/nvimConfig.git
synced 2026-08-22 17:43:13 -04:00
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.
132 lines
4.5 KiB
Lua
132 lines
4.5 KiB
Lua
local Config = require('config')
|
|
|
|
local M = {}
|
|
|
|
-- Helper function to normalize input to a list
|
|
local function normalize_filetypes_input(input)
|
|
if type(input) == "string" then
|
|
return { input }
|
|
elseif type(input) == "table" then
|
|
return input
|
|
else
|
|
vim.notify("get_recent_files_by_ft_or_ext: Invalid input type for filetypes", vim.log.levels.ERROR)
|
|
return nil
|
|
end
|
|
end
|
|
|
|
-- Helper function to check if a file matches any target filetype
|
|
local function matches_target_filetype(file_path, file_ext, detected_ft, target_ft_map)
|
|
for target_ft in pairs(target_ft_map) do
|
|
if file_ext:lower() == target_ft:lower() or
|
|
(detected_ft and detected_ft == target_ft) then
|
|
return target_ft
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Helper function to safely detect filetype
|
|
local function detect_filetype(file_path)
|
|
local success, ft_match_fn = pcall(function() return vim.filetype.match end)
|
|
if not (success and type(ft_match_fn) == "function") then
|
|
return nil
|
|
end
|
|
|
|
local ok, result = pcall(ft_match_fn, { filename = file_path })
|
|
return ok and type(result) == "string" and result ~= "" and result or nil
|
|
end
|
|
|
|
-- Helper function to capitalize first letter
|
|
local function capitalize_first(str)
|
|
return str:sub(1, 1):upper() .. str:sub(2)
|
|
end
|
|
|
|
function M.get_recent_files_by_ft_or_ext(target_filetypes_input)
|
|
local target_filetypes_list = normalize_filetypes_input(target_filetypes_input)
|
|
if not target_filetypes_list or #target_filetypes_list == 0 then
|
|
return {}
|
|
end
|
|
|
|
-- Create lookup map for O(1) filetype checking
|
|
local target_ft_map = {}
|
|
for _, ft in ipairs(target_filetypes_list) do
|
|
target_ft_map[ft] = true
|
|
end
|
|
|
|
local oldfiles = vim.v.oldfiles
|
|
if not oldfiles or #oldfiles == 0 then
|
|
return {}
|
|
end
|
|
|
|
local cwd = vim.fn.getcwd()
|
|
local fnamemodify = vim.fn.fnamemodify
|
|
local filereadable = vim.fn.filereadable
|
|
local getftime = vim.fn.getftime
|
|
|
|
-- Track most recent file for each target filetype
|
|
local most_recent_files = {}
|
|
for _, ft in ipairs(target_filetypes_list) do
|
|
most_recent_files[ft] = { file = nil, time = 0 }
|
|
end
|
|
|
|
local processed_paths = {}
|
|
|
|
for _, file_path in ipairs(oldfiles) do
|
|
local full_path = fnamemodify(file_path, ':p')
|
|
|
|
-- Skip if already processed or invalid
|
|
if processed_paths[full_path] or
|
|
filereadable(full_path) ~= 1 or
|
|
not full_path:find(cwd, 1, true) then
|
|
goto continue
|
|
end
|
|
|
|
processed_paths[full_path] = true
|
|
|
|
local file_ext = fnamemodify(full_path, ':e')
|
|
local detected_ft = detect_filetype(full_path)
|
|
local matched_ft = matches_target_filetype(full_path, file_ext, detected_ft, target_ft_map)
|
|
|
|
if matched_ft then
|
|
local mod_time = getftime(full_path)
|
|
if mod_time > most_recent_files[matched_ft].time then
|
|
most_recent_files[matched_ft] = { file = full_path, time = mod_time }
|
|
end
|
|
end
|
|
|
|
::continue::
|
|
end
|
|
|
|
-- Build result items
|
|
local result_items = {}
|
|
for ft, data in pairs(most_recent_files) do
|
|
if data.file then
|
|
local filename = fnamemodify(data.file, ':t')
|
|
local relative_path = fnamemodify(data.file, ':~:.')
|
|
|
|
table.insert(result_items, {
|
|
action = function() Config.edit(data.file) end,
|
|
name = string.format('%s (%s)', filename, relative_path),
|
|
section = 'Recent ' .. capitalize_first(ft),
|
|
})
|
|
end
|
|
end
|
|
|
|
return result_items
|
|
end
|
|
|
|
M.footer_text = (function()
|
|
return [[
|
|
$$$$$$$\ $$\ $$\ $$\ $$\ $$\ $$$$$$$\ $$\ $$\ $$\ $$\
|
|
$$ __$$\ \__| $$ |$ | $$ |$$ |$$ __$$\ $$ | $$ | $$ |\__|
|
|
$$ | $$ | $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$ |\_/$$$$$$$\ $$ /$$ / $$ | $$ | $$$$$$\ $$$$$$\ $$$$$$\ $$ | $$ |$$\ $$$$$$\$$$$\
|
|
$$ | $$ | \____$$\ $$ __$$\ $$ |$$ __$$\ $$ | $$ _____| $$ /$$ / $$ | $$ | \____$$\\_$$ _| \____$$\\$$\ $$ |$$ |$$ _$$ _$$\
|
|
$$ | $$ | $$$$$$$ |$$ | $$ |$$ |$$$$$$$$ |$$ | \$$$$$$\ $$ /$$ / $$ | $$ | $$$$$$$ | $$ | $$$$$$$ |\$$\$$ / $$ |$$ / $$ / $$ |
|
|
$$ | $$ |$$ __$$ |$$ | $$ |$$ |$$ ____|$$ | \____$$\ $$ /$$ / $$ | $$ |$$ __$$ | $$ |$$\ $$ __$$ | \$$$ / $$ |$$ | $$ | $$ |
|
|
$$$$$$$ |\$$$$$$$ |$$ | $$ |$$ |\$$$$$$$\ $$ | $$$$$$$ |$$ /$$ / $$$$$$$ |\$$$$$$$ | \$$$$ |\$$$$$$$ | \$ / $$ |$$ | $$ | $$ |
|
|
\_______/ \_______|\__| \__|\__| \_______|\__| \_______/ \__/ \__/ \_______/ \_______| \____/ \_______| \_/ \__|\__| \__| \__|
|
|
]]
|
|
end
|
|
)
|
|
|
|
Config.startup = M
|