Skills on AI 459 skills

Active theme: Light

Emacs

Use when asked to write or debug Emacs Lisp, configure Emacs (init.el/early-init.el, use-package, package.el/straight.el), explain Emacs keybinding notation, or work with buffers, windows, frames, and major/minor modes.

Emacs is an extensible, self-documenting text editor whose entire UI and behavior is programmed in Emacs Lisp (elisp), a Lisp-1 dialect. "Configuring Emacs" and "writing an Emacs Lisp program" are the same activity.

Keybinding notation

  • C-x — hold Control, press x. M-x — Meta (usually Alt, or Esc then the key) plus x. C-x C-f — two chords in sequence (find-file).
  • Every binding names an interactive command: C-x C-f is bound to find-file. M-x <command> RET runs any command by name regardless of its binding — this is the universal escape hatch when you don't know or don't have a keybinding.
  • C-h k (describe-key), C-h f (describe-function), C-h v (describe-variable), C-h m (describe-mode) are the built-in self-documentation commands — reach for these before guessing.

Core concepts

  • Buffer — an in-memory object holding text (a visited file, scratch, Messages, a shell, …). Not the same as a window or a file.
  • Window — a viewport onto a buffer within a frame. C-x 2 / C-x 3 split; C-x o moves between them; C-x 1 deletes all but the current one.
  • Frame — what most people call a "window" at the OS level (C-x 5 2 creates a new one).
  • Major mode — exactly one per buffer, defines core behavior (python-mode, org-mode, fundamental-mode). Minor modes — any number, layer on optional behavior (flyspell-mode, subword-mode).

Configuration

~/.emacs.d/init.el (or ~/.config/emacs/init.el) is elisp, evaluated top to bottom at startup. early-init.el runs before the package system and UI initialize (frame parameters, package-enable-at-startup).

;; Package management with built-in use-package (Emacs 29+) + package.el
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

(use-package magit
  :ensure t
  :bind ("C-x g" . magit-status))

(use-package company
  :ensure t
  :hook (prog-mode . company-mode)
  :config
  (setq company-idle-delay 0.2))

use-package keywords worth knowing: :ensure t (install if missing), :bind, :hook, :custom (set defcustom variables the "supported" way, distinct from plain setq), :config (runs after load), :init (runs before load).

Writing elisp

;; A minimal interactive command
(defun my/insert-todo ()
  "Insert a TODO comment at point."
  (interactive)
  (insert "TODO: "))

(global-set-key (kbd "C-c t") #'my/insert-todo)

;; Buffer-local state and hooks
(add-hook 'prog-mode-hook
          (lambda ()
            (setq-local show-trailing-whitespace t)))
  • (interactive) is what makes a defun callable via M-x or a keybinding; without it, it's a plain Lisp function.
  • (kbd "C-c t") parses human-readable keybinding notation into the internal key-sequence representation global-set-key expects.
  • Prefer setq-local / buffer-local hooks over global setq for anything mode-specific, so you don't leak behavior into unrelated buffers.
  • let and let for lexical scoping; enable lexical-binding: t as a file header (;;; -- lexical-binding: t; -*-) in any elisp file you write — Emacs still defaults new scratch buffers to it, but older code may not have it and behaves with dynamic scoping instead.

Common pitfalls

  • Editing custom-set-variables by hand. Emacs's Customize UI writes to a block in init.el (or a separate custom-file) — leave that block alone and prefer use-package :custom or explicit setq elsewhere, or the two systems fight each other.
  • Forgetting (interactive). A defun without it can still be called from other elisp, but M-x won't find it and it can't be bound to a key the normal way.
  • Global keybindings that shadow major-mode bindings. global-set-key always loses to a major or minor mode's own keymap. Bind into the specific mode's map ((define-key python-mode-map (kbd "...") #'...)) when you want a mode-specific override.
  • Blocking the UI with synchronous work. Emacs is single-threaded for elisp execution; a long-running loop freezes the whole editor. Use run-with-idle-timer, make-thread (native threads on modern builds), or an async subprocess instead of a busy loop.

Learn more

View emacs/SKILL.md on GitHub