Claude-skill-registry emacs-variables
Use when inspecting or modifying Emacs variables - provides elisp patterns for variable handling, customization, and state management
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/emacs-variables" ~/.claude/skills/majiayu000-claude-skill-registry-emacs-variables && rm -rf "$T"
manifest:
skills/data/emacs-variables/SKILL.mdsource content
Emacs Variables Skill
Display Guidelines
E-ink Monitor Compatibility: User uses an e-ink monitor that only supports black, white, and grey.
- NEVER use colors (
,:foreground "red"
, etc.):foreground "cyan" - Use
,:weight bold
,:weight light
,:slant italic
for differentiation:underline t - Standard Emacs faces (font-lock-*-face) are acceptable as they adapt to themes
Variable Basics
Define variable
(defvar my-var "default" "Documentation string.") (defvar my-var nil) ; define without overwriting
Set variable
(setq my-var "value") (setq var1 "val1" var2 "val2") ; multiple
Get variable value
my-var ; just use the symbol (symbol-value 'my-var) ; programmatic access
Buffer-Local Variables
Make buffer-local
(make-local-variable 'my-var) (setq-local my-var "buffer-specific")
Define as buffer-local by default
(defvar-local my-buffer-var nil "Always buffer-local.")
Get value in other buffer
(buffer-local-value 'my-var other-buffer)
Check if buffer-local
(local-variable-p 'my-var) (local-variable-if-set-p 'my-var)
User Options (Customizable)
Define custom option
(defcustom my-option "default" "Documentation for option." :type 'string :group 'my-group)
Common :type values
:type 'boolean :type 'string :type 'integer :type '(choice (const nil) (string :tag "Custom")) :type '(repeat string)
Variable Inspection
Check if bound
(boundp 'my-var) ; is it defined?
Describe variable
(describe-variable 'my-var) ; interactive
Get documentation
(documentation-property 'my-var 'variable-documentation)
Let Bindings
Local binding
(let ((x 1) (y 2)) (+ x y)) ; x, y only exist here
Sequential binding
(let* ((x 1) (y (+ x 1))) ; y can use x y)
Dynamic Binding
Temporarily change variable
(let ((some-global-var "temporary")) ;; functions called here see temporary value (do-something)) ;; original value restored
Common pattern
(let ((inhibit-read-only t)) ;; can modify read-only buffer here (erase-buffer))
Lexical Binding and Closures
IMPORTANT: Emacs has two binding modes - dynamic (default in older code) and lexical. Literate-elisp files and files without
lexical-binding: t use dynamic binding.
The Problem: Closures in Dynamic Binding
In dynamic binding, lambdas don't capture variables - they look them up at runtime:
;; BROKEN in dynamic binding - tn is void at call time (defun make-broken-fn (name) (let ((tn name)) (lambda () (message "Name: %s" tn)))) ; tn looked up when called, not defined!
Solution: Use lexical-let
lexical-let (from cl-lib) creates true lexical closures even in dynamic binding mode:
(require 'cl-lib) ;; WORKS - lexical-let captures variables properly (defun make-working-fn (name) (lexical-let ((tn name)) (lambda () (message "Name: %s" tn)))) ; tn captured at definition time
When to Use lexical-let
Use
lexical-let when:
- Creating closures/lambdas that reference outer variables
- Building callbacks or handler functions dynamically
- Any lambda that will be called later and needs captured state
;; Creating multiple closures that each capture different values (defun make-toggler (tag-name) "Create a function that toggles TAG-NAME." (lexical-let ((tn tag-name)) (lambda () (interactive) (toggle-tag tn)))) ;; Creating a description function for transient menu (defun make-description (tag desc) "Create a description function for TAG with DESC." (lexical-let ((t tag) (d desc)) (lambda () (format "[%s] %s" (if (selected-p t) "X" " ") d))))
Alternative: Enable Lexical Binding
For new files, prefer enabling lexical binding in the file header:
;;; my-file.el --- Description -*- lexical-binding: t; -*-
Then regular
let creates closures correctly:
;; Works with lexical-binding: t (defun make-fn (name) (let ((tn name)) (lambda () (message "Name: %s" tn))))
Hash Tables
Create hash table
(make-hash-table :test 'equal)
Access hash table
(gethash key table) (gethash key table default) (puthash key value table) (remhash key table)
Iterate hash table
(maphash (lambda (key value) ;; process key, value ) table)
Property Lists
Get property
(plist-get '(:a 1 :b 2) :a) ; => 1
Put property
(plist-put plist :key value)
In symbol properties
(get 'my-symbol 'property) (put 'my-symbol 'property value)
Common Patterns
Safe variable access
(when (boundp 'maybe-var) (symbol-value 'maybe-var))
Toggle boolean
(setq my-flag (not my-flag))
Increment/modify
(cl-incf counter) (cl-decf counter) (push item my-list) (pop my-list)
Environment variables
(getenv "PATH") (setenv "MY_VAR" "value")
Mode-line variables
;; For e-ink: use weight/slant, not colors (setq my-mode-line-string (propertize " [Status]" 'face '(:weight bold) 'help-echo "Tooltip text"))
Hooks
Add to hook
(add-hook 'some-mode-hook #'my-function) (add-hook 'some-mode-hook #'my-function nil t) ; buffer-local
Remove from hook
(remove-hook 'some-mode-hook #'my-function)
Run hooks
(run-hooks 'my-hook) (run-hook-with-args 'my-hook arg1 arg2)