diff options
Diffstat (limited to '.claude/rules/elisp.md')
| -rw-r--r-- | .claude/rules/elisp.md | 75 |
1 files changed, 0 insertions, 75 deletions
diff --git a/.claude/rules/elisp.md b/.claude/rules/elisp.md deleted file mode 100644 index e641058..0000000 --- a/.claude/rules/elisp.md +++ /dev/null @@ -1,75 +0,0 @@ -# Elisp / Emacs Rules - -Applies to: `**/*.el` - -## Style - -- 2-space indent, no tabs -- Hyphen-case for identifiers: `cj/do-thing`, not `cj/doThing` -- Naming prefixes: - - `cj/name` — user-facing functions and commands (bound to keys, called from init) - - `cj/--name` — private helpers (double-dash signals "internal") - - `<module>/name` — module-scoped where appropriate (e.g., `calendar-sync/parse-ics`) -- File header: `;;; foo-config.el --- brief description -*- lexical-binding: t -*-` -- `(provide 'foo-config)` at the bottom of every module -- `lexical-binding: t` is mandatory — no file without it - -## Function Design - -- Keep functions under 15 lines where possible -- One responsibility per function -- Extract helpers instead of nesting deeply — 5+ levels of nesting is a refactor signal -- Prefer named helpers over lambdas for anything nontrivial -- No premature abstraction — three similar lines beats a clever macro - -Small functions are the single strongest defense against paren errors. Deeply nested code is where AI and humans both fail. - -## Requires and Loading - -- Every `(require 'foo)` must correspond to a loadable file on the load-path -- Byte-compile warnings about free variables usually indicate a missing `require` or a typo in a symbol name — read them -- Use `use-package` for external (MELPA/ELPA) packages -- Use plain `(require 'foo-config)` for internal modules -- For optional features, `(when (require 'foo nil t) ...)` degrades gracefully if absent - -## Lexical-Binding Traps - -- `(boundp 'x)` where `x` is a lexical variable always returns nil. Bind with `defvar` at top level if you need `boundp` to work, or use the value directly. -- `setq` on an undeclared free variable is a warning — use `let` for locals or `defvar` for module-level state -- Closures capture by reference. Avoid capturing mutating loop variables in nested defuns. - -## Regex Gotchas - -- `\s` is NOT whitespace in Emacs regex. Use `[ \t]` or `\\s-` (syntax class). -- `^` in `string-match` matches after `\n` OR at position 0 — use `(= (match-beginning 0) start)` for positional checks when that matters. -- `replace-regexp-in-string` interprets backslashes in the replacement. Pass `t t` (FIXEDCASE LITERAL) when the replacement contains literal backslashes. - -## Keybindings - -- `keymap-global-set` for global; `keymap-set KEYMAP ...` for mode-local -- Group module-specific bindings inside the module's file -- Autoload cookies (`;;;###autoload`) don't activate through plain `(require ...)` — use the form directly, not an autoloaded wrapper - -## Module Template - -```elisp -;;; foo-config.el --- Foo feature configuration -*- lexical-binding: t -*- - -;;; Commentary: -;; One-line description. - -;;; Code: - -;; ... code ... - -(provide 'foo-config) -;;; foo-config.el ends here -``` - -Then `(require 'foo-config)` in `init.el` (or a config aggregator). - -## Editing Workflow - -- A PostToolUse hook runs `check-parens` and `byte-compile-file` on every `.el` save -- If it blocks, read the error — don't retry blindly -- Prefer Write over repeated Edits for nontrivial new code; incremental edits accumulate subtle paren mismatches |
