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.md
source 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"
    ,
    :foreground "cyan"
    , etc.)
  • Use
    :weight bold
    ,
    :weight light
    ,
    :slant italic
    ,
    :underline t
    for differentiation
  • 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)