do not edit — generated by btf.
git.druid.rocksindexdruid520radiumUSAGE

USAGE


radium - my emacs, tty only, no lsp, gtags does the work
 
this is my whole emacs setup: the config in this repo, the project
dsl (cfg.rp), and the keys i actually press. it runs on netbsd 11 as
a daemon + emacsclient, on the console (TERM=wsvt25), in 256 colors.
there is no lsp and there never will be - everything lsp claims to
do, gnu global (gtags) does, and everything else is small suckless-ish
packages plus my own elisp.
 
## install
 
  git clone git://git.druid.rocks/druid520/radium.git
  cp -r emacs ~/.emacs.d
  emacs --daemon
  emacsclient -nw
 
first boot installs every package off elpa/nongnu/melpa, so it wants
network + tls once. pkgsrc's emacs without builtin gnutls still works
(it falls back to gnutls-cli), and netbsd's cert paths are wired in,
so install mozilla-rootcerts and forget about it. TERM=wsvt25 gets
treated as xterm-256color automatically so the console isnt stuck on
16 colors.
 
in emacsclient: C-x C-c closes the client (the daemon stays up),
C-x # tells the daemon you're done with a server buffer.
 
radium-server.el is what makes any of this work without the explicit
`emacs --daemon` above: it starts the server itself (server-start,
guarded by server-running-p so a second `emacs` doesn't error trying
to start a second one) the moment any radium config loads, daemon or
not - a plain `emacs` (no --daemon) already leaves a socket for
emacsclient to attach to. server-client-instructions is off: no
"You can now switch to buffer..." banner every time a client attaches.
 
everything that's runtime state (elpa/, backups/, autosaves/, recentf,
history, places, desktop) is gitignored - the repo root *is*
~/.emacs.d once copied. none of my elisp gets byte-compiled, on
purpose.
 
## the config, how its built
 
init.el just puts itself on load-path and require's every radium-*
module, each wrapped so one module failing (no network, missing
binary, w/e) prints an "err:" line in *messages* and the rest still
load. modules are self-contained, most provide their own keys.
radium-bootstrap does package.el + use-package; radium-defaults does
all the stock emacs tuning (fonts are on, backups/savehist/recentf/
saveplace/desktop persist across daemon restarts, repeat-mode,
delete-selection, smooth tty scrolling, tabs + 78 cols). indentation is
tabs everywhere, 8 wide, no matter what - enforced in defaults, TAB
only ever indents (no snippet expansion, no completion cycling).
 
~/.radium.el, if it exists, loads dead last - after every radium-*
module above and its own hooks are already in place, so it can freely
override anything (a keybinding, a face, a hook), and a mistake in it
reports itself instead of crashing the daemon. this is where a real
machine's own settings that don't belong in a public git repo at all
go - an api key path, a font size, one extra package only this box
wants - not a fork of this repo, just one untracked file next to it.
 
## cfg.rp, the project dsl
 
per-project config lives in cfg.rp at the project root. suckless
`key = value` lines under [sections], toml without the ceremony.
values are raw strings to end of line - no quotes, no types. comments
(; or #) only on their own lines, because shell commands are legal
values and inline comments would fight them. cfg.rp itself is the
strongest project-root marker, so it wins over mk.conf detection.
a line that's none of blank/comment/[section]/key=value (an inline
comment tacked onto a [section] line, say) is a real, loud *Messages*
warning naming the file and line number, not a silent no-op - a line
like that used to just vanish, leaving every key after it silently
misattributed to whichever section came before, no error at all.
 
the sections:
 
  [build]
  tool     = mk | make | gmake | samu | muon | gnat    ; override autodetect
  build    = b        ; C-c b b goal, beats mk.conf target_build
  clean    = c
  run      = r
  install  = i
  test     = t
  run_cmd  = ./bin/serve -v   ; full command, beats the run goal for C-c b r/R
 
  [tags]
  gtags    = yes      ; auto-build the gtags db in the background when missing
 
  [push]
  remote   = origin
  branch   = trunk
  deploy   = git push {remote} {branch} && ssh box 'do-deploy'
  ; any other key here is a named push template, {remote}/{branch}
  ; get substituted. C-c v p completes over the names; typing a bare
  ; branch name pushes that branch, empty RET pushes the default.
 
unknown sections n keys are ignored, so it grows freely. without the
file nothing changes - every lookup falls back to the old behavior
(mk.conf letters, marker detection, plain git push).
 
## project.el, not projectile
 
radium-project.el is emacs's own built-in project.el, not projectile -
C-x p f/g/p/b (find file/grep/switch project/list buffers) and the
rest of its own standard keymap, for free, once a root is found.
project-vc-extra-root-markers is set to the exact same marker list
radium-build-root already uses (radium-build.el's own
radium-build-markers - one shared list, not two separately
maintained ones that can drift out of sync with each other, which
they genuinely had: a project keyed only by cfg.rp or a bare mk/
directory used to build fine via C-c b b while C-x p f/project-
switch-project didn't recognize it as a project at all).
 
## keys, the radium ones
 
all mine live under C-c plus a few:
 
build (radium-build) - picks mk/make/gmake/samu/muon/gnat by project,
errors jumpable in the compile buffer:
  C-c b b   build
  C-c b c   clean
  C-c b r   run (plain output)
  C-c b R   run in a real terminal (curses, repls, interactive stuff)
  C-c b i   install
  C-c b t   test
  C-c b T   prompt for a goal, remembered per project, completion from
            every target_* in mk.conf
  C-c b o   one-off goal, not remembered
  C-c b g   recompile last command
  C-c b k   kill the running build/check
 
tags (radium-tags) - gnu global:
  C-c t g   (re)build the gtags db at the project root
  C-c t r   rename a symbol project-wide (global -r + query-replace
            over every file that references it)
  M-.       ggtags' dwim finder (definition/reference), results pop
            into a clickable buffer at the bottom. falls back to
            dumb-jump (grep-based, pinned to plain grep rather than
            silently preferring rg/ag if either's on $PATH) when
            ggtags has no answer
 
git (radium-git) - vc, not magit:
  C-c v s   vc-dir status (stage, commit, everything there)
  C-c v d   diff
  C-c v l   log
  C-c v a   annotate
  C-c v p   push (cfg.rp [push] templates, see above)
  C-c v c   next action (commit etc.)
  plus diff-hl shows every modified line in the left margin and
  changed files get marked in dired
 
sign/verify (radium-sign) - ssh-keygen -Y or signify, whichever
radium-sign-method names if it's actually installed, else whichever
of the two is (a real error only if neither is):
  C-c v S   sign this file - writes file.sig next to it
  C-c v V   verify file.sig against this file
  both ask which key every time (completing-read over ~/.ssh/*.pub
  for ssh, or radium-sign-signify-key-dir's *.sec/*.pub for signify) -
  which key is exactly the kind of per-use decision worth asking, not
  defaulting. ssh signing uses ssh-agent when the matching private key
  is loaded there (falls back to the adjacent private key file
  otherwise - ssh-keygen's own behavior, not something wired up here),
  under the "file" namespace (radium-sign-ssh-namespace) - openssh's
  own manpage example for signing a plain file, not a git object.
 
editing:
  C-;       iedit (edit every occurrence of the symbol, live)
  C-c m c   multiple cursors: one per line
  C-c m a   multiple cursors: every match of the word at point
  C-> C-<   multiple cursors: next/prev match
  C-=       expand-region
  C-x u     vundo, the visual undo tree (plain undo is gone, and
            good riddance)
  M-n M-p   symbol-overlay: next/prev occurrence of the symbol under
            point (all of them stay highlighted)
  C-c o r   symbol-overlay: rename the symbol in this buffer
  C-c o t   symbol-overlay: toggle in-scope vs whole-buffer
  C-c o k   symbol-overlay: clear
  C-c C-e   in grep/occur buffers: wgrep, edit results in place and
            C-x C-s writes back to every real file. in dired it's
            wdired, the same trick for file names
 
completion (radium-completion) - vertico + orderless + marginalia +
corfu + cape + embark:
  C-c f   consult-find
  C-c g   consult-grep
  C-c r   consult-recent-file
  C-c i   consult-imenu
  C-x b   consult-buffer
  M-y     consult-yank-pop
  M-/     hippie-expand (dabbrev, files, kill ring, lisp symbols)
  C-.     embark: a menu of actions for whatever is at point or
          selected in the minibuffer
  TAB     completes; typing completes in orderless style (any order,
          space separated). corfu pops up in buffers on its own after
          2 chars.
 
prototypes (radium-proto + radium-stub):
  C-c c c   insert a call with real param names, pulled from the
            gtags db, one tabstop per param. works in c (foo(a, b)),
            ada/spark (same positional form), scheme ((foo a b)),
            perl (foo()).
  C-c c s   the function filler: turn every c/ada/perl prototype line
            in the region (or whole buffer) into a stub block in my
            style, point landing in the first body
  C-c c S   same, for just the prototype line under point
 
qemu + gdb (radium-qemu) - cfg.rp's own [qemu] section:
  [qemu]
  kernel = out/kaboom.elf
  disk   = out/disk.img       ; optional
  memory = 128                ; optional, mb, default 128
  args   = -smp 2             ; optional, raw extra flags
  C-c q q   boot -nographic in a real comint buffer (same "run it
            through a real process, not a black box" idea as C-c b R)
  C-c q g   the same boot, plus -s -S: qemu halts before the first
            real instruction, gdbserver stub on :1234
  C-c q d   `gdb -i=mi' on the [qemu] kernel, auto-attaching to a
            -s -S qemu already running under this same project root
            via `target remote :1234' the instant gdb's first real
            prompt appears - a plain M-x gdb otherwise
 
  electric fence / lmdbg (item 18's other half, in radium-build.el
  instead - a run-command concern, not a qemu one): cfg.rp's [run]
  section, efence = yes and/or lmdbg = yes, wraps C-c b r/R's own run
  command (env LD_PRELOAD=libefence.so for efence, `lmdbg -o
  ROOT/lmdbg.out' wrapping the whole thing for lmdbg - netbsd base's
  own malloc/leak tracer). disclosed plainly: neither tool exists in
  the sandbox this was built in, so this half is written against each
  tool's own documented usage, not independently verified running.
 
binary tools (radium-binary) - hex editing, radare2, objdump:
  C-c x h   toggle hexl-mode on this buffer (emacs's own built-in hex
            editor - .bin/.o already auto-open in it)
  C-c x r   radare2 on this buffer's file (or prompts), a real comint
            repl - r2's own command set (aa, pdf, V, ...) already
            does the real work, nothing here wraps it further
  C-c x d   objdump -d -M att on this buffer's file (or prompts), AT&T
            syntax matching this config's own asm convention, shown
            in a real asm-mode buffer (q to close)
  item 22 (capstone/keystone/unicorn) has no further integration
  beyond python.el itself, above - none of the three ship a
  standalone cli tool, only a library to import from real python.
 
macros (radium-macros) - text macros that fire as i type:
  ![date]       2026-09-01   (or any strftime: ![date:%Y])
  ![time] ![datetime] ![uuid] ![wid] (host:pid) ![host] ![user]
  ![env:VAR]  ![file] ![dir] ![basename] ![path] ![line] ![lang]
  ![guard]     header guard for this file
  ![spaces:12] that many spaces
  ![epoch]     the current unix timestamp
  ![rand:N]    a random integer in [0,N), 1000000 if N is missing
  ![branch]    the current git branch, empty outside a repo
  ![proto:name] auto-fills a call like C-c c c
  ![help]      lists every real macro with a one-line description
  C-c e   expand the macro before point by hand (also reaches the
          confirm-gated ones: ![shell:cmd], ![gitlog])
  C-c h   same listing as ![help], straight to the keybinding
  the instant i type the closing ], it expands - except in kona, where
  k's own ![] would collide. paredit is told to leave [ and ] alone
  for the same reason.
  C-c k n   name the last keyboard macro
  C-c k s   name + save it to macros.el (loaded on startup, macros
            survive the daemon)
 
  C-c p   process the whole buffer through a real script - "like
          babel" but for any file, not just an org #+begin_src block.
          prompts for a language (perl/elisp/scheme/commonlisp) and
          opens a real edit buffer for the script in that language's
          own major mode; C-c C-c there runs it against the buffer
          C-c p was called from and replaces that buffer's whole
          content with the result, C-c C-k cancels. perl gets the
          whole buffer as $_ (real perl one-liner style, -0777 -pe -
          a plain s/.../.../ or $_ = ... works); elisp needs no
          subprocess at all, just write real elisp whose last form
          evaluates to the new text, with `radium-proc-text' bound to
          the old text; scheme/commonlisp are real external processes
          (chez/sbcl), so they get the old text as a bound variable
          named `text' too, same shape as elisp, and are expected to
          leave their own last form's value to become the new text.
  C-c k e   show the last macro as elisp (elmacro)
 
checks (radium-checkonsave):
  every save silently syntax-checks the file for its language (gcc,
  nscc, gnatmake, as, perl -c, sh -n, python3's ast.parse, byte-compile
  for elisp) in the background; failures pop a jumpable buffer, M-g n
  / M-g p walk the errors. per-file buffers, a re-save kills the stale
  run.
 
shells n docs:
  C-c s e   eshell
  C-c s s   shell
  C-c s t   ansi-term (/bin/sh)
  C-c d     man (the real netbsd man, mandoc output)
  C-c j j   avy jump to any char on screen
  C-c j l   avy jump to line
  C-c j w   avy jump to word
  M-o       other window (windmove meta-arrows too)
 
languages:
  C-c C-c   kona: send region to the repl (or the current line when
            nothing is selected) - global, so a kona one-liner works
            from inside any file
  C-c C-l   kona: send line - global, and taken back from the cc/sh/
            ada/forth maps so every code buffer gets it (scheme keeps
            geiser's own eval/repl keys, M-` keeps module completion)
            (its electric-toggle) so c buffers get it too
  C-c C-z   kona: open the repl - global
  C-c C-r   crontab-mode: preview the schedule's next 5 runs
  C-c C-p   ada-mode: run gnatprove (spark proof) on a .gpr here
  C-c a w   one-time: build ada-mode's bundled wisi parser
 
gptel - any openai-compatible api in a buffer, straight to the api,
no separate daemon:
  C-c ? c   open/switch to the *gptel* chat buffer
  C-c ? s   send (region if active, else the whole buffer up to point)
  C-c ? m   the model/backend/params menu
  C-c ? r   rewrite the region via a prompt, in place
 
  which provider: radium-gptel-provider is nil (unconfigured) in this
  repo on purpose - this config makes no assumption about which api
  im on today, deepseek or otherwise (i move between them). set it,
  and call (radium-gptel-apply-provider), from ~/.radium.el - the
  same "real per-machine setting, not tracked here" spot item 13's
  own api-key-path precedent already is:
 
    (setq radium-gptel-provider
          (list :name "OpenRouter" :host "openrouter.ai"
                :endpoint "/api/v1/chat/completions"
                :key-file "~/.openrouter"
                :models '(anthropic/claude-opus-5.5 openai/gpt-5)
                :model 'anthropic/claude-opus-5.5))
    (radium-gptel-apply-provider)
 
  left unset, gptel-backend just stays nil - gptel's own real
  default too (checked directly against its source), not a gap this
  config leaves open.
 
  auth is always a plain api key, read fresh from the provider's own
  :key-file on every request (that file holds nothing but the raw
  key) - never in this repo, never in an env var. missing file -> a
  normal 401, not an elisp error mid-request.
 
  tools are on: the model can read_file/edit_region/write_file/
  list_directory/run_shell_command mid-conversation, not just talk.
  read/list never ask; write_file/edit_region/run_shell_command always
  pop the proposed change for me to approve first, and edited files
  pop into a real window so the change is visible in the actual
  editor, not just written to disk behind my back.
 
  mcp: set mcp-hub-servers (see mcp.el's own docs for the
  (NAME . (:command ... :args ...)) / (NAME . (:url ...)) shape) to
  whatever mcp servers i want - their tools merge into gptel-tools
  automatically once they connect. unset by default: nothing starts,
  nothing to configure if i dont want it.
 
org (radium-org) - notes live under ~/org, created if it isnt there:
  todo.org (tasks), notes.org (everything else), journal.org (dated
  entries via a datetree). C-c a/C-c c are already radium prefixes (ada
  wisi-build, proto/stub), so org gets its own C-c n prefix instead of
  the textbook global bindings from its own manual:
  C-c n a   org-agenda
  C-c n c   org-capture - "t" files a TODO under todo.org's inbox,
            "n" a plain note under notes.org's inbox, "j" a dated entry
            in journal.org
  C-c n l   org-store-link
  C-c n r   org-refile
 
  deliberately no org-modern/org-superstar/org-bullets - they replace
  headline stars and tags with unicode glyphs by default, and this
  whole config stays plain-ascii on a tty. stars stay stars.
 
  babel (C-c C-c on a #+begin_src block) is wired for shell, emacs-lisp,
  scheme, perl, and C - the languages i actually use that org has a
  working backend for (nothing for asm/kona/forth/ada, no ob- backend
  exists for those). still asks before running a block every time
  (org-confirm-babel-evaluate's own default) - same "ask before running
  embedded code" shape as ![shell:...] and gptel's write/edit/shell
  tools. perl blocks show what they actually print by default (ob-perl's
  own default shows the last expression's return value instead, which
  isnt what i want).
 
  org-roam (item 2) lives under ~/org/roam, not a second notes root -
  a real zettelkasten layer on the same tree todo/notes/journal.org
  already use:
  C-c n f   find (or create) a node by title
  C-c n i   insert a link to a node at point
  C-c n b   toggle the backlinks buffer for the node at point
  verified for real, not just loaded: created a real node, synced
  org-roam's own real sqlite db, and inserted a real [[id:...]] link
  from a second note back to it, the actual point of the feature.
 
## latex (radium-lang-latex)
 
  .tex - auctex, the real, established latex package (not org's own
  narrower latex-fragment preview). tabs, this config's own policy
  applied consistently. no checkonsave: a real one-shot check would
  mean chktex (bundled inside the full texlive distribution, no
  standalone package) or a genuine multi-pass pdflatex compile,
  neither of which is a quick syntax check the way perl -c/sh -n
  are - same "no one-shot checker" category scheme/forth/kona/cron
  already document.
 
## rd, and ~/.radium.ipc (radium-ipc)
 
  every one of rd's own subcommands is spelled with a leading '-' -
  on purpose, so a bare `rd' (or `rd FILE...') can never collide with
  one, however many files i throw at it - except a filename that
  itself starts with `-' (rd -- forces wrapper mode past that, same
  end-of-options convention getopt/many unix tools already use):
 
  rd [FILE...]  the emacs wrapper: daemon already up, emacsclient
                $RADIUM_CLIENT_ARGS FILE... ($RADIUM_CLIENT_ARGS is
                -nw unless overridden, see below). not up: errors,
                doesnt silently start one for you
  rd -- [FILE...]  same, forced - for a FILE that itself starts with `-'
  rd -start     emacs --daemon
  rd -stop      kill-emacs on the running daemon
  rd -status    up/down, exit code matches (0 up, 1 down)
  rd -eval EXPR emacsclient --eval EXPR - synchronous, prints the
                real return value
  rd -ipc FORM  fire-and-forget: appends FORM (one line) to
                ~/.radium.ipc, radium-ipc.el reads and evals it in the
                daemon, the result goes to *Messages* - nothing comes
                back down this pipe, by design (rd -eval already
                covers the synchronous/has-a-reply case). a keybind
                trigger is just another elisp form, no separate
                mini-language: `rd -ipc "(call-interactively 'save-buffer)"'.
  rd -          (a lone dash, no subcommand name after it) errors -
                same as any other unrecognized subcommand
 
  $RADIUM_CLIENT_ARGS controls what the plain wrapper hands
  emacsclient - -nw (a terminal frame) unless overridden, e.g.
  `export RADIUM_CLIENT_ARGS=-c' for a real graphical frame instead.
  necessarily shell-side config, not ~/.radium.el: rd is its own,
  separate process, already running (and already needing these flags)
  before it ever talks to the daemon that loads ~/.radium.el at all.
 
  install: rd isnt copied into ~/.emacs.d by the normal install step
  (that directory usually isnt on $PATH) - symlink it separately,
  `ln -s ~/.emacs.d/bin/rd.sh ~/bin/rd' or wherever else is on $PATH.
 
  the fifo path is configurable, one setting shared by both halves:
  $RADIUM_IPC (falls back to ~/.radium.ipc if unset) - rd itself reads
  it directly, and radium-ipc.el reads the same env var when it first
  loads. to change it after the daemon is already up instead (from
  ~/.radium.el, say), `(setq radium-ipc-path "...")' then
  `(radium-ipc-start)' to make the running daemon pick it up - safe to
  call again anytime, it always moves the live reader to wherever
  radium-ipc-path now points rather than leaving it on the old one.
 
  ~/.radium.ipc is a real fifo, mode 600 (owner-only - the same trust
  boundary ~/.emacs.d itself already has, not a new one), created by
  radium-ipc.el the moment the daemon starts. a plain shell redirect
  into a fifo closes its write end the instant the write finishes,
  which means the reader sees eof and exits after every single
  command, not just at shutdown - radium-ipc respawns its reader
  immediately every time so the channel stays open for the next one.
 
  lib/radium.lisp and lib/radium.scm are that same channel's far end
  for a real slime or geiser repl: `(load "~/.emacs.d/lib/radium.lisp")'
  (sbcl) or the .scm one (chez) defines one function, `(radium-ipc
  "FORM")', appending FORM (one real elisp form, as a string) to
  ~/.radium.ipc - genuinely extending the running daemon from cl or
  scheme code, not just printing to their own repl buffer, since
  there's no other bridge into emacs's own display/keymap/buffer
  internals from outside elisp at all. fire-and-forget, same as
  rd -ipc above and for the same reason - a synchronous reply would
  need a second channel this doesn't have, and the daemon's own
  *Messages* already shows the result if that's ever worth checking.
 
## sc, and dtc (radium-sc)
 
  sc (druid520/sc.git) is a suckless ./configure generator: a
  hand-written sc.conf (bin=/libs=/oss=/hard_cflags=/... plus any
  custom target functions) becomes mk.conf + config.h + makefile +
  mk/*.sh (b/c/dm/dh/d/dc) - the exact same convention every real
  sc-based project this whole config already builds shares (kaboom,
  nscc, jury, mp, forthc, btf itself). dtc ("devel toolchain") scaffolds
  and grows an sc(+btf)-based project on top of it.
 
  C-c b s        run `sc' at this project's root in a real compile
                 buffer - one line per cflag/lib/header/ldflag/func
                 check as it runs
 
  M-x radium-dtc-new     scaffold a brand-new sc(+btf) project. dtc's
                         own remaining prompts (binary name(s), oses,
                         docs/ folder) are genuinely interactive (a
                         plain read -r in dtc.sh), so this hands off
                         to a real ansi-term rather than a compile
                         buffer, which cant answer them
  M-x radium-dtc-doctor  checks sc/btf2html/btf2man/mk/cc/perl/
                         pkg-config are all really on $PATH
  M-x radium-dtc-addbin  add a binary (c/s/S/pl/sh/bin) to this
                         project's own sc.conf bin=, with a real
                         starter source file
  M-x radium-dtc-newdoc  add NAME.btft to this project's own docs/
  M-x radium-dtc-port    scaffold an mp package definition
                         (category/name) from this project's own
                         sc.conf
 
  a freshly-scaffolded, not-yet-configured project only has sc.conf on
  disk (mk.conf/mk/ dont exist until sc itself runs) - "sc.conf" joins
  radium-build-markers (and project-vc-extra-root-markers, updated
  separately - add-to-list doesnt mutate a list a second var already
  points at) so C-x p f/project-switch-project/radium-build-root all
  agree on that project root too, before sc has ever run there.
 
  verified for real, not just loaded: a real sc.conf + src/hello.c ->
  sc generated config.h/makefile/mk.conf/mk/*.sh, and the result
  actually built and ran (`hello`). dtc new/doctor/addbin/newdoc/port
  all run for real too - addbin appends to a live sc.conf and drops a
  real src/foo.c, newdoc drops a real docs/howto.btft, port scaffolds
  a real port/hello/pkg.conf and honestly says so when no git remote
  exists to fill pkg_fetch from.
 
## keys, plain emacs ones i use
 
  C-x C-f   find file          C-x C-s   save
  C-x C-w   write as           C-x k     kill buffer
  C-x 2/3   split window       C-x 0/1   unsplit
  C-x o     other window       C-x b     buffer (consult)
  C-s C-r   search fwd/back    M-%       query replace
  C-g       abort              C-u       numeric prefix
  C-k       kill line          C-w       kill region
  M-w       copy region        C-y       yank
  C-a C-e   line start/end     C-n C-p   next/prev line
  M-f M-b   word fwd/back      M-a M-e   sentence
  M-< M->   buffer start/end   C-l       recenter
  C-M-a/e   function start/end (defun-ish nav)
  M-!       run shell command  M-|       pipe region through command
  M-x       any command        C-h k/f/v help (key, function, var)
  M-g n/p   next/prev compile error   C-x `  next error
  TAB       complete/indent    M-p M-n   minibuffer history
  q         quit most modes (dired, compile, ggtags popups, vundo)
  RET/click jump to source in compile/ggtags/grep buffers
  C-x C-e   eval last sexp (elisp)   C-c C-c  eval defun
 
repeat-mode is on, so after any repeatable key (C-n, C-x o, M-n, ...)
the follow-up keys keep working without the prefix.
 
which-key pops up the available continuations 0.4s after any prefix
key (C-x, C-c, ...) - a cheatsheet, not a behavior change.
 
## what file becomes what
 
  .c .h        c89 style, tabs. gtags + checkonsave + proto + stub +
               autoinsert (main/header guard) + snippets. typing the
               closing `;' on a bare declaration (`int i;') rewrites
               it in place to a zero-initialized one (`int i = 0;',
               NULL for a pointer, {0} for an array/struct/union,
               '\0' for char, 0.0 for float/double) - i almost never
               want an uninitialized local
  .adb .ads    ada/spark via gnu elpa ada-mode. same
               feature set, ada stub style, autoinsert body/spec
  .pl .pm      cperl, tab indented. checkonsave + gtags + proto +
               stub (sub foo($$); keeps its prototype) + autoinsert
  .scm .ss     chez scheme via geiser (its own repl, eval keys, M-. and
               completion - gtags stays out of scheme entirely).
               gtags + proto with lisp call form + paredit + autoinsert.
               completion is connection-aware, same real bug/fix as
               common lisp above: geiser's own completion only runs
               once a real repl is actually connected, falling back
               to cape/dabbrev before that so corfu never errors
               meanwhile (see radium-lang-scheme.el's own header)
  .el          emacs-lisp. checkonsave byte-compiles it,
               gtags + paredit + autoinsert
  .s           at&t asm, tabs. gtags + checkonsave (as) + autoinsert
  .sh          posix sh (scripts all say /usr/bin/env sh).
               checkonsave (sh -n) + gtags + autoinsert. TAB is a
               literal self-insert here, not indent-for-tab-command
               (which was a silent no-op on an already-correctly-
               indented top-level line, column 0) - every other mode
               above still recalculates indent on TAB
  .fth .fs     forth. autoinsert word + snippet (forthc is a one-shot
               compiler, so no repl wiring)
  .k .ks        kona (my own major mode). repl + autoinsert + snippet
  .cron        crontab-mode + next-runs preview
  .nsc .nsh    nsc (druid520/nscc.git), c-mode derived - same tabs/
               allman style as .c for free (c-mode-hook fires for a
               derived mode too), plus nsc's own explicit-width types
               and global/include highlighted. checkonsave runs nscc
               itself (a .nsh compiles standalone same as a .nsc, no
               -x-c-style flag needed) + gtags + autoinsert
  .lisp .lsp   common lisp via slime + sbcl (slime, not sly - same
  .cl .asd     established-over-newer call as vc over magit). own
               keys (C-c C-c compile-defun, C-c C-l load-file - both
               slime-mode's own, kona and gtags both stay out).
               `M-x slime' is the actual command that starts sbcl and
               connects - C-c C-z (slime-switch-to-output-buffer)
               only switches to an already-connected repl, unlike
               scheme's own geiser C-c C-z, and errors "Not
               connected." if you press it first. completion is
               connection-aware: slime's own completion only runs
               once actually connected, cape/dabbrev fills in before
               that so corfu never errors meanwhile (see
               radium-lang-lisp.el's own header for the real bug this
               fixes). no checkonsave - real cl has no one-shot
               syntax check the way perl -c/sh -n do
  .py          python.el, tabs (this config's own policy, not pep8's -
               still standard-legal: python only forbids MIXING tabs
               and spaces within one block, tabs alone are fine).
               checkonsave via python3's own ast.parse (a real syntax-
               only check, no .pyc bytecode left behind the way
               py_compile would) - the real prerequisite for item 22
               (capstone/keystone/unicorn): none of the three ship a
               standalone cli tool at all, they're consumed by
               writing real python that imports them as a library,
               checked directly (all three installed fresh in this
               same sandbox specifically to check, no cstool-like
               binary appeared on $PATH for any of them)
  .tex         auctex, tabs. no checkonsave (chktex/pdflatex are too
               heavy/no standalone package - see below)
  .btft .btfs  btf (druid520/btf.git, my own markup format - one
               grammar, both extensions: a .btfs stylesheet's
               [rule sel=...]/[prop n=...] are ordinary tags under the
               same [name attr="val"]...[e] shape .btft pages use).
               my own major mode (no package covers it), fontification
               only - no gtags/checkonsave/proto, nothing there to
               index or syntax-check
 
everywhere: syntax highlighting (256-color), symbol-overlay, vundo,
diff-hl margins, multiple cursors, embark, the macro engine, wgrep.
scheme/forth/kona/cron have no one-shot syntax checker on netbsd, so
they just arent in checkonsave. languages without a
prototype/definition split (scheme, sh, asm, forth, kona, elisp) have
no stub either - nothing to fill from.
 
## things worth knowing
 
- completion in c/ada/scheme/perl hits the gtags db first (with real
  param autofill), and falls through to ggtags/cape/dabbrev when the
  db has nothing. a machine-wide db over /usr/include is optional:
  mkdir -p ~/.cache/gtags-sys && cd /usr/include && gtags ~/.cache/gtags-sys
  then printf and friends complete too.
- the macro confirm-gated commands (shell, gitlog) only run via C-c e
  and always ask first. everything else in the safe table is pure
  text.
- checkonsave runs on save, not on type, and never blocks - its
  buffer only pops when something actually fails.
 
powered by btf.