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.
484 lines
14 KiB
Lua
484 lines
14 KiB
Lua
local Config = require('config')
|
|
local add = Config.add
|
|
local later = MiniDeps.later
|
|
local now = MiniDeps.now
|
|
local now_if_args = Config.now_if_args
|
|
|
|
-- Constants
|
|
local BLINK_VERSION = "v1.10.2"
|
|
|
|
-- Plugin sources configuration
|
|
local PLUGIN_SOURCES = {
|
|
"hrsh7th/cmp-cmdline",
|
|
"xzbdmw/colorful-menu.nvim",
|
|
"zbirenbaum/copilot.lua",
|
|
"jmbuhr/cmp-pandoc-references",
|
|
"fang2hou/blink-copilot",
|
|
"olimorris/codecompanion.nvim"
|
|
}
|
|
|
|
local PLUGIN_ADDS = {
|
|
"cmp-cmdline",
|
|
"blink.compat",
|
|
"colorful-menu.nvim",
|
|
"cmp-pandoc-references",
|
|
}
|
|
|
|
-- Helper functions
|
|
local function create_system_prompt(role_description)
|
|
return function(context)
|
|
local lang = context.filetype or "programmer"
|
|
return "I want you to act as a senior " .. lang .. " developer. " .. role_description
|
|
end
|
|
end
|
|
|
|
local function get_code_block(context)
|
|
local text = require("codecompanion.helpers.code").get_code(context.start_line, context.end_line)
|
|
return "```" .. context.filetype .. "\n" .. text .. "\n```"
|
|
end
|
|
|
|
local function get_mini_icons_highlight(ctx)
|
|
local _, hl, _ = require("mini.icons").get("lsp", ctx.kind)
|
|
return hl
|
|
end
|
|
|
|
local function get_blink_fuzzy_setting()
|
|
local setting = {
|
|
sorts = { "exact", "score", "sort_text" }
|
|
}
|
|
|
|
if not Config.isNixCats then
|
|
setting.prebuilt_binaries = { force_version = BLINK_VERSION }
|
|
end
|
|
|
|
return setting
|
|
end
|
|
|
|
-- Plugin loading
|
|
if not Config.isNixCats then
|
|
local add = MiniDeps.add
|
|
|
|
now_if_args(function()
|
|
add({
|
|
source = "saghen/blink.cmp",
|
|
depends = { "rafamadriz/friendly-snippets" },
|
|
checkout = BLINK_VERSION,
|
|
})
|
|
end)
|
|
|
|
later(function()
|
|
for _, source in ipairs(PLUGIN_SOURCES) do
|
|
add({ source = source })
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function get_codecompanion_config()
|
|
return {
|
|
adapters = {
|
|
acp = {
|
|
-- Codex = heavy agent lane (ChatGPT Edu login via `codex login`; ~/.codex/auth.json).
|
|
-- Requires `codex-acp` on PATH (~/.nix-profile/bin). ACP-only slash commands in the
|
|
-- chat buffer: /resume (restore a past session, fresh chat only), /mode (switch agent
|
|
-- mode), /command, /acp_session_options (e.g. model per session); `\` triggers ACP
|
|
-- command completion (1-5s delay after chat open).
|
|
codex = function()
|
|
return require("codecompanion.adapters").extend("codex", {
|
|
defaults = {
|
|
auth_method = "chatgpt",
|
|
},
|
|
})
|
|
end,
|
|
},
|
|
},
|
|
interactions = {
|
|
chat = {
|
|
adapter = {
|
|
name = "copilot",
|
|
model = "claude-sonnet-5",
|
|
},
|
|
slash_commands = {
|
|
["share"] = {
|
|
opts = {
|
|
token = os.getenv("GITHUB_GIST_TOKEN"),
|
|
},
|
|
},
|
|
},
|
|
opts = {
|
|
completion_provider = "blink",
|
|
context_management = {
|
|
editing = {
|
|
trigger = 0.65,
|
|
keep_cycles = 3,
|
|
exclude_tools = { "memory" },
|
|
},
|
|
compaction = {
|
|
trigger = 0.85,
|
|
min_token_savings = 10000,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
inline = {
|
|
adapter = {
|
|
name = "copilot",
|
|
model = "gpt-5-mini",
|
|
},
|
|
},
|
|
shared = {
|
|
keymaps = {
|
|
accept_change = {
|
|
modes = { n = "ga" },
|
|
description = "Accept the suggested change",
|
|
},
|
|
reject_change = {
|
|
modes = { n = "gr" },
|
|
opts = { nowait = true },
|
|
description = "Reject the suggested change",
|
|
},
|
|
},
|
|
},
|
|
background = {
|
|
adapter = {
|
|
name = "copilot",
|
|
model = "gpt-5-mini",
|
|
},
|
|
chat = {
|
|
callbacks = {
|
|
["on_ready"] = {
|
|
actions = { "interactions.background.builtin.chat_make_title" },
|
|
enabled = true,
|
|
},
|
|
},
|
|
opts = {
|
|
enabled = true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
display = {
|
|
chat = {
|
|
show_settings = false,
|
|
window = {
|
|
layout = "horizontal",
|
|
position = "bottom",
|
|
height = 0.33,
|
|
},
|
|
},
|
|
diff = {
|
|
enabled = true,
|
|
threshold_for_chat = 6,
|
|
},
|
|
},
|
|
rules = {
|
|
default = {
|
|
description = "Collection of common files for all projects",
|
|
files = {
|
|
".clinerules",
|
|
".cursorrules",
|
|
".rules",
|
|
".github/copilot-instructions.md",
|
|
"AGENT.md",
|
|
"AGENTS.md",
|
|
{ path = "CLAUDE.md", parser = "claude" },
|
|
{ path = "CLAUDE.local.md", parser = "claude" },
|
|
{ path = "~/.claude/CLAUDE.md", parser = "claude" },
|
|
},
|
|
},
|
|
opts = {
|
|
chat = {
|
|
autoload = "default",
|
|
enabled = true,
|
|
},
|
|
},
|
|
},
|
|
prompt_library = {
|
|
["expert"] = {
|
|
interaction = "chat",
|
|
description = "Get expert advice from an LLM",
|
|
opts = { alias = "expert" },
|
|
prompts = {
|
|
{
|
|
role = "system",
|
|
content = create_system_prompt(
|
|
"I will ask you specific questions and I want you to return concise explanations and codeblock examples."
|
|
),
|
|
},
|
|
{
|
|
role = "user",
|
|
content = function(context)
|
|
return "I have the following code:\n\n" .. get_code_block(context) .. "\n\n"
|
|
end,
|
|
opts = { contains_code = true },
|
|
},
|
|
},
|
|
},
|
|
["fixer"] = {
|
|
interaction = "chat",
|
|
description = "Fix code errors with expert guidance",
|
|
opts = { alias = "fixer" },
|
|
prompts = {
|
|
{
|
|
role = "system",
|
|
content = create_system_prompt(
|
|
"I have a block of code that is not working and will give you a hint about the error. I want you to return the corrected code and a concise explanation of the corrections."
|
|
),
|
|
},
|
|
{
|
|
role = "user",
|
|
content = function(context)
|
|
return "The following code has an error:\n\n" .. get_code_block(context) .. "\n\nThe error is:"
|
|
end,
|
|
opts = { contains_code = true },
|
|
},
|
|
},
|
|
},
|
|
["suggest"] = {
|
|
interaction = "chat",
|
|
description = "Suggest improvements to the buffer",
|
|
opts = { alias = "suggest" },
|
|
prompts = {
|
|
{
|
|
role = "system",
|
|
content = create_system_prompt(
|
|
"When asked to improve code, follow these steps:\n" ..
|
|
"1. Identify the programming language.\n" ..
|
|
"2. Think separately for each function or significant block of code and think about possible improvements (e.g., for better readability or speed) in the context of the language.\n" ..
|
|
"3. Think about the whole document and think about possible improvements.\n" ..
|
|
"4. Provide the improved code.\n" ..
|
|
"5. Provide a concise explanation of the improvements."
|
|
),
|
|
},
|
|
{
|
|
role = "user",
|
|
content = function(context)
|
|
return "Please improve the following code:\n\n" .. get_code_block(context)
|
|
end,
|
|
opts = { contains_code = true },
|
|
},
|
|
},
|
|
},
|
|
["agent"] = {
|
|
interaction = "chat",
|
|
description = "Agentic coding with the @{agent} tool group (read/edit/grep/run)",
|
|
opts = { alias = "agent" },
|
|
prompts = {
|
|
{
|
|
role = "user",
|
|
content = function(context)
|
|
return "@{agent} Work on the following code:\n\n" .. get_code_block(context) .. "\n\n"
|
|
end,
|
|
opts = { contains_code = true },
|
|
},
|
|
},
|
|
},
|
|
["tdd"] = {
|
|
interaction = "chat",
|
|
description = "Workflow: plan the buffer change, implement it, run the tests",
|
|
opts = { alias = "tdd", is_workflow = true },
|
|
prompts = {
|
|
{
|
|
{
|
|
role = "user",
|
|
content = function(context)
|
|
return "Let's work test-driven. First, study #buffer and the relevant parts of the codebase, then propose a concise implementation plan (no code yet).\n\nThe code under discussion:\n\n"
|
|
.. get_code_block(context)
|
|
.. "\n\nThe task: "
|
|
end,
|
|
opts = { contains_code = true },
|
|
},
|
|
},
|
|
{
|
|
{
|
|
role = "user",
|
|
content = "Implement the plan now, writing or updating tests alongside the code. @{agent}",
|
|
opts = { auto_submit = true },
|
|
},
|
|
},
|
|
{
|
|
{
|
|
role = "user",
|
|
content = "Run the project's test suite with @{run_command} and fix any failures until it passes.",
|
|
opts = { auto_submit = true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
end
|
|
|
|
-- Batch add simple plugins
|
|
later(function()
|
|
for _, plugin in ipairs(PLUGIN_ADDS) do
|
|
add(plugin)
|
|
end
|
|
end)
|
|
|
|
-- Configure plugins with setup
|
|
later(function()
|
|
add("copilot.lua")
|
|
require("copilot").setup({
|
|
suggestion = { enabled = false },
|
|
panel = { enabled = false },
|
|
filetypes = {
|
|
help = true,
|
|
julia = true,
|
|
lua = true,
|
|
markdown = true,
|
|
nix = true,
|
|
python = true,
|
|
r = true,
|
|
sh = function()
|
|
if string.match(vim.fs.basename(vim.api.nvim_buf_get_name(0)), '^%.env.*') then
|
|
-- disable for .env files
|
|
return false
|
|
end
|
|
return true
|
|
end,
|
|
["."] = false
|
|
},
|
|
server_opts_overrides = {
|
|
settings = {
|
|
telemetry = { telemetryLevel = 'off' }
|
|
}
|
|
},
|
|
should_attach = function(_, bufname)
|
|
if string.match(bufname, "env") then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
})
|
|
end)
|
|
|
|
later(function()
|
|
add("blink-copilot")
|
|
require("blink-copilot").setup({
|
|
max_completions = 1,
|
|
})
|
|
end)
|
|
|
|
|
|
-- CodeCompanion habit notes (chat buffer unless stated):
|
|
-- /compact compact history, keep summary /fork fork the conversation
|
|
-- /symbols insert symbols for a file /share export chat to a GitHub gist
|
|
-- gm toggle "btw" ephemeral message gty YOLO: approve all tool calls
|
|
-- gba/gbd buffer sync add/drop gd debug window (adapter/tools info)
|
|
-- Codex (ACP) lane: /resume (fresh chat only), /mode, /command, /acp_session_options,
|
|
-- `\` ACP command completion. Prompt library: /expert /fixer /suggest /agent /tdd (workflow).
|
|
later(function()
|
|
add("codecompanion.nvim")
|
|
|
|
-- now use function
|
|
require("codecompanion").setup(get_codecompanion_config())
|
|
vim.cmd([[cab cc CodeCompanion]])
|
|
end)
|
|
|
|
now_if_args(function()
|
|
add("blink.cmp")
|
|
|
|
require("blink.cmp").setup({
|
|
keymap = {
|
|
preset = "default",
|
|
["<C-space>"] = { "show", "select_next" },
|
|
["<C-l>"] = { "accept" },
|
|
},
|
|
cmdline = {
|
|
enabled = true,
|
|
keymap = {
|
|
preset = "inherit",
|
|
["<Tab>"] = { "show", "select_next" },
|
|
["<S-Tab>"] = { "show", "select_prev" },
|
|
["<C-l>"] = { "accept" },
|
|
},
|
|
completion = {
|
|
menu = { auto_show = true },
|
|
list = {
|
|
selection = { preselect = false, auto_insert = true }
|
|
},
|
|
},
|
|
sources = function()
|
|
local cmd_type = vim.fn.getcmdtype()
|
|
if cmd_type == "/" or cmd_type == "?" then
|
|
return { "buffer" }
|
|
elseif cmd_type == ":" or cmd_type == "@" then
|
|
return { "cmdline", "cmp_cmdline" }
|
|
end
|
|
return {}
|
|
end,
|
|
},
|
|
fuzzy = get_blink_fuzzy_setting(),
|
|
signature = {
|
|
enabled = true,
|
|
window = { show_documentation = true }
|
|
},
|
|
completion = {
|
|
menu = {
|
|
draw = {
|
|
treesitter = { "lsp" },
|
|
components = {
|
|
label = {
|
|
text = function(ctx)
|
|
return require("colorful-menu").blink_components_text(ctx)
|
|
end,
|
|
highlight = function(ctx)
|
|
return require("colorful-menu").blink_components_highlight(ctx)
|
|
end,
|
|
},
|
|
kind_icon = { highlight = get_mini_icons_highlight },
|
|
kind = { highlight = get_mini_icons_highlight },
|
|
},
|
|
},
|
|
},
|
|
list = {
|
|
selection = { preselect = false, auto_insert = true }
|
|
},
|
|
documentation = { auto_show = true },
|
|
trigger = { show_in_snippet = false },
|
|
},
|
|
snippets = { preset = "mini_snippets" },
|
|
sources = {
|
|
per_filetype = {
|
|
codecompanion = { "codecompanion" },
|
|
},
|
|
default = { "references", "lsp", "path", "snippets", "buffer", "omni", "copilot" },
|
|
providers = {
|
|
path = {
|
|
score_offset = 50,
|
|
opts = {
|
|
get_cwd = function(_)
|
|
return vim.fn.getcwd()
|
|
end,
|
|
},
|
|
},
|
|
lsp = { score_offset = 40 },
|
|
snippets = { score_offset = 0 },
|
|
cmp_cmdline = {
|
|
name = "cmp_cmdline",
|
|
module = "blink.compat.source",
|
|
enabled = false,
|
|
score_offset = 10,
|
|
opts = { cmp_name = "cmdline" }
|
|
},
|
|
copilot = {
|
|
name = "copilot",
|
|
module = "blink-copilot",
|
|
score_offset = 45,
|
|
async = true,
|
|
},
|
|
codecompanion = {
|
|
name = "CodeCompanion",
|
|
module = "codecompanion.providers.completion.blink",
|
|
score_offset = 45,
|
|
async = true,
|
|
},
|
|
references = {
|
|
name = "pandoc_references",
|
|
module = "cmp-pandoc-references.blink",
|
|
score_offset = 50,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
end)
|