annalist.el icon indicating copy to clipboard operation
annalist.el copied to clipboard

Record and display information such as keybindings

#+TITLE: Annalist User Manual #+AUTHOR: Fox Kiester #+LANGUAGE: en #+TEXINFO_DIR_CATEGORY: Emacs #+TEXINFO_DIR_TITLE: Annalist: (annalist). #+TEXINFO_DIR_DESC: Record and display information such as keybindings.

NOTE: if you are viewing this in org-mode, it is recommended that you install and enable [[https://github.com/snosov1/toc-org][toc-org]], so that all internal links open correctly.

[[https://melpa.org/#/annalist][file:https://melpa.org/packages/annalist-badge.svg]] [[https://travis-ci.org/noctuid/annalist.el][https://travis-ci.org/noctuid/annalist.el.svg?branch=master]]

#+begin_quote Incessant wind sweeps the plain. It murmurs on across grey stone, carrying dust from far climes to nibble eternally at the memorial pillars. There are a few shadows out there still but they are the weak and the timid and the hopelessly lost.

It is immortality of a sort.

Memory is immortality of a sort.

In the night, when the wind dies and silence rules the place of glittering stone, I remember. And they all live again. #+end_quote

=annalist.el= is a library that can be used to record information and later print that information using =org-mode= headings and tables. It allows defining different types of things that can be recorded (e.g. keybindings, settings, hooks, and advice) and supports custom filtering, sorting, and formatting. =annalist= is primarily intended for use in other packages like =general= and =evil-collection=, but it can also be used directly in a user's configuration.

[[file:https://user-images.githubusercontent.com/4250696/63480582-64e2cb00-c460-11e9-9571-706b5b96992c.png]]

  • Table of Contents :noexport:TOC:
  • [[#usage][Usage]]
    • [[#disabling-annalist][Disabling Annalist]]
    • [[#terminology][Terminology]]
    • [[#settings][Settings]]
    • [[#defining-new-types][Defining New Types]]
      • [[#type-top-level-settings][Type Top-level Settings]]
      • [[#type-item-settings][Type Item Settings]]
      • [[#record-update-preprocess-and-postprocess-settings-argument][=:record-update=, =:preprocess=, and =:postprocess= Settings Argument]]
    • [[#defining-views][Defining Views]]
      • [[#view-top-level-settings][View Top-level Settings]]
      • [[#view-item-settings][View Item Settings]]
    • [[#recording][Recording]]
    • [[#describing][Describing]]
    • [[#helper-functions][Helper Functions]]
      • [[#list-helpers][List Helpers]]
      • [[#formatting-helpers][Formatting Helpers]]
        • [[#format-helpers][=:format= Helpers]]
        • [[#formatting-emacs-lisp-source-blocks][Formatting Emacs Lisp Source Blocks]]
      • [[#sorting-helpers][Sorting Helpers]]
    • [[#builtin-types][Builtin Types]]
      • [[#keybindings-type][Keybindings Type]]
  • Usage ** Disabling Annalist #+begin_quote What fool always has his nose in everywhere because he thinks he has to know so he can record it in his precious Annals? #+end_quote

If you use a library that uses =annalist= (e.g. =evil-collection= or =general=) but don't need it's functionality during init or at all, you can set =annalist-record= to nil to shave some milliseconds off of your init time (especially if you have a lot of keybindings). Alternatively, if you only want to prevent =annalist= from recording certain things or have it only record certain things, you can configure =annalist-record-blacklist= or =annalist-record-whitelist= respectively.

** Terminology

  • item - an individual recorded item; may be displayed as a heading or as a table column entry (e.g. a key such as =C-c=)
  • record - a list of related, printable items corresponding to one piece of information (e.g. a single keybinding: a list of a keymap, key, and definition)
  • metadata - a plist of information about a data list that should not be printed; appears as the last item in a record
  • tome - a collection of records of a specific type

** Settings Annalist provides =annalist-describe-hook= which is run in annalist description buffers after they have been populated but before they are marked read-only: #+begin_src emacs-lisp (add-hook 'annalist-describe-hook (lambda () (visual-fill-column-mode -1))) #+end_src

** Defining New Types #+begin_quote Three huge tomes bound in worn, cracked dark leather rested on a large, long stone lectern, as though waiting for three speakers to step up and read at the same time. #+end_quote

Annalist provides the function ~annalist-define-tome~ for defining new types of tomes: #+begin_src emacs-lisp (annalist-define-tome 'battles '(:primary-key (year name) :table-start-index 1 year name casualties ...)) #+end_src

At minimum, a type definition must include =:primary-key=, =:table-start-index=, and a symbol for each item records should store. Items should be defined in the order they should appear in org headings and then in the table.

*** Type Top-level Settings These settings apply to the entirety of the recorded information.

  • =:table-start-index= - the index of the first item to be printed in an org table; previous items are printed as headings (default: none)
  • =:primary-key= - the item or list of items that uniquely identifies the record; used with the =:test= values for those items to check for an old record that should be replaced/updated (default: none)
  • =:record-update= - a function used to update a record before recording it; this can be used to, for example, set the value of an item to store the previous value of another item; the function is called with =old-record= (nil if none), =new-record=, and =settings=; see ~annalist--update-keybindings~ for an example of how to create such a function (default: none)
  • =:preprocess= - a function used to alter a record before doing anything with it; it is passed =record= and =settings= and should return the altered record; see the default keybindings type for an example (default: none)
  • =:test= - test function used for comparing the primary key (as a list of each item in the order it appears in the definition); you will need to create the test with ~define-hash-table-test~ if it does not exist (default: ~equal~; generally should be unnecessary to change)
  • =:defaults= - a plist of default item settings; see below for valid item settings (default: none)

*** Type Item Settings Item settings only apply to a specific item. Defaults for items that don't explicitly specify a setting can be set using the top-level =:defaults= keyword.

  • =:test= - test function used for comparing items; only applicable to heading items; you will need to create the test with ~define-hash-table-test~ if it does not exist (default: ~equal~; generally should be unnecessary to change)

*** =:record-update=, =:preprocess=, and =:postprocess= Settings Argument The settings plist past to the =:record-update= function contains all information for both the tome type and view. The information is converted into a valid plist and some extra keywords are added. Here is an example: #+begin_src emacs-lisp '(:table-start-index 2 :primary-key (keymap state key) ;; the following keywords are generated for convenience :type keybindings :key-indices (2 1 0) :final-index 4 :metadata-index 5 ;; item settings can be accessed by their symbol or their index keymap (:name keymap :index 0 :format annalist-code) 0 (:name keymap :index 0 :format annalist-code) ...) #+end_src

** Defining Views #+begin_quote In those days the company was in service to… #+end_quote

Views contain settings for formatting and displaying recorded information. Settings from the type definition cannot be changed later. On the other hand, views are for all settings that a user may want to change for a particular ~annalist-describe~ call. They are defined using the same format as tome types: #+begin_src emacs-lisp (annalist-define-view 'battles 'default '(:defaults (:format capitalize) year name (casualties :title "Deaths") ...)) #+end_src

The =default= view is what ~annalist-describe~ will use if no view name is explicitly specified. To prevent naming conflicts, external packages that create views should prefix the views with their symbol (e.g. =general-alternate-view=).

*** View Top-level Settings These settings apply to the entirety of the recorded information.

  • =:predicate= - a function that is passed the entire record and returns non-nil if the record should be printed (default: none)
  • =:sort= - a function used to sort records in each printed table; the function is passed two records and and should return non-nil if the first record should come first (default: none; tables are printed in recorded order)
  • =:hooks= - a function or a list of functions to run in the describe buffer after printing all headings and tables before making the buffer readonly; these run before =annalist-describe-hook= (default: none)
  • =:postprocess= - a function used to alter a record just before printing it; it is passed =record= and =settings= and should return the altered record; an example use case would be to alter the record using its metadata (e.g. by replacing a keybinding definition with a which-key description, if one exists) (default: none)
  • =:defaults= - a plist of default item settings; see below for valid item settings (default: none)

There is also a special =:inherit= keyword that can be used to create a new type of tome that is based on another type: #+begin_src emacs-lisp (annalist-define-view 'keybindings 'alternate ;; override title for key column '((key :title "Keybinding") ...) :inherit 'keybindings) #+end_src

*** View Item Settings Item settings only apply to a specific item. Defaults for items that don't explicitly specify a setting can be set using the top-level =:defaults= keyword. #+begin_src emacs-lisp (annalist-define-view 'keybindings 'my-view '(:defaults (:format #'capitalize) ;; surround key with = instead of capitalizing (key :format #'annalist-verbatim) ;; perform no formatting on definition (definition :format nil))) #+end_src

Sorting/filtering (only for items displayed in headings):

  • =:predicate= - a function that is passed the item and returns non-nil if it should be printed; only applicable to heading items (default: none)
  • =:prioritize= - list of items that should be printed before any others; only applicable to heading items (default: none)
  • =:sort= - a function used to sort records; only applicable to heading items; the function is passed two items and and should return non-nil if the first item should come first (default: none; printed in recorded order)

Formatting:

  • =:title= - a description of the item; used as the column title (default: capitalize the symbol name; local only)
  • =:format= - function to run on the item value before it is printed (e.g. ~#'capitalize~, ~#'annalist-code~, ~#'annalist-verbatim~, etc.); note that this is run on the item as-is if it has not been truncated, so the function may need to convert the item to a string first; has no effect if the item is extracted to a footnote/source block (default: none)
  • =:max-width= - the max character width for an item; note that this is compared to the item as-is before any formatting (default: 50)
  • =:extractp= - function to determine whether to extract longer entries into footnotes instead of truncating them; (default: ~listp~)
  • =:src-block-p= function to determine whether to extract to a source block when the =:extractp= function returns non-nil (default: ~listp~)

** Recording #+begin_quote The Lady said, “I wanted you to see this, Annalist.” […] “What is about to transpire. So that it is properly recorded in at least one place.” #+end_quote

~annalist-record~ is used to record information. It requires three arguments: =annalist= =type= =record=. The =annalist= argument will usually be the same as the package prefix that is recording the data. =annalist= and any other names prefixed by =annalist= are reserved for this package. =type= is the type of data to record, and =record= is the actual data. Optionally, the user can also specify metadata that won't be printed after the final item. Buffer-local records should additionally specify =:local t=. Here is an example: #+begin_src emacs-lisp (annalist-record 'me 'keybindings (list ;; keymap state key definition previous-definition 'global-map nil (kbd "C-+") #'text-scale-increase nil ;; metadata can be specified after final item (list :zoom-related-binding t)))

;; alternatively, record using plist instead of ordered list (annalist-record 'me 'keybindings (list 'keymap 'global-map 'state nil 'key (kbd "C-+") 'definition #'text-scale-increase ;; metadata can be specified with `t' key t (list :zoom-related-binding t)) :plist t) #+end_src

Some items can potentially be recorded as nil. In the previous example, the evil =state= is recorded as nil (which will always be the case for non-evil users). When a heading item is nil, the heading at that level will just be skipped/not printed.

** Describing #+begin_quote Once each month, in the evening, the entire Company assembles so the Annalist can read from his predecessors. #+end_quote

~annalist-describe~ is used to describe information. It takes three arguments: =name= =type view=. =view= is optional (defaults to =default=). For example: #+begin_src emacs-lisp (annalist-describe 'me 'keybindings) #+end_src

It is possible to have custom filtering/sorting behavior by using a custom view: #+begin_src emacs-lisp (annalist-define-view 'keybindings 'active-keybindings-only '((keymap ;; only show keys bound in active keymaps :predicate #'annalist--active-keymap ;; sort keymaps alphabetically :sort #'annalist--string-<)))

(annalist-describe 'my 'keybindings 'active-keybindings-only) #+end_src

=annalist-org-startup-folded= will determine what =org-startup-folded= setting to use (defaults to nil; all headings will be unfolded).

** Helper Functions *** List Helpers ~annalist-plistify-record~ can be used to convert a record that is an ordered list to a plist. ~annalist-listify-record~ can be used to do the opposite. This is what the =:plist= argument for ~annalist-record~ uses internally. These functions can be useful, for example, inside a =:record-update= function, so that you can get record items by their name instead of by their index. However, if there will be a lot of data recorded for a type during Emacs initialization time, the extra time to convert between list types can add up, so it's recommended that you don't use these functions or =:plist= in such cases.

*** Formatting Helpers **** =:format= Helpers Annalist provides ~annalist-verbatim~ (e.g. ~=verbatim text=~), ~annalist-code~ (e.g. =~my-function~=), and ~annalist-capitalize~. There is also an ~annalist-compose~ helper for combining different formatting functions.

**** Formatting Emacs Lisp Source Blocks By default, Emacs Lisp extracted into source blocks will just be one long line. You can add ~annalist-multiline-source-blocks~ to a view's =:hooks= keyword or to =annalist-describe-hook= to autoformat org source blocks if lispy is installed. By default, it uses ~lispy-alt-multiline~. To use ~lispy-multiline~ instead, customize ~annalist-multiline-function~.

The builtin types have ~annlist-multiline-source-blocks~ in their =:hooks= setting by default.

Here is an example of what this looks like:

[[file:https://user-images.githubusercontent.com/4250696/62338313-1025e300-b4a6-11e9-845f-179c02abef35.png]]

*** Sorting Helpers Annalist provides ~annalist-string-<~ and ~annalist-key-<~ (e.g. ~(kbd "C-c a")~ vs ~(kbd "C-c b")~).

** Builtin Types *** Keybindings Type Annalist provides a type for recording keybindings that is used by =evil-collection= and =general=. When recording a keybinding, the keymap must be provided as a symbol. Here is an example: #+begin_src emacs-lisp (annalist-record 'annalist 'keybindings (list 'org-mode-map nil (kbd "C-c g") #'counsel-org-goto)) #+end_src

In addition to the default view, it has a =valid= to only show keybindings for keymaps/states that exist (since some keybindings may be in a ~with-eval-after-load~). It also has an =active= view to only show keybindings that are currently active.

increase max depth

Local Variables:

toc-org-max-depth: 4

End: