diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..7d1ff5d --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,58 @@ +# Publish this repo's static HTML to https://pages.rootiest.dev//. +# +# Runs on Gitea only: the racknerd runner's "pages" label is a container job +# that may mount the Caddy web root (~/docker/caddy/pages, the runner's only +# valid_volumes entry). Gitea mirrors this repo to GitHub, which also sees +# this file, so the job is skipped there; a skipped job costs nothing and +# leaves no pending status. +# +# To publish more files, add "source destination" lines to PAGES. The +# destination is relative to pages.rootiest.dev//; anything under a +# private/ directory needs a Rootiest ID login. +name: Pages + +on: + push: + branches: [main] + paths: + - docs/cheatsheet/cheatsheet.html + - .github/workflows/pages.yml + workflow_dispatch: + +env: + PAGES: | + docs/cheatsheet/cheatsheet.html cheatsheet/index.html + +jobs: + publish: + if: github.server_url != 'https://github.com' + runs-on: pages + container: + image: docker.gitea.com/runner-images:ubuntu-latest + volumes: + - /home/rootiest/docker/caddy/pages:/pages + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Publish + # act_runner defaults to sh; the loop below reads PAGES with <<<. + shell: bash + run: | + dest="/pages/${GITHUB_REPOSITORY#*/}" + # Stage everything first, then swap it in, so the site never shows + # a half-copied tree and files dropped from PAGES disappear. + stage="$(mktemp -d /pages/.stage.XXXXXX)" + trap 'rm -rf "$stage"' EXIT + while read -r src dst; do + [ -n "$src" ] || continue + install -D -m 644 "$src" "$stage/$dst" + echo "https://pages.rootiest.dev/${GITHUB_REPOSITORY#*/}/$dst" + done <<< "$PAGES" + chmod 755 "$stage" + # Files belong to the server user (uid 1000), not the job's root. + chown -R 1000:1000 "$stage" + rm -rf "$dest.old" + [ -e "$dest" ] && mv "$dest" "$dest.old" + mv "$stage" "$dest" + rm -rf "$dest.old" diff --git a/.gitignore b/.gitignore index 7951005..727d00c 100644 --- a/.gitignore +++ b/.gitignore @@ -377,3 +377,4 @@ docs/specs docs/devlogs # ────────────────────────────────────────────────────────────── +/docs/cheatsheet/__pycache__/ diff --git a/README.md b/README.md index 7c73df6..fae49dd 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ A modern, modular, and high-performance Neovim configuration built from scratch ![Neovim](https://img.shields.io/badge/Neovim-0.10+-blue?logo=neovim) ![License](https://img.shields.io/badge/License-GPLv3+-green) +📖 **[Interactive keymap cheatsheet](https://pages.rootiest.dev/neovim-config/cheatsheet/)**: every Vim key and every mapping in this config, searchable. + ## ✨ Highlights - **Built-in Package Management**: Exclusively uses `vim.pack` for lightweight, native plugin management. @@ -39,7 +41,8 @@ The configuration is strictly modular: - **Nvim-web-devicons**: Consistent icons across UI components. ### Editing & Navigation -- **Flash**: Enhanced motion and search. +- **Flash**: `f`/`F`/`t`/`T` with match highlighting, kept to the current line. +- **Leap**: Jump anywhere in the window with `` + two characters (`S` across windows). - **Focusline**: Keeps the active line at a configurable screen position (30%) during scrolling motions. - **Mini.ai**: Better text objects (including `g` for entire buffer). - **Mini.surround**: Surround text objects (add/delete/change). @@ -59,7 +62,7 @@ The configuration is strictly modular: ### LSP & Completion - **Blink.cmp** + **blink.lib**: High-performance Rust-based completion. Auto-detects binary across install layouts; falls back gracefully if unavailable. Rebuilds automatically on `PackChanged`. - **blink-copilot**: Surfaces GitHub Copilot inline suggestions inside the blink.cmp completion menu. -- **Sidekick.nvim**: AI assistant providing Next Edit Suggestions (NES) for multi-line refactoring via the Copilot LSP, plus an integrated AI CLI terminal (`aa`) with context-aware prompts. Snacks picker integration sends selections to the active AI session via ``. +- **Sidekick.nvim**: AI assistant providing Next Edit Suggestions (NES) for multi-line refactoring via the Copilot LSP, plus an integrated AI CLI terminal (`:Sidekick cli toggle`) with context-aware prompts. Snacks picker integration sends selections to the active AI session via ``. - **LSPConfig + Mason**: Managed LSP support for Lua, C/C++, Rust, Python, Fish, and Shell. - **Conform**: Formatter with format-on-save and range formatting. - **Inc-rename**: Incremental LSP rename with live preview. @@ -134,19 +137,17 @@ Place machine-specific or secret configuration in `~/.config/.user-dots/nvim/loc | `j` / `k` / `↓` / `↑` | Move by wrapped (display) line when no count is given; counts still move by real lines. Arrows do the same in insert mode | | `sr` | Search and Replace (Grug-far) | | `gg` | Open Lazygit | -| `qs` | Restore Last Session (Persistence) | +| `qs` / `ql` | Restore Session / Restore Last Session (Persistence) | | `cf` | Format Buffer (Conform) | | `uu` | Toggle Undo Tree | | `z` | Toggle Zen Mode | -| `gd` / `gr` | Goto Definition / References | +| `gd` / `grr` | Goto Definition / References (Snacks pickers); Neovim's `grn` `gra` `gri` `grt` stay available | | `K` | Hover Documentation | -| `s` / `S` | Leap Motion (Normal/Window) | -| `ys` / `ds` / `cs` | Surround (Add/Delete/Change) | -| `gx` / `gX` | Open URL under cursor (Gx.nvim) | +| `` / `S` | Leap Motion (Window/Across windows) | +| `sa` / `sd` / `sr` | Surround (Add/Delete/Replace, mini.surround) | +| `gx` | Open URL, file, plugin or issue under cursor (Gx.nvim) | | `` *(insert)* | Advance snippet → NES suggestion → native inline completion → fallback | -| `` *(normal)* | Jump to / apply Sidekick NES suggestion | -| `aa` | Toggle Sidekick AI CLI terminal | -| `as` | Select AI tool (Sidekick) | +| `` *(normal)* | Jump to / apply Sidekick NES suggestion, else jump forward | | `` *(picker)* | Send picker selection to active AI CLI session | | `ha` | Annotate current position (Haunt) | | `ht` | Toggle annotation visibility (Haunt) | @@ -159,6 +160,20 @@ Place machine-specific or secret configuration in `~/.config/.user-dots/nvim/loc | `cbk` | Browse Box Style Catalog | | `:Q` | Forced Write-All and Quit | +The full list, including built-in Vim keys and keys inside plugin windows, is in the cheatsheet below. + +## 📖 Keymap Cheatsheet + +**[pages.rootiest.dev/neovim-config/cheatsheet](https://pages.rootiest.dev/neovim-config/cheatsheet/)**: a single-page, searchable reference of every built-in Vim key plus this config's live keymaps, showing which built-ins a mapping replaces and where each mapping is defined. It also has an interactive keyboard, a leader-key tree, a text-object composer and the keys inside plugin windows (pickers, completion, Flash/Leap). + +It is generated from Neovim's own help index, a headless dump of this config and curated notes in `docs/cheatsheet/curated/`. After changing keymaps, regenerate it: + +```bash +python3 docs/cheatsheet/gen_cheatsheet.py +``` + +Pushing `docs/cheatsheet/cheatsheet.html` to `main` publishes it (`.github/workflows/pages.yml`). + ## 📜 License Distributed under the **GPLv3 or later** License. See `LICENSE` for more information. diff --git a/docs/cheatsheet/cheatsheet.html b/docs/cheatsheet/cheatsheet.html new file mode 100644 index 0000000..9ad10f3 --- /dev/null +++ b/docs/cheatsheet/cheatsheet.html @@ -0,0 +1,2026 @@ + + + + +Neovim Cheatsheet + + + + + + + + + + +
+
+
Rootiest Neovim
+
+

Vim keys & keymaps

+
+ + + +
+
+ +
    +
    + +
    +

    What's different here

    +

    How this config changes stock Vim. Cards first, then lists worked out from the live keymaps.

    +
    +
    + +
    +
    + +
    +

    Keyboard

    +

    What every key does in each mode or after each prefix. Hover or focus a key for details; click it to look it up in the finder. Click Shift or Ctrl on the board to hold them.

    +
    +
    +

    + +
    +
    +
    +
    +
    +
    + +
    +

    Leader keys

    +

    +
    +
    + +
    +

    Find any key

    +

    Every built-in key, Neovim default, plugin map and your own maps in one list. Search by keys (ctrl-w, <C-w>, ^w, space f, leader f) or by words. Start with : to search Ex commands.

    +
    +
    + + +
    +
    +
    +
    +
    +
    + + + + +
    + + +
    +
    +
    +
    +
    + +
    + +
    +

    Text-object composer

    +

    Pick an operator and a text object to see the keys and what they do.

    +
    +
    + +
    +

    Inside plugin windows

    +

    Keys that only work inside a plugin's own window, menu or buffer, from your live config. Pick a group, then jump to one window with the chips below it, or search them all.

    +
    + + +
    +
    +
    +
    +
    + +
    +

    Commands

    +

    User and plugin :commands defined by this config. Built-in Ex commands are in the finder (choose Ex commands or type :).

    +
    + + +
    +
    +
    +
    + +
    Generated from index.txt, your live config and curated/*.json by gen_cheatsheet.py.
    +
    + + + + + diff --git a/docs/cheatsheet/cheatsheet.template.html b/docs/cheatsheet/cheatsheet.template.html new file mode 100644 index 0000000..be42d98 --- /dev/null +++ b/docs/cheatsheet/cheatsheet.template.html @@ -0,0 +1,2027 @@ + + + + + +Neovim Cheatsheet + + + + + + + + + + +
    +
    +
    Rootiest Neovim
    +
    +

    Vim keys & keymaps

    +
    + + + +
    +
    + +
      +
      + +
      +

      What's different here

      +

      How this config changes stock Vim. Cards first, then lists worked out from the live keymaps.

      +
      +
      + +
      +
      + +
      +

      Keyboard

      +

      What every key does in each mode or after each prefix. Hover or focus a key for details; click it to look it up in the finder. Click Shift or Ctrl on the board to hold them.

      +
      +
      +

      + +
      +
      +
      +
      +
      +
      + +
      +

      Leader keys

      +

      +
      +
      + +
      +

      Find any key

      +

      Every built-in key, Neovim default, plugin map and your own maps in one list. Search by keys (ctrl-w, <C-w>, ^w, space f, leader f) or by words. Start with : to search Ex commands.

      +
      +
      + + +
      +
      +
      +
      +
      +
      + + + + +
      + + +
      +
      +
      +
      +
      + +
      + +
      +

      Text-object composer

      +

      Pick an operator and a text object to see the keys and what they do.

      +
      +
      + +
      +

      Inside plugin windows

      +

      Keys that only work inside a plugin's own window, menu or buffer, from your live config. Pick a group, then jump to one window with the chips below it, or search them all.

      +
      + + +
      +
      +
      +
      +
      + +
      +

      Commands

      +

      User and plugin :commands defined by this config. Built-in Ex commands are in the finder (choose Ex commands or type :).

      +
      + + +
      +
      +
      +
      + +
      Generated from index.txt, your live config and curated/*.json by gen_cheatsheet.py.
      +
      + + + + + diff --git a/docs/cheatsheet/curated/builtins.json b/docs/cheatsheet/curated/builtins.json new file mode 100644 index 0000000..0aef750 --- /dev/null +++ b/docs/cheatsheet/curated/builtins.json @@ -0,0 +1,994 @@ +{ + "essentials": [ + {"t": "h"}, + {"t": "j"}, + {"t": "k"}, + {"t": "l"}, + {"t": "w"}, + {"t": "b"}, + {"t": "e"}, + {"t": "ge"}, + {"t": "W"}, + {"t": "B"}, + {"t": "E"}, + {"t": "0"}, + {"t": "^"}, + {"t": "$"}, + {"t": "gg"}, + {"t": "G"}, + {"t": "%"}, + {"t": "{"}, + {"t": "}"}, + {"t": "("}, + {"t": ")"}, + {"t": "H"}, + {"t": "M"}, + {"t": "L"}, + {"t": "f"}, + {"t": "F"}, + {"t": "t"}, + {"t": "T"}, + {"t": ";"}, + {"t": ","}, + {"t": "star"}, + {"t": "#"}, + {"t": "n"}, + {"t": "N"}, + {"t": "/"}, + {"t": "?"}, + {"t": "gj"}, + {"t": "gk"}, + {"t": "d"}, + {"t": "c"}, + {"t": "y"}, + {"t": "p"}, + {"t": "P"}, + {"t": "dd"}, + {"t": "cc"}, + {"t": "yy"}, + {"t": "D"}, + {"t": "C"}, + {"t": "Y"}, + {"t": ">"}, + {"t": "<"}, + {"t": ">>"}, + {"t": "<<"}, + {"t": "="}, + {"t": "=="}, + {"t": "gu"}, + {"t": "gU"}, + {"t": "g~"}, + {"t": "gq"}, + {"t": "x"}, + {"t": "r"}, + {"t": "R"}, + {"t": "J"}, + {"t": "u"}, + {"t": "CTRL-R"}, + {"t": "."}, + {"t": "~"}, + {"t": "CTRL-A"}, + {"t": "CTRL-X"}, + {"t": "o"}, + {"t": "O"}, + {"t": "i"}, + {"t": "a"}, + {"t": "I"}, + {"t": "A"}, + {"t": "gi"}, + {"t": "&"}, + {"t": "v"}, + {"t": "V"}, + {"t": "CTRL-V"}, + {"t": "gv"}, + {"t": "v_o"}, + {"t": "v_b_I"}, + {"t": "v_b_A"}, + {"t": "v_P"}, + {"t": "gn"}, + {"t": "m"}, + {"t": "'"}, + {"t": "`"}, + {"t": "''"}, + {"t": "CTRL-O"}, + {"t": "CTRL-I"}, + {"t": "g;"}, + {"t": "quote"}, + {"t": "q"}, + {"t": "@"}, + {"t": "@@"}, + {"t": "Q"}, + {"t": "CTRL-D"}, + {"t": "CTRL-U"}, + {"t": "CTRL-F"}, + {"t": "CTRL-B"}, + {"t": "CTRL-E"}, + {"t": "CTRL-Y"}, + {"t": "zz"}, + {"t": "zt"}, + {"t": "zb"}, + {"t": "CTRL-W_s"}, + {"t": "CTRL-W_v"}, + {"t": "CTRL-W_w"}, + {"t": "CTRL-W_h"}, + {"t": "CTRL-W_j"}, + {"t": "CTRL-W_k"}, + {"t": "CTRL-W_l"}, + {"t": "CTRL-W_q"}, + {"t": "CTRL-W_o"}, + {"t": "CTRL-W_="}, + {"t": "za"}, + {"t": "zo"}, + {"t": "zc"}, + {"t": "zR"}, + {"t": "zM"}, + {"t": "z="}, + {"t": "]s"}, + {"t": "CTRL-]"}, + {"t": "CTRL-T"}, + {"t": "gf"}, + {"t": "gx"}, + {"t": "CTRL-^"}, + {"t": "ZZ"}, + {"t": "gt"}, + {"t": "CTRL-L"}, + {"t": "i_"}, + {"t": "i_CTRL-R"}, + {"t": "i_CTRL-W"}, + {"t": "i_CTRL-U"}, + {"t": "i_CTRL-O"}, + {"t": "i_CTRL-X_CTRL-F"}, + {"t": "i_CTRL-V"}, + {"t": "c_CTRL-R"}, + {"t": "c_"}, + {"t": "c_"}, + {"t": "c_CTRL-U"}, + {"t": "v_iw"}, + {"t": "v_aw"}, + {"t": "v_ip"}, + {"t": "v_ap"}, + {"t": "v_iquote"}, + {"t": "v_aquote"}, + {"t": "v_i'"}, + {"t": "v_i("}, + {"t": "v_a("}, + {"t": "v_iB"}, + {"t": "v_aB"}, + {"t": "v_i["}, + {"t": "v_it"}, + {"t": "v_at"}, + {"t": ":write"}, + {"t": ":quit"}, + {"t": ":wq"}, + {"t": ":xit"}, + {"t": ":edit"}, + {"t": ":substitute"}, + {"t": ":global"}, + {"t": ":normal"}, + {"t": ":sort"}, + {"t": ":earlier"}, + {"t": ":cdo"}, + {"t": ":buffer"}, + {"t": ":ls"}, + {"t": ":split"}, + {"t": ":vsplit"}, + {"t": ":tabnew"}, + {"t": ":nohlsearch"}, + {"t": ":registers"}, + {"t": ":marks"}, + {"t": ":help"}, + {"t": ":copen"}, + {"t": ":update"}, + {"t": ":wall"}, + {"t": ":qall"}, + {"t": ":move"}, + {"t": ":copy"}, + {"t": ":!"}, + {"t": ":source"}, + {"t": ":set"}, + {"t": ":verbose"}, + {"t": ":terminal"}, + {"t": ":checkhealth"} + ], + "builtin": { + "h": {"d": "Left N characters", "cat": "Motion"}, + "l": {"d": "Right N characters", "cat": "Motion"}, + "j": {"d": "Down N lines", "cat": "Motion", "n": "This config maps count-less j/k to gj/gk (display lines); 5j still moves 5 real lines"}, + "k": {"d": "Up N lines", "cat": "Motion"}, + "w": {"d": "Forward to start of next word", "cat": "Motion", "n": "word = letters, digits, underscore (or a run of other non-blanks); WORD = anything up to whitespace"}, + "W": {"d": "Forward to start of next WORD (blank-delimited)", "cat": "Motion", "n": "WORD = any run of non-blank characters, e.g. foo.bar(baz) is one WORD"}, + "b": {"d": "Back to start of word", "cat": "Motion"}, + "B": {"d": "Back to start of WORD", "cat": "Motion"}, + "e": {"d": "Forward to end of word", "cat": "Motion"}, + "E": {"d": "Forward to end of WORD", "cat": "Motion"}, + "ge": {"d": "Back to end of previous word", "cat": "Motion"}, + "gE": {"d": "Back to end of previous WORD", "cat": "Motion"}, + "0": {"d": "To first column of the line", "cat": "Motion"}, + "^": {"d": "To first non-blank character of the line", "cat": "Motion"}, + "$": {"d": "To end of line (with N: end of line N-1 lines down)", "cat": "Motion", "n": "In Visual block mode $ extends every line to its own end"}, + "g_": {"d": "To last non-blank character of the line (N-1 lines down)", "cat": "Motion"}, + "gg": {"d": "To first line, or to line N", "cat": "Motion"}, + "G": {"d": "To last line, or to line N", "cat": "Motion"}, + "N%": {"d": "To N percent through the file", "cat": "Motion"}, + "%": {"d": "Jump to the matching bracket, or to the next bracket on the line", "cat": "Motion", "n": "matchit (loaded by default) also jumps between if/else/endif, HTML tags and similar pairs; see g%, [%, ]%"}, + "{": {"d": "Back to previous blank line (paragraph)", "cat": "Motion"}, + "}": {"d": "Forward to next blank line (paragraph)", "cat": "Motion"}, + "(": {"d": "Back N sentences", "cat": "Motion"}, + ")": {"d": "Forward N sentences", "cat": "Motion"}, + "H": {"d": "To top line of the window (Nth line from top)", "cat": "Motion"}, + "M": {"d": "To middle line of the window", "cat": "Motion"}, + "L": {"d": "To bottom line of the window (Nth line from bottom)", "cat": "Motion"}, + "f": {"d": "To Nth {char} to the right on this line", "cat": "Motion"}, + "F": {"d": "To Nth {char} to the left on this line", "cat": "Motion"}, + "t": {"d": "Till before Nth {char} to the right", "cat": "Motion"}, + "T": {"d": "Till after Nth {char} to the left", "cat": "Motion"}, + ";": {"d": "Repeat last f/t/F/T in the same direction", "cat": "Motion"}, + ",": {"d": "Repeat last f/t/F/T in the opposite direction", "cat": "Motion"}, + "bar": {"d": "To column N of the line", "cat": "Motion"}, + "-": {"d": "Up N lines, on first non-blank", "cat": "Motion"}, + "+": {"d": "Down N lines, on first non-blank", "cat": "Motion"}, + "": {"d": "Down N lines, on first non-blank (same as +)", "cat": "Motion"}, + "CTRL-M": {"d": "Same as : down N lines, first non-blank", "cat": "Motion"}, + "_": {"d": "To first non-blank, N-1 lines down (linewise)", "cat": "Motion"}, + "gj": {"d": "Down N display (wrapped) lines", "cat": "Motion"}, + "gk": {"d": "Up N display (wrapped) lines", "cat": "Motion"}, + "g0": {"d": "To first character of the screen line", "cat": "Motion"}, + "g^": {"d": "To first non-blank of the screen line", "cat": "Motion"}, + "g$": {"d": "To last character of the screen line", "cat": "Motion"}, + "gm": {"d": "To middle of the screen width", "cat": "Motion"}, + "gM": {"d": "To middle of the text line (N percent with a count)", "cat": "Motion"}, + "go": {"d": "To byte N in the buffer", "cat": "Motion"}, + "": {"d": "Right N characters (same as l)", "cat": "Motion"}, + "": {"d": "Left N characters (same as h)", "cat": "Motion"}, + "CTRL-H": {"d": "Left N characters (same as h)", "cat": "Motion"}, + "CTRL-N": {"d": "Down N lines (same as j)", "cat": "Motion"}, + "CTRL-P": {"d": "Up N lines (same as k)", "cat": "Motion"}, + "CTRL-J": {"d": "Down N lines (same as j)", "cat": "Motion"}, + "": {"d": "Down N lines (same as j)", "cat": "Motion"}, + "[(": {"d": "Back to unmatched '('", "cat": "Motion"}, + "[{": {"d": "Back to unmatched '{' (start of block)", "cat": "Motion"}, + "])": {"d": "Forward to unmatched ')'", "cat": "Motion"}, + "]}": {"d": "Forward to unmatched '}' (end of block)", "cat": "Motion"}, + "[[": {"d": "Back to previous section ('{' in column 1)", "cat": "Motion"}, + "]]": {"d": "Forward to next section ('{' in column 1)", "cat": "Motion"}, + "[]": {"d": "Back to previous section end ('}' in column 1)", "cat": "Motion"}, + "][": {"d": "Forward to next section end ('}' in column 1)", "cat": "Motion"}, + "[m": {"d": "Back to start of a method (Java/C++ style class)", "cat": "Motion"}, + "]m": {"d": "Forward to start of the next method (Java/C++ style class)", "cat": "Motion"}, + "[/": {"d": "Back to start of a C comment /*", "cat": "Motion"}, + "]/": {"d": "Forward to end of a C comment */", "cat": "Motion"}, + "[#": {"d": "Back to unmatched #if / #else", "cat": "Motion"}, + "]#": {"d": "Forward to unmatched #else / #endif", "cat": "Motion"}, + "star": {"d": "Search forward for the whole word under the cursor", "cat": "Search", "n": "Whole-word match (\\<word\\>); 'ignorecase' applies, 'smartcase' does not. In Visual mode Neovim's default * searches for the selected text instead"}, + "#": {"d": "Search backward for the whole word under the cursor", "cat": "Search"}, + "gstar": {"d": "Like * but also matches inside longer words", "cat": "Search"}, + "g#": {"d": "Like # but also matches inside longer words", "cat": "Search"}, + "n": {"d": "Repeat last search in the same direction", "cat": "Search", "n": "n follows the direction of the last / or ?, so after ? it goes backward"}, + "N": {"d": "Repeat last search in the opposite direction", "cat": "Search", "n": "Opposite of the last search direction, not always 'backward'"}, + "/": {"d": "Search forward for {pattern}", "cat": "Search", "n": "Case-sensitive in this config ('ignorecase' off); add \\c to the pattern to ignore case. Search offsets: /foo/e end, /foo/+1 next line"}, + "?": {"d": "Search backward for {pattern}", "cat": "Search"}, + "/": {"d": "Search forward again with the last pattern", "cat": "Search"}, + "?": {"d": "Search backward again with the last pattern", "cat": "Search"}, + "gn": {"d": "Select next match of the last search (cgn + . changes them one by one)", "cat": "Search", "n": "Works as a text object: cgn changes the next match, then . repeats on the next one"}, + "gN": {"d": "Select previous match of the last search", "cat": "Search"}, + "gd": {"d": "Go to local declaration of the word (searches back in the function)", "cat": "Search", "n": "This config maps gd to the Snacks LSP definitions picker"}, + "gD": {"d": "Go to global declaration of the word (searches from top of file)", "cat": "Search", "n": "This config maps gD to the Snacks LSP declarations picker"}, + "CTRL-C": {"d": "Interrupt the current command or search", "cat": "Search"}, + "[I": {"d": "List lines containing the word under cursor (also in included files)", "cat": "Search"}, + "]I": {"d": "List lines containing the word under cursor, starting at the cursor", "cat": "Search"}, + "[i": {"d": "Show first line containing the word under cursor (incl. included files)", "cat": "Search"}, + "]i": {"d": "Show first line below the cursor containing the word", "cat": "Search"}, + "[_CTRL-I": {"d": "Jump to first line containing the word under cursor", "cat": "Search"}, + "]_CTRL-I": {"d": "Jump to first line below the cursor containing the word", "cat": "Search"}, + "[D": {"d": "List #defines matching the word under cursor", "cat": "Search", "n": "Neovim remaps this key to diagnostic navigation; the #define search is only reachable via :normal!"}, + "]D": {"d": "List #defines matching the word under cursor, starting at the cursor", "cat": "Search", "n": "Neovim remaps this key to diagnostic navigation; the #define search is only reachable via :normal!"}, + "[d": {"d": "Show first #define matching the word under cursor", "cat": "Search", "n": "Neovim remaps this key to diagnostic navigation; the #define search is only reachable via :normal!"}, + "]d": {"d": "Show first #define below the cursor matching the word", "cat": "Search", "n": "Neovim remaps this key to diagnostic navigation; the #define search is only reachable via :normal!"}, + "[_CTRL-D": {"d": "Jump to first #define matching the word under cursor", "cat": "Search"}, + "]_CTRL-D": {"d": "Jump to first #define below the cursor matching the word", "cat": "Search"}, + "": {"d": "Extend the selection to the mouse click", "cat": "Visual", "n": "Neovim's default 'mousemodel' is popup_setpos; only with extend does it search the clicked word like *"}, + "": {"d": "Search backward for the word at the mouse click (like #)", "cat": "Search"}, + "d": {"d": "Delete {motion} text [into register x]", "cat": "Operator", "n": "This config maps d, D, c, C, x, X to \"d…: deleted text goes to register d, not the clipboard. Paste it with \"dp"}, + "c": {"d": "Change: delete {motion} text and start Insert", "cat": "Operator", "n": "Deleted text goes to register d in this config (see d)"}, + "y": {"d": "Yank (copy) {motion} text [into register x]", "cat": "Operator", "n": "With 'clipboard' = unnamedplus, yanks go to the system clipboard (\"+)"}, + ">": {"d": "Indent {motion} lines one 'shiftwidth'", "cat": "Operator"}, + "<": {"d": "Dedent {motion} lines one 'shiftwidth'", "cat": "Operator"}, + "=": {"d": "Re-indent {motion} lines (indent rules or 'equalprg')", "cat": "Operator", "n": "Doubled operators act on lines: ==, dd, yy, gUU, gqq"}, + "!": {"d": "Filter {motion} lines through an external command", "cat": "Operator"}, + "gu": {"d": "Lowercase {motion} text", "cat": "Operator"}, + "gU": {"d": "Uppercase {motion} text", "cat": "Operator"}, + "g~": {"d": "Toggle case of {motion} text", "cat": "Operator"}, + "g?": {"d": "ROT13-encode {motion} text", "cat": "Operator"}, + "gq": {"d": "Format {motion} lines ('textwidth' / 'formatexpr'); cursor moves", "cat": "Operator", "n": "When an LSP attaches, Neovim sets 'formatexpr' to the LSP formatter if it was empty"}, + "gw": {"d": "Format {motion} lines and keep the cursor where it was", "cat": "Operator"}, + "g@": {"d": "Run 'operatorfunc' on {motion} (custom operators)", "cat": "Operator"}, + "o_v": {"d": "Force the operator to work charwise (e.g. dvj)", "cat": "Operator", "n": "Also toggles inclusive/exclusive for charwise motions: dve leaves the last char"}, + "o_V": {"d": "Force the operator to work linewise (e.g. dVw)", "cat": "Operator"}, + "o_CTRL-V": {"d": "Force the operator to work blockwise", "cat": "Operator"}, + "dd": {"d": "Delete N lines", "cat": "Edit"}, + "cc": {"d": "Change N whole lines", "cat": "Edit"}, + "yy": {"d": "Yank N lines", "cat": "Edit"}, + "D": {"d": "Delete to end of line (and N-1 more lines)", "cat": "Edit"}, + "C": {"d": "Change to end of line (and N-1 more lines)", "cat": "Edit"}, + "Y": {"d": "Yank N lines (Neovim maps Y to y$: yank to end of line)", "cat": "Edit", "n": "Neovim default mapping Y → y$ (like D and C); use yy for whole lines"}, + "p": {"d": "Put (paste) after the cursor N times", "cat": "Edit", "n": "With 'clipboard' = unnamedplus, p pastes the system clipboard. Deletes go to \"d in this config, so use \"dp to paste deleted text"}, + "P": {"d": "Put (paste) before the cursor N times", "cat": "Edit", "n": "Unlike p, P in Visual mode keeps the register, so you can paste repeatedly"}, + "gp": {"d": "Put after the cursor and leave the cursor after the pasted text", "cat": "Edit"}, + "gP": {"d": "Put before the cursor and leave the cursor after the pasted text", "cat": "Edit"}, + "]p": {"d": "Put after, adjusting indent to the current line", "cat": "Edit"}, + "[p": {"d": "Put before, adjusting indent to the current line", "cat": "Edit"}, + "]P": {"d": "Put before, adjusting indent (same as [p)", "cat": "Edit"}, + "[P": {"d": "Put before, adjusting indent (same as [p)", "cat": "Edit"}, + ">>": {"d": "Indent N lines one 'shiftwidth'", "cat": "Edit"}, + "<<": {"d": "Dedent N lines one 'shiftwidth'", "cat": "Edit"}, + "==": {"d": "Re-indent N lines", "cat": "Edit"}, + "!!": {"d": "Filter N lines through an external command", "cat": "Edit"}, + "x": {"d": "Delete N characters under and after the cursor", "cat": "Edit", "n": "Mapped to \"dx here: the character goes to register d"}, + "X": {"d": "Delete N characters before the cursor", "cat": "Edit"}, + "s": {"d": "Delete N characters and start Insert", "cat": "Edit"}, + "S": {"d": "Delete N lines and start Insert (same as cc)", "cat": "Edit"}, + "r": {"d": "Replace N characters with {char}", "cat": "Edit"}, + "R": {"d": "Enter Replace mode (overtype)", "cat": "Edit"}, + "gR": {"d": "Enter Virtual Replace mode (overtype screen cells, tabs stay intact)", "cat": "Edit"}, + "gr": {"d": "Virtual-replace N characters with {char}", "cat": "Edit", "n": "Neovim's grn/gra/grr/gri/grt/grx LSP maps extend gr, so it waits for 'timeoutlen' before acting"}, + "J": {"d": "Join N lines (default 2) with one space", "cat": "Edit", "n": "In Visual mode joins the selected lines"}, + "gJ": {"d": "Join lines without inserting or removing spaces", "cat": "Edit"}, + "CTRL-A": {"d": "Increment the number at or after the cursor by N", "cat": "Edit", "n": "Works on decimal, 0x hex, 0b binary and (with 'nrformats') letters; g<C-a> in Visual makes a sequence"}, + "CTRL-X": {"d": "Decrement the number at or after the cursor by N", "cat": "Edit"}, + "g?g?": {"d": "ROT13-encode the current line", "cat": "Edit"}, + "zp": {"d": "Put block without trailing spaces (after)", "cat": "Edit"}, + "zP": {"d": "Put block without trailing spaces (before)", "cat": "Edit"}, + "zy": {"d": "Yank {motion} without trailing spaces (block mode)", "cat": "Edit"}, + "": {"d": "Paste at the mouse position (like gP)", "cat": "Edit"}, + "": {"d": "Delete N characters under the cursor (same as x)", "cat": "Edit"}, + "i": {"d": "Insert before the cursor", "cat": "Insert", "n": "A count repeats the inserted text: 3ihey<Esc> → heyheyhey"}, + "a": {"d": "Append after the cursor", "cat": "Insert"}, + "I": {"d": "Insert at first non-blank of the line", "cat": "Insert"}, + "A": {"d": "Append at end of line", "cat": "Insert"}, + "gI": {"d": "Insert at column 1", "cat": "Insert", "n": "This config maps gI to the Snacks LSP implementations picker"}, + "gi": {"d": "Insert where Insert mode was last stopped", "cat": "Insert"}, + "o": {"d": "Open a new line below and start Insert", "cat": "Insert"}, + "O": {"d": "Open a new line above and start Insert", "cat": "Insert"}, + "": {"d": "Start Insert mode (same as i)", "cat": "Insert"}, + "u": {"d": "Undo the last change", "cat": "Undo & repeat", "n": "'undofile' is on in this config, so undo history survives closing the file"}, + "CTRL-R": {"d": "Redo the last undone change", "cat": "Undo & repeat"}, + "U": {"d": "Undo all recent changes on the last changed line (is itself undoable)", "cat": "Undo & repeat"}, + ".": {"d": "Repeat the last change (a count replaces the original count)", "cat": "Undo & repeat", "n": "Repeats inserts too: A;<Esc> then j. appends ; to the next line"}, + "&": {"d": "Repeat last :s on the current line (Neovim: same flags, :&&)", "cat": "Undo & repeat", "n": "Neovim default mapping & → :&&<CR> keeps the last flags (Vim's plain & drops them)"}, + "g&": {"d": "Repeat last :s on all lines with the same pattern and flags", "cat": "Undo & repeat"}, + "g-": {"d": "Go to older text state (walks undo branches by time)", "cat": "Undo & repeat"}, + "g+": {"d": "Go to newer text state (walks undo branches by time)", "cat": "Undo & repeat"}, + "": {"d": "Undo (same as u)", "cat": "Undo & repeat"}, + "@:": {"d": "Repeat the last command-line command N times", "cat": "Undo & repeat", "n": "After the first @:, @@ repeats it again"}, + "v": {"d": "Start charwise Visual mode", "cat": "Visual"}, + "V": {"d": "Start linewise Visual mode", "cat": "Visual"}, + "CTRL-V": {"d": "Start blockwise Visual mode", "cat": "Visual"}, + "gv": {"d": "Reselect the previous Visual area", "cat": "Visual"}, + "v_o": {"d": "Move the cursor to the other end of the selection", "cat": "Visual"}, + "v_O": {"d": "Block mode: move to the other corner on the same line", "cat": "Visual"}, + "v_b_I": {"d": "Block mode: insert the same text before the block on every line", "cat": "Visual", "n": "Type the text, then <Esc>: it appears on every line of the block"}, + "v_b_A": {"d": "Block mode: append the same text after the block on every line", "cat": "Visual"}, + "v_gv": {"d": "Swap the current and previous Visual areas", "cat": "Visual"}, + "v_v": {"d": "Switch to charwise Visual, or stop Visual mode", "cat": "Visual"}, + "v_V": {"d": "Switch to linewise Visual, or stop Visual mode", "cat": "Visual"}, + "v_CTRL-V": {"d": "Switch to blockwise Visual, or stop Visual mode", "cat": "Visual"}, + "v_": {"d": "Stop Visual mode", "cat": "Visual"}, + "v_CTRL-C": {"d": "Stop Visual mode", "cat": "Visual"}, + "v_CTRL-G": {"d": "Toggle between Visual and Select mode", "cat": "Visual"}, + "v_CTRL-O": {"d": "Select mode: switch to Visual mode for one command", "cat": "Visual"}, + "gh": {"d": "Start Select mode (charwise)", "cat": "Visual"}, + "gH": {"d": "Start Select mode (linewise)", "cat": "Visual"}, + "g_CTRL-H": {"d": "Start Select mode (blockwise)", "cat": "Visual"}, + "gV": {"d": "In a Select-mode mapping: don't reselect the area afterwards", "cat": "Visual"}, + "v_CTRL-\\_CTRL-N": {"d": "Stop Visual mode", "cat": "Visual"}, + "v_CTRL-\\_CTRL-G": {"d": "Stop Visual mode (go to Normal)", "cat": "Visual"}, + "": {"d": "Open the right-click popup menu (moves the cursor to the click)", "cat": "Misc", "n": "Neovim's default 'mousemodel' is popup_setpos; with extend it starts or extends Visual mode instead"}, + "v_c": {"d": "Change the selection (delete and start Insert)", "n": "Mapped to \"dc in this config (register d)"}, + "v_s": {"d": "Change the selection (same as c)"}, + "v_d": {"d": "Delete the selection", "n": "Mapped to \"dd in this config (register d)"}, + "v_x": {"d": "Delete the selection (same as d)", "n": "Mapped to \"dx in this config (register d)"}, + "v_y": {"d": "Yank the selection"}, + "v_C": {"d": "Change the selected lines (block: to end of each line)"}, + "v_S": {"d": "Change the selected lines"}, + "v_R": {"d": "Change the selected lines"}, + "v_D": {"d": "Delete the selected lines (block: to end of each line)"}, + "v_X": {"d": "Delete the selected lines"}, + "v_Y": {"d": "Yank the selected lines"}, + "v_p": {"d": "Replace the selection with the register (old text goes to the unnamed register)", "n": "The replaced text goes to the unnamed register, so a second p pastes the old text; use P to keep pasting the same thing"}, + "v_P": {"d": "Replace the selection with the register (registers unchanged)"}, + "v_r": {"d": "Replace every selected character with {char}"}, + "v_J": {"d": "Join the selected lines"}, + "v_gJ": {"d": "Join the selected lines without spaces"}, + "v_u": {"d": "Lowercase the selection"}, + "v_U": {"d": "Uppercase the selection"}, + "v_~": {"d": "Toggle case of the selection"}, + "v_>": {"d": "Indent the selected lines (N times)"}, + "v_<": {"d": "Dedent the selected lines (N times)"}, + "v_=": {"d": "Re-indent the selected lines"}, + "v_gq": {"d": "Format the selected lines"}, + "v_!": {"d": "Filter the selected lines through an external command"}, + "v_CTRL-A": {"d": "Increment numbers in the selection by N"}, + "v_CTRL-X": {"d": "Decrement numbers in the selection by N"}, + "v_g_CTRL-A": {"d": "Increment numbers progressively (+N, +2N, … per line)", "n": "Select a column of 0s and press g<C-a> to number them 1, 2, 3…"}, + "v_g_CTRL-X": {"d": "Decrement numbers progressively (-N, -2N, … per line)"}, + "v_:": {"d": "Start a command line with the range '<,'> of the selection", "cat": "Command line"}, + "v_": {"d": "Select mode: delete the selection"}, + "v_CTRL-H": {"d": "Select mode: delete the selection"}, + "m": {"d": "Set mark {a-zA-Z} at the cursor (A-Z are global, across files)", "cat": "Mark & jump", "n": "Special marks: . last change, ^ last insert, \" last exit, [ ] last change/yank, < > last selection"}, + "'": {"d": "Jump to the line of mark {a-zA-Z0-9} (first non-blank)", "cat": "Mark & jump"}, + "`": {"d": "Jump to the exact position of mark {a-zA-Z0-9}", "cat": "Mark & jump"}, + "''": {"d": "Jump back to the line before the latest jump", "cat": "Mark & jump"}, + "``": {"d": "Jump back to the position before the latest jump", "cat": "Mark & jump"}, + "CTRL-O": {"d": "Jump to an older position in the jump list", "cat": "Mark & jump", "n": "Jumps are made by G, /, n, %, (, {, marks, etc. — not by j/k or w"}, + "CTRL-I": {"d": "Jump to a newer position in the jump list", "cat": "Mark & jump", "n": "Shadowed here: this config maps <Tab> (Sidekick NES, no fallback) and <C-i> is not mapped separately, so per :h CTRL-I the <Tab> map also catches <C-i>, even in kitty or Neovide; use <leader>sj"}, + "": {"d": "Jump to a newer position in the jump list (same as CTRL-I)", "cat": "Mark & jump", "n": "This config maps Normal-mode <Tab> to Sidekick NES jump/apply (no fallback)"}, + "g;": {"d": "Go to an older position in the change list", "cat": "Mark & jump", "n": "Change list = positions of your edits; :changes lists them"}, + "g,": {"d": "Go to a newer position in the change list", "cat": "Mark & jump"}, + "`[": {"d": "To start of the last changed, yanked or put text", "cat": "Mark & jump"}, + "`]": {"d": "To end of the last changed, yanked or put text", "cat": "Mark & jump"}, + "'[": {"d": "To first line of the last changed, yanked or put text", "cat": "Mark & jump"}, + "']": {"d": "To last line of the last changed, yanked or put text", "cat": "Mark & jump"}, + "`<": {"d": "To start of the last Visual selection", "cat": "Mark & jump"}, + "`>": {"d": "To end of the last Visual selection", "cat": "Mark & jump"}, + "'<": {"d": "To first line of the last Visual selection", "cat": "Mark & jump"}, + "'>": {"d": "To last line of the last Visual selection", "cat": "Mark & jump"}, + "g'": {"d": "Jump to mark without adding to the jump list (line)", "cat": "Mark & jump"}, + "g`": {"d": "Jump to mark without adding to the jump list (exact position)", "cat": "Mark & jump"}, + "['": {"d": "To previous line with a lowercase mark", "cat": "Mark & jump"}, + "]'": {"d": "To next line with a lowercase mark", "cat": "Mark & jump"}, + "[`": {"d": "To previous lowercase mark", "cat": "Mark & jump"}, + "]`": {"d": "To next lowercase mark", "cat": "Mark & jump"}, + "quote": {"d": "Use register {x} for the next delete, yank or put", "cat": "Register & macro", "n": "\"\" unnamed, \"0 last yank, \"1-9 deletes, \"+ clipboard, \"_ black hole, \"/ search, \": last command. In this config d/c/x default to \"d but honour a register you type (except \"+)"}, + "@": {"d": "Run the macro in register {a-z} N times", "cat": "Register & macro", "n": "Macros are just registers: \"ap pastes one to edit, \"ayy stores it back"}, + "@@": {"d": "Repeat the last executed macro (or @:) N times", "cat": "Register & macro"}, + "Q": {"d": "Replay the last recorded macro N times", "cat": "Register & macro", "n": "Neovim: Q replays the last recorded register (Vim's Ex mode is gQ). In linewise Visual it runs on every selected line"}, + "CTRL-D": {"d": "Scroll half a screen down (N lines with a count)", "cat": "Scroll & view"}, + "CTRL-U": {"d": "Scroll half a screen up (N lines with a count)", "cat": "Scroll & view"}, + "CTRL-F": {"d": "Scroll one screen forward (page down)", "cat": "Scroll & view"}, + "CTRL-B": {"d": "Scroll one screen back (page up)", "cat": "Scroll & view"}, + "CTRL-E": {"d": "Scroll the view N lines down (text moves up, cursor stays)", "cat": "Scroll & view"}, + "CTRL-Y": {"d": "Scroll the view N lines up (text moves down, cursor stays)", "cat": "Scroll & view"}, + "zz": {"d": "Scroll so the cursor line is centered", "cat": "Scroll & view"}, + "zt": {"d": "Scroll so the cursor line is at the top", "cat": "Scroll & view"}, + "zb": {"d": "Scroll so the cursor line is at the bottom", "cat": "Scroll & view"}, + "z": {"d": "Cursor line to top of window, cursor on first non-blank", "cat": "Scroll & view"}, + "z.": {"d": "Cursor line to center of window, cursor on first non-blank", "cat": "Scroll & view"}, + "z-": {"d": "Cursor line to bottom of window, cursor on first non-blank", "cat": "Scroll & view"}, + "zh": {"d": "Scroll the view N characters left (nowrap only)", "cat": "Scroll & view"}, + "zl": {"d": "Scroll the view N characters right (nowrap only)", "cat": "Scroll & view"}, + "zH": {"d": "Scroll the view half a screen width left (nowrap only)", "cat": "Scroll & view"}, + "zL": {"d": "Scroll the view half a screen width right (nowrap only)", "cat": "Scroll & view"}, + "zs": {"d": "Scroll horizontally so the cursor is at the left edge (nowrap only)", "cat": "Scroll & view"}, + "ze": {"d": "Scroll horizontally so the cursor is at the right edge (nowrap only)", "cat": "Scroll & view"}, + "": {"d": "Scroll one screen forward (same as CTRL-F)", "cat": "Scroll & view"}, + "": {"d": "Scroll one screen back (same as CTRL-B)", "cat": "Scroll & view"}, + "i_": {"d": "Scroll one screen back", "cat": "Scroll & view"}, + "i_": {"d": "Scroll one screen forward", "cat": "Scroll & view"}, + "i_CTRL-X_CTRL-E": {"d": "Scroll the window one line down (like CTRL-E) in CTRL-X mode", "cat": "Scroll & view"}, + "i_CTRL-X_CTRL-Y": {"d": "Scroll the window one line up (like CTRL-Y) in CTRL-X mode", "cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "i_": {"cat": "Scroll & view"}, + "CTRL-W": {"d": "Window command prefix (see the keys)", "cat": "Window"}, + "CTRL-W_s": {"d": "Split the window horizontally", "cat": "Window"}, + "CTRL-W_S": {"d": "Split the window horizontally (same as s)", "cat": "Window"}, + "CTRL-W_v": {"d": "Split the window vertically", "cat": "Window"}, + "CTRL-W_w": {"d": "Go to the next window (wraps); with N: go to window N", "cat": "Window", "n": "Most CTRL-W keys also work with CTRL held for the second key: <C-w><C-w>"}, + "CTRL-W_W": {"d": "Go to the previous window (wraps)", "cat": "Window"}, + "CTRL-W_h": {"d": "Go to the window on the left", "cat": "Window"}, + "CTRL-W_j": {"d": "Go to the window below", "cat": "Window"}, + "CTRL-W_k": {"d": "Go to the window above", "cat": "Window"}, + "CTRL-W_l": {"d": "Go to the window on the right", "cat": "Window"}, + "CTRL-W_t": {"d": "Go to the top-left window", "cat": "Window"}, + "CTRL-W_b": {"d": "Go to the bottom-right window", "cat": "Window"}, + "CTRL-W_p": {"d": "Go to the previous (last accessed) window", "cat": "Window"}, + "CTRL-W_q": {"d": "Quit the window (like :quit; quits Neovim if it is the last)", "cat": "Window"}, + "CTRL-W_c": {"d": "Close the window (like :close; never closes the last one)", "cat": "Window", "n": "<C-w>c rather than <C-w><C-c> (CTRL-C cancels)"}, + "CTRL-W_o": {"d": "Close all other windows (like :only)", "cat": "Window"}, + "CTRL-W_=": {"d": "Make all windows (almost) the same size", "cat": "Window"}, + "CTRL-W__": {"d": "Maximize window height (or set it to N)", "cat": "Window"}, + "CTRL-W_bar": {"d": "Maximize window width (or set it to N columns)", "cat": "Window"}, + "CTRL-W_+": {"d": "Increase window height by N lines", "cat": "Window"}, + "CTRL-W_-": {"d": "Decrease window height by N lines", "cat": "Window"}, + "CTRL-W_>": {"d": "Increase window width by N columns", "cat": "Window"}, + "CTRL-W_<": {"d": "Decrease window width by N columns", "cat": "Window"}, + "CTRL-W_x": {"d": "Swap with the next window (or window N)", "cat": "Window"}, + "CTRL-W_r": {"d": "Rotate windows downwards / rightwards", "cat": "Window"}, + "CTRL-W_R": {"d": "Rotate windows upwards / leftwards", "cat": "Window"}, + "CTRL-W_H": {"d": "Move the window to the far left (full height)", "cat": "Window"}, + "CTRL-W_J": {"d": "Move the window to the very bottom (full width)", "cat": "Window"}, + "CTRL-W_K": {"d": "Move the window to the very top (full width)", "cat": "Window"}, + "CTRL-W_L": {"d": "Move the window to the far right (full height)", "cat": "Window"}, + "CTRL-W_T": {"d": "Move the window to a new tab page", "cat": "Window"}, + "CTRL-W_n": {"d": "Open a new window with an empty buffer", "cat": "Window"}, + "CTRL-W_f": {"d": "Split and edit the file name under the cursor", "cat": "Window"}, + "CTRL-W_F": {"d": "Split, edit the file under the cursor and go to its line number", "cat": "Window"}, + "CTRL-W_gf": {"d": "Edit the file name under the cursor in a new tab", "cat": "Window"}, + "CTRL-W_]": {"d": "Split and jump to the tag under the cursor", "cat": "Window"}, + "CTRL-W_}": {"d": "Show the tag under the cursor in the preview window", "cat": "Window"}, + "CTRL-W_z": {"d": "Close the preview window", "cat": "Window"}, + "CTRL-W_P": {"d": "Go to the preview window", "cat": "Window"}, + "CTRL-W_^": {"d": "Split and edit the alternate file (or buffer N)", "cat": "Window"}, + "CTRL-W_d": {"d": "Split and jump to the #define of the word under the cursor", "cat": "Window", "n": "Neovim remaps <C-w>d / <C-w><C-d> to show diagnostics in a float"}, + "CTRL-W_i": {"d": "Split and jump to the declaration of the word under the cursor", "cat": "Window"}, + "CTRL-W_CTRL-C": {"d": "Does nothing (cancels a pending CTRL-W)", "cat": "Window"}, + "za": {"d": "Toggle the fold under the cursor", "cat": "Fold"}, + "zA": {"d": "Toggle the fold under the cursor recursively", "cat": "Fold"}, + "zo": {"d": "Open the fold under the cursor", "cat": "Fold"}, + "zO": {"d": "Open all folds under the cursor recursively", "cat": "Fold"}, + "zc": {"d": "Close the fold under the cursor", "cat": "Fold"}, + "zC": {"d": "Close all folds under the cursor recursively", "cat": "Fold"}, + "zR": {"d": "Open all folds (set 'foldlevel' to the deepest)", "cat": "Fold"}, + "zM": {"d": "Close all folds (set 'foldlevel' to 0)", "cat": "Fold"}, + "zr": {"d": "Open one more fold level everywhere ('foldlevel' + 1)", "cat": "Fold"}, + "zm": {"d": "Close one more fold level everywhere ('foldlevel' - 1)", "cat": "Fold"}, + "zv": {"d": "Open just enough folds to show the cursor line", "cat": "Fold"}, + "zx": {"d": "Reset folds to 'foldlevel', then show the cursor line", "cat": "Fold"}, + "zX": {"d": "Reset folds to 'foldlevel'", "cat": "Fold"}, + "zj": {"d": "Move down to the start of the next fold", "cat": "Fold"}, + "zk": {"d": "Move up to the end of the previous fold", "cat": "Fold"}, + "[z": {"d": "Move to the start of the current open fold", "cat": "Fold"}, + "]z": {"d": "Move to the end of the current open fold", "cat": "Fold"}, + "zf": {"d": "Create a fold over {motion} (manual / marker folding)", "cat": "Fold"}, + "zF": {"d": "Create a fold for N lines", "cat": "Fold"}, + "zd": {"d": "Delete the fold under the cursor", "cat": "Fold"}, + "zD": {"d": "Delete folds under the cursor recursively", "cat": "Fold"}, + "zE": {"d": "Delete all folds in the window", "cat": "Fold"}, + "zi": {"d": "Toggle folding on/off ('foldenable')", "cat": "Fold"}, + "zn": {"d": "Disable folding (open everything)", "cat": "Fold"}, + "zN": {"d": "Re-enable folding", "cat": "Fold"}, + "z=": {"d": "Spelling suggestions for the word under the cursor", "cat": "Spell", "n": "Needs 'spell' on (:set spell); a count picks suggestion N directly"}, + "zg": {"d": "Add the word to the spell file as correct", "cat": "Spell"}, + "zw": {"d": "Add the word to the spell file as wrong", "cat": "Spell"}, + "zG": {"d": "Mark the word correct for this session only", "cat": "Spell"}, + "zW": {"d": "Mark the word wrong for this session only", "cat": "Spell"}, + "zug": {"d": "Undo zg (remove the word from the spell file)", "cat": "Spell"}, + "zuw": {"d": "Undo zw", "cat": "Spell"}, + "zuG": {"d": "Undo zG", "cat": "Spell"}, + "zuW": {"d": "Undo zW", "cat": "Spell"}, + "]s": {"d": "Move to the next misspelled word", "cat": "Spell"}, + "[s": {"d": "Move to the previous misspelled word", "cat": "Spell"}, + "do": {"d": "Diff: obtain the change from the other window (:diffget)", "cat": "Diff"}, + "dp": {"d": "Diff: put the change into the other window (:diffput)", "cat": "Diff"}, + "]c": {"d": "Diff: next change", "cat": "Diff", "n": "Not remapped here: gitsigns is set up without keymaps, so ]c/[c only move between diff changes"}, + "[c": {"d": "Diff: previous change", "cat": "Diff"}, + "CTRL-]": {"d": "Jump to the definition (tag) of the word under the cursor", "cat": "Tags", "n": "When an LSP client attaches, Neovim sets 'tagfunc', so <C-]> goes to the LSP definition and <C-t> comes back"}, + "CTRL-T": {"d": "Jump back up the tag stack", "cat": "Tags"}, + "g]": {"d": "List all tags matching the word under the cursor (:tselect)", "cat": "Tags"}, + "g_CTRL-]": {"d": "Jump to the tag, or list matches if there are several (:tjump)", "cat": "Tags"}, + "v_CTRL-]": {"d": "Jump to the tag of the selected text", "cat": "Tags"}, + "": {"d": "Jump to the tag of the word at the mouse click", "cat": "Tags"}, + "": {"d": "Jump back up the tag stack (same as CTRL-T)", "cat": "Tags"}, + "gf": {"d": "Edit the file whose name is under the cursor", "cat": "Files"}, + "gF": {"d": "Edit the file under the cursor and jump to the line number after it", "cat": "Files"}, + "gx": {"d": "Open the file path or URL under the cursor with the system handler", "cat": "Files", "n": "Neovim maps gx (Normal and Visual) to vim.ui.open(): URLs, paths, LSP document links; in help files, help tags"}, + "CTRL-G": {"d": "Show the file name, line count and cursor position", "cat": "Files"}, + "g_CTRL-G": {"d": "Show cursor position plus word, character and byte counts", "cat": "Files"}, + "ZZ": {"d": "Write if changed and close the window (like :x)", "cat": "Files"}, + "ZQ": {"d": "Close the window without writing (like :q!)", "cat": "Files"}, + "CTRL-Z": {"d": "Suspend Neovim (resume with fg in the shell)", "cat": "Files"}, + "[f": {"d": "Edit the file under the cursor (same as gf)", "cat": "Files"}, + "]f": {"d": "Edit the file under the cursor (same as gf)", "cat": "Files"}, + "CTRL-^": {"d": "Switch to the alternate (previous) buffer; with N: buffer N", "cat": "Buffer & tab", "n": "The quickest way to toggle between two files"}, + "gt": {"d": "Go to the next tab page (with N: tab page N)", "cat": "Buffer & tab"}, + "gT": {"d": "Go to the previous tab page", "cat": "Buffer & tab"}, + "g": {"d": "Go to the last accessed tab page", "cat": "Buffer & tab"}, + "CTRL-": {"d": "Go to the last accessed tab page", "cat": "Buffer & tab"}, + "": {"d": "Go to the last accessed tab page", "cat": "Buffer & tab"}, + "K": {"d": "Look up the keyword under the cursor with 'keywordprg'", "cat": "Help", "n": "This config maps K to LSP hover"}, + "v_K": {"d": "Look up the selected text with 'keywordprg'", "cat": "Help"}, + "": {"d": "Open the help window", "cat": "Help"}, + "": {"d": "Open the help window", "cat": "Help"}, + "i_": {"d": "Leave Insert mode and open the help window", "cat": "Help"}, + "CTRL-L": {"d": "Redraw the screen (Neovim default: also clears search highlight)", "cat": "Search", "n": "Neovim default mapping: :nohlsearch | diffupdate | normal! <C-L>"}, + "ga": {"d": "Show the character code under the cursor (decimal, hex, octal)", "cat": "Help", "n": "This config's gai/gao (LSP calls) make ga wait for 'timeoutlen'"}, + "g8": {"d": "Show the UTF-8 bytes of the character under the cursor", "cat": "Help"}, + "g<": {"d": "Show the output of the previous command again", "cat": "Misc"}, + "gs": {"d": "Sleep N seconds (default 1)", "cat": "Misc"}, + "count": {"d": "Count: repeat or scale the next command (3w, 5dd, 2p)", "cat": "Misc"}, + "N": {"d": "While typing a count: remove its last digit", "cat": "Misc"}, + "CTRL-\\_CTRL-N": {"d": "Go to Normal mode (no-op in Normal mode; useful in mappings)", "cat": "Misc"}, + "CTRL-\\_CTRL-G": {"d": "Go to Normal mode (no-op in Normal mode)", "cat": "Misc"}, + "[": {"d": "Square-bracket command prefix ([( [{ [s [c [d …)", "cat": "Misc"}, + "]": {"d": "Square-bracket command prefix (]) ]} ]s ]c ]d …)", "cat": "Misc"}, + "g": {"d": "Extended command prefix (gg, gu, gq, gv, gx …)", "cat": "Misc"}, + "z": {"d": "Fold / scroll / spell command prefix (za, zz, z= …)", "cat": "Misc"}, + ":": {"d": "Start an Ex command (command-line mode)", "cat": "Command line"}, + "N:": {"d": "Start an Ex command with the range .,.+(N-1)", "cat": "Command line", "n": "5: pre-fills :.,.+4"}, + "q:": {"d": "Open the command-line window with : history (edit and re-run)", "cat": "Command line", "n": "In this config <CR> in Normal mode starts Leap here; press i then <CR> to run the line"}, + "q/": {"d": "Open the command-line window with / search history", "cat": "Command line"}, + "q?": {"d": "Open the command-line window with ? search history", "cat": "Command line"}, + "gQ": {"d": "Enter Ex mode (keep entering : commands; :vi to return)", "cat": "Command line"}, + "": {"d": "Move the cursor to the mouse click", "cat": "Motion"}, + "v_iw": {"d": "Inner word", "cat": "Text object", "n": "Text objects work after an operator (diw, ci\", yap) or in Visual mode; i = inner, a = around"}, + "v_aw": {"d": "A word, plus surrounding white space", "cat": "Text object", "n": "daw deletes the word and one space, so the sentence stays tidy"}, + "v_iW": {"d": "Inner WORD", "cat": "Text object"}, + "v_aW": {"d": "A WORD, plus surrounding white space", "cat": "Text object"}, + "v_is": {"d": "Inner sentence", "cat": "Text object"}, + "v_as": {"d": "A sentence, plus white space", "cat": "Text object"}, + "v_ip": {"d": "Inner paragraph", "cat": "Text object"}, + "v_ap": {"d": "A paragraph, plus the following blank lines", "cat": "Text object"}, + "v_iquote": {"d": "Inside double quotes", "cat": "Text object"}, + "v_aquote": {"d": "Double-quoted string including the quotes", "cat": "Text object"}, + "v_i'": {"d": "Inside single quotes", "cat": "Text object"}, + "v_a'": {"d": "Single-quoted string including the quotes", "cat": "Text object"}, + "v_i`": {"d": "Inside backticks", "cat": "Text object"}, + "v_a`": {"d": "Backtick string including the backticks", "cat": "Text object"}, + "v_ib": {"d": "Inside ( ) parentheses", "cat": "Text object"}, + "v_ab": {"d": "( ) block including the parentheses", "cat": "Text object"}, + "v_i(": {"d": "Inside ( ) parentheses (same as ib)", "cat": "Text object"}, + "v_i)": {"d": "Inside ( ) parentheses (same as ib)", "cat": "Text object"}, + "v_a(": {"d": "( ) block including the parentheses (same as ab)", "cat": "Text object"}, + "v_a)": {"d": "( ) block including the parentheses (same as ab)", "cat": "Text object"}, + "v_iB": {"d": "Inside { } braces", "cat": "Text object"}, + "v_aB": {"d": "{ } block including the braces", "cat": "Text object"}, + "v_i{": {"d": "Inside { } braces (same as iB)", "cat": "Text object"}, + "v_i}": {"d": "Inside { } braces (same as iB)", "cat": "Text object"}, + "v_a{": {"d": "{ } block including the braces (same as aB)", "cat": "Text object"}, + "v_a}": {"d": "{ } block including the braces (same as aB)", "cat": "Text object"}, + "v_i[": {"d": "Inside [ ] brackets", "cat": "Text object"}, + "v_i]": {"d": "Inside [ ] brackets", "cat": "Text object"}, + "v_a[": {"d": "[ ] block including the brackets", "cat": "Text object"}, + "v_a]": {"d": "[ ] block including the brackets", "cat": "Text object"}, + "v_i<": {"d": "Inside < > angle brackets", "cat": "Text object"}, + "v_i>": {"d": "Inside < > angle brackets", "cat": "Text object"}, + "v_a<": {"d": "< > block including the angle brackets", "cat": "Text object"}, + "v_a>": {"d": "< > block including the angle brackets", "cat": "Text object"}, + "v_it": {"d": "Inside an XML/HTML tag pair", "cat": "Text object"}, + "v_at": {"d": "XML/HTML tag pair including the tags", "cat": "Text object"}, + "i_": {"d": "Leave Insert mode", "cat": "Insert"}, + "i_CTRL-[": {"d": "Leave Insert mode (same as )", "cat": "Insert"}, + "i_CTRL-C": {"d": "Leave Insert mode without abbreviations or InsertLeave", "cat": "Insert", "n": "Skips InsertLeave autocmds, which plugins may rely on; prefer <Esc>"}, + "i_CTRL-R": {"d": "Insert the contents of register {reg}", "cat": "Insert", "n": "<C-r>+ clipboard, <C-r>\" unnamed, <C-r>= expression (e.g. =5*7), <C-r>/ last search"}, + "i_CTRL-R_CTRL-R": {"d": "Insert register literally (keys like in it are not applied)", "cat": "Insert"}, + "i_CTRL-R_CTRL-O": {"d": "Insert register literally, without auto-indent", "cat": "Insert"}, + "i_CTRL-R_CTRL-P": {"d": "Insert register literally and fix the indent", "cat": "Insert"}, + "i_CTRL-W": {"d": "Delete the word before the cursor", "cat": "Insert", "n": "Neovim default: sets an undo point first, so u can bring the text back"}, + "i_CTRL-U": {"d": "Delete text typed before the cursor (if none, all text before it)", "cat": "Insert", "n": "Neovim default: sets an undo point first, so u can bring the text back"}, + "i_CTRL-H": {"d": "Delete the character before the cursor (same as )", "cat": "Insert"}, + "i_": {"d": "Delete the character before the cursor", "cat": "Insert"}, + "i_": {"d": "Delete the character under the cursor", "cat": "Insert"}, + "i_CTRL-O": {"d": "Run one Normal-mode command, then return to Insert", "cat": "Insert", "n": "Handy for one-off moves: <C-o>zz, <C-o>$, <C-o>p"}, + "i_CTRL-T": {"d": "Indent the current line one 'shiftwidth'", "cat": "Insert"}, + "i_CTRL-D": {"d": "Dedent the current line one 'shiftwidth'", "cat": "Insert"}, + "i_0_CTRL-D": {"d": "Delete all indent of the current line", "cat": "Insert"}, + "i_CTRL-A": {"d": "Insert the text from the last Insert session", "cat": "Insert"}, + "i_CTRL-@": {"d": "Insert the text from the last Insert session and leave Insert", "cat": "Insert"}, + "i_CTRL-E": {"d": "Insert the character below the cursor", "cat": "Insert"}, + "i_CTRL-Y": {"d": "Insert the character above the cursor", "cat": "Insert"}, + "i_CTRL-V": {"d": "Insert the next key literally (e.g. a real )", "cat": "Insert"}, + "i_CTRL-Q": {"d": "Insert the next key literally (same as CTRL-V)", "cat": "Insert"}, + "i_CTRL-V_digit": {"d": "Insert a character by code: 065, x41, u20ac, o101", "cat": "Insert"}, + "i_CTRL-K": {"d": "Insert a digraph (e.g. e: → ë, Eu → €)", "cat": "Insert"}, + "i_CTRL-G_u": {"d": "Start a new undoable change (undo break)", "cat": "Insert"}, + "i_CTRL-G_U": {"d": "Don't start a new undo step for the next cursor movement", "cat": "Insert"}, + "i_CTRL-G_j": {"d": "Down a line, to the column where Insert started", "cat": "Insert"}, + "i_CTRL-G_k": {"d": "Up a line, to the column where Insert started", "cat": "Insert"}, + "i_CTRL-]": {"d": "Expand an abbreviation without typing a character", "cat": "Insert"}, + "i_": {"d": "Toggle Insert / Replace mode", "cat": "Insert"}, + "i_CTRL-\\_CTRL-N": {"d": "Go to Normal mode", "cat": "Insert"}, + "i_CTRL-\\_CTRL-G": {"d": "Go to Normal mode", "cat": "Insert"}, + "i_": {"d": "Insert a tab (or spaces with 'expandtab')", "cat": "Insert", "n": "In buffers where blink.cmp is attached, <Tab> tries in order: next snippet placeholder, Sidekick next-edit suggestion, accept Copilot ghost text, else a normal Tab (lua/plugins.lua:331-340)"}, + "i_CTRL-I": {"d": "Insert a tab (same as )", "cat": "Insert"}, + "i_": {"d": "Start a new line", "cat": "Insert"}, + "i_CTRL-M": {"d": "Start a new line (same as )", "cat": "Insert"}, + "i_CTRL-J": {"d": "Start a new line (same as )", "cat": "Insert"}, + "i_": {"d": "Start a new line (same as )", "cat": "Insert"}, + "i_CTRL-N": {"d": "Complete keyword: next match (sources from 'complete')", "cat": "Completion & AI", "n": "With blink.cmp's menu closed, <C-n>/<C-p> open blink's menu instead (first / last item selected). <C-x><C-n> still runs built-in keyword completion"}, + "i_CTRL-P": {"d": "Complete keyword: previous match", "cat": "Completion & AI"}, + "i_CTRL-X": {"d": "Enter CTRL-X completion mode (next key picks the source)", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-F": {"d": "Complete file names", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-L": {"d": "Complete whole lines", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-O": {"d": "Omni completion ('omnifunc'; LSP when a client is attached)", "cat": "Completion & AI", "n": "Neovim sets 'omnifunc' to vim.lsp.omnifunc when an LSP attaches"}, + "i_CTRL-X_CTRL-N": {"d": "Complete keywords from the current buffer (forward)", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-P": {"d": "Complete keywords from the current buffer (backward)", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-I": {"d": "Complete keywords from current and included files", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-D": {"d": "Complete defined identifiers (#define)", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-K": {"d": "Complete words from 'dictionary'", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-T": {"d": "Complete words from 'thesaurus'", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-]": {"d": "Complete tags", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-U": {"d": "Complete with 'completefunc'", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-V": {"d": "Complete Vim command-line text", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-R": {"d": "Complete from register contents", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-S": {"d": "Spelling suggestions", "cat": "Completion & AI"}, + "i_CTRL-X_s": {"d": "Spelling suggestions", "cat": "Completion & AI"}, + "i_CTRL-X_CTRL-Z": {"d": "Stop completion, keep the typed text", "cat": "Completion & AI"}, + "complete_CTRL-Y": {"d": "Accept the selected completion", "cat": "Completion & AI"}, + "complete_CTRL-E": {"d": "Cancel completion, restore the text", "cat": "Completion & AI"}, + "c_CTRL-R": {"d": "Insert a register; word, WORD, line", "cat": "Command line", "n": ":%s/<C-r><C-w>/ pre-fills a substitute for the word under the cursor"}, + "c_CTRL-R_CTRL-O": {"d": "Insert a register or cursor object literally", "cat": "Command line"}, + "c_": {"d": "Complete the command or argument before the cursor", "cat": "Command line", "n": "In this config blink.cmp handles command-line completion: <Tab> opens its menu and inserts the first match (accepts a lone match), then cycles; <C-n>/<C-p> move, <C-y> accepts"}, + "c_CTRL-I": {"d": "Complete before the cursor (same as )", "cat": "Command line"}, + "c_": {"d": "Previous completion match", "cat": "Command line"}, + "c_wildchar": {"d": "Complete before the cursor ('wildchar', )", "cat": "Command line"}, + "c_CTRL-N": {"d": "Next completion match, else next (newer) history entry", "cat": "Command line"}, + "c_CTRL-P": {"d": "Previous completion match, else older history entry", "cat": "Command line"}, + "c_": {"d": "Older history entry starting with the typed text", "cat": "Command line"}, + "c_": {"d": "Newer history entry starting with the typed text", "cat": "Command line"}, + "c_": {"d": "Older history entry", "cat": "Command line"}, + "c_": {"d": "Newer history entry", "cat": "Command line"}, + "c_CTRL-U": {"d": "Delete everything before the cursor", "cat": "Command line"}, + "c_CTRL-W": {"d": "Delete the word before the cursor", "cat": "Command line"}, + "c_CTRL-B": {"d": "Cursor to start of the command line", "cat": "Command line"}, + "c_CTRL-E": {"d": "Cursor to end of the command line", "cat": "Command line"}, + "c_CTRL-D": {"d": "List completions matching the text before the cursor", "cat": "Command line"}, + "c_CTRL-A": {"d": "Insert all completion matches", "cat": "Command line"}, + "c_CTRL-L": {"d": "Insert the longest common part of the matches", "cat": "Command line"}, + "c_CTRL-G": {"d": "While typing a search: go to the next match ('incsearch')", "cat": "Command line"}, + "c_CTRL-T": {"d": "While typing a search: go to the previous match ('incsearch')", "cat": "Command line"}, + "c_CTRL-V": {"d": "Insert the next key literally (or a character code)", "cat": "Command line"}, + "c_CTRL-K": {"d": "Insert a digraph", "cat": "Command line"}, + "c_CTRL-\\_e": {"d": "Replace the command line with the result of {expr}", "cat": "Command line"}, + "c_": {"d": "Abandon the command line", "cat": "Command line"}, + "c_CTRL-C": {"d": "Abandon the command line", "cat": "Command line"}, + "c_": {"d": "Execute the command", "cat": "Command line"}, + "c_CTRL-]": {"d": "Expand an abbreviation", "cat": "Command line"}, + ":buffer": {"cat": "Buffer & tab", "d": "Go to buffer N, or by (partial) name with Tab completion"}, + ":bNext": {"cat": "Buffer & tab"}, + ":ball": {"cat": "Buffer & tab"}, + ":badd": {"cat": "Buffer & tab"}, + ":balt": {"cat": "Buffer & tab"}, + ":bdelete": {"cat": "Buffer & tab", "d": "Unload a buffer and remove it from the list (closes its windows)", "n": "This config's <leader>bd uses Snacks.bufdelete, which keeps the window layout"}, + ":bfirst": {"cat": "Buffer & tab", "d": "Go to the first buffer"}, + ":blast": {"cat": "Buffer & tab", "d": "Go to the last buffer"}, + ":bmodified": {"cat": "Buffer & tab"}, + ":bnext": {"cat": "Buffer & tab", "d": "Go to the next buffer"}, + ":bprevious": {"cat": "Buffer & tab", "d": "Go to the previous buffer"}, + ":brewind": {"cat": "Buffer & tab"}, + ":bufdo": {"cat": "Buffer & tab", "d": "Run a command in every listed buffer"}, + ":buffers": {"cat": "Buffer & tab", "d": "List buffers (same as :ls)"}, + ":bunload": {"cat": "Buffer & tab"}, + ":bwipeout": {"cat": "Buffer & tab", "d": "Wipe a buffer completely (marks, options, everything)"}, + ":files": {"cat": "Buffer & tab", "d": "List buffers (same as :ls)"}, + ":ls": {"cat": "Buffer & tab", "d": "List buffers (% current, # alternate, + modified)"}, + ":sbuffer": {"cat": "Buffer & tab"}, + ":sbNext": {"cat": "Buffer & tab"}, + ":sball": {"cat": "Buffer & tab"}, + ":sbfirst": {"cat": "Buffer & tab"}, + ":sblast": {"cat": "Buffer & tab"}, + ":sbmodified": {"cat": "Buffer & tab"}, + ":sbnext": {"cat": "Buffer & tab"}, + ":sbprevious": {"cat": "Buffer & tab"}, + ":sbrewind": {"cat": "Buffer & tab"}, + ":unhide": {"cat": "Buffer & tab"}, + ":sunhide": {"cat": "Buffer & tab"}, + ":marks": {"cat": "Mark & jump", "d": "List marks"}, + ":jumps": {"cat": "Mark & jump", "d": "Show the jump list"}, + ":changes": {"cat": "Mark & jump", "d": "Show the change list"}, + ":k": {"cat": "Mark & jump"}, + ":mark": {"cat": "Mark & jump"}, + ":delmarks": {"cat": "Mark & jump", "d": "Delete marks (:delm a b, :delm! for all lowercase)"}, + ":clearjumps": {"cat": "Mark & jump", "d": "Clear the jump list"}, + ":keepjumps": {"cat": "Mark & jump", "d": "Run a command without changing the jump list"}, + ":keepmarks": {"cat": "Mark & jump"}, + ":lockmarks": {"cat": "Mark & jump"}, + ":registers": {"cat": "Register & macro", "d": "Show register contents (:reg a b for specific ones)", "n": "Register d holds deletes in this config"}, + ":display": {"cat": "Register & macro", "d": "Show register contents (same as :registers)"}, + ":@": {"cat": "Register & macro"}, + ":@@": {"cat": "Register & macro"}, + ":global": {"cat": "Edit", "d": "Run an Ex command on every line matching {pattern}: :g/pat/cmd", "n": ":g/TODO/d deletes matching lines; :g/^/m0 reverses the file"}, + ":vglobal": {"cat": "Edit", "d": "Run an Ex command on every line NOT matching {pattern}: :v/pat/cmd"}, + ":normal": {"cat": "Edit", "d": "Run Normal-mode keys, on each line of [range]: :%norm A;", "n": "Use :normal! to ignore mappings (this config remaps d, c, x, j, k, s …)"}, + ":read": {"cat": "Files", "d": "Insert {file} (or :r !cmd output) below the cursor"}, + ":terminal": {"cat": "Terminal", "d": "Open a terminal buffer"}, + ":source": {"cat": "Misc", "d": "Run a Vim script or Lua file (:so % runs the current file)"}, + ":runtime": {"cat": "Misc"}, + ":packadd": {"cat": "Misc"}, + ":packloadall": {"cat": "Misc"}, + ":scriptnames": {"cat": "Misc"}, + ":scriptencoding": {"cat": "Misc"}, + ":finish": {"cat": "Misc"}, + ":debug": {"cat": "Misc"}, + ":debuggreedy": {"cat": "Misc"}, + ":breakadd": {"cat": "Misc"}, + ":breakdel": {"cat": "Misc"}, + ":breaklist": {"cat": "Misc"}, + ":profile": {"cat": "Misc"}, + ":profdel": {"cat": "Misc"}, + ":write": {"d": "Write the buffer to its file (or to {file})"}, + ":quit": {"d": "Close the window; exits Neovim when it is the last one"}, + ":wq": {"d": "Write and close the window"}, + ":xit": {"d": "Write only if changed, then close the window"}, + ":edit": {"d": "Edit {file}; with no argument, reload the current file"}, + ":enew": {"d": "Edit a new unnamed buffer"}, + ":substitute": {"d": "Find & replace: :[range]s/{pattern}/{string}/[flags]", "n": "Flags: g all on line, c confirm, i/I case, n count only. & in {string} = whole match, \\1 = group"}, + ":sort": {"d": "Sort lines in [range] (n numeric, i ignore case, u unique, r /pat/)"}, + ":uniq": {"d": "Remove adjacent duplicate lines in [range]"}, + ":earlier": {"d": "Go back in undo time: :earlier 10m, :earlier 3f (file writes)", "n": "'undofile' is on in this config, so this reaches back across sessions"}, + ":later": {"d": "Go forward in undo time (counterpart of :earlier)"}, + ":undolist": {"d": "List the leaves of the undo tree"}, + ":make": {"d": "Run 'makeprg' and load errors into the quickfix list"}, + ":cdo": {"d": "Run a command on each quickfix entry: :cdo s/a/b/ | update"}, + ":cfdo": {"d": "Run a command once per file in the quickfix list"}, + ":ldo": {"d": "Run a command on each location-list entry"}, + ":lfdo": {"d": "Run a command once per file in the location list"}, + ":windo": {"d": "Run a command in every window"}, + ":argdo": {"d": "Run a command on every file in the argument list"}, + ":split": {"d": "Split the window horizontally (optionally editing {file})"}, + ":vsplit": {"d": "Split the window vertically (optionally editing {file})"}, + ":new": {"d": "Open a new window with an empty buffer"}, + ":vnew": {"d": "Open a new vertical window with an empty buffer"}, + ":only": {"d": "Close all other windows"}, + ":close": {"d": "Close the current window"}, + ":tabnew": {"d": "Open a new tab page (optionally editing {file})"}, + ":tabedit": {"d": "Edit {file} in a new tab page"}, + ":tabclose": {"d": "Close the current tab page"}, + ":tabonly": {"d": "Close all other tab pages"}, + ":tabnext": {"d": "Go to the next tab page"}, + ":tabprevious": {"d": "Go to the previous tab page"}, + ":tabmove": {"d": "Move the tab page to position N (or +N / -N)"}, + ":nohlsearch": {"d": "Clear search highlighting until the next search"}, + ":help": {"d": "Open help for {subject} (:h i_CTRL-R, :h :s, :h 'wrap')"}, + ":copen": {"d": "Open the quickfix window"}, + ":cclose": {"d": "Close the quickfix window"}, + ":cwindow": {"d": "Open the quickfix window only if there are entries"}, + ":cnext": {"d": "Go to the next quickfix entry"}, + ":cprevious": {"d": "Go to the previous quickfix entry"}, + ":colder": {"d": "Go to the older quickfix list"}, + ":cnewer": {"d": "Go to the newer quickfix list"}, + ":vimgrep": {"d": "Search files into the quickfix list: :vim /pat/ **/*.lua"}, + ":grep": {"d": "Run 'grepprg' and load matches into the quickfix list"}, + ":saveas": {"d": "Save to {file} and make it the current file"}, + ":update": {"d": "Write only if the buffer was modified"}, + ":wall": {"d": "Write all modified buffers"}, + ":qall": {"d": "Quit Neovim (:qa! discards all changes)"}, + ":wqall": {"d": "Write all modified buffers and quit"}, + ":xall": {"d": "Write all modified buffers and quit"}, + ":move": {"d": "Move [range] lines below line {address}: :m+1, :m0"}, + ":copy": {"d": "Copy [range] lines below line {address}"}, + ":t": {"d": "Copy lines (same as :copy): :t. duplicates a line"}, + ":delete": {"d": "Delete [range] lines [into register]"}, + ":yank": {"d": "Yank [range] lines [into register]"}, + ":put": {"d": "Put a register linewise below the line (above with :put!)"}, + ":cd": {"d": "Change the global working directory"}, + ":lcd": {"d": "Change the working directory for this window"}, + ":tcd": {"d": "Change the working directory for this tab page"}, + ":pwd": {"d": "Show the working directory"}, + ":!": {"d": "Run a shell command; with a range, filter the lines through it"}, + ":lua": {"d": "Run Lua code: :lua print(vim.bo.filetype)"}, + ":=": {"d": "Print the last line number; := {expr} evaluates Lua (like :lua =)"}, + ":set": {"d": "Show or set options (:set wrap?, :set nowrap, :set sw=4)"}, + ":setlocal": {"d": "Set an option for this buffer/window only"}, + ":map": {"d": "Show or create mappings (:map with no args lists them)"}, + ":verbose": {"d": "Run a command verbosely; :verbose map x shows where x was set"}, + ":messages": {"d": "Show the message history"}, + ":checkhealth": {"d": "Run health checks (:checkhealth vim.lsp)"}, + ":diffthis": {"d": "Add this window to the diff"}, + ":diffoff": {"d": "Leave diff mode"}, + ":diffupdate": {"d": "Recompute the diff"}, + ":diffget": {"d": "Get the change from the other diff buffer"}, + ":diffput": {"d": "Put the change into the other diff buffer"}, + ":retab": {"d": "Convert tabs/spaces to match 'expandtab' / 'tabstop'"}, + ":&": {"d": "Repeat the last :substitute (:&& keeps the flags)"}, + ":restart": {"d": "Restart Neovim, restoring the session (:restart! does not)"}, + ":lsp": {"d": "Manage LSP: :lsp enable/disable/restart/stop [name]"}, + ":startinsert": {"d": "Start Insert mode"}, + ":stopinsert": {"d": "Stop Insert mode"}, + ":center": {"d": "Center lines in [range]"}, + ":right": {"d": "Right-align lines"}, + ":left": {"d": "Left-align lines"}, + ":join": {"d": "Join [range] lines"}, + ":oldfiles": {"d": "List recently edited files"}, + ":mksession": {"d": "Save the session (windows, buffers) to a file"}, + ":noautocmd": {"d": "Run a command without triggering autocommands"}, + ":silent": {"d": "Run a command without showing messages"}, + ":filter": {"d": "Filter the output of a listing command: :filter /pat/ ls"}, + ":redir": {"d": "Redirect messages to a file, register or variable"}, + ":wincmd": {"d": "Run a CTRL-W command: :wincmd l"}, + ":vertical": {"d": "Make the following split vertical: :vert help x"}, + ":tab": {"d": "Open the following command's window in a new tab: :tab help x"}, + "n|q{0-9a-zA-Z\"}": {"d": "Record keystrokes into register {a-z} (A-Z appends)", "cat": "Register & macro", "n": "Press q again to stop; then @a plays it, @@ / Q repeat"}, + "n|q": {"d": "Stop recording (while recording a macro)", "cat": "Register & macro"}, + "n|~": {"d": "Toggle case of N characters and move right", "cat": "Edit"}, + "n|~{motion}": {"d": "Toggle case of {motion} text (only when 'tildeop' is set)", "cat": "Operator"}, + "c|": {"d": "Open the command-line window to edit the command", "cat": "Command line", "n": "Edit the command with Normal-mode keys. In this config <CR> in Normal mode starts Leap, so press i then <CR> to run the line"}, + "i|": {"d": "Completion menu: insert one more character from the current match", "cat": "Completion & AI"}, + "i|other": {"d": "Completion menu: any other key stops completion and inserts itself", "cat": "Completion & AI"}, + "n|g": {"d": "Paste at the mouse click (same as )", "cat": "Edit"} + }, + "entries": [ + {"k": "", "m": ["t"], "d": "Leave Terminal mode for Normal mode (scroll, search, yank output)", "s": "builtin", "cat": "Terminal", "h": "CTRL-\\_CTRL-N", "e": 1, "n": "Every key except <C-\\> goes to the job. Snacks terminals also accept a quick double <Esc>"}, + {"k": "", "m": ["t"], "d": "Run one Normal-mode command, then return to Terminal mode", "s": "builtin", "cat": "Terminal", "h": "t_CTRL-\\_CTRL-O"}, + {"k": "{char}", "m": ["t"], "d": "Send {char} to the job (the itself is not sent)", "s": "builtin", "cat": "Terminal", "h": "terminal-input", "n": "Only <C-n> and <C-o> after <C-\\> are handled by Neovim; <C-\\><C-\\> sends one <C-\\>"}, + {"k": "i", "m": ["n"], "d": "Enter Terminal mode (keys go to the shell / job)", "s": "builtin", "cat": "Terminal", "ctx": "terminal buffers", "h": "terminal-input", "e": 1, "n": "a, I, A and :startinsert do the same. Snacks terminals re-enter Terminal mode automatically when you switch back to them"}, + {"k": "a", "m": ["n"], "d": "Enter Terminal mode (same as i)", "s": "builtin", "cat": "Terminal", "ctx": "terminal buffers", "h": "terminal-input"}, + {"k": "[[", "m": ["n", "x", "o"], "d": "Jump to the previous shell prompt", "s": "neovim", "cat": "Terminal", "ctx": "terminal buffers", "h": "terminal-config", "n": "Neovim 0.12 buffer-local default; needs a shell that emits OSC 133 prompt marks. In Normal mode it wins over this config's global [[/]] (Snacks words)"}, + {"k": "]]", "m": ["n", "x", "o"], "d": "Jump to the next shell prompt", "s": "neovim", "cat": "Terminal", "ctx": "terminal buffers", "h": "terminal-config"}, + {"k": "", "m": ["t"], "d": "Leave Terminal mode (two quick presses, within 200 ms)", "s": "snacks", "cat": "Terminal", "ctx": "Snacks terminal", "e": 1, "n": "A single <Esc> is still sent to the program (e.g. to leave vi-mode in the shell)"}, + {"k": "", "m": ["t"], "d": "Hide the terminal window (job keeps running)", "s": "snacks", "cat": "Terminal", "ctx": "Snacks terminal", "n": "Set in lua/plugins.lua (Snacks terminal win keys). From Normal mode the global <C-/> toggles it back; 2<C-/> opens a second terminal"}, + {"k": "q", "m": ["n"], "d": "Hide the terminal window", "s": "snacks", "cat": "Terminal", "ctx": "Snacks terminal"}, + {"k": "gf", "m": ["n"], "d": "Hide the terminal and edit the file name under the cursor", "s": "snacks", "cat": "Terminal", "ctx": "Snacks terminal"}, + {"k": "{char}", "m": ["s"], "d": "Typing replaces the selection and starts Insert mode", "s": "builtin", "cat": "Visual", "h": "Select-mode", "e": 1, "n": "Select mode is where snippet placeholders land. Caveat: this config maps d/D/c/C/x/X with {\"n\",\"v\"}, which includes Select mode, so typing those letters over a placeholder deletes it instead of replacing it"}, + {"k": "", "m": ["s"], "d": "Switch from Select to Visual mode", "s": "builtin", "cat": "Visual", "h": "v_CTRL-G"}, + {"k": "", "m": ["s"], "d": "Switch to Visual mode for one command", "s": "builtin", "cat": "Visual", "h": "v_CTRL-O"}, + {"k": "{register}", "m": ["s"], "d": "Pick the register that receives the replaced text", "s": "builtin", "cat": "Visual", "h": "v_CTRL-R"}, + {"k": "", "m": ["s"], "d": "Delete the selection", "s": "builtin", "cat": "Visual", "h": "v_"}, + {"k": "", "m": ["s"], "d": "Stop Select mode", "s": "builtin", "cat": "Visual", "h": "Select-mode"}, + {"k": "", "m": ["c"], "d": "Insert the word under the cursor", "s": "builtin", "cat": "Command line", "h": "c_CTRL-R_CTRL-W", "e": 1}, + {"k": "", "m": ["c"], "d": "Insert the WORD under the cursor", "s": "builtin", "cat": "Command line", "h": "c_CTRL-R_CTRL-A"}, + {"k": "", "m": ["c"], "d": "Insert the line under the cursor", "s": "builtin", "cat": "Command line", "h": "c_CTRL-R_CTRL-L"}, + {"k": "", "m": ["c"], "d": "Insert the file name under the cursor", "s": "builtin", "cat": "Command line", "h": "c_CTRL-R_CTRL-F"}, + {"k": "ZR", "m": ["n"], "d": "Restart Neovim and restore the session (:restart)", "s": "builtin", "cat": "Session", "h": "ZR", "n": "With a count 1-8 the session is not restored; 9 also skips the unsaved-changes check"} + ], + "map_desc": [ + {"s": "neovim", "m": "n", "k": "Y", "d": "Yank to end of line (y$)", "cat": "Edit", "e": 1, "force": true}, + {"s": "neovim", "m": "n", "k": "&", "d": "Repeat last :s on this line, keeping its flags (:&&)", "cat": "Undo & repeat", "force": true}, + {"s": "neovim", "m": "n", "k": "", "d": "Clear search highlight, update diffs and redraw", "cat": "Search", "e": 1, "force": true}, + {"s": "neovim", "m": "x", "k": "*", "d": "Search forward for the selected text", "cat": "Search", "e": 1, "force": true}, + {"s": "neovim", "m": "x", "k": "#", "d": "Search backward for the selected text", "cat": "Search", "force": true}, + {"s": "neovim", "m": "x", "k": "@", "d": "Linewise Visual: run macro {reg} on every selected line", "cat": "Register & macro", "n": "Only in linewise Visual (V); otherwise plain @", "force": true}, + {"s": "neovim", "m": "x", "k": "Q", "d": "Linewise Visual: replay the last recorded macro on every line", "cat": "Register & macro", "force": true}, + {"s": "neovim", "m": "i", "k": "", "d": "Delete text before the cursor on this line (undo point first)", "cat": "Insert", "force": true}, + {"s": "neovim", "m": "i", "k": "", "d": "Delete the word before the cursor (undo point first)", "cat": "Insert", "force": true}, + {"s": "neovim", "m": "n", "k": "[", "d": "Add N empty lines above the cursor (stay in Normal)", "cat": "Edit", "force": true}, + {"s": "neovim", "m": "n", "k": "]", "d": "Add N empty lines below the cursor (stay in Normal)", "cat": "Edit", "force": true}, + {"s": "neovim", "m": "n", "k": "[q", "d": "Previous quickfix entry (:cprevious)", "cat": "Quickfix & lists", "e": 1, "force": true}, + {"s": "neovim", "m": "n", "k": "]q", "d": "Next quickfix entry (:cnext)", "cat": "Quickfix & lists", "e": 1, "force": true}, + {"s": "neovim", "m": "n", "k": "[Q", "d": "First quickfix entry, or entry N (:crewind)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "]Q", "d": "Last quickfix entry, or entry N (:clast)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "[", "d": "Last entry in the previous file of the quickfix list (:cpfile)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "]", "d": "First entry in the next file of the quickfix list (:cnfile)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "[l", "d": "Previous location-list entry (:lprevious)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "]l", "d": "Next location-list entry (:lnext)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "[L", "d": "First location-list entry, or entry N (:lrewind)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "]L", "d": "Last location-list entry, or entry N (:llast)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "[", "d": "Last entry in the previous file of the location list (:lpfile)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "]", "d": "First entry in the next file of the location list (:lnfile)", "cat": "Quickfix & lists", "force": true}, + {"s": "neovim", "m": "n", "k": "[a", "d": "Previous file in the argument list (:previous)", "cat": "Files", "force": true}, + {"s": "neovim", "m": "n", "k": "]a", "d": "Next file in the argument list (:next)", "cat": "Files", "force": true}, + {"s": "neovim", "m": "n", "k": "[A", "d": "First argument-list file, or file N (:rewind)", "cat": "Files", "force": true}, + {"s": "neovim", "m": "n", "k": "]A", "d": "Last argument-list file, or file N (:last)", "cat": "Files", "force": true}, + {"s": "neovim", "m": "n", "k": "[t", "d": "Previous matching tag (:tprevious)", "cat": "Tags", "force": true}, + {"s": "neovim", "m": "n", "k": "]t", "d": "Next matching tag (:tnext)", "cat": "Tags", "force": true}, + {"s": "neovim", "m": "n", "k": "[T", "d": "First matching tag, or tag N (:trewind)", "cat": "Tags", "force": true}, + {"s": "neovim", "m": "n", "k": "]T", "d": "Last matching tag, or tag N (:tlast)", "cat": "Tags", "force": true}, + {"s": "neovim", "m": "n", "k": "[", "d": "Previous matching tag in the preview window (:ptprevious)", "cat": "Tags", "force": true}, + {"s": "neovim", "m": "n", "k": "]", "d": "Next matching tag in the preview window (:ptnext)", "cat": "Tags", "force": true}, + {"s": "neovim", "m": "n", "k": "[b", "d": "Previous buffer (:bprevious)", "cat": "Buffer & tab", "e": 1, "force": true}, + {"s": "neovim", "m": "n", "k": "]b", "d": "Next buffer (:bnext)", "cat": "Buffer & tab", "e": 1, "force": true}, + {"s": "neovim", "m": "n", "k": "[B", "d": "First buffer, or buffer N (:brewind)", "cat": "Buffer & tab", "force": true}, + {"s": "neovim", "m": "n", "k": "]B", "d": "Last buffer, or buffer N (:blast)", "cat": "Buffer & tab", "force": true}, + {"s": "neovim", "m": "n", "k": "[d", "d": "Previous diagnostic in this buffer", "cat": "LSP & diagnostics", "e": 1}, + {"s": "neovim", "m": "n", "k": "]d", "d": "Next diagnostic in this buffer", "cat": "LSP & diagnostics", "e": 1}, + {"s": "neovim", "m": "n", "k": "[D", "d": "First diagnostic in this buffer", "cat": "LSP & diagnostics"}, + {"s": "neovim", "m": "n", "k": "]D", "d": "Last diagnostic in this buffer", "cat": "LSP & diagnostics"}, + {"s": "neovim", "m": "n", "k": "d", "d": "Show diagnostics under the cursor in a float", "cat": "LSP & diagnostics", "e": 1}, + {"s": "neovim", "m": "n", "k": "", "d": "Show diagnostics under the cursor in a float", "cat": "LSP & diagnostics"}, + {"s": "neovim", "m": "n", "k": "grn", "d": "LSP: rename the symbol under the cursor", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "n", "k": "gra", "d": "LSP: code actions", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "x", "k": "gra", "d": "LSP: code actions for the selection", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "n", "k": "gri", "d": "LSP: go to implementation", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "n", "k": "grt", "d": "LSP: go to type definition", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "n", "k": "grx", "d": "LSP: run the code lens on this line", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "n", "k": "gO", "d": "Document outline: LSP symbols (or help / man / markdown headings)", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "i", "k": "", "d": "LSP: show signature help", "cat": "LSP & diagnostics", "n": "blink.cmp's own signature help (<C-k>) is also enabled here", "force": true}, + {"s": "neovim", "m": "s", "k": "", "d": "LSP: show signature help", "cat": "LSP & diagnostics", "force": true}, + {"s": "neovim", "m": "i", "k": "", "d": "Jump to the next snippet placeholder, else insert ", "cat": "Completion & AI", "force": true}, + {"s": "neovim", "m": "s", "k": "", "d": "Jump to the next snippet placeholder, else ", "cat": "Completion & AI", "force": true}, + {"s": "neovim", "m": "i", "k": "", "d": "Jump to the previous snippet placeholder", "cat": "Completion & AI", "force": true}, + {"s": "neovim", "m": "s", "k": "", "d": "Jump to the previous snippet placeholder", "cat": "Completion & AI", "force": true}, + {"s": "matchit", "m": "n", "k": "%", "d": "Jump to the matching bracket, if/else/endif or HTML tag", "cat": "Motion", "e": 1, "force": true}, + {"s": "matchit", "m": "x", "k": "%", "d": "Extend the selection to the matching pair", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "o", "k": "%", "d": "Up to the matching pair (d%, y%)", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "n", "k": "g%", "d": "Jump backwards through the matching group", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "x", "k": "g%", "d": "Extend the selection backwards through the matching group", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "o", "k": "g%", "d": "Backwards through the matching group", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "n", "k": "[%", "d": "To the previous unmatched group start (like [{ for if/endif)", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "n", "k": "]%", "d": "To the next unmatched group end (like ]} for if/endif)", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "x", "k": "[%", "d": "Extend to the previous unmatched group start", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "x", "k": "]%", "d": "Extend to the next unmatched group end", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "o", "k": "[%", "d": "To the previous unmatched group start", "cat": "Motion", "force": true}, + {"s": "matchit", "m": "o", "k": "]%", "d": "To the next unmatched group end", "cat": "Motion", "force": true}, + {"s": "ftplugin", "m": "n", "k": "gO", "d": "Outline of the Markdown headings", "cat": "Notes", "force": true} + ], + "notes": [ + {"title": "The grammar: operator + count + motion", "kind": "tip", "lead": "Most editing is a sentence: a verb (operator) applied to a noun (motion or text object).", "rows": [["dw", "delete to the next word"], ["ci\"", "change inside the quotes"], ["yap", "yank a paragraph"], ["gUiw", "uppercase the word"], [">iB", "indent the inside of a { } block"], ["dt)", "delete up to the next )"], ["dd · gcc", "doubling the operator acts on the whole line"]], "note": "Learn one new operator and it works with every motion you already know.", "keys": ["d", "c", "y", "gU", ">", "gc"]}, + {"title": "Counts and the dot", "kind": "tip", "lead": "A number in front repeats or scales; . repeats the last change.", "rows": [["3w · d3w · 3dw", "all the same: the count can go before the operator or the motion"], ["5j", "5 real lines down (relative numbers are on in this config)"], ["4G · 50%", "go to line 4 · go halfway through the file"], [". · 3.", "repeat the last change · repeat it with count 3"], ["*cgn…..", "change the word under the cursor, then each next match"], ["qa … q · 10@a", "record a macro, replay it 10 times (Q replays the last one)"]], "keys": [".", "@", "Q", "gn"]}, + {"title": "Registers in this config", "kind": "tip", "lead": "Put \"{reg} before a yank, delete or put to choose where text goes.", "rows": [["y · p", "'clipboard' is unnamedplus: yank and put use the system clipboard"], ["d c x D C X", "mapped to \"d…: deleted text lands in register d, the clipboard stays clean"], ["\"dp", "paste what you just deleted"], ["\"0p", "paste the last yank"], ["\"_d…", "black hole register: in this config it is overridden by the \"d mapping, use :normal! \"_d…"], ["<C-r>{reg}", "insert a register in Insert or Command-line mode"], [":reg", "show what every register holds"]], "note": "Because d/c/x are mappings that prepend \"d, any register you type before them is replaced by d (checked: \"adiw fills \"d, not \"a).", "keys": ["\"", "p", "d"]} + ] +} diff --git a/docs/cheatsheet/curated/config.json b/docs/cheatsheet/curated/config.json new file mode 100644 index 0000000..9b63de4 --- /dev/null +++ b/docs/cheatsheet/curated/config.json @@ -0,0 +1,156 @@ +{ + "groups": { + "b": {"name": "Buffer", "cat": "Buffer & tab"}, + "c": {"name": "Code", "cat": "LSP & diagnostics"}, + "cb": {"name": "Comment box", "cat": "Comment"}, + "f": {"name": "Find", "cat": "Find & pick"}, + "g": {"name": "Git", "cat": "Git"}, + "h": {"name": "Haunt bookmarks", "cat": "Mark & jump"}, + "q": {"name": "Session", "cat": "Session"}, + "s": {"name": "Search", "cat": "Find & pick"}, + "u": {"name": "UI / toggles", "cat": "UI & toggles"} + }, + "map_desc": [ + {"s": "config", "m": "*", "k": "", "cat": "Find & pick", "sl": "smart find", "e": 1, "n": "Snacks smart picker: open buffers, recent files and project files ranked together (frecency)"}, + {"s": "config", "m": "*", "k": ",", "cat": "Buffer & tab", "sl": "buffers", "e": 1}, + {"s": "config", "m": "*", "k": "/", "cat": "Find & pick", "sl": "grep", "e": 1, "n": "Live grep (ripgrep) from the current working directory, which auto-cd keeps at the project root"}, + {"s": "config", "m": "*", "k": ":", "cat": "Command line", "sl": "cmd hist"}, + {"s": "config", "m": "*", "k": "n", "cat": "UI & toggles", "sl": "notifs", "n": "Snacks notifier history window (lua/keymaps.lua:291)"}, + {"s": "config", "m": "*", "k": "e", "cat": "Files", "sl": "explorer", "e": 1, "n": "Snacks file explorer (sidebar)"}, + {"s": "config", "m": "*", "k": "N", "cat": "Help", "sl": "news", "n": "Shows Neovim's news.txt in a floating window"}, + {"s": "config", "m": "*", "k": ".", "cat": "Misc", "sl": "scratch", "n": "Toggle a persistent scratch buffer (Snacks)"}, + {"s": "config", "m": "*", "k": "S", "cat": "Misc", "sl": "scratches", "n": "Pick one of the saved scratch buffers"}, + {"s": "config", "m": "*", "k": "z", "cat": "UI & toggles", "sl": "zen", "n": "Snacks zen mode: distraction-free centered window"}, + {"s": "config", "m": "*", "k": "Z", "cat": "Window", "sl": "zoom", "n": "Snacks zoom: maximize the current window, again to restore"}, + {"s": "config", "m": "*", "k": "fb", "cat": "Buffer & tab", "sl": "buffers"}, + {"s": "config", "m": "*", "k": "fc", "cat": "Files", "sl": "config", "n": "Files under stdpath('config') (~/.config/nvim)"}, + {"s": "config", "m": "*", "k": "ff", "cat": "Find & pick", "sl": "files", "e": 1}, + {"s": "config", "m": "*", "k": "fg", "cat": "Find & pick", "sl": "git files", "n": "Only files tracked by git"}, + {"s": "config", "m": "*", "k": "fp", "cat": "Find & pick", "sl": "projects"}, + {"s": "config", "m": "*", "k": "fr", "cat": "Find & pick", "sl": "recent", "e": 1}, + {"s": "config", "m": "*", "k": "gb", "cat": "Git", "sl": "branches"}, + {"s": "config", "m": "*", "k": "gl", "cat": "Git", "sl": "log"}, + {"s": "config", "m": "*", "k": "gL", "cat": "Git", "sl": "line log", "n": "Git log of the current line"}, + {"s": "config", "m": "*", "k": "gs", "cat": "Git", "sl": "status", "e": 1}, + {"s": "config", "m": "*", "k": "gS", "cat": "Git", "sl": "stash"}, + {"s": "config", "m": "*", "k": "gd", "cat": "Git", "sl": "hunks", "n": "Picker of changed hunks (git diff)"}, + {"s": "config", "m": "*", "k": "gf", "cat": "Git", "sl": "file log", "n": "Git log of the current file"}, + {"s": "config", "m": "*", "k": "gB", "cat": "Git", "sl": "browse", "n": "Open the file on the remote's web UI; from Visual mode the selected lines are linked"}, + {"s": "config", "m": "*", "k": "gg", "cat": "Git", "sl": "lazygit", "e": 1, "n": "Lazygit in a Snacks floating terminal"}, + {"s": "config", "m": "*", "k": "gi", "cat": "Git", "sl": "issues", "n": "Snacks GitHub picker; needs the gh CLI, logged in"}, + {"s": "config", "m": "*", "k": "gI", "cat": "Git", "sl": "all issues", "n": "Snacks GitHub picker; needs the gh CLI, logged in"}, + {"s": "config", "m": "*", "k": "gp", "cat": "Git", "sl": "PRs", "n": "Snacks GitHub picker; needs the gh CLI, logged in"}, + {"s": "config", "m": "*", "k": "gP", "cat": "Git", "sl": "all PRs", "n": "Snacks GitHub picker; needs the gh CLI, logged in"}, + {"s": "config", "m": "*", "k": "gt", "cat": "Git", "sl": "tea PRs", "n": "snacks-tea (Gitea/Forgejo); needs the tea CLI logged in, uses the origin remote"}, + {"s": "config", "m": "*", "k": "gT", "cat": "Git", "sl": "tea all PRs", "n": "snacks-tea (Gitea/Forgejo); needs the tea CLI logged in, uses the origin remote"}, + {"s": "config", "m": "*", "k": "gc", "cat": "Git", "sl": "tea new PR", "n": "snacks-tea (Gitea/Forgejo); needs the tea CLI logged in, uses the origin remote"}, + {"s": "config", "m": "*", "k": "sb", "cat": "Search", "sl": "lines", "n": "Fuzzy search the lines of the current buffer"}, + {"s": "config", "m": "*", "k": "sB", "cat": "Search", "sl": "grep bufs"}, + {"s": "config", "m": "*", "k": "sg", "cat": "Find & pick", "sl": "grep", "n": "Same as <leader>/"}, + {"s": "config", "m": "*", "k": "sw", "cat": "Search", "sl": "grep word", "n": "Normal: word under the cursor. Visual: the selection"}, + {"s": "config", "m": "*", "k": "s\"", "cat": "Register & macro", "sl": "registers"}, + {"s": "config", "m": "*", "k": "s/", "cat": "Search", "sl": "search hist"}, + {"s": "config", "m": "*", "k": "sa", "cat": "Misc", "sl": "autocmds"}, + {"s": "config", "m": "*", "k": "sc", "cat": "Command line", "sl": "cmd hist"}, + {"s": "config", "m": "*", "k": "sC", "cat": "Command line", "sl": "commands"}, + {"s": "config", "m": "*", "k": "sd", "cat": "LSP & diagnostics", "sl": "diagnostics", "e": 1}, + {"s": "config", "m": "*", "k": "sD", "cat": "LSP & diagnostics", "sl": "buf diags"}, + {"s": "config", "m": "*", "k": "sh", "cat": "Help", "sl": "help", "e": 1}, + {"s": "config", "m": "*", "k": "sH", "cat": "UI & toggles", "sl": "highlights"}, + {"s": "config", "m": "*", "k": "si", "cat": "Misc", "sl": "icons"}, + {"s": "config", "m": "*", "k": "sj", "cat": "Mark & jump", "sl": "jumps", "n": "Jumplist picker: a way to go forward in the jumplist now that <Tab>/<C-I> is Sidekick NES"}, + {"s": "config", "m": "*", "k": "sk", "cat": "Help", "sl": "keymaps", "e": 1}, + {"s": "config", "m": "*", "k": "sl", "cat": "Quickfix & lists", "sl": "loclist"}, + {"s": "config", "m": "*", "k": "sm", "cat": "Mark & jump", "sl": "marks"}, + {"s": "config", "m": "*", "k": "sM", "cat": "Help", "sl": "man pages"}, + {"s": "config", "m": "*", "k": "sp", "cat": "Find & pick", "sl": "plugin spec", "n": "Greps lua/plugins.lua for every vim.pack spec (src = \"https://…); type to filter by plugin name"}, + {"s": "config", "m": "*", "k": "sq", "cat": "Quickfix & lists", "sl": "quickfix", "n": "Snacks picker over the quickfix list"}, + {"s": "config", "m": "*", "k": "sR", "cat": "Find & pick", "sl": "resume", "n": "Reopen the last picker with its query"}, + {"s": "config", "m": "*", "k": "su", "cat": "Undo & repeat", "sl": "undo hist", "n": "Snacks undo-tree picker with diff preview"}, + {"s": "config", "m": "*", "k": "ss", "cat": "LSP & diagnostics", "sl": "symbols"}, + {"s": "config", "m": "*", "k": "sS", "cat": "LSP & diagnostics", "sl": "ws symbols"}, + {"s": "config", "m": "*", "k": "sr", "cat": "Search", "sl": "replace", "e": 1, "n": "Grug-far project-wide search & replace, prefilled with the word under the cursor; q closes it"}, + {"s": "config", "m": "*", "k": "uC", "cat": "UI & toggles", "sl": "colors"}, + {"s": "config", "m": "*", "k": "un", "cat": "UI & toggles", "sl": "dismiss"}, + {"s": "config", "m": "*", "k": "uu", "cat": "Undo & repeat", "sl": "undotree", "e": 1, "n": "Toggle the undotree side panel (persistent undo is on)"}, + {"s": "config", "m": "*", "k": "ca", "cat": "LSP & diagnostics", "sl": "code action", "e": 1, "n": "Neovim's gra does the same"}, + {"s": "config", "m": "*", "k": "cr", "cat": "LSP & diagnostics", "sl": "rename", "e": 1, "n": "Puts :IncRename <cword> on the command line with a live preview: edit the name, then <CR>. Neovim's grn renames without the preview"}, + {"s": "config", "m": "*", "k": "cR", "cat": "Files", "sl": "rename file", "n": "Rename the current file on disk; tells LSP servers (willRenameFiles) so imports can follow"}, + {"s": "config", "m": "*", "k": "cf", "cat": "Edit", "sl": "format", "e": 1, "n": "conform.nvim with LSP fallback; in Visual mode only the selection is formatted"}, + {"s": "config", "m": "*", "k": "cbb", "cat": "Comment", "sl": "box", "n": "Wrap the line(s) in a centered comment box (comment-box.nvim)"}, + {"s": "config", "m": "*", "k": "cbl", "cat": "Comment", "sl": "line", "n": "Insert a centered comment line"}, + {"s": "config", "m": "*", "k": "cbd", "cat": "Comment", "sl": "del box", "n": "Remove a comment box or titled line around the text"}, + {"s": "config", "m": "*", "k": "cbk", "cat": "Comment", "sl": "catalog", "n": "Show all box and line styles"}, + {"s": "config", "m": "*", "k": "bd", "cat": "Buffer & tab", "sl": "del buffer", "e": 1, "n": "Snacks.bufdelete(): delete the buffer but keep the window layout"}, + {"s": "config", "m": "*", "k": "qs", "cat": "Session", "sl": "restore", "n": "persistence.nvim: one session per directory and git branch, saved automatically on exit"}, + {"s": "config", "m": "*", "k": "qS", "cat": "Session", "sl": "select", "n": "persistence.nvim: one session per directory and git branch, saved automatically on exit"}, + {"s": "config", "m": "*", "k": "ql", "cat": "Session", "sl": "last"}, + {"s": "config", "m": "*", "k": "qd", "cat": "Session", "sl": "no save", "n": "Stop persistence from saving the session when you quit"}, + {"s": "config", "m": "*", "k": "ha", "cat": "Mark & jump", "sl": "annotate", "n": "Bookmark the current line with an annotation shown as virtual text (prompts for the text)"}, + {"s": "config", "m": "*", "k": "ht", "cat": "Mark & jump", "sl": "show note", "n": "Show/hide the annotation of the bookmark on this line"}, + {"s": "config", "m": "*", "k": "hd", "cat": "Mark & jump", "sl": "del mark"}, + {"s": "config", "m": "*", "k": "hn", "cat": "Mark & jump", "sl": "next mark"}, + {"s": "config", "m": "*", "k": "hp", "cat": "Mark & jump", "sl": "prev mark"}, + {"s": "config", "m": "*", "k": "hl", "cat": "Mark & jump", "sl": "bookmarks", "n": "Snacks picker of all Haunt bookmarks"}, + {"s": "config", "m": "*", "k": "", "cat": "Completion & AI", "sl": "NES", "e": 1, "n": "Jumps to, then applies, the Sidekick next-edit suggestion. With no suggestion it is a plain jump forward (count works). <C-i> is mapped separately, so terminals that can tell them apart (kitty, Neovide) keep it as jump forward"}, + {"s": "config", "m": "*", "k": "d", "cat": "Operator", "d": "Delete {motion} into register d (clipboard untouched)", "force": false, "sl": "del→\"d", "e": 1, "n": "Remapped to \"d{key} (lua/keymaps.lua:23-28) so the system clipboard keeps your last yank. Paste deleted text with \"dp. A register you type first wins (\"add, \"_dd), except \"+, which looks like the default. Normal and Visual mode only"}, + {"s": "config", "m": "*", "k": "D", "cat": "Edit", "d": "Delete to end of line into register d", "force": false, "sl": "D→\"d", "n": "Remapped to \"d{key} (lua/keymaps.lua:23-28) so the system clipboard keeps your last yank. Paste deleted text with \"dp. A register you type first wins (\"add, \"_dd), except \"+, which looks like the default. Normal and Visual mode only"}, + {"s": "config", "m": "*", "k": "c", "cat": "Operator", "d": "Change {motion}; the old text goes to register d", "force": false, "sl": "chg→\"d", "e": 1, "n": "Remapped to \"d{key} (lua/keymaps.lua:23-28) so the system clipboard keeps your last yank. Paste deleted text with \"dp. A register you type first wins (\"add, \"_dd), except \"+, which looks like the default. Normal and Visual mode only"}, + {"s": "config", "m": "*", "k": "C", "cat": "Edit", "d": "Change to end of line; the old text goes to register d", "force": false, "sl": "C→\"d", "n": "Remapped to \"d{key} (lua/keymaps.lua:23-28) so the system clipboard keeps your last yank. Paste deleted text with \"dp. A register you type first wins (\"add, \"_dd), except \"+, which looks like the default. Normal and Visual mode only"}, + {"s": "config", "m": "*", "k": "x", "cat": "Edit", "d": "Delete character(s) under the cursor into register d", "force": false, "sl": "x→\"d", "e": 1, "n": "Remapped to \"d{key} (lua/keymaps.lua:23-28) so the system clipboard keeps your last yank. Paste deleted text with \"dp. A register you type first wins (\"add, \"_dd), except \"+, which looks like the default. Normal and Visual mode only"}, + {"s": "config", "m": "*", "k": "X", "cat": "Edit", "d": "Delete character(s) before the cursor into register d", "force": false, "sl": "X→\"d", "n": "Remapped to \"d{key} (lua/keymaps.lua:23-28) so the system clipboard keeps your last yank. Paste deleted text with \"dp. A register you type first wins (\"add, \"_dd), except \"+, which looks like the default. Normal and Visual mode only"}, + {"s": "config", "m": "*", "k": "K", "cat": "LSP & diagnostics", "sl": "hover", "e": 1, "n": "Always LSP hover, in every buffer: no fallback to 'keywordprg' (:Man, :help) when no LSP is attached"}, + {"s": "config", "m": "*", "k": "gd", "cat": "LSP & diagnostics", "sl": "definition", "e": 1, "n": "Snacks LSP picker (jumps directly when there is a single result). Needs an attached LSP server; replaces Vim's search-based gd"}, + {"s": "config", "m": "*", "k": "gD", "cat": "LSP & diagnostics", "sl": "declaration", "n": "Snacks LSP picker (jumps directly when there is a single result). Needs an attached LSP server; replaces Vim's search-based gD"}, + {"s": "config", "m": "*", "k": "gI", "cat": "LSP & diagnostics", "sl": "impl", "n": "Snacks LSP picker (jumps directly when there is a single result). Needs an attached LSP server; replaces Vim's gI (insert at column 1, use 0i)"}, + {"s": "config", "m": "*", "k": "gy", "cat": "LSP & diagnostics", "sl": "type def", "n": "Snacks LSP picker (jumps directly when there is a single result). Needs an attached LSP server"}, + {"s": "config", "m": "*", "k": "gai", "cat": "LSP & diagnostics", "sl": "calls in", "n": "Incoming calls picker. Makes Vim's ga wait 'timeoutlen'"}, + {"s": "config", "m": "*", "k": "gao", "cat": "LSP & diagnostics", "sl": "calls out", "n": "Outgoing calls picker. Makes Vim's ga wait 'timeoutlen'"}, + {"s": "config", "m": "*", "k": "j", "cat": "Motion", "sl": "down", "e": 1, "n": "Without a count moves by display (wrapped) line like gj/gk; with a count (5j, even 1j) moves by real lines. Not mapped in Operator-pending mode, so dj stays linewise"}, + {"s": "config", "m": "*", "k": "k", "cat": "Motion", "sl": "up", "e": 1, "n": "Without a count moves by display (wrapped) line like gj/gk; with a count (5j, even 1j) moves by real lines. Not mapped in Operator-pending mode, so dj stays linewise"}, + {"s": "config", "m": "*", "k": "", "cat": "Motion", "sl": "down", "n": "Without a count moves by display (wrapped) line like gj/gk; with a count (5j, even 1j) moves by real lines. Not mapped in Operator-pending mode, so dj stays linewise"}, + {"s": "config", "m": "*", "k": "", "cat": "Motion", "sl": "up", "n": "Without a count moves by display (wrapped) line like gj/gk; with a count (5j, even 1j) moves by real lines. Not mapped in Operator-pending mode, so dj stays linewise"}, + {"s": "config", "m": "i", "k": "", "cat": "Insert", "sl": "down", "n": "Moves by display line in Insert mode (normal! gj/gk); the native popup menu and blink.cmp's menu keep their own arrow keys"}, + {"s": "config", "m": "i", "k": "", "cat": "Insert", "sl": "up", "n": "Moves by display line in Insert mode (normal! gj/gk); the native popup menu and blink.cmp's menu keep their own arrow keys"}, + {"s": "config", "m": "*", "k": "", "cat": "Terminal", "sl": "terminal", "e": 1, "n": "Toggle a Snacks floating terminal (Normal and Terminal mode). <C-_> is the same for terminals that send Ctrl+/ as Ctrl+_"}, + {"s": "config", "m": "*", "k": "grr", "cat": "LSP & diagnostics", "sl": "references", "e": 1, "n": "Snacks picker for LSP references; replaces Neovim's grr list. The other gr* defaults (grn gra gri grt grx) work as normal"}, + {"s": "config", "m": "*", "k": "ur", "cat": "Search", "sl": "nohl", "n": "Clears search highlighting; Neovim's <C-L> does it too"}, + {"s": "config", "m": "*", "k": "", "cat": "Mark & jump", "sl": "jump fwd", "n": "Mapped to itself so it stays jump forward where the terminal can tell it from <Tab> (:h CTRL-I)"}, + {"s": "config", "m": "*", "k": "gx", "cat": "Files", "sl": "open", "e": 1, "n": "gx.nvim's :Browse: URLs, files, plugin names → GitHub, #123 issues, package.json deps, otherwise a web search. Works on a Visual selection too"} + ], + "entries": [ + {"k": "", "m": ["n"], "s": "config", "cat": "Register & macro", "ctx": "Neovide only", "sl": "paste", "d": "Paste the system clipboard (an empty line is replaced by the paste)", "n": "On an empty line the clipboard lines replace it and the cursor goes to the end of the paste; otherwise runs \"+]p (put after, indent adjusted). lua/options.lua:203"}, + {"k": "", "m": ["i", "c"], "s": "config", "cat": "Register & macro", "ctx": "Neovide only", "sl": "paste", "d": "Paste the system clipboard (+)", "n": "lua/options.lua:222-223"}, + {"k": "", "m": ["x", "s"], "s": "config", "cat": "Register & macro", "ctx": "Neovide only", "sl": "paste", "d": "Replace the selection with the system clipboard", "n": "\"+p, lua/options.lua:224"}, + {"k": "", "m": ["t"], "s": "config", "cat": "Terminal", "ctx": "Neovide only", "sl": "paste", "d": "Paste the system clipboard into the terminal", "n": "<C-\\><C-n>\"+pi, lua/options.lua:225"}, + {"k": "", "m": ["x", "s"], "s": "config", "cat": "Register & macro", "ctx": "Neovide only", "sl": "copy", "d": "Copy the selection to the system clipboard", "n": "lua/options.lua:231"}, + {"k": "", "m": ["n"], "s": "config", "cat": "Register & macro", "ctx": "Neovide only", "sl": "copy line", "d": "Copy the current line to the system clipboard", "n": "lua/options.lua:233"}, + {"k": "", "m": ["x", "s"], "s": "config", "cat": "Register & macro", "ctx": "Neovide only", "sl": "cut", "d": "Cut the selection to the system clipboard", "n": "Uses \"+d without remapping, so unlike d it really fills the clipboard. lua/options.lua:239"}, + {"k": "", "m": ["n"], "s": "config", "cat": "Register & macro", "ctx": "Neovide only", "sl": "cut line", "d": "Cut the current line to the system clipboard", "n": "lua/options.lua:241"}, + {"k": "", "m": ["n", "t"], "s": "config", "cat": "Terminal", "sl": "terminal", "d": "Toggle the Snacks terminal (alias of )", "n": "For terminals that encode Ctrl+/ as Ctrl+_ (no CSI u). Hidden from which-key (which_key_ignore), lua/keymaps.lua:312"}, + {"k": "q", "m": ["n"], "s": "config", "cat": "Search", "ctx": "Grug-far buffer", "sl": "close", "d": "Close the Grug-far window", "n": "Buffer-local, replaces macro recording there. lua/options.lua:127-133"} + ], + "commands": { + "Q": "Write all changed buffers (even read-only ones) and quit, no prompts (:xa!)", + "Format": "Format the buffer, or [range], with conform.nvim (async, LSP fallback)" + }, + "notes": [ + {"title": "Leader is Space, local leader is backslash", "kind": "change", "lead": "Set before any plugin loads, so every plugin sees them.", "rows": [["<Space>", "<leader>: 80+ Normal-mode maps and a few Visual ones, grouped as listed in the which-key popup"], ["\\", "<localleader> (plugin buffer keys such as Grug-far's)"], ["<Space> alone", "No longer moves right like l: Neovim waits for the next key (which-key shows the options)"]], "note": "lua/options.lua:15-16", "keys": [""]}, + {"title": "Yank and put use the system clipboard", "kind": "change", "lead": "clipboard=unnamedplus: every yank, delete and put that would use the unnamed register uses \"+ instead.", "rows": [["y yy Y", "Copy to the system clipboard (\"+) and \"0"], ["p P", "Paste the system clipboard: your last yank, or whatever another app copied"], ["\"0p", "Last yank made inside Neovim, even after copying elsewhere"]], "note": "lua/keymaps.lua:13. Deletes are redirected, see the next card.", "keys": ["y", "p", "P"]}, + {"title": "Deleting never touches the clipboard: d c x go to register d", "kind": "change", "lead": "Applies in Normal and Visual mode. A register you type first still wins.", "rows": [["d D c C x X", "Run as \"dd, \"dD, … so the deleted text lands in \"d (and \"1 for whole lines)"], ["\"ad… \"_d…", "An explicit register is honoured (black hole works); only \"+ is treated as the default, so use yy then dd to cut to the clipboard"], ["\"dp", "Paste what you just deleted (plain p pastes the clipboard)"], ["xp ddp", "Classic swap idioms paste the clipboard instead; use x\"dp / dd\"dp"], ["cc cw ciw…", "Change commands also go to \"d"], ["Visual p", "Not remapped: the replaced text overwrites the clipboard. Use Visual P to keep it"]], "note": "lua/keymaps.lua:17-28", "keys": ["d", "D", "c", "C", "x", "X", "p"]}, + {"title": "j k and arrows move by display line", "kind": "change", "lead": "'wrap' is on, so long lines span several screen rows.", "rows": [["j k <Down> <Up>", "No count: like gj/gk, one wrapped screen row at a time"], ["5j 12k", "With a count: real lines, so relative numbers still line up"], ["1j", "A real line down even on wrapped text (useful in macros)"], ["dj yk", "Operator-pending is not mapped: still linewise"], ["Insert <Down> <Up>", "Display-line moves too, unless a completion menu is open"]], "note": "A recorded macro that uses a bare j moves by screen row on wrapped lines; use + or 1j. lua/keymaps.lua:36-52", "keys": ["j", "k", "", ""]}, + {"title": "Enter does not work in quickfix and q: windows", "kind": "warn", "lead": "The global <CR> map wins because those windows have no buffer-local <CR> map.", "rows": [["Quickfix / location list <CR>", "Starts a Leap instead of opening the entry under the cursor"], ["Command-line window q: q/ <CR>", "Starts a Leap instead of running the line"], ["Instead", ":.cc / :.ll, <C-W><CR> (opens the entry in a split), or i then <CR> in q:"], ["Fix", "Map <CR> back buffer-locally on FileType qf and CmdwinEnter, or pick a different Leap key"]], "note": "Verified headless for both windows. lua/keymaps.lua:352", "keys": [""]}, + {"title": "LSP keys open Snacks pickers", "kind": "change", "rows": [["gd gD gI gy", "Definition, declaration, implementation, type definition"], ["grr gai gao", "References, incoming calls, outgoing calls"], ["<leader>ss <leader>sS", "Document / workspace symbols"], ["grn gra gri grt grx", "Neovim's own LSP keys: rename, code action, implementation, type definition, run code lens"], ["K <leader>ca <leader>cr", "Hover, code action, rename (IncRename with live preview)"], ["Lost", "Vim's search-based gd/gD (work without LSP), gI (use 0i), and K's 'keywordprg' (use :Man / :help)"]], "note": "Global maps, active in every buffer; without an LSP client they find nothing. lua/keymaps.lua:233-276", "keys": ["gd", "gD", "gI", "gy", "grr", "grn", "gra", "gai", "gao", "K"]}, + {"title": "Tab is Sidekick's next-edit suggestion", "kind": "change", "lead": "Copilot runs as an LSP server; ghost text comes from vim.lsp.inline_completion.", "rows": [["Normal <Tab>", "Jump to the next-edit suggestion, press again to apply it; with none, a plain jump forward"], ["Insert <Tab>", "In order: next snippet placeholder, Sidekick NES jump/apply, accept the Copilot ghost text, else a normal Tab"], ["<C-I>", "Mapped to itself, so where the terminal tells it apart from <Tab> (kitty, Neovide) it is always jump forward"]], "note": "lua/keymaps.lua:338-347; Insert chain lua/plugins.lua (blink keymap); see :help CTRL-I", "keys": ["", ""]}, + {"title": ":Q writes everything and quits", "kind": "change", "rows": [[":Q", "Runs :xa!: write all changed buffers, read-only ones too, then quit"], ["Still blocked by", "Unnamed buffers or files that cannot be written"]], "note": "lua/keymaps.lua:58", "keys": [":Q"]}, + {"title": "Files save themselves", "kind": "change", "lead": "Only normal file buffers (empty 'buftype').", "rows": [["Leaving Insert mode", ":update (only if modified)"], ["Any Normal-mode change", "x, dd, p, u, .… also save at once"], ["'autowrite'", "On: :make, :next, <C-]>… write first"], ["Undo", "Persistent ('undofile'): u, <leader>uu undotree, <leader>su picker; :e! cannot revert what is already saved"]], "note": "Each save also runs format-on-save, see the next card. lua/options.lua:25, :34, :38-47", "keys": ["u"]}, + {"title": "Formatting", "kind": "tip", "rows": [["On save", "conform formats every write (format_on_save, 500 ms); autosave makes that after each change. Turn off with vim.g.disable_autoformat / vim.b.disable_autoformat"], ["<leader>cf", "Format the buffer; in Visual mode only the selected lines"], [":Format :'<,'>Format", "Same, async, optional range"], ["Formatters", "stylua (Lua), clang-format (C/C++), rustfmt, ruff_format (Python), fish_indent, shfmt (sh/bash). Install them yourself (PATH or :MasonInstall): mason-lspconfig only installs LSP servers"], ["Other filetypes", "LSP formatting when the server supports it (lsp_format = \"fallback\")"]], "note": "lua/plugins.lua:249-268; lua/keymaps.lua:363-365; lua/options.lua:69-79", "keys": ["cf"]}, + {"title": "Working directory follows the buffer", "kind": "change", "rows": [["On BufEnter", ":cd to the project root of the file (global, all windows)"], ["Root markers", "Tried in order over all parent dirs: .git, then lua, package.json, go.mod, Cargo.toml, Makefile; else the file's own directory"], ["Affects", "<leader>ff, <leader>/, <leader>sr, :e relative/path, new terminals, :make"], ["Skipped", "Special buffers (help, terminals, pickers), except the Snacks explorer"]], "note": "lua/options.lua:100-124; marker order per vim.fs.root()"}, + {"title": "Small automatic behaviours", "kind": "change", "rows": [["Reopen a file", "Cursor returns to where you left it (g`\"), lua/options.lua:82-91"], ["File changed outside", "Reloaded on focus and when idle (checktime on FocusGained/CursorHold, 'updatetime' 200 ms) if the buffer has no unsaved edits, lua/options.lua:50-63"], ["Line numbers", "Absolute current line + relative others, made for counts like 5j, lua/options.lua:26-27"], ["Indent", "2 spaces, 'expandtab', lua/options.lua:19-21"], ["Terminal title", "NVIM: file, reset to the shell name on exit, lua/options.lua:139-192"]]}, + {"title": "]] and [[ jump between references", "kind": "change", "rows": [["]] [[", "Next / previous LSP reference of the word under the cursor (Snacks words, counts work)"], ["Markdown buffers", "Buffer-local ftplugin maps win: next / previous heading"], ["Terminal buffers (Normal mode)", "Neovim's buffer-local defaults win: previous / next shell prompt"], ["Section motions", "Still available in Visual and Operator-pending mode (v]], d]])"]], "note": "lua/keymaps.lua:315-320", "keys": ["]]", "[["]}, + {"title": "Terminal and scratch", "kind": "tip", "rows": [["<C-/>", "Toggle a floating terminal (Normal and Terminal mode; <C-_> in legacy terminals)"], ["<C-\\><C-N>", "Terminal mode to Normal mode (built-in)"], ["<leader>gg", "Lazygit in a floating terminal"], ["<leader>. <leader>S", "Toggle / pick a persistent scratch buffer"]], "note": "lua/keymaps.lua:285-314; lua/plugins.lua:106-120", "keys": ["", ""]}, + {"title": "Picker extras from this config", "kind": "tip", "rows": [["s (input, Normal)", "Flash labels on the result list: jump to an item in 2 keys"], ["<A-s>", "The same from Insert mode"], ["<A-a>", "Send the item(s) to the Sidekick AI CLI"], ["Matching", "Fuzzy, smart-case, frecency and cwd bonus"]], "note": "lua/plugins.lua:46-91", "keys": ["", ""]}, + {"title": "Sessions and bookmarks", "kind": "tip", "rows": [["<leader>qs <leader>ql <leader>qS", "Restore the session for this directory + git branch, the last one, or pick one"], ["<leader>qd", "Do not save this session on exit (saved automatically otherwise)"], ["<leader>ha", "Bookmark the line with a note (virtual text), <leader>ht shows/hides it"], ["<leader>hn <leader>hp <leader>hl", "Next / previous bookmark, bookmark picker"]], "note": "lua/keymaps.lua:382-416; lua/plugins.lua:133-138, :541-546", "keys": ["qs", "ha"]}, + {"title": "Neovide-only clipboard keys", "kind": "tip", "rows": [["<C-S-v>", "Paste the clipboard in every mode (Normal: an empty line is replaced)"], ["<C-S-c>", "Copy the selection, or the current line in Normal mode"], ["<C-S-x>", "Cut the selection or line to the clipboard (bypasses the \"d remap)"]], "note": "lua/options.lua:198-242; only when vim.g.neovide is set.", "keys": ["", "", ""]} + ] +} diff --git a/docs/cheatsheet/curated/extra.json b/docs/cheatsheet/curated/extra.json new file mode 100644 index 0000000..22e846d --- /dev/null +++ b/docs/cheatsheet/curated/extra.json @@ -0,0 +1,65 @@ +{ + "map_desc": [ + {"s": "config", "m": "n", "k": ".", "cat": "Buffer & tab", "sl": "scratch", "n": "Toggle a persistent scratch buffer (Snacks)"}, + {"s": "config", "m": "n", "k": "S", "cat": "Buffer & tab", "sl": "scratches", "n": "Pick one of the saved scratch buffers"}, + {"s": "config", "m": "n", "k": "sa", "cat": "Help", "sl": "autocmds", "n": "Snacks picker of every autocommand; <CR> jumps to where it was defined"}, + {"s": "config", "m": "n", "k": "si", "cat": "Insert", "sl": "icons", "n": "Snacks icon picker; <CR> puts the icon at the cursor (confirm = \"put\")"}, + {"s": "snacks", "m": "x", "k": "ii", "d": "Inner scope: the indented block around the cursor, without its header/footer", "cat": "Text object", "n": "Snacks scope (loaded on UIEnter, so the headless key dump misses it). Tested: inside an if body, yii yanks the body lines", "sl": "in scope", "force": true}, + {"s": "snacks", "m": "o", "k": "ii", "d": "Inner scope: the indented block around the cursor, without its header/footer", "cat": "Text object", "n": "Snacks scope (loaded on UIEnter, so the headless key dump misses it). Tested: inside an if body, yii yanks the body lines", "sl": "in scope", "force": true}, + {"s": "snacks", "m": "x", "k": "ai", "d": "Full scope: the indented block plus its header and closing lines", "cat": "Text object", "n": "Tested: inside an if body, yai yanks if … end; from the function body it takes the whole function", "sl": "a scope", "force": true}, + {"s": "snacks", "m": "o", "k": "ai", "d": "Full scope: the indented block plus its header and closing lines", "cat": "Text object", "n": "Tested: inside an if body, yai yanks if … end; from the function body it takes the whole function", "sl": "a scope", "force": true}, + {"s": "snacks", "m": "n", "k": "[i", "d": "Jump to the start of the current scope; repeat for the enclosing one", "cat": "Motion", "n": "Snacks scope, UI sessions only. Replaces Vim's [i (show the first line containing the keyword)", "sl": "scope↑", "force": true}, + {"s": "snacks", "m": "x", "k": "[i", "d": "Jump to the start of the current scope; repeat for the enclosing one", "cat": "Motion", "n": "Snacks scope, UI sessions only. Replaces Vim's [i (show the first line containing the keyword)", "sl": "scope↑", "force": true}, + {"s": "snacks", "m": "o", "k": "[i", "d": "Jump to the start of the current scope; repeat for the enclosing one", "cat": "Motion", "n": "Snacks scope, UI sessions only. Replaces Vim's [i (show the first line containing the keyword)", "sl": "scope↑", "force": true}, + {"s": "snacks", "m": "n", "k": "]i", "d": "Jump to the end of the current scope", "cat": "Motion", "n": "Snacks scope, UI sessions only. Replaces Vim's ]i (show the next line containing the keyword)", "sl": "scope↓", "force": true}, + {"s": "snacks", "m": "x", "k": "]i", "d": "Jump to the end of the current scope", "cat": "Motion", "n": "Snacks scope, UI sessions only. Replaces Vim's ]i (show the next line containing the keyword)", "sl": "scope↓", "force": true}, + {"s": "snacks", "m": "o", "k": "]i", "d": "Jump to the end of the current scope", "cat": "Motion", "n": "Snacks scope, UI sessions only. Replaces Vim's ]i (show the next line containing the keyword)", "sl": "scope↓", "force": true} + ], + "entries": [ + {"k": "`.", "m": ["n", "x", "o"], "s": "builtin", "cat": "Mark & jump", "h": "'.", "d": "Jump to where the last change was made ('. for its line)", "n": "For older changes use g; / g,"}, + {"k": "`^", "m": ["n", "x", "o"], "s": "builtin", "cat": "Mark & jump", "h": "'^", "d": "Jump to where Insert mode was last stopped (gi also re-enters Insert)"}, + {"k": "`\"", "m": ["n", "x", "o"], "s": "builtin", "cat": "Mark & jump", "h": "'quote", "d": "Jump to the cursor position when this buffer was last exited", "n": "This config jumps there automatically when a file is opened (lua/options.lua:82-91)"}, + {"k": "`0", "m": ["n", "x", "o"], "s": "builtin", "cat": "Mark & jump", "h": "'0", "d": "Jump to where you were when Neovim last exited ('1 the time before, …)", "n": "Numbered marks come from the ShaDa file and can't be set by hand"}, + {"k": "\"{1-9}", "m": ["n"], "s": "builtin", "cat": "Register & macro", "h": "quote_number", "d": "Last nine deletes of a line or more, newest in \"1 (still filled in this config)", "n": "Tested: dd fills both \"d and \"1, and the next dd shifts the old text to \"2"}, + {"k": "\"-", "m": ["n"], "s": "builtin", "cat": "Register & macro", "h": "quote_-", "d": "Small-delete register: stays empty here because d/c/x always name register d", "n": "Vim skips \"- when a register is given; tested: dw fills \"d, not \"-"}, + {"k": "\".", "m": ["n"], "s": "builtin", "cat": "Register & macro", "h": "quote_.", "d": "Last inserted text (read-only): \".p puts it again", "n": "Read-only registers \". \"% \": work with p, P, :put and <C-r>"}, + {"k": "\"%", "m": ["n"], "s": "builtin", "cat": "Register & macro", "h": "quote_%", "d": "Name of the current file (read-only): \"%p, or % in Insert mode"}, + {"k": "\"=", "m": ["n"], "s": "builtin", "cat": "Register & macro", "h": "quote_=", "d": "Expression register: \"=5*7p puts 35; =… inserts a result in Insert mode"}, + {"k": "", "m": ["i"], "s": "builtin", "cat": "Command line", "h": "cmdwin", "ctx": "Command-line window (q: q/ )", "d": "Run the line under the cursor (Normal-mode Enter is Leap here, so press i first)", "n": "Tested: q: i let g:x=3 <CR> runs it"}, + {"k": "", "m": ["n", "i"], "s": "builtin", "cat": "Command line", "h": "cmdwin", "ctx": "Command-line window (q: q/ )", "d": "Put the line under the cursor on the command line without running it", "n": "Edit further, then <CR> runs it from the normal command line (tested)"}, + {"k": ":q", "m": [":"], "s": "builtin", "cat": "Command line", "h": "cmdwin", "ctx": "Command-line window (q: q/ )", "d": "Close the command-line window without running anything (c also works)"}, + {"k": "", "m": ["n"], "s": "builtin", "cat": "Quickfix & lists", "h": "CTRL-W_", "ctx": "Quickfix / location-list window", "d": "Open the entry under the cursor in a new split", "n": "Works even though plain <CR> is Leap here (tested: opens the file at the entry's line)"}, + {"k": "{motion}$A{text}", "m": ["n"], "s": "builtin", "cat": "Visual", "h": "v_b_A", "d": "Append {text} at the end of every selected line, even ragged ones", "n": "Without $, A appends at the block's right edge instead. Tested: a / bbb / cc + <C-v>jj$AX<Esc> → aX / bbbX / ccX"}, + {"k": "", "m": ["i"], "s": "builtin", "cat": "Completion & AI", "h": "i_CTRL-X_CTRL-N", "ctx": "This config (blink.cmp)", "d": "Built-in keyword completion through CTRL-X (plain / open blink's menu)", "n": "Tested with blink active: <C-x><C-n>, <C-x><C-p>, <C-x><C-l> (lines) and <C-x><C-f> (file names) complete"}, + {"k": ":lua vim.pack.update()", "m": [":"], "s": "neovim", "cat": "Misc", "h": "vim.pack.update()", "d": "Fetch plugin updates and review them in a confirmation tab before applying", "n": "vim.pack.update(nil, { offline = true }) just lists installed plugins. Plugins on disk but not added (here: cord.nvim, git-blame, nvim-treesitter) show as \"(not active)\""}, + {"k": ":w", "m": [":"], "s": "neovim", "cat": "Misc", "h": "vim.pack.update()", "ctx": "vim.pack update tab", "d": "Apply the listed updates"}, + {"k": ":q", "m": [":"], "s": "neovim", "cat": "Misc", "h": "vim.pack.update()", "ctx": "vim.pack update tab", "d": "Discard the updates and close the tab"}, + {"k": "]]", "m": ["n", "x"], "s": "neovim", "cat": "Misc", "h": "vim.pack.update()", "ctx": "vim.pack update tab", "d": "Next plugin section ([[ for the previous one)", "n": "Buffer-local (ftplugin/nvim-pack.lua), so it wins over this config's Snacks reference jump"}, + {"k": "K", "m": ["n"], "s": "neovim", "cat": "Misc", "h": "vim.pack.update()", "ctx": "vim.pack update tab", "d": "Details of the pending change or newer tag under the cursor (LSP hover)"}, + {"k": "gra", "m": ["n"], "s": "neovim", "cat": "Misc", "h": "vim.pack.update()", "ctx": "vim.pack update tab", "d": "Plugin actions: update, skip updating, or delete a plugin that isn't active", "n": "Neovim's code-action key (<leader>ca works too). Tested: on cord.nvim the actions are Update / Skip updating / Delete"}, + {"k": ":checkhealth vim.lsp", "m": [":"], "s": "neovim", "cat": "LSP & diagnostics", "h": ":checkhealth", "d": "Show attached LSP clients, their configs and the log path (there is no :LspInfo)", "n": "nvim-lspconfig skips its :LspInfo/:LspStart/:LspStop/:LspRestart/:LspLog when Neovim has :lsp (plugin/lspconfig.lua:6-8); use :lsp restart, :lsp stop, :lsp enable"} + ], + "commands": { + "Man": "Open a man page: :Man {name}, :Man {sect} {name}; no arg = word under cursor", + "Explore": "netrw: browse the current file's directory (or [dir]) in this window", + "Hexplore": "netrw: browse the directory in a horizontal split (! = above)", + "Lexplore": "netrw: toggle a full-height explorer on the left", + "Sexplore": "netrw: split, then browse the current file's directory (! = vertical)", + "Vexplore": "netrw: browse the directory in a vertical split", + "Texplore": "netrw: browse the directory in a new tab", + "Nexplore": "netrw: next file matched by :Explore **/pattern", + "Pexplore": "netrw: previous file matched by :Explore **/pattern", + "Ntree": "netrw: make [dirname] (or the line under the cursor) the tree top", + "Nread": "netrw: re-read the last remote file into this buffer", + "Nwrite": "netrw: write [range] to the last remote file", + "Nsource": "netrw: source a remote {netfile}", + "NetUserPass": "netrw: set the user id and password for remote (ftp) access", + "DoMatchParen": "Turn matching-paren highlighting back on", + "NoMatchParen": "Turn off highlighting of the matching paren under the cursor", + "MatchDisable": "matchit: drop its % g% [% ]% maps (plain bracket %)", + "MatchEnable": "matchit: restore its % g% [% ]% maps", + "MatchDebug": "matchit: debug b:match_words (adds a Matchit menu)", + "Tutor": "Open the interactive Vim tutorial (:Tutor {name} for others)", + "UpdateRemotePlugins": "Register remote (rplugin/) plugins; run after installing or updating one" + } +} diff --git a/docs/cheatsheet/curated/motions.json b/docs/cheatsheet/curated/motions.json new file mode 100644 index 0000000..85dfdc0 --- /dev/null +++ b/docs/cheatsheet/curated/motions.json @@ -0,0 +1,250 @@ +{ + "entries": [ + {"k": "i(", "m": ["x", "o"], "d": "Inside (…) parentheses, leaving out whitespace next to the brackets", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "Opening bracket leaves out the whitespace just inside the brackets (di( on ( a ) gives ( )), closing bracket (i)) includes it (di) gives ()). Unlike Vim's built-in, it also works when the cursor is before the pair: mini.ai takes the pair covering the cursor, else the next one (first on the cursor line, then within 50 lines)."}, + {"k": "i)", "m": ["x", "o"], "d": "Inside (…) parentheses, including whitespace next to the brackets", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "a(", "m": ["x", "o"], "d": "Around (…) including the parentheses (same as a))", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "2a(, or typing a( again in Visual mode, selects the next enclosing pair."}, + {"k": "a)", "m": ["x", "o"], "d": "Around (…) including the parentheses (same as a()", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i[", "m": ["x", "o"], "d": "Inside […] brackets, leaving out whitespace next to the brackets", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i]", "m": ["x", "o"], "d": "Inside […] brackets, including whitespace next to the brackets", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "a[", "m": ["x", "o"], "d": "Around […] including the brackets (same as a])", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i{", "m": ["x", "o"], "d": "Inside {…} braces, leaving out whitespace next to the braces", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i}", "m": ["x", "o"], "d": "Inside {…} braces, including whitespace next to the braces", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "a{", "m": ["x", "o"], "d": "Around {…} including the braces (same as a})", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i<", "m": ["x", "o"], "d": "Inside <…> angle brackets, leaving out whitespace next to them", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i>", "m": ["x", "o"], "d": "Inside <…> angle brackets, including whitespace next to them", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "a<", "m": ["x", "o"], "d": "Around <…> including the angle brackets (same as a>)", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "ib", "m": ["x", "o"], "d": "Inside the nearest (), [] or {} block", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "e": 1, "n": "Vim's built-in ib only knows (); mini.ai's b is an alias for ), ] and } (edge whitespace included) and searches cover-or-next."}, + {"k": "ab", "m": ["x", "o"], "d": "Around the nearest (), [] or {} block, brackets included", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i\"", "m": ["x", "o"], "d": "Inside \"…\" double quotes", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "mini.ai matches balanced quote pairs and searches 50 lines (cover, else next). Built-in i\" only looked at the cursor line."}, + {"k": "a\"", "m": ["x", "o"], "d": "Around \"…\" including the quotes, without trailing whitespace", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "Differs from Vim's built-in a\", which also takes the whitespace after the closing quote."}, + {"k": "i'", "m": ["x", "o"], "d": "Inside '…' single quotes", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "a'", "m": ["x", "o"], "d": "Around '…' including the quotes, without trailing whitespace", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i`", "m": ["x", "o"], "d": "Inside `…` backticks", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "a`", "m": ["x", "o"], "d": "Around `…` including the backticks, without trailing whitespace", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "iq", "m": ["x", "o"], "d": "Inside the nearest quotes of any kind: '…', \"…\" or `…`", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "sl": "in quote"}, + {"k": "aq", "m": ["x", "o"], "d": "Around the nearest quotes of any kind, quotes included", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "if", "m": ["x", "o"], "d": "Inside a function call: the arguments between its parentheses", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "e": 1, "sl": "in call", "n": "A function call is a name made of letters, digits, _ or . followed by balanced (), e.g. vim.fn.getline(1). Pattern-based, so a ) inside a string can confuse it."}, + {"k": "af", "m": ["x", "o"], "d": "Around a function call, name and parentheses included", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "ia", "m": ["x", "o"], "d": "Inside an argument: just the argument, no comma or edge whitespace", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "Arguments live inside (), [] or {}, separated by commas; commas inside nested brackets or \"…\"/'…' strings are ignored (not inside `…`)."}, + {"k": "aa", "m": ["x", "o"], "d": "Around an argument plus one adjacent comma, so daa leaves a valid list", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "e": 1, "sl": "arg", "n": "First argument takes the comma on its right; middle and last arguments take the comma on their left. On the first argument the following space stays: daa turns f(a, b) into f( b)."}, + {"k": "it", "m": ["x", "o"], "d": "Inside a tag pair: the content between and ", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "Replaces Vim's built-in it/at with a pattern match, so same-name nested tags (<a><a></a></a>) can mis-match."}, + {"k": "at", "m": ["x", "o"], "d": "Around a tag pair, opening and closing tags included", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i?", "m": ["x", "o"], "d": "Inside custom edges: prompts for the left and right strings", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "Edges are literal strings, not patterns. Dot-repeat reuses the answers without asking again."}, + {"k": "a?", "m": ["x", "o"], "d": "Around custom edges (prompted), edges included", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "ig", "m": ["x", "o"], "d": "Entire buffer (custom textobject from lua/plugins.lua)", "s": "config", "cat": "Text object", "e": 1, "sl": "buffer", "n": "Defined in Config.plugins.mini_ai.custom_textobjects.g: charwise from line 1, column 1 to the end of the last line. ig and ag are identical. Try yig, =ig, cig, vig."}, + {"k": "ag", "m": ["x", "o"], "d": "Entire buffer (same as ig)", "s": "config", "cat": "Text object"}, + {"k": "i{punct}", "m": ["x", "o"], "d": "Between two {punct} characters, e.g. i_ i, i* i/ i- i", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "Any id that is not a Latin letter and not defined above works as a separator, e.g. ci_ in foo_bar_baz. Digits count too."}, + {"k": "a{punct}", "m": ["x", "o"], "d": "Like i{punct} plus the run of separators on its right", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "i{letter}", "m": ["x", "o"], "d": "Letters mini.ai doesn't define (w W s p B) fall back to Vim's text objects", "s": "mini.ai", "cat": "Text object", "n": "So iw, aW, is, ap, iB, aB behave exactly as in stock Vim."}, + {"k": "iN{id}", "m": ["x", "o"], "d": "Inside the next {id} textobject, e.g. ciN( diN\" viNq", "s": "mini.ai", "cat": "Text object", "h": "MiniAi.config", "e": 1, "sl": "in next", "n": "Uses search method next: always the one after the cursor, even if the cursor is inside one."}, + {"k": "aN{id}", "m": ["x", "o"], "d": "Around the next {id} textobject, e.g. daN) yaNf", "s": "mini.ai", "cat": "Text object", "h": "MiniAi.config"}, + {"k": "il{id}", "m": ["x", "o"], "d": "Inside the previous (last) {id} textobject, e.g. cil\"", "s": "mini.ai", "cat": "Text object", "h": "MiniAi.config"}, + {"k": "al{id}", "m": ["x", "o"], "d": "Around the previous (last) {id} textobject", "s": "mini.ai", "cat": "Text object", "h": "MiniAi.config"}, + {"k": "[count]i{id}", "m": ["x", "o"], "d": "The [count]-th enclosing textobject, e.g. d2i) empties the outer parens", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects"}, + {"k": "{Visual}i{id}", "m": ["x"], "d": "Repeat i{id}/a{id} while selected to grow to the next enclosing one", "s": "mini.ai", "cat": "Text object", "h": "MiniAi-builtin-textobjects", "n": "E.g. vi) then i) again, or va\"a\". The current selection is the reference, so the result never equals it."}, + {"k": "g[{id}", "m": ["n", "x", "o"], "d": "Move to the left edge of the a{id} textobject, e.g. g[( g[f g[\"", "s": "mini.ai", "cat": "Motion", "h": "MiniAi.move_cursor()"}, + {"k": "g]{id}", "m": ["n", "x", "o"], "d": "Move to the right edge of the a{id} textobject, e.g. g]) g]t", "s": "mini.ai", "cat": "Motion", "h": "MiniAi.move_cursor()"}, + {"k": "sa{motion}{char}", "m": ["n"], "d": "Add surrounding {char} around {motion} or textobject", "s": "mini.surround", "cat": "Surround", "h": "MiniSurround-builtin-surroundings", "e": 1, "sl": "surround", "n": "sa is an operator, so any motion or text object works, including mini.ai ones (saif\", saaa]). Dot-repeatable."}, + {"k": "saiw)", "m": ["n"], "d": "Surround the word with parentheses: (word)", "s": "mini.surround", "cat": "Surround"}, + {"k": "saiw(", "m": ["n"], "d": "Surround the word with padded parentheses: ( word )", "s": "mini.surround", "cat": "Surround"}, + {"k": "saiw\"", "m": ["n"], "d": "Surround the word with double quotes", "s": "mini.surround", "cat": "Surround"}, + {"k": "saiwf", "m": ["n"], "d": "Wrap the word in a function call; prompts for the name: name(word)", "s": "mini.surround", "cat": "Surround"}, + {"k": "saiwt", "m": ["n"], "d": "Wrap the word in a tag; prompts for it, e.g. div class=\"x\"", "s": "mini.surround", "cat": "Surround", "n": "The prompt takes the tag name plus attributes; the closing tag uses only the name."}, + {"k": "saiw?", "m": ["n"], "d": "Surround the word with custom text (prompts for left and right)", "s": "mini.surround", "cat": "Surround"}, + {"k": "sa$\"", "m": ["n"], "d": "Quote from the cursor to the end of the line", "s": "mini.surround", "cat": "Surround"}, + {"k": "[count]sa{motion}{char}", "m": ["n"], "d": "Repeat the surrounding [count] times, e.g. 2saiw) gives ((word))", "s": "mini.surround", "cat": "Surround", "h": "MiniSurround-count-with-actions"}, + {"k": "{Visual}sa{char}", "m": ["x"], "d": "Surround the selection with {char}", "s": "mini.surround", "cat": "Surround"}, + {"k": "sd{char}", "m": ["n"], "d": "Delete the surrounding {char} around the cursor", "s": "mini.surround", "cat": "Surround", "h": "MiniSurround-builtin-surroundings", "e": 1, "sl": "del surr", "n": "Search method is cover (it must enclose the cursor) within 20 lines. Add n/l after sd to target the next/previous one instead."}, + {"k": "sd'", "m": ["n"], "d": "Delete the surrounding single quotes", "s": "mini.surround", "cat": "Surround"}, + {"k": "sdq", "m": ["n"], "d": "Delete the nearest surrounding quotes of any kind", "s": "mini.surround", "cat": "Surround"}, + {"k": "sdb", "m": ["n"], "d": "Delete the nearest surrounding (), [] or {}", "s": "mini.surround", "cat": "Surround"}, + {"k": "sdf", "m": ["n"], "d": "Remove a function call but keep its arguments: f(x) → x", "s": "mini.surround", "cat": "Surround"}, + {"k": "sdt", "m": ["n"], "d": "Remove the surrounding tag pair, keeping the content", "s": "mini.surround", "cat": "Surround"}, + {"k": "sd(", "m": ["n"], "d": "Delete parentheses and the whitespace just inside them", "s": "mini.surround", "cat": "Surround"}, + {"k": "sr{old}{new}", "m": ["n"], "d": "Replace surrounding {old} with {new}", "s": "mini.surround", "cat": "Surround", "h": "MiniSurround-builtin-surroundings", "e": 1, "sl": "repl surr"}, + {"k": "sr)]", "m": ["n"], "d": "Replace (…) with […]", "s": "mini.surround", "cat": "Surround"}, + {"k": "sr'\"", "m": ["n"], "d": "Replace single quotes with double quotes", "s": "mini.surround", "cat": "Surround"}, + {"k": "srtt", "m": ["n"], "d": "Change the surrounding tag (prompts for the new one)", "s": "mini.surround", "cat": "Surround"}, + {"k": "sr)tdiv", "m": ["n"], "d": "Replace parentheses with a
      tag pair", "s": "mini.surround", "cat": "Surround"}, + {"k": "sf{char}", "m": ["n", "x", "o"], "d": "Move the cursor to the right part of surrounding {char}", "s": "mini.surround", "cat": "Surround"}, + {"k": "sF{char}", "m": ["n", "x", "o"], "d": "Move the cursor to the left part of surrounding {char}", "s": "mini.surround", "cat": "Surround"}, + {"k": "sh{char}", "m": ["n"], "d": "Flash-highlight the surrounding {char} for 500 ms", "s": "mini.surround", "cat": "Surround"}, + {"k": "sdn{char}", "m": ["n"], "d": "Delete the next {char} surrounding (after the cursor)", "s": "mini.surround", "cat": "Surround", "n": "The n (next) and l (last/previous) suffixes work after sd, sr, sf, sF and sh: srn)], sfl\", shnt."}, + {"k": "sdl{char}", "m": ["n"], "d": "Delete the previous (last) {char} surrounding", "s": "mini.surround", "cat": "Surround"}, + {"k": "[count]sd{char}", "m": ["n"], "d": "Act on the [count]-th enclosing surrounding: 2sd) on (a(b(c)b)a) gives (ab(c)ba)", "s": "mini.surround", "cat": "Surround", "h": "MiniSurround-count-with-actions"}, + {"k": "(", "m": ["n", "x"], "d": "Surround id: find () and drop inner spaces; add pads with spaces: ( x )", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)", "n": "Same for [, {, <."}, + {"k": ")", "m": ["n", "x"], "d": "Surround id: () with no padding, inner spaces kept", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)", "n": "Same for ], }, >."}, + {"k": "b", "m": ["n", "x"], "d": "Surround id: finds any of (), [], {}; adds ()", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)"}, + {"k": "q", "m": ["n", "x"], "d": "Surround id: finds any quote ' \" `; adds \"…\"", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)", "n": "Not balanced, unlike mini.ai's q: with the cursor on x in \"a\" x \"b\", sdq strips the quotes around \" x \"."}, + {"k": "t", "m": ["n", "x"], "d": "Surround id: finds a same-name tag pair; when adding, prompts for the tag", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)"}, + {"k": "f", "m": ["n", "x"], "d": "Surround id: finds name(…); when adding, prompts for the function name", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)"}, + {"k": "?", "m": ["n", "x"], "d": "Surround id: interactive, prompts for left and right parts", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)"}, + {"k": "{other}", "m": ["n", "x"], "d": "Surround id: any other char surrounds with itself, e.g. * _ | $", "s": "mini.surround", "cat": "Surround", "ctx": "Surround id (after sa/sd/sr/sf/sh)"}, + {"k": "\\(", "m": ["i"], "d": "No auto-pair right after a backslash: \\( types only (", "s": "config", "cat": "Pairs", "n": "Applies to every opener and quote (lua/smart_pairs.lua should_pair)."}, + {"k": "({text}", "m": ["i"], "d": "Opener before text types only the opener (wrapping existing text)", "s": "config", "cat": "Pairs", "e": 1, "n": "smart_pairs only inserts the pair when nothing but whitespace follows the cursor, or the next char is a closing ) ] }. Also blocks pairing before a quote or comma, e.g. typing ( inside an empty \"\"."}, + {"k": "don't", "m": ["i"], "d": "A quote right after a letter or digit is typed alone (apostrophes, closing quotes)", "s": "config", "cat": "Pairs"}, + {"k": "\"\"\"", "m": ["i"], "d": "A quote right after the same quote is typed alone, so \"\"\" docstrings work", "s": "config", "cat": "Pairs"}, + {"k": "```", "m": ["i"], "d": "On a blank line: create a fenced code block pair, cursor after the opener", "s": "config", "cat": "Pairs", "ctx": "Markdown buffers", "e": 1, "n": "Type ` three times: the first pairs to ``, the second steps over, the third turns the line into ``` with a twin ``` below. Only when the next line is blank, the buffer ends, or the next line closes the block you're in. Type the language name right away."}, + {"k": "````", "m": ["i"], "d": "Each extra ` lengthens both fences while the closer is untouched", "s": "config", "cat": "Pairs", "ctx": "Markdown buffers"}, + {"k": "~~~", "m": ["i"], "d": "Tilde fences work the same way (a lone ~ is never paired)", "s": "config", "cat": "Pairs", "ctx": "Markdown buffers"}, + {"k": "```", "m": ["i"], "d": "Inside a code block, a fence that can't close it (shorter, or ~ vs `) pairs as nested", "s": "config", "cat": "Pairs", "ctx": "Inside a Markdown code block", "n": "If the new run is long enough to close the enclosing block, it is not paired; growing a nested pair to that length collapses it into the block's closer."}, + {"k": "", "m": ["i"], "d": "At the end of an untouched fence pair: shrink both; below 3 the closer goes", "s": "config", "cat": "Pairs", "ctx": "Markdown buffers"}, + {"k": "", "m": ["i"], "d": "Between (), [] or {}: put the closer on its own line, cursor between", "s": "mini.pairs", "cat": "Pairs", "n": "Quotes are registered with cr = false, so Enter between \"\" is a plain newline."}, + {"k": "f", "m": ["n", "x", "o"], "d": "After f/F: next match in that motion's direction (no new {char})", "s": "flash", "cat": "Motion", "ctx": "Right after f/F/t/T (highlights visible)", "n": "Flash's clever-f style char_actions: the lowercase motion key is next, the uppercase one prev. After Fx that means f keeps going left and F goes right. Ends once the highlights go away (cursor moved by something else, Esc, Insert mode, leaving the buffer)."}, + {"k": "F", "m": ["n", "x", "o"], "d": "After f/F: match in the opposite direction (no new {char})", "s": "flash", "cat": "Motion", "ctx": "Right after f/F/t/T (highlights visible)"}, + {"k": "t", "m": ["n", "x", "o"], "d": "After t/T: next match in that motion's direction (no new {char})", "s": "flash", "cat": "Motion", "ctx": "Right after f/F/t/T (highlights visible)"}, + {"k": "T", "m": ["n", "x", "o"], "d": "After t/T: match in the opposite direction (no new {char})", "s": "flash", "cat": "Motion", "ctx": "Right after f/F/t/T (highlights visible)"}, + {"k": "", "m": ["n", "x"], "d": "Clear the Flash f/t highlights", "s": "flash", "cat": "Motion", "ctx": "Right after f/F/t/T (highlights visible)"}, + {"k": "[count]f{char}", "m": ["n", "x", "o"], "d": "Jump to the [count]-th {char} on the line (also with F t T ; ,)", "s": "flash", "cat": "Motion"}, + {"k": "s", "m": ["n"], "d": "Flash: label every visible result; type a label to move the selection there", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker", "n": "Picker input window, Normal mode (picker.win.input.keys in lua/plugins.lua). Runs flash.jump with pattern ^ restricted to the snacks_picker_list window; picking a label moves the list cursor, it does not confirm."}, + {"k": "", "m": ["n", "i"], "d": "Flash: label every visible result; type a label to move the selection there", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker"}, + {"k": "{char1}{char2}", "m": ["n", "x", "o"], "d": "Type the first two chars of the target; labels preview after the first", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap-usage", "n": "If the closest match is unlabeled you are already there (autojump) and the remaining labels are \"safe\" keys you would not type next (sfnut/SFNLHMUGTZ?); any other key just runs as a normal command. Case-sensitive here ('ignorecase' is off). In Operator-pending mode with several matches you always pick a label."}, + {"k": "{label}", "m": ["n", "x", "o"], "d": "Jump to the target showing that label", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump"}, + {"k": "", "m": ["n", "x", "o"], "d": "After the 2 chars: show the next group of labels (too many matches)", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap.opts.keys", "n": "Labels of later groups show dimmed/concealed until you switch to them."}, + {"k": "", "m": ["n", "x", "o"], "d": "Back to the previous group of labels", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap.opts.keys"}, + {"k": "", "m": ["n", "x", "o"], "d": "As the first key: repeat the previous Leap search (so re-leaps)", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap-repeat"}, + {"k": "{char}", "m": ["n", "x", "o"], "d": "Jump straight to the closest {char} (one-char shortcut)", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap-shortcut"}, + {"k": "{char}", "m": ["n", "x", "o"], "d": "Target {char} followed by a space, a tab or the end of the line", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap-till-eol"}, + {"k": "", "m": ["n", "x", "o"], "d": "Target an empty line or an end-of-line position", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap-to-eol"}, + {"k": "(", "m": ["n", "x", "o"], "d": "Equivalence classes: ( [ { match each other, ) ] } too, and ' \" ` too", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump", "h": "leap.opts.equivalence_classes", "n": "Space also stands for Tab and end of line. Typing any member matches the whole class."}, + {"k": "", "m": ["n", "x", "o"], "d": "Cancel the Leap", "s": "leap", "cat": "Motion", "ctx": "During a Leap jump"}, + {"k": "gcip", "m": ["n"], "d": "Toggle comment on the current paragraph", "s": "neovim", "cat": "Comment", "h": "gc-default"}, + {"k": "[count]gcc", "m": ["n"], "d": "Toggle comment on [count] lines from the cursor", "s": "neovim", "cat": "Comment", "h": "gcc-default"}, + {"k": "gcgc", "m": ["n"], "d": "Uncomment the whole comment block under the cursor", "s": "neovim", "cat": "Comment", "h": "o_gc-default"}, + {"k": "dgc", "m": ["n"], "d": "Delete the comment block under the cursor", "s": "neovim", "cat": "Comment", "h": "o_gc-default"}, + {"k": "a%", "m": ["o"], "d": "In Operator-pending mode a% is mini.ai's 'between % signs', not matchit's", "s": "mini.ai", "cat": "Text object", "n": "matchit only maps a% in Visual mode, so da% goes through mini.ai's a with % as a separator id. For matchit's group use va%, which stops on the first character of the closing word (e of end)."} + ], + "map_desc": [ + {"s": "flash", "m": "*", "k": "f", "d": "Flash: jump to the next {char} on this line", "cat": "Motion", "e": 1, "sl": "find→", "n": "flash.nvim char mode (lua/plugins.lua: modes.char.multi_line = false): never leaves the cursor line or wraps, no jump labels. All matches on the line light up and the window is dimmed until the cursor moves. Press f again for the next match. Each jump sets the ' mark, so `` jumps back (Ctrl-O can't: the jumplist keeps one entry per line). Inclusive with operators, like Vim."}, + {"s": "flash", "m": "*", "k": "F", "d": "Flash: jump to the previous {char} on this line", "cat": "Motion", "e": 1, "sl": "←find", "n": "Right after Fx, F moves to the right and f keeps going left (flash's clever-f mapping: lowercase = next in the motion's direction)."}, + {"s": "flash", "m": "*", "k": "t", "d": "Flash: jump till before the next {char} on this line", "cat": "Motion", "e": 1, "sl": "till→", "n": "Repeat with t (next) or T (opposite) while highlighted."}, + {"s": "flash", "m": "*", "k": "T", "d": "Flash: jump till after the previous {char} on this line", "cat": "Motion", "sl": "←till"}, + {"s": "flash", "m": "*", "k": ";", "d": "Flash: repeat the last f/F/t/T in the same direction", "cat": "Motion", "e": 1, "sl": "rep ft", "n": "Works any time later (not only while highlighted). After a yank (yf.) flash auto-hides its highlights."}, + {"s": "flash", "m": "*", "k": ",", "d": "Flash: repeat the last f/F/t/T in the opposite direction", "cat": "Motion", "sl": "ft rep←"}, + {"s": "config", "m": "*", "k": "", "d": "Leap: jump anywhere in this window (2 chars, then a label)", "cat": "Motion", "e": 1, "sl": "leap", "force": true, "n": "<Plug>(leap), lua/keymaps.lua. Bidirectional in the current window, closest targets first; inclusive with operators (d<CR>ab). Replaces Vim's Enter = next line (use + or j). Being a global Normal map it also takes over Enter in the quickfix/location list and the command-line window. No traversal and [count] is ignored (bidirectional mode). Dot-repeat of Leap operations needs vim-repeat, which isn't installed."}, + {"s": "config", "m": "n", "k": "S", "d": "Leap from window: jump to a spot in another window (2 chars + label)", "cat": "Motion", "e": 1, "sl": "leap win", "force": true, "n": "<Plug>(leap-from-window), Normal mode only: targets every other focusable, non-floating window in the tab and moves you there. Replaces Vim's S (use cc). Visual S is unchanged."}, + {"s": "mini.ai", "m": "*", "k": "a", "d": "Around textobject: a + id (mini.ai), e.g. af aa aq ab a)", "cat": "Text object", "e": 1, "sl": "around", "force": true, "n": "Waits for one id character (reminder after 1 s). Search method cover_or_next within n_lines = 50: the textobject covering the cursor, else the next one (first on the cursor line). Unknown letters fall back to Vim's built-ins (aw, ap, …)."}, + {"s": "mini.ai", "m": "*", "k": "i", "d": "Inside textobject: i + id (mini.ai), e.g. if ia iq ib i) ig", "cat": "Text object", "e": 1, "sl": "inside", "force": true}, + {"s": "mini.ai", "m": "*", "k": "aN", "d": "Around the next textobject: aN + id, e.g. daN)", "cat": "Text object", "sl": "a next"}, + {"s": "mini.ai", "m": "*", "k": "iN", "d": "Inside the next textobject: iN + id, e.g. ciN\"", "cat": "Text object", "sl": "i next"}, + {"s": "mini.ai", "m": "*", "k": "al", "d": "Around the previous (last) textobject: al + id", "cat": "Text object", "sl": "a last"}, + {"s": "mini.ai", "m": "*", "k": "il", "d": "Inside the previous (last) textobject: il + id", "cat": "Text object", "sl": "i last"}, + {"s": "mini.ai", "m": "*", "k": "g[", "d": "Move to the left edge of an a{id} textobject, e.g. g[(", "cat": "Motion", "sl": "obj start", "force": true}, + {"s": "mini.ai", "m": "*", "k": "g]", "d": "Move to the right edge of an a{id} textobject, e.g. g])", "cat": "Motion", "sl": "obj end", "force": true, "n": "Replaces Vim's g] (:tselect on the tag under the cursor); use :tselect or g<C-]> instead."}, + {"s": "mini.surround", "m": "*", "k": "s", "d": "Disabled by mini.surround (it's the sa/sd/sr/sf/sh prefix); use cl", "cat": "Surround", "sl": "surr…", "force": true, "n": "mini.surround maps a bare s to <Nop> in Normal, Visual and Operator-pending mode. Built-in s = cl; Visual s = c."}, + {"s": "mini.surround", "m": "n", "k": "sa", "d": "Add surrounding: sa{motion}{char}, e.g. saiw)", "cat": "Surround", "sl": "surr add", "force": true}, + {"s": "mini.surround", "m": "x", "k": "sa", "d": "Add surrounding to the selection: sa{char}", "cat": "Surround", "sl": "surr add", "force": true}, + {"s": "mini.surround", "m": "n", "k": "sd", "d": "Delete surrounding: sd{char}, e.g. sd\" sdf sdt", "cat": "Surround", "sl": "surr del", "force": true}, + {"s": "mini.surround", "m": "n", "k": "sdn", "d": "Delete the next surrounding: sdn{char}", "cat": "Surround", "sl": "del next"}, + {"s": "mini.surround", "m": "n", "k": "sdl", "d": "Delete the previous surrounding: sdl{char}", "cat": "Surround", "sl": "del prev"}, + {"s": "mini.surround", "m": "n", "k": "sr", "d": "Replace surrounding: sr{old}{new}, e.g. sr)] sr'\"", "cat": "Surround", "sl": "surr repl", "force": true}, + {"s": "mini.surround", "m": "n", "k": "srn", "d": "Replace the next surrounding: srn{old}{new}", "cat": "Surround", "sl": "repl next"}, + {"s": "mini.surround", "m": "n", "k": "srl", "d": "Replace the previous surrounding: srl{old}{new}", "cat": "Surround", "sl": "repl prev"}, + {"s": "mini.surround", "m": "*", "k": "sf", "d": "Find surrounding: move to its right part, sf{char}", "cat": "Surround", "sl": "surr →", "force": true}, + {"s": "mini.surround", "m": "*", "k": "sfn", "d": "Find the next surrounding's right part: sfn{char}", "cat": "Surround", "sl": "next →"}, + {"s": "mini.surround", "m": "*", "k": "sfl", "d": "Find the previous surrounding's right part: sfl{char}", "cat": "Surround", "sl": "prev →"}, + {"s": "mini.surround", "m": "*", "k": "sF", "d": "Find surrounding: move to its left part, sF{char}", "cat": "Surround", "sl": "← surr", "force": true}, + {"s": "mini.surround", "m": "*", "k": "sFn", "d": "Find the next surrounding's left part: sFn{char}", "cat": "Surround", "sl": "← next"}, + {"s": "mini.surround", "m": "*", "k": "sFl", "d": "Find the previous surrounding's left part: sFl{char}", "cat": "Surround", "sl": "← prev"}, + {"s": "mini.surround", "m": "n", "k": "sh", "d": "Highlight surrounding for 500 ms: sh{char}", "cat": "Surround", "sl": "surr hl", "force": true}, + {"s": "mini.surround", "m": "n", "k": "shn", "d": "Highlight the next surrounding: shn{char}", "cat": "Surround", "sl": "hl next"}, + {"s": "mini.surround", "m": "n", "k": "shl", "d": "Highlight the previous surrounding: shl{char}", "cat": "Surround", "sl": "hl prev"}, + {"s": "config", "m": "i", "k": "(", "d": "Smart pair (): only if the rest of the line is blank or starts with ) ] }", "cat": "Pairs", "e": 1, "sl": "( )", "force": true, "n": "lua/smart_pairs.lua gates mini.pairs: never after \\; otherwise types just (. Disable all pairing with vim.g.minipairs_disable or vim.b.minipairs_disable."}, + {"s": "config", "m": "i", "k": "[", "d": "Smart pair []: only if the rest of the line is blank or starts with ) ] }", "cat": "Pairs", "sl": "[ ]", "force": true}, + {"s": "config", "m": "i", "k": "{", "d": "Smart pair {}: only if the rest of the line is blank or starts with ) ] }", "cat": "Pairs", "sl": "{ }", "force": true}, + {"s": "config", "m": "i", "k": "\"", "d": "Smart quote: step over a \" right after the cursor, else pair if allowed", "cat": "Pairs", "sl": "\" \"", "force": true, "n": "Pairs only when the rest of the line is blank or starts with ) ] }, and not after a letter, digit, backslash or another \"."}, + {"s": "config", "m": "i", "k": "'", "d": "Smart quote: step over a ' right after the cursor, else pair if allowed", "cat": "Pairs", "sl": "' '", "force": true, "n": "Never pairs after a letter or digit, so don't and it's type normally."}, + {"s": "config", "m": "i", "k": "`", "d": "Smart backtick: step over a ` right after the cursor, else pair if allowed", "cat": "Pairs", "sl": "` `", "force": true}, + {"s": "config", "m": "i", "k": "`", "ctx": "markdown buffers", "d": "Markdown backtick: the 3rd ` on a blank line opens a ``` fence pair", "cat": "Pairs", "sl": "```", "force": true, "n": "More backticks lengthen both fences; otherwise behaves like the smart backtick pair."}, + {"s": "config", "m": "i", "k": "~", "ctx": "markdown buffers", "d": "Markdown tilde: the 3rd ~ on a blank line opens a ~~~ fence pair", "cat": "Pairs", "sl": "~~~", "force": true}, + {"s": "config", "m": "i", "k": "", "ctx": "markdown buffers", "d": "Markdown backspace: shrink an untouched fence pair, else mini.pairs ", "cat": "Pairs", "sl": "fence ⌫", "force": true}, + {"s": "mini.pairs", "m": "i", "k": ")", "d": "Close (): step over a ) right after the cursor, else type it", "cat": "Pairs", "sl": ")", "force": true}, + {"s": "mini.pairs", "m": "i", "k": "]", "d": "Close []: step over a ] right after the cursor, else type it", "cat": "Pairs", "sl": "]", "force": true}, + {"s": "mini.pairs", "m": "i", "k": "}", "d": "Close {}: step over a } right after the cursor, else type it", "cat": "Pairs", "sl": "}", "force": true}, + {"s": "mini.pairs", "m": "i", "k": "", "d": "Backspace; inside an empty pair (), [], {}, \"\", '', `` delete both", "cat": "Pairs", "sl": "⌫ pair", "force": true}, + {"s": "mini.pairs", "m": "i", "k": "", "d": "Newline; between (), [] or {} put the closer on its own line", "cat": "Pairs", "sl": "⏎ pair", "force": true}, + {"s": "neovim", "m": "n", "k": "gc", "d": "Toggle comment over {motion}, e.g. gcip gcj gcap", "cat": "Comment", "e": 1, "sl": "comment", "force": true, "n": "Neovim's built-in commenting, uses 'commentstring' (treesitter-aware for injected languages)."}, + {"s": "neovim", "m": "x", "k": "gc", "d": "Toggle comment on the selected lines", "cat": "Comment", "sl": "comment", "force": true}, + {"s": "neovim", "m": "n", "k": "gcc", "d": "Toggle comment on the current line ([count] lines)", "cat": "Comment", "e": 1, "sl": "comment ln", "force": true}, + {"s": "neovim", "m": "o", "k": "gc", "d": "Comment block textobject: contiguous commented lines (gcgc, dgc)", "cat": "Comment", "sl": "cmt obj", "force": true}, + {"s": "neovim", "m": "x", "k": "]n", "d": "Select the next treesitter node (replaces the selection)", "cat": "Visual", "sl": "node→", "force": true, "n": "Neovim 0.12 incremental selection. Its an/in (parent/child) are shadowed by mini.ai here."}, + {"s": "neovim", "m": "x", "k": "[n", "d": "Select the previous treesitter node (replaces the selection)", "cat": "Visual", "sl": "←node", "force": true}, + {"s": "neovim", "m": "x", "k": "]N", "d": "Extend the selection over the next treesitter node", "cat": "Visual", "sl": "node+→", "force": true}, + {"s": "neovim", "m": "x", "k": "[N", "d": "Extend the selection over the previous treesitter node", "cat": "Visual", "sl": "←node+", "force": true}, + {"s": "matchit", "m": "*", "k": "%", "d": "Jump to the matching bracket or keyword (if/else/end, tags…)", "cat": "Motion", "e": 1, "sl": "match", "force": true, "n": "matchit extends % with b:match_words: cycles forward through groups like if → else → end."}, + {"s": "matchit", "m": "*", "k": "g%", "d": "Cycle backwards through matching keywords", "cat": "Motion", "sl": "match←", "force": true}, + {"s": "matchit", "m": "*", "k": "[%", "d": "Go back to the [count]-th unmatched group word (e.g. if, or else from its branch)", "cat": "Motion", "sl": "[match", "force": true}, + {"s": "matchit", "m": "*", "k": "]%", "d": "Go to the [count]-th next unmatched group word (e.g. else, end)", "cat": "Motion", "sl": "match]", "force": true}, + {"s": "matchit", "m": "x", "k": "a%", "d": "Select the enclosing group, from its opener to the first char of the closer", "cat": "Text object", "sl": "a match", "force": true, "n": "Visual mode only. Per matchit's docs only the first character of the closing word is selected (va%d on an if … end block leaves nd), and a [count] is ignored. In Operator-pending mode a% is mini.ai's separator textobject instead."}, + {"s": "focusline.nvim", "m": "n", "k": "zz", "d": "Scroll so the cursor line sits 30% from the top (not centred)", "cat": "Scroll & view", "e": 1, "sl": "focus", "force": true, "n": "focusline.nvim (focus_target = \"30%\") runs the real zz, then scrolls with Ctrl-E/Ctrl-Y to put the line at 30% of the window height. Near the top of the buffer it just shows line 1 at the top. Normal mode only: Visual zz still centres."}, + {"s": "focusline.nvim", "m": "n", "k": "z,", "d": "Put the cursor line 30% from the top of the window", "cat": "Scroll & view", "sl": "focus", "force": true, "n": "z, is not a Vim command; focusline adds it, so it only does the 30% repositioning."}, + {"s": "focusline.nvim", "m": "n", "k": "", "d": "Scroll half a page down, then put the cursor line 30% from the top", "cat": "Scroll & view", "e": 1, "sl": "½pg↓ 30%", "force": true, "n": "A [count] is passed to the real <C-D> (it sets 'scroll')."}, + {"s": "focusline.nvim", "m": "n", "k": "", "d": "Scroll half a page up, then put the cursor line 30% from the top", "cat": "Scroll & view", "sl": "½pg↑ 30%", "force": true}, + {"s": "config", "m": "*", "k": "]]", "d": "Jump to the next LSP reference of the word under the cursor (Snacks words)", "cat": "LSP & diagnostics", "e": 1, "sl": "next ref", "force": true, "n": "Snacks.words.jump (lua/keymaps.lua) over the LSP document highlights; sets a jumplist entry and opens folds. Replaces Vim's ]] section motion in Normal mode; Markdown buffers override it with heading jumps."}, + {"s": "config", "m": "*", "k": "[[", "d": "Jump to the previous LSP reference of the word under the cursor (Snacks words)", "cat": "LSP & diagnostics", "sl": "prev ref", "force": true}, + {"s": "ftplugin", "m": "n", "k": "]]", "d": "Jump to the next Markdown heading (treesitter)", "cat": "Motion", "sl": "next hdg", "force": true, "n": "From $VIMRUNTIME/ftplugin/markdown.lua; buffer-local, so it beats the global Snacks words ]]."}, + {"s": "ftplugin", "m": "n", "k": "[[", "d": "Jump to the previous Markdown heading (treesitter)", "cat": "Motion", "sl": "prev hdg", "force": true}, + {"s": "ftplugin", "m": "x", "k": "]]", "d": "Extend the selection to the next Markdown heading", "cat": "Motion", "sl": "next hdg", "force": true}, + {"s": "ftplugin", "m": "x", "k": "[[", "d": "Extend the selection to the previous Markdown heading", "cat": "Motion", "sl": "prev hdg", "force": true}, + {"s": "neovim", "m": "x", "k": "an", "d": "Select the parent (outer) node: treesitter, or LSP selection range", "cat": "Visual", "sl": "parent node", "n": "Repeat to keep growing the selection. mini.ai's \"next\" objects moved to aN/iN so this stays reachable", "force": true}, + {"s": "neovim", "m": "x", "k": "in", "d": "Select the child (inner) node: treesitter, or LSP selection range", "cat": "Visual", "sl": "child node", "force": true}, + {"s": "neovim", "m": "o", "k": "an", "d": "Operate on the parent (outer) node", "cat": "Text object", "sl": "parent node", "force": true}, + {"s": "neovim", "m": "o", "k": "in", "d": "Operate on the child (inner) node", "cat": "Text object", "sl": "child node", "force": true} + ], + "builtin": { + "v_ab": {"n": "Handled by mini.ai here: b matches any of (), [], {}, and it works from outside the pair (cover-or-next search)."}, + "v_ib": {"n": "Handled by mini.ai here: b matches any of (), [], {}, and it works from outside the pair (cover-or-next search)."}, + "v_a(": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one (within 50 lines)."}, + "v_a)": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one (within 50 lines)."}, + "v_i(": {"n": "Handled by mini.ai: i( leaves out the whitespace just inside the brackets, i) includes it; searches forward if not inside a pair."}, + "v_i)": {"n": "Handled by mini.ai: includes the whitespace just inside the brackets (i( leaves it out); searches forward if not inside a pair."}, + "v_a[": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one."}, + "v_a]": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one."}, + "v_i[": {"n": "Handled by mini.ai: i[ leaves out inner edge whitespace, i] includes it."}, + "v_i]": {"n": "Handled by mini.ai: includes inner edge whitespace (i[ leaves it out)."}, + "v_a{": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one."}, + "v_a}": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one."}, + "v_i{": {"n": "Handled by mini.ai: i{ leaves out inner edge whitespace, i} includes it."}, + "v_i}": {"n": "Handled by mini.ai: includes inner edge whitespace (i{ leaves it out)."}, + "v_a<": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one."}, + "v_a>": {"n": "Handled by mini.ai: also finds the next pair when the cursor isn't inside one."}, + "v_i<": {"n": "Handled by mini.ai: i< leaves out inner edge whitespace, i> includes it."}, + "v_i>": {"n": "Handled by mini.ai: includes inner edge whitespace (i< leaves it out)."}, + "v_aquote": {"n": "Handled by mini.ai: balanced quotes searched over 50 lines; does not include the trailing whitespace."}, + "v_iquote": {"n": "Handled by mini.ai: balanced quote pairs, cover-or-next search over 50 lines."}, + "v_a'": {"n": "Handled by mini.ai: balanced quotes searched over 50 lines; does not include the trailing whitespace."}, + "v_i'": {"n": "Handled by mini.ai: balanced quote pairs, cover-or-next search over 50 lines."}, + "v_a`": {"n": "Handled by mini.ai: balanced backticks searched over 50 lines; does not include the trailing whitespace."}, + "v_i`": {"n": "Handled by mini.ai: balanced backtick pairs, cover-or-next search over 50 lines."}, + "v_at": {"n": "Handled by mini.ai's pattern-based tag textobject; same-name nested tags can mis-match."}, + "v_it": {"n": "Handled by mini.ai's pattern-based tag textobject; same-name nested tags can mis-match."}, + "v_aB": {"n": "Still Vim's built-in: mini.ai passes letters it doesn't define through."}, + "v_iB": {"n": "Still Vim's built-in: mini.ai passes letters it doesn't define through."}, + "s": {"n": "Disabled: s is mini.surround's prefix (<Nop>). Use cl."}, + "v_s": {"n": "Disabled: s is mini.surround's prefix in Visual mode too. Use c."}, + "S": {"n": "Now Leap from window (Normal mode). Use cc."}, + "": {"n": "Now Leap (<Plug>(leap)) in Normal, Visual and Operator-pending mode. Use + or j."}, + "g]": {"n": "Now mini.ai's move to right edge of textobject. Use :tselect or g<C-]>."}, + "f": {"n": "Now flash.nvim: same line only, highlights matches, f again = next match."}, + "F": {"n": "Now flash.nvim: same line only, highlights matches, F again = reverse."}, + "t": {"n": "Now flash.nvim: same line only, highlights matches, t again = next match."}, + "T": {"n": "Now flash.nvim: same line only, highlights matches, T again = reverse."}, + ";": {"n": "Now flash.nvim's repeat (same semantics)."}, + ",": {"n": "Now flash.nvim's reverse repeat (same semantics)."}, + "zz": {"n": "focusline.nvim: puts the line 30% from the top instead of the centre."}, + "CTRL-D": {"n": "focusline.nvim: scrolls, then puts the cursor line 30% from the top."}, + "CTRL-U": {"n": "focusline.nvim: scrolls, then puts the cursor line 30% from the top."}, + "[[": {"n": "Normal mode: Snacks words previous LSP reference; Markdown: previous heading."}, + "]]": {"n": "Normal mode: Snacks words next LSP reference; Markdown: next heading."} + }, + "notes": [ + {"title": "f/t are Flash now", "kind": "change", "lead": "f F t T ; , are flash.nvim's char mode, kept to the cursor line (multi_line = false) with no jump labels.", "rows": [["fx", "Jump to the next x; every x on the line lights up and the rest dims"], ["f again", "While lit: next x without retyping it. F goes the other way"], ["; / ,", "Repeat in the same / opposite direction, any time later"], ["3fx", "Counts work; no wrap, never leaves the line"], ["``", "Each Flash jump sets the ' mark, so you can jump back (not Ctrl-O: same line)"], ["Esc", "Clears the highlights"]], "note": "After Fx, F again moves right (flash maps lowercase = next in the motion's direction, uppercase = previous).", "keys": ["f", "F", "t", "T", ";", ","]}, + {"title": "Leap on Enter and S", "kind": "change", "lead": "Enter starts a Leap jump in the current window (Normal, Visual, Operator-pending); S leaps into another window.", "rows": [["Enterab", "Jump to an ab pair; if the closest is unlabeled you're already there, else type its label"], ["EnterEnter", "Repeat the previous Leap search"], ["EnterxEnter", "Jump to the closest x"], ["Space / Backspace", "Next / previous group of labels"], ["xSpace", "x before whitespace or at the end of a line; SpaceSpace targets line ends and empty lines"], ["dEnterab", "A motion for operators too (inclusive forward)"], ["Lost", "Enter = next line (use +/j); S = substitute line (use cc)"]], "note": "Brackets, quotes and whitespace match their whole class: ( = ([{, ' = '\"`. Matching is case-sensitive ('ignorecase' is off). Buffer-local Enter maps (Snacks picker, explorer) still win over the Leap map.", "keys": ["", "S"]}, + {"title": "Surround on s", "kind": "change", "lead": "mini.surround uses s as a prefix, so plain s does nothing in Normal, Visual and Operator-pending mode.", "rows": [["sa{motion}{char}", "Add: saiw) → (word), saiw( → ( word )"], ["sd{char}", "Delete: sd\", sdf (keep args), sdt (keep content)"], ["sr{old}{new}", "Replace: sr)], sr'\", srtt"], ["sf / sF / sh", "Find right part / find left part / highlight"], ["n / l suffix", "Next / previous surrounding: sdn), srl\"'"], ["Ids", "( [ { < padded, ) ] } > tight, b brackets, q quotes, t tag, f call, ? ask"], ["Lost", "s → use cl; Visual s → use c"]], "keys": ["s"]}, + {"title": "Text objects (mini.ai)", "kind": "change", "lead": "a/i are mini.ai: they extend Vim's text objects, search forward when you're not inside one, and pass unknown letters (w W s p B) to Vim.", "rows": [["if / af", "Function call: arguments / whole call"], ["ia / aa", "Argument / argument plus its comma"], ["iq / ib", "Any quotes / any of () [] {}"], ["it / i?", "Tag content / custom edges (prompted)"], ["ig", "Whole buffer (custom, lua/plugins.lua)"], ["i_ i, i* …", "Between any punctuation"], ["iN( / il(", "Next / previous one, e.g. ciN\" (capital N: an/in are Neovim's node selection)"], ["g[ / g]", "Move to the start / end of a{id}"], ["i( vs i)", "Open bracket leaves out the inner edge spaces, close bracket includes them"]], "note": "Search: cover_or_next over 50 lines. a\" no longer grabs trailing whitespace.", "keys": ["a", "i"]}, + {"title": "zz and half-page scrolls land at 30%", "kind": "change", "lead": "focusline.nvim re-targets these Normal-mode keys to put the cursor line 30% from the top of the window.", "rows": [["zz", "Line at 30%, not the centre (use z. to centre with the cursor at the first non-blank)"], ["z,", "New: same 30% placement"], ["Ctrl-D / Ctrl-U", "Half page down / up, then line at 30%"]], "keys": ["zz", "z,", "", ""]}, + {"title": "Smart pairs", "kind": "change", "lead": "mini.pairs plus lua/smart_pairs.lua: an opener only auto-closes when nothing but whitespace, or a closing bracket, follows the cursor.", "rows": [["( before text", "Types just (, handy for wrapping existing text"], ["' after a letter", "Typed alone: don't; \"\" + \" gives \"\"\""], ["\\ then opener", "Never pairs after a backslash"], [") \"", "Step over the closer that's already there"], ["Backspace", "Inside an empty pair deletes both"], ["Enter", "Between () [] {}: closer on its own line"]], "note": "Disable with vim.g.minipairs_disable = true (or vim.b. per buffer).", "keys": ["(", "[", "{", "\"", "'", "`"]}, + {"title": "Markdown code fences", "kind": "tip", "lead": "In Markdown buffers, backticks and tildes build fenced code blocks for you.", "rows": [["``` on a blank line", "Opener and closer, cursor after the opener: type the language"], ["more `", "Lengthens both fences (```` to wrap a block that contains ```)"], ["Backspace", "Shrinks both; below 3 the closer disappears"], ["~~~", "Same with tilde fences"]], "note": "Only opens a pair when the next line is blank, the buffer ends there, or it closes the block you're in.", "keys": ["`", "~"]}, + {"title": "an / in select syntax nodes", "kind": "tip", "lead": "Neovim 0.12's incremental selection, free because mini.ai's \"next\" objects live on aN/iN.", "rows": [["v an an…", "Grow the selection to the enclosing node each time"], ["in", "Shrink back to a child node"], ["]n [n", "Select the next / previous node"], ["]N [N", "Extend over the next / previous sibling"]], "note": "Treesitter where a parser exists, otherwise LSP selection range (:h v_an). lua/plugins.lua:424-429", "keys": ["an", "in"]} + ] +} diff --git a/docs/cheatsheet/curated/plugins.json b/docs/cheatsheet/curated/plugins.json new file mode 100644 index 0000000..983ba0b --- /dev/null +++ b/docs/cheatsheet/curated/plugins.json @@ -0,0 +1,561 @@ +{ + "entries": [ + {"k": "", "m": ["i"], "d": "Insert the full path of the file under the cursor", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["i"], "d": "Insert the word under the cursor (in the original buffer)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "j", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Send the selected items (or all results) to the quickfix list and open it", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "e": 1, "sl": "→qf"}, + {"k": "", "m": ["n", "i"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "/", "m": ["n"], "d": "Toggle focus between the input and the result list", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Open / jump to the item (or every selected item)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Runs the source's own confirm action: most open the file, some check out, apply or insert (see the per-picker entries).", "e": 1, "sl": "open"}, + {"k": "", "m": ["n", "i"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "#", "m": ["i"], "d": "Insert the alternate file name (#) into the search", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Toggle following symlinks (files / grep)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex."}, + {"k": "", "m": ["n", "i"], "d": "Toggle regex vs literal matching for the search", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex."}, + {"k": "", "m": ["n", "i"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Flash-jump: label every result and type a label to move there", "s": "config", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Custom action in lua/plugins.lua: runs flash.jump with pattern ^ restricted to the list window, then moves the cursor to the picked row (it does not confirm).", "e": 1, "sl": "flash"}, + {"k": "", "m": ["n", "i"], "d": "Toggle showing hidden (dot) files", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex.", "sl": "hidden"}, + {"k": "", "m": ["n", "i"], "d": "Send the selected items (or current) to the Sidekick AI CLI", "s": "config", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Custom action in lua/plugins.lua calling sidekick.cli.picker.snacks.send: closes the picker and sends file/position references to the running AI CLI tool."}, + {"k": "H", "m": ["n"], "d": "Move the picker to a left sidebar layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "J", "m": ["n"], "d": "Move the picker to a bottom split layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "K", "m": ["n"], "d": "Move the picker to a top split layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "L", "m": ["n"], "d": "Move the picker to a right sidebar layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "s", "m": ["n"], "d": "Flash-jump: label every result and type a label to move there", "s": "config", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Custom action in lua/plugins.lua: runs flash.jump with pattern ^ restricted to the list window, then moves the cursor to the picked row (it does not confirm). Normal mode only and buffer-local, so it beats mini.surround's s prefix inside the picker.", "sl": "flash"}, + {"k": "", "m": ["n", "i"], "d": "Debug-inspect the item under the cursor (Snacks.debug)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Scroll the result list up half a page", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Show / hide the preview window", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Open the item in a new tab", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["i"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "sl": "close"}, + {"k": "", "m": ["i"], "d": "Insert the WORD under the cursor (in the original buffer)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["i"], "d": "Insert the file path under the cursor ()", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Recall the next search from picker history", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Recall the previous search from picker history", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Scroll the preview window up", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "?", "m": ["n"], "d": "Show the key help for the input window", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Toggle live mode: re-run the search on every keystroke vs filter results", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "In the list window Ctrl-g prints the item path instead."}, + {"k": "", "m": ["i"], "d": "Delete the word before the cursor in the search input", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Maps <C-w> to <C-S-w>, which works in the prompt buffer."}, + {"k": "", "m": ["n", "i"], "d": "Scroll the preview window down", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "q", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "sl": "close"}, + {"k": "", "m": ["n", "i"], "d": "Toggle selection of the item and move down (multi-select)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "e": 1, "sl": "select"}, + {"k": "", "m": ["n", "i"], "d": "Cycle focus between the input, preview and list windows", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "G", "m": ["n"], "d": "Jump to the last result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "sl": "close", "n": "Normal mode only: in insert mode the first Esc just leaves insert mode in the search box, the second closes the picker (or use q / insert-mode Ctrl-c)."}, + {"k": "", "m": ["n", "i"], "d": "Scroll the result list down half a page", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Toggle selection of the item and move up (multi-select)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "k", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Pick a window with labels, then open the item there", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "{ \"pick_win\", \"jump\" }: the picker hides, every window gets a letter label; type it to open the item in that window."}, + {"k": "%", "m": ["i"], "d": "Insert the current file name (%)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Open the item in a horizontal split", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "sl": "split"}, + {"k": "", "m": ["n", "i"], "d": "Toggle showing git-ignored files", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex.", "sl": "ignored"}, + {"k": "", "m": ["n", "i"], "d": "Maximize / restore the picker layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Select every result, or clear the selection if all are selected", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n", "i"], "d": "Open the item in a vertical split", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input", "sl": "vsplit"}, + {"k": "", "m": ["n", "i"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "gg", "m": ["n"], "d": "Jump to the first result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["i"], "d": "Insert the current line of the original buffer", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · input"}, + {"k": "", "m": ["n"], "d": "Open the item in a new tab", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "j", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Send the selected items (or all results) to the quickfix list and open it", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "sl": "→qf"}, + {"k": "", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "/", "m": ["n"], "d": "Toggle focus between the input and the result list", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "sl": "close"}, + {"k": "gg", "m": ["n"], "d": "Jump to the first result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "zb", "m": ["n"], "d": "Scroll the list so the current item is at the bottom", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Open the item in a vertical split", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "sl": "vsplit"}, + {"k": "", "m": ["n"], "d": "Toggle showing hidden (dot) files", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex.", "sl": "hidden"}, + {"k": "H", "m": ["n"], "d": "Move the picker to a left sidebar layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "J", "m": ["n"], "d": "Move the picker to a bottom split layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "", "m": ["n"], "d": "Scroll the preview window up", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "L", "m": ["n"], "d": "Move the picker to a right sidebar layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "<2-LeftMouse>", "m": ["n"], "d": "Open / jump to the item (or every selected item)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Runs the source's own confirm action: most open the file, some check out, apply or insert (see the per-picker entries).", "sl": "open"}, + {"k": "", "m": ["n"], "d": "Debug-inspect the item under the cursor (Snacks.debug)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Show / hide the preview window", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "i", "m": ["n"], "d": "Focus the search input", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Scroll the result list up half a page", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Scroll the result list down half a page", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Print the path of the item under the cursor", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Scroll the preview window down", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Pick a window with labels, then open the item there", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "{ \"pick_win\", \"jump\" }: the picker hides, every window gets a letter label; type it to open the item in that window."}, + {"k": "", "m": ["n", "x"], "d": "Toggle selection of the item and move down (multi-select)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "sl": "select"}, + {"k": "?", "m": ["n"], "d": "Show the key help for the list window", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "k", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Maximize / restore the picker layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Toggle following symlinks (files / grep)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex."}, + {"k": "K", "m": ["n"], "d": "Move the picker to a top split layout", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "", "m": ["n"], "d": "Open the item in a horizontal split", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "sl": "split"}, + {"k": "", "m": ["n", "x"], "d": "Toggle selection of the item and move up (multi-select)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "zt", "m": ["n"], "d": "Scroll the list so the current item is at the top", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Cycle focus between the input, preview and list windows", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "zz", "m": ["n"], "d": "Scroll the list so the current item is centred", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "G", "m": ["n"], "d": "Jump to the last result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Select every result, or clear the selection if all are selected", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "", "m": ["n"], "d": "Toggle showing git-ignored files", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex.", "sl": "ignored"}, + {"k": "", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list"}, + {"k": "q", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "sl": "close"}, + {"k": "", "m": ["n"], "d": "Open / jump to the item (or every selected item)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · list", "n": "Runs the source's own confirm action: most open the file, some check out, apply or insert (see the per-picker entries).", "sl": "open"}, + {"k": "", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · preview", "sl": "close"}, + {"k": "", "m": ["n"], "d": "Cycle focus between the input, preview and list windows", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · preview"}, + {"k": "q", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · preview", "sl": "close"}, + {"k": "i", "m": ["n"], "d": "Focus the search input", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · preview"}, + {"k": "", "m": ["n", "i"], "d": "Delete (unload) the buffer under the cursor or the selected buffers", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · buffers", "n": "Opened with , / fb."}, + {"k": "dd", "m": ["n"], "d": "Delete the buffer (list window)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · buffers", "n": "Opened with , / fb."}, + {"k": "", "m": ["n", "i"], "d": "Stage / unstage the file (replaces multi-select here)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_status", "n": "Opened with gs. Runs git add or git restore --staged."}, + {"k": "", "m": ["n", "i"], "d": "Restore (discard) the file's changes after a confirm prompt", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_status", "n": "Opened with gs. Runs git restore; irreversible for unstaged edits."}, + {"k": "", "m": ["n", "i"], "d": "Stage / unstage the hunk under the cursor", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_diff", "n": "Opened with gd. Uses git apply --cached (or --reverse)."}, + {"k": "", "m": ["n", "i"], "d": "Discard the hunk after a confirm prompt", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_diff", "n": "Opened with gd."}, + {"k": "", "m": ["n", "i"], "d": "Check out the branch (remote branches get a local tracking branch)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_branches", "n": "Opened with gb."}, + {"k": "", "m": ["n", "i"], "d": "Create and check out a new branch (name prompt prefilled with the search text)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_branches", "n": "Opened with gb."}, + {"k": "", "m": ["n", "i"], "d": "Force-delete the branch (git branch -D) after a confirm prompt", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_branches", "n": "Opened with gb."}, + {"k": "", "m": ["n", "i"], "d": "Check out the commit: puts the repo in detached HEAD", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_log", "n": "Opened with gl. The source's confirm is git_checkout, so Enter on a commit really runs git checkout <sha>. Use Esc to just browse."}, + {"k": "", "m": ["n", "i"], "d": "Restore this file to the chosen commit's version (overwrites it)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_log_file / git_log_line", "n": "Opened with gf (file history) or gL (line history). Confirm is git_checkout with the file attached: git checkout <sha> -- <file> replaces the working copy."}, + {"k": "", "m": ["n", "i"], "d": "Apply the stash (git stash apply)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · git_stash", "n": "Opened with gS."}, + {"k": "", "m": ["n", "i"], "d": "cd to the project and try to restore its session, else open files", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · projects", "n": "Opened with fp. Session restore uses the dashboard's plugin detection, which only works with lazy.nvim, so here it just changes directory and opens the files picker after 100 ms."}, + {"k": "", "m": ["n", "i"], "d": "tcd to the project and open the explorer there", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · projects", "n": "Opened with fp."}, + {"k": "", "m": ["n", "i"], "d": "tcd to the project and find files there", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · projects", "n": "Opened with fp."}, + {"k": "", "m": ["n", "i"], "d": "tcd to the project and grep there", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · projects", "n": "Opened with fp."}, + {"k": "", "m": ["n", "i"], "d": "tcd to the project and list its recent files", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · projects", "n": "Opened with fp."}, + {"k": "", "m": ["n", "i"], "d": "Just tcd to the project (tab-local cwd)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · projects", "n": "Opened with fp."}, + {"k": "", "m": ["n", "i"], "d": "Open a new tab and pick a project for it", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · projects", "n": "Opened with fp."}, + {"k": "", "m": ["n", "i"], "d": "Restore the buffer to the chosen undo state", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · undo", "n": "Opened with su."}, + {"k": "", "m": ["n", "i"], "d": "Yank the lines added in that undo state", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · undo", "n": "Opened with su."}, + {"k": "", "m": ["n", "i"], "d": "Yank the lines removed in that undo state", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · undo", "n": "Opened with su."}, + {"k": "", "m": ["n", "i"], "d": "Delete the mark under the cursor", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · marks", "n": "Opened with sm."}, + {"k": "", "m": ["n", "i"], "d": "Copy the register's contents to the default register (the clipboard here) and close", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · registers", "n": "Opened with s\". Uses v:register, which is + with clipboard=unnamedplus."}, + {"k": "", "m": ["n", "i"], "d": "Put the command on the command line (edit, then Enter to run)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · command_history", "n": "Opened with : / sc."}, + {"k": "", "m": ["n", "i"], "d": "Put the command on the command line", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · commands", "n": "Opened with sC."}, + {"k": "", "m": ["n", "i"], "d": "Start a / search pre-filled with that pattern", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · search_history", "n": "Opened with s/."}, + {"k": "", "m": ["n", "i"], "d": "Insert the icon at the cursor", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · icons", "n": "Opened with si."}, + {"k": "", "m": ["n", "i"], "d": "Apply the colorscheme (moving through the list previews it live)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · colorschemes", "n": "Opened with uC."}, + {"k": "", "m": ["n", "i"], "d": "Open the help page; vertical, tab", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · help", "n": "Opened with sh."}, + {"k": "", "m": ["n", "i"], "d": "Close the picker and run the chosen keymap", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · keymaps", "n": "Opened with sk. Feeds the mapping's lhs with nvim_input, so it runs as if typed."}, + {"k": "", "m": ["n", "i"], "d": "Toggle including global keymaps", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · keymaps", "n": "Opened with sk."}, + {"k": "", "m": ["n", "i"], "d": "Toggle including buffer-local keymaps", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · keymaps", "n": "Opened with sk."}, + {"k": "", "m": ["n", "i"], "d": "Open the GitHub actions menu for the issue / PR", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · gh_issue / gh_pr", "n": "Opened with gi / gI / gp / gP."}, + {"k": "", "m": ["n", "i"], "d": "Open the issue / PR in the browser", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · gh_issue / gh_pr", "n": "Opened with gi / gI / gp / gP."}, + {"k": "", "m": ["n", "i"], "d": "Yank the issue / PR URL", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · gh_issue / gh_pr", "n": "Opened with gi / gI / gp / gP."}, + {"k": "y", "m": ["n", "x"], "d": "Yank the URL(s) of the item(s) (list window)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · gh_issue / gh_pr", "n": "Opened with gi / gI / gp / gP."}, + {"k": "", "m": ["n", "i"], "d": "Open the PR in a tea:// buffer (c checkout, d diff, A approve, a comment, x close)", "s": "snacks-tea", "cat": "Git", "ctx": "Snacks picker · tea_pr", "n": "Opened with gt / gT. This snacks-tea version registers its picker config only when Snacks.picker.config.defaults exists, which this snacks lacks, so it falls back to a bare picker: Enter is the default jump and the Alt-b / Ctrl-y / y keys are not mapped (verified headless)."}, + {"k": "", "m": ["n", "i"], "d": "Open the scratch buffer", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · scratch", "n": "Opened with S."}, + {"k": "", "m": ["n", "i"], "d": "Open the default scratch buffer for the current filetype (created if missing)", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · scratch", "n": "Opened with S. Runs Snacks.scratch.open(), the same buffer . toggles; it does not make an extra one."}, + {"k": "", "m": ["n", "i"], "d": "Delete the scratch file", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · scratch", "n": "Opened with S."}, + {"k": "", "m": ["n", "i"], "d": "Jump to the location; LSP pickers jump directly when there is only one result", "s": "snacks", "cat": "Find & pick", "ctx": "Snacks picker · lsp_* / smart / files / grep", "n": "Opened with gd gr gI gy gai gao, …. auto_confirm is on for lsp_definitions, references, implementations, type/declarations and call hierarchy."}, + {"k": "", "m": ["n", "i"], "d": "Create a new note named from the search text", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Snacks picker · Obsidian notes", "n": "In :Obsidian quick_switch / search pickers (snacks backend). Ctrl-b here replaces snacks' preview scroll-up."}, + {"k": "", "m": ["n", "i"], "d": "Insert a [[link]] to the chosen note at the cursor", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Snacks picker · Obsidian notes", "n": "In :Obsidian quick_switch / search pickers (snacks backend). Ctrl-b here replaces snacks' preview scroll-up."}, + {"k": "", "m": ["n", "i"], "d": "Bookmark the chosen note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Snacks picker · Obsidian notes", "n": "In :Obsidian quick_switch / search pickers (snacks backend). Ctrl-b here replaces snacks' preview scroll-up."}, + {"k": "", "m": ["n", "i"], "d": "Add the chosen tag(s) to the current note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Snacks picker · Obsidian tags", "n": "In the :Obsidian tags picker."}, + {"k": "", "m": ["n", "i"], "d": "Insert the chosen tag at the cursor", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Snacks picker · Obsidian tags", "n": "In the :Obsidian tags picker."}, + {"k": "", "m": ["n", "i"], "d": "Jump to the bookmark", "s": "haunt", "cat": "Mark & jump", "ctx": "Snacks picker · Haunt bookmarks", "n": "Opened with Space h l (picker = \"snacks\" in lua/plugins.lua)."}, + {"k": "d", "m": ["n"], "d": "Delete the bookmark under the cursor", "s": "haunt", "cat": "Mark & jump", "ctx": "Snacks picker · Haunt bookmarks", "n": "Normal mode in both the input and list windows (default picker_keys.delete)."}, + {"k": "a", "m": ["n"], "d": "Edit the bookmark's annotation", "s": "haunt", "cat": "Mark & jump", "ctx": "Snacks picker · Haunt bookmarks", "n": "Normal mode in both the input and list windows (default picker_keys.edit_annotation)."}, + {"k": "", "m": ["n"], "d": "Open a terminal in the directory under the cursor", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Replaces the picker's usual Ctrl-t (open in tab)."}, + {"k": "j", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Send the selected items (or all results) to the quickfix list and open it", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "o", "m": ["n"], "d": "Open the file with the system default application", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "r", "m": ["n"], "d": "Rename the file (LSP-aware: updates imports via Snacks.rename)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "e": 1, "sl": "rename"}, + {"k": "zb", "m": ["n"], "d": "Scroll the list so the current item is at the bottom", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "k", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "P", "m": ["n"], "d": "Toggle the preview window", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "I", "m": ["n"], "d": "Toggle showing git-ignored files", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "i", "m": ["n"], "d": "Focus the search input", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "zt", "m": ["n"], "d": "Scroll the list so the current item is at the top", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "a", "m": ["n"], "d": "Create a file or directory (end the name with / for a directory)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Missing parent directories are created.", "e": 1, "sl": "new"}, + {"k": "", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "Z", "m": ["n"], "d": "Collapse every directory", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Print the path of the item under the cursor", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Scroll the preview window down", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n", "x"], "d": "Toggle selection of the item and move up (multi-select)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n", "x"], "d": "Toggle selection of the item and move down (multi-select)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "]g", "m": ["n"], "d": "Jump to the next file with git changes", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "[g", "m": ["n"], "d": "Jump to the previous file with git changes", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Select every result, or clear the selection if all are selected", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "]d", "m": ["n"], "d": "Jump to the next file with diagnostics", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Set the tab-local working directory to the directory under the cursor", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "[d", "m": ["n"], "d": "Jump to the previous file with diagnostics", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "]w", "m": ["n"], "d": "Jump to the next file with warnings", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "c", "m": ["n"], "d": "Copy the selection into this directory, or copy the file to a new name", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "]e", "m": ["n"], "d": "Jump to the next file with errors", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "?", "m": ["n"], "d": "Show the key help for the list window", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "[e", "m": ["n"], "d": "Jump to the previous file with errors", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "/", "m": ["n"], "d": "Toggle focus between the input and the result list", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "gg", "m": ["n"], "d": "Jump to the first result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "h", "m": ["n"], "d": "Collapse the directory (or the parent of a file)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Toggle following symlinks (files / grep)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Toggle flags show as letters in the picker title: f follow, h hidden, i ignored, R regex."}, + {"k": "", "m": ["n"], "d": "Open the item in a vertical split", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Toggle showing hidden (dot) files", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "u", "m": ["n"], "d": "Refresh the tree from disk", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "J", "m": ["n"], "d": "Move the picker to a bottom split layout", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "K", "m": ["n"], "d": "Move the picker to a top split layout", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "", "m": ["n"], "d": "Maximize / restore the picker layout", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "<2-LeftMouse>", "m": ["n"], "d": "Open the file, or expand / collapse the directory", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Debug-inspect the item under the cursor (Snacks.debug)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Toggle the preview window", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Scroll the result list up half a page", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Scroll the result list down half a page", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "/", "m": ["n"], "d": "Grep inside the directory under the cursor", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Buffer-local <leader>/, shadowing the global Grep map while the explorer list has focus."}, + {"k": "H", "m": ["n"], "d": "Toggle showing hidden (dot) files", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "H", "m": ["n"], "d": "Move the picker to a left sidebar layout", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "l", "m": ["n"], "d": "Open the file, or expand / collapse the directory", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Go up: make the parent directory the explorer root", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": ".", "m": ["n"], "d": "Make the directory under the cursor the explorer root", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "q", "m": ["n"], "d": "Close the picker and go back to the window it was opened from", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Open the file, or expand / collapse the directory", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "e": 1}, + {"k": "", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Toggle showing git-ignored files", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "y", "m": ["n", "x"], "d": "Yank the path(s) of the file(s) to the clipboard", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Uses v:register, which is + with clipboard=unnamedplus."}, + {"k": "G", "m": ["n"], "d": "Jump to the last result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "p", "m": ["n"], "d": "Paste (copy) files whose paths are in the clipboard into this directory", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "zz", "m": ["n"], "d": "Scroll the list so the current item is centred", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "m", "m": ["n"], "d": "Move the selected files into this directory (renames if nothing selected)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Cycle focus between the input, preview and list windows", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Open the item in a horizontal split", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Pick a window with labels, then open the item there", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "{ \"pick_win\", \"jump\" }: the picker hides, every window gets a letter label; type it to open the item in that window."}, + {"k": "", "m": ["n"], "d": "Move to the previous result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "L", "m": ["n"], "d": "Move the picker to a right sidebar layout", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Split layouts disable auto-close and preview; the picker stays open after confirm."}, + {"k": "", "m": ["n"], "d": "Move to the next result", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "", "m": ["n"], "d": "Scroll the preview window up", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "d", "m": ["n"], "d": "Delete the file(s) after a confirm prompt (to the system trash)", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer", "n": "Trash is on by default and trash, gio and kioclient are installed here, so deletes are recoverable. Works on the multi-selection.", "e": 1, "sl": "delete"}, + {"k": "[w", "m": ["n"], "d": "Jump to the previous file with warnings", "s": "snacks", "cat": "Files", "ctx": "Snacks explorer"}, + {"k": "f", "m": ["n"], "d": "Find file (Snacks files picker)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks dashboard"}, + {"k": "n", "m": ["n"], "d": "New empty buffer in insert mode (:ene | startinsert)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks dashboard"}, + {"k": "g", "m": ["n"], "d": "Find text (Snacks grep picker)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks dashboard"}, + {"k": "r", "m": ["n"], "d": "Recent files (Snacks recent picker)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks dashboard"}, + {"k": "c", "m": ["n"], "d": "Find a file in the Neovim config (stdpath('config'))", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks dashboard"}, + {"k": "q", "m": ["n"], "d": "Quit Neovim (:qa)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks dashboard", "n": "The item key overrides the dashboard's own q = :bd."}, + {"k": "", "m": ["n"], "d": "Run the item under the cursor", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks dashboard", "n": "There is no s (Restore Session) or L (Lazy) item: Snacks detects session plugins only under lazy.nvim, so under vim.pack persistence is not found. Use Space q s / Space q l instead."}, + {"k": "q", "m": ["n"], "d": "Close the scratch window (the buffer is saved automatically)", "s": "snacks", "cat": "Buffer & tab", "ctx": "Snacks scratch window", "n": "Scratch files live in stdpath('data')/scratch, keyed by name, filetype, count, cwd and git branch, and are written when hidden. {count} Space . opens scratch number {count}."}, + {"k": "", "m": ["n", "x"], "d": "Run the Lua scratch buffer (or the selection) and show results inline", "s": "snacks", "cat": "Buffer & tab", "ctx": "Snacks scratch window", "n": "Only in Lua scratch buffers (win_by_ft.lua), via Snacks.debug.run."}, + {"k": "z", "m": ["n"], "d": "Leave zen mode (zen windows do not map q)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks zen / zoom", "n": "Zen opens the buffer in a centred 120-col float with dimming and git signs hidden; Space Z is the zoom variant (full width, keeps statusline/tabline). Run the same key again, or :close, to restore."}, + {"k": "q", "m": ["n"], "d": "Close the notification history window", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks notification history", "n": "Space n opens Snacks.notifier.show_history() (the second of two Space n maps in lua/keymaps.lua, so it wins over the notifications picker)."}, + {"k": "", "m": ["i", "n"], "d": "Confirm the input (or accept the completion item)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "", "m": ["i"], "d": "Close completion, else leave insert mode in the prompt", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "", "m": ["n"], "d": "Cancel the prompt", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "q", "m": ["n"], "d": "Cancel the prompt", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "", "m": ["i", "n"], "d": "Previous entry from the input history", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "", "m": ["i", "n"], "d": "Next entry from the input history", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "", "m": ["i"], "d": "Trigger / cycle completion (when the prompt supports it)", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "", "m": ["i"], "d": "Delete the word before the cursor", "s": "snacks", "cat": "UI & toggles", "ctx": "Snacks input (vim.ui.input prompts)", "n": "Used by rename (Space c R), explorer add/rename/copy and any plugin calling vim.ui.input."}, + {"k": "", "m": ["n"], "d": "Open the actions menu for the issue / PR", "s": "snacks", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "i", "m": ["n"], "d": "Edit the issue / PR (GitHub)", "s": "snacks", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "a", "m": ["n"], "d": "Add a comment", "s": "snacks", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "c", "m": ["n"], "d": "Close the issue / PR (GitHub) — or check out the PR (Forgejo)", "s": "snacks", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer", "n": "In snacks-tea (Forgejo/Gitea) buffers c is checkout and x closes."}, + {"k": "o", "m": ["n"], "d": "Reopen the issue / PR", "s": "snacks", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "d", "m": ["n"], "d": "View the PR diff (Forgejo)", "s": "snacks-tea", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "A", "m": ["n"], "d": "Approve the PR (Forgejo)", "s": "snacks-tea", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "x", "m": ["n"], "d": "Close the PR (Forgejo)", "s": "snacks-tea", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "r", "m": ["n"], "d": "Refresh the PR buffer (Forgejo)", "s": "snacks-tea", "cat": "Git", "ctx": "GitHub / Forgejo PR buffer"}, + {"k": "", "m": ["n", "i"], "d": "Submit the comment / review being written", "s": "snacks-tea", "cat": "Git", "ctx": "Forgejo comment window", "n": "The comment scratch window also has Alt-w cycle, Ctrl-b/Ctrl-f preview scroll."}, + {"k": "a", "m": ["n", "x"], "d": "Comment on the diff line(s) under the cursor (preview window)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · gh_diff", "n": "GitHub PR diff picker (snacks.gh). snacks-tea's Forgejo diff picker maps the same keys to actions it never defines, so they do nothing there."}, + {"k": "", "m": ["n", "x"], "d": "Open the actions menu for the diff line(s) (preview window)", "s": "snacks", "cat": "Git", "ctx": "Snacks picker · gh_diff", "n": "GitHub PR diff picker (snacks.gh). snacks-tea's Forgejo diff picker maps the same keys to actions it never defines, so they do nothing there."}, + {"k": "", "m": ["i"], "d": "Open the completion menu; again: show docs, again: hide docs", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Never falls back, so Ctrl-Space always goes to blink in insert mode.", "e": 1, "sl": "complete"}, + {"k": "", "m": ["i"], "d": "Accept the selected item (the first one if none is selected)", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Falls back to Ctrl-y (insert the character above) when the menu is closed.", "e": 1, "sl": "accept"}, + {"k": "", "m": ["i"], "d": "Close the menu and undo the previewed insert", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Falls back to Ctrl-e (insert the character below) when the menu is closed.", "e": 1, "sl": "dismiss"}, + {"k": "", "m": ["i"], "d": "Select the next item; with the menu closed, open it on the first item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Configured in lua/plugins.lua: { \"select_next\", \"show_and_insert\" }. Ctrl-p mirrors it (opens on the last item). Built-in keyword completion is still on Ctrl-x Ctrl-n"}, + {"k": "", "m": ["i"], "d": "Select the previous item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Same as Ctrl-n: no fallback to built-in completion."}, + {"k": "", "m": ["i"], "d": "Select the next item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "With the menu closed it falls back to this config's insert Down (move by display line, lua/keymaps.lua)."}, + {"k": "", "m": ["i"], "d": "Select the previous item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "With the menu closed it falls back to this config's insert Up (move by display line)."}, + {"k": "", "m": ["i"], "d": "Scroll the documentation window down", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Falls back to Ctrl-f when no docs window is open."}, + {"k": "", "m": ["i"], "d": "Scroll the documentation window up", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Falls back to Ctrl-b when no docs window is open."}, + {"k": "", "m": ["i", "s"], "d": "Next snippet field → apply AI next edit → accept ghost text → Tab", "s": "config", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Custom chain in lua/plugins.lua: 1) snippet_forward if a snippet is active; 2) sidekick.nes_jump_or_apply() (jump to, then apply, a Copilot next-edit suggestion; suggestions are cleared on insert, so rarely fires here); 3) vim.lsp.inline_completion.get() accepts native LSP ghost text (Copilot); 4) a literal Tab. Tab does not accept the blink menu; use Ctrl-y.", "e": 1, "sl": "tab chain"}, + {"k": "", "m": ["i", "s"], "d": "Jump to the previous snippet field", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Falls back to Shift-Tab."}, + {"k": "", "m": ["c"], "d": "Show the menu and insert the first match (accepts if it's the only one); then next", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)", "n": "Ghost text previews the top match; the menu does not open by itself on : (only in the cmdline window)."}, + {"k": "", "m": ["c"], "d": "Show the menu and insert the last match; then previous item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)"}, + {"k": "", "m": ["c"], "d": "Open the completion menu", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)"}, + {"k": "", "m": ["c"], "d": "Select the next item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)", "n": "With the menu closed it falls back to Ctrl-n: next completion match after Tab, otherwise the next (newer) command-line from history."}, + {"k": "", "m": ["c"], "d": "Select the previous item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)", "n": "With the menu closed it falls back to Ctrl-p: previous completion match, otherwise the previous (older) command-line from history."}, + {"k": "", "m": ["c"], "d": "Select the next item while the menu is open", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)", "n": "Arrow keys only move the cursor when the menu is closed."}, + {"k": "", "m": ["c"], "d": "Select the previous item while the menu is open", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)", "n": "Arrow keys only move the cursor when the menu is closed."}, + {"k": "", "m": ["c"], "d": "Accept the selected item", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)"}, + {"k": "", "m": ["c"], "d": "Close the menu and undo the preview", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)", "n": "Falls back to Ctrl-e (cursor to end of line)."}, + {"k": "", "m": ["c"], "d": "Hide the menu", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (cmdline)", "n": "Falls back to End."}, + {"k": " / / ", "m": ["t"], "d": "blink's terminal-mode keys: usually NOT active here", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion (terminal)", "n": "blink enables terminal mode by default and inherits the insert keymap (minus snippet keys), but has no terminal sources and installs the maps only on a direct n→t mode change. Snacks terminals go n→nt→t, so in testing no blink maps were set and keys reached the shell. If they ever do get installed, Ctrl-n/Ctrl-p/Ctrl-Space are swallowed (verified by forcing them)."}, + {"k": "Copilot items", "m": ["i"], "d": "Copilot suggestions also appear as blink menu items (kind Copilot, ranked first)", "s": "blink-copilot", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "Source copilot via blink-copilot: up to 3 completions, score_offset = 100. Sources order: lsp, path, snippets, buffer, copilot. Accept like any item with Ctrl-y."}, + {"k": "ghost text", "m": ["i"], "d": "Native LSP inline completion (Copilot ghost text) is enabled", "s": "config", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "vim.lsp.inline_completion.enable() in lua/plugins.lua. Accept with Tab (3rd step of the chain); there is no key to cycle alternatives (vim.lsp.inline_completion.select() is unmapped)."}, + {"k": "", "m": ["n"], "d": "Jump to the pending next-edit suggestion; press again to apply it", "s": "config", "cat": "Completion & AI", "ctx": "Sidekick NES (Copilot next edit)", "n": "lua/keymaps.lua maps normal Tab to sidekick.nes_jump_or_apply(); with no suggestion it falls back to jump forward. Ctrl-i is mapped separately for terminals that can tell the two apart", "e": 1, "sl": "NES"}, + {"k": "", "m": ["n", "i"], "d": "Dismiss the current next-edit suggestion", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick NES (Copilot next edit)", "n": "Not a mapping: a vim.on_key watcher clears suggestions whenever Esc is typed (nes.clear.esc = true). Suggestions refresh on leaving insert mode and on text changes."}, + {"k": "", "m": ["n", "t"], "d": "Pick buffers to send to the AI tool", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["n", "t"], "d": "Pick files to send to the AI tool", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["t"], "d": "Insert a prompt or context (incl. this config's haunt_all / haunt_buffer)", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua. This config maps no key to open the CLI; use :Sidekick cli toggle / select. Custom prompts haunt_all and haunt_buffer send Haunt bookmarks."}, + {"k": "", "m": ["n", "t"], "d": "Hide the CLI window", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["n", "t"], "d": "Go back to the previous window, keeping the CLI open", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["t"], "d": "Leave terminal mode (normal mode in the CLI)", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["n"], "d": "Hide the CLI window", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "q", "m": ["n"], "d": "Hide the CLI window", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["n"], "d": "Send Enter to the tool and go back to terminal mode", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["t"], "d": "Move to the window on the left (split layouts only)", "s": "sidekick", "cat": "Completion & AI", "ctx": "Sidekick AI CLI terminal", "n": "Defaults from sidekick/config.lua (CLI window opens on the right, 80 cols). No key opens it in this config: use :Sidekick cli toggle."}, + {"k": "", "m": ["n", "x", "o"], "d": "Go back up one level in the key tree", "s": "which-key", "cat": "Help", "ctx": "which-key popup", "e": 1}, + {"k": "", "m": ["n", "x", "o"], "d": "Close the popup (and cancel the pending keys)", "s": "which-key", "cat": "Help", "ctx": "which-key popup"}, + {"k": "", "m": ["n", "x", "o"], "d": "Scroll the popup down", "s": "which-key", "cat": "Help", "ctx": "which-key popup"}, + {"k": "", "m": ["n", "x", "o"], "d": "Scroll the popup up", "s": "which-key", "cat": "Help", "ctx": "which-key popup"}, + {"k": "{key}", "m": ["n", "x", "o"], "d": "Continue the key sequence / run the mapping shown next to it", "s": "which-key", "cat": "Help", "ctx": "which-key popup", "n": "The popup appears 200 ms after a prefix such as Space, g, z, ] (0 ms for its plugin popups). Groups show with +."}, + {"k": "\"", "m": ["n", "x"], "d": "Popup listing every register with its contents; type the register name", "s": "which-key", "cat": "Register & macro", "ctx": "which-key popup", "n": "which-key's registers plugin; the popup appears immediately."}, + {"k": "", "m": ["i", "c"], "d": "Popup listing registers before inserting one", "s": "which-key", "cat": "Register & macro", "ctx": "which-key popup", "n": "Verified as a buffer-local which-key-trigger registers map in insert mode."}, + {"k": "' ` g' g`", "m": ["n"], "d": "Popup listing marks with their line/file; type the mark name", "s": "which-key", "cat": "Mark & jump", "ctx": "which-key popup", "n": "which-key's marks plugin (normal mode only; its triggers are not installed in Visual or Operator-pending mode)."}, + {"k": "z=", "m": ["n"], "d": "Spelling suggestions popup: press 1-9, 0, a-z… to replace the word", "s": "which-key", "cat": "Spell", "ctx": "which-key popup", "n": "Shows 20 suggestions for the bad word under the cursor. With a count (2z=) Vim's own behaviour runs."}, + {"k": "r", "m": ["n"], "d": "Replace: apply the replacement to every match", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r.", "e": 1}, + {"k": "s", "m": ["n"], "d": "Sync all: write your edits of result lines back to the files", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Edit the result lines like normal text, then sync. Local leader is \\, so e.g. \\ r."}, + {"k": "l", "m": ["n"], "d": "Sync the result line under the cursor", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "n", "m": ["n"], "d": "Sync the result line under the cursor, then move to the next match", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "p", "m": ["n"], "d": "Sync the result line under the cursor, then move to the previous match", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "v", "m": ["n"], "d": "Sync all changes in the current file", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "j", "m": ["n"], "d": "Apply the change on this line, drop it from the list, open the next one", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "k", "m": ["n"], "d": "Apply the change on this line, drop it from the list, open the previous one", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "q", "m": ["n"], "d": "Send the results to the quickfix list", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "t", "m": ["n"], "d": "Open search history (Enter picks an entry)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "a", "m": ["n"], "d": "Add the current search to history", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "f", "m": ["n"], "d": "Refresh (re-run the search)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "o", "m": ["n"], "d": "Open the match location (cursor stays in Grug-far)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "i", "m": ["n"], "d": "Preview the match location in a float", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "b", "m": ["n"], "d": "Abort the running search / replace", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "c", "m": ["n"], "d": "Close Grug-far", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "w", "m": ["n"], "d": "Toggle showing the underlying search command (rg / ast-grep)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "e", "m": ["n"], "d": "Cycle the search engine (ripgrep → astgrep → astgrep-rules)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "x", "m": ["n"], "d": "Cycle the replacement interpreter (default → lua → vimscript)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "Local leader is \\, so e.g. \\ r."}, + {"k": "", "m": ["n"], "d": "Go to the match location (in the other window)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far", "n": "In the history buffer Enter picks the history entry."}, + {"k": "", "m": ["n"], "d": "Open the next match location", "s": "grug-far", "cat": "Search", "ctx": "Grug-far"}, + {"k": "", "m": ["n"], "d": "Open the previous match location", "s": "grug-far", "cat": "Search", "ctx": "Grug-far"}, + {"k": "", "m": ["n"], "d": "Jump to the next input field (Search, Replace, Files filter, Flags, Paths)", "s": "grug-far", "cat": "Search", "ctx": "Grug-far"}, + {"k": "", "m": ["n"], "d": "Jump to the previous input field", "s": "grug-far", "cat": "Search", "ctx": "Grug-far"}, + {"k": "g?", "m": ["n"], "d": "Show Grug-far help", "s": "grug-far", "cat": "Search", "ctx": "Grug-far"}, + {"k": "", "m": ["n"], "d": "Restore the buffer to the undo state under the cursor", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree", "n": "Toggle with Space u u. Config: focus jumps into the tree on toggle, 32-col panel, diff panel closed by default (D), help line hidden (? still works), Nerd Font tree glyphs. Persistent undofile is on, so history survives restarts.", "e": 1}, + {"k": "J", "m": ["n"], "d": "Move to the previous (older) undo state", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "K", "m": ["n"], "d": "Move to the next (newer) undo state", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "<", "m": ["n"], "d": "Move to the previous saved state", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": ">", "m": ["n"], "d": "Move to the next saved state", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "u", "m": ["n"], "d": "Undo (in the target buffer)", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "", "m": ["n"], "d": "Redo (in the target buffer)", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "D", "m": ["n"], "d": "Toggle the diff panel", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "=", "m": ["n"], "d": "Set the diff marker on this state (diff against it)", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "M", "m": ["n"], "d": "Clear the diff marker", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "T", "m": ["n"], "d": "Toggle relative / absolute timestamps", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "C", "m": ["n"], "d": "Clear the undo history (asks first)", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "", "m": ["n"], "d": "Move focus back to the edited buffer", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "?", "m": ["n"], "d": "Toggle quick help", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "q", "m": ["n"], "d": "Close the undotree panel", "s": "undotree", "cat": "Undo & repeat", "ctx": "Undotree"}, + {"k": "", "m": ["n"], "d": "Smart action: follow link / list tag / fold heading / toggle checkbox", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes. In order: accept an inlay-hint action; follow the link under the cursor; open the tag picker on a tag; za on a heading if Markdown folding is on; otherwise toggle the line's checkbox (create_new = true, so plain lines become - [ ]; states cycle [ ] [~] [!] [>] [x]). In vault notes this shadows the global Leap Enter.", "e": 1}, + {"k": "]o", "m": ["n"], "d": "Jump to the next link in the note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes."}, + {"k": "[o", "m": ["n"], "d": "Jump to the previous link in the note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes."}, + {"k": "gd", "m": ["n"], "d": "Follow the [[link]] under the cursor (obsidian LSP definition)", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes. obsidian.nvim starts an in-process LSP server for notes (definition, references, rename, code actions, symbols, completion of [[ links and # tags), so the config's LSP keys work on notes."}, + {"k": "gr", "m": ["n"], "d": "List backlinks / references to this note or heading (obsidian LSP)", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes. obsidian.nvim starts an in-process LSP server for notes (definition, references, rename, code actions, symbols, completion of [[ links and # tags), so the config's LSP keys work on notes."}, + {"k": "cr", "m": ["n"], "d": "Rename the note and update every link to it (LSP rename via IncRename)", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes. obsidian.nvim starts an in-process LSP server for notes (definition, references, rename, code actions, symbols, completion of [[ links and # tags), so the config's LSP keys work on notes."}, + {"k": "ca", "m": ["n"], "d": "Obsidian code actions (e.g. link selection to new note, extract note)", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes. obsidian.nvim starts an in-process LSP server for notes (definition, references, rename, code actions, symbols, completion of [[ links and # tags), so the config's LSP keys work on notes."}, + {"k": "ss", "m": ["n"], "d": "Headings outline of the note (LSP document symbols)", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes", "n": "Only in Markdown notes inside the vaults ~/Documents/Notebook and ~/Documents/Notes. obsidian.nvim starts an in-process LSP server for notes (definition, references, rename, code actions, symbols, completion of [[ links and # tags), so the config's LSP keys work on notes."}, + {"k": ":Obsidian today", "m": [":"], "d": "Open (or create) today's daily note", "s": "obsidian.nvim", "cat": "Notes", "n": "legacy_commands = false: only the :Obsidian <sub> form exists. Bare :Obsidian opens a menu of all subcommands."}, + {"k": ":Obsidian yesterday", "m": [":"], "d": "Open the daily note for the previous working day", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian tomorrow", "m": [":"], "d": "Open the daily note for the next working day", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian dailies", "m": [":"], "d": "Picker of daily notes", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian new", "m": [":"], "d": "Create a new note (title as argument or prompt)", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian new_from_template", "m": [":"], "d": "Create a new note from a template", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian open", "m": [":"], "d": "Open a note in the Obsidian app", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian quick_switch", "m": [":"], "d": "Fuzzy-find a note by name and open it", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian search", "m": [":"], "d": "Grep the vault", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian tags", "m": [":"], "d": "Picker of tags and the notes that use them", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian backlinks", "m": [":"], "d": "Picker of notes linking to this note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian links", "m": [":"], "d": "Picker of all links in this note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian follow_link", "m": [":"], "d": "Follow the link under the cursor", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian link", "m": [":"], "d": "Link the selection to an existing note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian link_new", "m": [":"], "d": "Link the selection to a new note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian extract_note", "m": [":"], "d": "Move the selection into a new note and link it", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian toggle_checkbox", "m": [":"], "d": "Cycle the checkbox state on the line(s)", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian rename", "m": [":"], "d": "Rename the note and update backlinks", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian template", "m": [":"], "d": "Insert a template at the cursor", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian paste_img", "m": [":"], "d": "Paste an image from the clipboard into the vault", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian toc", "m": [":"], "d": "Table of contents of this note in a picker", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian footnotes", "m": [":"], "d": "Picker of footnotes in this note", "s": "obsidian.nvim", "cat": "Notes", "ctx": "Obsidian notes"}, + {"k": ":Obsidian workspace", "m": [":"], "d": "Show or switch the workspace (notebook / notes)", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian unique_note", "m": [":"], "d": "Create a note with a unique (timestamp) id", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian bookmarks", "m": [":"], "d": "Picker of bookmarked notes", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian rebuild_cache", "m": [":"], "d": "Rebuild the vault cache", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian check", "m": [":"], "d": "Check the vault for problems", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian sync", "m": [":"], "d": "Run an Obsidian sync action", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian help", "m": [":"], "d": "Find a page in obsidian.nvim's bundled help docs", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": ":Obsidian helpgrep", "m": [":"], "d": "Grep obsidian.nvim's help vault", "s": "obsidian.nvim", "cat": "Notes"}, + {"k": "Bookmark signs", "m": ["n"], "d": "Haunt: bookmarks show a 󱙝 sign and the annotation as end-of-line virtual text", "s": "haunt", "cat": "Mark & jump", "ctx": "Haunt bookmarks", "n": "Space h a annotates (creating the bookmark if needed; empty input cancels), Space h t toggles the annotation text, Space h n/p walk bookmarks, Space h d deletes, Space h l lists. Stored per project and per git branch."}, + {"k": ":IncRename {new}", "m": [":"], "d": "Rename the LSP symbol with a live preview of every change", "s": "inc-rename", "cat": "LSP & diagnostics", "ctx": "IncRename (Space c r)", "n": "Space c r types :IncRename <cword> for you; with Noice's inc_rename preset the command line appears as a small popup above the cursor. Edit the name and watch all occurrences update (highlighted Substitute); Enter renames across files, Esc cancels. An empty name cancels the preview.", "e": 1}, + {"k": "format on save", "m": ["n"], "d": "Buffers are formatted on every write", "s": "conform", "cat": "Edit", "ctx": "Conform", "n": "format_on_save (500 ms, LSP fallback) in lua/plugins.lua; set vim.g.disable_autoformat / vim.b.disable_autoformat to turn it off. Autosave on InsertLeave/TextChanged means this runs often."}, + {"k": ":Gitsigns nav_hunk next", "m": [":"], "d": "Jump to the next changed hunk", "s": "gitsigns", "cat": "Git", "n": "Hunk navigation is NOT mapped in this config (no ]c/[c, no h… gitsigns keys); use these commands. The older next_hunk / prev_hunk still work but are deprecated."}, + {"k": ":Gitsigns nav_hunk prev", "m": [":"], "d": "Jump to the previous changed hunk", "s": "gitsigns", "cat": "Git", "n": "Hunk navigation is NOT mapped in this config (no ]c/[c, no h… gitsigns keys); use these commands. The older next_hunk / prev_hunk still work but are deprecated."}, + {"k": ":Gitsigns nav_hunk first|last", "m": [":"], "d": "Jump to the first / last hunk in the file", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns preview_hunk", "m": [":"], "d": "Show the hunk's diff in a float", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns preview_hunk_inline", "m": [":"], "d": "Show the hunk's removed lines inline", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns stage_hunk", "m": [":"], "d": "Stage the hunk under the cursor (or the range); on a staged hunk it unstages", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns reset_hunk", "m": [":"], "d": "Discard the hunk under the cursor (or the range)", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns undo_stage_hunk", "m": [":"], "d": "Undo the last stage_hunk (deprecated: run stage_hunk on the staged hunk)", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns stage_buffer", "m": [":"], "d": "Stage the whole file", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns reset_buffer", "m": [":"], "d": "Discard all changes in the file", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns reset_buffer_index", "m": [":"], "d": "Unstage the whole file", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns select_hunk", "m": [":"], "d": "Visually select the hunk", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns blame_line", "m": [":"], "d": "Show blame for the current line in a float", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns blame", "m": [":"], "d": "Open a full-file blame side panel", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns diffthis", "m": [":"], "d": "Diff the file against the index (or a revision)", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns show", "m": [":"], "d": "Open the file as it was at a revision", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns toggle_current_line_blame", "m": [":"], "d": "Toggle line blame (shown in lualine here, not virtual text)", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns toggle_word_diff", "m": [":"], "d": "Toggle word-level diff highlighting", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns toggle_linehl", "m": [":"], "d": "Toggle highlighting changed lines", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns toggle_numhl", "m": [":"], "d": "Toggle highlighting changed line numbers", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns toggle_deleted", "m": [":"], "d": "Toggle showing deleted lines inline (deprecated: use preview_hunk_inline)", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns setqflist all", "m": [":"], "d": "Send hunks to the quickfix list (current file, or all)", "s": "gitsigns", "cat": "Git"}, + {"k": ":Gitsigns change_base", "m": [":"], "d": "Diff signs against another revision", "s": "gitsigns", "cat": "Git"}, + {"k": "", "m": ["n"], "d": "Open the blame context menu", "s": "gitsigns", "cat": "Git", "ctx": "Gitsigns blame panel (:Gitsigns blame)"}, + {"k": "r", "m": ["n"], "d": "Re-blame at this commit", "s": "gitsigns", "cat": "Git", "ctx": "Gitsigns blame panel (:Gitsigns blame)"}, + {"k": "R", "m": ["n"], "d": "Re-blame at the commit's parent", "s": "gitsigns", "cat": "Git", "ctx": "Gitsigns blame panel (:Gitsigns blame)"}, + {"k": "d", "m": ["n"], "d": "Diff this commit (new tab)", "s": "gitsigns", "cat": "Git", "ctx": "Gitsigns blame panel (:Gitsigns blame)"}, + {"k": "s", "m": ["n"], "d": "Show the commit in a vertical split", "s": "gitsigns", "cat": "Git", "ctx": "Gitsigns blame panel (:Gitsigns blame)"}, + {"k": "S", "m": ["n"], "d": "Show the commit in a new tab", "s": "gitsigns", "cat": "Git", "ctx": "Gitsigns blame panel (:Gitsigns blame)"}, + {"k": "q", "m": ["n"], "d": "Close the gitsigns popup (after entering it)", "s": "gitsigns", "cat": "Git", "ctx": "Gitsigns preview / blame float"}, + {"k": "q", "m": ["n"], "d": "Close the message split / popup (:Noice, :Noice last, errors)", "s": "noice", "cat": "Command line", "ctx": "Noice", "n": "Long messages (≥ 20 lines) go to a split (long_message_to_split). : opens a centred command palette popup, / and ? stay at the bottom (bottom_search). Noice defines no other keys; scrolling LSP docs with Ctrl-f/Ctrl-b is not configured."}, + {"k": "K / gx", "m": ["n"], "d": "Inside Noice hover/docs windows: open the link under the cursor", "s": "noice", "cat": "LSP & diagnostics", "ctx": "Noice", "n": "Buffer-local in Noice markdown views; otherwise they fall through to normal K/gx."}, + {"k": "i", "m": ["n"], "d": "Install the package under the cursor", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "u", "m": ["n"], "d": "Update / reinstall the package", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "X", "m": ["n"], "d": "Uninstall the package", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "c", "m": ["n"], "d": "Check the package for a new version", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "U", "m": ["n"], "d": "Update all installed packages", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "C", "m": ["n"], "d": "Check which packages are outdated", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "", "m": ["n"], "d": "Expand the package details / toggle its install log", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "", "m": ["n"], "d": "Cancel the installation", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "", "m": ["n"], "d": "Filter by language", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "1 2 3 4 5", "m": ["n"], "d": "Show All / LSP / DAP / Linter / Formatter packages", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "g?", "m": ["n"], "d": "Toggle help", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "q", "m": ["n"], "d": "Close the window", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "", "m": ["n"], "d": "Clear the language filter / search, else close", "s": "mason", "cat": "LSP & diagnostics", "ctx": "Mason"}, + {"k": "y{motion}", "m": ["n", "x"], "d": "Yank into the paste window (edit, then send it to the shell)", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback", "n": "A plain yank opens a small paste window below; \"+y instead copies to the clipboard and quits back to Kitty. Only inside a kitty-scrollback.nvim session (opened from a Kitty mapping generated by :KittyScrollbackGenerateKittens)."}, + {"k": "y{motion}", "m": ["n"], "d": "Yank {motion} to the system clipboard and return to Kitty", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": "y", "m": ["x"], "d": "Yank the selection to the system clipboard and return to Kitty", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": "yy", "m": ["n"], "d": "Yank the line to the clipboard and return to Kitty", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": "Y", "m": ["n"], "d": "Yank to end of line to the clipboard and return to Kitty", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback", "n": "The visual Space Y default points at an undefined <Plug> map in this plugin version, so it does nothing."}, + {"k": "", "m": ["n", "i", "x"], "d": "Execute the paste window text (or selection) in the shell", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": "", "m": ["n", "i", "x"], "d": "Paste the text (or selection) to the shell without running it", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": "g?", "m": ["n"], "d": "Toggle the paste window's key footer", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": "q", "m": ["n"], "d": "Close the paste window, or quit back to Kitty from the scrollback", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": "", "m": ["n", "i", "t"], "d": "Quit back to Kitty", "s": "kitty-scrollback", "cat": "Terminal", "ctx": "Kitty scrollback"}, + {"k": ":Browse [url]", "m": [":"], "d": "gx.nvim: open the URL / file / plugin / GitHub ref / package under the cursor", "s": "gx", "cat": "Misc", "n": "Also on gx (Normal and Visual, lua/keymaps.lua:358). Handlers: plugin names → GitHub, #123 issues, package.json deps, search fallback."}, + {"k": ":Qalc[!] [file]", "m": [":"], "d": "Open a calculator buffer: each line is evaluated by qalc as you type", "s": "qalc", "cat": "Misc", "n": "Results appear as virtual text; ! makes it a scratch buffer. *.qalc files attach automatically. :QalcYank [reg] yanks the result of the current line (the unnamed register when none is given)."}, + {"k": "cb…", "m": ["n", "v"], "d": "Comment boxes: cbb centred box, cbl centred line, cbd delete, cbk catalog", "s": "comment-box", "cat": "Comment", "ctx": "Comment-box", "n": "The box and line commands take a style number (see the catalog) and work on a range; e.g. :CBccbox 3. :CBd removes a box, :CBy yanks a box's text without the frame."}, + {"k": ":Mason", "m": [":"], "d": "Open Mason's package manager window", "s": "mason", "cat": "LSP & diagnostics"}, + {"k": ":Sidekick nes toggle", "m": [":"], "d": "Enable / disable Copilot next-edit suggestions", "s": "sidekick", "cat": "Completion & AI", "n": "Other subcommands: nes apply|jump|update|clear|enable|disable, cli toggle|show|hide|close|focus|select|send|prompt."}, + {"k": ":Sidekick cli toggle", "m": [":"], "d": "Open / hide the AI CLI terminal (claude, codex, copilot, gemini…)", "s": "sidekick", "cat": "Completion & AI"}, + {"k": ":Noice", "m": [":"], "d": "Message history in a split (also :Noice last / errors / all / dismiss)", "s": "noice", "cat": "Command line"}, + {"k": "", "m": ["i"], "d": "Not mapped by blink: insert a digraph (Vim's i_CTRL-K)", "s": "blink.cmp", "cat": "Completion & AI", "ctx": "Completion menu (insert)", "n": "The preset's signature toggle is disabled ([\"<C-k>\"] = false) so digraphs work; signature help shows automatically, or press Neovim's Ctrl-s"} + ], + "commands": { + "BlinkCmp": "blink.cmp: status (checkhealth), build or build-log of the fuzzy matcher", + "Browse": "gx.nvim (also on gx): open URL, path, plugin, GitHub issue or package under the cursor / given arg", + "CBacbox": "Centred adapted box (box fits the text)", + "CBalbox": "Left-aligned adapted box (deprecated alias of CBlabox)", + "CBcabox": "Centred adapted box (deprecated alias of CBacbox)", + "CBcatalog": "Open the comment-box style catalog", + "CBccbox": "Centred box, text centred", + "CBccline": "Centred titled line, title centred", + "CBclbox": "Centred box, text left-aligned", + "CBcline": "Centred line (no title)", + "CBclline": "Centred titled line, title left-aligned", + "CBcrbox": "Centred box, text right-aligned", + "CBcrline": "Centred titled line, title right-aligned", + "CBd": "Remove the box or titled line around the range", + "CBlabox": "Left-aligned adapted box (box fits the text)", + "CBlcbox": "Left-aligned box, text centred", + "CBlcline": "Left-aligned titled line, title centred", + "CBline": "Left-aligned line (no title)", + "CBllbox": "Left-aligned box, text left-aligned", + "CBlline": "Left-aligned line (no title)", + "CBllline": "Left-aligned titled line, title left-aligned", + "CBlrbox": "Left-aligned box, text right-aligned", + "CBlrline": "Left-aligned titled line, title right-aligned", + "CBrabox": "Right-aligned adapted box (box fits the text)", + "CBrcbox": "Right-aligned box, text centred", + "CBrcline": "Right-aligned titled line, title centred", + "CBrlbox": "Right-aligned box, text left-aligned", + "CBrline": "Right-aligned line (no title)", + "CBrlline": "Right-aligned titled line, title left-aligned", + "CBrrbox": "Right-aligned box, text right-aligned", + "CBrrline": "Right-aligned titled line, title right-aligned", + "CBy": "Yank the text of a box or titled line without the frame", + "Catppuccin": "Switch Catppuccin flavour (latte, frappe, macchiato, mocha)", + "CatppuccinCompile": "Recompile the Catppuccin highlight cache", + "ConformInfo": "Show Conform's formatters, their status and the log", + "FZF": "fzf.vim (system): fuzzy-find files in a directory with the fzf binary", + "Gitsigns": "Gitsigns actions: nav_hunk, stage_hunk, blame_line, diffthis… (no keys mapped)", + "GrugFar": "Open Grug-far search & replace (optional engine arg; a visual range prefills the search)", + "GrugFarWithin": "Grug-far limited to the visual selection", + "HauntAnnotate": "Add or edit the annotation of the bookmark on this line", + "HauntChangeDataDir": "Change where Haunt stores bookmarks", + "HauntClear": "Clear all bookmarks in the current file", + "HauntClearAll": "Clear every bookmark", + "HauntDelete": "Delete the bookmark on this line", + "HauntList": "Picker of all bookmarks", + "HauntMigrate": "Migrate bookmarks from v1 to v2 storage", + "HauntNext": "Jump to the next bookmark", + "HauntPrev": "Jump to the previous bookmark", + "HauntQf": "Send this buffer's annotations to the quickfix list", + "HauntQfAll": "Send all annotations to the quickfix list", + "HauntReload": "Reload bookmarks from disk", + "HauntToggle": "Toggle annotation visibility", + "IncRename": "LSP rename with live preview of all changes (Space c r)", + "JdtShowLogs": "nvim-jdtls (system package): show the jdtls language server logs", + "JdtWipeDataAndRestart": "nvim-jdtls (system package): wipe the workspace data and restart jdtls", + "KittyScrollbackCheckHealth": "Run kitty-scrollback.nvim health checks", + "KittyScrollbackGenerateCommandLineEditing": "Print shell config to edit the command line in kitty-scrollback", + "KittyScrollbackGenerateKittens": "Print kitty.conf mappings that launch kitty-scrollback", + "LazyDev": "lazydev.nvim: debug info / show the Lua LSP library settings", + "LspInstall": "mason-lspconfig: install LSP servers", + "LspUninstall": "mason-lspconfig: uninstall LSP servers", + "Mason": "Open Mason's package manager window", + "MasonInstall": "Install Mason packages by name", + "MasonLog": "Open the Mason log", + "MasonUninstall": "Uninstall Mason packages", + "MasonUninstallAll": "Uninstall every Mason package", + "MasonUpdate": "Update the Mason registries", + "Noice": "Noice message history (subcommands: last, errors, all, dismiss, pick…)", + "NoiceAll": "All Noice messages in a split", + "NoiceConfig": "Show Noice's active configuration", + "NoiceDebug": "Toggle Noice debug mode", + "NoiceDisable": "Disable Noice (back to the native UI)", + "NoiceDismiss": "Dismiss all visible Noice messages", + "NoiceEnable": "Enable Noice", + "NoiceErrors": "Noice error messages in a popup", + "NoiceFzf": "Noice history in fzf-lua (not installed here)", + "NoiceHistory": "Noice message history in a split", + "NoiceLast": "Show the last Noice message in a popup", + "NoiceLog": "Open the Noice log file", + "NoicePick": "Noice history in the Snacks picker", + "NoiceRoutes": "Show Noice's message routes", + "NoiceSnacks": "Noice history in the Snacks picker", + "NoiceStats": "Show Noice statistics", + "NoiceTelescope": "Noice history in Telescope (not installed here)", + "NoiceViewstats": "Show Noice view statistics", + "Obsidian": "obsidian.nvim: :Obsidian today|new|search|quick_switch|tags|…; bare opens a menu", + "Qalc": "Open a qalc calculator buffer (! for a scratch buffer)", + "QalcAttach": "Evaluate the current buffer's lines with qalc", + "QalcYank": "Yank the qalc result of the current line (unnamed register unless one is given)", + "Sidekick": "Sidekick: nes {apply|jump|toggle|…}, cli {toggle|select|send|prompt|…}", + "TSContext": "Treesitter context: enable, disable or toggle the sticky header", + "UndotreeFocus": "Focus the undotree panel", + "UndotreeHide": "Close the undotree panel", + "UndotreePersistUndo": "Turn on persistent undo for this file (no-op here: 'undofile' is already on)", + "UndotreeShow": "Open the undotree panel", + "UndotreeToggle": "Toggle the undotree panel (Space u u)", + "WakaTimeApiKey": "Set the WakaTime API key", + "WakaTimeCliLocation": "Show the wakatime-cli path", + "WakaTimeCliVersion": "Show the wakatime-cli version", + "WakaTimeDebugDisable": "Turn WakaTime debug logging off", + "WakaTimeDebugEnable": "Turn WakaTime debug logging on", + "WakaTimeFileExpert": "Show who has spent the most time on this file", + "WakaTimeScreenRedrawDisable": "Never redraw the screen after sending heartbeats", + "WakaTimeScreenRedrawEnable": "Always redraw the screen after sending heartbeats", + "WakaTimeScreenRedrawEnableAuto": "Redraw the screen after heartbeats only when needed", + "WakaTimeStatusBarDisable": "Hide WakaTime's status bar text", + "WakaTimeStatusBarEnable": "Show WakaTime's status bar text", + "WakaTimeToday": "Show today's coding time", + "WhichKey": "Show the which-key popup: :WhichKey [mode] [keys]" + }, + "notes": [ + {"title": "Plugin keys that change stock behaviour", "kind": "warn", "lead": "Verified in plugin source and, where marked, by running the real config headless.", "rows": [["Enter in vault notes", "obsidian.nvim's smart action (buffer-local) shadows the global Leap Enter and turns plain lines into checkboxes."], ["Enter in git log pickers", "Snacks confirms with git checkout: detached HEAD (Space g l) or overwriting the file (Space g f, Space g L)."], ["Dashboard s", "Missing: Snacks only detects persistence.nvim under lazy.nvim, so there is no Restore Session item."]], "keys": [""]} + ] +} diff --git a/docs/cheatsheet/dump.lua b/docs/cheatsheet/dump.lua new file mode 100644 index 0000000..af338c9 --- /dev/null +++ b/docs/cheatsheet/dump.lua @@ -0,0 +1,239 @@ +-- Dump the live keymap/command state of this config as JSON. +-- +-- Run by gen_cheatsheet.py as: +-- CHEATSHEET_DUMP=out.json nvim --headless -V1 -c "luafile /abs/path/dump.lua" +-- -V1 makes Neovim record "last set from :" for Lua mappings, +-- which is how each map is attributed to the config, a plugin or the runtime. + +local out = assert(vim.env.CHEATSHEET_DUMP, "set CHEATSHEET_DUMP to the output path") +local MODES = { "n", "x", "s", "o", "i", "c", "t" } +local SAMPLES = { + { name = "lua", path = vim.fn.stdpath("config") .. "/lua/keymaps.lua", lsp = true }, + { name = "markdown", ft = "markdown" }, +} + +local function script_path(sid) + if not sid or sid <= 0 then + return nil + end + local info = vim.fn.getscriptinfo({ sid = sid })[1] + if not info then + return nil + end + -- Lua modules compiled into Neovim (vim/_core/defaults.lua, …) are reported + -- by module path, which getscriptinfo() resolves against the cwd. + local mod = info.name:match("/(vim/_core/.*)$") + if mod and not vim.uv.fs_stat(info.name) then + return vim.env.VIMRUNTIME .. "/lua/" .. mod .. (mod:match("%.lua$") and "" or ".lua") + end + return info.name +end + +local function collect(maps, scope) + local res = {} + for _, m in ipairs(maps) do + res[#res + 1] = { + mode = m.mode, + -- keytrans() turns a raw leader " " into , but would also fold + -- // into //, which map separately. + lhs = m.lhs:find("<[Cc]%-[IiMm%[]>") and m.lhs + or vim.fn.keytrans(vim.api.nvim_replace_termcodes(m.lhs, true, true, true)), + rhs = m.rhs ~= "" and m.rhs or nil, + callback = m.callback ~= nil or nil, + desc = m.desc, + expr = m.expr == 1 or nil, + nowait = m.nowait == 1 or nil, + file = script_path(m.sid), + line = m.lnum ~= 0 and m.lnum or nil, + scope = scope, + } + end + return res +end + +local function all_modes(get) + local res = {} + for _, mode in ipairs(MODES) do + for _, m in ipairs(get(mode)) do + -- nvim_get_keymap("x") etc. also report "v"/" " maps once per queried + -- mode; keep the queried mode so every entry belongs to exactly one. + m.mode = mode + res[#res + 1] = m + end + end + return res +end + +local function commands(cmds, scope) + local res = {} + for name, c in pairs(cmds) do + local file, line + if c.script_id and c.script_id > 0 then + file = script_path(c.script_id) + end + res[#res + 1] = { + name = name, + desc = c.definition ~= "" and c.definition or nil, + nargs = c.nargs, + range = c.range, + bang = c.bang or nil, + file = file, + scope = scope, + } + end + table.sort(res, function(a, b) + return a.name < b.name + end) + return res +end + +local function dump() + local data = { + version = tostring(vim.version()), + vimruntime = vim.env.VIMRUNTIME, + config = vim.fn.stdpath("config"), + data = vim.fn.stdpath("data"), + leader = vim.g.mapleader, + localleader = vim.g.maplocalleader, + neovide = vim.g.neovide and true or false, + options = { + clipboard = vim.o.clipboard, + relativenumber = vim.o.relativenumber, + timeoutlen = vim.o.timeoutlen, + wrap = vim.o.wrap, + ignorecase = vim.o.ignorecase, + smartcase = vim.o.smartcase, + autowrite = vim.o.autowrite, + undofile = vim.o.undofile, + shiftwidth = vim.o.shiftwidth, + }, + maps = collect(all_modes(vim.api.nvim_get_keymap), "global"), + commands = commands(vim.api.nvim_get_commands({ builtin = false }), "global"), + buffers = {}, + plugins = {}, + } + + for _, p in ipairs(vim.pack.get()) do + data.plugins[#data.plugins + 1] = { name = p.spec.name, src = p.spec.src, active = p.active } + end + + -- Plugin-internal keys that only exist inside their own windows. + local ok, snacks_cfg = pcall(function() + return require("snacks.picker.config").get({}) + end) + if ok then + local function keys(t) + local res = {} + for lhs, v in pairs(t or {}) do + if v then + local action = type(v) == "table" and (v[1] or v.action) or v + res[#res + 1] = { + lhs = lhs, + action = type(action) == "string" and action or "function", + mode = type(v) == "table" and v.mode or nil, + desc = type(v) == "table" and v.desc or nil, + } + end + end + return res + end + data.snacks_picker = { + input = keys(snacks_cfg.win.input.keys), + list = keys(snacks_cfg.win.list.keys), + preview = keys(snacks_cfg.win.preview.keys), + } + local eok, explorer = pcall(function() + return require("snacks.picker.config").get({ source = "explorer" }) + end) + if eok then + data.snacks_explorer = keys(explorer.win.list.keys) + end + end + + -- blink.cmp resolves its preset + overrides into keymap.mappings at setup. + local bok, blink_keymap = pcall(require, "blink.cmp.keymap") + if bok and blink_keymap.mappings then + data.blink = {} + for kind, mappings in pairs(blink_keymap.mappings) do + local res = {} + for lhs, actions in pairs(mappings) do + local names = {} + for _, a in ipairs(type(actions) == "table" and actions or {}) do + names[#names + 1] = type(a) == "string" and a or "function" + end + res[#res + 1] = { lhs = lhs, actions = names } + end + data.blink[kind] = res + end + end + + local fok, flash_cfg = pcall(function() + return require("flash.config") + end) + if fok then + data.flash = { + char_keys = flash_cfg.modes.char.keys, + multi_line = flash_cfg.modes.char.multi_line, + jump_labels = flash_cfg.modes.char.jump_labels, + } + end + + local function sample(s) + local buf + if s.path then + vim.cmd.edit(vim.fn.fnameescape(s.path)) + buf = vim.api.nvim_get_current_buf() + else + buf = vim.api.nvim_create_buf(true, false) + vim.api.nvim_set_current_buf(buf) + vim.bo[buf].filetype = s.ft + end + if s.lsp then + vim.wait(15000, function() + return #vim.lsp.get_clients({ bufnr = buf }) > 0 + end, 100) + vim.wait(500) + end + -- Plugins that attach on InsertEnter/BufEnter (blink.cmp, …). + vim.api.nvim_exec_autocmds({ "BufEnter", "InsertEnter" }, { buffer = buf, modeline = false }) + local get = function(mode) + return vim.api.nvim_buf_get_keymap(buf, mode) + end + data.buffers[#data.buffers + 1] = { + name = s.name, + filetype = vim.bo[buf].filetype, + lsp = vim.tbl_map(function(c) + return c.name + end, vim.lsp.get_clients({ bufnr = buf })), + maps = collect(all_modes(get), "buffer"), + commands = commands(vim.api.nvim_buf_get_commands(buf, {}), "buffer"), + } + end + for _, s in ipairs(SAMPLES) do + local sok, err = pcall(sample, s) + if not sok then + io.stderr:write("sample " .. s.name .. " failed: " .. tostring(err) .. "\n") + end + end + + local f = assert(io.open(out, "w")) + f:write(vim.json.encode(data)) + f:close() + vim.cmd("qa!") +end + +-- Plugins load on VimEnter (lua/lazyload.lua); wait for the last of them. +vim.api.nvim_create_autocmd("VimEnter", { + once = true, + callback = function() + -- Headless Neovim never fires UIEnter, which Snacks waits for before + -- setting up UI-only maps (scope: ii/ai/[i/]i). + vim.api.nvim_exec_autocmds("UIEnter", { modeline = false }) + vim.defer_fn(function() + vim.wait(10000, function() + return package.loaded["obsidian"] ~= nil and package.loaded["sidekick"] ~= nil + end, 50) + vim.defer_fn(dump, 300) + end, 50) + end, +}) diff --git a/docs/cheatsheet/gen_cheatsheet.py b/docs/cheatsheet/gen_cheatsheet.py new file mode 100644 index 0000000..115539c --- /dev/null +++ b/docs/cheatsheet/gen_cheatsheet.py @@ -0,0 +1,553 @@ +#!/usr/bin/env python3 +"""Generate cheatsheet.html: every Vim/Neovim key and this config's keymaps. + +Run from anywhere: python3 gen_cheatsheet.py [--dump FILE] + +Sources, merged into one searchable single-file page: + * $VIMRUNTIME/doc/index.txt every built-in key in every mode, plus Ex commands + * the live config dump.lua runs Neovim headless with this config and + records every keymap/command with the file and + line that set it (config, plugin or runtime) + * curated/*.json plugin-internal keys (pickers, completion, text + objects, …), descriptions, categories and notes + +Built-in keys that a mapping replaces, delays or makes unreachable are flagged. +--dump FILE reuses (or, when FILE is missing, writes) a dump instead of +starting Neovim each run. +""" + +import json +import os +import re +import subprocess +import sys +import tempfile +from datetime import date +from pathlib import Path + +HERE = Path(__file__).resolve().parent +OUT = HERE / "cheatsheet.html" + +MOTION_MODES = ["n", "x", "o"] + +# --------------------------------------------------------------------------- +# Live dump +# --------------------------------------------------------------------------- + + +def run_dump(path): + env = dict(os.environ, CHEATSHEET_DUMP=str(path)) + subprocess.run( + ["nvim", "--headless", "-V1", "-c", f"luafile {HERE / 'dump.lua'}"], + env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=120, check=False, + ) + if not Path(path).exists(): + sys.exit("dump.lua produced no output — run it by hand to see the error:\n" + f" CHEATSHEET_DUMP=/tmp/d.json nvim --headless -V1 -c 'luafile {HERE / 'dump.lua'}'") + return json.loads(Path(path).read_text()) + + +def load_dump(): + args = sys.argv[1:] + if "--dump" in args: + path = Path(args[args.index("--dump") + 1]) + return json.loads(path.read_text()) if path.exists() else run_dump(path) + with tempfile.TemporaryDirectory() as tmp: + return run_dump(Path(tmp) / "dump.json") + + +# --------------------------------------------------------------------------- +# Key notation +# --------------------------------------------------------------------------- + + +def index_keys(raw): + """index.txt key column ("CTRL-W CTRL-B", "z", "a\"") -> Vim notation.""" + out = [] + if raw == '"``"': # index.txt quotes this one so the help syntax stays intact + return "``" + for word in raw.split(" "): + if not word: + continue + word = re.sub(r"CTRL-SHIFT-(.)", lambda m: f"", word) + word = re.sub(r"CTRL-(<[^>]+>|\{char\}|.)", lambda m: ctrl(m.group(1)), word) + out.append(word) + return "".join(out) + + +def ctrl(k): + if k.startswith("<") and k.endswith(">"): + return f"" + if k == "{char}": + return "" + return f"" + + +def canon(lhs): + """Comparable form of a key sequence (keytrans style, case-folded names).""" + def fix(m): + inner = m.group(1) + parts = inner.split("-") + mods, key = parts[:-1], parts[-1] or "-" + if len(key) > 1: + key = {"cr": "CR", "esc": "Esc", "tab": "Tab", "bs": "BS", "space": "Space", "nl": "NL", + "lt": "lt", "bar": "Bar", "bslash": "Bslash"}.get(key.lower(), key[0].upper() + key[1:]) + elif mods and set(m.upper() for m in mods) == {"C"} and key.isalpha(): + key = key.upper() + return "<" + "-".join([*(x.upper() for x in mods), key]) + ">" + return re.sub(r"<([^<>]+)>", fix, lhs).replace("", "<").replace("", "<") + + +TOKEN = re.compile(r'\["x\]|\{[^}]+\}|<(?:[A-Za-z0-9]+-)*(?:[A-Za-z0-9]+|[^>\s])>|.') + + +def tokens(lhs): + return TOKEN.findall(lhs) + + +def is_ph(tok): + return tok == '["x]' or (tok.startswith("{") and len(tok) > 1) + + +def base(lhs): + """The keys a mapping would have to take over to replace this command: + leading count/register and trailing {placeholders} removed; None when a + placeholder sits in the middle (e.g. "{char1}{char2}" is fine, + "{char1}{char2}" is not).""" + t = tokens(lhs) + # Only a count or register can come before the keys; "{char1}{char2}" + # starts with a typed character, so no mapping can replace it. + while t and t[0] in ('["x]', "{count}"): + t.pop(0) + while t and is_ph(t[-1]): + t.pop() + if not t or any(is_ph(x) for x in t): + return None + return t + + +def starts_with(longer, shorter): + return len(longer) > len(shorter) and longer[: len(shorter)] == shorter + + +# --------------------------------------------------------------------------- +# Built-ins: index.txt +# --------------------------------------------------------------------------- + +SECTIONS = { + "insert-index": ("i", "Insert mode"), + "normal-index": ("n", "Normal mode"), + "objects": ("o", "Text objects"), + "CTRL-W": ("n", "Window commands"), + "[": ("n", "Square bracket commands"), + "g": ("n", "g commands"), + "z": ("n", "z commands"), + "operator-pending-index": ("o", "Operator-pending mode"), + "visual-index": ("x", "Visual mode"), + "ex-edit-index": ("c", "Command-line editing"), + "terminal-mode-index": ("t", "Terminal mode"), + "ex-cmd-index": (":", "Ex commands"), +} + +# help file -> category, for built-ins without a more specific rule below. +FILE_CAT = { + "motion.txt": "Motion", "change.txt": "Edit", "undo.txt": "Undo & repeat", "scroll.txt": "Scroll & view", + "windows.txt": "Window", "tabpage.txt": "Buffer & tab", "fold.txt": "Fold", "pattern.txt": "Search", + "insert.txt": "Insert", "visual.txt": "Visual", "cmdline.txt": "Command line", "quickfix.txt": "Quickfix & lists", + "tagsrch.txt": "Tags", "diff.txt": "Diff", "spell.txt": "Spell", "editing.txt": "Files", "terminal.txt": "Terminal", + "lsp.txt": "LSP & diagnostics", "diagnostic.txt": "LSP & diagnostics", "repeat.txt": "Register & macro", + "digraph.txt": "Insert", "map.txt": "Misc", "options.txt": "Misc", "various.txt": "Misc", "message.txt": "Misc", + "starting.txt": "Files", "helphelp.txt": "Help", "eval.txt": "Misc", "gui.txt": "Misc", "sign.txt": "Misc", + "if_pyth.txt": "Misc", "if_lua.txt": "Misc", "lua.txt": "Misc", "syntax.txt": "Misc", "autocmd.txt": "Misc", + "usr_41.txt": "Misc", "vimfn.txt": "Misc", "vimeval.txt": "Misc", "print.txt": "Misc", "remote.txt": "Misc", + "mbyte.txt": "Misc", "arabic.txt": "Misc", "rileft.txt": "Misc", "provider.txt": "Misc", "pi_netrw.txt": "Files", + "pi_spec.txt": "Misc", "treesitter.txt": "Misc", "news.txt": "Misc", "uganda.txt": "Help", "intro.txt": "Help", +} +OPERATORS = {"c", "d", "y", "<", ">", "!", "=", "g~", "gu", "gU", "g?", "gq", "gw", "g@", "zf", "gc"} +MARKS = re.compile(r"^(m|'|`|g'|g`||||g;|g,|''|``|\['|\]'|\[`|\]`|'\[|'\]|`\[|`\]|'<|'>|`<|`>|'\"|`\"|'\^|`\^|'\.|`\.)") +REGISTERS = re.compile(r'^("|q|@|Q)') + + +def parse_tags(runtime): + tags = {} + for line in (Path(runtime) / "doc/tags").read_text(errors="replace").splitlines(): + parts = line.split("\t") + if len(parts) >= 2: + tags[parts[0]] = parts[1] + return tags + + +def parse_index(runtime, tags): + lines = (Path(runtime) / "doc/index.txt").read_text().splitlines() + entries, section, in_table, cur, sub = [], None, False, None, None + + def flush(): + nonlocal cur + if cur: + cur["desc"] = re.sub(r"\s+", " ", cur["desc"]).strip() + if cur["desc"] and not cur["desc"].startswith("not used") and cur["desc"] != "reserved": + entries.append(cur) + cur = None + + for raw in lines: + m = re.search(r"\*([^*\s]+)\*", raw) + if raw.startswith(tuple("0123456789")) and m: + flush() + names = re.findall(r"\*([^*\s]+)\*", raw) + section = next((n for n in names if n in SECTIONS), None) + in_table, sub = False, None + continue + if section is None: + continue + if raw.rstrip().endswith("~") and raw.startswith("-----"): + in_table = True + continue + if not in_table: + continue + if raw.startswith("====="): + flush() + section, in_table = None, False + continue + line = raw.expandtabs(8) + if not line.strip(): + flush() + continue + indent = len(line) - len(line.lstrip()) + if raw.startswith("'") and "\t" in raw: + # "'cedit' CTRL-F ...": an option name in the tag column. + raw = raw.split("\t", 1)[1].lstrip("\t") + elif indent == 0 and not raw.startswith("|"): + # Sub-heading inside a table ("commands in CTRL-X submode"). + flush() + sub = re.sub(r"\s*\*[^*]+\*\s*", "", raw).replace("|", "").strip() + continue + if indent >= 24 and cur is not None: + cur["desc"] += " " + line.strip() + continue + if indent >= 24: + continue + flush() + m = re.match(r"^(?:\|([^|]+)\|)?\s*(\S.*)$", raw) + if not m: + continue + tag, body = m.group(1), m.group(2) + sep = re.search(r"\t+|\s{2,}", body) + if sep: + key, rest = body[: sep.start()].strip(), body[sep.end():] + else: + # Key column overflowed into the description with single spaces: + # keep key-like words ("CTRL-V {number}", "CTRL-\\ e {expr}"). + words, key_words = body.split(" "), [] + while words and (not key_words or len(words[0]) == 1 + or re.match(r"^(CTRL-|<|\{)", words[0])): + key_words.append(words.pop(0)) + key, rest = " ".join(key_words), " ".join(words) + note = "" + nm = re.match(r"^([12])\s+(.*)$", rest.strip()) + if nm and section in ("normal-index", "CTRL-W", "[", "g", "z", "visual-index"): + note, rest = nm.group(1), nm.group(2) + mode, sect_name = SECTIONS[section] + cur = {"raw": key, "tag": tag, "note": note, "desc": rest, "section": sect_name, "sect": section, "mode": mode, + "sub": sub} + flush() + + out = [] + for e in entries: + if e["mode"] == ":": + out.append(ex_entry(e, tags)) + continue + keys = index_keys(e["raw"]) + tag = e["tag"] + if e["sect"] == "objects": + modes = ["x", "o"] + elif e["note"] == "1" and e["mode"] == "n": + modes = MOTION_MODES + else: + modes = [e["mode"]] + out.append({ + "k": keys, "m": modes, "d": e["desc"], "s": "builtin", + "t": tag, "h": tags.get(tag) if tag else None, "sec": e["section"], + "cat": builtin_cat(e, keys, tags.get(tag, "") if tag else ""), + "mo": 1 if e["note"] == "1" else None, "ch": 1 if e["note"] == "2" else None, + "ctx": e["sub"][0].upper() + e["sub"][1:] if e["sub"] else None, + }) + return out + + +def ex_entry(e, tags): + name = e["raw"] + return { + "k": name, "m": [":"], "d": e["desc"], "s": "builtin", "t": e["tag"], + "h": tags.get(e["tag"]) if e["tag"] else None, "sec": "Ex commands", + "cat": FILE_CAT.get(tags.get(e["tag"], ""), "Misc") if e["tag"] else "Misc", "ex": 1, + } + + +def builtin_cat(e, keys, helpfile): + if e["sect"] == "objects": + return "Text object" + if e["sect"] == "CTRL-W": + return "Window" + if e["sect"] == "ex-edit-index": + return "Command line" + if e["sect"] == "insert-index": + return "Insert" + if e["sect"] in ("normal-index", "g", "z", "visual-index", "operator-pending-index", "["): + if keys in OPERATORS or (e["sect"] == "visual-index" and keys in OPERATORS): + return "Operator" + if MARKS.match(keys) and e["sect"] != "visual-index": + return "Mark & jump" + if REGISTERS.match(keys) and helpfile in ("change.txt", "repeat.txt", "undo.txt"): + return "Register & macro" + cat = FILE_CAT.get(helpfile) + if cat == "Motion" and e["note"] != "1": + return "Mark & jump" if "jump" in e["desc"] or "mark" in e["desc"] else "Motion" + return cat or ("Motion" if e["note"] == "1" else "Misc") + + +# --------------------------------------------------------------------------- +# Live maps +# --------------------------------------------------------------------------- + + +def source_of(path, d): + """(source id, short file label) for the file that set a mapping.""" + if not path: + return "unknown", None + cfg, runtime = d["config"].rstrip("/") + "/", d["vimruntime"].rstrip("/") + "/" + packs = d["data"].rstrip("/") + "/site/pack/" + if path.startswith(cfg): + return "config", path[len(cfg):] + if path.startswith(runtime): + rel = path[len(runtime):] + if rel.startswith("ftplugin/"): + return "ftplugin", rel + if "matchit" in rel: + return "matchit", rel + return "neovim", rel + if path.startswith(packs): + m = re.match(r"[^/]+/(?:opt|start)/([^/]+)/(.*)", path[len(packs):]) + if m: + return m.group(1), m.group(2) + if "/vimfiles/" in path or path.startswith("/usr/share/vim"): + return Path(path).stem + " (system)", path + return "other", path + + +def live_entries(d, curated_desc): + out = [] + leader = d.get("leader") or "\\" + lead = canon("") if leader == " " else leader + + def add(m, buffer_ctx=None): + lhs = m["lhs"] + if lhs.startswith("") or lhs.startswith("") or "" in lhs[:1]: + return + src, file = source_of(m.get("file"), d) + key = canon(lhs) + desc = m.get("desc") + rhs = m.get("rhs") + help_tag = desc[6:].strip() if desc and desc.startswith(":help ") else None + # map_desc may carry a `ctx` ("markdown buffers") to target one buffer-local + # map; entries without one apply everywhere. + cd = next((curated_desc[t] for c in dict.fromkeys((buffer_ctx, None)) + for t in ((src, m["mode"], key, c), (src, "*", key, c)) if t in curated_desc), {}) + # The config's own desc wins (which-key shows it too) unless it is + # missing or only a ":help" pointer; category/notes always apply. + if cd.get("d") and (not desc or desc.startswith(":help") or cd.get("force")): + desc = cd["d"] + if not desc: + if rhs and rhs.startswith("("): + desc = rhs[7:-1].replace("-", " ") + elif rhs: + desc = f"runs {rhs}" + else: + desc = "(disabled)" if not m.get("callback") else "(Lua function)" + if desc == "which_key_ignore" or (src == "which-key" and not cd.get("d")): + return # which-key's own trigger maps: they only show the popup + e = { + "k": key, "m": [m["mode"]], "d": desc, "s": src, "f": file, "l": m.get("line"), + "rhs": rhs if rhs and not rhs.startswith("(leap") else None, + "nw": 1 if m.get("nowait") else None, + "cat": cd.get("cat"), "n": cd.get("n"), "e": cd.get("e"), "sl": cd.get("sl"), + } + if key.startswith(lead) and leader == " " and m["mode"] in ("n", "x", "s", "o"): + e["ld"] = 1 + if help_tag: + e["t"] = help_tag + if buffer_ctx: + e["ctx"] = buffer_ctx + out.append(e) + + for m in d["maps"]: + add(m) + for b in d["buffers"]: + for m in b["maps"]: + add(m, f"{b['filetype']} buffers") + return merge_modes(out) + + +def merge_modes(entries): + """Collapse identical maps registered for several modes into one entry.""" + merged, order = {}, [] + for e in entries: + k = (e["k"], e["d"], e["s"], e.get("f"), e.get("l"), e.get("ctx")) + if k in merged: + for mode in e["m"]: + if mode not in merged[k]["m"]: + merged[k]["m"].append(mode) + else: + merged[k] = e + order.append(k) + return [merged[k] for k in order] + + +# --------------------------------------------------------------------------- +# Curated data +# --------------------------------------------------------------------------- + + +def load_curated(): + cur = {"entries": [], "notes": [], "map_desc": [], "builtin": {}, "essentials": [], "commands": {}, "groups": {}} + for f in sorted((HERE / "curated").glob("*.json")): + data = json.loads(f.read_text()) + for k in ("entries", "notes", "map_desc", "essentials"): + cur[k] += data.get(k, []) + for tag, fields in data.get("builtin", {}).items(): + cur["builtin"].setdefault(tag, {}).update(fields) # several files may add to one tag + cur["commands"].update(data.get("commands", {})) + cur["groups"].update(data.get("groups", {})) + return cur + + +# --------------------------------------------------------------------------- +# Conflicts: what the config changes about stock Vim +# --------------------------------------------------------------------------- + + +def annotate(builtins, live): + by_mode_key = {} + for i, b in enumerate(builtins): + if b.get("ex"): + continue + bt = base(canon(b["k"])) + if bt is None: + continue + b["_t"] = bt + for mode in b["m"]: + by_mode_key.setdefault((mode, tuple(bt)), []).append(i) + + live_by_mode = {} + for j, e in enumerate(live): + e["_t"] = tokens(e["k"]) + if e.get("ctx"): + continue + for mode in e["m"]: + live_by_mode.setdefault(mode, {})[tuple(e["_t"])] = j + + for j, e in enumerate(live): + for mode in e["m"]: + for i in by_mode_key.get((mode, tuple(e["_t"])), []): + builtins[i].setdefault("ov", []).append({"m": mode, "by": j}) + e.setdefault("rep", []).append({"m": mode, "b": i}) + + for mode, maps in live_by_mode.items(): + # Longer maps that a shorter `nowait` map makes unreachable. + for lhs, j in maps.items(): + if live[j].get("nw"): + for other, k in maps.items(): + if starts_with(list(other), list(lhs)): + live[k].setdefault("sh", []).append({"m": mode, "by": j}) + # Complete built-in commands that now wait for 'timeoutlen' because a + # mapping extends them (e.g. "ga" once "gai"/"gao" exist). + for (bmode, bkey), idxs in by_mode_key.items(): + if bmode != mode or bkey in maps: + continue + ext = [live[j]["k"] for lhs, j in maps.items() if starts_with(list(lhs), list(bkey))] + if ext: + for i in idxs: + if not is_ph(tokens(builtins[i]["k"])[-1]): + builtins[i].setdefault("wait", []).append({"m": mode, "ext": sorted(ext)[:6], "n": len(ext)}) + for x in builtins + live: + x.pop("_t", None) + + +# --------------------------------------------------------------------------- + + +def main(): + d = load_dump() + runtime = d["vimruntime"] + tags = parse_tags(runtime) + builtins = parse_index(runtime, tags) + cur = load_curated() + + for b in builtins: + extra = cur["builtin"].get(f"{''.join(b['m'])}|{b['k']}") or cur["builtin"].get(b.get("t") or "") + if extra: + b.update({k: v for k, v in extra.items() if v is not None}) + ess = {(e.get("t") or "", e.get("k") or "") for e in cur["essentials"]} + for b in builtins: + if (b.get("t") or "", "") in ess or ("", b["k"]) in ess: + b["e"] = 1 + + curated_desc = {(c["s"], c.get("m", "*"), canon(c["k"].replace("", "")), c.get("ctx")): c + for c in cur["map_desc"]} + live = live_entries(d, curated_desc) + annotate(builtins, live) + # Help links for the template: Neovim's own defaults describe themselves as + # ":help "; curated entries name a tag in "h". "hf" is the runtime help + # file holding that tag (None for plugin docs, which neovim.io doesn't host). + for e in live: + if e.get("t"): + e["hf"] = tags.get(e["t"]) + for c in cur["entries"]: + if c.get("h"): + c["hf"] = tags.get(c["h"]) + for e in live: + if not e.get("cat"): + group = next((g for g in sorted(cur["groups"], key=len, reverse=True) + if canon(g.replace("", "")) == e["k"][: len(canon(g.replace("", "")))] + and cur["groups"][g].get("cat")), None) + if group: + e["cat"] = cur["groups"][group]["cat"] + elif e.get("rep"): + e["cat"] = builtins[e["rep"][0]["b"]]["cat"] + else: + e["cat"] = "Misc" + + commands = [] + for c in d["commands"] + [dict(c, ctx=b["filetype"]) for b in d["buffers"] for c in b["commands"]]: + src, file = source_of(c.get("file"), d) + commands.append({"k": ":" + c["name"], "d": cur["commands"].get(c["name"]) or c.get("desc") or "", + "s": src, "f": file, "na": c.get("nargs"), "r": 1 if c.get("range") else None, + "ctx": c.get("ctx")}) + + plugins = sorted({p["name"] for p in d["plugins"] if p.get("active")}) + data = { + "meta": { + "version": d["version"], "leader": d["leader"], "localleader": d["localleader"], + "date": date.today().isoformat(), "plugins": plugins, "options": d["options"], + "timeoutlen": d["options"]["timeoutlen"], + }, + "builtins": builtins, + "live": live, + "curated": cur["entries"], + "notes": cur["notes"], + "groups": cur["groups"], + "commands": commands, + "picker": d.get("snacks_picker"), + "explorer": d.get("snacks_explorer"), + "blink": d.get("blink"), + "flash": d.get("flash"), + } + payload = json.dumps(data, ensure_ascii=False, separators=(",", ":")).replace(":", function() Snacks.picker.command_history() end, { desc = "Command History" }) -vim.keymap.set("n", "n", function() - Snacks.picker.notifications() -end, { desc = "Notification History" }) vim.keymap.set("n", "e", function() Snacks.explorer() end, { desc = "File Explorer" }) @@ -211,7 +215,14 @@ vim.keymap.set("n", "sM", function() Snacks.picker.man() end, { desc = "Man Pages" }) vim.keymap.set("n", "sp", function() - Snacks.picker.lazy() + -- Snacks.picker.lazy() needs lazy.nvim; list the vim.pack specs instead. + Snacks.picker.grep({ + cwd = vim.fn.stdpath("config"), + glob = "lua/plugins.lua", + search = 'src = "https://', + regex = false, + live = false, + }) end, { desc = "Search for Plugin Spec" }) vim.keymap.set("n", "sq", function() Snacks.picker.qflist() @@ -233,9 +244,10 @@ end, { desc = "Goto Definition" }) vim.keymap.set("n", "gD", function() Snacks.picker.lsp_declarations() end, { desc = "Goto Declaration" }) -vim.keymap.set("n", "gr", function() +-- grr rather than a nowait gr, which would hide Neovim's grn/gra/gri/grt/grx. +vim.keymap.set("n", "grr", function() Snacks.picker.lsp_references() -end, { nowait = true, desc = "References" }) +end, { desc = "References" }) vim.keymap.set("n", "gI", function() Snacks.picker.lsp_implementations() end, { desc = "Goto Implementation" }) @@ -263,7 +275,7 @@ vim.keymap.set("n", "cr", function() end, { expr = true, desc = "Rename Symbol" }) -- Other -vim.keymap.set("n", "sq", "nohlsearch", { desc = "Clear Search Highlights" }) +vim.keymap.set("n", "ur", "nohlsearch", { desc = "Clear Search Highlights" }) vim.keymap.set("n", "z", function() Snacks.zen() end, { desc = "Toggle Zen Mode" }) @@ -285,7 +297,7 @@ end, { desc = "Delete Buffer" }) vim.keymap.set("n", "cR", function() Snacks.rename.rename_file() end, { desc = "Rename File" }) -vim.keymap.set({ "n", "v" }, "gB", function() +vim.keymap.set({ "n", "x" }, "gB", function() Snacks.gitbrowse() end, { desc = "Git Browse" }) vim.keymap.set("n", "gg", function() @@ -300,10 +312,10 @@ end, { desc = "Toggle Terminal" }) vim.keymap.set({ "n", "t" }, "", function() Snacks.terminal() end, { desc = "which_key_ignore" }) -vim.keymap.set({ "n", "t" }, "]]", function() +vim.keymap.set("n", "]]", function() Snacks.words.jump(vim.v.count1) end, { desc = "Next Reference" }) -vim.keymap.set({ "n", "t" }, "[[", function() +vim.keymap.set("n", "[[", function() Snacks.words.jump(-vim.v.count1) end, { desc = "Prev Reference" }) vim.keymap.set("n", "N", function() @@ -324,9 +336,15 @@ end, { desc = "Neovim News" }) ---------------------------------------------------------- -- Sidekick NES: accept next-edit suggestion in normal mode +-- Without a suggestion keeps its built-in meaning (jump forward). +-- is mapped to itself so terminals that can tell it from (kitty, +-- Neovide) keep it as a plain jump forward (see :h CTRL-I). vim.keymap.set("n", "", function() - require("sidekick").nes_jump_or_apply() + if not require("sidekick").nes_jump_or_apply() then + vim.api.nvim_feedkeys(vim.v.count1 .. vim.keycode(""), "n", false) + end end, { desc = "Apply Sidekick NES Suggestion" }) +vim.keymap.set("n", "", "", { desc = "Jump Forward" }) ---------------------------------------------------------- @@ -336,8 +354,13 @@ vim.keymap.set("n", "S", "(leap-from-window)") ---------------------------------------------------------- +-- Gx Keymaps +vim.keymap.set({ "n", "x" }, "gx", "Browse", { desc = "Open with gx.nvim" }) + +---------------------------------------------------------- + -- Conform Keymaps -vim.keymap.set({ "n", "v" }, "cf", function() +vim.keymap.set({ "n", "x" }, "cf", function() require("conform").format({ lsp_format = "fallback" }) end, { desc = "Format Buffer" }) @@ -349,9 +372,9 @@ vim.keymap.set("n", "uu", "UndotreeToggle", { desc = "Toggle Un ---------------------------------------------------------- -- Comment Box Keymaps -vim.keymap.set({ "n", "v" }, "cbb", "CBccbox", { desc = "Centered Box" }) -vim.keymap.set({ "n", "v" }, "cbl", "CBcline", { desc = "Centered Line" }) -vim.keymap.set({ "n", "v" }, "cbd", "CBd", { desc = "Delete Box/Line" }) +vim.keymap.set({ "n", "x" }, "cbb", "CBccbox", { desc = "Centered Box" }) +vim.keymap.set({ "n", "x" }, "cbl", "CBcline", { desc = "Centered Line" }) +vim.keymap.set({ "n", "x" }, "cbd", "CBd", { desc = "Delete Box/Line" }) vim.keymap.set("n", "cbk", "CBcatalog", { desc = "Box Style Catalog" }) ---------------------------------------------------------- diff --git a/lua/options.lua b/lua/options.lua index dc0a632..8ca893c 100644 --- a/lua/options.lua +++ b/lua/options.lua @@ -78,17 +78,6 @@ vim.api.nvim_create_user_command("Format", function(args) require("conform").format({ async = true, lsp_format = "fallback", range = range }) end, { range = true }) --- FormatOnSave --- Formats the buffer before saving. --- This is a common practice to ensure code is consistently formatted. -vim.api.nvim_create_autocmd("BufWritePre", { - pattern = "*", - callback = function() - -- Call Format function - vim.api.nvim_command("Format") - end, -}) - -- Return to last-known cursor position when reopening files vim.api.nvim_create_autocmd("BufReadPost", { desc = "Jump to last known cursor position on open", diff --git a/lua/plugins.lua b/lua/plugins.lua index 31298ea..4f6e512 100644 --- a/lua/plugins.lua +++ b/lua/plugins.lua @@ -181,8 +181,7 @@ lazyload.on_vim_enter(function() -- Flash.nvim vim.pack.add({ { src = "https://github.com/folke/flash.nvim", name = "flash" } }) require("flash").setup({ - modes = { char = { keys = {} } }, - keys = {}, + modes = { char = { multi_line = false } }, }) -- Leap.nvim @@ -318,6 +317,17 @@ lazyload.on_vim_enter(function() cmp.setup({ keymap = { preset = "default", + -- The preset's signature toggle would shadow digraphs (i_CTRL-K); + -- signature help stays on (Neovim default) and auto-trigger. + [""] = false, + -- With the menu closed, open it instead of doing nothing. + [""] = { "select_next", "show_and_insert" }, + [""] = { + "select_prev", + function(cmp) + return cmp.show_and_insert({ initial_selected_item_idx = -1 }) + end, + }, [""] = { "snippet_forward", function() @@ -372,8 +382,16 @@ lazyload.on_vim_enter(function() end, }) - local lspconfig = require("lspconfig") - local capabilities = (has_blink_bin or has_cargo) and require("blink.cmp").get_lsp_capabilities() or nil + -- Server settings use vim.lsp.config(); mason-lspconfig v2 enables every + -- installed server (automatic_enable) and no longer reads `handlers`. + -- blink.cmp adds its completion capabilities to vim.lsp.config("*") itself. + vim.lsp.config("lua_ls", { + settings = { + Lua = { + diagnostics = { globals = { "Config" } }, + }, + }, + }) vim.lsp.config("rust_analyzer", { settings = { @@ -395,35 +413,6 @@ lazyload.on_vim_enter(function() "bashls", "copilot", }, - handlers = { - -- Default handler - function(server_name) - lspconfig[server_name].setup({ capabilities = capabilities }) - end, - -- Specific overrides - ["lua_ls"] = function() - lspconfig.lua_ls.setup({ - capabilities = capabilities, - settings = { - Lua = { - diagnostics = { globals = { "Config" } }, - }, - }, - }) - end, - ["rust_analyzer"] = function() - lspconfig.rust_analyzer.setup({ - capabilities = capabilities, - settings = { - ["rust-analyzer"] = { - check = { - command = "clippy", - }, - }, - }, - }) - end, - }, }) -- Enable native inline completion (ghost text in insert mode) @@ -433,6 +422,11 @@ lazyload.on_vim_enter(function() vim.pack.add({ { src = "https://github.com/echasnovski/mini.ai", name = "mini.ai" } }) Config.plugins.mini_ai = { + -- an/in stay Neovim's LSP selection-range maps (|v_an|, |v_in|). + mappings = { + around_next = "aN", + inside_next = "iN", + }, custom_textobjects = { g = function() local n_lines = vim.api.nvim_buf_line_count(0)