mirror of
https://github.com/dwinkler1/nvimConfig.git
synced 2026-10-07 22:07:55 -04:00
Compare commits
31 changed files with 772 additions and 845 deletions
4
.github/workflows/check.yml
vendored
4
.github/workflows/check.yml
vendored
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v4
|
||||
- uses: wimpysworld/nothing-but-nix@main
|
||||
if: runner.os == 'Linux'
|
||||
with:
|
||||
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
github_access_token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: cachix/cachix-action@v17
|
||||
- uses: cachix/cachix-action@v14
|
||||
with:
|
||||
name: rde
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
id-token: "write"
|
||||
contents: "read"
|
||||
steps:
|
||||
- uses: "actions/checkout@v7"
|
||||
- uses: "actions/checkout@v5"
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: "DeterminateSystems/determinate-nix-action@v3"
|
||||
|
|
|
|||
4
.github/workflows/update.yml
vendored
4
.github/workflows/update.yml
vendored
|
|
@ -10,13 +10,13 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v4
|
||||
- uses: wimpysworld/nothing-but-nix@main
|
||||
with:
|
||||
hatchet-protocol: 'carve'
|
||||
- name: Install Determinate Nix
|
||||
uses: DeterminateSystems/determinate-nix-action@v3
|
||||
- uses: cachix/cachix-action@v17
|
||||
- uses: cachix/cachix-action@v14
|
||||
with:
|
||||
name: rde
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
|
|
|
|||
85
.kilo/plans/1784851829193-codecompanion-revamp.md
Normal file
85
.kilo/plans/1784851829193-codecompanion-revamp.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# CodeCompanion Revamp Plan
|
||||
|
||||
## Context
|
||||
|
||||
- Current: CodeCompanion **19.18.0** via nixpkgs `vimPlugins.codecompanion-nvim` (nixpkgs input lastModified ~2026-05-15).
|
||||
- Latest: **19.20.0**. Key changes since 19.18.0:
|
||||
- v19.19.0: `claude-sonnet-5` support, async/dynamic model fetching, copilot `top_p` fixes, inline orphaned-keymap fix, background command deregistration.
|
||||
- v19.20.0: `gemini_interactions` adapter, PDF support for http adapters (`/file` on Anthropic/Copilot/OpenAI/OpenRouter), env vars from files, prompt-library items auto-receive default rule groups, copilot schema options removed.
|
||||
- **Bug in current setup:** config sets `model = "claude-sonnet-5"` but 19.18.0 predates its support; the model name may not resolve. The nixpkgs bump fixes this.
|
||||
- Files involved:
|
||||
- `plugin/24_completion.lua` — `get_codecompanion_config()`, setup, blink integration.
|
||||
- `plugin/10_keymap.lua` — `<leader>a*` keymaps (lines 69–84, contains duplicates/commented cruft).
|
||||
- `overlays/plugins.nix` — codecompanion overlay (nvimSkipModules); likely unchanged.
|
||||
- `modules/module/specs/plugins.nix` — plugin spec; unchanged.
|
||||
|
||||
## Decisions (confirmed with user)
|
||||
|
||||
1. **Version bump:** `nix flake update nixpkgs` (accepts wider plugin bump; do NOT override src).
|
||||
2. **Adopt:** agent-mode keymaps, one workflow prompt, minor slash-command config (`/share` token). **No MCP.**
|
||||
3. **Models:** `claude-sonnet-5` (copilot) for **chat only**; cheaper copilot model for **inline** and **background** (title generation) — use `gpt-5-mini` as placeholder; verify exact model id via `ga` model picker in a chat buffer after the bump and adjust.
|
||||
4. **Codex (ChatGPT Edu) = heavy agent lane.** Copilot stays the default chat adapter; Codex ACP is launched explicitly for heavy autonomous tasks. Auth via ChatGPT login only (no API key) — user's existing `auth_method = "chatgpt"` is correct per the v19.20.0 adapter source (`"openai-api-key"|"codex-api-key"|"chatgpt"`; the docs page comment `"chat-gpt"` is stale).
|
||||
|
||||
## Tasks
|
||||
|
||||
1. **Bump nixpkgs**
|
||||
- Run `nix flake update nixpkgs`.
|
||||
- Verify: `nix eval --raw nixpkgs#vimPlugins.codecompanion-nvim.version` reports ≥ 19.20.0.
|
||||
- Rebuild the wrapped Neovim per this repo's normal build (`nix build` / the repo's usual package target) and smoke-test that the editor starts.
|
||||
|
||||
2. **`plugin/24_completion.lua` — `get_codecompanion_config()`**
|
||||
- Keep `interactions.chat.adapter = { name = "copilot", model = "claude-sonnet-5" }`.
|
||||
- Change `interactions.inline.adapter` to `{ name = "copilot", model = "gpt-5-mini" }` (cheap/fast).
|
||||
- Add a background adapter so title generation doesn't use Sonnet: set the background chat adapter to copilot/`gpt-5-mini` (config lives under `interactions.background.chat`; confirm exact key against `:h codecompanion` / `:checkhealth codecompanion` after the bump — the docs page "Generating Titles" says an adapter must be configured for background interactions).
|
||||
- Keep existing context_management, rules, display, shared keymaps unchanged.
|
||||
- Add `interactions.chat.slash_commands["share"].opts.token = os.getenv("GITHUB_GIST_TOKEN")`.
|
||||
- **Prompt library additions** (keep `expert`, `fixer`, `suggest`):
|
||||
- `["agent"]`: interaction "chat", alias `agent`, first user prompt starts with `@{agent}` plus selected code block, so Copilot/Claude gets the file-editing tool group (read_file, insert_edit_into_file, grep_search, run_command, etc., with approvals).
|
||||
- `["tdd"]` (name flexible): interaction "chat", `opts = { is_workflow = true, alias = "tdd" }` — 3-stage workflow: (1) plan/understand `#buffer`, (2) `@{agent}` implement, (3) `@{run_command}` run the test suite (leverages run_command's test-flag for agentic workflows).
|
||||
|
||||
3. **`plugin/10_keymap.lua` — clean up + add**
|
||||
- Remove duplicated/commented lines (70–71, 77, 81).
|
||||
- Add:
|
||||
- `nmap_leader("aa", "<cmd>CodeCompanionChat<CR>", "Agent chat (use @{agent})")` or directly `<cmd>CodeCompanion /agent<CR>`.
|
||||
- `xmap_leader("aa", "<cmd>CodeCompanion /agent<CR>", "Agent on selection")`.
|
||||
- `nmap_leader("aw", "<cmd>CodeCompanion /tdd<CR>", "Workflow: plan→implement→test")`.
|
||||
- `nmap_leader("aC", "<cmd>CodeCompanion /compact<CR>", "Compact chat")` (verify `<leader>aC` doesn't clash).
|
||||
- Keep existing mappings unchanged.
|
||||
|
||||
4. **No changes** to `overlays/plugins.nix` or `modules/module/specs/plugins.nix` unless the new version introduces new lazy-module load failures (re-run the build's check phase; `doCheck = false` is already set on the spec entry).
|
||||
|
||||
5. **Codex ACP lane (ChatGPT Edu)**
|
||||
- **Resolved prerequisite:** user has installed `codex-acp` at `~/.nix-profile/bin/codex-acp` (verified on PATH 2026-07-24). The preset adapter's default command (`codex-acp`) now works as-is — **no `commands` override in the Lua config**. Fallback only if the binary misbehaves: override with `commands = { default = { "codex", "acp" } }` (requires the codex CLI's built-in `acp` subcommand; verify with `codex acp --help`).
|
||||
- **Config change (small):** none strictly required — the existing `extend("codex", { defaults = { auth_method = "chatgpt" } })` block in `plugin/24_completion.lua` is correct. Keep it.
|
||||
- Auth: prereq is an active `codex login` session with the ChatGPT Edu account (`~/.codex/auth.json` exists — user confirms validity; if expired, re-run `codex login`, browser/ChatGPT-app flow, no API key).
|
||||
- Keep codex on-demand (do NOT make it the default chat adapter):
|
||||
- Keep `<leader>ak` (`:CodeCompanionChat adapter=codex`).
|
||||
- Add comment documenting ACP-only slash commands for this lane: `/resume` (restore past codex session, fresh chat only), `/mode` (switch agent mode), `/command`, `/acp_session_options`, plus `\`-triggered ACP command completion in the chat buffer.
|
||||
- Do not set a default codex model in config; pick per-session via `ga` / `/acp_session_options` (avoids hardcoding model ids that change with the Edu plan).
|
||||
- PATH note: Neovim must inherit a PATH containing `~/.nix-profile/bin` so the spawned `codex-acp` resolves — true when nvim is launched from the user's normal shell; call it out if validation fails with "command not found".
|
||||
|
||||
6. **Docs/habit notes** (add as comment block above the codecompanion setup, no separate docs file): `/compact`, `/fork`, `/symbols`, `/share`, `/resume`+`/mode` (codex ACP), `gm` (btw), `gty` (YOLO), `gba`/`gbd` (buffer sync), `gd` (debug window).
|
||||
|
||||
## Validation
|
||||
|
||||
- `nix flake update nixpkgs` then repo build succeeds.
|
||||
- In Neovim: `:checkhealth codecompanion` clean.
|
||||
- `:CodeCompanionChat`, press `ga` → copilot adapter lists `claude-sonnet-5`; send a trivial message and confirm response + auto title generation.
|
||||
- In chat: type `@` → completion shows `agent`, `files`, `memory`, etc.; run `@{agent}` task on a scratch repo and confirm the approval prompt flow works.
|
||||
- `:CodeCompanion /tdd` starts the workflow stages in order.
|
||||
- Inline: visual-select code, `:CodeCompanion` prompt → confirm diff shows and `ga`/`gr` accept/reject still work on the cheap model.
|
||||
- `:CodeCompanion /share` prompts/errors sensibly if `GITHUB_GIST_TOKEN` unset.
|
||||
- **Codex lane:** `<leader>ak` opens a chat with the codex ACP adapter; the spawned `codex-acp` process initializes without auth errors (ChatGPT method), a trivial prompt gets a response, and `/mode` lists codex session modes. If it fails with command-not-found, check that Neovim inherited `~/.nix-profile/bin` in PATH (`:echo $PATH` inside nvim).
|
||||
|
||||
## Risks
|
||||
|
||||
- `nix flake update nixpkgs` bumps **all** vimPlugins and Neovim itself; other plugins may break. If the blast radius is too large, fall back to overriding codecompanion `src` to tag `v19.20.0` in `overlays/plugins.nix` (fetchFromGitHub, `doCheck = false`).
|
||||
- Model ids (`claude-sonnet-5`, `gpt-5-mini`) must match the copilot adapter's choices post-bump; verify via `ga` picker and adjust literals.
|
||||
- `gpt-5-mini` may not support tool use on copilot — fine, since inline/background don't use tools.
|
||||
- `codex-acp` lives in the user's nix profile (outside this flake). If the profile is rebuilt/removed, the codex lane breaks — long-term consider adding `codex-acp` to this flake's runtime deps so it's pinned with the rest of the setup (optional follow-up, not required now).
|
||||
- ChatGPT Edu accounts authenticate codex via browser/ChatGPT-app login; token expiry will surface as ACP auth errors → re-run `codex login`.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- MCP server integration.
|
||||
- Custom rules parsers, custom tools, extensions (mcphub/history/vectorcode).
|
||||
191
.kilo/plans/1784858731550-nix-neovim-review.md
Normal file
191
.kilo/plans/1784858731550-nix-neovim-review.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
# Neovim + Nix setup review
|
||||
|
||||
Scope: `flake.nix`, `overlays/`, `modules/`, `plugin/*.lua`, `lua/*`, `ftplugin/*`, CI/workflows, and cross-check against the pinned versions in `flake.lock` (nixpkgs weekly `241313f4`).
|
||||
|
||||
## Pinned package versions (from current lock)
|
||||
- neovim `0.12.2`
|
||||
- blink-cmp `1.10.2`
|
||||
- codecompanion-nvim `19.13.0`
|
||||
- nvim-treesitter `0.10.0-unstable-2026-04-03` (main-branch rewrite)
|
||||
- nvim-treesitter-textobjects `0-unstable-2026-04-07` (main-branch rewrite)
|
||||
- copilot-lua `2.0.3`
|
||||
- render-markdown-nvim `8.12.0-unstable-2026-05-07`
|
||||
- zk-nvim `0.4.7-unstable-2026-03-13`
|
||||
- mini.nvim `0.17.0-unstable-2026-05-12`
|
||||
- quarto-nvim `2.1.0`
|
||||
- otter-nvim `2.14.5`
|
||||
- lspconfig `2.9.0`
|
||||
|
||||
---
|
||||
|
||||
## CRITICAL: Treesitter is currently non-functional
|
||||
|
||||
### Finding
|
||||
The nixpkgs `nvim-treesitter` package is the **main-branch rewrite** — there is no `lua/nvim-treesitter/configs.lua`. The whole module in `plugin/20_startup.lua` lines 212-294 is dead code:
|
||||
|
||||
```lua
|
||||
local ok_configs, configs = pcall(require, "nvim-treesitter.configs") -- fails
|
||||
```
|
||||
|
||||
The working tree already removed the fallback `vim.treesitter.start()` FileType autocmd (the "fixed treesitter" commit deleted it). Net result: **no treesitter highlighting, indentexpr, foldexpr, or textobjects are configured**.
|
||||
|
||||
### Impact
|
||||
- Syntax highlighting falls back to Neovim's regex-only engine.
|
||||
- All textobject keymaps (`]a`, `[a`, `]f`, `[f`, `]e`/`[e`, `<leader>x`/`X`, lsp_interop `<leader>lm`) are inert.
|
||||
- `foldexpr`/`indentexpr` based folding does nothing.
|
||||
|
||||
### Fix direction
|
||||
Rewrite `plugin/20_startup.lua` for main-branch API:
|
||||
1. FileType autocmd → `vim.treesitter.start()` (and optionally `indentexpr`/`foldexpr`).
|
||||
2. `require("nvim-treesitter-textobjects").setup({ move = { set_jumps = true } })` + explicit keymaps using `move.goto_next_start(query, "textobjects")` and `swap.swap_next(...)`.
|
||||
3. The non-Nix `ensure_installed` block should set `opts.ensure_installed` BEFORE the filter (line 278 reads it before line 286 defines it).
|
||||
|
||||
Also remove duplicate `vim.treesitter.language.register("markdown", ...)` between `plugin/20_startup.lua` and `plugin/21_datascience.lua`.
|
||||
|
||||
---
|
||||
|
||||
## CodeCompanion version mismatch
|
||||
|
||||
### Finding
|
||||
Config targets features absent in `19.13.0` (present only in `≥19.19.0`/main):
|
||||
|
||||
| Config usage | Present in 19.13? | Present in main (`v19.20.0`) |
|
||||
|---|---|---|
|
||||
| `interactions.chat.opts.context_management.editing` | No (flat `trigger`/`enabled` only) | Yes |
|
||||
| `interactions.chat.opts.context_management.compaction` | No | Yes |
|
||||
| `<leader>aC` requiring `codecompanion.interactions.chat.context_management.compaction` | **RUNTIME ERROR** | Works |
|
||||
| `slash_commands.share` (`opts.token = ...`) | No (absent from defaults) | Yes |
|
||||
| `adapters.acp.codex.defaults.auth_method = "chatgpt"` | Yes | Yes |
|
||||
| `interactions.chat.adapter = { name = ..., model = ... }` table form | Yes | Yes |
|
||||
|
||||
### Risk
|
||||
On today's lock (`19.13.0`): `/share` is inert, compaction-only keymap throws module-not-found, and `editing`/`compaction` tuning is silently ignored. On `≥19.19.0`/main: everything works.
|
||||
|
||||
### Decision needed (BLOCKING)
|
||||
Choose **exactly one**:
|
||||
|
||||
- **A — Upgrade the plugin pin** (`overlays/plugins.nix` fetch to `v19.20.0` or `main`, bump the lock, and keep the current config). Riskiest change but matches what the config is written for.
|
||||
- **B — Downgrade config call sites** to match `19.13.0`: remove `slash_commands.share`, remove `editing`/`compaction` keys (or collapse to flat `trigger: 0.75`), remove or gate `<leader>aC` on "newer" version.
|
||||
- **C — Version-gate the config**: keep current code, read the installed version at startup and skip the new features when < 19.19.
|
||||
|
||||
I recommend A (the config clearly intends to track upstream main-ish behavior; `nix flake update` already bumped it to `19.18.0` in a prior session and you only reverted because the nightly was still too old — `v19.20.0` is now available).
|
||||
|
||||
---
|
||||
|
||||
## DEAD / DUPLICATE code
|
||||
|
||||
- `plugin/20_startup.lua` — the entire `configs.setup(opts)` block is dead (see Treesitter finding above).
|
||||
- `plugin/10_keymap.lua`:
|
||||
- `nmap_leader('od', '<Cmd>Neogen<CR>', ...)` — `neogen` plugin is **not shipped** in `specs/plugins.nix`. Mapping errors on press.
|
||||
- `nmap_leader('fp', '<Cmd>Pick projects<CR>', ...)` — `MiniExtra.pickers.projects` doesn't exist in mini.extra; would error.
|
||||
- `nmap_leader('oS', '<Cmd>lua Config.insert_section()<CR>', ...)` — `Config.insert_section` is never defined.
|
||||
- `vim.lsp.buf.definition()` is bound to `grd`; Neovim 0.11+ defaults include `gr` aliases. Cosmetic, but `]d`/`[d` exist on modern LSP config and would be more idiomatic.
|
||||
- `plugin/23_editor.lua`:
|
||||
- `my_styler` formatter (calls `R -s -e styler::...`) is defined but never referenced — dead.
|
||||
- `plugin/24_completion.lua`:
|
||||
- `providers.cmp_r` is defined in the blink source list but never enabled in `default` or `per_filetype`; inert.
|
||||
- `BLINK_VERSION = "v1.4.1"` — only consulted in the non-Nix install path; nixpkgs is `1.10.2`. Pin it to current or drop.
|
||||
- `get_blink_fuzzy_setting().prebuilt_binary = { force_version = BLINK_VERSION }` — singular key is wrong; blink option is `fuzzy.prebuilt_binaries` (plural). Being skipped in-Nix anyway, but still wrong key.
|
||||
- `modules/module/specs/plugins.nix` — `specs.utils-lazy` ships `nvim-dap*` and `nvim-lint` but there is zero config/tooling that references them in this repo.
|
||||
- `ftplugin/quarto.lua`:
|
||||
- Second top-level `require('quarto').setup()` with **no args** runs _after_ `21_datascience.lua`'s setup and **resets** it to defaults (no `lspFeatures`, no `codeRunner`).
|
||||
- Top-level `require('quarto')` at FileType load also crashes if the quarto plugin isn't installed (e.g. cats off and a `.qmd`/`.quarto` file is opened): Neovim detects `quarto` ft natively in 0.10+.
|
||||
- `plugin/00_options.lua`:
|
||||
- Lines 146-163: two back-to-back FileType autocmds that both remove `r`/`o` from `formatoptions`. The second references an undefined `augroup` variable (nil → autocmd is global, happens to still work).
|
||||
|
||||
---
|
||||
|
||||
## STALE / WRONG options
|
||||
|
||||
- `plugin/10_keymap.lua` line 174:
|
||||
```lua
|
||||
require("conform").format({ lsp_fallback = true })
|
||||
```
|
||||
`lsp_fallback` is the **deprecated** boolean form of `conform.nvim`; should be `lsp_format = "fallback"` (the form already used in `plugin/23_editor.lua` and `ftplugin/python.lua`). In newer conform this may warn or error.
|
||||
- `ftplugin/quarto.lua`:
|
||||
- Sets `<Plug>RDSendLine` and R-style keymaps on _all_ quarto buffers, including Python/Julia chunks. This collides with quarto-runner mappings set in `21_datascience.lua`. Should gate on `vim.bo.filetype == "r"` (the ftplugin already imports quarto.runner for python, but the R keys leak).
|
||||
- `.github/dependapot.yml` — filename typo. GitHub expects `dependabot.yml`; Dependabot won't run.
|
||||
- `.github/workflows/check.yml`:
|
||||
- `nix develop` without a `-c` command just launches an interactive shell. In CI it does nothing useful (or hangs). Replace with `nix develop -c echo ok` or drop.
|
||||
- Path filter `'modules'` only matches the root directory itself; should be `modules/**` (or `'modules/**'`).
|
||||
- Changing `plugin/`, `lua/`, `overlays/`, `ftplugin/`, etc. does **not** trigger CI.
|
||||
- `flake.nix` / `.envrc` shellHook:
|
||||
```sh
|
||||
export R_LIBS_SITE=$(strings "$(command -v R)" | grep -oP '/nix/store/[^:]+/library' ...)
|
||||
```
|
||||
`grep -oP` (PCRE) is **not available in macOS BSD grep**. On `aarch64-darwin` this silently fails → `R_LIBS_SITE` is empty. Either depend on `ripgrep` regex (`strings ... | rg -o '...'`) or use `gsed` (GNU sed).
|
||||
- `overlays/plugins.nix` — `zk-nvim` skip list contains `zk.pickers.fzf_lua`; current zk-nvim package likely doesn't load that regardless, but harmless.
|
||||
- `modules/module/settings/core.nix` — `config.settings.nvim_lua_env` references `lp.tiktoken_core` but `tiktoken_core` is **not in `catPkgs.general` or anywhere else** in these files. If there's an extra Lua/tiktoken module, fine; otherwise this option is placeholder dead code.
|
||||
- `modules/module/settings/hosts.nix` — host `m` (marimo) is defined with `enable = false` but also configured with `package`, `argv0`, `addFlag`. Dead block.
|
||||
|
||||
---
|
||||
|
||||
## PORTABILITY / DARWIN issues
|
||||
|
||||
- Shell hook `grep -oP` (above) fails on macOS.
|
||||
- `mkdir -p "$R_LIBS_USER"` fine; but `command -v R` on macOS returns the wrapper; the wrapper path injection still works.
|
||||
|
||||
---
|
||||
|
||||
## DEAD WEIGHT (shipped but unused)
|
||||
|
||||
- `catPkgs.r` includes `pkgs.rnvimserver` — `rnvimserver` is needed by R.nvim only when using the _socket_ transport; `21_datascience.lua` only uses vim-slime. Acceptable, but `rnvimserver` adds to build time. Include if you actually use it? Currently not used.
|
||||
- `specs.utils-lazy` ships `nvim-dap`, `nvim-dap-ui`, `nvim-dap-virtual-text`, `nvim-lint` — none of these are referenced anywhere in `plugin/` or `lua/`. Consider moving them to a devShell-only cat, or drop them.
|
||||
- `.gitignore` `*.R` — prevents tracking any new `.R` files. `tests/test.R` is already committed so it isn't actively harmful, but it's surprising for a repo whose default cats include R.
|
||||
- `.commandcode/` dir is listed in `.gitignore` but is in the worktree; fine, but worth cleaning up if it's an artifact.
|
||||
|
||||
---
|
||||
|
||||
## IMPROVEMENTS given updated packages
|
||||
|
||||
- `catPkgs.markdown` — add `marksman` (it's the LSP used by `render-markdown.nvim` wiki links and configured in LSP). Same binary is needed by `render-markdown` for wiki link ISP.
|
||||
- `vim.lsp.enable` servers configured but binaries missing in PATH:
|
||||
- `marksman` (LSP + render-markdown wiki integration).
|
||||
- `r_ls` (new R language server — package name in nixpkgs is likely `r-languageserver` or `r_ls`; current `catPkgs.r` doesn't ship either).
|
||||
- `clangd` — not required for this data-science setup; either drop or add to `external`.
|
||||
- `julials` — require `LanguageServer.jl` in `settings.lang_packages.julia` for it to be useful.
|
||||
- `blink.cmp` is `1.10.2` but config pins `BLINK_VERSION = "v1.4.1"` for the non-Nix path. Either drop the `BLINK_VERSION` constant (let MiniDeps track HEAD or the tag pinned in flake) or update it to `v1.10.2`.
|
||||
- `conform.nvim` already uses `lsp_format = "fallback"` in `23_editor.lua` and `ftplugin/python.lua`, but `10_keymap.lua` still calls the deprecated `lsp_fallback = true`. Align to `lsp_format = "fallback"`.
|
||||
- `.github/workflows/check.yml` — add path filters for `plugin/**`, `lua/**`, `overlays/**`, `ftplugin/**`, `modules/**`.
|
||||
- `tests/init.lua` smoke-test and `tests/test.R` are present but **not wired into `nix flake check`**. Add a trivial check that runs `lua tests/init.lua` via `nix-shell -A ...`.
|
||||
|
||||
---
|
||||
|
||||
## CONFLICTS
|
||||
|
||||
- `<leader>up` (terminal bracketed paste toggle in `10_keymap.lua`) conflicts with `mini.basics.mappings.option_toggle_prefix = "<leader>u"` (paste toggle), which is set up later in `20_startup.lua` via `now()`. Result: the user's `<leader>up` mapping gets overwritten. Portable workaround: remap to `<leader>tp` (terminal namespace) and keep `<leader>tb` as alternative — both already mapped in 10_keymap.
|
||||
- `ftplugin/quarto.lua` resets quarto-nvim config (kills `lspFeatures`/`codeRunner` set in `21_datascience.lua`) and injects R plug mappings into non-R quarto chunks.
|
||||
|
||||
---
|
||||
|
||||
## CLEAN summary for implementer
|
||||
|
||||
### Immediate (not version-dependent)
|
||||
1. Restore treesitter highlighting + textobjects in `plugin/20_startup.lua` using main-branch API.
|
||||
2. Remove duplicate `vim.treesitter.language.register` in `21_datascience.lua`.
|
||||
3. Fix `plugin/10_keymap.lua`: remove/resolve `Neogen`, `Pick projects`, `Config.insert_section` dangling mappings.
|
||||
4. Replace deprecated `lsp_fallback = true` with `lsp_format = "fallback"` in `plugin/10_keymap.lua`.
|
||||
5. Gate `ftplugin/quarto.lua` to not reset config and to not leak R keys into non-R chunks; guard against missing `quarto` plugin.
|
||||
6. Remove duplicate `formatoptions` autocmds (or at least dedupe).
|
||||
7. Dedupe `RNVIM_COMPLDIR` / `TMPDIR` setup (do it in one place).
|
||||
8. Rename `.github/dependapot.yml` to `.github/dependabot.yml`.
|
||||
9. Remove or gate dead host `m` in `hosts.nix`.
|
||||
10. Remove unused `rsplit`? no, irrelevant.
|
||||
11. Add `marksman` to `catPkgs.markdown` and remove orphan LSP entries or ship the binaries.
|
||||
12. Remove dead `conform` formatter `my_styler` and dead blink `cmp_r` provider, or wire them up.
|
||||
|
||||
### Version-dependent (BLOCKED)
|
||||
- **Reset the CodeCompanion version decision**: either pin to `v19.20.0` (keep config, fix `slash_commands.share`, drop the `<leader>aC` compatibility shim, keep `editing`/`compaction`) OR downgrade config to match `19.13.0` (remove `share`, collapse `context_management`).
|
||||
|
||||
### CI / packaging
|
||||
- Fix `nix develop` usage and path filters in `.github/workflows/check.yml`.
|
||||
- Replace `grep -oP` in `flake.nix` shellHook with portable `rg -o` or add `gnused` to `catPkgs.always` and use `gsed`.
|
||||
- Wire `tests/init.lua` into `flake check`.
|
||||
|
||||
### Verification
|
||||
- `nix flake check` on both `aarch64-darwin` and `x86_64-linux`.
|
||||
- `nix build .#packages.<system>.default` succeeds (already does).
|
||||
- Start nvim, verify :TSContext works, treesitter highlighting is on, and `]f`/`[f` textobjects move.
|
||||
- Open a `.qmd` file with cats off → no ftplugin crash.
|
||||
- Confirm `<leader>up` still toggles bracketed paste.
|
||||
- Confirm `:Pick projects` and `:Neogen` and `Config.insert_section` no longer error (or are mapped to valid handlers).
|
||||
|
|
@ -198,8 +198,7 @@ projectSettings = {
|
|||
|
||||
### Language packages (module defaults)
|
||||
|
||||
Add Python/R/Julia libraries. These are appended to the built-in defaults, which
|
||||
always apply (see `settings.langPackageDefaults` in `modules/module/settings/lang-packages.nix`):
|
||||
Add Python/R/Julia libraries that get appended to the module defaults. The built-in defaults are:
|
||||
|
||||
- Python: `duckdb`, `polars`
|
||||
- R: `arrow`, `broom`, `data_table`, `janitor`, `styler`
|
||||
|
|
@ -215,8 +214,7 @@ projectSettings = {
|
|||
};
|
||||
```
|
||||
|
||||
Use `lib.mkForce` to replace other `lang_packages` assignments; the built-in defaults still apply.
|
||||
To exclude defaults, override `catPkgs.<cat>` instead:
|
||||
Use `lib.mkForce` to replace rather than append:
|
||||
|
||||
```nix
|
||||
projectSettings = {
|
||||
|
|
@ -339,5 +337,5 @@ cats.nix ───────────────► config.cats │
|
|||
- **Overlays** (`overlays/`) inject dependency package sets into nixpkgs (`rpkgs`, `baseRPackages`, `basePythonPackages`, plugins). Top-level `rWrapper` and `quarto` are compatibility conveniences, but `pkgs.rpkgs` is the canonical R surface for downstream configuration.
|
||||
- **cat-packages.nix** is the single source of truth for per-category packages. Each list is gated by its cat toggle.
|
||||
- **deps.nix** wires `catPkgs` into the wrapper's runtime PATH.
|
||||
- **settings.lang_packages** holds project-specific language libraries appended to the built-in `settings.langPackageDefaults`, used by local outputs and downstream module consumers.
|
||||
- **settings.lang_packages** holds the shared defaults used by local outputs and downstream module consumers.
|
||||
- **flake.nix** exports `lib.eval`, `lib.mkWrapper`, `lib.devShellPackages`, and `lib.shellHook` as the canonical downstream helpers, and builds `packages.default` and `devShells.default` from the same module config.
|
||||
|
|
|
|||
24
flake.lock
generated
24
flake.lock
generated
|
|
@ -7,11 +7,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1787729487,
|
||||
"narHash": "sha256-3A5rKxnceQ+inWqeQDUrxoVPXr67JMrdIIfbLStx06s=",
|
||||
"lastModified": 1781337902,
|
||||
"narHash": "sha256-QjXxUvpOBbnlpSskBOZJ9nUbpYyFRD4cBo4s1aHniGs=",
|
||||
"owner": "dwinkler1",
|
||||
"repo": "fran",
|
||||
"rev": "b77635aeb1e70fffd09e5823dde5b76abb3df991",
|
||||
"rev": "30ab141871ede23b245a90afea58e076ef83b515",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -22,11 +22,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1788179007,
|
||||
"narHash": "sha256-hn1oU2rue2SYK8dAr8+WNZWtbsz1S2W5mnHlSEuh3bo=",
|
||||
"lastModified": 1784796856,
|
||||
"narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "34ab99075ac4f7e40cf037eef32cb1c360bb85e9",
|
||||
"rev": "e2587caef70cea85dd97d7daab492899902dbf5d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -76,11 +76,11 @@
|
|||
},
|
||||
"rixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1788208780,
|
||||
"narHash": "sha256-ouq3GjmTOXtWVufLOhMqLAEJXxrlkhDYtZJt3YfN5oU=",
|
||||
"lastModified": 1782576256,
|
||||
"narHash": "sha256-KOvpL9DJJmShb64mX9QTjhxHaxE8MizQIqNUAniuJ2E=",
|
||||
"owner": "dwinkler1",
|
||||
"repo": "rixpkgs",
|
||||
"rev": "f3d00cb8f39262e6ca15c2046bd29ca8a52aae31",
|
||||
"rev": "815afc01bc0cc9a2eba80906645bd08f976a4401",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -124,11 +124,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1787722691,
|
||||
"narHash": "sha256-bWBt7v2FhgG6WTLgSi8nYGi1zXaTdA+vTdCRdvR2vcw=",
|
||||
"lastModified": 1782135443,
|
||||
"narHash": "sha256-vAmbArdCyjqpVW+37aCy/PMBOLIqukUXLQuEKLwUhA4=",
|
||||
"owner": "BirdeeHub",
|
||||
"repo": "nix-wrapper-modules",
|
||||
"rev": "04ef216559b18214853879df862f493a9e6bb8cf",
|
||||
"rev": "6e7f66fa2cdf4d63162580b438f7fcf87c28a46f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
50
flake.nix
50
flake.nix
|
|
@ -161,11 +161,11 @@
|
|||
modules = [
|
||||
{
|
||||
cats = {
|
||||
r = true;
|
||||
r = false;
|
||||
python = true;
|
||||
};
|
||||
settings.lang_packages.python = nixpkgs.lib.mkForce (with pkgs.python3Packages; [
|
||||
polars
|
||||
pandas
|
||||
]);
|
||||
catPkgs.nix = nixpkgs.lib.mkForce [
|
||||
pkgs.alejandra
|
||||
|
|
@ -206,10 +206,10 @@
|
|||
(builtins.length (self.lib.devShellPackages defaultConfig) > 0)
|
||||
];
|
||||
overrideAssertions = [
|
||||
(overrideConfig.cats.r or false)
|
||||
(!(overrideConfig.cats.r or false))
|
||||
(builtins.length overrideNix == 1)
|
||||
((builtins.head overrideNix) == "alejandra")
|
||||
(builtins.match ".*R RHOME.*" overrideShellHook != null)
|
||||
(builtins.match ".*R RHOME.*" overrideShellHook == null)
|
||||
];
|
||||
in
|
||||
pkgs.runCommand "check-downstream-overrides" {
|
||||
|
|
@ -230,48 +230,6 @@
|
|||
nvim --headless -u NONE -c "set runtimepath+=${./.}" -l ${./tests/init.lua}
|
||||
touch $out
|
||||
'';
|
||||
|
||||
# Downstream modules assign settings.lang_packages.<lang> plainly. The
|
||||
# module system replaces same-option defaults, so built-in defaults must
|
||||
# live in langPackageDefaults and be composed at the consumption sites.
|
||||
# This check fails if someone moves defaults back into lang_packages:
|
||||
# then a downstream assignment would silently drop lintr etc.
|
||||
append-semantics = let
|
||||
downstreamCfg = (self.lib.eval {
|
||||
inherit pkgs;
|
||||
modules = [
|
||||
{
|
||||
cats.r = true;
|
||||
settings.lang_packages.r = [ pkgs.rpkgs.rPackages.fixest ];
|
||||
}
|
||||
];
|
||||
}).config;
|
||||
rWrapperPkg = builtins.head downstreamCfg.catPkgs.r;
|
||||
in
|
||||
pkgs.runCommand "check-lang-packages-append" { } ''
|
||||
script=$(readlink -f "${rWrapperPkg}/bin/R")
|
||||
grep -q "r-lintr-" "$script" || {
|
||||
echo "default R packages were dropped by a downstream lang_packages assignment" >&2
|
||||
exit 1
|
||||
}
|
||||
grep -q "r-fixest-" "$script" || {
|
||||
echo "downstream lang_packages.r additions were not appended" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "lang_packages append semantics OK" > $out
|
||||
'';
|
||||
|
||||
smoke-test = pkgs.runCommand "smoke-test" {} ''
|
||||
# The Nix build sandbox has a read-only HOME; point XDG dirs at a
|
||||
# writable location so vim.lsp/shaDa can write state headlessly.
|
||||
export XDG_CONFIG_HOME=$TMPDIR/xdg-config
|
||||
export XDG_STATE_HOME=$TMPDIR/xdg-state
|
||||
export XDG_CACHE_HOME=$TMPDIR/xdg-cache
|
||||
export XDG_DATA_HOME=$TMPDIR/xdg-data
|
||||
BINARY_PATH="${defaultNvimPkg}/bin/vv"
|
||||
"$BINARY_PATH" --headless -c "luafile ${./tests/smoke.lua}" -c "qa!"
|
||||
touch $out
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,39 +11,28 @@ nmap_leader('<Tab>', '<Cmd>bnext<CR>', 'Next buffer')
|
|||
nmap_leader('<S-Tab>', '<Cmd>bprev<CR>', 'Prev buffer')
|
||||
|
||||
-- a is for 'AI'
|
||||
nmap_leader("aa", function()
|
||||
require("opencode").ask("@this: ")
|
||||
end, "Ask OpenCode about this")
|
||||
nmap_leader("ab", function()
|
||||
require("opencode").prompt("Analyse @buffer")
|
||||
end, "Analyse buffer")
|
||||
nmap_leader("aB", function()
|
||||
require("opencode").prompt("Analyse @buffers")
|
||||
end, "Analyse open buffers")
|
||||
nmap_leader("ad", function()
|
||||
require("opencode").prompt("Explain @diagnostics and fix the underlying issue")
|
||||
end, "Explain/fix diagnostics")
|
||||
nmap_leader("an", function()
|
||||
require("opencode").command("session.new")
|
||||
end, "New OpenCode session")
|
||||
nmap_leader("ap", function()
|
||||
require("opencode").ask("@")
|
||||
end, "Ask OpenCode with context")
|
||||
nmap_leader("as", function()
|
||||
require("opencode").select()
|
||||
end, "Select OpenCode action")
|
||||
xmap_leader("aa", function()
|
||||
require("opencode").ask("@this: ")
|
||||
end, "Ask OpenCode about selection")
|
||||
xmap_leader("af", function()
|
||||
require("opencode").prompt("Fix the selected code and preserve its surrounding API:\n@this")
|
||||
end, "Fix selection")
|
||||
xmap_leader("ap", function()
|
||||
require("opencode").ask("@")
|
||||
end, "Ask OpenCode with context")
|
||||
xmap_leader("as", function()
|
||||
require("opencode").select()
|
||||
end, "Select OpenCode action")
|
||||
nmap_leader("aa", "<cmd>CodeCompanion /agent<CR>", "Agent chat (@{agent} tools)")
|
||||
nmap_leader("ac", "<cmd>CodeCompanionChat Toggle<CR>", "Chat Toggle")
|
||||
nmap_leader("aC", function()
|
||||
local chat = require("codecompanion").last_chat()
|
||||
if not chat then
|
||||
return vim.notify("No CodeCompanion chat to compact", vim.log.levels.WARN)
|
||||
end
|
||||
require("codecompanion.interactions.chat.context_management.compaction").compact(chat, { min_token_savings = 0 })
|
||||
end, "Compact chat")
|
||||
nmap_leader("ag", "<cmd>CodeCompanion /commit<CR>", "Generate commit message")
|
||||
nmap_leader("ai", "<cmd>CodeCompanionActions<CR>", "Chat Action")
|
||||
nmap_leader("al", "<cmd>CodeCompanion /lsp<CR>", "Explain LSP Diagnostics")
|
||||
nmap_leader("an", "<cmd>CodeCompanionChat Add<CR>", "Chat New")
|
||||
nmap_leader("as", "<cmd>CodeCompanion /suggest<CR>", "Suggest Improvements")
|
||||
nmap_leader("aw", "<cmd>CodeCompanion /tdd<CR>", "Workflow: plan, implement, test")
|
||||
nmap_leader("ax", "<cmd>CodeCompanion /fixer<CR>", "Code Fixer")
|
||||
xmap_leader("aa", "<cmd>CodeCompanion /agent<CR>", "Agent on selection")
|
||||
xmap_leader("ae", "<cmd>CodeCompanion /explain<CR>", "Explain Code")
|
||||
xmap_leader("af", "<cmd>CodeCompanion /fix<CR>", "Fix Code")
|
||||
xmap_leader("ap", "<cmd>CodeCompanion /expert<CR>", "Code Expert")
|
||||
xmap_leader("as", "<cmd>CodeCompanion /suggest<CR>", "Suggest Improvements")
|
||||
nmap_leader("ak", "<cmd>CodeCompanionChat adapter=codex<CR>", "Chat with Codex")
|
||||
|
||||
-- b is for 'buffer'
|
||||
nmap_leader('bb', '<Cmd>b#<CR>', 'Alternate')
|
||||
|
|
@ -148,8 +137,7 @@ nmap_leader('LL', '<Cmd>luafile %<CR><Cmd>echo "Sourced lua"<CR>', 'Source buffe
|
|||
nmap_leader('Ls', '<Cmd>lua Config.log_print()<CR>', 'Show log')
|
||||
nmap_leader('Lx', '<Cmd>lua Config.execute_lua_line()<CR>', 'Execute `lua` line')
|
||||
|
||||
-- m is for 'mark' (checkbox toggling)
|
||||
nmap_leader('mc', '<Cmd>ToggleCheckbox<CR>', 'Toggle checkbox')
|
||||
-- m is free
|
||||
|
||||
-- o is for 'other'
|
||||
local trailspace_toggle_command = '<Cmd>lua vim.b.minitrailspace_disable = not vim.b.minitrailspace_disable<CR>'
|
||||
|
|
@ -226,24 +214,6 @@ nmap_leader("wh", "<C-w>h", "Go to Left Window", { remap = true })
|
|||
nmap_leader("wj", "<C-w>j", "Go to Lower Window", { remap = true })
|
||||
nmap_leader("wk", "<C-w>k", "Go to Upper Window", { remap = true })
|
||||
nmap_leader("wl", "<C-w>l", "Go to Right Window", { remap = true })
|
||||
nmap_leader("w>", "<Cmd>vertical resize +5<CR>", "Increase Window Width")
|
||||
nmap_leader("w<lt>", "<Cmd>vertical resize -5<CR>", "Decrease Window Width")
|
||||
|
||||
local function set_window_width_percent(percent)
|
||||
return function()
|
||||
local target_width = math.max(1, math.floor(vim.o.columns * percent / 100 + 0.5))
|
||||
vim.api.nvim_win_set_width(0, target_width)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, 9 do
|
||||
local percent = i * 10
|
||||
nmap_leader(
|
||||
"w" .. i,
|
||||
set_window_width_percent(percent),
|
||||
string.format("Set Window Width to %d%%", percent)
|
||||
)
|
||||
end
|
||||
|
||||
nmap_leader("_", "<C-W>s", "Split Window Below", { remap = true })
|
||||
nmap_leader("|", "<C-W>v", "Split Window Right", { remap = true })
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
-- omp bridge: expose an RPC socket so the omp harness can read this Neovim
|
||||
-- instance's buffers on demand. Read-only on the agent side; this module only
|
||||
-- owns the socket lifecycle.
|
||||
--
|
||||
-- Contract
|
||||
-- * Socket path: $NVIM_OMP_SOCKET if set, else <stdpath('state')>/omp.sock
|
||||
-- (~/.local/state/nvim/omp.sock on macOS, ~/.local/state/nvim/omp.sock
|
||||
-- elsewhere). The omp-side tool in .omp/tools/nvim_buffers.mjs resolves the
|
||||
-- *same* path, so the two sides agree without configuration.
|
||||
-- * The agent reads buffers by evaluating read-only nvim API expressions over
|
||||
-- the socket with `nvim --server <path> --remote-expr 'json_encode(...)'`.
|
||||
-- Nothing here writes buffers or executes model-supplied commands.
|
||||
local M = {}
|
||||
|
||||
local SOCKET_NAME = "omp.sock"
|
||||
|
||||
-- Resolve the deterministic socket path. Copies the rule on the omp side; keep
|
||||
-- the two files in sync when changing the fallback or env override.
|
||||
function M.socket_path()
|
||||
local env = vim.env.NVIM_OMP_SOCKET
|
||||
if env and env ~= "" then
|
||||
return env
|
||||
end
|
||||
return vim.fn.stdpath("state") .. "/" .. SOCKET_NAME
|
||||
end
|
||||
|
||||
-- Start the RPC listener. Returns the live socket path, or nil.
|
||||
-- A second instance cannot bind the same address. This function deliberately
|
||||
-- never removes an existing socket file: a failed liveness probe must not
|
||||
-- disconnect another live Neovim instance.
|
||||
-- If a crash leaves a stale socket, remove it manually only after confirming
|
||||
-- that no Neovim process owns the path, then restart vv.
|
||||
function M.start()
|
||||
if vim.v.headless == 1 then
|
||||
-- Headless runs (tests, CI) get no socket; nothing should depend on one.
|
||||
return nil
|
||||
end
|
||||
|
||||
local path = M.socket_path()
|
||||
local ok, res = pcall(vim.fn.serverstart, path)
|
||||
if ok and type(res) == "string" and res ~= "" then
|
||||
-- serverstart returns the bound address string (e.g. "/tmp/omp.sock").
|
||||
vim.notify("nvim_omp: RPC socket ready at " .. path, vim.log.levels.INFO)
|
||||
return path
|
||||
end
|
||||
|
||||
vim.notify(
|
||||
"nvim_omp: could not bind RPC socket at " .. path
|
||||
.. "; another instance may own it or a stale socket needs manual cleanup.",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
return nil
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -9,17 +9,14 @@ let
|
|||
# NOTE: Package list expressions are lazily evaluated, and derivations are
|
||||
# not built until needed, so keep side-effecting expressions out of these
|
||||
# lists.
|
||||
maybe = cat: pkgsList: lib.optionals (config.cats.${cat} or false) pkgsList;
|
||||
rPackages =
|
||||
(pkgs.baseRPackages or [ ])
|
||||
++ config.settings.langPackageDefaults.r
|
||||
++ config.settings.lang_packages.r;
|
||||
maybe = cat: pkgsList:
|
||||
lib.optionals (config.cats.${cat} or false) pkgsList;
|
||||
rPackages = (pkgs.baseRPackages or [ ]) ++ config.settings.lang_packages.r;
|
||||
rWrapperPackages = rPackages;
|
||||
quartoPkg =
|
||||
if config.cats.r or false then
|
||||
pkgs.rpkgs.quarto.override { extraRPackages = rPackages; }
|
||||
else
|
||||
pkgs.quarto;
|
||||
if config.cats.r or false
|
||||
then pkgs.rpkgs.quarto.override { extraRPackages = rPackages; }
|
||||
else pkgs.quarto;
|
||||
in
|
||||
{
|
||||
options.catPkgs = lib.mkOption {
|
||||
|
|
@ -28,38 +25,28 @@ in
|
|||
};
|
||||
|
||||
config.catPkgs = {
|
||||
always = maybe "always" (
|
||||
with pkgs;
|
||||
[
|
||||
always = maybe "always" (with pkgs; [
|
||||
ripgrep
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
clickhouse = maybe "clickhouse" (with pkgs; [ clickhouse-lts ]);
|
||||
|
||||
external = maybe "external" (
|
||||
with pkgs;
|
||||
[
|
||||
external = maybe "external" (with pkgs; [
|
||||
nodejs
|
||||
perl
|
||||
ruby
|
||||
shfmt
|
||||
sqlfluff
|
||||
tree-sitter
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
julia = maybe "julia" [
|
||||
(pkgs.julia-bin.withPackages (
|
||||
config.settings.langPackageDefaults.julia ++ config.settings.lang_packages.julia
|
||||
))
|
||||
(pkgs.julia-bin.withPackages config.settings.lang_packages.julia)
|
||||
];
|
||||
|
||||
lua = maybe "lua" (with pkgs; [ lua-language-server ]);
|
||||
|
||||
markdown = maybe "markdown" (
|
||||
with pkgs;
|
||||
[
|
||||
markdown = maybe "markdown" (with pkgs; [
|
||||
python3Packages.pylatexenc
|
||||
quartoPkg
|
||||
zk
|
||||
|
|
@ -67,21 +54,15 @@ in
|
|||
texlab
|
||||
imagemagick
|
||||
harper
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
nix = maybe "nix" (
|
||||
with pkgs;
|
||||
[
|
||||
nix = maybe "nix" (with pkgs; [
|
||||
alejandra
|
||||
nix-doc
|
||||
nixd
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
optional = maybe "optional" (
|
||||
with pkgs;
|
||||
[
|
||||
optional = maybe "optional" (with pkgs; [
|
||||
bat
|
||||
broot
|
||||
devenv
|
||||
|
|
@ -106,29 +87,21 @@ in
|
|||
tokei
|
||||
wget
|
||||
yq
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
python = maybe "python" (
|
||||
let
|
||||
python = maybe "python" (let
|
||||
python_packages_fn =
|
||||
if pkgs ? basePythonPackages then
|
||||
ps:
|
||||
pkgs.basePythonPackages ps
|
||||
++ config.settings.langPackageDefaults.python
|
||||
++ config.settings.lang_packages.python
|
||||
else
|
||||
_: config.settings.langPackageDefaults.python ++ config.settings.lang_packages.python;
|
||||
if pkgs ? basePythonPackages
|
||||
then ps: pkgs.basePythonPackages ps ++ config.settings.lang_packages.python
|
||||
else _: config.settings.lang_packages.python;
|
||||
python_with_packages = pkgs.python3.withPackages python_packages_fn;
|
||||
in
|
||||
with pkgs;
|
||||
[
|
||||
with pkgs; [
|
||||
python_with_packages
|
||||
ruff
|
||||
basedpyright
|
||||
uv
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
r = maybe "r" [
|
||||
(pkgs.rpkgs.rWrapper.override { packages = rWrapperPackages; })
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
config.settings.config_directory = ../../..;
|
||||
|
||||
# Default colorscheme and background
|
||||
config.settings.colorscheme = lib.mkDefault "kanagawa";
|
||||
config.settings.colorscheme = lib.mkDefault "cyberdream";
|
||||
config.settings.background = lib.mkDefault "dark";
|
||||
|
||||
# Enable RC wrapping (allows neovim to find the config)
|
||||
|
|
|
|||
|
|
@ -14,15 +14,6 @@
|
|||
# R.nvim v1.x owns its cache and temporary directories and exports
|
||||
# RNVIM_COMPLDIR/RNVIM_TMPDIR during setup. Do not inject literal `$PWD`
|
||||
# values into the wrapper environment.
|
||||
#
|
||||
# ponytail: with R_LIBS_USER unset-or-empty, loading compiled namespaces
|
||||
# (S7 via btw) segfaults on macOS for bare-terminal launches outside the
|
||||
# devShell hook. Any non-empty value avoids it; this store path exists,
|
||||
# contains no libraries, and stays inert. The devShell hook overrides it
|
||||
# with $PWD/.r-libs. Upgrade path: teach wlib to emit expandable defaults.
|
||||
(lib.mkIf (config.cats.r or false) {
|
||||
R_LIBS_USER = "${pkgs.rpkgs.rWrapper}";
|
||||
})
|
||||
];
|
||||
|
||||
# Environment variables with defaults (can be overridden by user)
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@
|
|||
...
|
||||
}:
|
||||
let
|
||||
rPackages =
|
||||
(pkgs.baseRPackages or [ ])
|
||||
++ config.settings.langPackageDefaults.r
|
||||
++ config.settings.lang_packages.r;
|
||||
rPackages = (pkgs.baseRPackages or [ ]) ++ config.settings.lang_packages.r;
|
||||
rWrapperPkg = pkgs.rpkgs.rWrapper.override { packages = rPackages ++ [pkgs.nvimcom]; };
|
||||
in
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,30 +5,7 @@
|
|||
...
|
||||
}:
|
||||
{
|
||||
options.settings = {
|
||||
# Built-in language libraries composed into every language spec.
|
||||
# Consumers extend via settings.lang_packages, which is APPENDED to these.
|
||||
langPackageDefaults = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
python = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = [ ];
|
||||
};
|
||||
r = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = [ ];
|
||||
};
|
||||
julia = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
|
||||
lang_packages = lib.mkOption {
|
||||
options.settings.lang_packages = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
python = lib.mkOption {
|
||||
|
|
@ -50,18 +27,18 @@
|
|||
};
|
||||
default = { };
|
||||
description = ''
|
||||
Project-specific language libraries. Appended to settings.langPackageDefaults
|
||||
in each language spec's runtime packages.
|
||||
Language-specific package defaults and downstream overrides appended to each
|
||||
language spec's runtime packages.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config.settings.langPackageDefaults = {
|
||||
python = with pkgs.python3Packages; [
|
||||
config.settings.lang_packages = {
|
||||
python = lib.mkDefault (with pkgs.python3Packages; [
|
||||
duckdb
|
||||
polars
|
||||
];
|
||||
r = with pkgs.rpkgs.rPackages; [
|
||||
]);
|
||||
r = lib.mkDefault (
|
||||
(with pkgs.rpkgs.rPackages; [
|
||||
arrow
|
||||
broom
|
||||
data_table
|
||||
|
|
@ -72,8 +49,9 @@
|
|||
# want to use the nvim-dap R adapter (see plugin/26_dap.lua).
|
||||
# vscDebugger
|
||||
lintr
|
||||
];
|
||||
julia = [
|
||||
])
|
||||
);
|
||||
julia = lib.mkDefault [
|
||||
"DataFramesMeta"
|
||||
"QuackIO"
|
||||
];
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ in {
|
|||
lze
|
||||
lzextras
|
||||
plenary-nvim
|
||||
vimtex
|
||||
neogit
|
||||
{
|
||||
data = mini-nvim;
|
||||
|
|
@ -141,6 +140,7 @@ in {
|
|||
data = with pkgs.vimPlugins; [
|
||||
quarto-nvim
|
||||
render-markdown-nvim
|
||||
vimtex
|
||||
{
|
||||
data = otter-nvim;
|
||||
pname = "otter";
|
||||
|
|
@ -163,8 +163,10 @@ in {
|
|||
pname = "nvim-treesitter";
|
||||
}
|
||||
{
|
||||
data = pkgs.vimPlugins.opencode-nvim;
|
||||
pname = "opencode.nvim";
|
||||
data = pkgs.codecompanion-nvim.overrideAttrs (old: {
|
||||
doCheck = false;
|
||||
});
|
||||
pname = "codecompanion";
|
||||
}
|
||||
] ++ builtins.attrValues pkgs.vimPlugins.nvim-treesitter.queries;
|
||||
};
|
||||
|
|
|
|||
13
nvim.log
13
nvim.log
|
|
@ -1,13 +0,0 @@
|
|||
WRN 2026-07-31T16:36:01.037 ?.50508 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/TN25Kv/nvim.50508.0
|
||||
WRN 2026-07-31T16:36:38.634 ?.50644 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/6jT8As/nvim.50644.0
|
||||
WRN 2026-07-31T16:46:12.752 ?.54395 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/zY36Oi/nvim.54395.0
|
||||
WRN 2026-07-31T17:01:43.138 ?.58656 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/nDisRA/nvim.58656.0
|
||||
WRN 2026-07-31T17:01:50.641 ?.58696 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/okDyqb/nvim.58696.0
|
||||
WRN 2026-07-31T17:02:04.773 ?.58741 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/5wxfRu/nvim.58741.0
|
||||
WRN 2026-07-31T17:02:29.644 ?.58854 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/OMTfpB/nvim.58854.0
|
||||
WRN 2026-07-31T17:02:29.644 ?.58852 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/QZvLy2/nvim.58852.0
|
||||
WRN 2026-07-31T17:14:31.289 ?.64329 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/zh9tLm/nvim.64329.0
|
||||
WRN 2026-07-31T17:14:40.017 ?.64399 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/pGaDez/nvim.64399.0
|
||||
WRN 2026-07-31T17:17:48.228 ?.64921 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/ryMpX0/nvim.64921.0
|
||||
WRN 2026-07-31T17:17:52.664 ?.64953 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/X7RmXh/nvim.64953.0
|
||||
WRN 2026-07-31T17:18:00.691 ?.64989 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/C6njjq/nvim.64989.0
|
||||
|
|
@ -1,6 +1,22 @@
|
|||
{ ... }:
|
||||
final: prev:
|
||||
{
|
||||
codecompanion-nvim = prev.vimPlugins.codecompanion-nvim.overrideAttrs {
|
||||
checkInputs = with prev.vimPlugins; [
|
||||
blink-cmp
|
||||
mini-nvim
|
||||
];
|
||||
dependencies = [ prev.vimPlugins.plenary-nvim ];
|
||||
nvimSkipModules = [
|
||||
"codecompanion.actions.static"
|
||||
"codecompanion.actions.init"
|
||||
"minimal"
|
||||
"codecompanion.providers.actions.fzf_lua"
|
||||
"codecompanion.providers.completion.cmp.setup"
|
||||
"codecompanion.providers.actions.telescope"
|
||||
"codecompanion.providers.actions.snacks"
|
||||
];
|
||||
};
|
||||
zk-nvim = prev.vimPlugins.zk-nvim.overrideAttrs {
|
||||
nvimSkipModules = [
|
||||
"zk.pickers.fzf_lua"
|
||||
|
|
|
|||
|
|
@ -9,14 +9,4 @@ in
|
|||
{
|
||||
basePythonPackages = reqPkgs;
|
||||
python = prev.python3.withPackages reqPkgs;
|
||||
|
||||
# pyarrow's test_timezone_absent reads /usr/share/zoneinfo, which is
|
||||
# blocked by the build sandbox (PermissionError: Operation not permitted).
|
||||
python3Packages = prev.python3Packages // {
|
||||
pyarrow = prev.python3Packages.pyarrow.overridePythonAttrs (old: {
|
||||
disabledTests = (old.disabledTests or [ ]) ++ [
|
||||
"pyarrow/tests/test_orc.py::test_timezone_absent"
|
||||
];
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
};
|
||||
in {
|
||||
inherit rpkgs;
|
||||
baseRPackages = [rpkgs.nvimcom rpkgs.rPackages.btw];
|
||||
baseRPackages = [rpkgs.nvimcom];
|
||||
rWrapper = rpkgs.rWrapper.override {packages = [];};
|
||||
quarto = rpkgs.quarto.override {extraRPackages = [];};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,69 +119,3 @@ Config.add = (function(pkg)
|
|||
end)
|
||||
|
||||
Config.now_if_args = vim.fn.argc(-1) > 0 and MiniDeps.now or MiniDeps.later
|
||||
|
||||
-- Checkbox
|
||||
local checked_character = "x"
|
||||
|
||||
local checked_checkbox = "%[" .. checked_character .. "%]"
|
||||
local unchecked_checkbox = "%[ %]"
|
||||
|
||||
local line_contains_unchecked = function(line)
|
||||
return line:find(unchecked_checkbox)
|
||||
end
|
||||
|
||||
local line_contains_checked = function(line)
|
||||
return line:find(checked_checkbox)
|
||||
end
|
||||
|
||||
local line_with_checkbox = function(line)
|
||||
return line:find("^%s*- " .. checked_checkbox)
|
||||
or line:find("^%s*- " .. unchecked_checkbox)
|
||||
or line:find("^%s*%d%. " .. checked_checkbox)
|
||||
or line:find("^%s*%d%. " .. unchecked_checkbox)
|
||||
end
|
||||
|
||||
local checkbox = {
|
||||
check = function(line)
|
||||
return line:gsub(unchecked_checkbox, checked_checkbox, 1)
|
||||
end,
|
||||
|
||||
uncheck = function(line)
|
||||
return line:gsub(checked_checkbox, unchecked_checkbox, 1)
|
||||
end,
|
||||
|
||||
make_checkbox = function(line)
|
||||
if not line:match("^%s*%-%s") and not line:match("^%s*%d%.%s") then
|
||||
-- "xxx" -> "- [ ] xxx"
|
||||
return line:gsub("(%S+)", "- [ ] %1", 1)
|
||||
else
|
||||
-- "- xxx" -> "- [ ] xxx", "3. xxx" -> "3. [ ] xxx"
|
||||
return line:gsub("(%s*- )(.*)", "%1[ ] %2", 1):gsub("(%s*%d%. )(.*)", "%1[ ] %2", 1)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
Config.togglecb = function()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||
local start_line = cursor[1] - 1
|
||||
local current_line = vim.api.nvim_buf_get_lines(bufnr, start_line, start_line + 1, false)[1] or ""
|
||||
|
||||
-- If the line contains a checked checkbox then uncheck it.
|
||||
-- Otherwise, if it contains an unchecked checkbox, check it.
|
||||
local new_line = ""
|
||||
|
||||
if not line_with_checkbox(current_line) then
|
||||
new_line = checkbox.make_checkbox(current_line)
|
||||
elseif line_contains_unchecked(current_line) then
|
||||
new_line = checkbox.check(current_line)
|
||||
elseif line_contains_checked(current_line) then
|
||||
new_line = checkbox.uncheck(current_line)
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_lines(bufnr, start_line, start_line + 1, false, { new_line })
|
||||
vim.api.nvim_win_set_cursor(0, cursor)
|
||||
end
|
||||
|
||||
vim.api.nvim_create_user_command("ToggleCheckbox", Config.togglecb, {})
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ local defaults = {
|
|||
duckdb = "duckdb",
|
||||
julia = "julia",
|
||||
python = "ipython",
|
||||
shell = "echo 'Hello " .. (vim.env.USER or "user") .. "!'",
|
||||
shell = "echo 'Hello " .. vim.env.USER .. "!'",
|
||||
}
|
||||
|
||||
-- Registry of terminal commands
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ end)
|
|||
-- Treesitter
|
||||
|
||||
now_if_args(function()
|
||||
vim.treesitter.language.register("markdown", { "markdown", "rmd", "quarto" })
|
||||
vim.treesitter.language.register("markdown", { "markdown", "codecompanion", "rmd", "quarto" })
|
||||
|
||||
require 'treesitter-context'.setup {
|
||||
enable = true,
|
||||
|
|
@ -216,7 +216,7 @@ now_if_args(function()
|
|||
"julia", "rnoweb", "latex", "gitcommit", "gitignore",
|
||||
"git_config", "git_rebase", "diff", "dockerfile",
|
||||
"make", "xml", "zig", "regex", "csv", "bash",
|
||||
"markdown_inline", "quarto", "rmd",
|
||||
"markdown_inline", "quarto", "rmd", "codecompanion",
|
||||
}
|
||||
local function start_treesitter(buf, filetype)
|
||||
local lang = vim.treesitter.language.get_lang(filetype) or filetype
|
||||
|
|
|
|||
|
|
@ -55,24 +55,21 @@ later(function()
|
|||
ignore_exitcode = true,
|
||||
parser = function(output, bufnr, linter_cwd)
|
||||
local diagnostics = {}
|
||||
-- Expected format: /path/file.R:10:5: style: Some message
|
||||
-- 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,
|
||||
}
|
||||
for line in output:gmatch("[^\r\n]+") do
|
||||
-- Capture the file path as well so lnum/col line up with the numbers.
|
||||
local path, lnum, col, severity, message = line:match("^(.-):(%d+):(%d+):%s*(%w+):%s*(.+)$")
|
||||
if path and lnum and col and severity then
|
||||
local line_num = tonumber(lnum)
|
||||
local col_num = tonumber(col)
|
||||
table.insert(diagnostics, {
|
||||
bufnr = bufnr,
|
||||
lnum = math.max(0, line_num - 1),
|
||||
col = math.max(0, col_num - 1),
|
||||
end_lnum = line_num - 1,
|
||||
end_col = col_num,
|
||||
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",
|
||||
|
|
@ -102,7 +99,7 @@ now_if_args(function()
|
|||
add("render-markdown.nvim")
|
||||
require('render-markdown').setup({
|
||||
-- completions = { blink = { enabled = true } },
|
||||
file_types = { 'markdown', },
|
||||
file_types = { 'markdown', 'codecompanion', },
|
||||
link = {
|
||||
wiki = {
|
||||
body = function(ctx)
|
||||
|
|
|
|||
|
|
@ -37,12 +37,7 @@ later(function()
|
|||
end)
|
||||
|
||||
later(function()
|
||||
require("mini.align").setup({
|
||||
mappings = {
|
||||
start = "gA",
|
||||
start_with_preview = "g<C-A>",
|
||||
},
|
||||
})
|
||||
require("mini.align").setup()
|
||||
end)
|
||||
|
||||
later(function()
|
||||
|
|
@ -218,8 +213,6 @@ later(function()
|
|||
local minikeymap = require("mini.keymap")
|
||||
minikeymap.setup()
|
||||
local map_multistep = minikeymap.map_multistep
|
||||
-- blink_next/blink_prev/blink_accept are blink.cmp actions.
|
||||
-- Direct blink keymaps (C-space/C-l) are set in 24_completion.lua.
|
||||
local tab_steps = {
|
||||
"blink_next",
|
||||
"pmenu_next",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ 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",
|
||||
|
|
@ -11,7 +14,7 @@ local PLUGIN_SOURCES = {
|
|||
"zbirenbaum/copilot.lua",
|
||||
"jmbuhr/cmp-pandoc-references",
|
||||
"fang2hou/blink-copilot",
|
||||
"nickjvandyke/opencode.nvim",
|
||||
"olimorris/codecompanion.nvim"
|
||||
}
|
||||
|
||||
local PLUGIN_ADDS = {
|
||||
|
|
@ -21,16 +24,34 @@ local PLUGIN_ADDS = {
|
|||
"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()
|
||||
return {
|
||||
sorts = { "exact", "score", "sort_text" },
|
||||
use_proximity = true,
|
||||
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
|
||||
|
|
@ -41,6 +62,7 @@ if not Config.isNixCats then
|
|||
add({
|
||||
source = "saghen/blink.cmp",
|
||||
depends = { "rafamadriz/friendly-snippets" },
|
||||
checkout = BLINK_VERSION,
|
||||
})
|
||||
end)
|
||||
|
||||
|
|
@ -51,6 +73,241 @@ if not Config.isNixCats then
|
|||
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
|
||||
|
|
@ -102,33 +359,26 @@ later(function()
|
|||
})
|
||||
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()
|
||||
vim.g.opencode_opts = {
|
||||
events = {
|
||||
reload = true,
|
||||
permissions = {
|
||||
enabled = true,
|
||||
edits = { enabled = true },
|
||||
},
|
||||
},
|
||||
server = {
|
||||
start = function()
|
||||
vim.cmd("vsplit term://opencode --port")
|
||||
vim.cmd("vertical resize " .. math.floor(vim.o.columns * 0.4))
|
||||
vim.cmd("wincmd p")
|
||||
end
|
||||
},
|
||||
}
|
||||
add("opencode.nvim")
|
||||
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({
|
||||
-- Direct blink keymaps (C-space/C-l).
|
||||
-- Tab/Enter/Up/Down are handled via multistep chains in 23_editor.lua,
|
||||
-- which chain blink_next/blink_prev/blink_accept with other editor actions.
|
||||
keymap = {
|
||||
preset = "default",
|
||||
["<C-space>"] = { "show", "select_next" },
|
||||
|
|
@ -165,7 +415,6 @@ now_if_args(function()
|
|||
},
|
||||
completion = {
|
||||
menu = {
|
||||
border = "rounded",
|
||||
draw = {
|
||||
treesitter = { "lsp" },
|
||||
components = {
|
||||
|
|
@ -185,12 +434,14 @@ now_if_args(function()
|
|||
list = {
|
||||
selection = { preselect = false, auto_insert = true }
|
||||
},
|
||||
ghost_text = { enabled = true, show_with_menu = true },
|
||||
documentation = { auto_show = true, window = { border = "rounded" } },
|
||||
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 = {
|
||||
|
|
@ -216,6 +467,12 @@ now_if_args(function()
|
|||
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",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ now_if_args(function()
|
|||
basedpyright = {},
|
||||
ruff = {},
|
||||
marksman = {
|
||||
filetypes = { "markdown", "markdown_inline" },
|
||||
filetypes = { "markdown", "markdown_inline", "codecompanion" },
|
||||
},
|
||||
harper_ls = {
|
||||
cmd = { "harper-ls", "--stdio" },
|
||||
|
|
|
|||
|
|
@ -11,19 +11,24 @@ later(function()
|
|||
return
|
||||
end
|
||||
|
||||
-- vimtex is a VimL plugin: it does not expose a Lua module. Configure it
|
||||
-- via globals before loading so the plugin picks them up on startup.
|
||||
vim.g.vimtex_compiler_method = "latexmk"
|
||||
vim.g.vimtex_compiler_latexmk = {
|
||||
-- Keep conservative defaults: no continuous background compilation and no
|
||||
-- callback chatter. Manual :VimtexCompile still works on demand.
|
||||
continuous = 0,
|
||||
callback = 0,
|
||||
}
|
||||
-- Let vimtex choose the first available viewer (zathura, Skim, Evince, ...).
|
||||
|
||||
Config.add("vimtex")
|
||||
|
||||
local ok, vimtex = pcall(require, "vimtex")
|
||||
if not ok then
|
||||
vim.notify("vimtex not available", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
-- Keep conservative defaults: latexmk continuous compilation off, single
|
||||
-- viewer (zathura falls back to Evince on most setups).
|
||||
vimtex.setup({
|
||||
enabled = true,
|
||||
compile_on_save = false,
|
||||
compiler = "latexmk",
|
||||
-- Avoid hooking spell/formatting into our global <leader> group;
|
||||
-- vimtex stashes its own <localleader> mappings automatically.
|
||||
})
|
||||
|
||||
-- Filetype detection is normally on, but force it explicitly so .tex files
|
||||
-- opened outside Quarto still pick up the LSP + viewer hooks.
|
||||
vim.api.nvim_create_autocmd("BufReadPost", {
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
-- Minimal Markdown checkbox toggler: `:ToggleCheckbox` (mapped to <leader>mc).
|
||||
-- Toggles `- [ ]`/`- [x]` (and numbered `N. [ ]`/`N. [x]`) on the current line;
|
||||
-- creates a checkbox on lines without one.
|
||||
|
||||
local checked_character = "x"
|
||||
|
||||
local checked_checkbox = "%[" .. checked_character .. "%]"
|
||||
local unchecked_checkbox = "%[ %]"
|
||||
|
||||
local line_contains_unchecked = function(line)
|
||||
return line:find(unchecked_checkbox)
|
||||
end
|
||||
|
||||
local line_contains_checked = function(line)
|
||||
return line:find(checked_checkbox)
|
||||
end
|
||||
|
||||
local line_with_checkbox = function(line)
|
||||
return line:find("^%s*- " .. checked_checkbox)
|
||||
or line:find("^%s*- " .. unchecked_checkbox)
|
||||
or line:find("^%s*%d%. " .. checked_checkbox)
|
||||
or line:find("^%s*%d%. " .. unchecked_checkbox)
|
||||
end
|
||||
|
||||
local checkbox = {
|
||||
check = function(line)
|
||||
return line:gsub(unchecked_checkbox, checked_checkbox, 1)
|
||||
end,
|
||||
|
||||
uncheck = function(line)
|
||||
return line:gsub(checked_checkbox, unchecked_checkbox, 1)
|
||||
end,
|
||||
|
||||
make_checkbox = function(line)
|
||||
if not line:match("^%s*%-%s") and not line:match("^%s*%d%.%s") then
|
||||
-- "xxx" -> "- [ ] xxx"
|
||||
return line:gsub("(%S+)", "- [ ] %1", 1)
|
||||
else
|
||||
-- "- xxx" -> "- [ ] xxx", "3. xxx" -> "3. [ ] xxx"
|
||||
return line:gsub("(%s*- )(.*)", "%1[ ] %2", 1):gsub("(%s*%d%. )(.*)", "%1[ ] %2", 1)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local M = {}
|
||||
|
||||
M.toggle = function()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||
local start_line = cursor[1] - 1
|
||||
local current_line = vim.api.nvim_buf_get_lines(bufnr, start_line, start_line + 1, false)[1] or ""
|
||||
|
||||
-- If the line contains a checked checkbox then uncheck it.
|
||||
-- Otherwise, if it contains an unchecked checkbox, check it.
|
||||
local new_line = ""
|
||||
|
||||
if not line_with_checkbox(current_line) then
|
||||
new_line = checkbox.make_checkbox(current_line)
|
||||
elseif line_contains_unchecked(current_line) then
|
||||
new_line = checkbox.check(current_line)
|
||||
elseif line_contains_checked(current_line) then
|
||||
new_line = checkbox.uncheck(current_line)
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_lines(bufnr, start_line, start_line + 1, false, { new_line })
|
||||
vim.api.nvim_win_set_cursor(0, cursor)
|
||||
end
|
||||
|
||||
vim.api.nvim_create_user_command("ToggleCheckbox", M.toggle, {})
|
||||
|
||||
return M
|
||||
1
result
1
result
|
|
@ -1 +0,0 @@
|
|||
/nix/store/12g17cxiz4wkn5zy483xhbkybpqwc9k4-neovim-unwrapped-0.12.4
|
||||
268
tests/smoke.lua
268
tests/smoke.lua
|
|
@ -1,268 +0,0 @@
|
|||
-- Comprehensive CI smoke tests for the Neovim configuration.
|
||||
-- Run inside the wrapped Neovim binary, e.g.:
|
||||
-- vv --headless -c "luafile tests/smoke.lua" -c "qa!"
|
||||
--
|
||||
-- The script loads the full config (plugin/*.lua files are sourced by Neovim on
|
||||
-- startup), waits for mini.deps deferred work, then exercises key functionality
|
||||
-- without user interaction.
|
||||
|
||||
local M = {}
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Tiny test harness
|
||||
-- ---------------------------------------------------------------------------
|
||||
local failures = {}
|
||||
local passed = 0
|
||||
|
||||
local function fail(msg)
|
||||
table.insert(failures, msg)
|
||||
print(" FAIL: " .. msg)
|
||||
end
|
||||
|
||||
local function pass(msg)
|
||||
passed = passed + 1
|
||||
print(" PASS: " .. msg)
|
||||
end
|
||||
|
||||
local function assert_eq(a, b, msg)
|
||||
if a == b then
|
||||
pass(msg)
|
||||
else
|
||||
fail(string.format("%s (expected %s, got %s)", msg, vim.inspect(b), vim.inspect(a)))
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_true(cond, msg)
|
||||
if cond then
|
||||
pass(msg)
|
||||
else
|
||||
fail(msg)
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_loaded(mod, msg)
|
||||
assert_true(package.loaded[mod] ~= nil, msg or ("module '" .. mod .. "' loaded"))
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Wait for deferred mini.deps/later work.
|
||||
-- We patch MiniDeps.later to count pending callbacks and then drain the event
|
||||
-- loop until the counter returns to zero (or we time out).
|
||||
-- ---------------------------------------------------------------------------
|
||||
local function wait_for_deferred(timeout_ms)
|
||||
timeout_ms = timeout_ms or 10000
|
||||
|
||||
-- Fire VimEnter so startup autocmds run, then drain the event loop so
|
||||
-- mini.deps' deferred setup closures have a chance to execute before we
|
||||
-- assert anything.
|
||||
-- The headless smoke run has no start screen to display. Disabling
|
||||
-- mini.starter also prevents it from replacing the current buffer and
|
||||
-- trying to create a swap file in the read-only Nix sandbox.
|
||||
vim.g.ministarter_disable = true
|
||||
vim.cmd('doautocmd VimEnter')
|
||||
vim.wait(timeout_ms, function() return false end, 50)
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Helper: check whether a keymap is registered for a given lhs in normal mode.
|
||||
-- ---------------------------------------------------------------------------
|
||||
local function has_normal_map(lhs)
|
||||
-- <leader> is stored as the literal leader key, so test both the raw
|
||||
-- symbolic form and the expanded form (e.g. "<leader>ff" and " ff").
|
||||
local expanded = lhs:gsub('^<leader>', vim.g.mapleader or '\\')
|
||||
|
||||
if vim.keymap and vim.keymap.get then
|
||||
local maps = vim.keymap.get('n')
|
||||
for _, map in ipairs(maps) do
|
||||
if map.lhs == lhs or map.lhs == expanded then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return vim.fn.maparg(lhs, 'n') ~= '' or vim.fn.maparg(expanded, 'n') ~= ''
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Helper: check whether a Treesitter parser is available.
|
||||
-- ---------------------------------------------------------------------------
|
||||
local function has_parser(lang)
|
||||
return #vim.api.nvim_get_runtime_file("parser/" .. lang .. ".*", false) > 0
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Helper: check whether an LSP config was registered (Neovim 0.11+ native API).
|
||||
-- ---------------------------------------------------------------------------
|
||||
local function has_lsp_config(name)
|
||||
if vim.lsp and vim.lsp.config then
|
||||
-- Neovim 0.12 exposes configs through the callable `vim.lsp.config`
|
||||
-- table; older versions expose a function-like API.
|
||||
local ok, cfg = pcall(function() return vim.lsp.config[name] end)
|
||||
if not ok or type(cfg) ~= 'table' or next(cfg) == nil then
|
||||
ok, cfg = pcall(vim.lsp.config, name)
|
||||
end
|
||||
if ok and cfg and next(cfg) ~= nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- Fallback: inspect lspconfig internal table.
|
||||
local ok, configs = pcall(require, 'lspconfig.configs')
|
||||
if ok and configs and configs[name] then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Test suites
|
||||
-- ---------------------------------------------------------------------------
|
||||
function M.test_core_config()
|
||||
print("\n=== Core configuration ===")
|
||||
assert_eq(vim.g.mapleader, ' ', "leader is space")
|
||||
assert_eq(vim.g.maplocalleader, ',', "localleader is comma")
|
||||
assert_true(vim.o.backup, "backup option enabled")
|
||||
assert_true(vim.o.undofile, "undofile enabled")
|
||||
assert_eq(vim.o.mouse, 'a', "mouse enabled")
|
||||
end
|
||||
|
||||
function M.test_config_module()
|
||||
print("\n=== Config module ===")
|
||||
assert_true(_G.Config ~= nil, "global Config table exists")
|
||||
assert_true(type(Config.edit) == 'function', "Config.edit helper exists")
|
||||
assert_true(type(Config.terminal) == 'table', "Config.terminal namespace exists")
|
||||
assert_true(type(Config.treesitter_helpers) == 'table', "Config.treesitter_helpers exists")
|
||||
end
|
||||
|
||||
function M.test_mini_modules()
|
||||
print("\n=== mini.nvim modules ===")
|
||||
assert_loaded('mini.basics', 'mini.basics loaded')
|
||||
assert_loaded('mini.statusline', 'mini.statusline loaded')
|
||||
assert_loaded('mini.tabline', 'mini.tabline loaded')
|
||||
assert_loaded('mini.clue', 'mini.clue loaded')
|
||||
assert_loaded('mini.pick', 'mini.pick loaded')
|
||||
assert_loaded('mini.notify', 'mini.notify loaded')
|
||||
end
|
||||
|
||||
function M.test_keymaps()
|
||||
print("\n=== Keymaps ===")
|
||||
assert_true(has_normal_map('<leader>ff'), "leader ff -> pick files")
|
||||
assert_true(has_normal_map('<leader>fg'), "leader fg -> live grep")
|
||||
assert_true(has_normal_map('<leader>bb'), "leader bb -> alternate buffer")
|
||||
assert_true(has_normal_map('<leader>ed'), "leader ed -> mini.files open")
|
||||
assert_true(has_normal_map('<Esc>'), "Esc clears search highlight")
|
||||
end
|
||||
|
||||
function M.test_treesitter()
|
||||
print("\n=== Treesitter parsers ===")
|
||||
local expected = { 'lua', 'python', 'nix', 'markdown', 'latex', 'r', 'julia' }
|
||||
for _, lang in ipairs(expected) do
|
||||
assert_true(has_parser(lang), "parser available: " .. lang)
|
||||
end
|
||||
end
|
||||
|
||||
function M.test_filetype_detection()
|
||||
print("\n=== Filetype detection ===")
|
||||
local test_buf = vim.api.nvim_create_buf(false, true)
|
||||
local orig_buf = vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_set_current_buf(test_buf)
|
||||
|
||||
-- Test by manually triggering filetype detection for a couple of languages.
|
||||
vim.api.nvim_buf_set_name(test_buf, 'test.py')
|
||||
vim.api.nvim_set_option_value('filetype', 'python', { buf = test_buf })
|
||||
assert_eq(vim.bo.filetype, 'python', "python filetype set")
|
||||
|
||||
vim.api.nvim_buf_set_name(test_buf, 'test.lua')
|
||||
vim.api.nvim_set_option_value('filetype', 'lua', { buf = test_buf })
|
||||
assert_eq(vim.bo.filetype, 'lua', "lua filetype set")
|
||||
|
||||
vim.api.nvim_set_current_buf(orig_buf)
|
||||
vim.api.nvim_buf_delete(test_buf, { force = true })
|
||||
end
|
||||
|
||||
function M.test_lsp_config()
|
||||
print("\n=== LSP server registration ===")
|
||||
local servers = { 'lua_ls', 'basedpyright', 'ruff', 'nil_ls', 'texlab', 'marksman', 'harper_ls' }
|
||||
for _, name in ipairs(servers) do
|
||||
assert_true(has_lsp_config(name), "LSP config registered: " .. name)
|
||||
end
|
||||
end
|
||||
|
||||
function M.test_plugin_configs()
|
||||
print("\n=== Plugin-specific configuration ===")
|
||||
-- vimtex globals should be set when the markdown cat is on.
|
||||
if vim.g.vimtex_compiler_method then
|
||||
assert_eq(vim.g.vimtex_compiler_method, 'latexmk', "vimtex compiler is latexmk")
|
||||
else
|
||||
pass("vimtex not configured (markdown cat disabled)")
|
||||
end
|
||||
|
||||
-- conform formatters should be registered.
|
||||
local ok, conform = pcall(require, 'conform')
|
||||
if ok and conform then
|
||||
local formatters = require('conform').formatters
|
||||
assert_true(formatters ~= nil, "conform formatters table exists")
|
||||
else
|
||||
pass("conform not loaded (expected if utils cat disabled)")
|
||||
end
|
||||
|
||||
-- blink.cmp keymap preset should be available.
|
||||
local blink_ok, blink = pcall(require, 'blink.cmp')
|
||||
if blink_ok and blink then
|
||||
pass("blink.cmp loaded")
|
||||
else
|
||||
pass("blink.cmp not loaded (expected if utils cat disabled)")
|
||||
end
|
||||
end
|
||||
|
||||
function M.test_nix_cats_helper()
|
||||
print("\n=== nixCats helper ===")
|
||||
local ok, nix = pcall(require, 'config.nix')
|
||||
assert_true(ok and nix ~= nil, "config.nix can be required")
|
||||
if ok and nix then
|
||||
local cat = nix.get_cat('general', true)
|
||||
assert_true(type(cat) == 'boolean', "get_cat returns boolean default")
|
||||
end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Entry point
|
||||
-- ---------------------------------------------------------------------------
|
||||
function M.run()
|
||||
print("Neovim config CI smoke tests")
|
||||
local v = vim.version()
|
||||
print(string.format("Neovim version: %d.%d.%d", v.major, v.minor, v.patch))
|
||||
|
||||
-- Drain deferred plugin setup before asserting.
|
||||
wait_for_deferred()
|
||||
|
||||
M.test_core_config()
|
||||
M.test_config_module()
|
||||
M.test_mini_modules()
|
||||
M.test_keymaps()
|
||||
M.test_treesitter()
|
||||
M.test_filetype_detection()
|
||||
M.test_lsp_config()
|
||||
M.test_plugin_configs()
|
||||
M.test_nix_cats_helper()
|
||||
|
||||
print("\n=== Summary ===")
|
||||
print(string.format("Passed: %d", passed))
|
||||
print(string.format("Failed: %d", #failures))
|
||||
|
||||
if #failures > 0 then
|
||||
print("\nFailed tests:")
|
||||
for _, f in ipairs(failures) do
|
||||
print(" - " .. f)
|
||||
end
|
||||
vim.cmd('cquit 1')
|
||||
else
|
||||
print("\nAll smoke tests passed!")
|
||||
vim.cmd('cquit 0')
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = pcall(M.run)
|
||||
if not ok then
|
||||
print("CRASH: " .. tostring(err))
|
||||
vim.cmd('cquit 1')
|
||||
end
|
||||
Loading…
Reference in a new issue