An open API service indexing awesome lists of open source software.

https://github.com/alphapapa/emacs-package-dev-handbook

An Emacs package development handbook. Built with Emacs, by Emacs package developers, for Emacs package developers.
https://github.com/alphapapa/emacs-package-dev-handbook

emacs emacs-lisp

Last synced: about 1 year ago
JSON representation

An Emacs package development handbook. Built with Emacs, by Emacs package developers, for Emacs package developers.

Awesome Lists containing this project

README

          

#+TITLE: The Emacs Package Developer's Handbook

#+OPTIONS: broken-links:t num:nil H:8 d:nil
#+TAGS: Emacs
#+SETUPFILE: export/setup/theme-darksun-local.setup

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 [[https://github.com/alphapapa/emacs-package-dev-handbook][repository]] rendered by GitHub (which lacks support for some minor features of the document), or you can read the [[https://alphapapa.github.io/emacs-package-dev-handbook/][HTML version]].

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

* Contents :noexport:
:PROPERTIES:
:TOC: :include siblings :ignore this :depth 1 :force depth
:END:
:CONTENTS:
- [[#emacs-lisp][Emacs Lisp]]
- [[#blogs][Blogs]]
- [[#people][People]]
- [[#contributions][Contributions]]
- [[#tasks][Tasks]]
:END:

* Emacs Lisp :elisp:
:PROPERTIES:
:TOC: :include descendants :depth 1 :force depth
:ID: 50ce456f-4988-4984-9fa3-858089429580
:CUSTOM_ID: emacs-lisp
:END:
:CONTENTS:
- [[#animations--screencasts][Animations / Screencasts]]
- [[#asynchronicity][Asynchronicity]]
- [[#auditing--reviewing][Auditing / Reviewing]]
- [[#binding][Binding]]
- [[#buffers][Buffers]]
- [[#checkers--linters][Checkers / linters]]
- [[#collections-lists-vectors-hash-tables-etc][Collections (lists, vectors, hash-tables, etc.)]]
- [[#color][Color]]
- [[#data-structure][Data structure]]
- [[#date--time][Date / Time]]
- [[#debugging][Debugging]]
- [[#destructuring][Destructuring]]
- [[#documentation][Documentation]]
- [[#editing][Editing]]
- [[#general][General]]
- [[#highlighting--font-locking][Highlighting / font-locking]]
- [[#multiprocessing-generators-threads][Multiprocessing (generators, threads)]]
- [[#networking][Networking]]
- [[#packaging][Packaging]]
- [[#pattern-matching][Pattern matching]]
- [[#processes-incl-ipc-rpc][Processes (incl. IPC, RPC)]]
- [[#optimization][Optimization]]
- [[#refactoring][Refactoring]]
- [[#regular-expressions][Regular expressions]]
- [[#strings][Strings]]
- [[#testing][Testing]]
- [[#user-interface][User interface]]
- [[#version-control][Version control]]
- [[#xml--html][XML / HTML]]
:END:

*Note:* Usable Emacs Lisp code snippets (not examples) are tangled to the file =epdh.el=, which may be found in the [[https://github.com/alphapapa/emacs-package-dev-handbook/blob/master/epdh.el][repository]]. You could even install the file as a package with [[https://framagit.org/steckerhalter/quelpa-use-package][quelpa-use-package]], like this:

#+BEGIN_SRC elisp :exports code
(use-package epdh
:quelpa (epdh :fetcher github :repo "alphapapa/emacs-package-dev-handbook"))
#+END_SRC

# Elisp header

#+BEGIN_SRC elisp :exports none :tangle epdh.el
;;; epdh.el --- Code useful for developing Emacs packages -*- lexical-binding: t; -*-

;; Copyright (C) 2018 Adam Porter

;; Author: Adam Porter
;; Keywords: convenience, development
;; URL: https://github.com/alphapapa/emacs-package-dev-handbook
;; Package-Requires: ((emacs "25.1") (map "2.1") (dash "2.13") (s "1.10.0"))

;;; 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
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; 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 .

;;; Commentary:

;; Code useful for developing Emacs packages. Contributions welcome.

;;; Code:

;;;; Requirements

(require 'cl-lib)

(require 'dash)
(require 's)

#+END_SRC

** Animations / Screencasts :animations:screencasts:video:
:PROPERTIES:
:TOC: :include descendants :depth 2
:CUSTOM_ID: animations--screencasts
:END:
:CONTENTS:
- [[#tools][Tools]]
- [[#demonstration][Demonstration]]
- [[#recording][Recording]]
:END:

*** Tools :tools:
:PROPERTIES:
:CUSTOM_ID: tools
:END:

**** Demonstration :demonstration:
:PROPERTIES:
:CUSTOM_ID: demonstration
:END:

Tools to aid demonstrating use of Emacs.

***** [[https://github.com/tarsius/keycast][keycast: Show current command and its key in the mode line]]

***** [[https://github.com/lewang/command-log-mode][command-log-mode: log commands to buffer]]

**** Recording :recording:
:PROPERTIES:
:CUSTOM_ID: recording
:END:

Tools to record screencasts of Emacs.

***** [[https://gitlab.com/ambrevar/emacs-gif-screencast][emacs-gif-screencast]] :GIFs:

#+BEGIN_QUOTE
Most Emacs screencasts are done by first recording a video, then converting it to a GIF. It seems like a waste of resources considering the little animation Emacs features.

Most of the time, one user action equals one visual change. By exploiting this fact, this package tailors GIF-screencasting for Emacs and captures one frame per user action. It’s much more efficient than capturing frames even at a frequency as low as 10 per second. You could roughly expect 3 to 10 times smaller files compared to videos of the same quality. (Benchmarks are welcome.)

Another neat perk of action-based captures is that it produces editable GIF files: programs such as Gifsicle or The GIMP can be used to edit the delays or rearrange frames. This is particularly useful to fix mistakes in “post-processing”, without having to record the demo all over again.
#+END_QUOTE

The author of this document can vouch for the fact that this package is the easiest, most powerful way to make screencast animations in Emacs!

***** [[https://github.com/alphapapa/bashcaster][Bashcaster]]

While =emacs-gif-screencast= should usually be your first choice, the way it works, recording one frame per Emacs command, isn't suitable for every case. For general use, your editor can recommend Bashcaster, which is an easy-to-use script that can record the whole screen or individual windows, to videos or GIFs.

** Asynchronicity :async:
:PROPERTIES:
:CUSTOM_ID: asynchronicity
:END:

See [[id:94092c16-061c-45c9-9c66-77c5fdeceee8][Multiprocessing (generators, threads)]].

** Auditing / Reviewing :auditing:reviewing:
:PROPERTIES:
:CUSTOM_ID: auditing--reviewing
:END:

Auditing, reviewing, and analyzing source code.

*** Tools :tools:

**** [[https://github.com/cyrus-and/comb][comb: Interactive grep annotation tool for manual static analysis]]

#+BEGIN_QUOTE
Comb is a native Emacs Lisp solution to search, browse and annotate occurrences of regular expressions in files. The interactive interface allows to perform an exhaustive classification of all the results to rule out false positives and asses proper matches during manual static analysis.
#+END_QUOTE

** Binding :scope:binding:
:PROPERTIES:
:ID: 50f0cde2-34ca-4131-a688-b434ecde6819
:TOC: :include descendants :depth 2
:CUSTOM_ID: binding
:END:
:CONTENTS:
- [[#articles][Articles]]
- [[#libraries][Libraries]]
- [[#dashel][dash.el]]
- [[#thunk][thunk]]
- [[#tools-0][Tools]]
- [[#lexical-binding][Lexical binding]]
:END:

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

*** Articles :articles:
:PROPERTIES:
:TOC: :depth 0
:CUSTOM_ID: articles
:END:

**** [[https://nullprogram.com/blog/2017/10/27/][Make Flet Great Again « null program]] :macros:flet:letf:
:PROPERTIES:
:archive.today: http://archive.today/T8dHM
:END:

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:
:PROPERTIES:
:CUSTOM_ID: libraries
:END:

**** [[id:e85e4252-ea03-4473-b52f-9393e7527fad][dash.el]]
:PROPERTIES:
:CUSTOM_ID: dashel
:END:

**** [[https://github.com/emacs-mirror/emacs/blob/master/lisp/emacs-lisp/thunk.el][thunk]] :built_in:
:PROPERTIES:
:ID: 2f389382-af81-4f18-b6bc-ef33e39df1eb
:CUSTOM_ID: thunk
:END:

#+BEGIN_QUOTE
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).
#+END_QUOTE

*** Tools :tools:
:PROPERTIES:
:CUSTOM_ID: tools-0
:END:

**** [[id:7247be4d-4f66-43ff-bb70-1b4a7458611b][Lexical binding]] :built_in:lexical_binding:
:PROPERTIES:
:CUSTOM_ID: lexical-binding
:END:

** Buffers :buffers:
:PROPERTIES:
:TOC: :include descendants
:CUSTOM_ID: buffers
:END:
:CONTENTS:
- [[#articles-0][Articles]]
- [[#buffer-passing-style][Buffer-Passing Style]]
- [[#best-practices][Best practices]]
- [[#accessing-buffer-local-variables][Accessing buffer-local variables]]
- [[#inserting-strings][Inserting strings]]
- [[#libraries-0][Libraries]]
:END:

*** Articles
:PROPERTIES:
:CUSTOM_ID: articles-0
:END:

**** [[*%5B%5Bhttps://nullprogram.com/blog/2014/05/27/%5D%5BEmacs%20Lisp%20Buffer%20Passing%20Style%5D%5D][Buffer-Passing Style]] :strings:
:PROPERTIES:
:CUSTOM_ID: buffer-passing-style
:END:

*** Best practices :best_practices:
:PROPERTIES:
:TOC: :depth 1
:CUSTOM_ID: best-practices
:END:

**** Accessing buffer-local variables
:PROPERTIES:
:CUSTOM_ID: accessing-buffer-local-variables
:END:

It's much faster to use ~buffer-local-value~ than ~with-current-buffer~ to access the value of a variable in a buffer.

#+BEGIN_SRC elisp :exports both :eval no-export
(bench-multi :times 1000 :ensure-equal t
:forms (("buffer-local-value" (--filter (equal 'magit-status-mode (buffer-local-value 'major-mode it))
(buffer-list)))
("with-current-buffer" (--filter (equal 'magit-status-mode (with-current-buffer it
major-mode))
(buffer-list)))))
#+END_SRC

#+RESULTS:
| Form | x faster than next | Total runtime | # of GCs | Total GC runtime |
|---------------------+--------------------+---------------+----------+------------------|
| buffer-local-value | 50.34 | 0.047657734 | 0 | 0.0 |
| with-current-buffer | slowest | 2.399323452 | 0 | 0.0 |

**** Inserting strings :strings:
:PROPERTIES:
:CUSTOM_ID: inserting-strings
:END:

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:

#+BEGIN_SRC elisp :exports both :cache yes
(let ((strings (cl-loop for i from 1 to 1000
collect (number-to-string i))))
(garbage-collect)
(--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
(with-temp-buffer
(cl-loop for string in strings
do (insert string)))))
"(apply #'insert ..."
(cons times
(benchmark-run-compiled times
(with-temp-buffer
(apply #'insert strings))))
"(insert (apply #'concat ..."
(cons times
(benchmark-run-compiled times
(with-temp-buffer
(insert (apply #'concat strings)))))))))
#+END_SRC

#+RESULTS[aa866ca87dbf71476c735ed51fca7373934bbf4f]:
| (insert (apply #'concat ... | 100 | 0.000142085 | 0 | 0.0 |
| (insert (apply #'concat ... | 10 | 0.000161172 | 0 | 0.0 |
| (insert (apply #'concat ... | 1 | 0.00018764 | 0 | 0.0 |
| (apply #'insert ... | 10 | 0.000665472 | 0 | 0.0 |
| (apply #'insert ... | 100 | 0.000678471 | 0 | 0.0 |
| (apply #'insert ... | 1 | 0.000755329 | 0 | 0.0 |
| (loop do (insert ... | 10 | 0.000817031 | 0 | 0.0 |
| (loop do (insert ... | 100 | 0.000869779 | 0 | 0.0 |
| (loop do (insert ... | 1 | 0.001490397 | 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. With 100 iterations, it's about 6x faster than the next-fastest method, and even with 1 iteration, it's over 2x faster.

*** Libraries :libraries:
:PROPERTIES:
:ID: 523aa766-36a3-4827-a114-6babf72edc6b
:TOC: :depth 0
:CUSTOM_ID: libraries-0
:END:

**** [[https://github.com/phillord/m-buffer-el][m-buffer-el: List Oriented Buffer Operations]]
:PROPERTIES:
:ID: 6858c112-9756-43b4-a2e3-fa00a71e9367
:END:

**** [[https://github.com/alezost/bui.el][bui.el: Buffer interface library]]

#+BEGIN_QUOTE
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.
#+END_QUOTE

** Checkers / linters :linters:checkers:
:PROPERTIES:
:CUSTOM_ID: checkers--linters
:END:

*** [[https://github.com/gonewest818/elisp-lint][elisp-lint: basic linting for Emacs Lisp]]

Includes the following validators:

+ byte-compile
+ check-declare
+ checkdoc
+ fill-column
+ indent
+ indent-character
+ package-format
+ trailing-whitespace

*** [[https://github.com/purcell/flycheck-package][flycheck-package: Flycheck checker for Elisp package metadata]]

#+BEGIN_QUOTE
This library provides a =flycheck= checker for the metadata in Emacs Lisp files which are intended to be packages. That metadata includes the package description, its dependencies and more. The checks are performed by the separate =package-lint= library.
#+END_QUOTE

*** [[https://github.com/mattiase/relint][relint: regexp lint tool]]

#+BEGIN_QUOTE
Relint scans elisp files for mistakes in regexps, including deprecated syntax and bad practice. It also checks the regexp-like arguments to =skip-chars-forward=, =skip-chars-backward=, =skip-syntax-forward= and =skip-syntax-backward=.
#+END_QUOTE

** Collections (lists, vectors, hash-tables, etc.) :collections:
:PROPERTIES:
:TOC: :include descendants :depth 1
:CUSTOM_ID: collections-lists-vectors-hash-tables-etc
:END:
:CONTENTS:
- [[#best-practices-0][Best practices]]
- [[#examples][Examples]]
- [[#libraries-1][Libraries]]
- [[#tools-1][Tools]]
:END:

*** Best practices :best_practices:
:PROPERTIES:
:TOC: :include descendants
:CUSTOM_ID: best-practices-0
:END:
:CONTENTS:
- [[#collecting-items-into-a-list][Collecting items into a list]]
- [[#diffing-two-lists][Diffing two lists]]
- [[#filtering-a-list][Filtering a list]]
- [[#looking-up-associations][Looking up associations]]
:END:

**** Collecting items into a list :lists:
:PROPERTIES:
:CUSTOM_ID: collecting-items-into-a-list
:END:

Here are some examples of fast ways to collect items into a list.

#+CAPTION: Benchmarking code that collects items into a list.
#+BEGIN_SRC elisp
(bench-multi-lexical :times 500000 :ensure-equal t
:forms (("cl-loop" (let ((l '(1 2 3 4)))
(cl-loop for val in l
collect val)))
("push-nreverse with setf/pop" (let ((l '(1 2 3 4))
val r)
(while (setf val (pop l))
(push val r))
(nreverse r)))
("push-nreverse with when-let*/pop" (let ((l '(1 2 3 4))
r)
(while (when-let* ((val (pop l)))
(push val r)))
(nreverse r)))
("nconc with when-let*/pop" (let ((l '(1 2 3 4))
r)
(while (when-let* ((val (pop l)))
(setf r (nconc r (list val)))))
r))
("nconc with setf/pop" (let ((l '(1 2 3 4))
val r)
(while (setf val (pop l))
(setf r (nconc r (list val))))
r))))
#+END_SRC

#+RESULTS:
| Form | x faster than next | Total runtime | # of GCs | Total GC runtime |
|----------------------------------+--------------------+---------------+----------+------------------|
| cl-loop | 1.01 | 0.154578 | 0 | 0 |
| push-nreverse with setf/pop | 1.02 | 0.155930 | 0 | 0 |
| push-nreverse with when-let*/pop | 1.23 | 0.159211 | 0 | 0 |
| nconc with setf/pop | 1.06 | 0.195685 | 0 | 0 |
| nconc with when-let*/pop | slowest | 0.207103 | 0 | 0 |

As is usually the case, the =cl-loop= macro expands to the most efficient code, which uses =(setq val (car ...=, =push=, and =nreverse=:

#+CAPTION: Expansion of =cl-loop= form in the benchmark.
#+BEGIN_SRC elisp
(cl-block nil
(let* ((--cl-var-- l)
(val nil)
(--cl-var-- nil))
(while (consp --cl-var--)
(setq val (car --cl-var--))
(push val --cl-var--)
(setq --cl-var-- (cdr --cl-var--)))
(nreverse --cl-var--)))
#+END_SRC

However, in some cases =cl-loop= may expand to code which uses =nconc=, which, as the benchmark shows, is much slower. In that case, you may write the loop without =cl-loop= to avoid using =nconc=.

**** Diffing two lists :lists:
:PROPERTIES:
:CUSTOM_ID: diffing-two-lists
:END:

As expected, =seq-difference= is the slowest, because it's a generic function that dispatches based on the types of its arguments, which is relatively slow in Emacs. And it's not surprising that =cl-nset-difference= is generally slightly faster than =cl-set-difference=, since it's destructive.

However, it is surprising how much faster =-difference= is than =cl-nset-difference=.

It's also nonintuitive that =-difference= suffers a large performance penalty by binding =-compare-fn= (the equivalent of the =:test= argument to =cl-set-difference=): while one might expect that setting it to =string== would give a slight performance increase, it's actually faster to let =-difference= use its default, =equal=.

Note that since this benchmark compares lists of strings, =cl-nset-difference= requires setting the =:test= argument, since it uses =eql= by default, which does not work for comparing strings.

#+BEGIN_SRC elisp :exports both :eval no-export
(defmacro test/set-lists ()
`(setf list1 (cl-loop for i from 0 below 1000
collect (number-to-string i))
list2 (cl-loop for i from 500 below 1500
collect (number-to-string i))))

(let (list1 list2)
(bench-multi-lexical :times 10 :ensure-equal t
:forms (("-difference"
(progn
(test/set-lists)
(-difference list1 list2)))
("-difference string="
(progn
;; This is much slower because of the way `-contains?'
;; works when `-compare-fn' is non-nil.
(test/set-lists)
(let ((-compare-fn #'string=))
(-difference list1 list2))))
("cl-set-difference equal"
(progn
(test/set-lists)
(cl-set-difference list1 list2 :test #'equal)))
("cl-set-difference string="
(progn
(test/set-lists)
(cl-set-difference list1 list2 :test #'string=)))
("cl-nset-difference equal"
(progn
(test/set-lists)
(cl-nset-difference list1 list2 :test #'equal)))
("cl-nset-difference string="
(progn
(test/set-lists)
(cl-nset-difference list1 list2 :test #'string=)))
("seq-difference"
(progn
(test/set-lists)
(seq-difference list1 list2)))
("seq-difference string="
(progn
(test/set-lists)
(seq-difference list1 list2 #'string=))))))
#+END_SRC

#+RESULTS:
| Form | x faster than next | Total runtime | # of GCs | Total GC runtime |
|----------------------------+--------------------+---------------+----------+------------------|
| -difference | 7.16 | 0.084484 | 0 | 0 |
| cl-nset-difference equal | 1.05 | 0.605193 | 0 | 0 |
| cl-set-difference string= | 1.01 | 0.636973 | 0 | 0 |
| cl-set-difference equal | 1.01 | 0.644919 | 0 | 0 |
| cl-nset-difference string= | 1.19 | 0.650708 | 0 | 0 |
| -difference string= | 1.59 | 0.773919 | 0 | 0 |
| seq-difference | 1.05 | 1.232616 | 0 | 0 |
| seq-difference string= | slowest | 1.293030 | 0 | 0 |

**** Filtering a list :lists:
:PROPERTIES:
:CUSTOM_ID: filtering-a-list
:END:

Using ~-select~ from =dash.el= seems to be the fastest way:

#+BEGIN_SRC elisp :exports both :cache yes
(let ((list (cl-loop for i from 1 to 1000
collect i)))
(bench-multi :times 100
:ensure-equal t
:forms (("(-non-nil (--map (when ..." (-non-nil
(--map (when (cl-evenp it) it) list)))
("(delq nil (--map (when ..." (delq nil
(--map (when (cl-evenp it) it) list)))
("cl-loop" (cl-loop for i in list
when (cl-evenp i)
collect i))
("-select" (-select #'cl-evenp list))
("cl-remove-if-not" (cl-remove-if-not #'cl-evenp list))
("seq-filter" (seq-filter #'cl-evenp list)))))
#+END_SRC

#+RESULTS[6b2e97c1ebead84a53fd771684cc3e155e7f6b1e]:
| Form | x faster than next | Total runtime | # of GCs | Total GC runtime |
|----------------------------+--------------------+---------------+----------+------------------|
| -select | 1.17 | 0.01540391 | 0 | 0.0 |
| cl-loop | 1.05 | 0.01808226 | 0 | 0.0 |
| seq-filter | 1.13 | 0.01891708 | 0 | 0.0 |
| (delq nil (--map (when ... | 1.15 | 0.02134727 | 0 | 0.0 |
| cl-remove-if-not | 1.18 | 0.02459478 | 0 | 0.0 |
| (-non-nil (--map (when ... | slowest | 0.02903999 | 0 | 0.0 |

**** Looking up associations
:PROPERTIES:
:CUSTOM_ID: looking-up-associations
:END:

There are a few options in Emacs Lisp for looking up values in associative data structures: association lists (alists), property lists (plists), and hash tables. Which one performs best in a situation may depend on several factors. This benchmark shows what may be a common case: looking up values using a string as the key. We compare several combinations, including the case of prepending a prefix to the string, interning it, and looking up the resulting symbol (which might be done, e.g. when looking up a function to call based on the value of a string).

#+BEGIN_SRC elisp :exports both :cache yes
(bench-multi-lets :times 10000 :ensure-equal t
:lets (("with 26 pairs"
((char-range (cons ?A ?Z))
(strings (cl-loop for char from (car char-range) to (cdr char-range)
collect (concat "prefix-" (char-to-string char))))
(strings-alist (cl-loop for string in strings
collect (cons string string)))
(symbols-alist (cl-loop for string in strings
collect (cons (intern string) string)))
(strings-plist (map-into strings-alist 'plist))
(symbols-plist (map-into symbols-alist 'plist))
(strings-ht (map-into strings-alist '(hash-table :test equal)))
(symbols-ht-equal (map-into symbols-alist '(hash-table :test equal)))
(symbols-ht-eq (map-into symbols-alist '(hash-table :test eq)))))
("with 52 pairs"
((char-range (cons ?A ?z))
(strings (cl-loop for char from (car char-range) to (cdr char-range)
collect (concat "prefix-" (char-to-string char))))
(strings-alist (cl-loop for string in strings
collect (cons string string)))
(symbols-alist (cl-loop for string in strings
collect (cons (intern string) string)))
(strings-plist (map-into strings-alist 'plist))
(symbols-plist (map-into symbols-alist 'plist))
(strings-ht (map-into strings-alist '(hash-table :test equal)))
(symbols-ht-equal (map-into symbols-alist '(hash-table :test equal)))
(symbols-ht-eq (map-into symbols-alist '(hash-table :test eq))))))
:forms (("strings/alist-get/string=" (sort (cl-loop for string in strings
collect (alist-get string strings-alist nil nil #'string=))
#'string<))
("strings/plist" (sort (cl-loop for string in strings
collect (plist-get strings-plist string))
#'string<))
("symbols/concat/intern/plist" (sort (cl-loop for char from (car char-range) to (cdr char-range)
for string = (concat "prefix-" (char-to-string char))
for symbol = (intern string)
collect (plist-get symbols-plist symbol))
#'string<))
("strings/alist-get/equal" (sort (cl-loop for string in strings
collect (alist-get string strings-alist nil nil #'equal))
#'string<))
("strings/hash-table/equal" (sort (cl-loop for string in strings
collect (gethash string strings-ht))
#'string<))
("symbols/concat/intern/hash-table/equal" (sort (cl-loop for char from (car char-range) to (cdr char-range)
for string = (concat "prefix-" (char-to-string char))
for symbol = (intern string)
collect (gethash symbol symbols-ht-equal))
#'string<))
("symbols/concat/intern/hash-table/eq" (sort (cl-loop for char from (car char-range) to (cdr char-range)
for string = (concat "prefix-" (char-to-string char))
for symbol = (intern string)
collect (gethash symbol symbols-ht-eq))
#'string<))
("symbols/concat/intern/alist-get" (sort (cl-loop for char from (car char-range) to (cdr char-range)
for string = (concat "prefix-" (char-to-string char))
for symbol = (intern string)
collect (alist-get symbol symbols-alist))
#'string<))
("symbols/concat/intern/alist-get/equal" (sort (cl-loop for char from (car char-range) to (cdr char-range)
for string = (concat "prefix-" (char-to-string char))
for symbol = (intern string)
collect (alist-get symbol symbols-alist nil nil #'equal))
#'string<))))
#+END_SRC

#+RESULTS[041dd7c6644612027379e3558fcf60e61eb4896a]:
| Form | x faster than next | Total runtime | # of GCs | Total GC runtime |
|-------------------------------------------------------+--------------------+---------------+----------+------------------|
| with 26 pairs: strings/hash-table/equal | 1.06 | 0.040321 | 0 | 0 |
| with 26 pairs: strings/plist | 2.26 | 0.042848 | 0 | 0 |
| with 52 pairs: strings/hash-table/equal | 1.27 | 0.096877 | 0 | 0 |
| with 26 pairs: strings/alist-get/equal | 1.04 | 0.123039 | 0 | 0 |
| with 26 pairs: strings/alist-get/string= | 1.03 | 0.128221 | 0 | 0 |
| with 52 pairs: strings/plist | 2.62 | 0.131451 | 0 | 0 |
| with 26 pairs: symbols/concat/intern/hash-table/eq | 1.00 | 0.344524 | 1 | 0.266744 |
| with 26 pairs: symbols/concat/intern/hash-table/equal | 1.01 | 0.344951 | 1 | 0.267860 |
| with 26 pairs: symbols/concat/intern/plist | 1.02 | 0.349360 | 1 | 0.266529 |
| with 26 pairs: symbols/concat/intern/alist-get | 1.19 | 0.358071 | 1 | 0.267457 |
| with 26 pairs: symbols/concat/intern/alist-get/equal | 1.11 | 0.424895 | 1 | 0.271568 |
| with 52 pairs: strings/alist-get/equal | 1.03 | 0.471979 | 0 | 0 |
| with 52 pairs: strings/alist-get/string= | 1.50 | 0.485663 | 0 | 0 |
| with 52 pairs: symbols/concat/intern/hash-table/equal | 1.00 | 0.730628 | 2 | 0.547082 |
| with 52 pairs: symbols/concat/intern/hash-table/eq | 1.05 | 0.733726 | 2 | 0.548910 |
| with 52 pairs: symbols/concat/intern/alist-get | 1.00 | 0.773320 | 2 | 0.545707 |
| with 52 pairs: symbols/concat/intern/plist | 1.36 | 0.774225 | 2 | 0.549963 |
| with 52 pairs: symbols/concat/intern/alist-get/equal | slowest | 1.056641 | 2 | 0.545522 |

We see that hash-tables are generally the fastest solution.

Comparing alists and plists, we see that, when using string keys, plists are significantly faster than alists, even with 52 pairs. When using symbol keys, plists are faster with 26 pairs; with 52, plists and alists (using ~alist-get~ with ~eq~ as the test function) are nearly the same in performance.

Also, perhaps surprisingly, when looking up a string in an alist, using ~equal~ as the test function may be faster than using the type-specific ~string=~ function (possibly indicating an optimization to be made in Emacs's C code).

***** TODO Compare looking up interned symbols in obarray instead of hash table
:PROPERTIES:
:TOC: :ignore (this)
:END:

***** TODO Compare a larger number of pairs
:PROPERTIES:
:TOC: :ignore (this)
:END:

*** Examples :examples:
:PROPERTIES:
:CUSTOM_ID: examples
:END:

**** Alists :alists:

***** Creation

#+BEGIN_SRC elisp :exports code
;;;; 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))
#+END_SRC

***** Adding to

****** Single elements

#+BEGIN_SRC elisp :exports code
;;;; 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))
#+END_SRC

****** Multiple elements

#+BEGIN_SRC elisp :exports code
;;;; 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))
#+END_SRC

**** Property lists (plists) :plists:

***** Removing properties

[[https://www.reddit.com/r/emacs/comments/aupifm/what_is_the_best_way_to_do_plistremove/eh9xom5/][According to Stefan Monnier]]:

#+BEGIN_QUOTE
The plist design in Elisp was based on the idea that you shouldn't distinguish an entry with a nil value from an entry that's absent. Hence =plist-remove= is not needed because you can do ~(plist-put PLIST PROP nil)~ instead.

Of course, in the mean time, =plist-member= appeared, breaking the design, so this answer is probably not 100% satisfactory, but I still think you'll generally be better off if you can ignore the difference between =nil= and "absent".
#+END_QUOTE

If you do need to remove a key and value from a plist, you could use =cl-remf= or =map-delete= (the former probably being faster).

*** Libraries :libraries:
:PROPERTIES:
:TOC: :depth 0
:CUSTOM_ID: libraries-1
:END:

**** [[https://github.com/plexus/a.el][a.el: functions for dealing with association lists and hash tables. Inspired by Clojure.]] :alists:hash_tables:

**** [[https://github.com/troyp/asoc.el][asoc.el: alist library]] :alists:

**** [[https://github.com/nicferrier/emacs-kv][emacs-kv: key/value collection-type functions, for alists, hash tables and plists]] :alists:hash_tables:plists:

**** [[https://github.com/Wilfred/ht.el][ht.el: The missing hash table library]] :hash_tables:
:PROPERTIES:
:ID: 22b35972-c32f-467a-92ee-f8a155920756
:END:

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.

**** [[https://github.com/rolandwalker/list-utils][list-utils: List-manipulation utility functions]] :lists:
:PROPERTIES:
:ID: 4b4cbe99-f048-4043-a946-97f9d1a4be52
:END:

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= |
| =list-utils-safe-equal= | |

**** [[https://elpa.gnu.org/packages/map.html][map.el: Map manipulation functions]] :maps:hash_tables:alists:arrays:

=map= is included with Emacs, but the latest version, which may include improvements since the last Emacs release, is now available separately on [[https://elpa.gnu.org/][GNU ELPA]].

**** [[https://github.com/NicolasPetton/stream][stream: Lazy sequences]] :streams:sequences:lazy:

#+BEGIN_QUOTE
=stream.el= provides an implementation of streams, implemented as delayed evaluation of cons cells.

Functions defined in seq.el can also take a stream as input.

Streams could be created from any sequential input data:

- sequences, making operation on them lazy
- a set of 2 forms (first and rest), making it easy to represent infinite sequences
- buffers (by character)
- buffers (by line)
- buffers (by page)
- IO streams
- orgmode table cells
- ...
#+END_QUOTE

**** [[https://elpa.gnu.org/packages/persist.html][persist.el: Persist variables between sessions]]

#+BEGIN_QUOTE
This package provides variables which persist across sessions.

The main entry point is `persist-defvar' which behaves like `defvar' but which persists the variables between session. Variables are automatically saved when Emacs exits.

Other useful functions are `persist-save' which saves the variable immediately, `persist-load' which loads the saved value, `persist-reset' which resets to the default value.

Values are stored in a directory in `user-emacs-directory', using one file per value. This makes it easy to delete or remove unused variables.
#+END_QUOTE

*** Tools :tools:
:PROPERTIES:
:TOC: :depth 0
:CUSTOM_ID: tools-1
:END:

**** [[id:f8f0755b-c23b-45ad-98da-780d4044676a][let-alist]]

**** [[id:a58f65dc-d4a4-4a40-a573-a66a28f3619c][=with-dict=, =with-plist-vals=]]

** Color :color:
:PROPERTIES:
:CUSTOM_ID: color
:END:

*** Libraries :libraries:

**** [[https://github.com/yurikhan/yk-color][yk-color: Linear RGB color manipulation]]

Includes these functions:

+ =yk-color-adjust=
+ =yk-color-adjust-rgb=
+ =yk-color-blend=
+ =yk-color-blend-rgb=
+ =yk-color-contrast-ratio=
+ =yk-color-contrast-ratio-rgb=
+ =yk-color-from-rgb=
+ =yk-color-relative-luminance=
+ =yk-color-relative-luminance-rgb=
+ =yk-color-rgb-to-srgb=
+ =yk-color-srgb-to-rgb=
+ =yk-color-to-rgb=

** Data structure :data_structure:
:PROPERTIES:
:TOC: :include descendants :depth 1
:CUSTOM_ID: data-structure
:END:
:CONTENTS:
- [[#articles-1][Articles]]
- [[#libraries-2][Libraries]]
:END:

*** Articles :articles:
:PROPERTIES:
:CUSTOM_ID: articles-1
:END:

**** [[http://nullprogram.com/blog/2018/02/14/][Options for Structured Data in Emacs Lisp « null program]]
:PROPERTIES:
:archive.today: http://archive.today/YxwP5
:END:

**** [[http://www.dr-qubit.org/emacs_data-structures.html][Toby 'qubit' Cubitt: Data structure packages]]

Individual libraries from this article are listed below.

*** Libraries :libraries:
:PROPERTIES:
:TOC: :include descendants
:CUSTOM_ID: libraries-2
:END:
:CONTENTS:
- [[#heapel][heap.el]]
- [[#myers][myers]]
- [[#queueel][queue.el]]
- [[#tagged-non-deterministic-finite-state-automata-tnfael][Tagged Non-deterministic Finite state Automata (tNFA.el)]]
- [[#trieel][trie.el]]
- [[#dict-treeel][dict-tree.el]]
- [[#extmap-externally-stored-constant-mapping][extmap: Externally-stored constant mapping]]
:END:

**** [[http://www.dr-qubit.org/emacs_data-structures.html][heap.el]]
:PROPERTIES:
:CUSTOM_ID: heapel
:END:

#+BEGIN_QUOTE
A heap is a form of efficient self-sorting tree. In particular, the root node is guaranteed to be the highest-ranked entry in the tree. (The comparison function used for ranking the data can, of course, be freely defined). They are often used as priority queues, for scheduling tasks in order of importance, and for implementing efficient sorting algorithms (such as heap-sort).
#+END_QUOTE

**** [[https://elpa.gnu.org/packages/myers.html][myers]] :ELPA:
:PROPERTIES:
:CUSTOM_ID: myers
:END:

#+BEGIN_QUOTE
This package implements Eugene W. Myers's "stacks" which are like standard singly-linked lists, except that they also provide efficient lookup. More specifically:

cons/car/cdr are O(1), while (nthcdr N L) is O(min (N, log L))

For details, see [[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.188.9344&rep=rep1&type=pdf]["An applicative random-access stack", Eugene W. Myers, 1983, Information Processing Letters]].
#+END_QUOTE

**** [[http://www.dr-qubit.org/emacs_data-structures.html][queue.el]] :queue:
:PROPERTIES:
:CUSTOM_ID: queueel
:END:

#+BEGIN_QUOTE
A queue can be used both as a first-in last-out and as a first-in first-out stack, i.e. elements can be added to and removed from the front or back of the queue. (This library is an updated re-implementation of the old Elib queue library.)
#+END_QUOTE

**** [[http://www.dr-qubit.org/emacs_data-structures.html][Tagged Non-deterministic Finite state Automata (tNFA.el)]]
:PROPERTIES:
:CUSTOM_ID: tagged-non-deterministic-finite-state-automata-tnfael
:END:

#+BEGIN_QUOTE
Features of modern regexp implementations, including Emacs', mean they can recognise much more than regular languages. This comes with a big downside: matching certain pathological regexps is very time-consuming. (In fact, it's NP-complete.)

A tagged, non-deterministic finite state automata (NFA) is an abstract computing machine that recognises regular languages. In layman's terms, they are used to decide whether a string matches a regular expression. The "tagged" part lets the NFA do group-capture: it returns information about which parts of a string matched which subgroup of the regular expression.

Why re-implement regular expression matching when Emacs comes with extensive built-in support for regexps? Primarily, because some algorithms require access to the NFA states produced part way through the regular expression matching process. Secondarily, because Emacs regexps only work on strings, whereas regular expressions can equally well be used to match other Lisp sequence types.
#+END_QUOTE

**** [[http://www.dr-qubit.org/emacs_data-structures.html][trie.el]]
:PROPERTIES:
:CUSTOM_ID: trieel
:END:

#+BEGIN_QUOTE
A trie stores data associated with "strings" (not necessarily the string data type; any ordered sequence of elements can be used). It stores them in such a way that both storage size and data lookup are reasonably space- and time- efficient, respectively. But, more importantly, advanced string queries are also very efficient, such as finding all strings with a given prefix, finding approximate matches, finding all strings matching a regular expression, returning results in alphabetical or any other order, returning only the first few results, etc.
#+END_QUOTE

**** [[http://www.dr-qubit.org/emacs_data-structures.html][dict-tree.el]]
:PROPERTIES:
:CUSTOM_ID: dict-treeel
:END:

#+BEGIN_QUOTE
The dictionary tree data structures are a hybrid between tries and hash tables. Data is stored in a trie, but results that take particularly long to retrieve are cached in hash tables, which are automatically synchronised with the trie. The dictionary package provides persistent storage of the data structures in files, and many other convenience features.
#+END_QUOTE

**** [[https://github.com/doublep/extmap][extmap: Externally-stored constant mapping]]
:PROPERTIES:
:CUSTOM_ID: extmap-externally-stored-constant-mapping
:END:

#+BEGIN_QUOTE
extmap is a very simple package that lets you build a read-only, constant database that maps Elisp symbols to almost arbitrary Elisp objects. The idea is to avoid preloading all data to memory and only retrieve it when needed. This package doesn’t use any external programs, making it a suitable dependency for smaller libraries.
#+END_QUOTE

** Date / Time :dates:times:
:PROPERTIES:
:CUSTOM_ID: date--time
:END:

*** Libraries :libraries:

**** [[https://github.com/alphapapa/ts.el][ts.el: Timestamp and date-time library]]

=ts= aids in parsing, formatting, and manipulating timestamps.

#+BEGIN_QUOTE
ts is a date and time library for Emacs. It aims to be more convenient than patterns like ~(string-to-number (format-time-string "%Y"))~ by providing easy accessors, like ~(ts-year (ts-now))~.

To improve performance (significantly), formatted date parts are computed lazily rather than when a timestamp object is instantiated, and the computed parts are then cached for later access without recomputing. Behind the scenes, this avoids unnecessary ~(string-to-number (format-time-string...~ calls, which are surprisingly expensive.
#+END_QUOTE

***** Examples

Get parts of the current date:

#+BEGIN_SRC elisp
;; When the current date is 2018-12-08 23:09:14 -0600:
(ts-year (ts-now)) ;=> 2018
(ts-month (ts-now)) ;=> 12
(ts-day (ts-now)) ;=> 8
(ts-hour (ts-now)) ;=> 23
(ts-minute (ts-now)) ;=> 9
(ts-second (ts-now)) ;=> 14
(ts-tz-offset (ts-now)) ;=> "-0600"

(ts-dow (ts-now)) ;=> 6
(ts-day-abbr (ts-now)) ;=> "Sat"
(ts-day-name (ts-now)) ;=> "Saturday"

(ts-month-abbr (ts-now)) ;=> "Dec"
(ts-month-name (ts-now)) ;=> "December"

(ts-tz-abbr (ts-now)) ;=> "CST"
#+END_SRC

Increment the current date:

#+BEGIN_SRC elisp
;; By 10 years:
(list :now (ts-format)
:future (ts-format (ts-adjust 'year 10 (ts-now))))
;;=> ( :now "2018-12-15 22:00:34 -0600"
;; :future "2028-12-15 22:00:34 -0600")

;; By 10 years, 2 months, 3 days, 5 hours, and 4 seconds:
(list :now (ts-format)
:future (ts-format
(ts-adjust 'year 10 'month 2 'day 3
'hour 5 'second 4
(ts-now))))
;;=> ( :now "2018-12-15 22:02:31 -0600"
;; :future "2029-02-19 03:02:35 -0600")
#+END_SRC

What day of the week was 2 days ago?

#+BEGIN_SRC elisp
(ts-day-name (ts-dec 'day 2 (ts-now))) ;=> "Thursday"

;; Or, with threading macros:
(thread-last (ts-now) (ts-dec 'day 2) ts-day-name) ;=> "Thursday"
(->> (ts-now) (ts-dec 'day 2) ts-day-name) ;=> "Thursday"
#+END_SRC

Get timestamp for this time last week:

#+BEGIN_SRC elisp
(ts-unix (ts-adjust 'day -7 (ts-now)))
;;=> 1543728398.0

;; To confirm that the difference really is 7 days:
(/ (- (ts-unix (ts-now))
(ts-unix (ts-adjust 'day -7 (ts-now))))
86400)
;;=> 7.000000567521762

;; Or human-friendly as a list:
(ts-human-duration
(ts-difference (ts-now)
(ts-dec 'day 7 (ts-now))))
;;=> (:years 0 :days 7 :hours 0 :minutes 0 :seconds 0)

;; Or as a string:
(ts-human-format-duration
(ts-difference (ts-now)
(ts-dec 'day 7 (ts-now))))
;;=> "7 days"

;; Or confirm by formatting:
(list :now (ts-format)
:last-week (ts-format (ts-dec 'day 7 (ts-now))))
;;=> ( :now "2018-12-08 23:31:37 -0600"
;; :last-week "2018-12-01 23:31:37 -0600")
#+END_SRC

**** [[https://github.com/emacs-php/emacs-datetime][emacs-datetime]]
:PROPERTIES:
:ID: 45613ae9-97a2-4807-b099-a6c051f1a2c4
:END:

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

#+BEGIN_SRC elisp :exports code
(datetime-format "%Y-%m-%d") ;=> "2018-08-22"
(datetime-format 'atom) ;=> "2018-08-22T18:23:47-05:00"
(datetime-format 'atom "2112-09-03 00:00:00" :timezone "UTC") ;=> "2112-09-03T00:00:00+00:00"
#+END_SRC

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

** Debugging :debugging:
:PROPERTIES:
:TOC: :depth 2 :include descendants
:CUSTOM_ID: debugging
:END:
:CONTENTS:
- [[#tools-2][Tools]]
- [[#edebug][Edebug]]
- [[#debug-warn-macro][debug-warn macro]]
:END:

*** Tools :tools:
:PROPERTIES:
:CUSTOM_ID: tools-2
:END:

**** Edebug :Edebug:built_in:
:PROPERTIES:
:CUSTOM_ID: edebug
:END:

Edebug is a built-in stepping debugger in Emacs. It's [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Edebug.html][thoroughly documented in the =elisp= manual]].

***** Declaring debug forms for keyword arguments :tip:

Declaring ~debug~ forms for functions and macros that take keyword arguments can be confusing. Here's a contrived example:

#+BEGIN_SRC elisp :exports code
(cl-defmacro make-fn (name docstring &key args bindings body)
(declare (indent defun)
(debug (&define symbolp stringp
&rest [&or [":body" def-form] [keywordp listp]])))
`(defun ,name ,args
,docstring
(let* ,bindings
,body)))

(make-fn my-fn
"This is my function."
:bindings ((one 1)
(two 2))
:body (list one two))
#+END_SRC

****** TODO Submit this as an improvement to the Elisp manual

Probably should first replace the ~:bindings~ part with this, which correctly matches ~let~ bindings: ~(&rest &or symbolp (gate symbolp &optional def-form))~.

**** =debug-warn= macro
:PROPERTIES:
:CUSTOM_ID: debug-warn-macro
:END:

This macro simplifies print-style debugging by automatically including the names of the containing function and argument forms, rather than requiring the programmer to write =format= strings manually. If ~warning-minimum-log-level~ is not ~:debug~ at expansion time, the macro expands to nil, which the byte-compiler eliminates, so in byte-compiled code, the macro has no overhead at runtime when not debugging. In interpreted code, the overhead when not debugging is minimal. When debugging, the expanded form also returns nil so, e.g. it may be used in a conditional in place of nil. The macro is tangled to =epdh.el=, but it may also be added directly to source files.

For example, when used like:

#+BEGIN_SRC elisp :exports code
(eval-and-compile
(setq-local warning-minimum-log-level :debug))

(defun argh (var)
(debug-warn (current-buffer) "This is bad!" (point) var)
var)

(argh 1)
#+END_SRC

This warning would be shown in the =*Warnings*= buffer:

#+BEGIN_EXAMPLE
Debug (argh): (CURRENT-BUFFER):*scratch* This is bad! (POINT):491845 VAR:1
#+END_EXAMPLE

But if ~warning-minimum-log-level~ had any other value, and the buffer were recompiled, there would be no output, and none of the arguments to the macro would be evaluated at runtime.

It may even be used in place of comments! For example, instead of:

#+BEGIN_SRC elisp
(if (foo-p thing)
;; It's a foo: frob it.
(frob thing)
;; Not a foo: flub it.
(flub thing))
#+END_SRC

You could write:

#+BEGIN_SRC elisp
(if (foo-p thing)
(progn
(debug-warn "It's a foo: frob it." thing)
(frob thing))
(debug-warn "Not a foo: flub it." thing)
(flub thing))
#+END_SRC

#+BEGIN_SRC elisp :exports code :results silent :tangle epdh.el
;; To make newer versions of `map' load for the `pcase' pattern.
(require 'map)

(cl-defmacro epdh/debug-warn (&rest args)
"Display a debug warning showing the runtime value of ARGS.
The warning automatically includes the name of the containing
function, and it is only displayed if `warning-minimum-log-level'
is `:debug' at expansion time (otherwise the macro expands to nil
and is eliminated by the byte-compiler). When debugging, the
form also returns nil so, e.g. it may be used in a conditional in
place of nil.

Each of ARGS may be a string, which is displayed as-is, or a
symbol, the value of which is displayed prefixed by its name, or
a Lisp form, which is displayed prefixed by its first symbol.

Before the actual ARGS arguments, you can write keyword
arguments, i.e. alternating keywords and values. The following
keywords are supported:

:buffer BUFFER Name of buffer to pass to `display-warning'.
:level LEVEL Level passed to `display-warning', which see.
Default is :debug."
;; TODO: Can we use a compiler macro to handle this more elegantly?
(pcase-let* ((fn-name (when byte-compile-current-buffer
(with-current-buffer byte-compile-current-buffer
;; This is a hack, but a nifty one.
(save-excursion
(beginning-of-defun)
(cl-second (read (current-buffer)))))))
(plist-args (cl-loop while (keywordp (car args))
collect (pop args)
collect (pop args)))
((map (:buffer buffer) (:level level)) plist-args)
(level (or level :debug))
(string (cl-loop for arg in args
concat (pcase arg
((pred stringp) "%S ")
((pred symbolp)
(concat (upcase (symbol-name arg)) ":%S "))
((pred listp)
(concat "(" (upcase (symbol-name (car arg)))
(pcase (length arg)
(1 ")")
(_ "...)"))
":%S "))))))
(when (eq :debug warning-minimum-log-level)
`(let ((fn-name ,(if fn-name
`',fn-name
;; In an interpreted function: use `backtrace-frame' to get the
;; function name (we have to use a little hackery to figure out
;; how far up the frame to look, but this seems to work).
`(cl-loop for frame in (backtrace-frames)
for fn = (cl-second frame)
when (not (or (subrp fn)
(special-form-p fn)
(eq 'backtrace-frames fn)))
return (make-symbol (format "%s [interpreted]" fn))))))
(display-warning fn-name (format ,string ,@args) ,level ,buffer)
nil))))
#+END_SRC

** Destructuring :destructuring:
:PROPERTIES:
:CUSTOM_ID: destructuring
:END:

See [[id:b699e1a1-e34c-4ce8-a5dd-41161d2a1cbf][Pattern matching]].

** Documentation :documentation:
:PROPERTIES:
:CUSTOM_ID: documentation
:END:

*** Tools :tools:

** Editing :editing:
:PROPERTIES:
:TOC: :include descendants :depth 2
:CUSTOM_ID: editing
:END:
:CONTENTS:
- [[#tools-3][Tools]]
- [[#aggressive-indent-mode-minor-mode-that-keeps-your-code-always-indented][aggressive-indent-mode: minor mode that keeps your code always indented]]
- [[#beginendel][beginend.el]]
- [[#expand-regionel-increase-selected-region-by-semantic-units][expand-region.el: Increase selected region by semantic units]]
- [[#helm-navi-navigate-file-sections-and-language-keywords-using-helm][helm-navi: Navigate file sections and language keywords using Helm]]
- [[#iedit-edit-multiple-regions-simultaneously-in-a-buffer-or-a-region][iedit: Edit multiple regions simultaneously in a buffer or a region]]
- [[#lispy-short-and-sweet-lisp-editing][lispy: short and sweet LISP editing]]
- [[#multi-line-multi-line-everything-from-function-invocations-and-definitions-to-array-and-map-literals-in-a-wide-variety-of-languages][multi-line: multi-line everything from function invocations and definitions to array and map literals in a wide variety of languages]]
- [[#multiple-cursorsel-multiple-cursors][multiple-cursors.el: Multiple cursors]]
- [[#smartparens-minor-mode--that-deals-with-parens-pairs-and-tries-to-be-smart-about-it][smartparens: Minor mode that deals with parens pairs and tries to be smart about it]]
:END:

*** Tools :tools:
:PROPERTIES:
:CUSTOM_ID: tools-3
:END:

**** [[https://github.com/Malabarba/aggressive-indent-mode][aggressive-indent-mode: minor mode that keeps your code always indented]] :formatting:indentation:parentheses:
:PROPERTIES:
:ID: 4dc7c607-a116-4c39-b063-fe34bd20cccf
:CUSTOM_ID: aggressive-indent-mode-minor-mode-that-keeps-your-code-always-indented
:END:

**** [[https://emacs.cafe/emacs/package/2017/08/01/beginend.html][beginend.el]] :navigation:
:PROPERTIES:
:ID: a32ed391-8ce6-46b7-9367-8117829ce2e7
:CUSTOM_ID: beginendel
:END:

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

**** [[https://github.com/magnars/expand-region.el][expand-region.el: Increase selected region by semantic units]] :selection:region:
:PROPERTIES:
:ID: 88b496f4-8230-474e-b2ee-d8e4e8ca30d0
:CUSTOM_ID: expand-regionel-increase-selected-region-by-semantic-units
:END:

**** [[https://github.com/emacs-helm/helm-navi][helm-navi: Navigate file sections and language keywords using Helm]] :navigation:
:PROPERTIES:
:CUSTOM_ID: helm-navi-navigate-file-sections-and-language-keywords-using-helm
:END:

**** [[https://github.com/victorhge/iedit][iedit: Edit multiple regions simultaneously in a buffer or a region]] :refactoring:
:PROPERTIES:
:CUSTOM_ID: iedit-edit-multiple-regions-simultaneously-in-a-buffer-or-a-region
:END:

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

+ ~ap/iedit-or-flyspell~ :: Globally bound to @@html:@@C-;@@html:@@. 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~.

#+BEGIN_SRC elisp :exports code
(defun ap/iedit-or-flyspell ()
"Call `iedit-mode' or correct misspelling with flyspell, depending..."
(interactive)
(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
(progn
(save-excursion
(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)))))
#+END_SRC

+ ~ap/iedit-mode~ :: Calls ~iedit-mode~ with function-local scope by default, or global scope when called with a universal prefix.

#+BEGIN_SRC elisp :exports code
(defun ap/iedit-mode (orig-fn)
"Call `iedit-mode' with function-local scope by default, or global scope if called with a universal prefix."
(interactive)
(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)
#+END_SRC

+ Helpful minibuffer message :: Confirms when an ~iedit~ session has started.

#+BEGIN_SRC elisp :exports code
(advice-add 'iedit-mode :after (lambda (&optional ignore)
(when iedit-mode
(minibuffer-message "iedit session started. Press C-; to end."))))
#+END_SRC

***** TODO Refer to version published in =unpackaged.el=

**** [[https://github.com/abo-abo/lispy][lispy: short and sweet LISP editing]] :elisp:navigation:parentheses:
:PROPERTIES:
:ID: ca3809ba-5900-4dfd-84a1-1ceecc048296
:CUSTOM_ID: lispy-short-and-sweet-lisp-editing
:END:

**** [[https://github.com/IvanMalison/multi-line][multi-line: multi-line everything from function invocations and definitions to array and map literals in a wide variety of languages]] :formatting:
:PROPERTIES:
:CUSTOM_ID: multi-line-multi-line-everything-from-function-invocations-and-definitions-to-array-and-map-literals-in-a-wide-variety-of-languages
:END:

**** [[https://github.com/magnars/multiple-cursors.el][multiple-cursors.el: Multiple cursors]] :selection:editing:
:PROPERTIES:
:ID: deefcbc5-0c37-4936-a820-df99ae31a401
:CUSTOM_ID: multiple-cursorsel-multiple-cursors
:END:

**** [[https://github.com/Fuco1/smartparens][smartparens: Minor mode that deals with parens pairs and tries to be smart about it]] :navigation:editing:parentheses:
:PROPERTIES:
:ID: e806d7f3-43e9-4260-aae4-479efbd41653
:CUSTOM_ID: smartparens-minor-mode--that-deals-with-parens-pairs-and-tries-to-be-smart-about-it
:END:

** General :general:
:PROPERTIES:
:TOC: :include descendants :depth 2
:ID: 26708ac7-9fa3-4f21-9324-ba0e8c939bd7
:CUSTOM_ID: general
:END:
:CONTENTS:
- [[#libraries-3][Libraries]]
- [[#common-lisp-extensions-cl-lib][Common Lisp Extensions (cl-lib)]]
- [[#dashel-0][dash.el]]
- [[#loopel-friendly-imperative-loop-structures][loop.el: friendly imperative loop structures]]
- [[#subr-x][subr-x]]
- [[#tools-4][Tools]]
- [[#chemacs-emacs-profile-switcher][chemacs: Emacs profile switcher]]
- [[#el2markdown-convert-emacs-lisp-comments-to-markdown][el2markdown: Convert Emacs Lisp comments to MarkDown]]
- [[#multicolumn-multiple-side-by-side-windows-support][multicolumn: Multiple side-by-side windows support]]
- [[#lentic-create-views-of-the-same-content-in-two-buffers][lentic: Create views of the same content in two buffers]]
- [[#suggestel-discover-elisp-functions-that-do-what-you-want][suggest.el: discover elisp functions that do what you want]]
- [[#byte-compile-and-load-directory][Byte-compile and load directory]]
- [[#emacs-lisp-macroreplace][emacs-lisp-macroreplace]]
- [[#tutorials][Tutorials]]
- [[#wilfred-hughes-walks-through-a-function-from-lispy][Wilfred Hughes walks through a function from Lispy]]
:END:

#+BEGIN_SRC elisp :exports none :eval never :tangle epdh.el
;;;; General tools

#+END_SRC

*** Libraries :libraries:
:PROPERTIES:
:CUSTOM_ID: libraries-3
:END:

**** [[https://www.gnu.org/software/emacs/manual/html_node/cl/index.html][Common Lisp Extensions]] (=cl-lib=) :built_in:
:PROPERTIES:
:CUSTOM_ID: common-lisp-extensions-cl-lib
:END:

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

**** [[https://github.com/magnars/dash.el][dash.el]] :dash:
:PROPERTIES:
:ID: e85e4252-ea03-4473-b52f-9393e7527fad
:CUSTOM_ID: dashel-0
:END:

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

**** [[https://github.com/Wilfred/loop.el][loop.el: friendly imperative loop structures]] :flow_control:
:PROPERTIES:
:CUSTOM_ID: loopel-friendly-imperative-loop-structures
:END:

#+BEGIN_QUOTE
Emacs Lisp is missing loop structures familiar to users of newer languages. This library adds a selection of popular loop structures as well as break and continue.
#+END_QUOTE

**** [[https://github.com/emacs-mirror/emacs/blob/master/lisp/emacs-lisp/subr-x.el][subr-x]] :built_in:strings:flow_control:
:PROPERTIES:
:CUSTOM_ID: subr-x
:END:

#+BEGIN_QUOTE
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.
#+END_QUOTE

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:

#+BEGIN_QUOTE
Do not document these functions in the lispref. http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01006.html
#+END_QUOTE

*** Tools :tools:
:PROPERTIES:
:CUSTOM_ID: tools-4
:END:

**** [[https://github.com/plexus/chemacs][chemacs: Emacs profile switcher]] :testing:
:PROPERTIES:
:CUSTOM_ID: chemacs-emacs-profile-switcher
:END:

This package may be especially helpful for developing in one's own environment and testing in another, like default Emacs, Spacemacs, etc.

#+BEGIN_QUOTE
Chemacs is an Emacs profile switcher, it makes it easy to run multiple Emacs configurations side by side. Think of it as a bootloader for Emacs.

Emacs configuration is either kept in a =~/.emacs= file or, more commonly, in a =~/.emacs.d= directory. These paths are hard-coded. If you want to try out someone else’s configuration, or run different distributions like Prelude or Spacemacs, then you either need to swap out =~/.emacs.d=, or run Emacs with a different =$HOME= directory set. This last approach is quite common, but has some real drawbacks, since now packages will no longer know where your actual home directory is.

All of these makes trying out different Emacs configurations and distributions needlessly cumbersome. Various approaches to solving this have been floated over the years. There’s an Emacs patch around that adds an extra command line option, and various examples of how to add a command line option in userspace from Emacs Lisp.

Chemacs tries to implement this idea in a user-friendly way, taking care of the various edge cases and use cases that come up.
#+END_QUOTE

**** [[https://github.com/Lindydancer/el2markdown][el2markdown: Convert Emacs Lisp comments to MarkDown]]
:PROPERTIES:
:ID: 9aacf8f3-5244-4b71-8802-1c7876a9d19e
:CUSTOM_ID: el2markdown-convert-emacs-lisp-comments-to-markdown
:END:

**** [[https://github.com/Lindydancer/multicolumn][multicolumn: Multiple side-by-side windows support]]
:PROPERTIES:
:ID: 267c8c25-53db-4ced-9242-176094e101e3
:CUSTOM_ID: multicolumn-multiple-side-by-side-windows-support
:END:

**** [[https://github.com/phillord/lentic][lentic: Create views of the same content in two buffers]]
:PROPERTIES:
:ID: 0c220bc9-7173-4b0f-8955-e10ab6db640f
:CUSTOM_ID: lentic-create-views-of-the-same-content-in-two-buffers
:END:

**** [[https://github.com/Wilfred/suggest.el][suggest.el: discover elisp functions that do what you want]]
:PROPERTIES:
:ID: 57ecc064-7291-4cc5-a545-958e2bca295b
:CUSTOM_ID: suggestel-discover-elisp-functions-that-do-what-you-want
:END:

**** Byte-compile and load directory
:PROPERTIES:
:ID: e9b527d5-ae5e-4052-95f3-781ebc1b4ce1
:CUSTOM_ID: byte-compile-and-load-directory
:END:

Byte-compile and load all elisp files in ~DIRECTORY~. Interactively, directory defaults to ~default-directory~ and asks for confirmation.

#+BEGIN_SRC elisp :exports code :tangle epdh.el
;;;###autoload
(defun epdh/byte-compile-and-load-directory (directory)
"Byte-compile and load all elisp files in DIRECTORY.
Interactively, directory defaults to `default-directory' and asks
for confirmation."
(interactive (list default-directory))
(if (or (not (called-interactively-p))
(yes-or-no-p (format "Compile and load all files in %s?" directory)))
;; Not sure if binding `load-path' is necessary.
(let* ((load-path (cons directory load-path))
(files (directory-files directory 't (rx ".el" eos))))
(dolist (file files)
(byte-compile-file file 'load)))))
#+END_SRC

**** =emacs-lisp-macroreplace=
:PROPERTIES:
:ID: e84a8d0a-f68a-43c5-a499-f27b8ad90457
:CUSTOM_ID: emacs-lisp-macroreplace
:END:

Replace macro form before or after point with its expansion.

#+BEGIN_SRC elisp :exports code :eval no-export :tangle epdh.el
;;;###autoload
(defun epdh/emacs-lisp-macroreplace ()
"Replace macro form before or after point with its expansion."
(interactive)
(if-let* ((beg (point))
(end t)
(form (or (ignore-errors
(save-excursion
(prog1 (read (current-buffer))
(setq end (point)))))
(ignore-errors
(forward-sexp -1)
(setq beg (point))
(prog1 (read (current-buffer))
(setq end (point))))))
(expansion (macroexpand-all form)))
(setf (buffer-substring beg end) (pp-to-string expansion))
(user-error "Unable to expand")))
#+END_SRC

*** Tutorials :tutorials:
:PROPERTIES:
:CUSTOM_ID: tutorials
:END:

**** [[https://old.reddit.com/r/emacs/comments/60tl6o/tips_on_reading_dense_emacs_lisp_code/dfa92hg/][Wilfred Hughes walks through a function from Lispy]] :ATTACH:
:PROPERTIES:
:Attachments: https%3A%2F%2Fold.reddit.com%2Fr%2Femacs%2Fcomments%2F60tl6o%2Ftips_on_reading_dense_emacs_lisp_code%2Fdfa92hg%2F--PA7yQk.tar.xz
:ID: 57f093b3-c2c9-436a-95d4-244ad9f64778
:author: Wilfred Hughes
:CUSTOM_ID: wilfred-hughes-walks-through-a-function-from-lispy
:END:

Wilfred's walkthrough is helpful for learning how to study an Elisp function and determine what it does and how. He also shows the use of ~trace-function~.

** Highlighting / font-locking :highlighting:font_lock:
:PROPERTIES:
:TOC: :include descendants :depth 2
:CUSTOM_ID: highlighting--font-locking
:END:
:CONTENTS:
- [[#packages][Packages]]
- [[#lisp-extra-font-lock-highlight-bound-variables-and-quoted-expressions-in-lisp][lisp-extra-font-lock: Highlight bound variables and quoted expressions in lisp]]
- [[#tools-5][Tools]]
- [[#face-explorer-library-and-tools-for-faces-and-text-properties][face-explorer: Library and tools for faces and text properties]]
- [[#faceup-regression-test-system-for-font-lock-keywords][faceup: Regression test system for font-lock keywords]]
- [[#font-lock-profiler-coverage-and-timing-tool-for-font-lock-keywords][font-lock-profiler: Coverage and timing tool for font-lock keywords]]
- [[#font-lock-regression-suite-regression-test-suite-for-font-lock-keywords-of-emacs-standard-modes][font-lock-regression-suite: Regression test suite for font-lock keywords of Emacs standard modes]]
- [[#font-lock-studio-debugger-for-font-lock-keywords][font-lock-studio: Debugger for Font Lock keywords]]
- [[#highlight-refontification-visualize-how-font-lock-refontifies-a-buffer][highlight-refontification: Visualize how font-lock refontifies a buffer]]
:END:

*** Packages :packages:
:PROPERTIES:
:CUSTOM_ID: packages
:END:

Packages that do highlighting/font-locking.

**** [[https://github.com/Lindydancer/lisp-extra-font-lock][lisp-extra-font-lock: Highlight bound variables and quoted expressions in lisp]]
:PROPERTIES:
:ID: 5b4a9320-1d3d-441b-8505-7b35a8d323d3
:CUSTOM_ID: lisp-extra-font-lock-highlight-bound-variables-and-quoted-expressions-in-lisp
:END:

*** Tools :tools:
:PROPERTIES:
:CUSTOM_ID: tools-5
:END:

Tools for developing highlighting/font-locking packages.

**** [[https://github.com/Lindydancer/face-explorer][face-explorer: Library and tools for faces and text properties]]
:PROPERTIES:
:ID: 66fef80f-0f92-4492-8199-01c24c635914
:CUSTOM_ID: face-explorer-library-and-tools-for-faces-and-text-properties
:END:

**** [[https://github.com/Lindydancer/faceup][faceup: Regression test system for font-lock keywords]]
:PROPERTIES:
:ID: 5ab6c330-15ae-455a-981c-bf298d9a2788
:CUSTOM_ID: faceup-regression-test-system-for-font-lock-keywords
:END:

**** [[https://github.com/Lindydancer/font-lock-profiler][font-lock-profiler: Coverage and timing tool for font-lock keywords]]
:PROPERTIES:
:ID: 0f3ba4be-949b-4efe-8301-acf3873ab345
:CUSTOM_ID: font-lock-profiler-coverage-and-timing-tool-for-font-lock-keywords
:END:

**** [[https://github.com/Lindydancer/font-lock-regression-suite][font-lock-regression-suite: Regression test suite for font-lock keywords of Emacs standard modes]]
:PROPERTIES:
:ID: e54a2697-8b4a-4b65-a971-f79e0fa22120
:CUSTOM_ID: font-lock-regression-suite-regression-test-suite-for-font-lock-keywords-of-emacs-standard-modes
:END:

**** [[https://github.com/Lindydancer/font-lock-studio][font-lock-studio: Debugger for Font Lock keywords]]
:PROPERTIES:
:ID: fb9315d1-111d-4eab-8f99-00710e39b04c
:CUSTOM_ID: font-lock-studio-debugger-for-font-lock-keywords
:END:

**** [[https://github.com/Lindydancer/highlight-refontification][highlight-refontification: Visualize how font-lock refontifies a buffer]]
:PROPERTIES:
:ID: 85a2421d-6fbd-4c4d-a6f9-587258c8f625
:CUSTOM_ID: highlight-refontification-visualize-how-font-lock-refontifies-a-buffer
:END:

** Multiprocessing (generators, threads) :multiprocessing:
:PROPERTIES:
:ID: 94092c16-061c-45c9-9c66-77c5fdeceee8
:TOC: :include descendants :depth 2
:CUSTOM_ID: multiprocessing-generators-threads
:END:
:CONTENTS:
- [[#articles-2][Articles]]
- [[#libraries-4][Libraries]]
- [[#emacs-aio-asyncawait-for-emacs-lisp][emacs-aio: async/await for Emacs Lisp]]
- [[#manual][Manual]]
:END:

*** Articles :articles:
:PROPERTIES:
:TOC: :depth 0
:CUSTOM_ID: articles-2
:END:

**** [[https://nullprogram.com/blog/2018/05/31/][Emacs 26 Brings Generators and Threads « null program]] :threads:generators:compilation:closures:iterators:
:PROPERTIES:
:archive.today: http://archive.today/Irane
:END:

Chris Wellons explains the new generators and threads that Emacs 26 provides. He also shows an example of writing a ~cl-case~ form that uses the new ~switch~ jump table opcode in Emacs 26.

**** [[https://nullprogram.com/blog/2013/01/14/][Turning Asynchronous into Synchronous in Elisp « null program]] :async:
:PROPERTIES:
:archive.today: http://archive.today/AfL0y
:END:

**** [[https://nullprogram.com/blog/2017/02/14/][Asynchronous Requests from Emacs Dynamic Modules « null program]] :modules:async:
:PROPERTIES:
:archive.today: http://archive.today/ZS6pU
:END:

*** Libraries :libraries:
:PROPERTIES:
:CUSTOM_ID: libraries-4
:END:

**** [[https://github.com/skeeto/emacs-aio][emacs-aio: async/await for Emacs Lisp]]
:PROPERTIES:
:CUSTOM_ID: emacs-aio-asyncawait-for-emacs-lisp
:END:

#+BEGIN_QUOTE
=aio= is to Emacs Lisp as asyncio is to Python. This package builds upon Emacs 25 generators to provide functions that pause while they wait on asynchronous events. They do not block any thread while paused.
#+END_QUOTE

*** Manual :manual:
:PROPERTIES:
:TOC: :depth 0
:CUSTOM_ID: manual
:END:

**** [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Generators.html][GNU Emacs Lisp Reference Manual: Generators]] :generators:

**** [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Threads.html][GNU Emacs Lisp Reference Manual: Threads]] :threads:

** Networking :networking:
:PROPERTIES:
:CUSTOM_ID: networking
:END:

*** HTTP :HTTP:

**** Libraries :libraries:
:PROPERTIES:
:TOC: :include descendants :depth 1
:END:

For very simple use cases, the built-in [[*=url=][url]] library should be sufficient. But for more complex cases, and for a simpler API and more reliability, it's generally recommended to use [[*\[\[https://github.com/alphapapa/plz.el\]\[plz.el: HTTP client library\]\]][plz]].

Other libraries, such as [[*%5B%5Bhttps://github.com/tkf/emacs-request%5D%5BRequest.el%20--%20Easy%20HTTP%20requests%5D%5D][request]], can also provide improvements over ~url~. However, in this author's experience, both ~url~ and ~request~, while mostly reliable, tend to have some obscure bugs that can occasionally be problematic. ~plz~, while not yet as featureful as ~request~, is intended to be developed into the best all-around client, and is now a mature, reliable library.

:CONTENTS:
- [[#plzel-http-client-library][plz.el: HTTP client library]]
- [[#url][url]]
- [[#requestel----easy-http-requests][Request.el -- Easy HTTP requests]]
- [[#elfeed-curl][elfeed-curl]]
:END:

***** [[https://github.com/alphapapa/plz.el][plz.el: HTTP client library]] :curl:GNU_ELPA:
:PROPERTIES:
:CUSTOM_ID: plzel-http-client-library
:END:

Inspired by [[*\[\[https://github.com/skeeto/elfeed/blob/master/elfeed-curl.el\]\[elfeed-curl\]\]][elfeed-curl]] and endorsed by its author, ~plz~ supports both synchronous and asynchronous requests. Its API is intended to be simple, natural, and expressive. Its code is intended to be simple and well-organized. Every feature is tested against [[https://httpbin.org/][httpbin]]. Written by this author, ~plz~ is available on GNU ELPA, so it's installable in Emacs by default.

***** =url= :url_el:built_in:
:PROPERTIES:
:CUSTOM_ID: url
:END:

=url= is included with Emacs and used by a variety of packages.

***** [[https://github.com/tkf/emacs-request][Request.el -- Easy HTTP requests]] :curl:url_el:NonGNU_ELPA:MELPA:
:PROPERTIES:
:CUSTOM_ID: requestel----easy-http-requests
:END:

=request= is the most commonly used third-party HTTP library. It has both =curl= and =url.el= backends.

***** [[https://github.com/skeeto/elfeed/blob/master/elfeed-curl.el][elfeed-curl]] :curl:
:PROPERTIES:
:CUSTOM_ID: elfeed-curl
:END:

Not a standalone package, but part of [[https://github.com/skeeto/elfeed/blob/master/elfeed-curl.el][Elfeed]]. A solid, well-designed library, but purpose-built for Elfeed. Inspired the development of [[*\[\[https://github.com/alphapapa/plz.el\]\[plz.el: HTTP client library\]\]][plz]].

** Packaging :packaging:
:PROPERTIES:
:TOC: :include descendants :depth 2 :force depth
:CUSTOM_ID: packaging
:END:
:CONTENTS:
- [[#articles-3][Articles]]
- [[#good-style-in-modern-emacs-packages][Good Style in modern Emacs Packages]]
- [[#best-practices-1][Best practices]]
- [[#autoloads][Autoloads]]
- [[#integration-with-other-packages][Integration with other packages]]
- [[#lexical-binding-0][Lexical binding]]
- [[#template][Template]]
- [[#readme][Readme]]
- [[#version-numbers][Version numbers]]
- [[#libraries-5][Libraries]]
- [[#reference][Reference]]
- [[#package-headers-and-structure][Package headers and structure]]
- [[#tools-6][Tools]]
- [[#building--testing][Building / Testing]]
- [[#package-installationmanagement][Package installation/management]]
:END:

*** Articles :articles:
:PROPERTIES:
:CUSTOM_ID: articles-3
:END:

**** [[https://amodernist.com/texts/emacs-style.html][Good Style in modern Emacs Packages]]
:PROPERTIES:
:author: Philip Kaludercic
:CUSTOM_ID: good-style-in-modern-emacs-packages
:END:

*** Best practices :best_practices:
:PROPERTIES:
:CUSTOM_ID: best-practices-1
:END:

**** Autoloads :autoloads:
:PROPERTIES:
:CUSTOM_ID: autoloads
:END:

***** TODO Autoloading macro-generated functions

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

[[https://www.reddit.com/r/emacs/comments/63u5yn/how_to_use_autoload_cookies_for_custom_defunlike/][How to use autoload cookies for custom defun-like macros? : emacs]]:

#+BEGIN_QUOTE
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,

#+BEGIN_EXAMPLE
;;;###autoload (deffoo bar ...)
#+END_EXAMPLE

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

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

should be used instead. What confuses me is [[http://stackoverflow.com/a/38805102][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?
#+END_QUOTE

[2018-01-15 Mon 03:37] The correct way to do this is documented in [[https://github.com/alphapapa/helm-org-rifle/issues/13][this bug report]].

***** Articles :articles:

****** [[https://www.lunaryorn.com/posts/autoloads-in-emacs-lisp][Autoloads in Emacs Lisp | Sebastian Wiesner]]
:PROPERTIES:
:archive.today: http://archive.today/UZHhS
:END:

**** Integration with other packages
:PROPERTIES:
:CUSTOM_ID: integration-with-other-packages
:END:

***** 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 [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Hooks-for-Loading.html][=with-eval-after-load=]] macro. The [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Hooks-for-Loading.html][Emacs manual]] has a page on it, and [[https://stackoverflow.com/questions/21880139/what-is-with-eval-after-load-in-emacs-lisp][this StackOverflow question]] has some more info. You can also see an [[https://github.com/alphapapa/org-recent-headings/blob/master/org-recent-headings.el#L350][example]], which also [[https://github.com/alphapapa/org-recent-headings/blob/master/org-recent-headings.el#L377][uses]] [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Declaring-Functions.html][=declare-function=]] to prevent byte-compiler warnings. Note as well that, according to [[https://stackoverflow.com/questions/21075092/elisp-how-to-avoid-functions-are-not-known-to-be-defined-when-byte-compiling#comment31703432_21075724][this StackOverflow comment]], when a function call is guarded by =fboundp=, it's not necessary to use =declare-function= to avoid a warning.

**** [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html][Lexical binding]] :lexical_binding:built_in:
:PROPERTIES:
:ID: 7247be4d-4f66-43ff-bb70-1b4a7458611b
:CUSTOM_ID: lexical-binding-0
:END:

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

#+BEGIN_EXAMPLE
;;; filename.el --- File description -*- lexical-binding: t; -*-
#+END_EXAMPLE

***** Articles :articles:

****** [[https://yoo2080.wordpress.com/2013/09/11/emacs-lisp-lexical-binding-gotchas-and-related-best-practices/][Emacs Lisp lexical binding gotchas and related best practices | Yoo Box]]
:PROPERTIES:
:archive.today: http://archive.today/0nfB4
:END:

****** [[https://emacs.stackexchange.com/questions/2129/why-is-let-faster-with-lexical-scope][elisp - Why is `let' faster with lexical scope? - Emacs Stack Exchange]]
:PROPERTIES:
:archive.today: http://archive.today/LUtfZ
:END:

Sebastian Wiesner provides a detailed explanation.

****** [[https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding][EmacsWiki: Dynamic Binding Vs Lexical Binding]]
:PROPERTIES:
:archive.today: http://archive.today/2VtOU
:END:

A lot of good examples and discussion.

****** [[id:3659d32e-3e9e-466f-94ff-33cfed5dc49d][Some Performance Advantages of Lexical Scope « null program]]

**** Template
:PROPERTIES:
:ID: aacd55b6-a56f-4064-bf0d-25173ce83ef3
:CUSTOM_ID: template
:END:

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:

#+BEGIN_SRC elisp :exports code
;;; package-name.el --- Package description (don't include the word "Emacs") -*- lexical-binding: t; -*-

;; Copyright (C) 2017 First Last

;; Author: First Last
;; URL: https://example.com/package-name.el
;; Version: 0.1-pre
;; Package-Requires: ((emacs "25.2"))
;; Keywords: something

;; This file is not part of GNU Emacs.

;; 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
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; 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 .

;;; Commentary:

;; This package allows flanges to be easily frobnicated.

;;;; 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] https://example.com/foo.el
;; [2] https://example.com/bar.el

;;; Code:

;;;; Requirements

(require 'foo)
(require 'bar)

;;;; Customization

(defgroup package-name nil
"Settings for `package-name'."
:link '(url-link "https://example.com/package-name.el"))

(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
;; that have many bindings.

(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)))
map))

;;;; Commands

;;;###autoload
(defun package-name-command (args)
"Frobnicate the flange."
(interactive)
(package-name-foo
(package-name--bar args)))

;;;; Functions

;;;;; Public

(defun package-name-foo (args)
"Return foo for ARGS."
(foo args))

;;;;; Private

(defun package-name--bar (args)
"Return bar for ARGS."
(bar args))

;;;; Footer

(provide 'package-name)

;;; package-name.el ends here
#+END_SRC

**** Readme
:PROPERTIES:
:CUSTOM_ID: readme
:END:

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

#+BEGIN_SRC org
,#+TITLE: package-name

,#+PROPERTY: LOGGING nil

# Note: This readme works with the org-make-toc package, which automatically updates the table of contents.

[[https://melpa.org/#/package-name][file:https://melpa.org/packages/package-name-badge.svg]] [[https://stable.melpa.org/#/package-name][file:https://stable.melpa.org/packages/package-name-badge.svg]]

This is my package. It is nice. You should try it.

,* Screenshots

This screenshot shows how to frobnicate the fripulator:

[[screenshot1.png]]

,* Contents :noexport:
:PROPERTIES:
:TOC: :include siblings
:END:
:CONTENTS:

:END:

,* Installation
:PROPERTIES:
:TOC: :depth 0
:END:

,** 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)
,#+END_SRC

,* Usage
:PROPERTIES:
:TOC: :depth 0
:END:

Run one of these commands:

+ =package-name-command=: Frobnicate the flange.

,** Tips

+ You can customize settings in the =package-name= group.

,* Changelog
:PROPERTIES:
:TOC: :depth 0
:END:

,** 1.1.0

,*Additions*
+ Add command =package-name-debarnacle= to de-barnacle the hull.

,*Changes*
+ Command =package-name-anchor= now takes an argument, =weigh= or =let-go=.

,*Internal*
+ Rewrote input parsing.
+ Factored out anchor-weighing.

,** 1.0.1

,*Fixes*
+ 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: [[https://example.com/foo.el][foo]] which showed me how to bifurcate, and [[https://example.com/bar.el][bar]], which takes care of flanges.

,* Development

Bug reports, feature requests, suggestions — /oh my/!

,* License

GPLv3

# Local Variables:
# eval: (require 'org-make-toc)
# before-save-hook: org-make-toc
# org-export-with-properties: ()
# org-export-with-title: t
# End:

#+END_SRC

**** Version numbers
:PROPERTIES:
:CUSTOM_ID: version-numbers
:END:

Version numbers which are valid in Emacs are those accepted by the function ~version-to-list~, which uses the variables ~version-separator~ and ~version-regexp-alist~. See their documentation for specific, up-to-date information. ~version-to-list~'s documentation (as of Emacs 26.1) is reproduced here for convenience:

#+BEGIN_EXAMPLE
The version syntax is given by the following EBNF:

VERSION ::= NUMBER ( SEPARATOR NUMBER )*.

NUMBER ::= (0|1|2|3|4|5|6|7|8|9)+.

SEPARATOR ::= ‘version-separator’ (which see)
| ‘version-regexp-alist’ (which see).

The NUMBER part is optional if SEPARATOR is a match for an element
in ‘version-regexp-alist’.

Examples of valid version syntax:

1.0pre2 1.0.7.5 22.8beta3 0.9alpha1 6.9.30Beta 2.4.snapshot .5

Examples of invalid version syntax:

1.0prepre2 1.0..7.5 22.8X3 alpha3.2

Examples of version conversion:

Version String Version as a List of Integers
".5" (0 5)
"0.9 alpha" (0 9 -3)
"0.9AlphA1" (0 9 -3 1)
"0.9snapshot" (0 9 -4)
"1.0-git" (1 0 -4)
"1.0.7.5" (1 0 7 5)
"1.0.cvs" (1 0 -4)
"1.0PRE2" (1 0 -1 2)
"1.0pre2" (1 0 -1 2)
"22.8 Beta3" (22 8 -2 3)
"22.8beta3" (22 8 -2 3)
#+END_EXAMPLE

*** Libraries :libraries:
:PROPERTIES:
:TOC: :depth 0
:CUSTOM_ID: libraries-5
:END:

**** =lisp-mnt.el= (=lm=) :built_in:
:PROPERTIES:
:TOC: :ignore this
:END:

This library includes functions helpful for working with and verifying the format of Emacs Lisp package files, including headers, commentary, etc. It's easy to overlook and hard to re-discover this package because of its =lm= symbol prefix. It's listed here because your editor keeps forgetting what it's called.

*** Reference :reference:
:PROPERTIES:
:CUSTOM_ID: reference
:END:

**** Package headers and structure
:PROPERTIES:
:ID: 036e2a71-f392-4c9a-a524-c5354f291e2f
:CUSTOM_ID: package-headers-and-structure
:END:

The [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Simple-Packages.html][Emacs manual]] gives this example (I've added the lexical-binding part). Also see [[id:aacd55b6-a56f-4064-bf0d-25173ce83ef3][template]].

#+BEGIN_EXAMPLE
;;; superfrobnicator.el --- Frobnicate and bifurcate fl