;;; init-cheatsheet.el --- In-Emacs keybinding reference -*- lexical-binding: t; -*- ;;; Commentary: ;; A static reference buffer listing this config's custom keybindings, ;; opened as a non-blocking side window — same window-management model as ;; the build panel in `init-build.el' — so it can be toggled in and out of ;; view without interrupting whatever else is on screen. ;; ;; Content is real Markdown, viewed read-only via `markdown-view-mode' ;; (from the `markdown-mode' package already pulled in by `init-dev.el') ;; so headers/tables/code spans get proper fontification for free. ;;; Code: (require 'markdown-mode) ; markdown-view-mode; make the dep explicit (add-to-list 'display-buffer-alist '("\\*Cheatsheet\\*" (display-buffer-reuse-window display-buffer-in-side-window) (side . left) (window-width . 0.4))) (defconst my/cheatsheet-text "# SimplyEmacs Cheat Sheet Custom keybindings layered on top of vanilla Emacs. Toggle this buffer any time with `C-c /`. Many keys use a project/fallback pattern (`my/project-or`): one action inside a known project, a `consult`-based fallback everywhere else. ## Navigation & Search *(Evil normal/motion state)* | Key | In a project | Fallback | |------------|----------------------------|-------------------| | `C-e` | `project-dired` | `dired-jump` | | `C-m` | `project-switch-to-buffer` | `consult-buffer` | | `C-f` | `consult-ripgrep` | `consult-ripgrep` | | `C-p` | `project-find-file` | `consult-fd` | | `C-s` | `consult-line` | | | `` | `evil-ret` | | `C-f` runs `consult-ripgrep` everywhere — it already scopes to the project root when there is one, so it needs no project/fallback split. ## Projects | Key | Action | |-----------|-------------------------------------------------------------------------------| | `C-c C-p` | Switch project — saves & closes current project's buffers first, then prompts | Projects are rescanned from `~/Projects` on every startup (direct children only, no symlinks, most-recently-modified first). Jumping into a dependency's source keeps the *current project* pinned to your last real project instead of hijacking to it. ## Build | Key | Action | |-----------|-----------------------------------------------------------| | `C-c C-c` | Run `./build.sh` from project root (saves buffers first) | | `C-c C-o` | Same, but prompts for extra args to append | | `C-j` | Toggle the `*compilation*` output panel (right side, 35%) | Requires `build.sh` at the project root; output is a real `compile` buffer, so `M-g n` / `M-g p` jump between errors. ## Dev / Eglot (LSP) Wired up for Odin (`.odin`, tree-sitter + `odin-ts-mode` + OLS). | Key | Action | |------------|----------------------------------| | `C-c r` | `eglot-rename` | | `C-c a` | `eglot-code-actions` | | `K` norm | Full hover doc in a focused pane | | `q` or `K` | Close the hover pane | Add a new language: new `;;;; ` section in `init-dev.el`. ## Git (Magit) `C-c C-g` opens `magit-status` (replaces the lazygit TUI). Magit keeps its own native keys — nothing reimplemented — evil's normal state just shadows most of them by default, so they're restored below plus a couple of hjkl navigation aliases. ### Workflow - Expand only the file you're working on with `TAB`. - Move between hunks with `j` / `k`. - Press `s` on a hunk to stage only that hunk. - Continue staging related hunks until they form a single logical change. - Press `c c` to create a commit from the staged changes. - Repeat for the remaining hunks, creating small, focused commits instead of one large commit. > **Tip:** If a single hunk contains unrelated changes, press `e` to edit the patch before staging, allowing you to split the changes into separate commits. | Key | Action | |-----------|----------------------| | `j` / `n` | next section | | `s` | stage | | `k` / `p` | previous section | | `u` | unstage | | `h` | up to parent section | | `U` | unstage all | | `RET` | visit thing at point | | `x` | discard | | `g r` | refresh | | `q` | quit / bury buffer | **Popups** *(transient menus)*: | Key | Action | |-----|----------------| | `c` | commit | | `m` | merge | | `d` | diff | | `r` | rebase | | `b` | branch | | `t` | tag | | `l` | log | | `z` | stash | | `f` | fetch | | `e` | ediff | | `F` | pull | | `$` | process buffer | | `P` | push | Covers the daily-driver surface, not magit's entire command set — anything not listed falls through to evil's default on that key (harmless, just ``). ## Dired Evil `hjkl` grafted onto stock dired commands, remapped only so evil doesn't swallow dired's own letters. | Key | Action | |-------------|---------------------------| | `j` / `k` | next / previous line | | `d` | flag for deletion | | `h` | up a directory | | `x` | execute flagged deletions | | `l` / `RET` | open file or directory | | `D` | delete now, no flag step | | `q` | quit window | | `R` | rename / move | | `SPC` / `m` | mark | | `C` | copy | | `u` | unmark | | `y` | copy filename as kill | | `U` | unmark all | | `!` | shell command on file(s) | | `t` | toggle marks | | `+` / `f` | create directory | | `g r` | refresh listing | | `a` | create empty file | | `(` | toggle file details | ## Evil *(buffer editing only — dired/magit/etc. keep native binds)* **Ex commands:** | Command | Action | |---------|---------------------| | `:w` | save buffer | | `:q` | kill current buffer | | `:wq` | save and kill Emacs | **Insert state:** | Key | Action | |-------|----------------------------------------------------| | `TAB` | insert a literal tab (overrides major-mode indent) | ## In-buffer Completion (Corfu) Auto-popup after 2 characters, 0.15s delay. | Key | Action | |-----------------|--------------------| | `C-n` / `TAB` | next candidate | | `C-p` / `S-TAB` | previous candidate | | `RET` | insert selection | | `` | quit completion | ## Minibuffer Completion Vertico *(centered posframe)* + orderless matching + marginalia + consult. Type fragments in any order to filter. ## Misc / Global | Key | Action | |----------------|-----------------------------------| | `C-S-p` | `execute-extended-command` (M-x) | | `` | acts like `C-g` everywhere | | *(any prefix)* | which-key shows available options | ## Look & Feel Theme: **gruvbox-dark-hard**, background/fringe/line-numbers darkened to `#111111`. Font: JetBrainsMono Nerd Font @ 12pt. Line numbers and current-line highlight on globally; scroll margin 15 lines. " "Reference text shown by `my/toggle-cheatsheet'.") (define-derived-mode my/cheatsheet-mode markdown-view-mode "Cheatsheet" "Read-only `markdown-view-mode' variant for the keybinding cheat sheet.") (with-eval-after-load 'evil (evil-define-key '(normal motion) my/cheatsheet-mode-map (kbd "q") #'quit-window)) (defun my/cheatsheet-buffer () "Return the cheatsheet buffer, creating and populating it on first use." (let ((buf (get-buffer-create "*Cheatsheet*"))) (with-current-buffer buf (unless (derived-mode-p 'my/cheatsheet-mode) (let ((inhibit-read-only t)) (erase-buffer) (insert my/cheatsheet-text) (my/cheatsheet-mode) (goto-char (point-min))))) buf)) (defun my/toggle-cheatsheet () "Toggle a non-blocking side window showing this config's cheat sheet. Same show/hide-only-the-window behaviour as `my/toggle-build-panel' — doesn't rebuild the buffer each time, just shows or hides it." (interactive) (let* ((buf (my/cheatsheet-buffer)) (win (get-buffer-window buf))) (if win (delete-window win) (display-buffer buf)))) ;; Bound globally (not evil-scoped), matching the build panel keys, so it ;; works the same from normal state, insert state, or anywhere else. (global-set-key (kbd "C-c /") #'my/toggle-cheatsheet) (with-eval-after-load 'which-key (which-key-add-key-based-replacements "C-c /" "cheatsheet")) (provide 'init-cheatsheet) ;;; init-cheatsheet.el ends here