mirror of
https://github.com/dwinkler1/nvimConfig.git
synced 2026-08-22 17:43:13 -04:00
Compare commits
60 changed files with 1532 additions and 3069 deletions
|
|
@ -1,5 +0,0 @@
|
||||||
if (Sys.getenv("RNVIM_TMPDIR") == "") {
|
|
||||||
options(defaultPackages = c("utils", "grDevices", "graphics", "stats", "methods"))
|
|
||||||
} else {
|
|
||||||
options(defaultPackages = c("utils", "grDevices", "graphics", "stats", "methods", "nvimcom"))
|
|
||||||
}
|
|
||||||
18
.github/workflows/check.yml
vendored
18
.github/workflows/check.yml
vendored
|
|
@ -5,20 +5,12 @@ on:
|
||||||
paths:
|
paths:
|
||||||
- 'flake.lock'
|
- 'flake.lock'
|
||||||
- 'flake.nix'
|
- 'flake.nix'
|
||||||
- 'modules/**'
|
- 'modules'
|
||||||
- 'plugin/**'
|
|
||||||
- 'lua/**'
|
|
||||||
- 'overlays/**'
|
|
||||||
- 'ftplugin/**'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'flake.lock'
|
- 'flake.lock'
|
||||||
- 'flake.nix'
|
- 'flake.nix'
|
||||||
- 'modules/**'
|
- 'modules'
|
||||||
- 'plugin/**'
|
|
||||||
- 'lua/**'
|
|
||||||
- 'overlays/**'
|
|
||||||
- 'ftplugin/**'
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
@ -29,7 +21,7 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest, macos-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- uses: wimpysworld/nothing-but-nix@main
|
- uses: wimpysworld/nothing-but-nix@main
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
with:
|
with:
|
||||||
|
|
@ -37,11 +29,11 @@ jobs:
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
github_access_token: ${{ secrets.GH_TOKEN }}
|
github_access_token: ${{ secrets.GH_TOKEN }}
|
||||||
- uses: cachix/cachix-action@v17
|
- uses: cachix/cachix-action@v14
|
||||||
with:
|
with:
|
||||||
name: rde
|
name: rde
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
extraPullNames: rstats-on-nix, nix-community
|
extraPullNames: rstats-on-nix, nix-community
|
||||||
- run: nix build
|
- run: nix build
|
||||||
- run: nix develop -c echo ok
|
- run: nix develop
|
||||||
- run: nix flake check
|
- run: nix flake check
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ jobs:
|
||||||
id-token: "write"
|
id-token: "write"
|
||||||
contents: "read"
|
contents: "read"
|
||||||
steps:
|
steps:
|
||||||
- uses: "actions/checkout@v7"
|
- uses: "actions/checkout@v5"
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: "DeterminateSystems/determinate-nix-action@v3"
|
- 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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v4
|
||||||
- uses: wimpysworld/nothing-but-nix@main
|
- uses: wimpysworld/nothing-but-nix@main
|
||||||
with:
|
with:
|
||||||
hatchet-protocol: 'carve'
|
hatchet-protocol: 'carve'
|
||||||
- name: Install Determinate Nix
|
- name: Install Determinate Nix
|
||||||
uses: DeterminateSystems/determinate-nix-action@v3
|
uses: DeterminateSystems/determinate-nix-action@v3
|
||||||
- uses: cachix/cachix-action@v17
|
- uses: cachix/cachix-action@v14
|
||||||
with:
|
with:
|
||||||
name: rde
|
name: rde
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,6 +3,3 @@
|
||||||
*.R
|
*.R
|
||||||
.Rlibs
|
.Rlibs
|
||||||
.nvimcom
|
.nvimcom
|
||||||
.commandcode
|
|
||||||
.commandcode/
|
|
||||||
.r-*
|
|
||||||
|
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
# 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).
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
# 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).
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"lastAgent": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf",
|
|
||||||
"sessionsByServer": {
|
|
||||||
"api.letta.com": {
|
|
||||||
"agentId": "agent-21b21fd6-49e4-4d5c-a1b7-1c6dcd8dacc9",
|
|
||||||
"conversationId": "default"
|
|
||||||
},
|
|
||||||
"local:/Users/daniel/.letta/lc-local-backend": {
|
|
||||||
"agentId": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf",
|
|
||||||
"conversationId": "default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lastSession": {
|
|
||||||
"agentId": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf",
|
|
||||||
"conversationId": "default"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
||||||
// omp custom tool: read this machine's running Neovim buffers over its RPC
|
|
||||||
// socket. Read-only and on-demand — the model calls it when it needs buffer
|
|
||||||
// content; nothing is injected into prompts automatically.
|
|
||||||
//
|
|
||||||
// Install options
|
|
||||||
// 1. Project scope (this project): keep the file in .omp/tools/ — omp picks it
|
|
||||||
// up when a session's cwd is inside this repository.
|
|
||||||
// 2. All projects: mkdir -p ~/.omp/agent/tools
|
|
||||||
// ln -s "$PWD/.omp/tools/nvim_buffers.mjs" ~/.omp/agent/tools/
|
|
||||||
// then restart `omp` (custom tools are loaded at session bootstrap).
|
|
||||||
//
|
|
||||||
// Socket contract (must match lua/nvim_omp/init.lua):
|
|
||||||
// path = $NVIM_OMP_SOCKET or <XDG_STATE_HOME|~/.local/state>/nvim/omp.sock
|
|
||||||
// The nvim instance running this config starts the listener at startup.
|
|
||||||
// Only one instance can own the socket; a second instance never replaces a
|
|
||||||
// live owner. Remove a stale socket manually only after confirming no nvim
|
|
||||||
// process owns it.
|
|
||||||
|
|
||||||
import os from "node:os";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
// Safe default cap so a large buffer cannot flood the conversation.
|
|
||||||
const DEFAULT_MAX_LINES = 2000;
|
|
||||||
|
|
||||||
export default function (pi) {
|
|
||||||
// Resolve socket path with the same rule as lua/nvim_omp/init.lua.
|
|
||||||
const socketPath = () => {
|
|
||||||
const env = process.env.NVIM_OMP_SOCKET;
|
|
||||||
if (env) return env;
|
|
||||||
const state =
|
|
||||||
process.env.XDG_STATE_HOME ||
|
|
||||||
path.join(os.homedir(), ".local", "state");
|
|
||||||
return path.join(state, "nvim", "omp.sock");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Evaluate expr in the remote nvim via `nvim --server`; returns parsed JSON.
|
|
||||||
const rpc = async (expr, signal) => {
|
|
||||||
const bin = process.env.NVIM_BIN || "nvim";
|
|
||||||
const { code, stdout, stderr, killed } = await pi.exec(
|
|
||||||
bin,
|
|
||||||
["--server", socketPath(), "--remote-expr", expr],
|
|
||||||
{ signal },
|
|
||||||
);
|
|
||||||
if (killed) throw new Error("cancelled");
|
|
||||||
if (code !== 0) {
|
|
||||||
const why = String(stderr || "").trim();
|
|
||||||
throw new Error(
|
|
||||||
why.includes("E247")
|
|
||||||
? `No Neovim RPC socket at ${socketPath()}. ` +
|
|
||||||
"Start nvim (or vv) first — the 31_nvim_omp.lua plugin binds the socket at startup."
|
|
||||||
: `nvim RPC failed (${code}): ${why}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const text = String(stdout ?? "").trim();
|
|
||||||
if (!text) throw new Error("empty response from nvim RPC");
|
|
||||||
return JSON.parse(text);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Resolve a buffer argument (number, string, or partial path) to a buffer
|
|
||||||
// number. Returns null when nothing matches.
|
|
||||||
const resolveBuf = async (buffer, signal) => {
|
|
||||||
if (buffer === undefined || buffer === null || buffer === "") {
|
|
||||||
return rpc("json_encode(nvim_get_current_buf())", signal);
|
|
||||||
}
|
|
||||||
if (typeof buffer === "number") return buffer;
|
|
||||||
const bufnum = Number(buffer);
|
|
||||||
if (Number.isInteger(bufnum) && bufnum > 0) return bufnum;
|
|
||||||
const list = await rpc(
|
|
||||||
"json_encode(map(nvim_list_bufs(), {i, v -> " +
|
|
||||||
"{'bufnr': v, 'name': nvim_buf_get_name(v)}}))",
|
|
||||||
signal,
|
|
||||||
);
|
|
||||||
const q = String(buffer);
|
|
||||||
const hit =
|
|
||||||
list.find(
|
|
||||||
(b) => b.name === q || b.name.endsWith("/" + q) || b.name.endsWith(q),
|
|
||||||
) ||
|
|
||||||
list.find((b) => b.name.includes(q));
|
|
||||||
return hit ? hit.bufnr : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tools = [];
|
|
||||||
|
|
||||||
// List open buffers -----------------------------------------------
|
|
||||||
tools.push({
|
|
||||||
name: "nvim_buffers",
|
|
||||||
label: "Neovim Buffers",
|
|
||||||
description:
|
|
||||||
"List the buffers currently open in the user's running Neovim " +
|
|
||||||
"(number, name, current/loaded state, modified). Use before " +
|
|
||||||
"nvim_buffer to pick a buffer.",
|
|
||||||
parameters: pi.arktype({}),
|
|
||||||
async execute(_id, _params, _onUpdate, _ctx, _signal) {
|
|
||||||
const list = await rpc(
|
|
||||||
"json_encode(map(nvim_list_bufs(), {i, v -> {'nr': v, " +
|
|
||||||
"'name': nvim_buf_get_name(v), " +
|
|
||||||
"'current': v == nvim_get_current_buf(), " +
|
|
||||||
"'loaded': nvim_buf_is_loaded(v), " +
|
|
||||||
"'modified': getbufvar(v, '&modified')}}))",
|
|
||||||
);
|
|
||||||
if (!list.length) {
|
|
||||||
return { content: [{ type: "text", text: "No buffers open." }] };
|
|
||||||
}
|
|
||||||
const lines = list.map(
|
|
||||||
(b) =>
|
|
||||||
`${b.nr}\t${b.name || "[no name]"}\t` +
|
|
||||||
`${b.current ? "current " : ""}` +
|
|
||||||
`${b.loaded ? "" : "unloaded "}` +
|
|
||||||
`${b.modified ? "modified" : ""}`.trim(),
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Open buffers (${list.length}):\n` + lines.join("\n"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Read a single buffer, line-capped -------------------------------
|
|
||||||
tools.push({
|
|
||||||
name: "nvim_buffer",
|
|
||||||
label: "Neovim Buffer",
|
|
||||||
description:
|
|
||||||
"Read lines of a Neovim buffer. `buffer` accepts the current buffer " +
|
|
||||||
"(default), a buffer number from nvim_buffers, or a file name/path " +
|
|
||||||
"open in Neovim. `maxLines` caps the returned lines (default 2000); " +
|
|
||||||
"pass a larger value explicitly to read more of a long buffer.",
|
|
||||||
parameters: pi.arktype({
|
|
||||||
buffer: "string? | number?",
|
|
||||||
maxLines: "number?",
|
|
||||||
}),
|
|
||||||
async execute(_id, params, _onUpdate, _ctx, signal) {
|
|
||||||
const buf = await resolveBuf(params.buffer, signal);
|
|
||||||
if (buf === null) {
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `No buffer matches '${params.buffer}'. List open buffers with nvim_buffers.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const maxLines =
|
|
||||||
Number.isInteger(params.maxLines) && params.maxLines > 0
|
|
||||||
? Math.min(params.maxLines, 100000)
|
|
||||||
: DEFAULT_MAX_LINES;
|
|
||||||
|
|
||||||
const expr =
|
|
||||||
`json_encode({'name': nvim_buf_get_name(${buf}), ` +
|
|
||||||
`'total': nvim_buf_line_count(${buf}), ` +
|
|
||||||
`'lines': nvim_buf_get_lines(${buf}, 0, ${maxLines}, 0)})`;
|
|
||||||
const { name, total, lines } = await rpc(expr, signal);
|
|
||||||
if (!total) {
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{ type: "text", text: `Buffer ${buf} (${name}) is empty.` },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const truncated =
|
|
||||||
total > lines.length
|
|
||||||
? `\n... ${total - lines.length} more lines ` +
|
|
||||||
`(raise maxLines to read them)`
|
|
||||||
: "";
|
|
||||||
const numbered = lines.map((l, i) => `${i + 1}: ${l}`).join("\n");
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text:
|
|
||||||
`Buffer ${buf} (${name}), ${total} lines:\n` +
|
|
||||||
numbered +
|
|
||||||
truncated,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return tools;
|
|
||||||
}
|
|
||||||
341
README.md
341
README.md
|
|
@ -1,341 +0,0 @@
|
||||||
# nvimConfig
|
|
||||||
|
|
||||||
Modular Neovim wrapper config. Defines a wrapped `vv` binary with per-category packages, plugins, and environment variables. Available as NixOS module, Home Manager module, or single-starflake import.
|
|
||||||
|
|
||||||
## Quick start (imported into another flake)
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
rixpkgs.url = "github:dwinkler1/rixpkgs/af2dd3f7b4b172077747c0869d4e30702fb71b0e";
|
|
||||||
fran = {
|
|
||||||
url = "github:dwinkler1/fran";
|
|
||||||
inputs.nixpkgs.follows = "rixpkgs";
|
|
||||||
};
|
|
||||||
nvimConfig = {
|
|
||||||
url = "github:dwinkler1/nvimConfig";
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.follows = "nixpkgs";
|
|
||||||
rixpkgs.follows = "rixpkgs";
|
|
||||||
fran.follows = "fran";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, nvimConfig, ... } @ inputs: let
|
|
||||||
systems = ["aarch64-darwin" "x86_64-linux" "aarch64-linux"];
|
|
||||||
forAllSystems = nixpkgs.lib.genAttrs systems;
|
|
||||||
in {
|
|
||||||
packages = forAllSystems (system: let
|
|
||||||
pkgs = import nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
overlays = [ nvimConfig.overlays.dependencies ];
|
|
||||||
};
|
|
||||||
evalResult = nvimConfig.lib.eval {
|
|
||||||
inherit pkgs;
|
|
||||||
modules = [ projectSettings ]; # see below
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
default = evalResult.config.wrap { inherit pkgs; };
|
|
||||||
});
|
|
||||||
|
|
||||||
devShells = forAllSystems (system: let
|
|
||||||
pkgs = import nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
overlays = [ nvimConfig.overlays.dependencies ];
|
|
||||||
};
|
|
||||||
evalResult = nvimConfig.lib.eval {
|
|
||||||
inherit pkgs;
|
|
||||||
modules = [ projectSettings ];
|
|
||||||
};
|
|
||||||
nv = evalResult.config.wrap { inherit pkgs; };
|
|
||||||
in {
|
|
||||||
default = pkgs.mkShell {
|
|
||||||
packages = [ nv ] ++ nvimConfig.lib.devShellPackages evalResult.config;
|
|
||||||
shellHook = nvimConfig.lib.shellHook evalResult.config;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Home Manager module
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{ inputs, ... }: {
|
|
||||||
home.packages = [
|
|
||||||
(inputs.nvimConfig.homeModules.default { inherit pkgs; })
|
|
||||||
# Or via the overlay:
|
|
||||||
# pkgs.vv
|
|
||||||
];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## NixOS module
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{ inputs, ... }: {
|
|
||||||
environment.systemPackages = [
|
|
||||||
(inputs.nvimConfig.nixosModules.default { inherit pkgs; })
|
|
||||||
];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Overriding settings
|
|
||||||
|
|
||||||
Define a `projectSettings` attrset and pass it to `nvimConfig.lib.eval` or `nvimConfig.lib.mkWrapper`. The helper injects your overlaid `pkgs` automatically, so downstream consumers do not need to wire `specialArgs` themselves. Every option in `nvimConfig.wrapperConfigs.default` can be overridden here. Local `packages.default`, local `devShells.default`, downstream helper usage, and `pkgs.vv` all resolve the same module defaults unless you override them.
|
|
||||||
|
|
||||||
### Basic downstream flake example
|
|
||||||
|
|
||||||
This example enables a few cats, adds runtime tools to the shared wrapper/devShell package set, and appends language libraries to the default Python and R environments.
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
rixpkgs.url = "github:dwinkler1/rixpkgs/af2dd3f7b4b172077747c0869d4e30702fb71b0e";
|
|
||||||
fran = {
|
|
||||||
url = "github:dwinkler1/fran";
|
|
||||||
inputs.nixpkgs.follows = "rixpkgs";
|
|
||||||
};
|
|
||||||
nvimConfig = {
|
|
||||||
url = "github:dwinkler1/nvimConfig";
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.follows = "nixpkgs";
|
|
||||||
rixpkgs.follows = "rixpkgs";
|
|
||||||
fran.follows = "fran";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { nixpkgs, nvimConfig, ... }: let
|
|
||||||
system = "aarch64-darwin";
|
|
||||||
pkgs = import nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
overlays = [ nvimConfig.overlays.dependencies ];
|
|
||||||
};
|
|
||||||
projectSettings = {
|
|
||||||
cats = {
|
|
||||||
python = true;
|
|
||||||
r = true;
|
|
||||||
nix = true;
|
|
||||||
markdown = true;
|
|
||||||
optional = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
settings.lang_packages = {
|
|
||||||
python = with pkgs.python3Packages; [
|
|
||||||
pandas
|
|
||||||
pyarrow
|
|
||||||
];
|
|
||||||
r = with pkgs.rpkgs.rPackages; [
|
|
||||||
fixest
|
|
||||||
modelsummary
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
catPkgs = {
|
|
||||||
python = [
|
|
||||||
(pkgs.python3.withPackages (ps:
|
|
||||||
ps
|
|
||||||
++ (with ps; [
|
|
||||||
pandas
|
|
||||||
pyarrow
|
|
||||||
])))
|
|
||||||
pkgs.ruff
|
|
||||||
pkgs.basedpyright
|
|
||||||
pkgs.uv
|
|
||||||
pkgs.nodejs
|
|
||||||
];
|
|
||||||
nix = [
|
|
||||||
pkgs.alejandra
|
|
||||||
pkgs.nil
|
|
||||||
];
|
|
||||||
optional = [
|
|
||||||
pkgs.git
|
|
||||||
pkgs.fd
|
|
||||||
pkgs.ripgrep
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
evalResult = nvimConfig.lib.eval {
|
|
||||||
inherit pkgs;
|
|
||||||
modules = [ projectSettings ];
|
|
||||||
};
|
|
||||||
nv = evalResult.config.wrap { inherit pkgs; };
|
|
||||||
in {
|
|
||||||
packages.${system}.default = nv;
|
|
||||||
|
|
||||||
devShells.${system}.default = pkgs.mkShell {
|
|
||||||
packages = [ nv ] ++ nvimConfig.lib.devShellPackages evalResult.config;
|
|
||||||
shellHook = nvimConfig.lib.shellHook evalResult.config;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Categories
|
|
||||||
|
|
||||||
Enable only the cats you need. Defaults are listed in `modules/module/settings/cats.nix`.
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
cats = {
|
|
||||||
python = true; # enable Python tooling
|
|
||||||
r = false; # disable R
|
|
||||||
nix = true;
|
|
||||||
lua = false;
|
|
||||||
markdown = false;
|
|
||||||
optional = false;
|
|
||||||
gitPlugins = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Language packages (module defaults)
|
|
||||||
|
|
||||||
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`
|
|
||||||
- Julia: `DataFramesMeta`, `QuackIO`
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
settings.lang_packages = {
|
|
||||||
python = with pkgs.python3Packages; [ pandas numpy ];
|
|
||||||
r = with pkgs.rpkgs.rPackages; [ fixest data_table ];
|
|
||||||
julia = [ "StatsBase" "Plots" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `lib.mkForce` to replace rather than append:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
settings.lang_packages = {
|
|
||||||
python = pkgs.lib.mkForce (with pkgs.python3Packages; [ polars duckdb ]);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Runtime dependencies (per-cat packages)
|
|
||||||
|
|
||||||
Override `catPkgs` to change the full runtime tool lists that appear in both the wrapper PATH and the devShell. Use normal assignments to merge list values, or `lib.mkForce` to replace them.
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
catPkgs = {
|
|
||||||
nix = [ pkgs.nil pkgs.nixfmt ];
|
|
||||||
python = [
|
|
||||||
(pkgs.python3.withPackages (ps: [ ps.pandas ps.duckdb ]))
|
|
||||||
pkgs.ruff
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Direct runtimePkgs override (always-on)
|
|
||||||
|
|
||||||
Add packages to `catPkgs.always` for tools that should always be available regardless of other cat toggles.
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
catPkgs = {
|
|
||||||
always = [ pkgs.git pkgs.curl pkgs.pre-commit ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Runtime env vars
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
env = {
|
|
||||||
MY_PROJECT_VAR = "some-value";
|
|
||||||
};
|
|
||||||
envDefault = {
|
|
||||||
EDITOR = "vv";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
`env` values are forced; `envDefault` values can be overridden by the user's shell.
|
|
||||||
|
|
||||||
### Choosing the right override surface
|
|
||||||
|
|
||||||
- Use `cats` to enable or disable a whole language or tooling category.
|
|
||||||
- Use `settings.lang_packages` to add or replace Python, R, or Julia libraries within a language environment.
|
|
||||||
- Use `catPkgs` to change the full runtime tool list for a category. `config.runtimePkgs` drives wrapper PATH composition, while `nvimConfig.lib.devShellPackages config` returns the package-only list suitable for `mkShell`.
|
|
||||||
- Use `env` or `envDefault` for wrapper environment variables.
|
|
||||||
- Use `specs` for plugin additions or custom runtime behavior.
|
|
||||||
|
|
||||||
### Neovim settings
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
settings = {
|
|
||||||
colorscheme = "kanagawa";
|
|
||||||
background = "dark";
|
|
||||||
wrapRc = true;
|
|
||||||
config_directory = ./nvim; # path to init.lua + lua/ dir
|
|
||||||
aliases = [ "v" "nvim" ]; # extra binary names (default: [ "vvim" ])
|
|
||||||
};
|
|
||||||
binName = "nv"; # binary name (default: vv)
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding plugins
|
|
||||||
|
|
||||||
```nix
|
|
||||||
projectSettings = {
|
|
||||||
specs = {
|
|
||||||
extraPlugins = {
|
|
||||||
data = with pkgs.vimPlugins; [
|
|
||||||
telescope-nvim
|
|
||||||
which-key-nvim
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
extraLazy = {
|
|
||||||
lazy = true;
|
|
||||||
data = with pkgs.vimPlugins; [ dashboard-nvim ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Inject Lua config at startup
|
|
||||||
extraLua = {
|
|
||||||
data = pkgs.writeText "extra.lua" ''
|
|
||||||
vim.opt.number = true
|
|
||||||
vim.opt.relativenumber = true
|
|
||||||
'';
|
|
||||||
before = ["INIT_MAIN"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
settings/lang-packages.nix ──► settings.lang_packages
|
|
||||||
│
|
|
||||||
cats.nix ───────────────► config.cats │
|
|
||||||
│ │
|
|
||||||
▼ ▼
|
|
||||||
cat-packages.nix ───────► config.catPkgs.<cat>
|
|
||||||
│ │
|
|
||||||
├──────────────┐ │
|
|
||||||
▼ ▼ ▼
|
|
||||||
deps.nix PATH wrapper.config.wrap devShells.default
|
|
||||||
```
|
|
||||||
|
|
||||||
- **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 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.
|
|
||||||
99
flake.lock
generated
99
flake.lock
generated
|
|
@ -7,11 +7,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781337902,
|
"lastModified": 1776413584,
|
||||||
"narHash": "sha256-QjXxUvpOBbnlpSskBOZJ9nUbpYyFRD4cBo4s1aHniGs=",
|
"narHash": "sha256-xqqv46MTveuT4yJH2YihmbHGy5mdLnnLFDebVmUws/E=",
|
||||||
"owner": "dwinkler1",
|
"owner": "dwinkler1",
|
||||||
"repo": "fran",
|
"repo": "fran",
|
||||||
"rev": "30ab141871ede23b245a90afea58e076ef83b515",
|
"rev": "da09626e4dd8f0f57078b3a04e0443a8c20defa1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -22,11 +22,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785967620,
|
"lastModified": 1776169885,
|
||||||
"narHash": "sha256-IItrdb7Puk05RqOBWZYFC5X6Wl1sJmCfh5MWVHw5iMM=",
|
"narHash": "sha256-l/iNYDZ4bGOAFQY2q8y5OAfBBtrDAaPuRQqWaFHVRXM=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b7c2ada94fe99c15b0dbcf4d11fd7850b957a436",
|
"rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -36,22 +36,6 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins-bloocky": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1786042339,
|
|
||||||
"narHash": "sha256-8QcoC1bS8pRcwBkW3mqD2vgP9Mu0aCdIy6umZO6mUGM=",
|
|
||||||
"owner": "atiladefreitas",
|
|
||||||
"repo": "bloocky",
|
|
||||||
"rev": "a052c3b1a8126e04b194bb3db3c4c6ca641c0e4e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "atiladefreitas",
|
|
||||||
"repo": "bloocky",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"plugins-cmp-pandoc-references": {
|
"plugins-cmp-pandoc-references": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|
@ -68,51 +52,29 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins-dooing": {
|
"plugins-r": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785971574,
|
"lastModified": 1776340770,
|
||||||
"narHash": "sha256-GtEC5kWcH0DmiYXkzxzNjjfYLfyEb7zpTLQWdi7n3zs=",
|
"narHash": "sha256-o/8UZIc/Bq9dWTjA+MpSR5uMUpE7KHTErk+TwWID8Ww=",
|
||||||
"owner": "atiladefreitas",
|
"owner": "R-nvim",
|
||||||
"repo": "dooing",
|
"repo": "R.nvim",
|
||||||
"rev": "6748316bc6b4601797fb952a83694cc0a5ef6da2",
|
"rev": "b9cfffeb9b4e484aa9e13f01c0eb80230aada455",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "atiladefreitas",
|
"owner": "R-nvim",
|
||||||
"repo": "dooing",
|
"repo": "R.nvim",
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"r-nvim-nix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"rixpkgs"
|
|
||||||
],
|
|
||||||
"rnvimsrc": "rnvimsrc"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1784867506,
|
|
||||||
"narHash": "sha256-5zXVJLG7+Gi9sgb4Dn7P6O/ywQQxpvcjjoHSJXZEuVo=",
|
|
||||||
"owner": "dwinkler1",
|
|
||||||
"repo": "r_nvim_nix",
|
|
||||||
"rev": "1d26452e3c940153271fc57fbad0b295fcb0e04f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "dwinkler1",
|
|
||||||
"ref": "v1.0.0",
|
|
||||||
"repo": "r_nvim_nix",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rixpkgs": {
|
"rixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785776890,
|
"lastModified": 1771303851,
|
||||||
"narHash": "sha256-CzzgB1teVbroR/QhGAnsQToUYCDXEZZtFTcISAooASM=",
|
"narHash": "sha256-tgveHozOJ2D/mi3LxVy/FcmLFDlM5XKZxsNB2XpvzaM=",
|
||||||
"owner": "dwinkler1",
|
"owner": "dwinkler1",
|
||||||
"repo": "rixpkgs",
|
"repo": "rixpkgs",
|
||||||
"rev": "a54080a58b57d5785dc85901850b817bef2aaf83",
|
"rev": "af2dd3f7b4b172077747c0869d4e30702fb71b0e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -122,31 +84,12 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rnvimsrc": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1783264911,
|
|
||||||
"narHash": "sha256-FywUL3mV2+kfu+rO6uUFyUv80EflbdgSkYuSnW965UE=",
|
|
||||||
"owner": "R-nvim",
|
|
||||||
"repo": "R.nvim",
|
|
||||||
"rev": "c56ebe0f8445e251673981c40ac2d74659ecd6ed",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "R-nvim",
|
|
||||||
"ref": "v1.0.0",
|
|
||||||
"repo": "R.nvim",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"fran": "fran",
|
"fran": "fran",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"plugins-bloocky": "plugins-bloocky",
|
|
||||||
"plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references",
|
"plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references",
|
||||||
"plugins-dooing": "plugins-dooing",
|
"plugins-r": "plugins-r",
|
||||||
"r-nvim-nix": "r-nvim-nix",
|
|
||||||
"rixpkgs": "rixpkgs",
|
"rixpkgs": "rixpkgs",
|
||||||
"wrappers": "wrappers"
|
"wrappers": "wrappers"
|
||||||
}
|
}
|
||||||
|
|
@ -158,11 +101,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1782135443,
|
"lastModified": 1776375800,
|
||||||
"narHash": "sha256-vAmbArdCyjqpVW+37aCy/PMBOLIqukUXLQuEKLwUhA4=",
|
"narHash": "sha256-/SSAR77Brr9fbapsh1cb2K47JXCbvwS1GjM4yyDxle8=",
|
||||||
"owner": "BirdeeHub",
|
"owner": "BirdeeHub",
|
||||||
"repo": "nix-wrapper-modules",
|
"repo": "nix-wrapper-modules",
|
||||||
"rev": "6e7f66fa2cdf4d63162580b438f7fcf87c28a46f",
|
"rev": "f11469ca69068bac13d9e163b2bd268cc06dff57",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
248
flake.nix
248
flake.nix
|
|
@ -1,23 +1,17 @@
|
||||||
# Copyright (c) 2026 BirdeeHub & Daniel
|
# Copyright (c) 2026 BirdeeHub
|
||||||
# Licensed under the MIT license
|
# Licensed under the MIT license
|
||||||
{
|
{
|
||||||
description = "Daniel's NixCats";
|
description = "Daniel's NixCats";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
#nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||||
wrappers = {
|
wrappers = {
|
||||||
url = "github:BirdeeHub/nix-wrapper-modules";
|
url = "github:BirdeeHub/nix-wrapper-modules";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
rixpkgs.url = "github:dwinkler1/rixpkgs/nixpkgs";
|
rixpkgs.url = "github:dwinkler1/rixpkgs/nixpkgs";
|
||||||
|
|
||||||
r-nvim-nix = {
|
|
||||||
url = "github:dwinkler1/r_nvim_nix/v1.0.0";
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.follows = "rixpkgs";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
fran = {
|
fran = {
|
||||||
url = "github:dwinkler1/fran";
|
url = "github:dwinkler1/fran";
|
||||||
inputs = {
|
inputs = {
|
||||||
|
|
@ -25,20 +19,15 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"plugins-r" = {
|
||||||
|
url = "github:R-nvim/R.nvim";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
"plugins-cmp-pandoc-references" = {
|
"plugins-cmp-pandoc-references" = {
|
||||||
url = "github:jmbuhr/cmp-pandoc-references";
|
url = "github:jmbuhr/cmp-pandoc-references";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
"plugins-bloocky" = {
|
|
||||||
url = "github:atiladefreitas/bloocky";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
"plugins-dooing" = {
|
|
||||||
url = "github:atiladefreitas/dooing";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
|
|
@ -47,42 +36,46 @@
|
||||||
wrappers,
|
wrappers,
|
||||||
...
|
...
|
||||||
} @ inputs: let
|
} @ inputs: let
|
||||||
devShellCatOrder = [
|
wrapperSettings = pkgs: let
|
||||||
"always"
|
def = pkgs.lib.mkDefault;
|
||||||
"clickhouse"
|
in
|
||||||
"external"
|
wrapper.config.wrap {
|
||||||
"julia"
|
inherit pkgs;
|
||||||
"lua"
|
cats = {
|
||||||
"markdown"
|
clickhouse = def false;
|
||||||
"nix"
|
gitPlugins = def true;
|
||||||
"optional"
|
julia = def false;
|
||||||
"python"
|
lua = def true;
|
||||||
"r"
|
markdown = def false;
|
||||||
"treesitterParsers"
|
nix = def true;
|
||||||
];
|
optional = def false;
|
||||||
evalWithPkgs = pkgs: extraModules:
|
python = def false;
|
||||||
wrappers.lib.evalModules {
|
r = def false;
|
||||||
specialArgs = {
|
|
||||||
inherit pkgs;
|
|
||||||
};
|
};
|
||||||
modules =
|
|
||||||
[
|
settings = {
|
||||||
module
|
lang_packages = {
|
||||||
]
|
python = with pkgs.python3Packages; [
|
||||||
++ extraModules;
|
duckdb
|
||||||
|
polars
|
||||||
|
];
|
||||||
|
|
||||||
|
r = with pkgs.rpkgs.rPackages; [
|
||||||
|
arrow
|
||||||
|
broom
|
||||||
|
data_table
|
||||||
|
janitor
|
||||||
|
styler
|
||||||
|
];
|
||||||
|
|
||||||
|
julia = ["DataFramesMeta" "QuackIO"];
|
||||||
|
};
|
||||||
|
colorscheme = def "cyberdream";
|
||||||
|
background = def "dark";
|
||||||
|
wrapRc = def true;
|
||||||
|
};
|
||||||
|
binName = def "vv";
|
||||||
};
|
};
|
||||||
mkDevShellPackages = config:
|
|
||||||
builtins.concatLists (map (name: config.catPkgs.${name} or []) devShellCatOrder);
|
|
||||||
mkShellHook = config:
|
|
||||||
''
|
|
||||||
echo 'I am a NixShell'
|
|
||||||
''
|
|
||||||
+ nixpkgs.lib.optionalString (config.cats.r or false) ''
|
|
||||||
export R_HOME=$(R RHOME)
|
|
||||||
export R_LIBS_SITE=$(strings "$(command -v R)" | rg -o '/nix/store/[^:]+/library' | sort -u | paste -sd: -)
|
|
||||||
export R_LIBS_USER="$PWD/.r-libs"
|
|
||||||
mkdir -p "$R_LIBS_USER"
|
|
||||||
'';
|
|
||||||
|
|
||||||
systems = [
|
systems = [
|
||||||
"aarch64-darwin"
|
"aarch64-darwin"
|
||||||
|
|
@ -92,49 +85,54 @@
|
||||||
|
|
||||||
forAllSystems = nixpkgs.lib.genAttrs systems;
|
forAllSystems = nixpkgs.lib.genAttrs systems;
|
||||||
|
|
||||||
|
extra_pkg_config = {
|
||||||
|
# allowUnfree = true;
|
||||||
|
};
|
||||||
|
|
||||||
overlayDefs = import ./overlays inputs;
|
overlayDefs = import ./overlays inputs;
|
||||||
|
|
||||||
|
dependencyOverlays = overlayDefs.dependencyOverlays;
|
||||||
|
|
||||||
|
dependencyOverlay = overlayDefs.dependencyOverlay;
|
||||||
|
|
||||||
mkPkgs = system:
|
mkPkgs = system:
|
||||||
import nixpkgs {
|
import nixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
config = {allowUnfree = true;};
|
config = extra_pkg_config;
|
||||||
overlays = [
|
overlays = [dependencyOverlay];
|
||||||
overlayDefs.dependencyOverlay
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module = (import ./modules/neovim.nix) inputs;
|
module = nixpkgs.lib.modules.importApply ./modules/neovim.nix inputs;
|
||||||
|
wrapper = wrappers.lib.evalModule module;
|
||||||
in {
|
in {
|
||||||
lib = {
|
|
||||||
eval = {pkgs, modules ? []}: evalWithPkgs pkgs modules;
|
|
||||||
mkWrapper = {pkgs, modules ? []}: (evalWithPkgs pkgs modules).config.wrap {inherit pkgs;};
|
|
||||||
devShellPackages = config: mkDevShellPackages config;
|
|
||||||
shellHook = config: mkShellHook config;
|
|
||||||
};
|
|
||||||
|
|
||||||
overlays = {
|
overlays = {
|
||||||
# overlay `vv` wraps the module with default settings only.
|
|
||||||
# It is evaluated against the final package set so module defaults can
|
|
||||||
# depend on overlays such as rixpkgs-backed `pkgs.rpkgs`.
|
|
||||||
default = nixpkgs.lib.composeManyExtensions [
|
default = nixpkgs.lib.composeManyExtensions [
|
||||||
overlayDefs.dependencyOverlay
|
dependencyOverlay
|
||||||
(final: prev: {
|
(final: prev: {
|
||||||
vv = (evalWithPkgs final []).config.wrap {pkgs = final;};
|
vv = wrapper.config.wrap {pkgs = final;};
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
dependencies = overlayDefs.dependencyOverlay;
|
dependencies = dependencyOverlay;
|
||||||
|
vv = self.overlays.default;
|
||||||
};
|
};
|
||||||
|
|
||||||
wrapperModules.default = module;
|
wrapperModules = {
|
||||||
wrapperConfigs.default = {pkgs, modules ? []}: (self.lib.eval {inherit pkgs modules;}).config;
|
default = module;
|
||||||
|
neovim = self.wrapperModules.default;
|
||||||
|
};
|
||||||
|
|
||||||
|
wrappers = {
|
||||||
|
default = wrapper.config;
|
||||||
|
neovim = self.wrappers.default;
|
||||||
|
};
|
||||||
|
|
||||||
packages = forAllSystems (
|
packages = forAllSystems (
|
||||||
system: let
|
system: let
|
||||||
pkgs = mkPkgs system;
|
pkgs = mkPkgs system;
|
||||||
|
nvimPkg = wrapperSettings pkgs;
|
||||||
in {
|
in {
|
||||||
default = self.lib.mkWrapper {
|
default = nvimPkg;
|
||||||
inherit pkgs;
|
vv = nvimPkg;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -148,13 +146,14 @@
|
||||||
devShells = forAllSystems (
|
devShells = forAllSystems (
|
||||||
system: let
|
system: let
|
||||||
pkgs = mkPkgs system;
|
pkgs = mkPkgs system;
|
||||||
config = (self.lib.eval {inherit pkgs;}).config;
|
nvimPkg = wrapperSettings pkgs;
|
||||||
nvimPkg = config.wrap {inherit pkgs;};
|
|
||||||
in {
|
in {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
name = "vShell";
|
name = "vShell";
|
||||||
packages = [nvimPkg] ++ self.lib.devShellPackages config;
|
packages = [nvimPkg];
|
||||||
shellHook = mkShellHook config;
|
nativeBuildInputs = with pkgs; [] ++ (pkgs.lib.optionals self.wrappers.default.cats.optional [devenv]);
|
||||||
|
inputsFrom = [];
|
||||||
|
shellHook = "";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -162,34 +161,20 @@
|
||||||
checks = forAllSystems (
|
checks = forAllSystems (
|
||||||
system: let
|
system: let
|
||||||
pkgs = mkPkgs system;
|
pkgs = mkPkgs system;
|
||||||
defaultConfig = (self.lib.eval {inherit pkgs;}).config;
|
nvimPkg = wrapperSettings pkgs;
|
||||||
defaultNvimPkg = defaultConfig.wrap {inherit pkgs;};
|
|
||||||
defaultShellHook = mkShellHook defaultConfig;
|
|
||||||
overrideConfig =
|
|
||||||
(self.lib.eval {
|
|
||||||
inherit pkgs;
|
|
||||||
modules = [
|
|
||||||
{
|
|
||||||
cats = {
|
|
||||||
r = false;
|
|
||||||
python = true;
|
|
||||||
};
|
|
||||||
settings.lang_packages.python = nixpkgs.lib.mkForce (with pkgs.python3Packages; [
|
|
||||||
pandas
|
|
||||||
]);
|
|
||||||
catPkgs.nix = nixpkgs.lib.mkForce [
|
|
||||||
pkgs.alejandra
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}).config;
|
|
||||||
overrideShellHook = mkShellHook overrideConfig;
|
|
||||||
in {
|
in {
|
||||||
default = pkgs.runCommand "check-vv" {} ''
|
default = nvimPkg;
|
||||||
BINARY_PATH="${defaultNvimPkg}/bin/vv"
|
module-eval = let
|
||||||
|
_ = wrapper.config;
|
||||||
|
in
|
||||||
|
pkgs.runCommand "check-module-eval" {} ''
|
||||||
|
echo "Module evaluation successful" > $out
|
||||||
|
'';
|
||||||
|
package-build = pkgs.runCommand "check-vv" {} ''
|
||||||
|
BINARY_PATH="${nvimPkg}/bin/vv"
|
||||||
|
|
||||||
if [ ! -x "$BINARY_PATH" ]; then
|
if [ ! -x "$BINARY_PATH" ]; then
|
||||||
echo "Error: Binary not found or not executable"
|
echo "Error: Binary n not found or not executable"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -202,67 +187,18 @@
|
||||||
cat version_output.txt >> $out
|
cat version_output.txt >> $out
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
module-eval = let
|
|
||||||
_ = (self.lib.eval {inherit pkgs;}).config;
|
|
||||||
in
|
|
||||||
pkgs.runCommand "check-module-eval" {} ''
|
|
||||||
echo "Module evaluation successful" > $out
|
|
||||||
'';
|
|
||||||
downstream-overrides = let
|
|
||||||
overrideNix = builtins.map (p: p.pname or p.name) overrideConfig.catPkgs.nix;
|
|
||||||
defaultAssertions = [
|
|
||||||
(defaultConfig.cats.r or false)
|
|
||||||
(builtins.match ".*R RHOME.*" defaultShellHook != null)
|
|
||||||
(builtins.length (self.lib.devShellPackages defaultConfig) > 0)
|
|
||||||
];
|
|
||||||
overrideAssertions = [
|
|
||||||
(!(overrideConfig.cats.r or false))
|
|
||||||
(builtins.length overrideNix == 1)
|
|
||||||
((builtins.head overrideNix) == "alejandra")
|
|
||||||
(builtins.match ".*R RHOME.*" overrideShellHook == null)
|
|
||||||
];
|
|
||||||
in
|
|
||||||
pkgs.runCommand "check-downstream-overrides" {
|
|
||||||
pass =
|
|
||||||
if builtins.all (x: x) (defaultAssertions ++ overrideAssertions)
|
|
||||||
then "1"
|
|
||||||
else "";
|
|
||||||
} ''
|
|
||||||
if [ -z "$pass" ]; then
|
|
||||||
echo "Downstream override assertions failed" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Downstream override assertions passed" > $out
|
|
||||||
'';
|
|
||||||
lua-test = pkgs.runCommand "lua-test" {
|
|
||||||
buildInputs = [ pkgs.neovim-unwrapped ];
|
|
||||||
} ''
|
|
||||||
nvim --headless -u NONE -c "set runtimepath+=${./.}" -l ${./tests/init.lua}
|
|
||||||
touch $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
|
|
||||||
'';
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
nixosModules.default = wrappers.lib.getInstallModule {
|
nixosModules.default = wrappers.lib.mkInstallModule {
|
||||||
name = "vModule";
|
name = "vModule";
|
||||||
value = module;
|
value = module;
|
||||||
};
|
};
|
||||||
|
|
||||||
homeModules.default = wrappers.lib.getInstallModule {
|
homeModules.default = wrappers.lib.mkInstallModule {
|
||||||
name = "vModule";
|
name = "vModule";
|
||||||
value = module;
|
value = module;
|
||||||
|
loc = ["home" "packages"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,32 @@
|
||||||
-- Add the key mappings only for Markdown files in a zk notebook.
|
-- Add the key mappings only for Markdown files in a zk notebook.
|
||||||
local nix = require("config.nix")
|
if require("zk.util").notebook_root(vim.fn.expand('%:p')) ~= nil then
|
||||||
if nix.get_cat("markdown", false) then
|
local map = vim.keymap.set
|
||||||
if require("zk.util").notebook_root(vim.fn.expand('%:p')) ~= nil then
|
-- Open the link under the caret.
|
||||||
local map = vim.keymap.set
|
map("n", "<CR>", "<Cmd>lua vim.lsp.buf.definition()<CR>", { noremap = true, silent = false, buffer = true })
|
||||||
-- Open the link under the caret.
|
|
||||||
map("n", "<CR>", "<Cmd>lua vim.lsp.buf.definition()<CR>", { noremap = true, silent = false, buffer = true })
|
|
||||||
|
|
||||||
-- Create a new note after asking for its title.
|
-- Create a new note after asking for its title.
|
||||||
-- This overrides the global `<leader>zn` mapping to create the note in the same directory as the current buffer.
|
-- This overrides the global `<leader>zn` mapping to create the note in the same directory as the current buffer.
|
||||||
map("n", "<leader>zhn", "<Cmd>ZkNew { dir = vim.fn.expand('%:p:h'), title = vim.fn.input('Title: ') }<CR>",
|
map("n", "<leader>zhn", "<Cmd>ZkNew { dir = vim.fn.expand('%:p:h'), title = vim.fn.input('Title: ') }<CR>",
|
||||||
{ noremap = true, silent = false, buffer = true, desc = "Note (here)" })
|
{ noremap = true, silent = false, buffer = true, desc = "Note (here)" })
|
||||||
-- Create a new note in the same directory as the current buffer, using the current selection for title.
|
-- Create a new note in the same directory as the current buffer, using the current selection for title.
|
||||||
map("v", "<leader>zhnt", ":'<,'>ZkNewFromTitleSelection { dir = vim.fn.expand('%:p:h') }<CR>",
|
map("v", "<leader>zhnt", ":'<,'>ZkNewFromTitleSelection { dir = vim.fn.expand('%:p:h') }<CR>",
|
||||||
{ noremap = true, silent = false, buffer = true, desc = "Note from selection (title)" })
|
{ noremap = true, silent = false, buffer = true, desc = "Note from selection (title)" })
|
||||||
-- Create a new note in the same directory as the current buffer, using the current selection for note content and asking for its title.
|
-- Create a new note in the same directory as the current buffer, using the current selection for note content and asking for its title.
|
||||||
map("v", "<leader>zhnc",
|
map("v", "<leader>zhnc",
|
||||||
":'<,'>ZkNewFromContentSelection { dir = vim.fn.expand('%:p:h'), title = vim.fn.input('Title: ') }<CR>",
|
":'<,'>ZkNewFromContentSelection { dir = vim.fn.expand('%:p:h'), title = vim.fn.input('Title: ') }<CR>",
|
||||||
{ noremap = true, silent = false, buffer = true, desc = "Note from selection (content)" })
|
{ noremap = true, silent = false, buffer = true, desc = "Note from selection (content)" })
|
||||||
|
|
||||||
-- Open notes linking to the current buffer.
|
-- Open notes linking to the current buffer.
|
||||||
map("n", "<leader>zb", "<Cmd>ZkBacklinks<CR>", { noremap = true, silent = false, buffer = true, desc = "Backlinks" })
|
map("n", "<leader>zb", "<Cmd>ZkBacklinks<CR>", { noremap = true, silent = false, buffer = true, desc = "Backlinks" })
|
||||||
-- Alternative for backlinks using pure LSP and showing the source context.
|
-- Alternative for backlinks using pure LSP and showing the source context.
|
||||||
--map('n', '<leader>zb', '<Cmd>lua vim.lsp.buf.references()<CR>', opts)
|
--map('n', '<leader>zb', '<Cmd>lua vim.lsp.buf.references()<CR>', opts)
|
||||||
-- Open notes linked by the current buffer.
|
-- Open notes linked by the current buffer.
|
||||||
map("n", "<leader>zL", "<Cmd>ZkLinks<CR>", { noremap = true, silent = false, buffer = true, desc = "Links" })
|
map("n", "<leader>zL", "<Cmd>ZkLinks<CR>", { noremap = true, silent = false, buffer = true, desc = "Links" })
|
||||||
map("n", "<leader>zi", "<Cmd>ZkInsertLink<CR>", { noremap = true, silent = false, buffer = true, desc = "Insert link" })
|
map("n", "<leader>zi", "<Cmd>ZkInsertLink<CR>", { noremap = true, silent = false, buffer = true, desc = "Insert link" })
|
||||||
|
|
||||||
-- Preview a linked note.
|
-- Preview a linked note.
|
||||||
-- Open the code actions for a visual selection.
|
-- Open the code actions for a visual selection.
|
||||||
map("v", "<leader>za", ":'<,'>lua vim.lsp.buf.range_code_action()<CR>",
|
map("v", "<leader>za", ":'<,'>lua vim.lsp.buf.range_code_action()<CR>",
|
||||||
{ noremap = true, silent = false, buffer = true, desc = "Code actions" })
|
{ noremap = true, silent = false, buffer = true, desc = "Code actions" })
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,23 @@
|
||||||
|
|
||||||
|
|
||||||
local quarto_ok, quarto = pcall(require, 'quarto')
|
local quarto = require('quarto')
|
||||||
if quarto_ok then
|
quarto.setup()
|
||||||
vim.keymap.set('n', '<leader>qp', quarto.quartoPreview, { silent = true, noremap = true, buffer = true })
|
vim.keymap.set('n', '<leader>qp', quarto.quartoPreview, { silent = true, noremap = true })
|
||||||
end
|
|
||||||
|
|
||||||
if vim.bo.filetype == "r" then
|
vim.keymap.set("n", "<Enter>", "<Plug>RDSendLine", { buffer = true })
|
||||||
vim.keymap.set("n", "<Enter>", "<Plug>RDSendLine", { buffer = true })
|
vim.keymap.set("v", "<Enter>", "<Plug>RSendSelection", { buffer = true })
|
||||||
vim.keymap.set("v", "<Enter>", "<Plug>RSendSelection", { buffer = true })
|
|
||||||
|
|
||||||
-- Assignment operator (--)
|
-- Assignment operator (--)
|
||||||
vim.keymap.set("i", "--", "<Cmd>lua MiniTrailspace.trim()<CR><Plug>RInsertAssign", { buffer = true, noremap = true })
|
vim.keymap.set("i", "--", "<Cmd>lua MiniTrailspace.trim()<CR><Plug>RInsertAssign", { buffer = true, noremap = true })
|
||||||
|
|
||||||
-- Pipe operator (;;)
|
-- Pipe operator (;;)
|
||||||
vim.keymap.set("i", ";;", "<Cmd>lua MiniTrailspace.trim()<CR><Plug>RInsertPipe<CR>", { buffer = true, noremap = true })
|
vim.keymap.set("i", ";;", "<Cmd>lua MiniTrailspace.trim()<CR><Plug>RInsertPipe<CR>", { buffer = true, noremap = true })
|
||||||
end
|
|
||||||
|
|
||||||
local runner_ok, runner = pcall(require, "quarto.runner")
|
local runner = require("quarto.runner")
|
||||||
if runner_ok then
|
vim.keymap.set("n", "<localleader>a", runner.run_cell, { desc = "run cell", silent = true })
|
||||||
vim.keymap.set("n", "<localleader>a", runner.run_cell, { desc = "run cell", silent = true, buffer = true })
|
vim.keymap.set("n", "<localleader>A", runner.run_all, { desc = "run all cells", silent = true })
|
||||||
vim.keymap.set("n", "<localleader>A", runner.run_all, { desc = "run all cells", silent = true, buffer = true })
|
vim.keymap.set("n", "<localleader>RA", function()
|
||||||
vim.keymap.set("n", "<localleader>RA", function()
|
runner.run_all(true)
|
||||||
runner.run_all(true)
|
end, { desc = "run all cells of all languages", silent = true })
|
||||||
end, { desc = "run all cells of all languages", silent = true, buffer = true })
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
11
init.lua
11
init.lua
|
|
@ -1,5 +1,7 @@
|
||||||
local Config = require('config')
|
_G.Config = {}
|
||||||
_G.Config = Config -- keep global alias for keymaps/backward compatibility
|
local nix = require('config.nix').init { non_nix_value = true }
|
||||||
|
Config.isNixCats = nix.is_nix
|
||||||
|
Config.nixConfig = nix
|
||||||
|
|
||||||
require('lze').register_handlers(require('nixCatsUtils.lzUtils').for_cat)
|
require('lze').register_handlers(require('nixCatsUtils.lzUtils').for_cat)
|
||||||
|
|
||||||
|
|
@ -10,11 +12,8 @@ if not Config.isNixCats then
|
||||||
local mini_path = path_package .. 'pack/deps/start/mini.nvim'
|
local mini_path = path_package .. 'pack/deps/start/mini.nvim'
|
||||||
if not vim.uv.fs_stat(mini_path) then
|
if not vim.uv.fs_stat(mini_path) then
|
||||||
vim.cmd('echo "Installing `mini.nvim`" | redraw')
|
vim.cmd('echo "Installing `mini.nvim`" | redraw')
|
||||||
-- Pin to the stable branch for reproducible non-Nix installs.
|
|
||||||
-- Change the tag/branch here if you need a newer version.
|
|
||||||
local mini_nvim_tag = 'stable'
|
|
||||||
local clone_cmd = {
|
local clone_cmd = {
|
||||||
'git', 'clone', '--filter=blob:none', '--branch=' .. mini_nvim_tag,
|
'git', 'clone', '--filter=blob:none',
|
||||||
'https://github.com/echasnovski/mini.nvim', mini_path
|
'https://github.com/echasnovski/mini.nvim', mini_path
|
||||||
}
|
}
|
||||||
vim.fn.system(clone_cmd)
|
vim.fn.system(clone_cmd)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
--- Shared configuration / state table.
|
|
||||||
--- Plugin submodules attach their helpers to this table at runtime.
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
-- Detect whether this Neovim was launched via nixCats.
|
|
||||||
local nix = require('config.nix').init { non_nix_value = true }
|
|
||||||
M.isNixCats = nix.is_nix
|
|
||||||
M.nixConfig = nix
|
|
||||||
|
|
||||||
return M
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
-- Basic mappings =============================================================
|
|
||||||
-- NOTE: Most basic mappings come from 'mini.basics'
|
|
||||||
-- Shorter version of the most frequent way of going outside of terminal window
|
|
||||||
vim.keymap.set('t', '<C-h>', [[<C-\><C-N><C-w>h]])
|
|
||||||
-- Select all
|
|
||||||
-- vim.keymap.set({ "n", "v", "x" }, "<C-a>", "gg3vG$", { noremap = true, silent = true, desc = "Select all" })
|
|
||||||
-- Escape deletes highlights
|
|
||||||
vim.keymap.set("n", "<Esc>", "<cmd>nohlsearch<CR>")
|
|
||||||
-- Paste before/after linewise
|
|
||||||
local cmd = vim.fn.has('nvim-0.12') == 1 and 'iput' or 'put'
|
|
||||||
vim.keymap.set({ 'n', 'x' }, '[p', '<Cmd>exe "' .. cmd .. '! " . v:register<CR>', { desc = 'Paste Above' })
|
|
||||||
vim.keymap.set({ 'n', 'x' }, ']p', '<Cmd>exe "' .. cmd .. ' " . v:register<CR>', { desc = 'Paste Below' })
|
|
||||||
|
|
||||||
vim.keymap.set({ "n", "v", "x" }, "<leader>p", '"+p', { noremap = true, silent = true, desc = "Paste from clipboard" })
|
|
||||||
vim.keymap.set({ "n", "v", "x" }, "<leader>y", '"+y', { noremap = true, silent = true, desc = "Copy toclipboard" })
|
|
||||||
|
|
||||||
-- Create global tables with information about clue group in certain modes
|
|
||||||
-- Structure of tables is taken to be compatible with 'mini.clue'.
|
|
||||||
Config.leader_group_clues = {
|
|
||||||
{ mode = 'n', keys = '<Leader>a', desc = '+AI' },
|
|
||||||
{ mode = 'n', keys = '<Leader>b', desc = '+Buffer' },
|
|
||||||
{ mode = 'n', keys = '<Leader>e', desc = '+Explore' },
|
|
||||||
{ mode = 'n', keys = '<Leader>f', desc = '+Find' },
|
|
||||||
{ mode = 'n', keys = '<Leader>fl', desc = '+LSP' },
|
|
||||||
{ mode = 'n', keys = '<Leader>fa', desc = '+Git' },
|
|
||||||
{ mode = 'n', keys = '<Leader>g', desc = '+Git' },
|
|
||||||
{ mode = 'n', keys = '<Leader>l', desc = '+LSP' },
|
|
||||||
{ mode = 'n', keys = '<Leader>L', desc = '+Lua/Log' },
|
|
||||||
{ mode = 'n', keys = '<Leader>o', desc = '+Other' },
|
|
||||||
{ mode = 'n', keys = '<Leader>r', desc = '+R' },
|
|
||||||
{ mode = 'n', keys = '<Leader>s', desc = '+Send' },
|
|
||||||
{ mode = 'n', keys = '<Leader>d', desc = '+Debug' },
|
|
||||||
{ mode = 'n', keys = '<Leader>t', desc = '+Terminal' },
|
|
||||||
{ mode = 'n', keys = '<Leader>u', desc = '+UI' },
|
|
||||||
{ mode = 'n', keys = '<Leader>v', desc = '+Visits' },
|
|
||||||
{ mode = 'n', keys = '<Leader>w', desc = '+Windows' },
|
|
||||||
{ mode = 'x', keys = '<Leader>l', desc = '+LSP' },
|
|
||||||
{ mode = 'x', keys = '<Leader>r', desc = '+R' },
|
|
||||||
{ mode = 'n', keys = '<Leader>z', desc = '+ZK' },
|
|
||||||
{ mode = 'n', keys = '<Leader>zr', desc = '+Reviews' },
|
|
||||||
{ mode = 'x', keys = '<Leader>a', desc = '+AI' },
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
local M = {}
|
|
||||||
|
|
||||||
---Create a normal-mode `<Leader>` mapping.
|
|
||||||
function M.nmap_leader(suffix, rhs, desc, opts)
|
|
||||||
opts = opts or {}
|
|
||||||
opts.desc = desc
|
|
||||||
vim.keymap.set('n', '<Leader>' .. suffix, rhs, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
---Create a visual-mode `<Leader>` mapping.
|
|
||||||
function M.xmap_leader(suffix, rhs, desc, opts)
|
|
||||||
opts = opts or {}
|
|
||||||
opts.desc = desc
|
|
||||||
vim.keymap.set('x', '<Leader>' .. suffix, rhs, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
---Create a normal-mode LSP keymap with an "(LSP)" suffix in the description.
|
|
||||||
function M.nmap_lsp(keys, func, desc)
|
|
||||||
if desc then
|
|
||||||
desc = desc .. "(LSP)"
|
|
||||||
end
|
|
||||||
vim.keymap.set("n", keys, func, { desc = desc })
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
|
|
@ -1,293 +0,0 @@
|
||||||
local Config = require('config')
|
|
||||||
local helpers = require('keymap.helpers')
|
|
||||||
local nmap_leader = helpers.nmap_leader
|
|
||||||
local xmap_leader = helpers.xmap_leader
|
|
||||||
local nmap_lsp = helpers.nmap_lsp
|
|
||||||
|
|
||||||
-- stylua: ignore start
|
|
||||||
|
|
||||||
-- Switch buffers
|
|
||||||
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")
|
|
||||||
|
|
||||||
-- b is for 'buffer'
|
|
||||||
nmap_leader('bb', '<Cmd>b#<CR>', 'Alternate')
|
|
||||||
nmap_leader('bd', '<Cmd>lua MiniBufremove.delete()<CR>', 'Delete')
|
|
||||||
nmap_leader('bD', '<Cmd>lua MiniBufremove.delete(0, true)<CR>', 'Delete!')
|
|
||||||
nmap_leader('bs', '<Cmd>lua Config.new_scratch_buffer()<CR>', 'Scratch')
|
|
||||||
nmap_leader('bw', '<Cmd>lua MiniBufremove.wipeout()<CR>', 'Wipeout')
|
|
||||||
nmap_leader('bW', '<Cmd>lua MiniBufremove.wipeout(0, true)<CR>', 'Wipeout!')
|
|
||||||
nmap_leader('bq', '<Cmd>qall<CR>', 'Quit all')
|
|
||||||
|
|
||||||
-- e is for 'explore' and 'edit'
|
|
||||||
nmap_leader('ed', '<Cmd>lua MiniFiles.open()<CR>', 'Directory')
|
|
||||||
nmap_leader('ef', '<Cmd>lua Config.try_opendir()<CR>', 'File directory')
|
|
||||||
nmap_leader('es', '<Cmd>lua MiniSessions.select()<CR>', 'Sessions')
|
|
||||||
nmap_leader('eq', '<Cmd>lua Config.toggle_quickfix()<CR>', 'Quickfix')
|
|
||||||
nmap_leader('ez', '<Cmd>lua MiniFiles.open(os.getenv("ZK_NOTEBOOK_DIR"))<CR>', 'Notes directory')
|
|
||||||
|
|
||||||
-- f is for 'fuzzy find'
|
|
||||||
nmap_leader('f/', '<Cmd>Pick history scope="/"<CR>', '"/" history')
|
|
||||||
nmap_leader('f:', '<Cmd>Pick history scope=":"<CR>', '":" history')
|
|
||||||
nmap_leader('f,', '<Cmd>Pick visit_labels<CR>', 'Visit labels')
|
|
||||||
nmap_leader('faa', '<Cmd>Pick git_hunks scope="staged"<CR>', 'Added hunks (all)')
|
|
||||||
nmap_leader('faA', '<Cmd>Pick git_hunks path="%" scope="staged"<CR>', 'Added hunks (current)')
|
|
||||||
nmap_leader('fb', '<Cmd>Pick buffers<CR>', 'Buffers')
|
|
||||||
nmap_leader(',', '<Cmd>Pick buffers<CR>', 'Buffers')
|
|
||||||
nmap_leader('fac', '<Cmd>Pick git_commits<CR>', 'Commits (all)')
|
|
||||||
nmap_leader('faC', '<Cmd>Pick git_commits path="%"<CR>', 'Commits (current)')
|
|
||||||
nmap_leader('fd', '<Cmd>Pick diagnostic scope="all"<CR>', 'Diagnostic workspace')
|
|
||||||
nmap_leader('fD', '<Cmd>Pick diagnostic scope="current"<CR>', 'Diagnostic buffer')
|
|
||||||
nmap_leader('ff', '<Cmd>Pick files<CR>', 'Files')
|
|
||||||
nmap_leader('fg', '<Cmd>Pick grep_live<CR>', 'Grep live')
|
|
||||||
nmap_leader('fG', '<Cmd>Pick grep pattern="<cword>"<CR>', 'Grep current word')
|
|
||||||
nmap_leader('fh', '<Cmd>Pick help<CR>', 'Help tags')
|
|
||||||
nmap_leader('fH', '<Cmd>Pick hl_groups<CR>', 'Highlight groups')
|
|
||||||
nmap_leader('fj', '<Cmd>Pick buf_lines scope="all"<CR>', 'Lines (all)')
|
|
||||||
nmap_leader('fJ', '<Cmd>Pick buf_lines scope="current"<CR>', 'Lines (current)')
|
|
||||||
nmap_leader('fam', '<Cmd>Pick git_hunks<CR>', 'Modified hunks (all)')
|
|
||||||
nmap_leader('faM', '<Cmd>Pick git_hunks path="%"<CR>', 'Modified hunks (current)')
|
|
||||||
nmap_leader('fm', '<Cmd>Pick marks<CR>', 'Marks')
|
|
||||||
nmap_leader('fn', '<cmd>ZkNotes<CR>', "Notes")
|
|
||||||
nmap_leader('fk', '<Cmd>Pick keymaps<CR>', 'Keymaps')
|
|
||||||
nmap_leader('fR', '<Cmd>Pick resume<CR>', 'Resume')
|
|
||||||
nmap_leader('fp', '<Cmd>Pick files<CR>', 'Files')
|
|
||||||
nmap_leader('fq', '<Cmd>Pick list scope="quickfix"<CR>', 'Quickfix')
|
|
||||||
nmap_leader('fr', '<Cmd>Pick lsp scope="references"<CR>', 'References (LSP)')
|
|
||||||
nmap_leader('flr', '<Cmd>Pick lsp scope="references"<CR>', 'References (LSP)')
|
|
||||||
nmap_leader('fS', '<Cmd>Pick lsp scope="workspace_symbol"<CR>', 'Symbols workspace (LSP)')
|
|
||||||
nmap_leader('flS', '<Cmd>Pick lsp scope="workspace_symbol"<CR>', 'Symbols workspace (LSP)')
|
|
||||||
nmap_leader('fs', '<Cmd>Pick lsp scope="document_symbol"<CR>', 'Symbols buffer (LSP)')
|
|
||||||
nmap_leader('fls', '<Cmd>Pick lsp scope="document_symbol"<CR>', 'Symbols buffer (LSP)')
|
|
||||||
nmap_leader('fld', '<Cmd>Pick lsp scope="definition"<CR>', 'Definition (LSP)')
|
|
||||||
nmap_leader('flD', '<Cmd>Pick lsp scope="declaration"<CR>', 'Declaration (LSP)')
|
|
||||||
nmap_leader('flt', '<Cmd>Pick lsp scope="type_definition"<CR>', 'Type Definition (LSP)')
|
|
||||||
nmap_leader('fv', '<Cmd>Pick visit_paths cwd=""<CR>', 'Visit paths (all)')
|
|
||||||
nmap_leader('fV', '<Cmd>Pick visit_paths<CR>', 'Visit paths (cwd)')
|
|
||||||
|
|
||||||
-- g is for git
|
|
||||||
local git_log_cmd = [[Git log --pretty=format:\%h\ \%as\ │\ \%s --topo-order]]
|
|
||||||
|
|
||||||
nmap_leader('gc', '<Cmd>Git commit<CR>', 'Commit')
|
|
||||||
nmap_leader('gC', '<Cmd>Git commit --amend<CR>', 'Commit amend')
|
|
||||||
nmap_leader('gd', '<Cmd>Git diff<CR>', 'Diff')
|
|
||||||
nmap_leader('gD', '<Cmd>Git diff -- %<CR>', 'Diff buffer')
|
|
||||||
nmap_leader("gg", "<cmd>Neogit<cr>", "Open Neogit UI")
|
|
||||||
nmap_leader('gl', '<Cmd>' .. git_log_cmd .. '<CR>', 'Log')
|
|
||||||
nmap_leader('gL', '<Cmd>' .. git_log_cmd .. ' --follow -- %<CR>', 'Log buffer')
|
|
||||||
nmap_leader('go', '<Cmd>lua MiniDiff.toggle_overlay()<CR>', 'Toggle overlay')
|
|
||||||
nmap_leader('gp', '<Cmd>Git pull<CR>', 'Pull')
|
|
||||||
nmap_leader('gP', '<Cmd>Git push<CR>', 'Push')
|
|
||||||
nmap_leader('gs', '<Cmd>lua MiniGit.show_at_cursor()<CR>', 'Show at cursor')
|
|
||||||
|
|
||||||
xmap_leader('gs', '<Cmd>lua MiniGit.show_at_cursor()<CR>', 'Show at selection')
|
|
||||||
|
|
||||||
-- j/k navigate quickfix
|
|
||||||
nmap_leader("j", '<cmd>cnext<CR>zz', "Quickfix next")
|
|
||||||
nmap_leader("k", '<cmd>cprev<CR>zz', "Quickfix prev")
|
|
||||||
|
|
||||||
-- l is for 'LSP' (Language Server Protocol)
|
|
||||||
vim.keymap.set({ 'n' }, 'grd', '<Cmd>lua vim.lsp.buf.definition()<CR>', { desc = 'Definition' })
|
|
||||||
vim.keymap.set({ 'n' }, 'grk', '<Cmd>lua vim.lsp.buf.hover()<CR>', { desc = 'Documentation' })
|
|
||||||
vim.keymap.set({ 'n' }, 'gre', '<Cmd>lua vim.diagnostic.open_float()<CR>', { desc = 'Diagnostics' })
|
|
||||||
|
|
||||||
nmap_lsp("K", '<Cmd>lua vim.lsp.buf.hover()<CR>', "Documentation")
|
|
||||||
local formatting_cmd = '<Cmd>lua require("conform").format({ lsp_format = "fallback" })<CR>'
|
|
||||||
nmap_leader('la', '<Cmd>lua vim.lsp.buf.code_action()<CR>', 'Actions')
|
|
||||||
nmap_leader('le', '<Cmd>lua vim.diagnostic.open_float()<CR>', 'Diagnostics popup')
|
|
||||||
nmap_leader('lf', formatting_cmd, 'Format')
|
|
||||||
nmap_leader('lk', '<Cmd>lua vim.lsp.buf.hover()<CR>', 'Documentation')
|
|
||||||
nmap_leader('li', '<Cmd>lua vim.lsp.buf.implementation()<CR>', 'Information')
|
|
||||||
-- use ]d and [d
|
|
||||||
--nmap_leader('lj', '<Cmd>lua vim.diagnostic.goto_next()<CR>', 'Next diagnostic')
|
|
||||||
--nmap_leader('lk', '<Cmd>lua vim.diagnostic.goto_prev()<CR>', 'Prev diagnostic')
|
|
||||||
nmap_leader('lR', '<Cmd>lua vim.lsp.buf.references()<CR>', 'References')
|
|
||||||
nmap_leader('lr', '<Cmd>lua vim.lsp.buf.rename()<CR>', 'Rename')
|
|
||||||
nmap_leader('ls', '<Cmd>lua vim.lsp.buf.definition()<CR>', 'Source definition')
|
|
||||||
|
|
||||||
xmap_leader('lf', formatting_cmd, 'Format selection')
|
|
||||||
|
|
||||||
-- L is for 'Lua'
|
|
||||||
nmap_leader('Lc', '<Cmd>lua Config.log_clear()<CR>', 'Clear log')
|
|
||||||
nmap_leader('LL', '<Cmd>luafile %<CR><Cmd>echo "Sourced lua"<CR>', 'Source buffer')
|
|
||||||
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 free
|
|
||||||
|
|
||||||
-- o is for 'other'
|
|
||||||
local trailspace_toggle_command = '<Cmd>lua vim.b.minitrailspace_disable = not vim.b.minitrailspace_disable<CR>'
|
|
||||||
nmap_leader('oh', '<Cmd>normal gxiagxila<CR>', 'Move arg left')
|
|
||||||
nmap_leader('ol', '<Cmd>normal gxiagxina<CR>', 'Move arg right')
|
|
||||||
nmap_leader('or', '<Cmd>lua MiniMisc.resize_window()<CR>', 'Resize to default width')
|
|
||||||
nmap_leader('ot', '<Cmd>lua MiniTrailspace.trim()<CR>', 'Trim trailspace')
|
|
||||||
nmap_leader('oT', trailspace_toggle_command, 'Trailspace hl toggle')
|
|
||||||
nmap_leader('oz', '<Cmd>lua MiniMisc.zoom()<CR>', 'Zoom toggle')
|
|
||||||
nmap_leader('ow',
|
|
||||||
"<Cmd>lua MiniSessions.write(vim.fn.input('Session name: ', string.match(vim.fn.getcwd(), \"[^/]+$\") .. '-session.vim'))<CR>",
|
|
||||||
'Write session')
|
|
||||||
|
|
||||||
-- r is for 'R'
|
|
||||||
nmap_leader('rc', '<Cmd>RSend devtools::check()<CR>', 'Check')
|
|
||||||
nmap_leader('rC', '<Cmd>RSend devtools::test_coverage()<CR>', 'Coverage')
|
|
||||||
nmap_leader('rd', '<Cmd>RSend devtools::document()<CR>', 'Document')
|
|
||||||
nmap_leader('ri', '<Cmd>RSend devtools::install(keep_source=TRUE)<CR>', 'Install')
|
|
||||||
nmap_leader('rk', '<Cmd>RSend quarto::quarto_preview("%")<CR>', 'Knit file')
|
|
||||||
nmap_leader('rl', '<Cmd>RSend devtools::load_all()<CR>', 'Load all')
|
|
||||||
nmap_leader('rL', '<Cmd>RSend devtools::load_all(recompile=TRUE)<CR>', 'Load all recompile')
|
|
||||||
nmap_leader('rm', '<Cmd>RSend Rcpp::compileAttributes()<CR>', 'Run examples')
|
|
||||||
nmap_leader('rT', '<Cmd>RSend testthat::test_file("%")<CR>', 'Test file')
|
|
||||||
nmap_leader('rt', '<Cmd>RSend devtools::test()<CR>', 'Test')
|
|
||||||
|
|
||||||
-- Visual `<leader>r` bindings (dispatched by the filetype-aware REPL
|
|
||||||
-- runner in `lua/keymap/repl.lua`). Previously the visual clue group was
|
|
||||||
-- empty, so this restores parity between the visual and normal `r`-group.
|
|
||||||
xmap_leader('rr', function() require('keymap.repl').send_selection() end, 'Send selection to REPL')
|
|
||||||
-- - Copy to clipboard and make reprex (which itself is loaded to clipboard)
|
|
||||||
xmap_leader('rx', '"+y :RSend reprex::reprex()<CR>', 'Reprex selection')
|
|
||||||
|
|
||||||
-- s is for 'send' (Send text to the active REPL/runner)
|
|
||||||
nmap_leader('s', function() require('keymap.repl').send_line() end, 'Send to REPL')
|
|
||||||
xmap_leader('s', function() require('keymap.repl').send_selection() end, 'Send selection to REPL')
|
|
||||||
|
|
||||||
-- d is for 'debug' (nvim-dap)
|
|
||||||
nmap_leader('db', '<Cmd>lua require("dap").toggle_breakpoint()<CR>', 'Toggle breakpoint')
|
|
||||||
nmap_leader('dB', '<Cmd>lua require("dap").set_breakpoint(vim.fn.input("Condition: "))<CR>', 'Conditional breakpoint')
|
|
||||||
nmap_leader('dc', '<Cmd>lua require("dap").continue()<CR>', 'Continue')
|
|
||||||
nmap_leader('do', '<Cmd>lua require("dap").step_over()<CR>', 'Step over')
|
|
||||||
nmap_leader('di', '<Cmd>lua require("dap").step_into()<CR>', 'Step into')
|
|
||||||
nmap_leader('dO', '<Cmd>lua require("dap").step_out()<CR>', 'Step out')
|
|
||||||
nmap_leader('dr', '<Cmd>lua require("dap").repl.open()<CR>', 'Open DAP REPL')
|
|
||||||
nmap_leader('du', '<Cmd>lua require("dapui").toggle()<CR>', 'Toggle DAP UI')
|
|
||||||
nmap_leader('dK', '<Cmd>lua require("dapui").eval()<CR>', 'Evaluate expression')
|
|
||||||
|
|
||||||
-- u is for UI
|
|
||||||
nmap_leader('ut', '<Cmd>TSContext toggle<CR>', 'Toggle TScontext')
|
|
||||||
nmap_leader('ua', '<Cmd>Copilot toggle<CR>', 'Toggle AI completion')
|
|
||||||
|
|
||||||
-- v is for 'visits'
|
|
||||||
nmap_leader('vv', '<Cmd>lua MiniVisits.add_label("core")<CR>', 'Add "core" label')
|
|
||||||
nmap_leader('vV', '<Cmd>lua MiniVisits.remove_label("core")<CR>', 'Remove "core" label')
|
|
||||||
nmap_leader('vl', '<Cmd>lua MiniVisits.add_label()<CR>', 'Add label')
|
|
||||||
nmap_leader('vL', '<Cmd>lua MiniVisits.remove_label()<CR>', 'Remove label')
|
|
||||||
|
|
||||||
local map_pick_core = function(keys, cwd, desc)
|
|
||||||
local rhs = function()
|
|
||||||
local sort_latest = MiniVisits.gen_sort.default({ recency_weight = 1 })
|
|
||||||
MiniExtra.pickers.visit_paths({
|
|
||||||
cwd = cwd,
|
|
||||||
filter = 'core',
|
|
||||||
sort = sort_latest
|
|
||||||
}, { source = { name = desc } })
|
|
||||||
end
|
|
||||||
nmap_leader(keys, rhs, desc)
|
|
||||||
end
|
|
||||||
map_pick_core('vc', '', 'Core visits (all)')
|
|
||||||
map_pick_core('vC', nil, 'Core visits (cwd)')
|
|
||||||
|
|
||||||
-- w is for 'windows'
|
|
||||||
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 })
|
|
||||||
nmap_leader("wd", "<C-W>c", "Delete Window", { remap = true })
|
|
||||||
nmap_leader("wo", "<C-W>o", "Delete Other Windows", { remap = true })
|
|
||||||
|
|
||||||
-- z is for 'ZettelKasten'
|
|
||||||
nmap_leader("zo", '<Cmd>ZkNotes<CR>', "Notes")
|
|
||||||
nmap_leader("zt", '<Cmd>ZkTags<cr>', "Tags")
|
|
||||||
|
|
||||||
nmap_leader(
|
|
||||||
"zrd",
|
|
||||||
'<Cmd>ZkNew { group = "dreviews" }<CR>',
|
|
||||||
"Daily Review"
|
|
||||||
)
|
|
||||||
nmap_leader(
|
|
||||||
"zrw",
|
|
||||||
'<Cmd>ZkNew { group = "wreviews" }<CR>',
|
|
||||||
"Weekly Review"
|
|
||||||
)
|
|
||||||
nmap_leader(
|
|
||||||
"zn",
|
|
||||||
'<Cmd>ZkNew { group = "inbox", title = vim.fn.input("Title: ") }<CR>',
|
|
||||||
"New"
|
|
||||||
)
|
|
||||||
nmap_leader(
|
|
||||||
"zp",
|
|
||||||
"<Cmd>ZkNew { group = 'permanent', title = vim.fn.input('Title: ') }<CR>",
|
|
||||||
"Permanent"
|
|
||||||
)
|
|
||||||
|
|
||||||
nmap_leader(
|
|
||||||
"zl",
|
|
||||||
"<Cmd>ZkNew { group = 'literature', title = vim.fn.input('Title: '), extra.author = vim.fn.input('Author: '), extra.year = vim.fn.input('Year: ') }<CR>",
|
|
||||||
"Literature"
|
|
||||||
)
|
|
||||||
|
|
||||||
nmap_leader(
|
|
||||||
"zd",
|
|
||||||
"<Cmd>ZkNew { group = 'dashboard', title = vim.fn.input('Title: ') }<CR>",
|
|
||||||
"Dashboard"
|
|
||||||
)
|
|
||||||
nmap_leader(
|
|
||||||
"zP",
|
|
||||||
"<Cmd>ZkNew { group = 'project', title = vim.fn.input('Title: ')}<CR>",
|
|
||||||
"Project"
|
|
||||||
)
|
|
||||||
-- stylua: ignore end
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
--- Filetype-aware REPL/runner dispatcher.
|
|
||||||
--- Keeps the existing terminal setup intact; it only decides *which* runner
|
|
||||||
--- to use for the current buffer/filetype.
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
--- Resolve the filetype to use for dispatching.
|
|
||||||
--- Quarto buffers report `quarto`; R buffers report `r`. Fallback to the
|
|
||||||
--- actual filetype if no special handling is needed.
|
|
||||||
local function dispatch_ft()
|
|
||||||
local ft = vim.bo.filetype
|
|
||||||
if ft == "quarto" or ft == "rmd" or ft == "markdown" then
|
|
||||||
return "quarto"
|
|
||||||
end
|
|
||||||
return ft
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Send the current line to the active REPL/runner.
|
|
||||||
function M.send_line()
|
|
||||||
local ft = dispatch_ft()
|
|
||||||
|
|
||||||
if ft == "r" then
|
|
||||||
-- R.nvim v1+ exposes a Lua API; try the modern `r.send` module first,
|
|
||||||
-- then fall back to the older `r.run` module, and finally to <Plug>.
|
|
||||||
local ok, rmod = pcall(require, "r.send")
|
|
||||||
if not ok or not rmod then
|
|
||||||
ok, rmod = pcall(require, "r.run")
|
|
||||||
end
|
|
||||||
if ok and rmod then
|
|
||||||
if type(rmod.line) == "function" then
|
|
||||||
rmod.line()
|
|
||||||
return
|
|
||||||
elseif type(rmod.send_line) == "function" then
|
|
||||||
rmod.send_line()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vim.api.nvim_feedkeys(
|
|
||||||
vim.api.nvim_replace_termcodes("<Plug>RDSendLine", true, false, true),
|
|
||||||
"m",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if ft == "quarto" then
|
|
||||||
local ok, runner = pcall(require, "quarto.runner")
|
|
||||||
if ok and runner then
|
|
||||||
if runner.run_line then
|
|
||||||
runner.run_line()
|
|
||||||
elseif runner.run_cell then
|
|
||||||
runner.run_cell()
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Default: vim-slime (terminal).
|
|
||||||
local line = vim.api.nvim_get_current_line()
|
|
||||||
vim.fn["slime#send"](line .. "\n")
|
|
||||||
-- Move to the next line, matching the previous behaviour.
|
|
||||||
vim.cmd("normal! j")
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Send the current visual selection to the active REPL/runner.
|
|
||||||
function M.send_selection()
|
|
||||||
local ft = dispatch_ft()
|
|
||||||
|
|
||||||
if ft == "r" then
|
|
||||||
-- Prefer R.nvim v1+ Lua API; try the modern `r.send` module first,
|
|
||||||
-- then fall back to the older `r.run` module, and finally to <Plug>.
|
|
||||||
local ok, rmod = pcall(require, "r.send")
|
|
||||||
if not ok or not rmod then
|
|
||||||
ok, rmod = pcall(require, "r.run")
|
|
||||||
end
|
|
||||||
if ok and rmod then
|
|
||||||
if type(rmod.selection) == "function" then
|
|
||||||
rmod.selection()
|
|
||||||
return
|
|
||||||
elseif type(rmod.send_selection) == "function" then
|
|
||||||
rmod.send_selection()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vim.api.nvim_feedkeys(
|
|
||||||
vim.api.nvim_replace_termcodes("<Plug>RSendSelection", true, false, true),
|
|
||||||
"m",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- For Quarto/others, fall back to vim-slime's visual send.
|
|
||||||
vim.api.nvim_feedkeys(
|
|
||||||
vim.api.nvim_replace_termcodes("<Plug>SlimeRegionSend", true, false, true),
|
|
||||||
"m",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
local Config = require('config')
|
|
||||||
local helpers = require('keymap.helpers')
|
|
||||||
local nmap_leader = helpers.nmap_leader
|
|
||||||
|
|
||||||
-- Exit terminal insert mode with <Esc>
|
|
||||||
vim.keymap.set("t", "<Esc>", [[<C-\><C-n>]], { desc = "Exit terminal mode" })
|
|
||||||
|
|
||||||
-- t is for 'terminal'
|
|
||||||
nmap_leader("tc", '<Cmd>lua Config.terminal.open_clickhouse_client()<CR>', 'Open Clickhouse client')
|
|
||||||
nmap_leader("tl", '<Cmd>lua Config.terminal.open_clickhouse_local()<CR>', 'Open Clickhouse local')
|
|
||||||
nmap_leader("tp", '<Cmd>lua Config.terminal.open_python()<CR>', 'Open Python')
|
|
||||||
nmap_leader("tj", '<Cmd>lua Config.terminal.open_julia()<CR>', 'Open Julia')
|
|
||||||
nmap_leader("td", '<Cmd>lua Config.terminal.open_duckdb();Config.terminal.toggle_bracket()<CR>', 'Open DuckDB')
|
|
||||||
nmap_leader("tx", '<Cmd>lua Config.terminal.open_in_terminal()<CR>', 'Terminal Command')
|
|
||||||
nmap_leader("tt", '<Cmd>lua Config.terminal.open_shell()<CR>', 'Terminal')
|
|
||||||
nmap_leader("tb", '<Cmd>lua Config.terminal.toggle_bracket()<CR>', 'Toggle bracketed paste')
|
|
||||||
nmap_leader("up", '<Cmd>lua Config.terminal.toggle_bracket()<CR>', 'Toggle bracketed paste')
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
-- Define comment node types as constants
|
|
||||||
local COMMENT_TYPES = {
|
|
||||||
comment = true,
|
|
||||||
block_comment = true,
|
|
||||||
line_comment = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Helper function to check if value exists in list (optimized with early return)
|
-- Helper function to check if value exists in list (optimized with early return)
|
||||||
local function is_in_list(list, value)
|
local function is_in_list(list, value)
|
||||||
if not list or not value then
|
if not list or not value then
|
||||||
|
|
@ -21,153 +14,183 @@ local function is_in_list(list, value)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Safely get the Tree-sitter node under the cursor.
|
-- Define comment node types as constants
|
||||||
|
local COMMENT_TYPES = {
|
||||||
|
comment = true,
|
||||||
|
block_comment = true,
|
||||||
|
line_comment = true,
|
||||||
|
}
|
||||||
|
|
||||||
function M.get_current_node()
|
function M.get_current_node()
|
||||||
local ok, node = pcall(vim.treesitter.get_node, { ignore_injections = false })
|
local ts_utils = require('nvim-treesitter.ts_utils')
|
||||||
return ok and node or nil
|
local cur_win = vim.api.nvim_get_current_win()
|
||||||
|
return ts_utils.get_node_at_cursor(cur_win, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Detect the root node type of the current buffer's Tree-sitter tree.
|
|
||||||
function M.detect_global_node()
|
function M.detect_global_node()
|
||||||
local cur_node = M.get_current_node()
|
local cur_node = M.get_current_node()
|
||||||
local root
|
local root
|
||||||
|
|
||||||
if not cur_node then
|
if not cur_node then
|
||||||
local ok, parser = pcall(vim.treesitter.get_parser)
|
-- print("No node detected")
|
||||||
if not ok or not parser then
|
local parser = vim.treesitter.get_parser()
|
||||||
|
if not parser then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local trees = parser:parse()
|
root = parser:parse()[1]:root()
|
||||||
if not trees or not trees[1] then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
root = trees[1]:root()
|
|
||||||
else
|
else
|
||||||
root = cur_node:root()
|
root = cur_node:root()
|
||||||
end
|
end
|
||||||
|
|
||||||
return root and root:type() or nil
|
if not root then
|
||||||
end
|
|
||||||
|
|
||||||
-- Ascend the tree from the current node until we hit a node whose parent is a
|
|
||||||
-- "global" node (or the root). This is the unit of code we want to send.
|
|
||||||
local function get_target_node(global_nodes)
|
|
||||||
local root_type = M.detect_global_node()
|
|
||||||
global_nodes = global_nodes or {}
|
|
||||||
|
|
||||||
local node = M.get_current_node()
|
|
||||||
if not node then
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
while node do
|
return root:type()
|
||||||
local parent = node:parent()
|
|
||||||
if not parent then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local p_type = parent:type()
|
|
||||||
if is_in_list(global_nodes, p_type) or p_type == root_type then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
node = parent
|
|
||||||
end
|
|
||||||
|
|
||||||
return node
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Move the cursor to the next named sibling that is not a comment.
|
function M.move_to_next_non_empty_line()
|
||||||
-- Operates on the Tree-sitter AST instead of scanning lines, so it is fast and
|
local ts_utils = require('nvim-treesitter.ts_utils')
|
||||||
-- language-agnostic.
|
-- Search for the next non-empty line
|
||||||
function M.move_to_next_non_empty_line(current_node)
|
local line_num = vim.fn.search("[^;\\s]", "W")
|
||||||
local node = current_node
|
|
||||||
if not node then
|
if line_num <= 0 then
|
||||||
node = get_target_node({})
|
-- print("No non-empty line found below the current position")
|
||||||
end
|
|
||||||
if not node then
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Walk up the tree until we find a node with a next named sibling,
|
-- Get the line content and find first non-whitespace character
|
||||||
-- so we escape nested blocks when we are on the last statement.
|
local line_content = vim.api.nvim_buf_get_lines(0, line_num - 1, line_num, false)[1]
|
||||||
while node and not node:next_named_sibling() do
|
local first_non_ws = line_content:find("%S") or 1
|
||||||
node = node:parent()
|
vim.api.nvim_win_set_cursor(0, { line_num, first_non_ws - 1 })
|
||||||
end
|
|
||||||
|
|
||||||
node = node:next_named_sibling()
|
local node = M.get_current_node()
|
||||||
while node do
|
if not node or not node:type() then
|
||||||
if not COMMENT_TYPES[node:type()] then
|
-- print("No node found")
|
||||||
local start_row, start_col = node:range()
|
|
||||||
pcall(vim.api.nvim_win_set_cursor, 0, { start_row + 1, start_col })
|
|
||||||
return true, node
|
|
||||||
end
|
|
||||||
node = node:next_named_sibling()
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.vselect_node(node)
|
|
||||||
if not node then
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local start_row, _, end_row, _ = node:range()
|
local global_node_type = M.detect_global_node()
|
||||||
|
|
||||||
vim.api.nvim_win_set_cursor(0, { start_row + 1, 0 })
|
-- Skip comments and global nodes
|
||||||
vim.cmd("normal! V")
|
while node and (COMMENT_TYPES[node:type()] or node:type() == global_node_type) do
|
||||||
vim.api.nvim_win_set_cursor(0, { end_row + 1, 0 })
|
line_num = line_num + 1
|
||||||
|
local max_lines = vim.api.nvim_buf_line_count(0)
|
||||||
|
|
||||||
|
if line_num > max_lines then
|
||||||
|
-- print("Reached end of buffer")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the line content and find first non-whitespace character
|
||||||
|
line_content = vim.api.nvim_buf_get_lines(0, line_num - 1, line_num, false)[1]
|
||||||
|
first_non_ws = line_content:find("%S") or 1
|
||||||
|
vim.api.nvim_win_set_cursor(0, { line_num, first_non_ws - 1 })
|
||||||
|
node = ts_utils.get_node_at_cursor()
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.vselect_node(node)
|
||||||
|
local ts_utils = require('nvim-treesitter.ts_utils')
|
||||||
|
if not node then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local cur_buf = vim.api.nvim_get_current_buf()
|
||||||
|
ts_utils.update_selection(cur_buf, node, "V")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
function M.select_until_global(global_nodes)
|
function M.select_until_global(global_nodes)
|
||||||
local target = get_target_node(global_nodes)
|
local ts_utils = require('nvim-treesitter.ts_utils')
|
||||||
if not target then
|
local root_node = M.detect_global_node()
|
||||||
|
if not root_node and global_nodes then
|
||||||
|
root_node = global_nodes[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Use empty table if no global nodes provided
|
||||||
|
global_nodes = global_nodes or {}
|
||||||
|
|
||||||
|
local node = ts_utils.get_node_at_cursor()
|
||||||
|
if not node then
|
||||||
|
-- print("No syntax node found at cursor position")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if M.vselect_node(target) then
|
local node_type = node:type()
|
||||||
return target
|
|
||||||
|
if node_type == root_node then
|
||||||
|
-- print("Cursor is on the root " .. root_node .. " node or in an empty area.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if current node is a global
|
||||||
|
if is_in_list(global_nodes, node_type) then
|
||||||
|
if M.vselect_node(node) then
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Traverse up the tree until we find a global node or reach the root
|
||||||
|
local parent = node:parent()
|
||||||
|
local parent_type = parent:type() or ""
|
||||||
|
if parent and is_in_list(global_nodes, parent_type) then
|
||||||
|
if M.vselect_node(node) then
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while parent and not is_in_list(global_nodes, parent:type()) do
|
||||||
|
node = parent
|
||||||
|
parent = node:parent()
|
||||||
|
end
|
||||||
|
|
||||||
|
if M.vselect_node(node) then
|
||||||
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.slime_send_region()
|
function M.slime_send_region()
|
||||||
vim.api.nvim_feedkeys(
|
-- Check if slime plugin is available
|
||||||
vim.api.nvim_replace_termcodes("<Plug>SlimeRegionSend", true, false, true),
|
if not vim.fn.exists('*slime#send_op') then
|
||||||
"m",
|
vim.notify("slime plugin not available", vim.log.levels.ERROR)
|
||||||
false
|
return
|
||||||
)
|
end
|
||||||
|
|
||||||
|
local slime_command = ":<C-u>call slime#send_op(visualmode(), 1)<CR>"
|
||||||
|
local termcodes = vim.api.nvim_replace_termcodes(slime_command, true, true, true)
|
||||||
|
|
||||||
|
vim.api.nvim_feedkeys(termcodes, "x", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.send_repl(global_nodes)
|
function M.send_repl(global_nodes)
|
||||||
local target_node = get_target_node(global_nodes)
|
local ts_utils = require('nvim-treesitter.ts_utils')
|
||||||
if not target_node then
|
local cur_node = M.get_current_node()
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If sitting on a comment, step forward first so we don't send comments.
|
if not cur_node then
|
||||||
if COMMENT_TYPES[target_node:type()] then
|
M.move_to_next_non_empty_line()
|
||||||
local moved, next_node = M.move_to_next_non_empty_line(target_node)
|
else
|
||||||
if not moved or not next_node then
|
local cur_type = cur_node:type()
|
||||||
return
|
if COMMENT_TYPES[cur_type] or is_in_list(global_nodes, cur_type) then
|
||||||
|
M.move_to_next_non_empty_line()
|
||||||
end
|
end
|
||||||
target_node = next_node
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Extract node text and send directly to avoid visual-mode/feedkeys races.
|
local sel_node = M.select_until_global(global_nodes)
|
||||||
local ok, text = pcall(vim.treesitter.get_node_text, target_node, 0)
|
if not sel_node then
|
||||||
if not ok or not text then
|
-- print("No node selected for REPL")
|
||||||
vim.notify("Could not extract code from Tree-sitter node", vim.log.levels.WARN)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.fn["slime#send"](text .. "\n")
|
-- Send the selected text to the terminal using vim-slime
|
||||||
|
M.slime_send_region()
|
||||||
|
|
||||||
-- Jump to the next relevant AST node instead of scanning lines
|
-- Move cursor and continue
|
||||||
M.move_to_next_non_empty_line(target_node)
|
ts_utils.goto_node(sel_node, true)
|
||||||
|
M.move_to_next_non_empty_line()
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
{
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
# Include packages from a category only if that category is enabled.
|
|
||||||
# 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.lang_packages.r;
|
|
||||||
rWrapperPackages = rPackages;
|
|
||||||
quartoPkg =
|
|
||||||
if config.cats.r or false
|
|
||||||
then pkgs.rpkgs.quarto.override { extraRPackages = rPackages; }
|
|
||||||
else pkgs.quarto;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.catPkgs = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (lib.types.listOf lib.types.package);
|
|
||||||
description = "Per-cat package lists, gated by cat toggles. Single source of truth for runtimeDeps and devShells.";
|
|
||||||
};
|
|
||||||
|
|
||||||
config.catPkgs = {
|
|
||||||
always = maybe "always" (with pkgs; [
|
|
||||||
ripgrep
|
|
||||||
]);
|
|
||||||
|
|
||||||
clickhouse = maybe "clickhouse" (with pkgs; [ clickhouse-lts ]);
|
|
||||||
|
|
||||||
external = maybe "external" (with pkgs; [
|
|
||||||
nodejs
|
|
||||||
perl
|
|
||||||
ruby
|
|
||||||
shfmt
|
|
||||||
sqlfluff
|
|
||||||
tree-sitter
|
|
||||||
]);
|
|
||||||
|
|
||||||
julia = maybe "julia" [
|
|
||||||
(pkgs.julia-bin.withPackages config.settings.lang_packages.julia)
|
|
||||||
];
|
|
||||||
|
|
||||||
lua = maybe "lua" (with pkgs; [ lua-language-server ]);
|
|
||||||
|
|
||||||
markdown = maybe "markdown" (with pkgs; [
|
|
||||||
python3Packages.pylatexenc
|
|
||||||
quartoPkg
|
|
||||||
zk
|
|
||||||
marksman
|
|
||||||
texlab
|
|
||||||
imagemagick
|
|
||||||
harper
|
|
||||||
]);
|
|
||||||
|
|
||||||
nix = maybe "nix" (with pkgs; [
|
|
||||||
alejandra
|
|
||||||
nix-doc
|
|
||||||
nixd
|
|
||||||
]);
|
|
||||||
|
|
||||||
optional = maybe "optional" (with pkgs; [
|
|
||||||
bat
|
|
||||||
broot
|
|
||||||
devenv
|
|
||||||
dust
|
|
||||||
fd
|
|
||||||
fzf
|
|
||||||
gawk
|
|
||||||
gh
|
|
||||||
git
|
|
||||||
hunspell
|
|
||||||
hunspellDicts.de-at
|
|
||||||
hunspellDicts.en-us
|
|
||||||
ispell
|
|
||||||
jq
|
|
||||||
just
|
|
||||||
lazygit
|
|
||||||
man
|
|
||||||
ncdu
|
|
||||||
pigz
|
|
||||||
poppler
|
|
||||||
ripgrep
|
|
||||||
tokei
|
|
||||||
wget
|
|
||||||
yq
|
|
||||||
]);
|
|
||||||
|
|
||||||
python = maybe "python" (let
|
|
||||||
python_packages_fn =
|
|
||||||
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; [
|
|
||||||
python_with_packages
|
|
||||||
ruff
|
|
||||||
basedpyright
|
|
||||||
uv
|
|
||||||
]);
|
|
||||||
|
|
||||||
r = maybe "r" [
|
|
||||||
(pkgs.rpkgs.rWrapper.override { packages = rWrapperPackages; })
|
|
||||||
pkgs.rpkgs.radianWrapper
|
|
||||||
quartoPkg
|
|
||||||
pkgs.air-formatter
|
|
||||||
pkgs.yaml-language-server
|
|
||||||
];
|
|
||||||
|
|
||||||
# cats without packages get empty lists
|
|
||||||
general = [ ];
|
|
||||||
gitPlugins = [ ];
|
|
||||||
treesitterParsers = [ ];
|
|
||||||
utils = [ ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -13,8 +13,8 @@
|
||||||
Set a category to `false` to skip its dependency/plugin specs.
|
Set a category to `false` to skip its dependency/plugin specs.
|
||||||
|
|
||||||
Available categories:
|
Available categories:
|
||||||
- always: always-on packages (not gated by a toggle)
|
|
||||||
- clickhouse: Clickhouse client and tools
|
- clickhouse: Clickhouse client and tools
|
||||||
|
- customPlugins: local plugin specs
|
||||||
- external: external tools and integrations
|
- external: external tools and integrations
|
||||||
- general: core Neovim plugins/features
|
- general: core Neovim plugins/features
|
||||||
- gitPlugins: git-related plugins
|
- gitPlugins: git-related plugins
|
||||||
|
|
@ -25,24 +25,26 @@
|
||||||
- optional: optional tools and utilities
|
- optional: optional tools and utilities
|
||||||
- python: Python tooling and plugins
|
- python: Python tooling and plugins
|
||||||
- r: R tooling and plugins
|
- r: R tooling and plugins
|
||||||
|
- test: test-only tooling (disabled by default)
|
||||||
- treesitterParsers: Treesitter parsers
|
- treesitterParsers: Treesitter parsers
|
||||||
- utils: general utilities
|
- utils: general utilities
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
config.cats = {
|
config.cats = {
|
||||||
always = lib.mkDefault true;
|
|
||||||
clickhouse = lib.mkDefault false;
|
clickhouse = lib.mkDefault false;
|
||||||
|
customPlugins = lib.mkDefault true;
|
||||||
external = lib.mkDefault true;
|
external = lib.mkDefault true;
|
||||||
general = lib.mkDefault true;
|
general = lib.mkDefault true;
|
||||||
gitPlugins = lib.mkDefault true;
|
gitPlugins = lib.mkDefault true;
|
||||||
julia = lib.mkDefault false;
|
julia = lib.mkDefault false;
|
||||||
lua = lib.mkDefault true;
|
lua = lib.mkDefault true;
|
||||||
markdown = lib.mkDefault true;
|
markdown = lib.mkDefault false;
|
||||||
nix = lib.mkDefault true;
|
nix = lib.mkDefault true;
|
||||||
optional = lib.mkDefault false;
|
optional = lib.mkDefault false;
|
||||||
python = lib.mkDefault false;
|
python = lib.mkDefault false;
|
||||||
r = lib.mkDefault true;
|
r = lib.mkDefault false;
|
||||||
|
test = lib.mkDefault false;
|
||||||
treesitterParsers = lib.mkDefault true;
|
treesitterParsers = lib.mkDefault true;
|
||||||
utils = lib.mkDefault true;
|
utils = lib.mkDefault true;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@
|
||||||
# Enable RC wrapping (allows neovim to find the config)
|
# Enable RC wrapping (allows neovim to find the config)
|
||||||
config.settings.wrapRc = lib.mkDefault true;
|
config.settings.wrapRc = lib.mkDefault true;
|
||||||
|
|
||||||
|
# Lua packages available to neovim (for :lua require())
|
||||||
|
config.settings.nvim_lua_env = lp:
|
||||||
|
lib.optionals (config.cats.general or false) [ lp.tiktoken_core ];
|
||||||
|
|
||||||
# Binary name for the wrapper
|
# Binary name for the wrapper
|
||||||
config.binName = lib.mkDefault "vv";
|
config.binName = lib.mkDefault "vv";
|
||||||
|
|
||||||
|
|
@ -21,17 +25,11 @@
|
||||||
config.settings.block_normal_config = true;
|
config.settings.block_normal_config = true;
|
||||||
|
|
||||||
# Don't symlink the config (we wrap it instead)
|
# Don't symlink the config (we wrap it instead)
|
||||||
config.settings.dont_link = lib.mkDefault false;
|
config.settings.dont_link = false;
|
||||||
|
|
||||||
# Create additional aliases for the binary
|
# Create additional aliases for the binary
|
||||||
config.settings.aliases = lib.mkDefault [ "vvim" ];
|
config.settings.aliases = [ "vvim" ];
|
||||||
|
|
||||||
# Enable wrapper handling of spec runtimeDeps (template pattern).
|
# Enable wrapper handling of spec runtimeDeps (template pattern).
|
||||||
config.settings.autowrapRuntimeDeps = true;
|
config.settings.autowrapRuntimeDeps = true;
|
||||||
|
|
||||||
# The wrapper library currently emits runtime PATH additions via `suffixVar`,
|
|
||||||
# which lets host-level tools in `/usr/local/bin` win inside `nix run`.
|
|
||||||
# Mirror those additions into `prefixVar` so wrapped Neovim resolves the
|
|
||||||
# Nix-provided toolchain first while preserving existing wrapper behavior.
|
|
||||||
config.prefixVar = lib.mkAfter config.suffixVar;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,12 @@
|
||||||
UV_PYTHON_DOWNLOADS = "never";
|
UV_PYTHON_DOWNLOADS = "never";
|
||||||
UV_PYTHON = pkgs.python.interpreter;
|
UV_PYTHON = pkgs.python.interpreter;
|
||||||
})
|
})
|
||||||
# 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.
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# Environment variables with defaults (can be overridden by user)
|
# Environment variables with defaults (can be overridden by user)
|
||||||
config.envDefault = lib.mkMerge [ ];
|
config.envDefault = lib.mkMerge [
|
||||||
|
(lib.mkIf (config.cats.r or false) {
|
||||||
|
R_LIBS_USER = "./.Rlibs";
|
||||||
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,6 @@
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
rPackages = (pkgs.baseRPackages or [ ]) ++ config.settings.lang_packages.r;
|
|
||||||
rWrapperPkg = pkgs.rpkgs.rWrapper.override { packages = rPackages ++ [pkgs.nvimcom]; };
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
config.hosts = lib.mkMerge [
|
config.hosts = lib.mkMerge [
|
||||||
{
|
{
|
||||||
|
|
@ -19,11 +15,21 @@ in
|
||||||
nvim-host.enable = true;
|
nvim-host.enable = true;
|
||||||
nvim-host.package = "${pkgs.neovide}/bin/neovide";
|
nvim-host.package = "${pkgs.neovide}/bin/neovide";
|
||||||
nvim-host.argv0 = "neovide";
|
nvim-host.argv0 = "neovide";
|
||||||
nvim-host.flags."--neovim-bin" = "${builtins.placeholder "out"}/bin/${config.binName}";
|
nvim-host.flags."--neovim-bin" = "${placeholder "out"}/bin/${config.binName}";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
m = {
|
||||||
|
nvim-host.enable = false;
|
||||||
|
nvim-host.package = "${pkgs.uv}/bin/uv";
|
||||||
|
nvim-host.argv0 = "uv";
|
||||||
|
nvim-host.addFlag = [
|
||||||
|
"run"
|
||||||
|
"marimo"
|
||||||
|
"edit"
|
||||||
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
(lib.mkIf (config.cats.julia or false) {
|
(lib.mkIf (config.cats.julia or true) {
|
||||||
jl = {
|
jl = {
|
||||||
nvim-host.enable = true;
|
nvim-host.enable = true;
|
||||||
nvim-host.package = "${pkgs.julia-bin}/bin/julia";
|
nvim-host.package = "${pkgs.julia-bin}/bin/julia";
|
||||||
|
|
@ -33,13 +39,13 @@ in
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
(lib.mkIf (config.cats.python or false) {
|
(lib.mkIf (config.cats.python or true) {
|
||||||
python3.nvim-host.enable = true;
|
python3.nvim-host.enable = true;
|
||||||
})
|
})
|
||||||
(lib.mkIf (config.cats.r or false) {
|
(lib.mkIf (config.cats.r or true) {
|
||||||
r = {
|
r = {
|
||||||
nvim-host.enable = true;
|
nvim-host.enable = true;
|
||||||
nvim-host.package = "${rWrapperPkg}/bin/R";
|
nvim-host.package = "${pkgs.rWrapper}/bin/R";
|
||||||
nvim-host.argv0 = "R";
|
nvim-host.argv0 = "R";
|
||||||
nvim-host.addFlag = [
|
nvim-host.addFlag = [
|
||||||
"--no-save"
|
"--no-save"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
pkgs,
|
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
@ -27,33 +26,14 @@
|
||||||
};
|
};
|
||||||
default = { };
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Language-specific package defaults and downstream overrides appended to each
|
Language-specific package overrides appended to each language spec's extraPackages.
|
||||||
language spec's runtime packages.
|
Intended for flake.nix overrides via wrapper.config.wrap.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
config.settings.lang_packages = {
|
config.settings.lang_packages = {
|
||||||
python = lib.mkDefault (with pkgs.python3Packages; [
|
python = lib.mkDefault [ ];
|
||||||
duckdb
|
r = lib.mkDefault [ ];
|
||||||
polars
|
julia = lib.mkDefault [ ];
|
||||||
]);
|
|
||||||
r = lib.mkDefault (
|
|
||||||
(with pkgs.rpkgs.rPackages; [
|
|
||||||
arrow
|
|
||||||
broom
|
|
||||||
data_table
|
|
||||||
janitor
|
|
||||||
styler
|
|
||||||
# vscDebugger is not on CRAN/Bioconductor, so it is not available in
|
|
||||||
# pkgs.rpkgs.rPackages. Install it manually in your R library if you
|
|
||||||
# want to use the nvim-dap R adapter (see plugin/26_dap.lua).
|
|
||||||
# vscDebugger
|
|
||||||
lintr
|
|
||||||
])
|
|
||||||
);
|
|
||||||
julia = lib.mkDefault [
|
|
||||||
"DataFramesMeta"
|
|
||||||
"QuackIO"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
modules/module/settings/runtime-path.nix
Normal file
35
modules/module/settings/runtime-path.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
collect_runtime_packages = runtime_deps_type:
|
||||||
|
config.specCollect
|
||||||
|
(acc: spec:
|
||||||
|
let
|
||||||
|
is_enabled = if spec ? enable then spec.enable else true;
|
||||||
|
has_runtime_deps = (spec.runtimeDeps or false) == runtime_deps_type;
|
||||||
|
packages = spec.extraPackages or [ ];
|
||||||
|
in
|
||||||
|
acc ++ lib.optionals (is_enabled && has_runtime_deps) packages
|
||||||
|
)
|
||||||
|
[ ];
|
||||||
|
|
||||||
|
prefix_packages = collect_runtime_packages "prefix";
|
||||||
|
suffix_packages = collect_runtime_packages "suffix";
|
||||||
|
|
||||||
|
to_path_specs = packages: [
|
||||||
|
{
|
||||||
|
data = [
|
||||||
|
"PATH"
|
||||||
|
":"
|
||||||
|
"${lib.makeBinPath packages}"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config.prefixVar = lib.optionals (prefix_packages != [ ]) (to_path_specs prefix_packages);
|
||||||
|
config.suffixVar = lib.optionals (suffix_packages != [ ]) (to_path_specs suffix_packages);
|
||||||
|
}
|
||||||
51
modules/module/specs/cats-enable.nix
Normal file
51
modules/module/specs/cats-enable.nix
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
# This module implements category-based enabling of specs.
|
||||||
|
# It runs early (order 200) so other specMaps can see the enable flags.
|
||||||
|
#
|
||||||
|
# How it works:
|
||||||
|
# 1. For each spec, extract its name (removing -lazy suffix if present)
|
||||||
|
# 2. Check if there's a corresponding cats.<name> toggle
|
||||||
|
# 3. Set spec.value.enable based on the cats toggle (default: true)
|
||||||
|
# 4. This allows specs to be conditionally included based on config.cats settings
|
||||||
|
#
|
||||||
|
# Example: If config.cats.python = false, then specs.python.enable = false
|
||||||
|
|
||||||
|
config.specMaps = lib.mkOrder 200 [
|
||||||
|
{
|
||||||
|
name = "CATS_ENABLE";
|
||||||
|
data =
|
||||||
|
list:
|
||||||
|
map (
|
||||||
|
v:
|
||||||
|
if v.type == "spec" || v.type == "parent" then
|
||||||
|
let
|
||||||
|
# Extract spec name, handling lazy specs (remove -lazy suffix)
|
||||||
|
specName =
|
||||||
|
if v.name == null then
|
||||||
|
null
|
||||||
|
else if lib.hasSuffix "-lazy" v.name then
|
||||||
|
lib.removeSuffix "-lazy" v.name
|
||||||
|
else
|
||||||
|
v.name;
|
||||||
|
|
||||||
|
# Check if this spec has a corresponding cat toggle
|
||||||
|
catEnabled =
|
||||||
|
if specName != null && builtins.hasAttr specName config.cats then
|
||||||
|
config.cats.${specName}
|
||||||
|
else
|
||||||
|
true; # Default to enabled if no cat toggle exists
|
||||||
|
in
|
||||||
|
v
|
||||||
|
// {
|
||||||
|
value = v.value // {
|
||||||
|
# Use explicit enable if set, otherwise use cat toggle
|
||||||
|
enable = if v.value ? enable then v.value.enable else catEnabled;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
v
|
||||||
|
) list;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -5,78 +5,184 @@
|
||||||
wlib,
|
wlib,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
|
# ============================================================================
|
||||||
|
# SPEC MODULE DEFAULTS
|
||||||
|
# ============================================================================
|
||||||
|
# Define default options available to all specs
|
||||||
|
|
||||||
config.specMods = {parentSpec ? null, ...}: {
|
config.specMods = {parentSpec ? null, ...}: {
|
||||||
options.runtimePkgs = lib.mkOption {
|
options.extraPackages = lib.mkOption {
|
||||||
type = lib.types.listOf wlib.types.stringable;
|
type = lib.types.listOf wlib.types.stringable;
|
||||||
default = [];
|
default = [];
|
||||||
description = "a runtimePkgs spec field to put packages to suffix to the PATH";
|
description = "a extraPackages spec field to put packages to suffix to the PATH";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.always = lib.mkIf (config.cats.always or false) {
|
# ============================================================================
|
||||||
data = lib.mkDefault null;
|
# EXTERNAL TOOLS SPEC
|
||||||
runtimeDeps = "prefix";
|
# ============================================================================
|
||||||
runtimePkgs = config.catPkgs.always;
|
# Core system tools and utilities
|
||||||
};
|
|
||||||
|
|
||||||
config.specs.external = lib.mkIf (config.cats.external or false) {
|
config.specs.external = {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
before = ["INIT_MAIN"];
|
before = ["INIT_MAIN"];
|
||||||
config = ''
|
config = ''
|
||||||
vim.o.shell = "${pkgs.zsh}/bin/zsh"
|
vim.o.shell = "${pkgs.zsh}/bin/zsh"
|
||||||
'';
|
'';
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.external;
|
extraPackages = with pkgs; [
|
||||||
|
perl
|
||||||
|
ruby
|
||||||
|
shfmt
|
||||||
|
sqlfluff
|
||||||
|
tree-sitter
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.optional = lib.mkIf (config.cats.optional or false) {
|
# ============================================================================
|
||||||
|
# OPTIONAL TOOLS SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.optional = lib.mkIf (config.cats.optional or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
before = ["INIT_MAIN"];
|
before = ["INIT_MAIN"];
|
||||||
runtimePkgs = config.catPkgs.optional;
|
extraPackages = with pkgs; [
|
||||||
|
bat
|
||||||
|
broot
|
||||||
|
devenv
|
||||||
|
dust
|
||||||
|
fd
|
||||||
|
fzf
|
||||||
|
gawk
|
||||||
|
gh
|
||||||
|
git
|
||||||
|
hunspell
|
||||||
|
hunspellDicts.de-at
|
||||||
|
hunspellDicts.en-us
|
||||||
|
ispell
|
||||||
|
jq
|
||||||
|
just
|
||||||
|
lazygit
|
||||||
|
man
|
||||||
|
ncdu
|
||||||
|
pigz
|
||||||
|
poppler
|
||||||
|
ripgrep
|
||||||
|
tokei
|
||||||
|
wget
|
||||||
|
yq
|
||||||
|
zathura
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.markdown = lib.mkIf (config.cats.markdown or false) {
|
# ============================================================================
|
||||||
|
# MARKDOWN SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.markdown = lib.mkIf (config.cats.markdown or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.markdown;
|
extraPackages = with pkgs; [
|
||||||
|
python313Packages.pylatexenc
|
||||||
|
quarto
|
||||||
|
zk
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.nix = lib.mkIf (config.cats.nix or false) {
|
# ============================================================================
|
||||||
|
# NIX SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.nix = lib.mkIf (config.cats.nix or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.nix;
|
extraPackages = with pkgs; [
|
||||||
|
alejandra
|
||||||
|
nix-doc
|
||||||
|
nixd
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.lua = lib.mkIf (config.cats.lua or false) {
|
# ============================================================================
|
||||||
|
# LUA SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.lua = lib.mkIf (config.cats.lua or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.lua;
|
extraPackages = with pkgs; [
|
||||||
|
lua-language-server
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.python = lib.mkIf (config.cats.python or false) {
|
# ============================================================================
|
||||||
|
# PYTHON SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.python = lib.mkIf (config.cats.python or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.python;
|
extraPackages = let
|
||||||
|
python_packages_fn =
|
||||||
|
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; [
|
||||||
|
python_with_packages
|
||||||
|
nodejs
|
||||||
|
ruff
|
||||||
|
basedpyright
|
||||||
|
uv
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.r = lib.mkIf (config.cats.r or false) {
|
# ============================================================================
|
||||||
|
# R SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.r = lib.mkIf (config.cats.r or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.r;
|
extraPackages = let
|
||||||
|
r_packages = (pkgs.baseRPackages or []) ++ config.settings.lang_packages.r;
|
||||||
|
in
|
||||||
|
with pkgs; [
|
||||||
|
(rWrapper.override {packages = r_packages;})
|
||||||
|
radianWrapper
|
||||||
|
(quarto.override {extraRPackages = r_packages;})
|
||||||
|
air-formatter
|
||||||
|
yaml-language-server
|
||||||
|
updateR
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.julia = lib.mkIf (config.cats.julia or false) {
|
# ============================================================================
|
||||||
|
# JULIA SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.julia = lib.mkIf (config.cats.julia or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.julia;
|
extraPackages = let
|
||||||
|
julia_with_packages =
|
||||||
|
pkgs.julia-bin.withPackages config.settings.lang_packages.julia;
|
||||||
|
in [julia_with_packages];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.clickhouse = lib.mkIf (config.cats.clickhouse or false) {
|
# ============================================================================
|
||||||
|
# CLICKHOUSE SPEC
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
config.specs.clickhouse = lib.mkIf (config.cats.clickhouse or true) {
|
||||||
data = lib.mkDefault null;
|
data = lib.mkDefault null;
|
||||||
runtimeDeps = "prefix";
|
runtimeDeps = "prefix";
|
||||||
runtimePkgs = config.catPkgs.clickhouse;
|
extraPackages = with pkgs; [
|
||||||
|
clickhouse-lts
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.runtimePkgs = config.specCollect (acc: v: acc ++ (v.runtimePkgs or [])) [];
|
config.extraPackages = config.specCollect (acc: v: acc ++ (v.extraPackages or [])) [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,59 +4,14 @@
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
{
|
||||||
parserList = [
|
config.specs.gitPlugins = {
|
||||||
"bash"
|
data = [ ];
|
||||||
"bibtex"
|
|
||||||
"c"
|
|
||||||
"cpp"
|
|
||||||
"csv"
|
|
||||||
"diff"
|
|
||||||
"dockerfile"
|
|
||||||
"git_config"
|
|
||||||
"git_rebase"
|
|
||||||
"gitattributes"
|
|
||||||
"gitcommit"
|
|
||||||
"gitignore"
|
|
||||||
"html"
|
|
||||||
"javascript"
|
|
||||||
"json"
|
|
||||||
"julia"
|
|
||||||
"latex"
|
|
||||||
"lua"
|
|
||||||
"luadoc"
|
|
||||||
"make"
|
|
||||||
"markdown"
|
|
||||||
"markdown_inline"
|
|
||||||
"matlab"
|
|
||||||
"nix"
|
|
||||||
"python"
|
|
||||||
"query"
|
|
||||||
"r"
|
|
||||||
"rnoweb"
|
|
||||||
"regex"
|
|
||||||
"sql"
|
|
||||||
"toml"
|
|
||||||
"vim"
|
|
||||||
"vimdoc"
|
|
||||||
"xml"
|
|
||||||
"yaml"
|
|
||||||
"zig"
|
|
||||||
];
|
|
||||||
in {
|
|
||||||
options.settings.treesitter_parsers = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
default = parserList;
|
|
||||||
description = "Tree-sitter parser names to install when the treesitterParsers category is enabled.";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.gitPlugins = lib.mkIf (config.cats.gitPlugins or false) {
|
config.specs.r = {
|
||||||
data = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
config.specs.r = lib.mkIf (config.cats.r or false) {
|
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
pkgs.r-nvim
|
config.nvim-lib.neovimPlugins.r
|
||||||
quarto-nvim
|
quarto-nvim
|
||||||
{
|
{
|
||||||
data = otter-nvim;
|
data = otter-nvim;
|
||||||
|
|
@ -65,23 +20,19 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.markdown-lazy = lib.mkIf (config.cats.markdown or false) {
|
config.specs.markdown-lazy = {
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = [
|
data = [
|
||||||
config.nvim-lib.neovimPlugins.cmp-pandoc-references
|
config.nvim-lib.neovimPlugins.cmp-pandoc-references
|
||||||
pkgs.vimPlugins.image-nvim
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.general = lib.mkIf (config.cats.general or false) {
|
config.specs.general = {
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
lze
|
lze
|
||||||
lzextras
|
lzextras
|
||||||
plenary-nvim
|
plenary-nvim
|
||||||
vimtex
|
|
||||||
neogit
|
neogit
|
||||||
config.nvim-lib.neovimPlugins.bloocky
|
|
||||||
config.nvim-lib.neovimPlugins.dooing
|
|
||||||
{
|
{
|
||||||
data = mini-nvim;
|
data = mini-nvim;
|
||||||
pname = "mini.nvim";
|
pname = "mini.nvim";
|
||||||
|
|
@ -129,7 +80,7 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.lua = lib.mkIf (config.cats.lua or false) {
|
config.specs.lua = {
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
luvit-meta
|
luvit-meta
|
||||||
{
|
{
|
||||||
|
|
@ -139,7 +90,7 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.markdown = lib.mkIf (config.cats.markdown or false) {
|
config.specs.markdown = {
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
quarto-nvim
|
quarto-nvim
|
||||||
render-markdown-nvim
|
render-markdown-nvim
|
||||||
|
|
@ -154,28 +105,59 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.utils = lib.mkIf (config.cats.utils or false) {
|
config.specs.utils = {
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
blink-cmp
|
blink-cmp
|
||||||
nvim-lspconfig
|
nvim-lspconfig
|
||||||
nvim-treesitter-context
|
nvim-treesitter-context
|
||||||
nvim-treesitter-textobjects
|
nvim-treesitter-textobjects
|
||||||
{
|
{
|
||||||
data = pkgs.vimPlugins.nvim-treesitter;
|
data = pkgs.codecompanion-nvim;
|
||||||
pname = "nvim-treesitter";
|
pname = "codecompanion";
|
||||||
}
|
}
|
||||||
{
|
];
|
||||||
data = pkgs.vimPlugins.opencode-nvim;
|
|
||||||
pname = "opencode.nvim";
|
|
||||||
}
|
|
||||||
] ++ builtins.attrValues pkgs.vimPlugins.nvim-treesitter.queries;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.treesitterParsers = lib.mkIf (config.cats.treesitterParsers or false) {
|
config.specs.treesitterParsers = {
|
||||||
data = map (name: pkgs.vimPlugins.nvim-treesitter-parsers.${name}) config.settings.treesitter_parsers;
|
data = with pkgs.vimPlugins.nvim-treesitter-parsers; [
|
||||||
|
bash
|
||||||
|
c
|
||||||
|
cpp
|
||||||
|
csv
|
||||||
|
diff
|
||||||
|
dockerfile
|
||||||
|
git_config
|
||||||
|
git_rebase
|
||||||
|
gitattributes
|
||||||
|
gitcommit
|
||||||
|
gitignore
|
||||||
|
html
|
||||||
|
javascript
|
||||||
|
json
|
||||||
|
julia
|
||||||
|
latex
|
||||||
|
lua
|
||||||
|
luadoc
|
||||||
|
make
|
||||||
|
markdown
|
||||||
|
markdown_inline
|
||||||
|
nix
|
||||||
|
python
|
||||||
|
query
|
||||||
|
r
|
||||||
|
rnoweb
|
||||||
|
regex
|
||||||
|
sql
|
||||||
|
toml
|
||||||
|
vim
|
||||||
|
vimdoc
|
||||||
|
xml
|
||||||
|
yaml
|
||||||
|
zig
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
config.specs.utils-lazy = lib.mkIf (config.cats.utils or false) {
|
config.specs.utils-lazy = {
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
blink-compat
|
blink-compat
|
||||||
|
|
@ -184,26 +166,16 @@ in {
|
||||||
colorful-menu-nvim
|
colorful-menu-nvim
|
||||||
conform-nvim
|
conform-nvim
|
||||||
copilot-lua
|
copilot-lua
|
||||||
|
nvim-dap
|
||||||
|
nvim-dap-ui
|
||||||
|
nvim-dap-virtual-text
|
||||||
nvim-lint
|
nvim-lint
|
||||||
vim-slime
|
vim-slime
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Lazy-loaded plugins needed when the `r` cat is on. Kept separate from
|
config.specs.gitPlugins-lazy = {
|
||||||
# `utils-lazy` so users with `r=true` and `utils=false` still get the
|
|
||||||
# nvim-dap R adapter and in-buffer image rendering for plots.
|
|
||||||
config.specs.r-lazy = lib.mkIf (config.cats.r or false) {
|
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = [ ];
|
||||||
nvim-dap
|
|
||||||
nvim-dap-ui
|
|
||||||
nvim-dap-virtual-text
|
|
||||||
image-nvim
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
config.specs.gitPlugins-lazy = lib.mkIf (config.cats.gitPlugins or false) {
|
|
||||||
lazy = true;
|
|
||||||
data = [];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,29 @@ inputs:
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
|
# ============================================================================
|
||||||
|
# IMPORTS
|
||||||
|
# ============================================================================
|
||||||
|
# Import the base neovim wrapper module and all configuration modules
|
||||||
|
|
||||||
imports = [
|
imports = [
|
||||||
wlib.wrapperModules.neovim
|
wlib.wrapperModules.neovim
|
||||||
./module/settings/cat-packages.nix
|
|
||||||
./module/specs/deps.nix
|
./module/specs/deps.nix
|
||||||
./module/specs/plugins.nix
|
./module/specs/plugins.nix
|
||||||
|
./module/specs/cats-enable.nix
|
||||||
./module/settings/core.nix
|
./module/settings/core.nix
|
||||||
./module/settings/cats.nix
|
./module/settings/cats.nix
|
||||||
./module/settings/env.nix
|
./module/settings/env.nix
|
||||||
./module/settings/hosts.nix
|
./module/settings/hosts.nix
|
||||||
./module/settings/lang-packages.nix
|
./module/settings/lang-packages.nix
|
||||||
|
./module/settings/runtime-path.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HELPER FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
# Utilities for working with plugin inputs
|
||||||
|
|
||||||
options.nvim-lib.neovimPlugins = lib.mkOption {
|
options.nvim-lib.neovimPlugins = lib.mkOption {
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
type = lib.types.attrsOf wlib.types.stringable;
|
type = lib.types.attrsOf wlib.types.stringable;
|
||||||
|
|
@ -47,6 +58,11 @@ inputs:
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CONFIGURATION
|
||||||
|
# ============================================================================
|
||||||
|
# Pass cats configuration to neovim and expose metadata
|
||||||
|
|
||||||
config.settings.cats = config.cats;
|
config.settings.cats = config.cats;
|
||||||
config.info.cats = config.cats;
|
config.info.cats = config.cats;
|
||||||
config.info.nixCats_config_location = config.settings.config_directory;
|
config.info.nixCats_config_location = config.settings.config_directory;
|
||||||
|
|
|
||||||
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
|
|
||||||
|
|
@ -3,33 +3,39 @@ let
|
||||||
lib = nixpkgs.lib;
|
lib = nixpkgs.lib;
|
||||||
|
|
||||||
rOverlay = import ./r.nix {inherit inputs;};
|
rOverlay = import ./r.nix {inherit inputs;};
|
||||||
rNvimNixOverlay = inputs.r-nvim-nix.overlays.default;
|
franOverlay = inputs.fran.overlays.default;
|
||||||
pythonOverlay = import ./python.nix {inherit inputs;};
|
pythonOverlay = import ./python.nix inputs;
|
||||||
pluginsOverlay = import ./plugins.nix {inherit inputs;};
|
pluginsOverlay = import ./plugins.nix inputs;
|
||||||
|
|
||||||
dependencyOverlays = [
|
dependencyOverlays = [
|
||||||
rOverlay
|
rOverlay
|
||||||
rNvimNixOverlay
|
|
||||||
pythonOverlay
|
pythonOverlay
|
||||||
pluginsOverlay
|
pluginsOverlay
|
||||||
];
|
];
|
||||||
dependencyOverlay = lib.composeManyExtensions dependencyOverlays;
|
dependencyOverlay = lib.composeManyExtensions dependencyOverlays;
|
||||||
|
|
||||||
# franOverlay provides R-specific tooling (radianWrapper, air-formatter).
|
|
||||||
# It is scoped to rixpkgs (via overlays/r.nix) rather than the global
|
|
||||||
# package set, since it only applies to R package derivations.
|
|
||||||
franOverlay = inputs.fran.overlays.default;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
inherit
|
inherit
|
||||||
rOverlay
|
rOverlay
|
||||||
rNvimNixOverlay
|
|
||||||
franOverlay
|
franOverlay
|
||||||
pythonOverlay
|
pythonOverlay
|
||||||
pluginsOverlay
|
pluginsOverlay
|
||||||
dependencyOverlays
|
dependencyOverlays
|
||||||
dependencyOverlay;
|
dependencyOverlay;
|
||||||
|
|
||||||
|
# Named exports for downstream composition.
|
||||||
default = dependencyOverlay;
|
default = dependencyOverlay;
|
||||||
dependencies = dependencyOverlays;
|
dependencies = dependencyOverlays;
|
||||||
|
|
||||||
|
overlays = {
|
||||||
|
inherit
|
||||||
|
rOverlay
|
||||||
|
franOverlay
|
||||||
|
pythonOverlay
|
||||||
|
pluginsOverlay
|
||||||
|
dependencyOverlays
|
||||||
|
dependencyOverlay;
|
||||||
|
default = dependencyOverlay;
|
||||||
|
dependencies = dependencyOverlays;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,22 @@
|
||||||
{ ... }:
|
{ ... }:
|
||||||
final: prev:
|
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 {
|
zk-nvim = prev.vimPlugins.zk-nvim.overrideAttrs {
|
||||||
nvimSkipModules = [
|
nvimSkipModules = [
|
||||||
"zk.pickers.fzf_lua"
|
"zk.pickers.fzf_lua"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,52 @@
|
||||||
{inputs, ...}: final: prev: let
|
# R packages overlay (rix)
|
||||||
|
#
|
||||||
|
# This overlay provides access to R packages from rstats-on-nix.
|
||||||
|
#
|
||||||
|
# rstats-on-nix maintains snapshots of CRAN packages built with Nix:
|
||||||
|
# - Provides reproducible R package versions
|
||||||
|
# - Ensures binary cache availability for faster builds
|
||||||
|
# - Maintained by the rstats-on-nix community
|
||||||
|
#
|
||||||
|
# Available attributes after applying this overlay:
|
||||||
|
# - pkgs.rpkgs: R packages from rstats-on-nix
|
||||||
|
# - pkgs.rpkgs.rPackages: All CRAN packages
|
||||||
|
# - pkgs.rpkgs.quarto: Quarto publishing system
|
||||||
|
# - pkgs.rpkgs.rWrapper: R with package management
|
||||||
|
# - pkgs.rWrapper: R wrapper with standard packages pre-configured
|
||||||
|
# - pkgs.quarto: Quarto with R integration and standard packages
|
||||||
|
#
|
||||||
|
# Custom R packages and tools (radianWrapper, air-formatter) come from
|
||||||
|
# the fran overlay which should be applied separately.
|
||||||
|
#
|
||||||
|
# To use specific R packages, reference them via:
|
||||||
|
# with pkgs.rpkgs.rPackages; [ package1 package2 ]
|
||||||
|
#
|
||||||
|
# Update the R snapshot date in flake.nix inputs section:
|
||||||
|
# rixpkgs.url = "github:rstats-on-nix/nixpkgs/YYYY-MM-DD"
|
||||||
|
{
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}: final: prev: let
|
||||||
|
# R packages from rstats-on-nix for the current system
|
||||||
rpkgs = import inputs.rixpkgs {
|
rpkgs = import inputs.rixpkgs {
|
||||||
system = prev.stdenv.hostPlatform.system;
|
system = prev.stdenv.hostPlatform.system;
|
||||||
overlays = [inputs.fran.overlays.default inputs.r-nvim-nix.overlays.default];
|
overlays = [inputs.fran.overlays.default];
|
||||||
};
|
}; # rixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system};
|
||||||
|
|
||||||
|
# Standard R packages used by default in rWrapper and quarto
|
||||||
|
reqPkgs = with rpkgs.rPackages; [
|
||||||
|
# languageserver
|
||||||
|
];
|
||||||
in {
|
in {
|
||||||
inherit rpkgs;
|
inherit rpkgs;
|
||||||
baseRPackages = [rpkgs.nvimcom rpkgs.rPackages.btw];
|
baseRPackages = reqPkgs;
|
||||||
rWrapper = rpkgs.rWrapper.override {packages = [];};
|
|
||||||
quarto = rpkgs.quarto.override {extraRPackages = [];};
|
# R wrapper with standard packages
|
||||||
|
rWrapper = rpkgs.rWrapper.override {packages = reqPkgs;};
|
||||||
|
|
||||||
|
# Quarto with R integration
|
||||||
|
quarto = rpkgs.quarto.override {extraRPackages = reqPkgs;};
|
||||||
|
|
||||||
|
# Update helper for rix
|
||||||
|
updateR = import ../scripts/updater.nix {pkgs = final;};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,20 +128,27 @@ local diagnostic_opts = {
|
||||||
-- Don't update diagnostics when typing
|
-- Don't update diagnostics when typing
|
||||||
update_in_insert = false,
|
update_in_insert = false,
|
||||||
}
|
}
|
||||||
later(function()
|
later(function() vim.diagnostic.config(diagnostic_opts) end)
|
||||||
vim.diagnostic.config(diagnostic_opts)
|
|
||||||
|
|
||||||
-- Relax diagnostics for markdown (many false positives)
|
|
||||||
vim.api.nvim_create_autocmd('FileType', {
|
-- Custom autocommands ========================================================
|
||||||
pattern = { 'markdown' },
|
local augroup = vim.api.nvim_create_augroup('CustomSettings', {})
|
||||||
callback = function()
|
|
||||||
vim.diagnostic.config({
|
vim.api.nvim_create_autocmd('FileType', {
|
||||||
signs = { severity = { min = 'WARN', max = 'ERROR' } },
|
pattern = { 'markdown' },
|
||||||
virtual_text = { severity = { min = 'HINT', max = 'ERROR' }, current_line = false },
|
group = augroup,
|
||||||
})
|
callback = function()
|
||||||
end,
|
vim.diagnostic.config({
|
||||||
})
|
signs = {
|
||||||
end)
|
severity = { min = 'WARN', max = 'ERROR' }
|
||||||
|
},
|
||||||
|
virtual_text = {
|
||||||
|
current_line = false,
|
||||||
|
severity = { min = 'HINT', max = 'ERROR' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
desc = "remove formatoptions",
|
desc = "remove formatoptions",
|
||||||
|
|
@ -150,6 +157,17 @@ vim.api.nvim_create_autocmd("FileType", {
|
||||||
vim.b.minitrailspace_disable = true -- Don't highlight trailing space by default
|
vim.b.minitrailspace_disable = true -- Don't highlight trailing space by default
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd('FileType', {
|
||||||
|
group = augroup,
|
||||||
|
callback = function()
|
||||||
|
-- Don't auto-wrap comments and don't insert comment leader after hitting 'o'
|
||||||
|
-- If don't do this on `FileType`, this keeps reappearing due to being set in
|
||||||
|
-- filetype plugins.
|
||||||
|
vim.cmd('setlocal formatoptions-=r formatoptions-=o')
|
||||||
|
end,
|
||||||
|
desc = [[Ensure proper 'formatoptions']],
|
||||||
|
})
|
||||||
-- Neovide ==============================================
|
-- Neovide ==============================================
|
||||||
if vim.g.neovide then
|
if vim.g.neovide then
|
||||||
vim.g.neovide_cursor_vfx_mode = "pixiedust"
|
vim.g.neovide_cursor_vfx_mode = "pixiedust"
|
||||||
|
|
@ -158,7 +176,7 @@ if vim.g.neovide then
|
||||||
vim.g.neovide_cursor_short_animation_length = 0
|
vim.g.neovide_cursor_short_animation_length = 0
|
||||||
vim.g.neovide_font_hinting = 'none'
|
vim.g.neovide_font_hinting = 'none'
|
||||||
vim.g.neovide_font_edging = 'subpixelantialias'
|
vim.g.neovide_font_edging = 'subpixelantialias'
|
||||||
vim.o.guifont = 'JetBrainsMono Nerd Font:h14:#e-subpixelantialias:#h-none'
|
vim.o.guifont = 'Iosevka Nerd Font,Symbols Nerd Font:h14:#e-subpixelantialias:#h-none'
|
||||||
vim.g.neovide_floating_corner_radius = 0.35
|
vim.g.neovide_floating_corner_radius = 0.35
|
||||||
vim.keymap.set("n", "<leader>nf", "<cmd>NeovideFullscreen<CR>", { desc = "Toggle Neovide Fullscreen" })
|
vim.keymap.set("n", "<leader>nf", "<cmd>NeovideFullscreen<CR>", { desc = "Toggle Neovide Fullscreen" })
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
-- Global Functions
|
-- Global Functions
|
||||||
Config.new_scratch_buffer = function() vim.api.nvim_win_set_buf(0, vim.api.nvim_create_buf(true, true)) end
|
Config.new_scratch_buffer = function() vim.api.nvim_win_set_buf(0, vim.api.nvim_create_buf(true, true)) end
|
||||||
|
|
||||||
|
|
@ -14,7 +12,7 @@ end
|
||||||
|
|
||||||
Config.log = {}
|
Config.log = {}
|
||||||
Config.log_buf_id = Config.log_buf_id or nil
|
Config.log_buf_id = Config.log_buf_id or nil
|
||||||
Config.start_hrtime = Config.start_hrtime or vim.uv.hrtime()
|
Config.start_hrtime = Config.start_hrtime or vim.loop.hrtime()
|
||||||
|
|
||||||
Config.log_print = function()
|
Config.log_print = function()
|
||||||
if Config.log_buf_id == nil or not vim.api.nvim_buf_is_valid(Config.log_buf_id) then
|
if Config.log_buf_id == nil or not vim.api.nvim_buf_is_valid(Config.log_buf_id) then
|
||||||
|
|
@ -26,7 +24,7 @@ end
|
||||||
|
|
||||||
Config.log_clear = function()
|
Config.log_clear = function()
|
||||||
Config.log = {}
|
Config.log = {}
|
||||||
Config.start_hrtime = vim.uv.hrtime()
|
Config.start_hrtime = vim.loop.hrtime()
|
||||||
vim.cmd('echo "Cleared log"')
|
vim.cmd('echo "Cleared log"')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -34,7 +32,7 @@ end
|
||||||
Config.execute_lua_line = function()
|
Config.execute_lua_line = function()
|
||||||
local line = 'lua ' .. vim.api.nvim_get_current_line()
|
local line = 'lua ' .. vim.api.nvim_get_current_line()
|
||||||
vim.api.nvim_command(line)
|
vim.api.nvim_command(line)
|
||||||
vim.notify(line, vim.log.levels.INFO)
|
print(line)
|
||||||
vim.api.nvim_input('<Down>')
|
vim.api.nvim_input('<Down>')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
-- Helper function to normalize input to a list
|
-- Helper function to normalize input to a list
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,3 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
-- vim-slime target: use Neovim's built-in terminal.
|
|
||||||
-- Must be set before any slime send happens.
|
|
||||||
vim.g.slime_target = "neovim"
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
-- Configuration
|
-- Configuration
|
||||||
|
|
@ -18,7 +12,7 @@ local defaults = {
|
||||||
duckdb = "duckdb",
|
duckdb = "duckdb",
|
||||||
julia = "julia",
|
julia = "julia",
|
||||||
python = "ipython",
|
python = "ipython",
|
||||||
shell = "echo 'Hello " .. (vim.env.USER or "user") .. "!'",
|
shell = "echo 'Hello " .. vim.env.USER .. "!'",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Registry of terminal commands
|
-- Registry of terminal commands
|
||||||
|
|
@ -37,15 +31,11 @@ function M.split_and_open_terminal()
|
||||||
vim.cmd("resize " .. math.floor(vim.fn.winheight(0) * 0.9))
|
vim.cmd("resize " .. math.floor(vim.fn.winheight(0) * 0.9))
|
||||||
local term_buf = vim.api.nvim_win_get_buf(vim.api.nvim_get_current_win())
|
local term_buf = vim.api.nvim_win_get_buf(vim.api.nvim_get_current_win())
|
||||||
M.opt_term = term_buf
|
M.opt_term = term_buf
|
||||||
|
|
||||||
-- Set buffer-local variables for vim-slime
|
-- Set buffer-local variables for vim-slime
|
||||||
local job_id = vim.b[term_buf].terminal_job_id
|
local job_id = vim.b[term_buf].terminal_job_id
|
||||||
if not job_id then
|
|
||||||
vim.notify("Terminal job id not available", vim.log.levels.WARN)
|
|
||||||
return term_buf
|
|
||||||
end
|
|
||||||
vim.b[term_buf].slime_config = { jobid = job_id }
|
vim.b[term_buf].slime_config = { jobid = job_id }
|
||||||
|
|
||||||
return M.opt_term
|
return M.opt_term
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -54,45 +44,26 @@ function M.open_in_terminal(cmd)
|
||||||
local command = cmd or ""
|
local command = cmd or ""
|
||||||
local current_window = vim.api.nvim_get_current_win()
|
local current_window = vim.api.nvim_get_current_win()
|
||||||
local code_buf = vim.api.nvim_get_current_buf()
|
local code_buf = vim.api.nvim_get_current_buf()
|
||||||
|
|
||||||
if not vim.api.nvim_buf_is_valid(code_buf) then
|
|
||||||
vim.notify("Code buffer is not valid", vim.log.levels.ERROR)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open terminal and get buffer
|
-- Open terminal and get buffer
|
||||||
local term_buf = M.split_and_open_terminal()
|
local term_buf = M.split_and_open_terminal()
|
||||||
|
|
||||||
if not term_buf or not vim.api.nvim_buf_is_valid(term_buf) then
|
|
||||||
vim.notify("Failed to open terminal buffer", vim.log.levels.ERROR)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Send command if provided
|
-- Send command if provided
|
||||||
local job_id = vim.b[term_buf].terminal_job_id
|
|
||||||
if command ~= "" then
|
if command ~= "" then
|
||||||
if not job_id then
|
-- We can use standard slime sending if needed, or direct chan_send for initialization
|
||||||
vim.notify("Terminal job not ready, cannot send command", vim.log.levels.WARN)
|
local job_id = vim.b[term_buf].terminal_job_id
|
||||||
else
|
if job_id then
|
||||||
local ok, err = pcall(vim.api.nvim_chan_send, job_id, command .. "\r")
|
vim.api.nvim_chan_send(job_id, command .. "\r")
|
||||||
if not ok then
|
|
||||||
vim.notify("Failed to send command to terminal: " .. tostring(err), vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Configure slime for the ORIGINAL code buffer to point to this new terminal
|
-- Configure slime for the ORIGINAL code buffer to point to this new terminal
|
||||||
-- This makes "Send to Terminal" work immediately
|
-- This makes "Send to Terminal" work immediately
|
||||||
if job_id then
|
local slime_config = { jobid = vim.b[term_buf].terminal_job_id }
|
||||||
local slime_config = { jobid = job_id }
|
|
||||||
|
-- Fix: Set the variable on the captured code buffer, not the current (terminal) buffer
|
||||||
-- Fix: Set the variable on the captured code buffer, not the current (terminal) buffer
|
vim.api.nvim_buf_set_var(code_buf, "slime_config", slime_config)
|
||||||
local ok, err = pcall(vim.api.nvim_buf_set_var, code_buf, "slime_config", slime_config)
|
|
||||||
if not ok then
|
|
||||||
vim.notify("Failed to set slime_config on code buffer: " .. tostring(err), vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Switch back to code buffer
|
-- Switch back to code buffer
|
||||||
vim.api.nvim_set_current_win(current_window)
|
vim.api.nvim_set_current_win(current_window)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
-- Default parsers list moved from startup config
|
-- Default parsers list moved from startup config
|
||||||
|
|
@ -95,12 +93,12 @@ end
|
||||||
function M.get_type()
|
function M.get_type()
|
||||||
local cur_node = smart_send.get_current_node()
|
local cur_node = smart_send.get_current_node()
|
||||||
if not cur_node then
|
if not cur_node then
|
||||||
vim.notify("No Tree-sitter node under cursor", vim.log.levels.WARN)
|
print("Not a node")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local node_type = cur_node:type()
|
local node_type = cur_node:type()
|
||||||
vim.notify("Node type: " .. node_type, vim.log.levels.INFO)
|
print("Node type: " .. node_type)
|
||||||
return node_type
|
return node_type
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -121,17 +119,11 @@ function M.setup_keybindings(global_nodes)
|
||||||
vim.keymap.set('n', '<localleader>a', function() smart_send.send_repl(current_global_nodes) end,
|
vim.keymap.set('n', '<localleader>a', function() smart_send.send_repl(current_global_nodes) end,
|
||||||
{ noremap = true, silent = true, desc = "Send node to REPL", buffer = true })
|
{ noremap = true, silent = true, desc = "Send node to REPL", buffer = true })
|
||||||
|
|
||||||
-- Both `<CR>` and `<S-CR>` were removed from `M.setup_keybindings`. They
|
vim.keymap.set({ 'n', 'i' }, '<S-CR>', function() smart_send.send_repl(current_global_nodes) end,
|
||||||
-- were hard, implicit overrides that clobbered Vim/filetype defaults and
|
{ noremap = true, silent = true, desc = "Send node to REPL", buffer = true })
|
||||||
-- the user's snippet + insert-mode workflows (see C2 + H5 in the PR
|
|
||||||
-- review). To opt back in for a specific filetype, override per-buffer
|
vim.keymap.set('n', '<CR>', function() smart_send.send_repl(current_global_nodes) end,
|
||||||
-- from a `ftplugin/<lang>.lua`:
|
{ noremap = true, silent = true, desc = "Send node to REPL", buffer = true })
|
||||||
--
|
|
||||||
-- -- e.g. ftplugin/r.lua or ftplugin/quarto.lua
|
|
||||||
-- vim.keymap.set('n', '<CR>', function()
|
|
||||||
-- require('config').treesitter_helpers.setup_keybindings(global_nodes)
|
|
||||||
-- require('nix_smart_send').send_repl(global_nodes)
|
|
||||||
-- end, { buffer = true, desc = 'Send node to REPL' })
|
|
||||||
|
|
||||||
vim.keymap.set('n', '<localleader>n',
|
vim.keymap.set('n', '<localleader>n',
|
||||||
function() current_global_nodes = M.add_global_node(current_global_nodes) end,
|
function() current_global_nodes = M.add_global_node(current_global_nodes) end,
|
||||||
|
|
@ -142,8 +134,8 @@ function M.setup_keybindings(global_nodes)
|
||||||
{ noremap = true, silent = true, desc = "Remove node under cursor from globals", buffer = true })
|
{ noremap = true, silent = true, desc = "Remove node under cursor from globals", buffer = true })
|
||||||
|
|
||||||
vim.keymap.set('n', '<localleader>o', function()
|
vim.keymap.set('n', '<localleader>o', function()
|
||||||
local pout = table.concat(global_nodes, ', ')
|
pout = table.concat(global_nodes, ', ') .. ""
|
||||||
vim.notify("global_nodes: " .. pout, vim.log.levels.INFO)
|
print(pout)
|
||||||
end, { noremap = true, silent = true, desc = "Print globals", buffer = true })
|
end, { noremap = true, silent = true, desc = "Print globals", buffer = true })
|
||||||
|
|
||||||
vim.keymap.set('n', '<localleader>p', function() M.get_type() end,
|
vim.keymap.set('n', '<localleader>p', function() M.get_type() end,
|
||||||
|
|
@ -152,46 +144,4 @@ end
|
||||||
|
|
||||||
Config.treesitter_helpers = M
|
Config.treesitter_helpers = M
|
||||||
|
|
||||||
-- Tree-sitter text objects: functions, calls, and assignments are especially
|
|
||||||
-- useful when editing R/tidyverse pipelines (e.g. `df |> mutate(...)`).
|
|
||||||
local ts_ok, treesitter = pcall(require, "nvim-treesitter.configs")
|
|
||||||
if ts_ok then
|
|
||||||
treesitter.setup({
|
|
||||||
textobjects = {
|
|
||||||
select = {
|
|
||||||
enable = true,
|
|
||||||
lookahead = true,
|
|
||||||
keymaps = {
|
|
||||||
["af"] = "@function.outer",
|
|
||||||
["if"] = "@function.inner",
|
|
||||||
["ac"] = "@call.outer",
|
|
||||||
["ic"] = "@call.inner",
|
|
||||||
["aa"] = "@assignment.outer",
|
|
||||||
["ia"] = "@assignment.inner",
|
|
||||||
},
|
|
||||||
selection_modes = {
|
|
||||||
["@function.outer"] = "V",
|
|
||||||
["@function.inner"] = "V",
|
|
||||||
["@call.outer"] = "v",
|
|
||||||
["@call.inner"] = "v",
|
|
||||||
["@assignment.outer"] = "v",
|
|
||||||
["@assignment.inner"] = "v",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
move = {
|
|
||||||
enable = true,
|
|
||||||
set_jumps = true,
|
|
||||||
goto_next_start = {
|
|
||||||
["]f"] = "@function.outer",
|
|
||||||
["]c"] = "@call.outer",
|
|
||||||
},
|
|
||||||
goto_previous_start = {
|
|
||||||
["[f"] = "@function.outer",
|
|
||||||
["[c"] = "@call.outer",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,318 @@
|
||||||
local Config = require('config')
|
-- Basic mappings =============================================================
|
||||||
|
-- NOTE: Most basic mappings come from 'mini.basics'
|
||||||
|
-- Shorter version of the most frequent way of going outside of terminal window
|
||||||
|
vim.keymap.set('t', '<C-h>', [[<C-\><C-N><C-w>h]])
|
||||||
|
-- Select all
|
||||||
|
-- vim.keymap.set({ "n", "v", "x" }, "<C-a>", "gg3vG$", { noremap = true, silent = true, desc = "Select all" })
|
||||||
|
-- Escape deletes highlights
|
||||||
|
vim.keymap.set("n", "<Esc>", "<cmd>nohlsearch<CR>")
|
||||||
|
-- Paste before/after linewise
|
||||||
|
local cmd = vim.fn.has('nvim-0.12') == 1 and 'iput' or 'put'
|
||||||
|
vim.keymap.set({ 'n', 'x' }, '[p', '<Cmd>exe "' .. cmd .. '! " . v:register<CR>', { desc = 'Paste Above' })
|
||||||
|
vim.keymap.set({ 'n', 'x' }, ']p', '<Cmd>exe "' .. cmd .. ' " . v:register<CR>', { desc = 'Paste Below' })
|
||||||
|
|
||||||
-- Domain-specific keymap modules. Core must load first because it defines the
|
vim.keymap.set({ "n", "v", "x" }, "<leader>p", '"+p', { noremap = true, silent = true, desc = "Paste from clipboard" })
|
||||||
-- leader clue groups used by the mini.clue setup in the startup plugins.
|
vim.keymap.set({ "n", "v", "x" }, "<leader>y", '"+y', { noremap = true, silent = true, desc = "Copy toclipboard" })
|
||||||
require('keymap.core')
|
-- Leader mappings ============================================================
|
||||||
require('keymap.leader')
|
-- stylua: ignore start
|
||||||
require('keymap.terminal')
|
|
||||||
|
|
||||||
-- `_G.Config` is set by `init.lua:2` once at start-up; no need to re-export
|
-- Create global tables with information about clue groups in certain modes
|
||||||
-- here. The single source of truth is `init.lua` so future refactors don't
|
-- Structure of tables is taken to be compatible with 'mini.clue'.
|
||||||
-- have to chase which file currently publishes the alias.
|
_G.Config.leader_group_clues = {
|
||||||
|
{ mode = 'n', keys = '<Leader>a', desc = '+AI' },
|
||||||
|
{ mode = 'n', keys = '<Leader>b', desc = '+Buffer' },
|
||||||
|
{ mode = 'n', keys = '<Leader>e', desc = '+Explore' },
|
||||||
|
{ mode = 'n', keys = '<Leader>f', desc = '+Find' },
|
||||||
|
{ mode = 'n', keys = '<Leader>fl', desc = '+LSP' },
|
||||||
|
{ mode = 'n', keys = '<Leader>fa', desc = '+Git' },
|
||||||
|
{ mode = 'n', keys = '<Leader>g', desc = '+Git' },
|
||||||
|
{ mode = 'n', keys = '<Leader>l', desc = '+LSP' },
|
||||||
|
{ mode = 'n', keys = '<Leader>L', desc = '+Lua/Log' },
|
||||||
|
{ mode = 'n', keys = '<Leader>o', desc = '+Other' },
|
||||||
|
{ mode = 'n', keys = '<Leader>r', desc = '+R' },
|
||||||
|
{ mode = 'n', keys = '<Leader>t', desc = '+Terminal' },
|
||||||
|
{ mode = 'n', keys = '<Leader>u', desc = '+UI' },
|
||||||
|
{ mode = 'n', keys = '<Leader>v', desc = '+Visits' },
|
||||||
|
{ mode = 'n', keys = '<Leader>w', desc = '+Windows' },
|
||||||
|
{ mode = 'x', keys = '<Leader>l', desc = '+LSP' },
|
||||||
|
{ mode = 'x', keys = '<Leader>r', desc = '+R' },
|
||||||
|
{ mode = 'n', keys = '<Leader>z', desc = '+ZK' },
|
||||||
|
{ mode = 'n', keys = '<Leader>zr', desc = '+Reviews' },
|
||||||
|
{ mode = 'x', keys = '<leader>a', desc = '+AI' },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Create `<Leader>` mappings
|
||||||
|
local nmap_leader = function(suffix, rhs, desc, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
opts.desc = desc
|
||||||
|
vim.keymap.set('n', '<Leader>' .. suffix, rhs, opts)
|
||||||
|
end
|
||||||
|
local xmap_leader = function(suffix, rhs, desc, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
opts.desc = desc
|
||||||
|
vim.keymap.set('x', '<Leader>' .. suffix, rhs, opts)
|
||||||
|
end
|
||||||
|
-- Other mappings
|
||||||
|
local nmap_lsp = function(keys, func, desc)
|
||||||
|
if desc then
|
||||||
|
desc = desc .. "(LSP)"
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.keymap.set("n", keys, func, { desc = desc })
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Switch buffers
|
||||||
|
nmap_leader('<Tab>', '<Cmd>bnext<CR>', 'Next buffer')
|
||||||
|
nmap_leader('<S-Tab>', '<Cmd>bprev<CR>', 'Prev buffer')
|
||||||
|
|
||||||
|
-- a is for 'AI'
|
||||||
|
nmap_leader("ac", "<cmd>CodeCompanionChat Toggle<CR>", "Chat Toggle")
|
||||||
|
-- nmap_leader("ae", "<cmd>CodeCompanion /explain<CR>", "Explain Code")
|
||||||
|
-- nmap_leader("af", "<cmd>CodeCompanion /fix<CR>", "Fix Code")
|
||||||
|
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("ax", "<cmd>CodeCompanion /fixer<CR>", "Code Fixer")
|
||||||
|
nmap_leader("ax", "<cmd>CodeCompanion /fixer<CR>", "Code Fixer")
|
||||||
|
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 Fixer")
|
||||||
|
xmap_leader("ap", "<cmd>CodeCompanion /expert<CR>", "Code Fixer")
|
||||||
|
xmap_leader("as", "<cmd>CodeCompanion /suggest<CR>", "Suggest Improvements")
|
||||||
|
|
||||||
|
-- b is for 'buffer'
|
||||||
|
nmap_leader('bb', '<Cmd>b#<CR>', 'Alternate')
|
||||||
|
nmap_leader('bd', '<Cmd>lua MiniBufremove.delete()<CR>', 'Delete')
|
||||||
|
nmap_leader('bD', '<Cmd>lua MiniBufremove.delete(0, true)<CR>', 'Delete!')
|
||||||
|
nmap_leader('bs', '<Cmd>lua Config.new_scratch_buffer()<CR>', 'Scratch')
|
||||||
|
nmap_leader('bw', '<Cmd>lua MiniBufremove.wipeout()<CR>', 'Wipeout')
|
||||||
|
nmap_leader('bW', '<Cmd>lua MiniBufremove.wipeout(0, true)<CR>', 'Wipeout!')
|
||||||
|
nmap_leader('bq', '<Cmd>qall<CR>', 'Quit all')
|
||||||
|
|
||||||
|
-- e is for 'explore' and 'edit'
|
||||||
|
nmap_leader('ed', '<Cmd>lua MiniFiles.open()<CR>', 'Directory')
|
||||||
|
nmap_leader('ef', '<Cmd>lua Config.try_opendir()<CR>', 'File directory')
|
||||||
|
nmap_leader('es', '<Cmd>lua MiniSessions.select()<CR>', 'Sessions')
|
||||||
|
nmap_leader('eq', '<Cmd>lua Config.toggle_quickfix()<CR>', 'Quickfix')
|
||||||
|
nmap_leader('ez', '<Cmd>lua MiniFiles.open(os.getenv("ZK_NOTEBOOK_DIR"))<CR>', 'Notes directory')
|
||||||
|
|
||||||
|
-- f is for 'fuzzy find'
|
||||||
|
nmap_leader('f/', '<Cmd>Pick history scope="/"<CR>', '"/" history')
|
||||||
|
nmap_leader('f:', '<Cmd>Pick history scope=":"<CR>', '":" history')
|
||||||
|
nmap_leader('f,', '<Cmd>Pick visit_labels<CR>', 'Visit labels')
|
||||||
|
nmap_leader('faa', '<Cmd>Pick git_hunks scope="staged"<CR>', 'Added hunks (all)')
|
||||||
|
nmap_leader('faA', '<Cmd>Pick git_hunks path="%" scope="staged"<CR>', 'Added hunks (current)')
|
||||||
|
nmap_leader('fb', '<Cmd>Pick buffers<CR>', 'Buffers')
|
||||||
|
nmap_leader(',', '<Cmd>Pick buffers<CR>', 'Buffers')
|
||||||
|
nmap_leader('fac', '<Cmd>Pick git_commits<CR>', 'Commits (all)')
|
||||||
|
nmap_leader('faC', '<Cmd>Pick git_commits path="%"<CR>', 'Commits (current)')
|
||||||
|
nmap_leader('fd', '<Cmd>Pick diagnostic scope="all"<CR>', 'Diagnostic workspace')
|
||||||
|
nmap_leader('fD', '<Cmd>Pick diagnostic scope="current"<CR>', 'Diagnostic buffer')
|
||||||
|
nmap_leader('ff', '<Cmd>Pick files<CR>', 'Files')
|
||||||
|
nmap_leader('fg', '<Cmd>Pick grep_live<CR>', 'Grep live')
|
||||||
|
nmap_leader('fG', '<Cmd>Pick grep pattern="<cword>"<CR>', 'Grep current word')
|
||||||
|
nmap_leader('fh', '<Cmd>Pick help<CR>', 'Help tags')
|
||||||
|
nmap_leader('fH', '<Cmd>Pick hl_groups<CR>', 'Highlight groups')
|
||||||
|
nmap_leader('fj', '<Cmd>Pick buf_lines scope="all"<CR>', 'Lines (all)')
|
||||||
|
nmap_leader('fJ', '<Cmd>Pick buf_lines scope="current"<CR>', 'Lines (current)')
|
||||||
|
nmap_leader('fam', '<Cmd>Pick git_hunks<CR>', 'Modified hunks (all)')
|
||||||
|
nmap_leader('faM', '<Cmd>Pick git_hunks path="%"<CR>', 'Modified hunks (current)')
|
||||||
|
nmap_leader('fm', '<Cmd>Pick marks<CR>', 'Marks')
|
||||||
|
nmap_leader('fn', '<cmd>ZkNotes<CR>', "Notes")
|
||||||
|
nmap_leader('fk', '<Cmd>Pick keymaps<CR>', 'Keymaps')
|
||||||
|
nmap_leader('fR', '<Cmd>Pick resume<CR>', 'Resume')
|
||||||
|
nmap_leader('fp', '<Cmd>Pick projects<CR>', 'Projects')
|
||||||
|
nmap_leader('fq', '<Cmd>Pick list scope="quickfix"<CR>', 'Quickfix')
|
||||||
|
nmap_leader('fr', '<Cmd>Pick lsp scope="references"<CR>', 'References (LSP)')
|
||||||
|
nmap_leader('flr', '<Cmd>Pick lsp scope="references"<CR>', 'References (LSP)')
|
||||||
|
nmap_leader('fS', '<Cmd>Pick lsp scope="workspace_symbol"<CR>', 'Symbols workspace (LSP)')
|
||||||
|
nmap_leader('flS', '<Cmd>Pick lsp scope="workspace_symbol"<CR>', 'Symbols workspace (LSP)')
|
||||||
|
nmap_leader('fs', '<Cmd>Pick lsp scope="document_symbol"<CR>', 'Symbols buffer (LSP)')
|
||||||
|
nmap_leader('fls', '<Cmd>Pick lsp scope="document_symbol"<CR>', 'Symbols buffer (LSP)')
|
||||||
|
nmap_leader('fld', '<Cmd>Pick lsp scope="definition"<CR>', 'Definition (LSP)')
|
||||||
|
nmap_leader('flD', '<Cmd>Pick lsp scope="declaration"<CR>', 'Declaration (LSP)')
|
||||||
|
nmap_leader('flt', '<Cmd>Pick lsp scope="type_definition"<CR>', 'Type Definition (LSP)')
|
||||||
|
nmap_leader('fv', '<Cmd>Pick visit_paths cwd=""<CR>', 'Visit paths (all)')
|
||||||
|
nmap_leader('fV', '<Cmd>Pick visit_paths<CR>', 'Visit paths (cwd)')
|
||||||
|
|
||||||
|
-- g is for git
|
||||||
|
local git_log_cmd = [[Git log --pretty=format:\%h\ \%as\ │\ \%s --topo-order]]
|
||||||
|
|
||||||
|
nmap_leader('gc', '<Cmd>Git commit<CR>', 'Commit')
|
||||||
|
nmap_leader('gC', '<Cmd>Git commit --amend<CR>', 'Commit amend')
|
||||||
|
nmap_leader('gd', '<Cmd>Git diff<CR>', 'Diff')
|
||||||
|
nmap_leader('gD', '<Cmd>Git diff -- %<CR>', 'Diff buffer')
|
||||||
|
nmap_leader('gg', '<Cmd>lua require("neogit").open()<CR>', 'Git tab')
|
||||||
|
nmap_leader('gl', '<Cmd>' .. git_log_cmd .. '<CR>', 'Log')
|
||||||
|
nmap_leader('gL', '<Cmd>' .. git_log_cmd .. ' --follow -- %<CR>', 'Log buffer')
|
||||||
|
nmap_leader('go', '<Cmd>lua MiniDiff.toggle_overlay()<CR>', 'Toggle overlay')
|
||||||
|
nmap_leader('gp', '<Cmd>Git pull<CR>', 'Pull')
|
||||||
|
nmap_leader('gP', '<Cmd>Git push<CR>', 'Push')
|
||||||
|
nmap_leader('gs', '<Cmd>lua MiniGit.show_at_cursor()<CR>', 'Show at cursor')
|
||||||
|
|
||||||
|
xmap_leader('gs', '<Cmd>lua MiniGit.show_at_cursor()<CR>', 'Show at selection')
|
||||||
|
|
||||||
|
-- j/k navigate quickfix
|
||||||
|
nmap_leader("j", '<cmd>cnext<CR>zz', "Quickfix next")
|
||||||
|
nmap_leader("k", '<cmd>cprev<CR>zz', "Quickfix prev")
|
||||||
|
|
||||||
|
-- l is for 'LSP' (Language Server Protocol)
|
||||||
|
vim.keymap.set({ 'n' }, 'grd', '<Cmd>lua vim.lsp.buf.definition()<CR>', { desc = 'Definition' })
|
||||||
|
vim.keymap.set({ 'n' }, 'grk', '<Cmd>lua vim.lsp.buf.hover()<CR>', { desc = 'Documentation' })
|
||||||
|
vim.keymap.set({ 'n' }, 'gre', '<Cmd>lua vim.diagnostic.open_float()<CR>', { desc = 'Diagnostics' })
|
||||||
|
|
||||||
|
nmap_lsp("K", '<Cmd>lua vim.lsp.buf.hover()<CR>', "Documentation")
|
||||||
|
local formatting_cmd = '<Cmd>lua require("conform").format({ lsp_fallback = true })<CR>'
|
||||||
|
nmap_leader('la', '<Cmd>lua vim.lsp.buf.code_action()<CR>', 'Actions')
|
||||||
|
nmap_leader('le', '<Cmd>lua vim.diagnostic.open_float()<CR>', 'Diagnostics popup')
|
||||||
|
nmap_leader('lf', formatting_cmd, 'Format')
|
||||||
|
nmap_leader('lk', '<Cmd>lua vim.lsp.buf.hover()<CR>', 'Documentation')
|
||||||
|
nmap_leader('li', '<Cmd>lua vim.lsp.buf.implementation()<CR>', 'Information')
|
||||||
|
-- use ]d and [d
|
||||||
|
--nmap_leader('lj', '<Cmd>lua vim.diagnostic.goto_next()<CR>', 'Next diagnostic')
|
||||||
|
--nmap_leader('lk', '<Cmd>lua vim.diagnostic.goto_prev()<CR>', 'Prev diagnostic')
|
||||||
|
nmap_leader('lR', '<Cmd>lua vim.lsp.buf.references()<CR>', 'References')
|
||||||
|
nmap_leader('lr', '<Cmd>lua vim.lsp.buf.rename()<CR>', 'Rename')
|
||||||
|
nmap_leader('ls', '<Cmd>lua vim.lsp.buf.definition()<CR>', 'Source definition')
|
||||||
|
|
||||||
|
xmap_leader('lf', formatting_cmd, 'Format selection')
|
||||||
|
|
||||||
|
-- L is for 'Lua'
|
||||||
|
nmap_leader('Lc', '<Cmd>lua Config.log_clear()<CR>', 'Clear log')
|
||||||
|
nmap_leader('LL', '<Cmd>luafile %<CR><Cmd>echo "Sourced lua"<CR>', 'Source buffer')
|
||||||
|
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 free
|
||||||
|
|
||||||
|
-- o is for 'other'
|
||||||
|
local trailspace_toggle_command = '<Cmd>lua vim.b.minitrailspace_disable = not vim.b.minitrailspace_disable<CR>'
|
||||||
|
nmap_leader('od', '<Cmd>Neogen<CR>', 'Document')
|
||||||
|
nmap_leader('oh', '<Cmd>normal gxiagxila<CR>', 'Move arg left')
|
||||||
|
nmap_leader('ol', '<Cmd>normal gxiagxina<CR>', 'Move arg right')
|
||||||
|
nmap_leader('or', '<Cmd>lua MiniMisc.resize_window()<CR>', 'Resize to default width')
|
||||||
|
nmap_leader('oS', '<Cmd>lua Config.insert_section()<CR>', 'Section insert')
|
||||||
|
nmap_leader('ot', '<Cmd>lua MiniTrailspace.trim()<CR>', 'Trim trailspace')
|
||||||
|
nmap_leader('oT', trailspace_toggle_command, 'Trailspace hl toggle')
|
||||||
|
nmap_leader('oz', '<Cmd>lua MiniMisc.zoom()<CR>', 'Zoom toggle')
|
||||||
|
nmap_leader('ow',
|
||||||
|
"<Cmd>lua MiniSessions.write(vim.fn.input('Session name: ', string.match(vim.fn.getcwd(), \"[^/]+$\") .. '-session.vim'))<CR>",
|
||||||
|
'Write session')
|
||||||
|
|
||||||
|
-- r is for 'R'
|
||||||
|
nmap_leader('rc', '<Cmd>RSend devtools::check()<CR>', 'Check')
|
||||||
|
nmap_leader('rC', '<Cmd>RSend devtools::test_coverage()<CR>', 'Coverage')
|
||||||
|
nmap_leader('rd', '<Cmd>RSend devtools::document()<CR>', 'Document')
|
||||||
|
nmap_leader('ri', '<Cmd>RSend devtools::install(keep_source=TRUE)<CR>', 'Install')
|
||||||
|
nmap_leader('rk', '<Cmd>RSend quarto::quarto_preview("%")<CR>', 'Knit file')
|
||||||
|
nmap_leader('rl', '<Cmd>RSend devtools::load_all()<CR>', 'Load all')
|
||||||
|
nmap_leader('rL', '<Cmd>RSend devtools::load_all(recompile=TRUE)<CR>', 'Load all recompile')
|
||||||
|
nmap_leader('rm', '<Cmd>RSend Rcpp::compileAttributes()<CR>', 'Run examples')
|
||||||
|
nmap_leader('rT', '<Cmd>RSend testthat::test_file("%")<CR>', 'Test file')
|
||||||
|
nmap_leader('rt', '<Cmd>RSend devtools::test()<CR>', 'Test')
|
||||||
|
|
||||||
|
-- - Copy to clipboard and make reprex (which itself is loaded to clipboard)
|
||||||
|
xmap_leader('rx', '"+y :RSend reprex::reprex()<CR>', 'Reprex selection')
|
||||||
|
|
||||||
|
-- s is for 'send' (Send text to neoterm buffer)
|
||||||
|
nmap_leader('s', '<Cmd>SlimeSendCurrentLine<CR>j', 'Send to terminal')
|
||||||
|
|
||||||
|
-- - In simple visual mode send text and move to the last character in
|
||||||
|
-- selection and move to the right. Otherwise (like in line or block visual
|
||||||
|
-- mode) send text and move one line down from bottom of selection.
|
||||||
|
xmap_leader('s', '<Plug>SlimeRegionSend<CR>', 'Send to terminal')
|
||||||
|
|
||||||
|
-- t is for 'terminal'
|
||||||
|
vim.keymap.set("t", "<Esc>", [[<C-\><C-n>]], { desc = "Exit terminal mode" })
|
||||||
|
vim.keymap.set("n", "<leader>tc", '<Cmd>lua Config.terminal.open_clickhouse_client()<CR>',
|
||||||
|
{ desc = "Open Clickhouse client" })
|
||||||
|
vim.keymap.set("n", "<leader>tl", '<Cmd>lua Config.terminal.open_clickhouse_local()<CR>',
|
||||||
|
{ desc = "Open Clickhouse local" })
|
||||||
|
vim.keymap.set("n", "<leader>tp", '<Cmd>lua Config.terminal.open_python()<CR>', { desc = "Open Python" })
|
||||||
|
vim.keymap.set("n", "<leader>tj", '<Cmd>lua Config.terminal.open_julia()<CR>', { desc = "Open Julia" })
|
||||||
|
vim.keymap.set("n", "<leader>td", '<Cmd>lua Config.terminal.open_duckdb();Config.terminal.toggle_bracket()<CR>',
|
||||||
|
{ desc = "Open DuckDB" })
|
||||||
|
vim.keymap.set("n", "<leader>tx", '<Cmd>lua Config.terminal.open_in_terminal()<CR>', { desc = "Terminal Command" })
|
||||||
|
vim.keymap.set("n", "<leader>tt", '<Cmd>lua Config.terminal.open_shell()<CR>', { desc = "Terminal" })
|
||||||
|
nmap_leader("tb", '<Cmd>lua Config.terminal.toggle_bracket()<CR>', "Toggle bracketed paste")
|
||||||
|
nmap_leader("up", '<Cmd>lua Config.terminal.toggle_bracket()<CR>', "Toggle bracketed paste")
|
||||||
|
|
||||||
|
-- u is for UI
|
||||||
|
nmap_leader('ut', '<Cmd>TSContext toggle<CR>', 'Toggle TScontext')
|
||||||
|
nmap_leader('ua', '<Cmd>Copilot toggle<CR>', 'Toggle AI completion')
|
||||||
|
|
||||||
|
-- v is for 'visits'
|
||||||
|
nmap_leader('vv', '<Cmd>lua MiniVisits.add_label("core")<CR>', 'Add "core" label')
|
||||||
|
nmap_leader('vV', '<Cmd>lua MiniVisits.remove_label("core")<CR>', 'Remove "core" label')
|
||||||
|
nmap_leader('vl', '<Cmd>lua MiniVisits.add_label()<CR>', 'Add label')
|
||||||
|
nmap_leader('vL', '<Cmd>lua MiniVisits.remove_label()<CR>', 'Remove label')
|
||||||
|
|
||||||
|
local map_pick_core = function(keys, cwd, desc)
|
||||||
|
local rhs = function()
|
||||||
|
local sort_latest = MiniVisits.gen_sort.default({ recency_weight = 1 })
|
||||||
|
MiniExtra.pickers.visit_paths({
|
||||||
|
cwd = cwd,
|
||||||
|
filter = 'core',
|
||||||
|
sort = sort_latest
|
||||||
|
}, { source = { name = desc } })
|
||||||
|
end
|
||||||
|
nmap_leader(keys, rhs, desc)
|
||||||
|
end
|
||||||
|
map_pick_core('vc', '', 'Core visits (all)')
|
||||||
|
map_pick_core('vC', nil, 'Core visits (cwd)')
|
||||||
|
|
||||||
|
-- w is for 'windows'
|
||||||
|
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("_", "<C-W>s", "Split Window Below", { remap = true })
|
||||||
|
nmap_leader("|", "<C-W>v", "Split Window Right", { remap = true })
|
||||||
|
nmap_leader("wd", "<C-W>c", "Delete Window", { remap = true })
|
||||||
|
nmap_leader("wo", "<C-W>o", "Delete Other Windows", { remap = true })
|
||||||
|
|
||||||
|
-- z is for 'ZettelKasten'
|
||||||
|
nmap_leader("zo", '<Cmd>ZkNotes<CR>', "Notes")
|
||||||
|
nmap_leader("zt", '<Cmd>ZkTags<cr>', "Tags")
|
||||||
|
|
||||||
|
nmap_leader(
|
||||||
|
"zrd",
|
||||||
|
'<Cmd>ZkNew { group = "dreviews" }<CR>',
|
||||||
|
"Daily Review"
|
||||||
|
)
|
||||||
|
nmap_leader(
|
||||||
|
"zrw",
|
||||||
|
'<Cmd>ZkNew { group = "wreviews" }<CR>',
|
||||||
|
"Weekly Review"
|
||||||
|
)
|
||||||
|
nmap_leader(
|
||||||
|
"zn",
|
||||||
|
'<Cmd>ZkNew { group = "inbox", title = vim.fn.input("Title: ") }<CR>',
|
||||||
|
"New"
|
||||||
|
)
|
||||||
|
nmap_leader(
|
||||||
|
"zp",
|
||||||
|
"<Cmd>ZkNew { group = 'permanent', title = vim.fn.input('Title: ') }<CR>",
|
||||||
|
"Permanent"
|
||||||
|
)
|
||||||
|
|
||||||
|
nmap_leader(
|
||||||
|
"zl",
|
||||||
|
"<Cmd>ZkNew { group = 'literature', title = vim.fn.input('Title: '), extra.author = vim.fn.input('Author: '), extra.year = vim.fn.input('Year: ') }<CR>",
|
||||||
|
"Literature"
|
||||||
|
)
|
||||||
|
|
||||||
|
nmap_leader(
|
||||||
|
"zd",
|
||||||
|
"<Cmd>ZkNew { group = 'dashboard', title = vim.fn.input('Title: ') }<CR>",
|
||||||
|
"Dashboard"
|
||||||
|
)
|
||||||
|
nmap_leader(
|
||||||
|
"zP",
|
||||||
|
"<Cmd>ZkNew { group = 'project', title = vim.fn.input('Title: ')}<CR>",
|
||||||
|
"Project"
|
||||||
|
)
|
||||||
|
-- stylua: ignore end
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
local Config = require('config')
|
|
||||||
local now = MiniDeps.now
|
local now = MiniDeps.now
|
||||||
local later = MiniDeps.later
|
local later = MiniDeps.later
|
||||||
local now_if_args = Config.now_if_args
|
local now_if_args = Config.now_if_args
|
||||||
|
|
@ -191,7 +190,7 @@ end)
|
||||||
-- Treesitter
|
-- Treesitter
|
||||||
|
|
||||||
now_if_args(function()
|
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 {
|
require 'treesitter-context'.setup {
|
||||||
enable = true,
|
enable = true,
|
||||||
|
|
@ -209,37 +208,112 @@ now_if_args(function()
|
||||||
on_attach = nil, -- (fun(buf: integer): boolean) return false to disable attaching
|
on_attach = nil, -- (fun(buf: integer): boolean) return false to disable attaching
|
||||||
}
|
}
|
||||||
|
|
||||||
local ts_filetypes = {
|
|
||||||
"c", "cpp", "lua", "nix", "python", "r", "markdown", "query",
|
local ok_configs, configs = pcall(require, "nvim-treesitter.configs")
|
||||||
"vim", "vimdoc", "yaml", "json", "toml", "rust", "go",
|
|
||||||
"javascript", "typescript", "tsx", "html", "css", "sql",
|
if ok_configs and configs.setup then
|
||||||
"julia", "rnoweb", "latex", "gitcommit", "gitignore",
|
local opts = {
|
||||||
"git_config", "git_rebase", "diff", "dockerfile",
|
highlight = { enable = true },
|
||||||
"make", "xml", "zig", "regex", "csv", "bash",
|
indent = { enable = false },
|
||||||
"markdown_inline", "quarto", "rmd",
|
textobjects = {
|
||||||
}
|
move = {
|
||||||
local function start_treesitter(buf, filetype)
|
enable = true,
|
||||||
local lang = vim.treesitter.language.get_lang(filetype) or filetype
|
set_jumps = true,
|
||||||
if #vim.api.nvim_get_runtime_file("parser/" .. lang .. ".*", false) == 0 then
|
goto_next_start = {
|
||||||
return
|
["]a"] = "@parameter.inner", -- fixed typo
|
||||||
|
["]f"] = "@function.outer",
|
||||||
|
["]o"] = "@loop.*",
|
||||||
|
["]s"] = { query = "@local.scope", desc = "Next scope" },
|
||||||
|
["]z"] = { query = "@fold", desc = "Next fold" },
|
||||||
|
},
|
||||||
|
goto_next_end = {
|
||||||
|
["]M"] = "@function.outer",
|
||||||
|
["]["] = "@class.outer",
|
||||||
|
},
|
||||||
|
goto_previous_start = {
|
||||||
|
["[a"] = "@parameter.inner",
|
||||||
|
["[f"] = "@function.outer",
|
||||||
|
["[o"] = "@loop.*",
|
||||||
|
["[s"] = { query = "@local.scope", query_group = "locals", desc = "Prev. scope" },
|
||||||
|
["[z"] = { query = "@fold", query_group = "folds", desc = "Prev. fold" },
|
||||||
|
},
|
||||||
|
goto_previous_end = {
|
||||||
|
["[M"] = "@function.outer",
|
||||||
|
["[]"] = "@class.outer",
|
||||||
|
},
|
||||||
|
goto_next = {
|
||||||
|
["]e"] = "@conditional.outer",
|
||||||
|
},
|
||||||
|
goto_previous = {
|
||||||
|
["[e"] = "@conditional.outer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
swap = {
|
||||||
|
enable = true,
|
||||||
|
swap_next = {
|
||||||
|
["<leader>x"] = "@parameter.inner",
|
||||||
|
},
|
||||||
|
swap_previous = {
|
||||||
|
["<leader>X"] = "@parameter.inner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lsp_interop = {
|
||||||
|
enable = true,
|
||||||
|
border = "none",
|
||||||
|
floating_preview_opts = {},
|
||||||
|
peek_definition_code = {
|
||||||
|
["<leader>lm"] = "@function.outer",
|
||||||
|
["<leader>lM"] = "@class.outer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-- Manual parser check for non-Nix users
|
||||||
|
if not Config.isNixCats then
|
||||||
|
local installed_check = function(lang)
|
||||||
|
return #vim.api.nvim_get_runtime_file("parser/" .. lang .. ".*", false) == 0
|
||||||
|
end
|
||||||
|
local to_install = vim.tbl_filter(installed_check, opts.ensure_installed)
|
||||||
|
if #to_install > 0 then
|
||||||
|
require("nvim-treesitter").install(to_install)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
vim.treesitter.start(buf, lang)
|
-- Environment-specific Overrides
|
||||||
vim.bo[buf].indentexpr = "v:lua.require('nvim-treesitter').indentexpr()"
|
if not Config.isNixCats then
|
||||||
|
opts.auto_install = true
|
||||||
|
opts.ensure_installed = Config.treesitter_helpers.default_parsers
|
||||||
|
else
|
||||||
|
opts.auto_install = false
|
||||||
|
-- Nix handles installation, so ensure_installed is skipped/empty
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
configs.setup(opts)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
pattern = ts_filetypes,
|
pattern = "*",
|
||||||
callback = function(ev)
|
callback = function(args)
|
||||||
start_treesitter(ev.buf, vim.bo[ev.buf].filetype)
|
-- Use explicit buffer + filetype to avoid any ambiguity
|
||||||
|
local ok = pcall(vim.treesitter.start, args.buf, args.match)
|
||||||
|
vim.bo.syntax = 'on'
|
||||||
|
vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
|
||||||
|
vim.wo[0][0].foldexpr = 'v:lua.vim.treesitter.foldexpr()'
|
||||||
|
vim.wo[0][0].foldmethod = 'expr'
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- FileType may have fired before this deferred setup ran.
|
-- Textobjects: require plugin and bail out quietly if missing
|
||||||
if vim.bo.filetype ~= "" then
|
local ok_nto, nto = pcall(require, "nvim-treesitter-textobjects")
|
||||||
start_treesitter(0, vim.bo.filetype)
|
if not ok_nto then
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
require("nvim-treesitter-textobjects").setup({
|
vim.g.no_plugin_maps = true
|
||||||
|
nto.setup({
|
||||||
move = {
|
move = {
|
||||||
set_jumps = true,
|
set_jumps = true,
|
||||||
},
|
},
|
||||||
|
|
@ -248,59 +322,91 @@ now_if_args(function()
|
||||||
local move = require("nvim-treesitter-textobjects.move")
|
local move = require("nvim-treesitter-textobjects.move")
|
||||||
local swap = require("nvim-treesitter-textobjects.swap")
|
local swap = require("nvim-treesitter-textobjects.swap")
|
||||||
|
|
||||||
vim.keymap.set("n", "]a", function() move.goto_next_start("@parameter.inner", "textobjects") end, { desc = "Next parameter" })
|
-- Map motion function names to actual functions
|
||||||
vim.keymap.set("n", "[a", function() move.goto_previous_start("@parameter.inner", "textobjects") end, { desc = "Prev parameter" })
|
local move_fns = {
|
||||||
vim.keymap.set("n", "]f", function() move.goto_next_start("@function.outer", "textobjects") end, { desc = "Next function" })
|
goto_next_start = move.goto_next_start,
|
||||||
vim.keymap.set("n", "[f", function() move.goto_previous_start("@function.outer", "textobjects") end, { desc = "Prev function" })
|
goto_next_end = move.goto_next_end,
|
||||||
vim.keymap.set("n", "]o", function() move.goto_next_start("@loop.*", "textobjects") end, { desc = "Next loop" })
|
goto_previous_start = move.goto_previous_start,
|
||||||
vim.keymap.set("n", "[o", function() move.goto_previous_start("@loop.*", "textobjects") end, { desc = "Prev loop" })
|
goto_previous_end = move.goto_previous_end,
|
||||||
vim.keymap.set("n", "]s", function() move.goto_next_start("@local.scope", "textobjects") end, { desc = "Next scope" })
|
goto_next = move.goto_next,
|
||||||
vim.keymap.set("n", "[s", function() move.goto_previous_start("@local.scope", "locals") end, { desc = "Prev scope" })
|
goto_previous = move.goto_previous,
|
||||||
vim.keymap.set("n", "]z", function() move.goto_next_start("@fold", "folds") end, { desc = "Next fold" })
|
}
|
||||||
vim.keymap.set("n", "[z", function() move.goto_previous_start("@fold", "folds") end, { desc = "Prev fold" })
|
|
||||||
vim.keymap.set("n", "]M", function() move.goto_next_end("@function.outer", "textobjects") end, { desc = "Next function end" })
|
|
||||||
vim.keymap.set("n", "][", function() move.goto_next_end("@class.outer", "textobjects") end, { desc = "Next class end" })
|
|
||||||
vim.keymap.set("n", "[M", function() move.goto_previous_end("@function.outer", "textobjects") end, { desc = "Prev function end" })
|
|
||||||
vim.keymap.set("n", "[]", function() move.goto_previous_end("@class.outer", "textobjects") end, { desc = "Prev class end" })
|
|
||||||
vim.keymap.set("n", "]e", function() move.goto_next("@conditional.outer", "textobjects") end, { desc = "Next conditional" })
|
|
||||||
vim.keymap.set("n", "[e", function() move.goto_previous("@conditional.outer", "textobjects") end, { desc = "Prev conditional" })
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<leader>x", function() swap.swap_next("@parameter.inner", "textobjects") end, { desc = "Swap parameter next" })
|
-- All motions defined in one place
|
||||||
vim.keymap.set("n", "<leader>X", function() swap.swap_previous("@parameter.inner", "textobjects") end, { desc = "Swap parameter prev" })
|
-- spec = { query_or_list, query_group, desc }
|
||||||
|
local move_maps = {
|
||||||
|
goto_next_start = {
|
||||||
|
["]a"] = { "@parameter.inner", "textobjects", "Next parameter" },
|
||||||
|
["]f"] = { "@function.outer", "textobjects", "Next function start" },
|
||||||
|
["]o"] = { { "@loop.inner", "@loop.outer" }, "textobjects", "Next loop" },
|
||||||
|
["]s"] = { "@local.scope", "locals", "Next scope" },
|
||||||
|
["]z"] = { "@fold", "folds", "Next fold" },
|
||||||
|
},
|
||||||
|
goto_next_end = {
|
||||||
|
["]M"] = { "@function.outer", "textobjects", "Next function end" },
|
||||||
|
["]["] = { "@class.outer", "textobjects", "Next class end" },
|
||||||
|
},
|
||||||
|
goto_previous_start = {
|
||||||
|
["[a"] = { "@parameter.inner", "textobjects", "Previous parameter" },
|
||||||
|
["[f"] = { "@function.outer", "textobjects", "Previous function start" },
|
||||||
|
["[o"] = { { "@loop.inner", "@loop.outer" }, "textobjects", "Previous loop" },
|
||||||
|
["[s"] = { "@local.scope", "locals", "Previous scope" },
|
||||||
|
["[z"] = { "@fold", "folds", "Previous fold" },
|
||||||
|
},
|
||||||
|
goto_previous_end = {
|
||||||
|
["[M"] = { "@function.outer", "textobjects", "Previous function end" },
|
||||||
|
["[]"] = { "@class.outer", "textobjects", "Previous class end" },
|
||||||
|
},
|
||||||
|
goto_next = {
|
||||||
|
["]e"] = { "@conditional.outer", "textobjects", "Next conditional" },
|
||||||
|
},
|
||||||
|
goto_previous = {
|
||||||
|
["[e"] = { "@conditional.outer", "textobjects", "Previous conditional" },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
if not Config.isNixCats then
|
-- Generate motion keymaps
|
||||||
local installed_check = function(lang)
|
for fn_name, maps in pairs(move_maps) do
|
||||||
return #vim.api.nvim_get_runtime_file("parser/" .. lang .. ".*", false) == 0
|
local fn = move_fns[fn_name]
|
||||||
end
|
if fn then
|
||||||
local default_parsers = Config.treesitter_helpers.default_parsers
|
for lhs, spec in pairs(maps) do
|
||||||
local to_install = vim.tbl_filter(installed_check, default_parsers)
|
local query_or_list, group, desc = spec[1], spec[2], spec[3]
|
||||||
if #to_install > 0 then
|
vim.keymap.set({ "n", "x", "o" }, lhs, function()
|
||||||
require("nvim-treesitter").install(to_install)
|
fn(query_or_list, group)
|
||||||
|
end, { desc = desc })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Swap keymaps (unchanged, but minimal)
|
||||||
|
vim.keymap.set("n", "<leader>x", function()
|
||||||
|
swap.swap_next("@parameter.inner")
|
||||||
|
end, { desc = "Swap with next parameter" })
|
||||||
|
|
||||||
|
vim.keymap.set("n", "<leader>X", function()
|
||||||
|
swap.swap_previous("@parameter.inner")
|
||||||
|
end, { desc = "Swap with previous parameter" })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- zk
|
-- zk
|
||||||
now_if_args(function()
|
now_if_args(function()
|
||||||
if nix.get_cat("markdown", false) then
|
require("zk").setup({
|
||||||
require("zk").setup({
|
picker = "minipick",
|
||||||
picker = "minipick",
|
lsp = {
|
||||||
lsp = {
|
-- `config` is passed to `vim.lsp.start_client(config)`
|
||||||
-- `config` is passed to `vim.lsp.start_client(config)`
|
config = {
|
||||||
config = {
|
cmd = { "zk", "lsp" },
|
||||||
cmd = { "zk", "lsp" },
|
name = "zk",
|
||||||
name = "zk",
|
-- on_attach = ...
|
||||||
-- on_attach = ...
|
-- etc, see `:h vim.lsp.start_client()`
|
||||||
-- etc, see `:h vim.lsp.start_client()`
|
|
||||||
},
|
|
||||||
|
|
||||||
-- automatically attach buffers in a zk notebook that match the given filetypes
|
|
||||||
auto_attach = {
|
|
||||||
enabled = true,
|
|
||||||
filetypes = { "markdown" },
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
},
|
||||||
})
|
|
||||||
end
|
-- automatically attach buffers in a zk notebook that match the given filetypes
|
||||||
|
auto_attach = {
|
||||||
|
enabled = true,
|
||||||
|
filetypes = { "markdown" },
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -5,23 +5,24 @@ local add = Config.add
|
||||||
local nix = require('config.nix')
|
local nix = require('config.nix')
|
||||||
|
|
||||||
if not Config.isNixCats then
|
if not Config.isNixCats then
|
||||||
local add = MiniDeps.add
|
local m_add = MiniDeps.add
|
||||||
|
|
||||||
now(function()
|
now(function()
|
||||||
add({ source = "R-nvim/R.nvim" })
|
m_add({ source = "R-nvim/R.nvim" })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
now_if_args(function()
|
now_if_args(function()
|
||||||
add({ source = "jmbuhr/otter.nvim" })
|
m_add({ source = "jmbuhr/otter.nvim" })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
add({ source = "jpalardy/vim-slime" })
|
m_add({ source = "jpalardy/vim-slime" })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- terminal
|
-- terminal
|
||||||
later(function()
|
later(function()
|
||||||
|
vim.g.slime_target = "neovim"
|
||||||
vim.g.slime_no_mappings = true
|
vim.g.slime_no_mappings = true
|
||||||
add("vim-slime")
|
add("vim-slime")
|
||||||
vim.g.slime_cell_delimiter = vim.g.slime_cell_delimiter or "# %%"
|
vim.g.slime_cell_delimiter = vim.g.slime_cell_delimiter or "# %%"
|
||||||
|
|
@ -54,21 +55,7 @@ now(function()
|
||||||
min_editor_width = 80,
|
min_editor_width = 80,
|
||||||
rconsole_height = 20,
|
rconsole_height = 20,
|
||||||
nvimpager = "split_h",
|
nvimpager = "split_h",
|
||||||
pdfviewer = "",
|
pdfviewer = "zathura",
|
||||||
-- Use R.nvim's built-in rnvimserver-backed language server. Do not
|
|
||||||
-- configure the external R languageserver through plugin/25_lsp.lua.
|
|
||||||
r_ls = {
|
|
||||||
completion = true,
|
|
||||||
hover = true,
|
|
||||||
signature = true,
|
|
||||||
definition = true,
|
|
||||||
references = true,
|
|
||||||
implementation = true,
|
|
||||||
document_symbol = true,
|
|
||||||
workspace_symbol = true,
|
|
||||||
document_highlight = true,
|
|
||||||
rename = true,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
@ -76,45 +63,43 @@ end)
|
||||||
|
|
||||||
-- Quarto
|
-- Quarto
|
||||||
now(function()
|
now(function()
|
||||||
if nix.get_cat({ "r", "markdown" }, false) then
|
vim.treesitter.language.register("markdown", { "quarto", "rmd" })
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
|
||||||
pattern = { "quarto" },
|
|
||||||
callback = function()
|
|
||||||
require("otter").activate()
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
require("otter").setup({
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
lsp = {
|
pattern = { "quarto" },
|
||||||
diagnostic_update_events = { "BufWritePost", "InsertLeave" },
|
callback = function()
|
||||||
},
|
require("otter").activate()
|
||||||
buffers = {
|
end,
|
||||||
set_filetype = true,
|
})
|
||||||
write_to_disk = true,
|
|
||||||
},
|
require("otter").setup({
|
||||||
})
|
lsp = {
|
||||||
end
|
diagnostic_update_events = { "BufWritePost", "InsertLeave" },
|
||||||
|
},
|
||||||
|
buffers = {
|
||||||
|
set_filetype = true,
|
||||||
|
write_to_disk = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
if nix.get_cat({ "r", "markdown" }, false) then
|
require("quarto").setup({
|
||||||
require("quarto").setup({
|
lspFeatures = {
|
||||||
lspFeatures = {
|
enabled = true,
|
||||||
|
chunks = "curly",
|
||||||
|
languages = { "r", "python", "julia" },
|
||||||
|
diagnostics = {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
chunks = "curly",
|
triggers = { "BufWritePost" },
|
||||||
languages = { "r", "python", "julia" },
|
|
||||||
diagnostics = {
|
|
||||||
enabled = true,
|
|
||||||
triggers = { "BufWritePost" },
|
|
||||||
},
|
|
||||||
completion = {
|
|
||||||
enabled = true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
codeRunner = {
|
completion = {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
default_method = "slime",
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
end
|
codeRunner = {
|
||||||
|
enabled = true,
|
||||||
|
default_method = "slime",
|
||||||
|
},
|
||||||
|
})
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -1,99 +1,29 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
local add = Config.add
|
local add = Config.add
|
||||||
local now_if_args = Config.now_if_args
|
local now_if_args = Config.now_if_args
|
||||||
local later = MiniDeps.later
|
local later = MiniDeps.later
|
||||||
local nix = require('config.nix')
|
|
||||||
|
|
||||||
if not Config.isNixCats then
|
if not Config.isNixCats then
|
||||||
local add = MiniDeps.add
|
local m_add = MiniDeps.add
|
||||||
later(function()
|
later(function()
|
||||||
add({ source = "Bilal2453/luvit-meta" })
|
m_add({ source = "Bilal2453/luvit-meta" })
|
||||||
add({ source = "folke/lazydev.nvim" })
|
m_add({ source = "folke/lazydev.nvim" })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- lua
|
-- lua
|
||||||
later(function()
|
later(function()
|
||||||
if nix.get_cat("lua", false) then
|
add("luvit-meta")
|
||||||
add("luvit-meta")
|
add("lazydev")
|
||||||
add("lazydev")
|
require("lazydev").setup({
|
||||||
require("lazydev").setup({
|
library = {
|
||||||
library = {
|
-- See the configuration section for more details
|
||||||
-- See the configuration section for more details
|
-- Load luvit types when the `vim.uv` word is found
|
||||||
-- Load luvit types when the `vim.uv` word is found
|
"lua",
|
||||||
"lua",
|
"mini.nvim",
|
||||||
"mini.nvim",
|
"MiniDeps",
|
||||||
"MiniDeps",
|
{ path = "luvit-meta/library", words = { "vim%.uv" } },
|
||||||
{ path = "luvit-meta/library", words = { "vim%.uv" } },
|
{ path = "${3rd}/luv/library", words = { "vim%.uv" } },
|
||||||
{ path = "${3rd}/luv/library", words = { "vim%.uv" } },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Linting (via nvim-lint)
|
|
||||||
later(function()
|
|
||||||
Config.add("nvim-lint")
|
|
||||||
local lint_ok, lint = pcall(require, "lint")
|
|
||||||
if not lint_ok then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- R code style via lintr (must be available in the R runtime).
|
|
||||||
-- lintr::lint() returns a "lints" object; format() turns it into the
|
|
||||||
-- standard "file:line:col: severity: message" lines.
|
|
||||||
lint.linters.lintr = {
|
|
||||||
cmd = "Rscript",
|
|
||||||
stdin = false,
|
|
||||||
args = {
|
|
||||||
"-e",
|
|
||||||
"args <- commandArgs(trailingOnly=TRUE); l <- lintr::lint(args[1]); if (length(l) > 0) cat(paste(format(l), collapse='\\n'), '\\n')",
|
|
||||||
},
|
},
|
||||||
append_fname = true,
|
|
||||||
stream = "both",
|
|
||||||
ignore_exitcode = true,
|
|
||||||
parser = function(output, bufnr, linter_cwd)
|
|
||||||
local diagnostics = {}
|
|
||||||
-- Expected format: /path/file.R:10:5: style: Some message
|
|
||||||
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,
|
|
||||||
severity = severity_map[severity:lower()] or vim.diagnostic.severity.WARN,
|
|
||||||
message = message or "lintr issue",
|
|
||||||
source = "lintr",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return diagnostics
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
lint.linters_by_ft = {
|
|
||||||
r = { "lintr" },
|
|
||||||
rmd = { "lintr" },
|
|
||||||
quarto = { "lintr" },
|
|
||||||
}
|
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({ "BufReadPost", "BufWritePost", "InsertLeave" }, {
|
|
||||||
group = vim.api.nvim_create_augroup("LintOnEvents", { clear = true }),
|
|
||||||
callback = function()
|
|
||||||
lint.try_lint()
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -102,7 +32,7 @@ now_if_args(function()
|
||||||
add("render-markdown.nvim")
|
add("render-markdown.nvim")
|
||||||
require('render-markdown').setup({
|
require('render-markdown').setup({
|
||||||
-- completions = { blink = { enabled = true } },
|
-- completions = { blink = { enabled = true } },
|
||||||
file_types = { 'markdown', },
|
file_types = { 'markdown', 'codecompanion', },
|
||||||
link = {
|
link = {
|
||||||
wiki = {
|
wiki = {
|
||||||
body = function(ctx)
|
body = function(ctx)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ local later = MiniDeps.later
|
||||||
local add = Config.add
|
local add = Config.add
|
||||||
|
|
||||||
if not Config.isNixCats then
|
if not Config.isNixCats then
|
||||||
local add = MiniDeps.add
|
local m_add = MiniDeps.add
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
add("stevearc/conform.nvim")
|
m_add("stevearc/conform.nvim")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -23,6 +23,20 @@ later(function()
|
||||||
rmd = { "injected" },
|
rmd = { "injected" },
|
||||||
quarto = { "injected" },
|
quarto = { "injected" },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
default_format_opts = {
|
||||||
|
lsp_format = "fallback",
|
||||||
|
},
|
||||||
|
|
||||||
|
formatters = {
|
||||||
|
my_styler = {
|
||||||
|
command = "R",
|
||||||
|
-- A list of strings, or a function that returns a list of strings
|
||||||
|
-- Return a single string instead of a list to run the command in a shell
|
||||||
|
args = { "-s", "-e", "styler::style_file(commandArgs(TRUE)[1])", "--args", "$FILENAME" },
|
||||||
|
stdin = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -37,12 +51,7 @@ later(function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
require("mini.align").setup({
|
require("mini.align").setup()
|
||||||
mappings = {
|
|
||||||
start = "gA",
|
|
||||||
start_with_preview = "g<C-A>",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
|
|
@ -218,8 +227,6 @@ later(function()
|
||||||
local minikeymap = require("mini.keymap")
|
local minikeymap = require("mini.keymap")
|
||||||
minikeymap.setup()
|
minikeymap.setup()
|
||||||
local map_multistep = minikeymap.map_multistep
|
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 = {
|
local tab_steps = {
|
||||||
"blink_next",
|
"blink_next",
|
||||||
"pmenu_next",
|
"pmenu_next",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
local Config = require('config')
|
|
||||||
local add = Config.add
|
local add = Config.add
|
||||||
local later = MiniDeps.later
|
local later = MiniDeps.later
|
||||||
local now = MiniDeps.now
|
local now = MiniDeps.now
|
||||||
local now_if_args = Config.now_if_args
|
local now_if_args = Config.now_if_args
|
||||||
|
|
||||||
|
-- Constants
|
||||||
|
local BLINK_VERSION = "v1.4.1"
|
||||||
|
|
||||||
-- Plugin sources configuration
|
-- Plugin sources configuration
|
||||||
local PLUGIN_SOURCES = {
|
local PLUGIN_SOURCES = {
|
||||||
"hrsh7th/cmp-cmdline",
|
"hrsh7th/cmp-cmdline",
|
||||||
|
|
@ -11,7 +13,7 @@ local PLUGIN_SOURCES = {
|
||||||
"zbirenbaum/copilot.lua",
|
"zbirenbaum/copilot.lua",
|
||||||
"jmbuhr/cmp-pandoc-references",
|
"jmbuhr/cmp-pandoc-references",
|
||||||
"fang2hou/blink-copilot",
|
"fang2hou/blink-copilot",
|
||||||
"nickjvandyke/opencode.nvim",
|
"olimorris/codecompanion.nvim"
|
||||||
}
|
}
|
||||||
|
|
||||||
local PLUGIN_ADDS = {
|
local PLUGIN_ADDS = {
|
||||||
|
|
@ -21,36 +23,175 @@ local PLUGIN_ADDS = {
|
||||||
"cmp-pandoc-references",
|
"cmp-pandoc-references",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Helper functions
|
||||||
|
local function create_system_prompt(role_description)
|
||||||
|
return function(context)
|
||||||
|
return "I want you to act as a senior " .. context.filetype .. " developer. " .. role_description
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_code_block(context)
|
||||||
|
local text = require("codecompanion.helpers.actions").get_code(context.start_line, context.end_line)
|
||||||
|
return "```" .. context.filetype .. "\n" .. text .. "\n```"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_common_opts(mapping, short_name)
|
||||||
|
return {
|
||||||
|
mapping = mapping,
|
||||||
|
modes = { "v" },
|
||||||
|
short_name = short_name,
|
||||||
|
auto_submit = true,
|
||||||
|
stop_context_insertion = true,
|
||||||
|
user_prompt = true,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
local function get_mini_icons_highlight(ctx)
|
local function get_mini_icons_highlight(ctx)
|
||||||
local _, hl, _ = require("mini.icons").get("lsp", ctx.kind)
|
local _, hl, _ = require("mini.icons").get("lsp", ctx.kind)
|
||||||
return hl
|
return hl
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_blink_fuzzy_setting()
|
local function get_blink_fuzzy_setting()
|
||||||
return {
|
local setting = {
|
||||||
sorts = { "exact", "score", "sort_text" },
|
sorts = { "exact", "score", "sort_text" }
|
||||||
use_proximity = true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if not Config.isNixCats then
|
||||||
|
setting.prebuilt_binary = { force_version = BLINK_VERSION }
|
||||||
|
end
|
||||||
|
|
||||||
|
return setting
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Plugin loading
|
-- Plugin loading
|
||||||
if not Config.isNixCats then
|
if not Config.isNixCats then
|
||||||
local add = MiniDeps.add
|
local m_add = MiniDeps.add
|
||||||
|
|
||||||
now_if_args(function()
|
now_if_args(function()
|
||||||
add({
|
m_add({
|
||||||
source = "saghen/blink.cmp",
|
source = "saghen/blink.cmp",
|
||||||
depends = { "rafamadriz/friendly-snippets" },
|
depends = { "rafamadriz/friendly-snippets" },
|
||||||
|
checkout = BLINK_VERSION,
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
for _, source in ipairs(PLUGIN_SOURCES) do
|
for _, source in ipairs(PLUGIN_SOURCES) do
|
||||||
add({ source = source })
|
m_add({ source = source })
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function get_codecompanion_config()
|
||||||
|
return {
|
||||||
|
interactions = {
|
||||||
|
chat = {
|
||||||
|
adapter = {
|
||||||
|
name = "copilot",
|
||||||
|
model = "gemini-3.1-pro-preview",
|
||||||
|
},
|
||||||
|
opts = {
|
||||||
|
completion_provider = "blink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inline = {
|
||||||
|
adapter = {
|
||||||
|
name = "copilot",
|
||||||
|
model = "gemini-3.1-pro-preview",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keymaps = {
|
||||||
|
accept_change = {
|
||||||
|
modes = { n = "ga" },
|
||||||
|
description = "Accept the suggested change",
|
||||||
|
},
|
||||||
|
reject_change = {
|
||||||
|
modes = { n = "gr" },
|
||||||
|
opts = { nowait = true },
|
||||||
|
description = "Reject the suggested change",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
display = {
|
||||||
|
chat = {
|
||||||
|
show_settings = false,
|
||||||
|
window = {
|
||||||
|
layout = "horizontal",
|
||||||
|
position = "bottom",
|
||||||
|
height = 0.33,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prompt_library = {
|
||||||
|
["expert"] = {
|
||||||
|
interaction = "chat",
|
||||||
|
description = "Get expert advice from an LLM",
|
||||||
|
--opts = create_common_opts("<localleader>ae", "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 = create_common_opts("<localleader>af", "afixer"),
|
||||||
|
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 = create_common_opts("<localleader>as", "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 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
-- Batch add simple plugins
|
-- Batch add simple plugins
|
||||||
later(function()
|
later(function()
|
||||||
for _, plugin in ipairs(PLUGIN_ADDS) do
|
for _, plugin in ipairs(PLUGIN_ADDS) do
|
||||||
|
|
@ -102,33 +243,19 @@ later(function()
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
vim.g.opencode_opts = {
|
add("codecompanion.nvim")
|
||||||
events = {
|
|
||||||
reload = true,
|
-- now use function
|
||||||
permissions = {
|
require("codecompanion").setup(get_codecompanion_config())
|
||||||
enabled = true,
|
vim.cmd([[cab cc CodeCompanion]])
|
||||||
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")
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
now_if_args(function()
|
now_if_args(function()
|
||||||
add("blink.cmp")
|
add("blink.cmp")
|
||||||
|
|
||||||
require("blink.cmp").setup({
|
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 = {
|
keymap = {
|
||||||
preset = "default",
|
preset = "default",
|
||||||
["<C-space>"] = { "show", "select_next" },
|
["<C-space>"] = { "show", "select_next" },
|
||||||
|
|
@ -165,7 +292,6 @@ now_if_args(function()
|
||||||
},
|
},
|
||||||
completion = {
|
completion = {
|
||||||
menu = {
|
menu = {
|
||||||
border = "rounded",
|
|
||||||
draw = {
|
draw = {
|
||||||
treesitter = { "lsp" },
|
treesitter = { "lsp" },
|
||||||
components = {
|
components = {
|
||||||
|
|
@ -185,13 +311,12 @@ now_if_args(function()
|
||||||
list = {
|
list = {
|
||||||
selection = { preselect = false, auto_insert = true }
|
selection = { preselect = false, auto_insert = true }
|
||||||
},
|
},
|
||||||
ghost_text = { enabled = true, show_with_menu = true },
|
documentation = { auto_show = true },
|
||||||
documentation = { auto_show = true, window = { border = "rounded" } },
|
|
||||||
trigger = { show_in_snippet = false },
|
trigger = { show_in_snippet = false },
|
||||||
},
|
},
|
||||||
snippets = { preset = "mini_snippets" },
|
snippets = { preset = "mini_snippets" },
|
||||||
sources = {
|
sources = {
|
||||||
default = { "references", "lsp", "path", "snippets", "buffer", "omni", "copilot" },
|
default = { "references", "lsp", "path", "snippets", "buffer", "omni", "copilot", "codecompanion" },
|
||||||
providers = {
|
providers = {
|
||||||
path = {
|
path = {
|
||||||
score_offset = 50,
|
score_offset = 50,
|
||||||
|
|
@ -210,12 +335,22 @@ now_if_args(function()
|
||||||
score_offset = 10,
|
score_offset = 10,
|
||||||
opts = { cmp_name = "cmdline" }
|
opts = { cmp_name = "cmdline" }
|
||||||
},
|
},
|
||||||
|
cmp_r = {
|
||||||
|
name = "cmp_r",
|
||||||
|
module = "blink.compat.source",
|
||||||
|
},
|
||||||
copilot = {
|
copilot = {
|
||||||
name = "copilot",
|
name = "copilot",
|
||||||
module = "blink-copilot",
|
module = "blink-copilot",
|
||||||
score_offset = 45,
|
score_offset = 45,
|
||||||
async = true,
|
async = true,
|
||||||
},
|
},
|
||||||
|
codecompanion = {
|
||||||
|
name = "CodeCompanion",
|
||||||
|
module = "codecompanion.providers.completion.blink",
|
||||||
|
score_offset = 45,
|
||||||
|
async = true,
|
||||||
|
},
|
||||||
references = {
|
references = {
|
||||||
name = "pandoc_references",
|
name = "pandoc_references",
|
||||||
module = "cmp-pandoc-references.blink",
|
module = "cmp-pandoc-references.blink",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
local Config = require('config')
|
|
||||||
local now_if_args = Config.now_if_args
|
local now_if_args = Config.now_if_args
|
||||||
|
|
||||||
if not Config.isNixCats then
|
if not Config.isNixCats then
|
||||||
|
|
@ -9,81 +8,24 @@ if not Config.isNixCats then
|
||||||
end
|
end
|
||||||
|
|
||||||
now_if_args(function()
|
now_if_args(function()
|
||||||
-- R.nvim owns the R `r_ls` client and starts its bundled rnvimserver from
|
|
||||||
-- its own plugin directory. Keep R out of this generic server registry.
|
|
||||||
local servers = {
|
local servers = {
|
||||||
|
clangd = {},
|
||||||
basedpyright = {},
|
basedpyright = {},
|
||||||
ruff = {},
|
ruff = {},
|
||||||
marksman = {
|
marksman = {
|
||||||
filetypes = { "markdown", "markdown_inline" },
|
filetypes = { "markdown", "markdown_inline", "codecompanion" },
|
||||||
},
|
|
||||||
harper_ls = {
|
|
||||||
cmd = { "harper-ls", "--stdio" },
|
|
||||||
filetypes = { "markdown", "quarto", "text", "tex", "typst" },
|
|
||||||
root_markers = { ".git", ".harper" },
|
|
||||||
settings = {
|
|
||||||
["harper-ls"] = {
|
|
||||||
linters = {
|
|
||||||
SpellCheck = true,
|
|
||||||
SpelledNumbers = false,
|
|
||||||
AnA = true,
|
|
||||||
SentenceCapitalization = true,
|
|
||||||
UnclosedQuotes = true,
|
|
||||||
WrongApostrophe = false,
|
|
||||||
LongSentences = true,
|
|
||||||
RepeatedWords = true,
|
|
||||||
Spaces = true,
|
|
||||||
CorrectNumberSuffix = true,
|
|
||||||
},
|
|
||||||
codeActions = {
|
|
||||||
ForceStable = false,
|
|
||||||
},
|
|
||||||
markdown = {
|
|
||||||
IgnoreLinkTitle = false,
|
|
||||||
},
|
|
||||||
diagnosticSeverity = "hint",
|
|
||||||
isolateEnglish = false,
|
|
||||||
dialect = "American",
|
|
||||||
maxFileLength = 120000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil_ls = {
|
|
||||||
settings = {
|
|
||||||
["nil"] = {
|
|
||||||
formatting = {
|
|
||||||
command = { "alejandra" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nixd = {
|
|
||||||
settings = {
|
|
||||||
nixd = {
|
|
||||||
formatting = {
|
|
||||||
command = { "alejandra" },
|
|
||||||
},
|
|
||||||
options = {
|
|
||||||
-- Downstream flakes can override these via lib.mkMerge on the lsp config.
|
|
||||||
nixos = { expr = "(builtins.getFlake \"/etc/nixos\").nixosConfigurations.\"default\".options" },
|
|
||||||
home_manager = { expr = "(builtins.getFlake \"/etc/nixos\").homeConfigurations.\"default\".options" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yamlls = {
|
|
||||||
settings = {
|
|
||||||
yaml = {
|
|
||||||
schemas = {
|
|
||||||
["https://raw.githubusercontent.com/quarto-dev/quarto-cli/main/src/resources/schema/project.json"] = "**/_quarto.yml",
|
|
||||||
["https://raw.githubusercontent.com/quarto-dev/quarto-cli/main/src/resources/schema/document-quarto.json"] = "**/*.qmd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
texlab = {
|
|
||||||
single_file_support = true,
|
|
||||||
},
|
},
|
||||||
|
-- r_language_server = {
|
||||||
|
-- filetypes = { 'r', 'rmd', 'rmarkdown' },
|
||||||
|
-- settings = {
|
||||||
|
-- ['r_language_server'] = {
|
||||||
|
-- lsp = {
|
||||||
|
-- rich_documentation = true,
|
||||||
|
-- enable = true,
|
||||||
|
-- },
|
||||||
|
-- },
|
||||||
|
-- }
|
||||||
|
-- },
|
||||||
julials = {
|
julials = {
|
||||||
settings = {
|
settings = {
|
||||||
julia = {
|
julia = {
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
local later = MiniDeps.later
|
|
||||||
local nix = require('config.nix')
|
|
||||||
|
|
||||||
later(function()
|
|
||||||
-- Only pull in the DAP packages when R (the only cat that has a real
|
|
||||||
-- adapter for now) is enabled; otherwise the lazy load + autoload chain
|
|
||||||
-- still works but those three would sit unused on the runtime path.
|
|
||||||
if not nix.get_cat("r", false) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
Config.add("nvim-dap")
|
|
||||||
Config.add("nvim-dap-ui")
|
|
||||||
Config.add("nvim-dap-virtual-text")
|
|
||||||
end)
|
|
||||||
|
|
||||||
later(function()
|
|
||||||
if not nix.get_cat("r", false) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local dap_ok, dap = pcall(require, "dap")
|
|
||||||
if not dap_ok then
|
|
||||||
vim.notify("nvim-dap not available", vim.log.levels.WARN)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local dapui_ok, dapui = pcall(require, "dapui")
|
|
||||||
if dapui_ok then
|
|
||||||
dapui.setup()
|
|
||||||
dap.listeners.after.event_initialized["dapui_config"] = function()
|
|
||||||
dapui.open()
|
|
||||||
end
|
|
||||||
dap.listeners.before.event_terminated["dapui_config"] = function()
|
|
||||||
dapui.close()
|
|
||||||
end
|
|
||||||
dap.listeners.before.event_exited["dapui_config"] = function()
|
|
||||||
dapui.close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local vt_ok, _ = pcall(require, "nvim-dap-virtual-text")
|
|
||||||
if vt_ok then
|
|
||||||
-- Default setup is enough; virtual text is enabled automatically.
|
|
||||||
end
|
|
||||||
|
|
||||||
-- R adapter via vscDebugger (https://github.com/cwida/vscDebugger)
|
|
||||||
if nix.get_cat("r", false) then
|
|
||||||
dap.adapters.r = {
|
|
||||||
type = "executable",
|
|
||||||
command = "R",
|
|
||||||
args = {
|
|
||||||
"--quiet",
|
|
||||||
"--no-save",
|
|
||||||
"-e",
|
|
||||||
"vscDebugger::main()",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
dap.configurations.r = {
|
|
||||||
{
|
|
||||||
type = "r",
|
|
||||||
name = "Debug current R script",
|
|
||||||
request = "launch",
|
|
||||||
program = "${file}",
|
|
||||||
debugMode = "function",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type = "r",
|
|
||||||
name = "Attach to R process",
|
|
||||||
request = "attach",
|
|
||||||
hostName = "localhost",
|
|
||||||
port = 18721,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
local later = MiniDeps.later
|
|
||||||
local nix = require('config.nix')
|
|
||||||
|
|
||||||
-- Only load image-nvim when a cat that benefits from in-buffer plots is on.
|
|
||||||
later(function()
|
|
||||||
if not nix.get_cat({ "r", "markdown" }, false) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
Config.add("image-nvim")
|
|
||||||
end)
|
|
||||||
|
|
||||||
later(function()
|
|
||||||
if not nix.get_cat({ "r", "markdown" }, false) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, image = pcall(require, "image")
|
|
||||||
if not ok then
|
|
||||||
vim.notify("image.nvim not available", vim.log.levels.DEBUG)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
image.setup({
|
|
||||||
-- Backend is intentionally NOT set so image.nvim auto-detects the
|
|
||||||
-- graphics protocol (kitty / wezterm / iterm / sixel) at runtime and
|
|
||||||
-- falls back to no rendering on unsupported terminals. The literal
|
|
||||||
-- strings "auto" / "none" are not accepted by image.nvim.setup(), so
|
|
||||||
-- setting either would silently disable rendering everywhere.
|
|
||||||
integrations = {
|
|
||||||
markdown = {
|
|
||||||
enabled = true,
|
|
||||||
clear_in_insert_mode = false,
|
|
||||||
download_remote_images = true,
|
|
||||||
only_render_image_at_cursor = false,
|
|
||||||
filetypes = { "markdown", "quarto" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
max_width = nil,
|
|
||||||
max_height = nil,
|
|
||||||
max_width_window_percentage = nil,
|
|
||||||
max_height_window_percentage = 50,
|
|
||||||
window_overlap_clear_enabled = false,
|
|
||||||
window_overlap_clear_ft_ignore = { "cmp_menu", "cmp_docs" },
|
|
||||||
editor_only_render_when_focused = false,
|
|
||||||
hijack_file_patterns = { "*.png", "*.jpg", "*.jpeg", "*.gif", "*.webp" },
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
-- vimtex integration for raw .tex workflows (paper drafts, AEA submissions,
|
|
||||||
-- beamer slides). Rides in the `markdown` cat because Quarto users routinely
|
|
||||||
-- also write standalone .tex.
|
|
||||||
|
|
||||||
local Config = require('config')
|
|
||||||
local later = MiniDeps.later
|
|
||||||
local nix = require('config.nix')
|
|
||||||
|
|
||||||
later(function()
|
|
||||||
if not nix.get_cat("markdown", false) then
|
|
||||||
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")
|
|
||||||
|
|
||||||
-- 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", {
|
|
||||||
pattern = { "*.tex", "*.sty", "*.cls" },
|
|
||||||
callback = function(args)
|
|
||||||
local buf = args.buf
|
|
||||||
if vim.api.nvim_buf_is_loaded(buf) then
|
|
||||||
vim.bo[buf].filetype = "tex"
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
-- Bloocky: timeblocking calendar (day/week/month views), persisted to JSON.
|
|
||||||
-- Loaded via the `general` cat spec (nix mode) or MiniDeps (non-nix).
|
|
||||||
-- Default global toggle is <leader>tb, which collides with the terminal map
|
|
||||||
-- (toggle bracketed paste), so it is moved to the calendar/tasks group <leader>c.
|
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
if not Config.isNixCats then
|
|
||||||
local later = MiniDeps.later
|
|
||||||
later(function()
|
|
||||||
MiniDeps.add({ source = 'atiladefreitas/bloocky' })
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local nix = require('config.nix')
|
|
||||||
local later = MiniDeps.later
|
|
||||||
|
|
||||||
later(function()
|
|
||||||
if not nix.get_cat('general', false) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
require('bloocky').setup({
|
|
||||||
week_start = "monday",
|
|
||||||
window = {
|
|
||||||
-- Width per view: fraction of the editor width (or absolute columns if > 1).
|
|
||||||
-- A single number applies to every view.
|
|
||||||
width = {
|
|
||||||
month = 0.99,
|
|
||||||
week = 0.99,
|
|
||||||
day = 0.8,
|
|
||||||
},
|
|
||||||
border = "rounded",
|
|
||||||
},
|
|
||||||
integrations = {
|
|
||||||
dooing = {
|
|
||||||
enabled = true, -- show Dooing todos on their due date
|
|
||||||
show_done = false, -- also show completed todos
|
|
||||||
},
|
|
||||||
},
|
|
||||||
keymaps = {
|
|
||||||
toggle = '<leader>cb',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
-- Dooing: minimalist todo list manager with a floating window, persisted to JSON.
|
|
||||||
-- Loaded via the `general` cat spec (nix mode) or MiniDeps (non-nix).
|
|
||||||
-- Default globals <leader>td / <leader>tN occupy the terminal group, so the
|
|
||||||
-- todo toggles are moved to the calendar/tasks group <leader>c.
|
|
||||||
local Config = require('config')
|
|
||||||
|
|
||||||
if not Config.isNixCats then
|
|
||||||
local later = MiniDeps.later
|
|
||||||
later(function()
|
|
||||||
MiniDeps.add({ source = 'atiladefreitas/dooing' })
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local nix = require('config.nix')
|
|
||||||
local later = MiniDeps.later
|
|
||||||
local now = MiniDeps.now
|
|
||||||
later(function()
|
|
||||||
if not nix.get_cat('general', false) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local dooing = require('dooing')
|
|
||||||
require("dooing").setup({
|
|
||||||
keymaps = {
|
|
||||||
toggle_window = "<leader>cd",
|
|
||||||
open_project_todo = "<leader>cD",
|
|
||||||
show_due_notification = "<leader>cN",
|
|
||||||
create_nested_task = "<leader>cn", -- Create nested subtask under current todo
|
|
||||||
toggle_priority = "a",
|
|
||||||
},
|
|
||||||
calendar = {
|
|
||||||
week_start_day = "monday",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
-- Remove Dooing's old defaults.
|
|
||||||
vim.keymap.del("n", "<leader>td")
|
|
||||||
vim.keymap.del("n", "<leader>tD")
|
|
||||||
vim.keymap.del("n", "<leader>tN")
|
|
||||||
|
|
||||||
-- Restore mappings overwritten by Dooing.
|
|
||||||
local helpers = require('keymap.helpers')
|
|
||||||
local nmap_leader = helpers.nmap_leader
|
|
||||||
-- t is for 'terminal'
|
|
||||||
nmap_leader("td", '<Cmd>lua Config.terminal.open_duckdb();Config.terminal.toggle_bracket()<CR>', 'Open DuckDB')
|
|
||||||
end)
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
-- omp bridge entrypoint: start the RPC socket as early as possible so the omp
|
|
||||||
-- harness can read this instance's buffers on demand (see lua/nvim_omp/init.lua
|
|
||||||
-- and .omp/tools/nvim_buffers.mjs). Safe to fail silently — the socket is a
|
|
||||||
-- convenience, not a dependency of the editor.
|
|
||||||
local ok, nvim_omp = pcall(require, "nvim_omp")
|
|
||||||
if ok then
|
|
||||||
nvim_omp.start()
|
|
||||||
end
|
|
||||||
14
scripts/updater.nix
Normal file
14
scripts/updater.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{pkgs}:
|
||||||
|
pkgs.writeShellApplication {
|
||||||
|
name = "updateR";
|
||||||
|
|
||||||
|
# Tools your script needs at runtime
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.wget
|
||||||
|
pkgs.gnused
|
||||||
|
pkgs.coreutils
|
||||||
|
];
|
||||||
|
|
||||||
|
# Keep script in separate file, but embed contents
|
||||||
|
text = builtins.readFile ./updater.sh;
|
||||||
|
}
|
||||||
22
scripts/updater.sh
Normal file
22
scripts/updater.sh
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
echo "📡 Fetching latest R version from rstats-on-nix..."
|
||||||
|
RVER=$( wget -qO- 'https://raw.githubusercontent.com/ropensci/rix/refs/heads/main/inst/extdata/available_df.csv' | tail -n 1 | head -n 1 | cut -d',' -f4 | tr -d '"' )
|
||||||
|
|
||||||
|
# Validate RVER matches YYYY-MM-DD format
|
||||||
|
if [[ ! "$RVER" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||||
|
echo "❌ Error: Failed to fetch valid R version date. Got: '$RVER'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ R date is $RVER"
|
||||||
|
|
||||||
|
# Create backup of flake.nix before modifying
|
||||||
|
cp flake.nix flake.nix.backup
|
||||||
|
|
||||||
|
# Update rixpkgs date in flake.nix
|
||||||
|
if sed -i "s|rixpkgs.url = \"github:rstats-on-nix/nixpkgs/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\";|rixpkgs.url = \"github:rstats-on-nix/nixpkgs/$RVER\";|" flake.nix; then
|
||||||
|
echo "✅ Updated rixpkgs date in flake.nix"
|
||||||
|
rm flake.nix.backup
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: Failed to update flake.nix, restoring backup"
|
||||||
|
mv flake.nix.backup flake.nix
|
||||||
|
fi
|
||||||
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