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