Agent skill

emacs-variables

Use when inspecting or modifying Emacs variables - provides elisp patterns for variable handling, customization, and state management

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/testing/emacs-variables

SKILL.md

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

elisp
(defvar my-var "default" "Documentation string.")
(defvar my-var nil)  ; define without overwriting

Set variable

elisp
(setq my-var "value")
(setq var1 "val1" var2 "val2")  ; multiple

Get variable value

elisp
my-var  ; just use the symbol
(symbol-value 'my-var)  ; programmatic access

Buffer-Local Variables

Make buffer-local

elisp
(make-local-variable 'my-var)
(setq-local my-var "buffer-specific")

Define as buffer-local by default

elisp
(defvar-local my-buffer-var nil "Always buffer-local.")

Get value in other buffer

elisp
(buffer-local-value 'my-var other-buffer)

Check if buffer-local

elisp
(local-variable-p 'my-var)
(local-variable-if-set-p 'my-var)

User Options (Customizable)

Define custom option

elisp
(defcustom my-option "default"
  "Documentation for option."
  :type 'string
  :group 'my-group)

Common :type values

elisp
:type 'boolean
:type 'string
:type 'integer
:type '(choice (const nil) (string :tag "Custom"))
:type '(repeat string)

Variable Inspection

Check if bound

elisp
(boundp 'my-var)  ; is it defined?

Describe variable

elisp
(describe-variable 'my-var)  ; interactive

Get documentation

elisp
(documentation-property 'my-var 'variable-documentation)

Let Bindings

Local binding

elisp
(let ((x 1)
      (y 2))
  (+ x y))  ; x, y only exist here

Sequential binding

elisp
(let* ((x 1)
       (y (+ x 1)))  ; y can use x
  y)

Dynamic Binding

Temporarily change variable

elisp
(let ((some-global-var "temporary"))
  ;; functions called here see temporary value
  (do-something))
;; original value restored

Common pattern

elisp
(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:

elisp
;; 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:

elisp
(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
elisp
;; 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:

elisp
;;; my-file.el --- Description -*- lexical-binding: t; -*-

Then regular let creates closures correctly:

elisp
;; Works with lexical-binding: t
(defun make-fn (name)
  (let ((tn name))
    (lambda () (message "Name: %s" tn))))

Hash Tables

Create hash table

elisp
(make-hash-table :test 'equal)

Access hash table

elisp
(gethash key table)
(gethash key table default)
(puthash key value table)
(remhash key table)

Iterate hash table

elisp
(maphash (lambda (key value)
           ;; process key, value
           )
         table)

Property Lists

Get property

elisp
(plist-get '(:a 1 :b 2) :a)  ; => 1

Put property

elisp
(plist-put plist :key value)

In symbol properties

elisp
(get 'my-symbol 'property)
(put 'my-symbol 'property value)

Common Patterns

Safe variable access

elisp
(when (boundp 'maybe-var)
  (symbol-value 'maybe-var))

Toggle boolean

elisp
(setq my-flag (not my-flag))

Increment/modify

elisp
(cl-incf counter)
(cl-decf counter)
(push item my-list)
(pop my-list)

Environment variables

elisp
(getenv "PATH")
(setenv "MY_VAR" "value")

Mode-line variables

elisp
;; 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

elisp
(add-hook 'some-mode-hook #'my-function)
(add-hook 'some-mode-hook #'my-function nil t)  ; buffer-local

Remove from hook

elisp
(remove-hook 'some-mode-hook #'my-function)

Run hooks

elisp
(run-hooks 'my-hook)
(run-hook-with-args 'my-hook arg1 arg2)

Didn't find tool you were looking for?

Be as detailed as possible for better results