A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://alphapapa.github.io/emacs-package-dev-handbook/ below:

The Emacs Package Developer's Handbook

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 rendered by GitHub (which lacks support for some minor features of the document), 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

Note: Usable Emacs Lisp code snippets (not examples) are tangled to the file epdh.el, which may be found in the repository. You could even install the file as a package with quelpa-use-package, like this:

(use-package epdh
  :quelpa (epdh :fetcher github :repo "alphapapa/emacs-package-dev-handbook"))
Animations / Screencasts   animations screencasts video Auditing / Reviewing   auditing reviewing

Auditing, reviewing, and analyzing source code.

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

Buffers   buffers Best practices   best_practices Accessing buffer-local variables

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

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

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))))
  (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)))))))))
(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 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 Collections (lists, vectors, hash-tables, etc.)   collections Best practices   best_practices Collecting items into a list   lists

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

Listing 1: Benchmarking code that collects items into a list.
(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))))

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

Listing 2: Expansion of cl-loop form in the benchmark.
(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--)))

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

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.

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

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

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

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

(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<))))
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 TODO Compare a larger number of pairs 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))
Property lists (plists)   plists Removing properties

According to Stefan Monnier:

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

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 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 list-utils-safe-equal   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 GNU ELPA.

stream: Lazy sequences   streams sequences lazy

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:

persist.el: Persist variables between sessions

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.

Color   color Libraries   libraries yk-color: Linear RGB color manipulation

Includes these functions:

Data structure   data_structure Libraries   libraries heap.el

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

queue.el   queue

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

Tagged Non-deterministic Finite state Automata (tNFA.el)

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.

trie.el

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.

dict-tree.el

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.

extmap: Externally-stored constant mapping

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.

Date / Time   dates times Libraries   libraries ts.el: Timestamp and date-time library

ts aids in parsing, formatting, and manipulating timestamps.

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.

Examples

Get parts of the current date:

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

Increment the current date:

;; 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")

What day of the week was 2 days ago?

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

Get timestamp for this time last week:

(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")
emacs-datetime

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

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

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

Documentation   documentation

Tools   tools

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.

loop.el: friendly imperative loop structures   flow_control

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.

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. http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01006.html

Highlighting / font-locking   highlighting font_lock Packages   packages

Packages that do highlighting/font-locking.

Multiprocessing (generators, threads)   multiprocessing Articles   articles Emacs 26 Brings Generators and Threads « null program   threads generators compilation closures iterators

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.

Libraries   libraries emacs-aio: async/await for Emacs Lisp

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.

Networking   networking HTTP   HTTP Libraries   libraries

For very simple use cases, the built-in url library should be sufficient. But for more complex cases, and for a simpler API and more reliability, it's generally recommended to use plz.

Other libraries, such as , 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.

plz.el: HTTP client library   curl GNU_ELPA

Inspired by 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 httpbin. Written by this author, plz is available on GNU ELPA, so it's installable in Emacs by default.

url   url_el built_in

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

Request.el – Easy HTTP requests   curl url_el NonGNU_ELPA MELPA

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

elfeed-curl   curl

Not a standalone package, but part of Elfeed. A solid, well-designed library, but purpose-built for Elfeed. Inspired the development of plz.

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.

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 warnings. Note as well that, according to this StackOverflow comment, when a function call is guarded by fboundp, it's not necessary to use declare-function to avoid a warning.

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 <name@example.com>
;; 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 <http://www.gnu.org/licenses/>.

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

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:

TITLE: package-name

PROPERTY: LOGGING nil

# Note: This readme works with the org-make-toc <https://github.com/alphapapa/org-make-toc> package, which automatically updates the table of contents.

file:https://melpa.org/packages/package-name-badge.svg 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:

  ★ foobar

  Then put this file in your load-path, and put this in your init file:

SRC elisp
(require 'package-name)
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: foo which showed me how to bifurcate, and 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:

Version numbers

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:

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)
Libraries   libraries lisp-mnt.el (lm)   built_in

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 Package headers and structure

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

;;; superfrobnicator.el --- Frobnicate and bifurcate flanges  -*- lexical-binding: t; -*-

;; Copyright (C) 2011 Free Software Foundation, Inc.

;; Author: J. R. Hacker <jrh@example.com>
;; Version: 1.3
;; Package-Requires: ((flange "1.0"))
;; Keywords: multimedia, frobnicate
;; URL: http://example.com/jrhacker/superfrobnicate

...

;;; Commentary:

;; This package provides a minor mode to frobnicate and/or
;; bifurcate any flanges you desire. To activate it, just type
...

;;;###autoload
(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.

Examples   examples TODO dash

[2018-07-27 Fri 23:29] Dash has new abilities, including -setq, and destructuring plists with implied variable names (i.e. just the keys can be specified, reducing repetition).

pcase-let    destructuring

This example shows the use of pcase-let* to destructure and bind a nested alist:

(let ((alphabets (a-list 'English (a-list 'first "a"
                                          'second "b")
                         'Greek (a-list 'first "α"
                                        'second "β"))))
  (pcase-let* (((map English) alphabets)
               ((map ('first letter) second) English))
    (list letter second)))  ;; => ("a" "b")
shadchen-el   shadchen el

A powerful, Racket-style pattern-matching library.

Processes (incl. IPC, RPC)   processes

Including inter-process communication (IPC) and remote procedure calls (RPC).

Libraries   libraries debase: D-Bus<->EIEIO bridge   IPC RPC Linux

D-Bus is an IPC system which is ubiquitous on Linux, and (in this author’s opinion) not very good. Emacs has bindings for interfacing with it (see the former point), which are annoying to use (see the latter point).

These days, numerous common system management tasks are implemented as D-Bus services rather than tradidional *nix commands, and many of the command-line tools themselves are now front-ends which communicate via D-Bus. Mounting and unmounting disks, monitoring battery status, controlling display brightness, connecting to wireless networks and more are now handled with D-Bus services.

It makes no sense to shell out to the tools when one could interact with them directly via D-Bus, if only it was less annoying to do so.

Debase frees you from writing repetitive, annoying boilerplate code to drive D-Bus services by throwing another pile of abstraction at the problem, in the form of unreadably dense, macro-heavy, profoundly cursed Lisp.

Optimization   optimization

Including benchmarking, byte-compilation, profiling, etc.

Articles   articles Emacs Byte-code Internals   compilation bytecode

Byte-code compilation is an underdocumented — and in the case of the recent lexical binding updates, undocumented — part of Emacs. Most users know that Elisp is usually compiled into a byte-code saved to .elc files, and that byte-code loads and runs faster than uncompiled Elisp. That’s all users really need to know, and the GNU Emacs Lisp Reference Manual specifically discourages poking around too much.

People do not write byte-code; that job is left to the byte compiler. But we provide a disassembler to satisfy a cat-like curiosity.

Screw that! What if I want to handcraft some byte-code myself? :-) The purpose of this article is to introduce the internals of Elisp byte-code interpreter. I will explain how it works, why lexically scoped code is faster, and demonstrate writing some byte-code by hand.

Faster Elfeed Search Through JIT Byte-code Compilation   benchmarking compilation bytecode

Chris Wellons shows how he substantially improved Elfeed's search by byte-compiling functions at runtime and avoiding function call overhead. He also demonstrates some simple benchmarking tools he wrote in the process.

Some Performance Advantages of Lexical Scope « null program   lexical_binding compilation bytecode

I recently had a discussion with Xah Lee about lexical scope in Emacs Lisp. The topic was why lexical-binding exists at a file-level when there was already lexical-let (from cl-lib), prompted by my previous article on JIT byte-code compilation. The specific context is Emacs Lisp, but these concepts apply to language design in general.

Regular expressions   regular_expressions Libraries   libraries lex

lex is a regular expression matching engine with syntax similar to rx. It appears to be more implemented in elisp than standard Emacs regexp tools, so it may be slower, but its additional capabilities may be useful.

Format of regexps is the same as used for `rx' and `sregex'. Additions:

Strings   strings Articles   articles Buffer-Passing Style   buffers

Chris Wellons explains how to build strings in several steps. This is achieved thanks to the creation of a temporary buffer that is passed to helper methods using the "current" buffer mechanism. A nice and simple design pattern for Emacs.

Testing   testing Articles   articles Continuous Integration of Emacs Packages with CircleCI

I was very inspired by Damien Cassou's great presentation during EmacsConf 2019 to write this post and I encourage you to check it out if you haven't already. In short, when writing packages for Emacs, it is best practice to run several quality tools on them, like syntax and documentation checkers, or even ERT Tests. But once these packages are public and pull requests start coming in, it is a huge time saver to have these same tools ran automatically and provide feedback to contributors. That's right, we're talking about Continuous Integration for Emacs packages.

Frameworks   frameworks

Frameworks for writing, organizing, and running tests.

buttercup: Behavior-Driven Emacs Lisp Testing

Buttercup is a behavior-driven development framework for testing Emacs Lisp code. It allows to group related tests so they can share common set-up and tear-down code, and allows the programmer to “spy” on functions to ensure they are called with the right arguments during testing.

The framework is heavily inspired by Jasmine.

ecukes: Cucumber for Emacs

There are plenty of unit/regression testing tools for Emacs, and even some for functional testing. What Emacs is missing though is a really good testing framework for integration testing. This is where Ecukes comes in.

Cucumber is a great integration testing tool, used mostly for testing web applications. Ecukes is Cucumber for Emacs. No, it's not a major mode to edit feature files. It is a package that makes it possible to write Cucumber like tests for your Emacs packages.

Emacs Lisp Regression Testing (ERT)   built_in ERT

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

Libraries   libraries

Libraries that help with writing tests.

assess: Test support functions

Assess provides additional support for testing Emacs packages.

It provides:

Assess aims to be a stateless as possible, leaving Emacs unchanged whether the tests succeed or fail, with respect to buffers, open files and so on; this helps to keep tests independent from each other.

ert-expectations   ERT

expectations allows more concise definitions of ERT tests. For example:

;; With ERT:

(ert-deftest erte-test-00001 ()
  (should (equal 10 (+ 4 6))))

;; With Expectations:
(expect 10 (+ 4 6))

;; Or:
(expectations
 (desc "success")
 (expect 10 (+ 4 6))
 (expect 5 (length "abcde"))
 (desc "fail")
 (expect 11 (+ 4 6))
 (expect 6 (length "abcde")))
with-simulated-input: Test interactive functions non-interactively

This package provides an Emacs Lisp macro, with-simulated-input, which evaluates one or more forms while simulating a sequence of input events for those forms to read. The result is the same as if you had evaluated the forms and then manually typed in the same input. This macro is useful for non-interactive testing of normally interactive commands and functions, such as completing-read.

Some interactive functions rely on idle timers to do their work, so you might need a way to simulate idleness. For that, there is the wsi-simulate-idle-time function. You can insert calls to this function in between input strings.

xtest: Extensions for ERT   ERT

XTest is a simple set of extensions for ERT. XTest speeds up the creation of tests that follow the “one assertion per test” rule of thumb. It also simplifies testing functions that manipulate buffers. XTest aims to do a few things well, instead of being a monolithic library that attempts to solve every conceivable testing need. XTest is designed to be paired with vanilla ERT and other ERT libraries, where the user mixes and matches depending on their needs.

Tips   tips Error message strings may not match after substitute-quotes

When testing error messages, the message string in the test suite might not match the value captured by the test output, because Emacs may run substitute-quotes on the message. This may result in various quotation marks being changed. It can even be the case that the difference only manifests on other systems (e.g. the tests may pass on the developer's system but fail on a remote CI system due to different locale settings).

To work around this, pass a format string to error, like (error "%s" "Foo doesn't bar"), which prevents Emacs from changing the quotes. (For more information, see this issue.)

User interface   UI Libraries   libraries bui: 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:

Usage

BUI provides means to display entries in 2 types of buffers:

In short, you define how a list / info interface looks like (using bui-define-interface macro), and then you can make some user commands that will display entries (using bui-get-display-entries and similar functions).

lister: Yet another list printer

Lister is a library for creating interactive "lists" of any kind. In contrast to similar packages like hierarchy.el or tablist.el, it aims at not simply mapping a data structure to a navigatable list. Rather, it treats the list like emacs treats buffers: It is an empty space to which you can successively add stuff. So in Emacs lingo, lister should be rather called listed - it is a library for editing lists, instead of displaying them.

calfw: Calendar framework

This program displays a calendar view in the Emacs buffer.

It is also usable as a library to display items on a calendar.

ctable: Table Component

ctable.el is a table component for Emacs Lisp. Emacs Lisp programs can display a nice table view from an abstract data model. The many emacs programs have the code for displaying table views, such as dired, list-process, buffer-list and so on. So, ctable.el would provide functions and a table framework for the table views.

hydra   key_binding

This is a package for GNU Emacs that can be used to tie related commands into a family of short bindings with a common prefix - a Hydra.

navigel

The navigel package is a library that makes it simpler for Emacs Lisp developers to define user-interfaces based on tablists (also known as tabulated-lists). Overriding a few (CL) methods and calling navigel-open is all that’s required to get a nice UI to navigate your domain objects (files, music library, database, etc.).

tabulated-list-mode   built_in

Tabulated List mode is a major mode for displaying tabulated data, i.e., data consisting of entries, each entry occupying one row of text with its contents divided into columns. Tabulated List mode provides facilities for pretty-printing rows and columns, and sorting the rows according to the values in each column.

Transient

The library that powers Magit's command/option UI.

Taking inspiration from prefix keys and prefix arguments, Transient implements a similar abstraction involving a prefix command, infix arguments and suffix commands.

tui: An experimental text-based UI framework modeled after React

This is an experiment in building purely text-based user interfaces (TUI’s). The ultimate goal is to explore new paradigms for user interface design and development using Emacs. To this end, tui.el implements an API based on the popular React JavaScript framework in order to reduce the demands involved with designing and building complex text-based UI’s. This is all currently experimental! Expect things to change as I get feedback about what works, what does not!

Version control   version_control XML / HTML   xml html Libraries   libraries

These libraries can all be used for HTML.

esxml

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        | 

Example:

(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 "span.author 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

It’s like jQuery, but way less useful.

Example:

<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="example.org">
    </iframe>
  </body>
</html>
(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.

;; Grab the top-level paragraph content from XHTML.
(xml-query-all '(html body p *) xhtml)

;; Extract all the links from an Atom feed.
(xml-query-all '(feed entry link [rel "alternate"] :href) xml)
enlive

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

Example:

(require 'enlive)

(enlive-text
 (enlive-query (enlive-fetch "http://gnu.org/") [title])) ; => "The GNU Operating System and the Free Software Movement"
xml-plus

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
xmlgen: An s-expression to XML DSL

Generate XML using sexps with the function xmlgen:

(xmlgen '(html
          (head
           (title "hello")
           (meta :something "hi"))
          (body
           (h1 "woohhooo")
           (p "text")
           (p "more text"))))

produces this:

<html>
  <head>
    <title>hello</title>
    <meta something="hi" />
  </head>
  <body>
    <h1>woohhooo</h1>
    <p>text</p>
    <p>more text</p>
  </body>
</html>
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!

Irreal

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.

Anders Lindgren

Anders, aka Lindydancer, has written numerous packages to help with developing highlighting and font-lock packages, as well as some other useful tools.

Henrik Lissner

Author and maintainer of Doom Emacs, one of the most popular configurations.

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.

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.

Contributions

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

Guidelines
Catalog and tag appropriately
New entries in the outline should have the appropriate tags and should follow the existing hierarchy. For example, articles should be tagged articles, and generally filed under an Articles heading using tag inheritance to apply the tag.
"Loosely" or "usefully" opinionated
Rather than being a place to dump links for users to sort out, we should do that ourselves. Links should have summaries and examples. Where there are multiple links to similar projects, we should compare them and guide users to what we think is generally the best solution, or the best solution for each use case.
Archive reference material
Much of the shared wisdom in the Emacs community is written in a variety of blogs by users and developers, as well as posts on Reddit, StackOverflow, etc. These tend to hang around for a long time, but being the Internet, this is never guaranteed. When linking to an article or other reference material, we should store a link to an archived version using .
Requirements
org-make-toc
This package updates the table of contents. It's automatically used by this document through file-local variables, which you should be prompted to allow when opening the file.
Tasks

These resources should be added to the appropriate sections above. Since it takes some work to catalog and organize them, they are dumped here for future reference. Pull requests for these are welcome!

TODO Add el-search

Useful for searching Elisp code, doing query/replace on it in a Lisp-aware way, etc.

TODO stream.el benchmarks

[2020-11-03 Tue 15:27] It's very surprising that stream seems faster than a plain cl-loop. I wonder if I'm doing something wrong…

Also see:

(bench-multi-lexical :times 100 :ensure-equal t
  :forms (("cl-loop"
           (let* ((buffer (find-file-noselect "~/org/main.org"))
                  (regexp (rx bow "Emacs" eow)))
             (with-current-buffer buffer
               (goto-char (point-min))
               (length (cl-loop while (re-search-forward regexp nil t)
                                collect (match-string-no-properties 0))))))

          ("stream-regexp/seq-do/push"
           (let* ((buffer (find-file-noselect "~/org/main.org"))
                  (regexp (rx bow "Emacs" eow))
                  (stream (stream-regexp buffer regexp))
                  result)
             (with-current-buffer buffer
               (goto-char (point-min))
               (seq-do (lambda (_match)
                         (push (match-string-no-properties 0) result))
                       stream)
               (length result))))

          ("stream-regexp/cl-loop"
           (let* ((buffer (find-file-noselect "~/org/main.org"))
                  (regexp (rx bow "Emacs" eow))
                  (stream (stream-regexp buffer regexp)))
             (with-current-buffer buffer
               (goto-char (point-min))
               (length (cl-loop while (stream-pop stream)
                                collect (match-string-no-properties 0))))))))

[2020-11-03 Tue 16:57] Other stream-related tests (these methods are bespoke):

Buffer lines

The benchmark macros need to be extended to allow definitions that aren't part of the benchmarked code.

(cl-defmethod stream-lines ((buffer buffer) &optional pos no-properties)
  "Return a stream of the lines of the buffer BUFFER.
BUFFER may be a buffer or a string (buffer name).
The sequence starts at POS if non-nil, `point-min' otherwise."
  ;; Copied from the buffer method.
  (let ((fn (if no-properties
                #'buffer-substring-no-properties
              #'buffer-substring)))
    (with-current-buffer buffer
      (unless pos (setq pos (point-min)))
      (if (>= pos (point-max))
          (stream-empty))
      (stream-cons
       (with-current-buffer buffer
         (save-excursion
           (save-restriction
             (widen)
             (goto-char pos)
             (prog1 (funcall fn (point-at-bol) (point-at-eol))
               (setf pos (progn
                           (forward-line 1)
                           (point)))))))
       (stream-lines buffer pos no-properties)))))

(bench-multi-lexical :times 100 :ensure-equal t
  :forms (("stream-lines/seq-take/seq-into"
           (let* ((buffer (find-file-noselect "~/org/main.org"))
                  (stream (stream-lines buffer nil 'no-properties)))
             (seq-into (seq-take stream 10) 'list)))

          ("cl-loop"
           (let* ((buffer (find-file-noselect "~/org/main.org"))
                  (no-properties t))
             (with-current-buffer buffer
               (save-excursion
                 (save-restriction
                   (widen)
                   (goto-char (point-min))
                   (cl-loop for fn = (if no-properties
                                         #'buffer-substring-no-properties
                                       #'buffer-substring)
                            collect (funcall fn (point-at-bol) (point-at-eol))
                            do (forward-line 1)
                            until (eobp)))))))))
String lines

The benchmark macros need to be extended to allow definitions that aren't part of the benchmarked code. I'm not sure how I even got those results because it's not working now…

(cl-defmethod stream-lines ((string string) &optional pos no-properties)
  "Return a stream of the lines of the string STRING.
The sequence starts at POS if non-nil, 0 otherwise."
  ;; Copied from the buffer method.
  (unless pos (setq pos 0))
  (let ((fn (if no-properties
                #'buffer-substring-no-properties
              #'buffer-substring))
        (eol (when (string-match "\n" string pos)
               (match-beginning 0))))
    (stream-cons
     (seq-subseq string pos eol)
     (if (not eol)
         (stream-empty)
       (stream-lines string (1+ eol) no-properties)))))

(bench-multi-lexical :times 100 :ensure-equal t
  :forms (("stream-lines/seq-into"
           (let* ((string "abcd
efgh
hijk
zz"))
             (seq-into (stream-lines string nil 'no-properties) 'list)))

          ("s-lines"
           (let* ((string "abcd
efgh
hijk
zz"))
             (s-lines string)))))
TODO Emacs Lisp Animations | Digital | Dan Torop

Emacs, a programmer's text editor with roots in the 1970s, is a great tool for animation. In Fall, 2010 I taught a digital art class at NYU's interdisciplinary Steinhardt art school. Clearly, the thing to do was to teach how to make animations in Emacs by programming it in Lisp.

These exercises are meant for non-programmers to get a glimpse of what a program and a language can do, by creating "physical" objects, in this case punctuation marks on the screen. ASCII art is the future, yes? At the very least, ASCII art in the '70s was to computer science what post-minimalism was to the contemporary art of the period. Think of a pile of ampersands and exclamation marks as earth art.

TODO Add section about files

e.g. saving files to disk.

Best practices

TODO Mention file-precious-flag

TODO Add Elisp manual's Appendix D "Tips and Conventions"

TODO easy-mmode.el

Especially easy-mmode-defmap.

TODO Articles to add [0/13] TODO Why package.el? (1 min read) TODO Add people [1/8] 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 tips for new developers

e.g.:

TODO Add MELPA

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

TODO Dynamic modules section TODO Documentation best practices

Describe things like exporting an Org readme to an Info manual, e.g. like Magit, org-super-agenda, etc.

TODO Add databases section 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).

TODO Sequence shuffling examples and benchmarks Benchmarking sequence shuffling

See https://github.com/melpa/melpa/pull/6191#issuecomment-498101336

(defun key-quiz--shuffle-list (list)
  "Shuffles LIST randomly, modying it in-place."
  (dolist (i (reverse (number-sequence 1 (1- (length list)))))
    (let ((j (random (1+ i)))
          (tmp (elt list i)))
      (setf (elt list i) (elt list j))
      (setf (elt list j) tmp)))
  list)

(defun key-quiz--shuffle-list-nreverse (list)
  "Shuffles LIST randomly, modying it in-place."
  (dolist (i (nreverse (number-sequence 1 (1- (length list)))))
    (let ((j (random (1+ i)))
          (tmp (elt list i)))
      (setf (elt list i) (elt list j))
      (setf (elt list j) tmp)))
  list)

(defun elfeed--shuffle (seq)
  "Destructively shuffle SEQ."
  (let ((n (length seq)))
    (prog1 seq                  ; don't use dotimes result (bug#16206)
      (dotimes (i n)
        (cl-rotatef (elt seq i) (elt seq (+ i (random (- n i)))))))))

(defun faster-seq-sort-by (function pred sequence)
  "Sort SEQUENCE using PRED as a comparison function.
Elements of SEQUENCE are transformed by FUNCTION before being
sorted.  FUNCTION must be a function of one argument."
  ;; This version is modified to avoid calling "random" twice every time the predicate is called.
  (seq-map 'cdr
           (sort (seq-map (lambda (x) (cons (funcall function x) x)) sequence)
                 (lambda (a b)
                   (funcall pred (car a) (car b))))))

(defun seq-sort-by--shuffle (seq)
  (seq-sort-by (lambda (_) (random)) #'<= seq))

(defun faster-seq-sort-by--shuffle (seq)
  (faster-seq-sort-by (lambda (_) (random)) #'<= seq))
Lists
(let ((big-list (seq-into (seq-take obarray 5000) 'list)))
  (bench-multi-lexical :times 100
    :forms (("key-quiz--shuffle-list" (key-quiz--shuffle-list big-list))
            ("key-quiz--shuffle-list-nreverse" (key-quiz--shuffle-list-nreverse big-list))
            ("elfeed--shuffle" (elfeed--shuffle big-list))
            ("seq-sort-by--shuffle" (seq-sort-by--shuffle big-list))
            ("faster-seq-sort-by--shuffle" (faster-seq-sort-by--shuffle big-list)))))
Vectors
(let ((big-list (seq-into (seq-take obarray 5000) 'vector)))
  (bench-multi-lexical :times 100
    :forms (("key-quiz--shuffle-list" (key-quiz--shuffle-list big-list))
            ("key-quiz--shuffle-list-nreverse" (key-quiz--shuffle-list-nreverse big-list))
            ("elfeed--shuffle" (elfeed--shuffle big-list))
            ("seq-sort-by--shuffle" (seq-sort-by--shuffle big-list))
            ("faster-seq-sort-by--shuffle" (faster-seq-sort-by--shuffle big-list)))))
MAYBE Benchmarking seq-let vs pcase-let* with backquoted patterns
(bench-multi-lexical :times 1000 :ensure-equal t
  :forms (("pcase-let* backquoted pattern"
           (pcase-let* ((`(,name ,argument) (list 'NAME 'ARGUMENT)))
             (list name argument)))

          ("seq-let"
           (seq-let (name argument) (list 'NAME 'ARGUMENT)
             (list name argument)))))

Created: 2024-04-22 Mon 11:13

Validate


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4