---
name: Emacs Ecosystem
description: This skill should be used when the user asks to "write elisp", "emacs config", "init.el", "use-package", ".el file", "emacs lisp", or "magit". Provides comprehensive Emacs ecosystem patterns and best practices. For org-mode, use org-ecosystem skill.
---
Provide comprehensive patterns for Emacs Lisp, configuration management, package systems, and major packages including Magit and LSP integration. For org-mode patterns, see org-ecosystem skill.
S-expressions as code and data (homoiconicity). Prefix notation for all operations.
Emacs Lisp data types: symbol, cons cell, list, vector, hash-table, string, number
;; symbol: Named objects
'foo
:keyword
;; cons_cell: Pair
(cons 1 2) ; => (1 . 2)
;; list: Linked cons cells
'(1 2 3)
;; vector: Fixed-size array
[1 2 3]
;; hash-table: Key-value store
(make-hash-table)
;; string: Text
"hello"
;; number: Integer or float
42
3.14
Define functions with defun
(defun my-function (arg1 arg2)
"Docstring describing the function."
(+ arg1 arg2))
Local variable binding with let and let*
(let ((x 1)
(y 2))
(+ x y))
(let\* ((x 1)
(y (+ x 1))) ; y can reference x
y)
Conditional forms: if, when, unless, cond, pcase
(if condition
then-form
else-form)
(when condition
body-forms...)
(unless condition
body-forms...)
(cond
(condition1 result1)
(condition2 result2)
(t default-result))
(pcase value
('symbol (handle-symbol))
((pred stringp) (handle-string))
(\_ (handle-default)))
Iteration patterns: dolist, dotimes, cl-loop, seq functions
(dolist (item list)
(process item))
(dotimes (i 10)
(process i))
(cl-loop for item in list
collect (transform item))
(seq-map #'transform sequence)
(seq-filter #'predicate sequence)
(seq-reduce #'fn sequence initial)
Anonymous functions with lambda
(lambda (x) (* x 2))
(mapcar (lambda (x) (\* x 2)) '(1 2 3))
;; Short form (Emacs 28+)
(mapcar (lambda (x) (+ x 1)) list)
Define macros with defmacro. Use backquote for templates, comma for evaluation
(defmacro with-temp-message (msg &rest body)
"Execute BODY with MSG displayed temporarily."
`(let ((message-log-max nil))
(message "%s" ,msg)
(unwind-protect
(progn ,@body)
(message nil))))
Modern init.el organization
;;; init.el --- Emacs configuration -\*- lexical-binding: t; -\_-
;;; Commentary:
;; Personal Emacs configuration
;;; Code:
;; Bootstrap package manager
(require 'package)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
(package-initialize)
;; Install use-package if not present
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(eval-when-compile
(require 'use-package))
;; Configuration sections...
(provide 'init)
;;; init.el ends here
Declarative package configuration with use-package keywords
Does the package need lazy loading or declarative configuration?
Use use-package for clean, maintainable configuration
Use require for simple packages with no configuration needs
(use-package company
:ensure t
:defer t
:hook (prog-mode . company-mode)
:bind (:map company-active-map
("C-n" . company-select-next)
("C-p" . company-select-previous))
:custom
(company-idle-delay 0.2)
(company-minimum-prefix-length 2)
:config
(setq company-backends '(company-capf)))
Keywords:
- :ensure - Install package if not present
- :defer - Lazy load (t or seconds)
- :hook - Add to mode hooks
- :bind - Define keybindings
- :custom - Set customizable variables
- :init - Run before package loads
- :config - Run after package loads
- :commands - Autoload commands
- :after - Load after specified packages
- :if/:when/:unless - Conditional loading
Key binding patterns: global-set-key, define-key, use-package :bind
;; Global keybinding
(global-set-key (kbd "C-c l") #'org-store-link)
;; Mode-specific
(define-key emacs-lisp-mode-map (kbd "C-c C-e") #'eval-last-sexp)
;; With use-package
(use-package magit
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch)))
;; Keymap definition
(defvar my-prefix-map (make-sparse-keymap)
"Keymap for my custom commands.")
(global-set-key (kbd "C-c m") my-prefix-map)
(define-key my-prefix-map (kbd "f") #'find-file)
Hook management with add-hook and use-package :hook
;; Add function to hook
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
;; Remove function from hook
(remove-hook 'prog-mode-hook #'display-line-numbers-mode)
;; Lambda in hook (discouraged for removability)
(add-hook 'after-save-hook
(lambda () (message "Saved!")))
;; With use-package
(use-package flycheck
:hook (prog-mode . flycheck-mode))
Modify existing functions with advice-add and advice-remove
(defun my-after-save-message (orig-fun &rest args)
"Show message after save."
(apply orig-fun args)
(message "Buffer saved at %s" (current-time-string)))
(advice-add 'save-buffer :around #'my-after-save-message)
;; Remove advice
(advice-remove 'save-buffer #'my-after-save-message)
Define customizable variables with defgroup and defcustom
(defgroup my-package nil
"My package customization."
:group 'convenience
:prefix "my-package-")
(defcustom my-package-option t
"Enable my-package option."
:type 'boolean
:group 'my-package)
(defcustom my-package-list '("a" "b")
"List of strings."
:type '(repeat string)
:group 'my-package)
Built-in package manager for Emacs
;; Commands:
;; - package-install - Install a package
;; - package-delete - Remove a package
;; - package-refresh-contents - Update package list
;; - package-list-packages - Browse packages
(require 'package)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")))
(package-initialize)
;; Install a package
(package-install 'magit)
Functional package manager with Git integration
;; Bootstrap
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el"
user-emacs-directory)))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el")
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
;; Use with use-package
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
;; Install package
(use-package magit
:straight t)
Modern async package manager
;; Bootstrap
(defvar elpaca-installer-version 0.7)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
;; ... (bootstrap code)
;; Use with use-package
(elpaca elpaca-use-package
(elpaca-use-package-mode))
(use-package magit
:ensure t)
Git porcelain for Emacs
Basic Magit setup with use-package
(use-package magit
:ensure t
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch)
("C-c M-g" . magit-file-dispatch)))
Magit status buffer keybindings
;; s - Stage file/hunk
;; u - Unstage file/hunk
;; c c - Commit
;; P p - Push
;; F p - Pull
;; b b - Checkout branch
;; b c - Create branch
;; l l - Log current branch
;; d d - Diff
Magit configuration settings
(setq magit-save-repository-buffers 'dontask)
(setq magit-display-buffer-function
#'magit-display-buffer-same-window-except-diff-v1)
(setq magit-diff-refine-hunk 'all)
GitHub/GitLab integration with Forge
(use-package forge
:after magit
:ensure t)
Do you need LSP features like completion, go-to-definition, and diagnostics?
Use eglot for built-in simplicity or lsp-mode for rich features
Use basic major modes without LSP overhead
Built-in LSP client (Emacs 29+)
(use-package eglot
:ensure nil ; built-in
:hook ((python-mode . eglot-ensure)
(typescript-mode . eglot-ensure)
(rust-mode . eglot-ensure))
:config
(setq eglot-autoshutdown t)
(setq eglot-events-buffer-size 0))
;; Custom server configuration
(add-to-list 'eglot-server-programs
'(rust-mode . ("rust-analyzer")))
Feature-rich LSP client with lsp-mode and lsp-ui
(use-package lsp-mode
:ensure t
:hook ((python-mode . lsp-deferred)
(typescript-mode . lsp-deferred))
:commands (lsp lsp-deferred)
:custom
(lsp-keymap-prefix "C-c l")
(lsp-idle-delay 0.5)
(lsp-log-io nil)
:config
(lsp-enable-which-key-integration t))
(use-package lsp-ui
:ensure t
:hook (lsp-mode . lsp-ui-mode)
:custom
(lsp-ui-doc-enable t)
(lsp-ui-sideline-enable t))
LSP completion with corfu/company
;; With corfu (modern)
(use-package corfu
:ensure t
:custom
(corfu-auto t)
(corfu-cycle t)
:init
(global-corfu-mode))
;; With company (traditional)
(use-package company
:ensure t
:hook (after-init . global-company-mode)
:custom
(company-idle-delay 0.2))
Vertical completion UI with orderless, marginalia, and consult
(use-package vertico
:ensure t
:init (vertico-mode))
(use-package orderless
:ensure t
:custom
(completion-styles '(orderless basic)))
(use-package marginalia
:ensure t
:init (marginalia-mode))
(use-package consult
:ensure t
:bind (("C-s" . consult-line)
("C-x b" . consult-buffer)
("M-g g" . consult-goto-line)))
Display available keybindings
(use-package which-key
:ensure t
:diminish
:init (which-key-mode))
Tree-sitter integration (Emacs 29+)
(setq treesit-language-source-alist
'((python "https://github.com/tree-sitter/tree-sitter-python")
(javascript "https://github.com/tree-sitter/tree-sitter-javascript")
(typescript "https://github.com/tree-sitter/tree-sitter-typescript"
"master" "typescript/src")))
;; Install grammars
(mapc #'treesit-install-language-grammar
(mapcar #'car treesit-language-source-alist))
;; Remap modes
(setq major-mode-remap-alist
'((python-mode . python-ts-mode)
(javascript-mode . js-ts-mode)))
Resolve library ID (known: /websites/emacsdocs)
Fetch documentation with specific topic
Emacs Lisp programming patterns
Package configuration patterns
Org mode configuration
Magit usage and configuration
Hook usage patterns
Key binding patterns
Function definition
Advice system usage
Customization variables
Enable lexical-binding in all Elisp files: -\*- lexical-binding: t; -\_-
Use #'function-name for function references (enables byte-compiler warnings)
Document functions with docstrings
Namespace all symbols with package prefix
Prefer seq.el functions for sequence operations
Use pcase for complex pattern matching
Use defcustom for user-configurable options
Use provide at end of file
Prefer :custom over setq in use-package
Use :hook instead of add-hook in use-package
Lazy load packages with :defer, :commands, or :hook
Use native-compilation when available (Emacs 28+)
Prefer eglot for LSP (built-in, simpler)
Use tree-sitter modes when available (Emacs 29+)
Using dynamic binding when lexical is needed
Add lexical-binding: t to file header
Hardcoding absolute paths
Use expand-file-name, user-emacs-directory, or locate-user-emacs-file
Requiring packages at top level unconditionally
Use autoload, use-package with :defer, or eval-after-load
Modifying global state without restoration
Use let-binding or save-excursion/save-restriction
Adding lambdas to hooks (hard to remove)
Define named functions and add those
Using setq for defcustom variables
Use customize-set-variable or :custom in use-package
Using deprecated cl library
Use cl-lib with cl- prefixed functions
Using eval-after-load with string
Use with-eval-after-load or use-package :config
Complex logic in early-init.el
Keep early-init.el minimal (frame settings, package setup)
Understand Emacs Lisp requirements
1. Check package dependencies and autoloads
2. Review existing configuration patterns
3. Identify hook and advice usage
Write idiomatic Emacs Lisp code
1. Use lexical binding
2. Follow Emacs Lisp conventions
3. Provide appropriate customization options
Verify Emacs Lisp correctness
1. Byte-compile without warnings
2. Test in clean Emacs instance
3. Verify keybindings don't conflict
Architecture analysis for elisp package structure
Docstring and commentary generation
Elisp implementation and configuration tasks
Debugging elisp errors and hook issues
Org-mode document creation, GTD workflow, Babel, export patterns
Symbol operations for elisp code navigation
Emacs documentation lookup via /websites/emacsdocs
Debugging package conflicts and performance issues
Creating package documentation and README files
Byte-compilation warning
Fix warning, ensure clean compilation
Configuration error on startup
Debug with --debug-init, fix issue
Package conflict or version mismatch
Stop, present resolution options to user
Emacs becomes unusable
Provide recovery steps, require user action
Use lexical-binding: t in all files
Provide customization via defcustom
Follow Emacs Lisp naming conventions
Dynamic binding without justification
Overriding standard keybindings silently
Blocking operations in hooks