The Emacs Package Developer's Handbook

Table of Contents

After developing some little Emacs packages for a year or so, I began to notice how I'd forget little things that I learned, and then I'd have to go hunting for that information again. I also noticed how there are some issues for which there doesn't seem to be a "best practice" or "Standard Operating Procedure" to refer to.

So this is intended to be a place to collect and organize information related to Emacs package development. Built with Emacs, by Emacs package developers, for Emacs package developers.

You can read this Org file directly on the repository, or you can read the HTML version.

Note: The primary sections are listed at the top of the page in the horizontal bar.

Emacs Lisp   elisp

Asynchronicity   async

Binding   scope binding

Information related to variable scope and binding in elisp code (e.g. lexical vs. dynamic scope).

Articles   articles

  • Make Flet Great Again « null program   macros flet letf

    Chris Wellons explains how the old cl macro flet changes in its new cl-lib version, cl-flet, and how to use cl-letf to achieve the old functionality. It's a way to override functions in both lexical and dynamic scope, which is especially useful for unit testing.

Libraries   libraries

  • dash.el
  • thunk   built_in

    Thunk provides functions and macros to delay the evaluation of forms.

    Use thunk-delay to delay the evaluation of a form (requires lexical-binding), and thunk-force to evaluate it. The result of the evaluation is cached, and only happens once.

    Here is an example of a form which evaluation is delayed:

    (setq delayed (thunk-delay (message "this message is delayed")))

    delayed is not evaluated until thunk-force is called, like the following:

    (thunk-force delayed)

    This file also defines macros thunk-let and thunk-let* that are analogous to let and let* but provide lazy evaluation of bindings by using thunks implicitly (i.e. in the expansion).

Tools   tools

Buffers   buffers

Best practices   best_practices

  • Inserting strings   strings

    Inserting strings into buffers with insert is generally fast, but it can slow down in buffers with lots of markers or overlays. In general, it can be faster to insert one large string (which may include newlines). For example:

    (let ((strings (cl-loop for i from 1 to 1000
                            collect (number-to-string i))))
      (--sort (< (caddr it) (caddr other))
              (cl-loop for times in '(1 10 100)
                       append (a-list "(loop do (insert ..."
                                      (cons times
                                            (benchmark-run-compiled times
                                                (cl-loop for string in strings
                                                         do (insert string)))))
                                      "(apply #'insert ..."
                                      (cons times
                                            (benchmark-run-compiled times
                                                (apply #'insert strings))))
                                      "(insert (apply #'concat ..."
                                      (cons times
                                            (benchmark-run-compiled times
                                                (insert (apply #'concat strings)))))))))
    (insert (apply #'concat … 10 0.000116547 0 0.0
    (insert (apply #'concat … 100 0.000117023 0 0.0
    (insert (apply #'concat … 1 0.000124629 0 0.0
    (apply #'insert … 10 0.000614555 0 0.0
    (apply #'insert … 1 0.000631174 0 0.0
    (loop do (insert … 100 0.000631768 0 0.0
    (loop do (insert … 10 0.000644066 0 0.0
    (apply #'insert … 100 0.000644767 0 0.0
    (loop do (insert … 1 0.001106179 0 0.0

    The fastest method here is to call insert once with the result of calling concat once, using apply to pass all of the strings.

Libraries   libraries

  • m-buffer-el: List Oriented Buffer Operations
  • bui.el: Buffer interface library

    BUI (Buffer User Interface) is an Emacs library that can be used to make user interfaces to display some kind of entries (like packages, buffers, functions, etc.). The intention of BUI is to be a high-level library which is convenient to be used both by:

    package makers, as there is no need to bother about implementing routine details and usual features (like buffer history, filtering displayed entries, etc.);

    users, as it provides familiar and intuitive interfaces with usual keys (for moving by lines, marking, sorting, switching between buttons); and what is also important, the defined interfaces are highly configurable through various generated variables.

Checkers / linters   linters checkers

TODO Flycheck-package

Collections (lists, vectors, hash-tables, etc.)   collections

Examples   examples

  • Alists   alists
    • Creation
      ;;;; Built-in methods
      (list (cons 'one 1) (cons 'two 2))  ;; => ((one . 1) (two . 2))
      '((one . 1) (two . 2))  ;; => ((one . 1) (two . 2))
      (let ((numbers (list)))
        (map-put numbers 'one 1)
        (map-put numbers 'two 2))  ;; => ((two . 2) (one . 1))
      ;;;; Packages
      ;; `a-list' from a.el is the best way to make a new alist.
      (a-list 'one 1
              'two 2)  ;; => ((one . 1) (two . 2))
    • Adding to
      • Single elements
        ;;;; Built-in methods
        ;; `map-put' is the best built-in way.  Requires Emacs 25.1+.
        (let ((numbers (list (cons 'one 1))))
          (map-put numbers 'two 2)
          numbers)  ; => ((two . 2) (one . 1))
        ;; More primitive methods
        ;; Not recommended, but not too complicated:
        (let ((numbers (list (cons 'one 1)))
              (more-numbers (a-list 'two 2
                                    'three 3)))
          (append numbers more-numbers)) ;; => ((one . 1) (two . 2) (three . 3))
        ;; Don't do it this way, but it does demonstrate list/cons-cell
        ;; structure:
        (let ((numbers (list (cons 'one 1))))
          (cons (cons 'three 3)
                (cons (cons 'two 2)
                      numbers)))  ;; => ((three . 3) (two . 2) (one . 1))
      • Multiple elements
        ;;;; Built-in methods
        ;; `map-merge': if you're restricted to built-in packages, this works
        ;; well (requires Emacs 25.1+):
        (let ((numbers (list (cons 'one 1)))
              (more-numbers (a-list 'two 2
                                    'three 3)))
          (map-merge 'list numbers more-numbers))  ;; => ((three . 3) (two . 2) (one . 1))
        ;; Without map.el, you could use `append':
        (let ((numbers (list (cons 'one 1)))
              (more-numbers (a-list 'two 2
                                    'three 3)))
          (append numbers more-numbers)) ;; => ((one . 1) (two . 2) (three . 3))
        ;;;; Packages
        ;; `a-merge' from a.el is probably the best way:
        (let ((numbers (list (cons 'one 1)))
              (more-numbers (a-list 'two 2
                                    'three 3)))
          (a-merge numbers more-numbers))  ;; => ((three . 3) (two . 2) (one . 1))

Libraries   libraries

  • a.el: functions for dealing with association lists and hash tables. Inspired by Clojure.   alists hash_tables
  • asoc.el: alist library   alists
  • emacs-kv: key/value collection-type functions, for alists, hash tables and plists   alists hash_tables plists
  • ht.el: The missing hash table library   hash_tables

    This library provides a consistent and comprehensive set of functions for working with hash tables: they're named consistently, take a natural and consistent argument order, and cover operations that the standard Emacs functions don't.

  • list-utils: List-manipulation utility functions   lists

    Similar to dash.el, but with slightly different behavior that may be useful, and some unique features. These functions are provided:

    make-tconc list-utils-depth
    tconc-p list-utils-flat-length
    tconc-list list-utils-flatten
    tconc list-utils-alist-or-flat-length
    list-utils-cons-cell-p list-utils-alist-flatten
    list-utils-cyclic-length list-utils-insert-before
    list-utils-improper-p list-utils-insert-after
    list-utils-make-proper-copy list-utils-insert-before-pos
    list-utils-make-proper-inplace list-utils-insert-after-pos
    list-utils-make-improper-copy list-utils-and
    list-utils-make-improper-inplace list-utils-not
    list-utils-linear-p list-utils-xor
    list-utils-linear-subseq list-utils-uniq
    list-utils-cyclic-p list-utils-dupes
    list-utils-cyclic-subseq list-utils-singlets
    list-utils-make-linear-copy list-utils-partition-dupes
    list-utils-make-linear-inplace list-utils-plist-reverse
    list-utils-safe-length list-utils-plist-del

Data structure   data_structure

Date / Time   dates times

Libraries   libraries

  • emacs-datetime

    The primary function provided is: (datetime-format SYM-OR-FMT &optional TIME &rest OPTION)

    (datetime-format "%Y-%m-%d")
    (datetime-format 'atom)
    (datetime-format 'atom "2112-09-03 00:00:00" :timezone "UTC")

    There are several other symbols provided besides atom, such as rfc-3339, which formats dates according to that RFC.

Destructuring   destructuring

Editing   editing

Tools   tools

  • aggressive-indent-mode: minor mode that keeps your code always indented   formatting indentation parentheses
  • beginend.el   navigation

    This package, by Damien Cassou and Matus Goljer, helps navigation by redefining the M-< and M-> keys do, depending on the major-mode.

  • expand-region.el: Increase selected region by semantic units   selection region
  • helm-navi: Navigate file sections and language keywords using Helm   navigation
  • iedit: Edit multiple regions simultaneously in a buffer or a region   refactoring

    iedit makes it easy to rename symbols within a function or in a whole buffer. Simply activate iedit-mode with point on a symbol, and it will be highlighted in the chosen scope, and any changes you make to the symbol are made in each highlighted occurrence. It's like a smart, purposeful version of multiple-cursors.

    The editor of this handbook uses iedit with these customizations:

    Globally bound to C-;. In a prog-mode-derived buffer, either corrects the last misspelled word with flyspell when point is in a comment or string, or activates iedit-mode. In non- prog-mode-derived buffers, corrects with flyspell.
    (defun ap/iedit-or-flyspell ()
      "Call `iedit-mode' or correct misspelling with flyspell, depending..."
      (if (or iedit-mode
              (and (derived-mode-p 'prog-mode)
                   (not (or (nth 4 (syntax-ppss))
                            (nth 3 (syntax-ppss))))))
          ;; prog-mode is active and point is in a comment, string, or
          ;; already in iedit-mode
          (call-interactively #'ap/iedit-mode)
        ;; Not prog-mode or not in comment or string
        (if (not (equal flyspell-previous-command this-command))
            ;; FIXME: This mostly works, but if there are two words on the
            ;; same line that are misspelled, it doesn't work quite right
            ;; when correcting the earlier word after correcting the later
            ;; one
            ;; First correction; autocorrect
            (call-interactively 'flyspell-auto-correct-previous-word)
          ;; First correction was not wanted; use popup to choose
              (undo)) ; This doesn't move point, which I think may be the problem.
            (flyspell-region (line-beginning-position) (line-end-position))
            (call-interactively 'flyspell-correct-previous-word-generic)))))
    Calls iedit-mode with function-local scope by default, or global scope when called with a universal prefix.
    (defun ap/iedit-mode (orig-fn)
      "Call `iedit-mode' with function-local scope by default, or global scope if called with a universal prefix."
      (pcase current-prefix-arg
        ('nil (funcall orig-fn '(0)))
        ('(4) (funcall orig-fn))
        (_ (user-error "`ap/iedit-mode' called with prefix: %s" prefix))))
    ;; Override default `iedit-mode' function with advice.
    (advice-add #'iedit-mode :around #'ap/iedit-mode)
    Helpful minibuffer message
    Confirms when an iedit session has started.
    (advice-add 'iedit-mode :after (lambda (&optional ignore)
                                     (when iedit-mode
                                       (minibuffer-message "iedit session started. Press C-; to end."))))
  • lispy: short and sweet LISP editing   elisp navigation parentheses
  • multi-line: multi-line everything from function invocations and definitions to array and map literals in a wide variety of languages   formatting
  • multiple-cursors.el: Multiple cursors   selection editing
  • smartparens: Minor mode that deals with parens pairs and tries to be smart about it   navigation editing parentheses

Functions   functions

Including anonymous functions (lambdas).


General   general

Libraries   libraries

  • Common Lisp Extensions (cl-lib)   built_in

    This is the built-in cl-lib package which implements Common Lisp functions and control structures for Emacs Lisp.

  • dash.el   dash

    Dash is a powerful general-purpose library that provides many useful functions and macros.

  • subr-x   built_in strings flow_control

    Less commonly used functions that complement basic APIs, often implemented in C code (like hash-tables and strings), and are not eligible for inclusion in subr.el.

    This is a built-in package that provides several useful functions and macros, such as thread-first / last, if-let / when-let, hash-table functions, and string functions. It's easy to forget about this, since:

    Do not document these functions in the lispref.

Multiprocessing (generators, threads)   multiprocessing

Articles   articles

Packaging   packaging

Best practices   best_practices

  • Autoloads   autoloads
    • TODO Autoloading macro-generated functions

      This may actually be a bug, or at least an unanswered question.

      How to use autoload cookies for custom defun-like macros? : emacs:

      Say I have a macro deffoo that expands to some custom kind of defun, and I want to use an autoload cookie to autoload the result. According to the manual,

      ;;;###autoload (deffoo bar   ...)

      copies the entire form to autoloads.el, and something like

      ;;;###autoload (autoload 'bar "this-file") (deffoo bar   ...)

      should be used instead. What confuses me is this StackOverflow comment by who appears to be Stefan Monnier, saying that Emacs should expand the macro before generating the autoload, and that it's probably a bug when this does not happen.

      Can anyone clear up what the intended behaviour is?

      [2018-01-15 Mon 03:37] The correct way to do this is documented in this bug report.

    • Articles   articles
  • Integration with other packages
    • Optional support

      Sometimes you want your package to integrate with other packages, but you don't want to require users to install those other packages. For example, you might want your package to work with Helm, Ivy, or the built-in Emacs completing-read, but you don't want to declare a dependency on and require Helm or Ivy, which would force users to install them to use your package.

      The best way to handle this is with the with-eval-after-load macro. The Emacs manual has a page on it, and this StackOverflow question has some more info. You can also see an example, which also uses declare-function to prevent byte-compiler errors.

  • Lexical binding   lexical_binding built_in

    You should always use lexical binding by setting the header in the first line of the file:

    ;;; filename.el --- File description  -*- lexical-binding: t; -*-
  • Template

    When you make a new package, the auto-insert command will insert a set of standard package headers for you. However, here is a more comprehensive template you can use:

    ;;; package-name.el --- Package description (don't include the word "Emacs")  -*- lexical-binding: t; -*-
    ;; Copyright (C) 2017 First Last
    ;; Author: First Last <>
    ;; URL:
    ;; Version: 0.1-pre
    ;; Package-Requires: ((emacs "25.2"))
    ;; Keywords: something
    ;;; Commentary:
    ;; This is my package.  It is nice.  You should try it.
    ;;;; Installation
    ;;;;; MELPA
    ;; If you installed from MELPA, you're done.
    ;;;;; Manual
    ;; Install these required packages:
    ;; + foo
    ;; + bar
    ;; Then put this file in your load-path, and put this in your init
    ;; file:
    ;; (require 'package-name)
    ;;;; Usage
    ;; Run one of these commands:
    ;; `package-name-command': Frobnicate the flange.
    ;;;; Tips
    ;; + You can customize settings in the `package-name' group.
    ;;;; Credits
    ;; This package would not have been possible without the following
    ;; packages: foo[1], which showed me how to bifurcate, and bar[2],
    ;; which takes care of flanges.
    ;;  [1]
    ;;  [2]
    ;;; License:
    ;; This program is free software; you can redistribute it and/or modify
    ;; it under the terms of the GNU General Public License as published by
    ;; the Free Software Foundation, either version 3 of the License, or
    ;; (at your option) any later version.
    ;; This program is distributed in the hope that it will be useful,
    ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
    ;; GNU General Public License for more details.
    ;; You should have received a copy of the GNU General Public License
    ;; along with this program.  If not, see <>.
    ;;; Code:
    ;;;; Requirements
    (require 'foo)
    (require 'bar)
    ;;;; Customization
    (defgroup package-name nil
      "Settings for `package-name'."
      :link '(url-link ""))
    (defcustom package-name-something nil
      "This setting does something."
      :type 'something)
    ;;;; Variables
    (defvar package-name-var nil
      "A variable.")
    ;;;;; Keymaps
    ;; This technique makes it easier and less verbose to define keymaps.
    (defvar package-name-map
      ;; This makes it easy and much less verbose to define keys
      (let ((map (make-sparse-keymap "package-name map"))
            (maps (list
                   ;; Mappings go here, e.g.:
                   "RET" #'package-name-RET-command
                   [remap search-forward] #'package-name-search-forward
        (cl-loop for (key fn) on maps by #'cddr
                 do (progn
                      (when (stringp key)
                        (setq key (kbd key)))
                      (define-key map key fn)))
    ;;;; Functions
    ;;;;; Commands
    (defun package-name-command (args)
      "Frobnicate the flange."
    ;;;;; Support
    (defun package-name--something (args)
      "This function helps frobnicate the flange."
    ;;;; Footer
    (provide 'package-name)
    ;;; package-name.el ends here
  • Readme

    You should always include a readme with your project. Typically it will be include most of the commentary section. Here's a skeleton that goes with the package skeleton above:

    # Note: This readme works with the org-make-toc <> package, which automatically updates the table of contents.
    * package-name
    :TOC:      ignore
    file: file:
    This is my package.  It is nice.  You should try it.
    ** Screenshots
    This screenshot shows how to frobnicate the fripulator:
    * Contents                                                         :noexport:
    :TOC:      this
      -  Installation
      -  Usage
      -  Changelog
      -  Credits
      -  Development
      -  License
    * Installation
    :TOC:      0
    ** MELPA
    If you installed from MELPA, you're done.  Just run one of the commands below.
    ** Manual
      Install these required packages:
      + foo
      + bar
      Then put this file in your load-path, and put this in your init file:
      #+BEGIN_SRC elisp
    (require 'package-name)
    * Usage
    :TOC:      0
      Run one of these commands:
      + package-name-command: Frobnicate the flange.
    ** Tips
    + You can customize settings in the package-name group.
    * Changelog
    :TOC:      0
    ** 1.1.0
    +  Add command package-name-debarnacle to de-barnacle the hull.
    + Command package-name-anchor now takes an argument, weigh or let-go.
    +  Rewrote input parsing.
    +  Factored out anchor-weighing.
    ** 1.0.1
    +  Ensure anchor is secure before returning from package-name-anchor.
    ** 1.0.0
    Initial release.
    * Credits
      This package would not have been possible without the following packages: foo which showed me how to bifurcate, and bar, which takes care of flanges.
    * Development
    Bug reports, feature requests, suggestions — oh my!
    * License
    # Local Variables:
    # eval: (require 'org-make-toc)
    # before-save-hook: (lambda nil (org-make-toc-make-toc))
    # org-export-with-properties: ()
    # org-export-with-title: t
    # End:

Reference   reference

  • Package headers and structure

    The Emacs manual gives this example (I've added the lexical-binding part). Also see Skeleton.

    ;;; superfrobnicator.el --- Frobnicate and bifurcate flanges  -*- lexical-binding: t; -*-
    ;; Copyright (C) 2011 Free Software Foundation, Inc.
    ;; Author: J. R. Hacker <>
    ;; Version: 1.3
    ;; Package-Requires: ((flange "1.0"))
    ;; Keywords: multimedia, frobnicate
    ;; URL:
    ;;; Commentary:
    ;; This package provides a minor mode to frobnicate and/or
    ;; bifurcate any flanges you desire. To activate it, just type
    (define-minor-mode superfrobnicator-mode

Pattern matching   destructuring pattern_matching

Articles   articles

  • Pattern Matching in Emacs Lisp – Wilfred Hughes::Blog   pcase shadchen cl dash

    Pattern matching is invaluable in elisp. Lists are ubiquitous, and a small amount of pattern matching can often replace a ton of verbose list fiddling.

    Since this is Lisp, we have lots of choices! In this post, we'll compare cl.el, pcase.el, dash.el, and shadchen, so you can choose the best fit for your project. We'll look at the most common use cases, and end with some recommendations.

    For the sake of this post, we'll consider both pattern matching and destructuring, as they're closely related concepts.

  • A callable plist data structure for Emacs   plists macros

    John Kitchin demonstrates some macros that make it easy to access plist values.

Libraries   libraries

  • dash.el   dash

    Dash is a powerful library, and one of its features is powerful destructuring with its -let macro, and several others that work the same way.

  • pcase   built_in pcase

    pcase is built-in to Emacs. Its syntax can be confusing, but it is very powerful.

  • shadchen-el   shadchen el

    A powerful, Racket-style pattern-matching library.

Tools   tools

  • let-alist   alists macros destructuring

    let-alist is the best thing to happen to associative lists since the invention of the cons cell. This little macro lets you easily access the contents of an alist, concisely and efficiently, without having to specify them preemptively. It comes built-in with 25.1, and is also available on GNU Elpa for older Emacsen.


    (defun sx-question-list--print-info (question-data)
      (let-alist question-data
          (int-to-string .score)
          (int-to-string .answer_count)
          .title " "
          .last_activity_date sx-question-list-ago-string
          " " .tags))))
  • with-dict, with-plist-vals   macros plists

    Courtesy of John Kitchin:1

    (defmacro with-dict (key-vals &rest body)
      "A context-manager for a plist where each key is a callable
    function that returns the value."
      (declare (indent 1))
      (let* ((g (if (symbolp key-vals)
                    (symbol-value key-vals)
             (keys (-slice g 0 nil 2)))
        `(labels ,(loop for key in keys
                        (list key '() `(plist-get ',g ,key)))
    ;; Used as:
    (with-dict (:a 1 :b 'some-symbol :c 3)
    (let ((d '(:key1 1 :key2 some-other-symbol :key3 3)))
      (with-dict d
                 (format "We got %s" (:key2))))


    (defmacro with-plist-vals (plist &rest body)
      "Bind the values of a plist to variables with the name of the keys."
      (declare (indent 1))
      `(let ,(loop for key in (-slice plist 0 nil 2)
                   for val in (-slice plist 1 nil 2)
                   collect (list (intern
                                  (substring (symbol-name key) 1))
    ;; Used like:
    (with-plist-vals (:a 4 :b 6)
                     (* 2 a))

Profiling / Optimization   profiling optimization

Articles   articles

Macros   macros

  • bench

    From Phil Lord's m-buffer-el:

    (cl-defmacro bench (&optional (times 100000) &rest body)
      "Call `benchmark-run-compiled' on BODY with TIMES iterations, returning list suitable for Org source block evaluation."
      (declare (indent defun))
      `(list '("Total runtime" "# of GCs" "Total GC runtime")
             (benchmark-run-compiled ,times

    Used like this:

    (bench 1000000
      (cons 'time (current-time)))

    When called from an Org source block, it gives output like this:

    Total runtime # of GCs Total GC runtime
    1.657838266 3 1.4723854609999876
  • elp-profile

    Call this macro from an Org source block and you'll get a results block showing which 20 functions were called the most times, how long they took to run, etc. prefixes should be a list of symbols matching the prefixes of the functions you want to instrument.

    (defmacro elp-profile (times prefixes &rest body)
      (declare (indent defun))
      `(let (output)
         (dolist (prefix ,prefixes)
           (elp-instrument-package (symbol-name prefix)))
         (dotimes (x ,times)
         (forward-line 20)
         (delete-region (point) (point-max))
         (setq output (buffer-substring-no-properties (point-min) (point-max)))
         (let ((rows (s-lines output)))
           (append (list (list "Function" "Times called" "Total time" "Average time")
                   (cl-loop for row in rows
                            collect (s-split (rx (1+ space)) row 'omit-nulls))))))
    ;; Use like this:
    (elp-profile 10 '(map search goto-char car append)
      (goto-char (point-min))
      (search-forward "something"))

    This gives a table like:

    Function Times called Total time Average time
    mapcar 30 0.0036004130 0.0001200137
    search-forward 10 2.089…e-05 2.089…e-06
    goto-char 10 6.926e-06 6.926e-07
    car 13 3.956…e-06 3.043…e-07
    append 1 5.96e-07 5.96e-07
    mapatoms 1 0 0.0

Refactoring   refactoring

Strings   strings

Regular expressions   regular_expressions

Tools   tools

  • format$ macro   macros interpolation

    The format$ macro (currently hosted here) allows for easy string interpolation, including optional % sequences as used by format. For example, this:

    (format$ "Amount: ${amount% .02f} $name %s" date)

    Expands to:

    (format "Amount: % .02f %s %s" amount name date)

    Since this happens at macro expansion time rather than at runtime, there is no performance penalty, in contrast to using s-lex-format.

Testing   testing

Libraries   libraries

Tools   tools

  • buttercup: Behavior-Driven Emacs Lisp Testing
  • ecukes: Cucumber for Emacs
  • Emacs Lisp Regression Testing (ERT)   built_in

    This is the standard, built-in Emacs testing library, used by core code and third-party packages alike.

  • emake.el: Test Elisp without the hoops

    Test Elisp with services like Travis CI without the fuss of Cask – just you, your project, and (Emacs-)Make.

    Things EMake does:

    • parses, installs, and runs tests for your package
    • provides all the power of Elisp to extend its capabilities on-demand

    Things EMake will never do (or ‘reasons you may still need Cask’):

    • manage your development environment or provide tools to do so
    • provide ’bundler-like’ exec abilities (this includes Cask’s emacs and eval commands)

Version control   version_control

Packages   packages

  • Magit   git

    One of the "killer apps" for Emacs–and for git!

XML / HTML   xml html

Libraries   libraries

  • esxml: An elisp library for working with xml, esxml and sxml

    Probably the most featureful, usable library at the moment.

    This library provides to formats for xml code generation. The primary form is esxml. esxml is the form that is returned by such functions as libxml-parse-xml-region and is used internally by emacs in many xml related libraries.

    It also provides esxml-query:

    ;; Traditionally people pick one of the following options when faced
    ;; with the task of extracting data from XML in Emacs Lisp:
    ;; - Using regular expressions on the unparsed document
    ;; - Manual tree traversal with `assoc', `car' and `cdr'
    ;; Browsers faced a similar problem until jQuery happened, shortly
    ;; afterwards they started providing the `node.querySelector' and
    ;; `node.querySelectorAll' API for retrieving one or all nodes
    ;; matching a given CSS selector. This code implements the same API
    ;; with the `esxml-query' and `esxml-query-all' functions. The
    ;; following table summarizes the currently supported modifiers and
    ;; combinators:
    ;; | Name                               | Supported? | Syntax      | 
    ;; |------------------------------------+------------+-------------|
    ;; | Namespaces                         | No         | foo|bar     | 
    ;; | Commas                             | Yes        | foo, bar    | 
    ;; | Descendant combinator              | Yes        | foo bar     | 
    ;; | Child combinator                   | Yes        | foo>bar     | 
    ;; | Adjacent sibling combinator        | No         | foo+bar     | 
    ;; | General sibling combinator         | No         | foo~bar     | 
    ;; | Universal selector                 | Yes        | *           | 
    ;; | Type selector                      | Yes        | tag         | 
    ;; | ID selector                        | Yes        | #foo        | 
    ;; | Class selector                     | Yes        | .foo        | 
    ;; | Attribute selector                 | Yes        | [foo]       | 
    ;; | Exact match attribute selector     | Yes        | [foo=bar]   | 
    ;; | Prefix match attribute selector    | Yes        | [foo^=bar]  | 
    ;; | Suffix match attribute selector    | Yes        | [foo$=bar]  | 
    ;; | Substring match attribute selector | Yes        | [foo*=bar]  | 
    ;; | Include match attribute selector   | Yes        | [foo~=bar]  | 
    ;; | Dash match attribute selector      | Yes        | [foo|=bar]  | 
    ;; | Attribute selector modifiers       | No         | [foo=bar i] | 
    ;; | Pseudo elements                    | No         | ::foo       | 
    ;; | Pseudo classes                     | No         | :foo        |


    (defun org-books--amazon (url)
      "Return plist of data for book at Amazon URL."
      (cl-flet ((field (target-field list)
                       (cl-loop for li in list
                                for (field value) = (ignore-errors
                                                      (-let (((_ _ (_ _ field) value) li))
                                                        (list field value)))
                                when (equal field target-field)
                                return (s-trim value))))
        (let* ((html (org-web-tools--get-url url))
               (tree (with-temp-buffer
                       (insert html)
                       (libxml-parse-html-region (point-min) (point-max))))
               (author (esxml-query " a.contributorNameID *" tree))
               (title (esxml-query "div#booksTitle h1#title > span *" tree))
               (details (esxml-query-all "table#productDetailsTable ul li" tree))
               (date (if-let ((printed (third (esxml-query-all "div#booksTitle h1#title span *" tree))))
                         ;; Printed book
                         (s-replace "– " "" printed)
                       ;; Kindle book
                       (field "Publication Date:" details)))
               (asin (field "ASIN:" details))
               (publisher (-some->> (field "Publisher:" details)
                                    (replace-regexp-in-string (rx " (" (1+ anything) ")") "")))
               (isbn-10 (field "ISBN-10:" details))
               (isbn-13 (field "ISBN-13:" details)))
          (list :author author :title title :publisher publisher :date date
                :asin asin :isbn-10 isbn-10 :isbn-13 isbn-13))))
  • elquery: Read and manipulate HTML

    It’s like jQuery, but way less useful.


    <html style="height: 100vh">
      <head class="kek"><title class="kek" data-bar="foo">Complex HTML Page</title></head>
      <body class="kek bur" style="height: 100%">
        <h1 id="bar" class="kek wow">Wow this is an example</h1>
        <input id="quux" class="kek foo"/>
        <iframe id="baz" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
                width="100%" height="100%" src="">
    (let ((html (elq-read-file "~/kek.html")))
      (elq-el (car (elq-$ ".kek#quux" html))) ; => "input"
      (mapcar 'elq-el (elq-$ ".kek" html)) ; => ("input" "h1" "body" "title" "head")
      (mapcar (lambda (el) (elq-el (elq-parent el)))
              (elq-$ ".kek" html)) ; => ("body" "body" "html" "head" "html")
      (mapcar (lambda (el) (mapcar 'elq-el (elq-siblings el)))
              (elq-$ ".kek" html)) ; => (("h1" "input" "iframe") ("h1" "input" "iframe") ("head" "body") ("title") ("head" "body"))
      (elq-$ ".kek" html) ; => Hope you didn't like your messages buffer
      (elq-write html nil)) ; => "<html style=\"height: 100vh\"> ... </html>"
  • elfeed/xml-query.el

    Provides lisp-based (rather than string-based) selectors. This library is primarily aimed at internal elfeed use rather than general use, however it may be useful to others. The author is considering publishing it separately.

    ;; This query grabs the top-level paragraph content from XHTML.
    ;; (xml-query-all '(html body p *) xhtml)
    ;; This query extracts all the links from an Atom feed.
    ;; (xml-query-all '(feed entry link [rel "alternate"] :href) xml)
  • enlive: query html document with css selectors

    This provides a limited set of lisp-based selectors (rather than string-based selectors).


    (require 'enlive)
     (enlive-query (enlive-fetch "") [title])) ; => "The GNU Operating System and the Free Software Movement"
  • xml-plus: XML/HTML utilities

    Mostly undocumented, providing three main functions:

    ;; Utility functions for xml parse trees.
    ;; - `xml+-query-all' and `xml+-query-first' are query functions that search
    ;; descendants in node lists. They don't work with namespace-aware parsing yet
    ;; - `xml+-node-text' gets node text

Blogs   blogs

Planet Emacsen

This is the main community aggregator. You can find just about everyone's Emacs-related blog posts here.

Sacha Chua's Emacs News

This is Sacha's weekly Emacs news digest. Don't miss it!


One of the top Emacs blogs, frequently updated, and often highlights other interesting blog entries in the community.

People   people

The Emacs community is so full of brilliant, generous people that I can't keep track of them all! I will surely overlook many, and I will add them in no particular order, but merely as I come across them again and again.

Artur Malabarba

Another prolific Emacs contributor, package developer, and blogger.

Damien Cassou


John Wiegley

John is the current Emacs maintainer.


Jonas Bernoulli

Jonas is a prolific Emacs package developer and maintainer. You could spend hours on his GitHub repo.


Matus Goljer


Oleh Krehel

Oleh is a prolific package author, having contributed many very high-quality packages. He also writes at his blog.

Roland Walker

Roland has published a wide variety of useful Emacs packages.

Sacha Chua

Sacha could easily be nominated the official Emacs ambassador, were there to be one. Her contributions to the Emacs and Org-mode communities are innumerable. One of her greatest recent contributions is her weekly Emacs news posts that serve as a digest of everything that happened in the Emacs world over the past week.

Wilfred Hughes

Wilfred has published several useful packages, and he's also leading the Rust Emacs port.


Yes, please! Please send pull requests and file issues on the GitHub repo. This is intended to be a community project.


TODO Articles to add [0/13]

TODO I wished GNU Emacs had… (2 min read)

TODO Reproduce bugs in emacs -Q (4 min read)

TODO Why package.el? (1 min read)

TODO Emacs script pitfalls (13 min read)

TODO Autoloads in Emacs Lisp (5 min read)

TODO Calling Python from Haskell (12 min read)

TODO Add tips for new developers


  • Commonly used minor modes
    • highlight-funcalls
    • highlight-quoted
    • outline-minor-mode

TODO Add Sean Allred

TODO Add Chris Wellons

TODO Vincent Toups' projects

He has a lot of interesting libraries on his repo, and some of them are extensively documented. An aspiring Emacs Lisp developer could learn a lot from his code.

TODO Add more of Roland Walker's packages


Mention @milkypostman, @purcell, @syohex, etc. Mention sandbox.

TODO Add Modern Emacs site

TODO Test in MELPA sandbox

[2017-07-29 Sat 00:33] Not only should you test installing and using your package in the sandbox, but you should also test then exiting the sandbox Emacs, running it again with the package already installed, and loading it. This is because, when the sandbox installs the package, the byte-compilation seems to load some things that won't be loaded the same way when only loading the byte-compiled file (especially if you have any eval-when-compile lines, or unusual macros or things that modify the environment when loaded).


This section contains code used to add to and update this document.

UNDERWAY Automate adding new links and summaries

TODO Get summary of page

DONE Get link for page

This bookmarklet should provide a way to get the URL:


Seems to only work if run in a browser, with JavaScript. But there's a Python package that has a shell command. So:

(require 's)

(defun emacs-package-dev-handbook--archiveis-capture (url)
  "Return archived URL for URL."
    (when (zerop (call-process "archiveis" nil t nil url))
      (s-trim (buffer-string)))))

(cl-defun emacs-package-dev-handbook-insert-archiveis-property (&optional url)
  "Set the \"\" property for entry at point to the archived URL.
 Assumes heading on/before point is an Org link to a web page. If
 URL is given, archive that URL instead."
  (when-let ((url (or url (save-excursion
                            (unless (org-at-heading-p)
                            (when (re-search-forward org-bracket-link-regexp (line-end-position) 'noerror)
                              (org-link-unescape (match-string-no-properties 1))))))
             (archive-url (emacs-package-dev-handbook--archiveis-capture url)))
    (org-set-property "" archive-url)))

TODO Insert new entry at point

Maybe use capture templates and refile?

Table of Contents

Currently using org-make-toc, which is uploaded but unfinished and unpackaged.


I love Emacs and Org mode. This makes it so easy to make the document…alive! And automated! Beautiful.


Copyright by John Kitchin, licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Author: Adam Porter

Created: 2018-07-10 Tue 10:32