Table of Contents
- Literate configuration
-
Literate config examples
- 4 Emacs
-
5 Hammerspoon
- General variables and configuration
- Spoon Management
- BetterTouchTool integration (experimental)
- URL dispatching to site-specific browsers
- Window and screen manipulation
- Organization and Productivity
- System and UI
- Other applications
- Seal application launcher/controller
- Network transitions
- Pop-up translation
- Leanpub integration
- Showing application keybindings
- Loading private configuration
- End-of-config animation
-
6 Elvish
- Paths
- Package installation
- Automatic proxy settings
- General modules and settings
- Aliases and miscellaneous functions
- Completions
- Prompt theme
- iTerm2 shell integration support
- Long-running-command notifications
- Directory and command navigation and history
- Dynamic terminal title
- Loading private settings
- O’Reilly Atlas
- OpsGenie
- LeanPub
- TinyTeX
- Environment variables
- Utility functions
- Work-specific stuff
- Exporting aliases
Literate configuration
Welcome! In this booklet I will introduce you to Literate Configuration, which is the application of Literate Programming to configuration files. If you are already familiar with Literate Programming you might find most of the basic concepts familiar. Literate Programming is a beautiful concept, but it is ambitious and hard to apply in general software development. However, I have found that it can be especially applicable to configuration files, for several reasons:
- Configuration files are inherently focused, since they correspond to a single application, program or set of programs, all related. This makes it easier to draw a narrative for them;
- Most configuration files are self-contained but their structure and syntax may not be immediately evident, so they benefit from a human-readable explanation of their contents;
- Configuration files are often shared and read by others, as we all like to learn by reading the config files of other people. Applying Literate Programming to config files makes them much easier to share, since their explanation is naturally woven into the code;
- Org mode has emerged in the last few years as a powerful and simple markup language for general writing, but with unique features that make it easy to include code within the text, and even further, to easily extract that code into stand-alone source files which can be interpreted by their corresponding programs.
Whether you already use Emacs and Org or not, I hope you will find value in this book by seeing how uniquely Literate Programming can help you better write, maintain, understand and share your config files.
1 Literate Programming
Literate programming was first proposed by Donald Knuth in 1984 (Literate Programming, The Computer Journal, 27 (2): 97–111) as a technique of producing computer code from prose. The general idea is that you write code as you would write a story or an article, describing what you want to do and how to solve it. The code gets interweaved with the code as you explain it. Ultimately, you can produce one of two outputs from the same file:
- A human-readable version of the code, nicely typeset with all the explanations and code, through a process called weaving;
- A computer-executable version of the code, which extracts only the code and puts it in the correct order for the computer to run, through a process called tangling.
The idea of literate programming has been around since then, and a number of tools have been created, starting from Knuth’s own CWEB, Ramsey’s noweb, and others. However, the lack of a standard set of tools has made its adoption slow and limited to specialized instances.
During my first encounter with literate programming I used noweb
, but beyond some school projects and homeworks, I could never get Literate Programming to “stick”, until I encountered Org mode.
Org mode
Org mode is a markup format and corresponding toolset developed originally within the Emacs text editor. Org provides a wide range of functionality, including text formatting, calendar, task tracking, and much more. For our purposes, we are interested in the combination of two of its functions:
- Text formatting and exporting. This is one of Org mode’s core functionalities, which provides the weaving aspect of Literate Programming by allowing us to produce nicely rendered versions of our document in a number of output formats, including both the text narration and the code. Out of the box, Org supports exporting to LaTeX, Markdown, HTML and other common formats, but many more are available through third-party libraries. The output document may even include syntax highlighting appropriate for the language in which it is written.
- Code block evaluation and exporting. This is made possible by org-babel, one of Org’s built-in modules, which allows flexible manipulation, execution and exporting of code embedded within an Org document. Babel supports tangling code blocks into one or more separate files which include only the code portions of the document.
Using Org mode with Babel for literate programming is, of course, not a new idea. However, most cases of Literate Configuration that I have seen focus on using Literate Programming for configuring Emacs itself. This is a good use case, but limited in my opinion. In this booklet we will explore how you can use Literate Programming for all your configuration files.
2 The Tools and Process
Emacs and Org mode
The basic tools in our setup will be Emacs and Org mode. I assume you have a basic knowledge of Emacs, but if you don’t: don’t worry! It takes little to get started. If you want a gentle introduction, check out the Guided Tour of Emacs. If you have already installed Emacs, you can start an interactive tutorial within Emacs itself by pressing C-h
followed by t
(if you are using a graphical version of Emacs, this is likely also available from within the Help menu). If you currently use vi
or vim
, you can check out Doom Emacs or Spacemacs, both are mature and stable Emacs distributions which use vi
-style keybindings (through the Emacs evil
module) by default, and which allow you to start using Emacs without having to relearn any new keybindings.
Org mode is included with all recent versions of Emacs, so chances are you already have it installed. Emacs 26.3 (the one I use as of this writing) includes Org 9.1.9, which is quite recent and more than enough for the uses I describe here. If you want to upgrade to the latest version, see the Org mode installation instructions.
How it works
In general, the process of generating config files from Org files can be illustrated as follows:
Org mode is extremely flexible, so you can decide how to structure and organize both your source (Org) and destination (config) files in the way that suits you best. Here are some options:
- Org file stored in the same directory as the resulting config file. Usually one org file corresponds to one config file, but multiple related config files (e.g. for the same program or application) could be combined in the same Org file, from which all of them are generated. The advantage of this method is that the source Org files and the configuration files they produce are stored in the same location, which makes it easier to relate them for your own use, and to share them if you put them, for example, in a Github repository. The disadvantage is that the config directory gets “polluted” by the source files, and some applications might even produce errors or complain about having extraneous files in their configuration directories. However, this is uncommon and can be dealt with if needed. This is the setup I use and recommend, and which is described in this booklet.
- All the org files stored in a single directory (or directory tree, or even in a single file), separate from the final destinations of the config files they contain. This has the advantage of giving you a central location for all your org files, regardless of the config files they produce. The drawback is that it makes it harder to selectively share the configuration files for a single program.
- Of course, you can combine the two approaches if it makes sense for you. For example, you might choose to have all your shell’s config files produced from a single Org file, while having separate Org files for other, individual config files.
Ultimately it is up to you to decide which scheme to use. The basic techniques are the same, and the point of literate config is to make things easier for you to understand and maintain.
Configuring Emacs and Org for literate programming
To get started with literate configuration, the only really indispensable package is org
, which can be loaded without prior installation since it is included with Emacs:
As you write your config file, you can use the org-babel-tangle
command (bound by default to C-c C-v t
) to extract the code from the current file into the corresponding code files. Conversely, you can use org-export-dispatch
(C-c C-e
) to export your file to any of Org’s supported output formats.
There are other optional configurations which you can use to make the experience more efficient and pleasant. You can find these below.
Automatic tangle on save
After some time, it can get tedious to press C-c C-v t
to tangle your files, and you run the risk of forgetting to do it after a change, resulting in a discrepancy between your Org file and your config files. To avoid this, you can configure a hook that automatically runs org-babel-tangle
upon saving any Org buffer, which means the resulting files will be automatically kept up to date.
Language-specific org-babel support
You can load and enable org-babel
language-specific packages. Many are included in Org mode, while others can be found online. Most org-babel support packages are named ob-<language>
. For example, these are some of the ones I use:
- CFEngine, used extensively for my book Learning CFEngine.
- Elvish, my favorite shell.
After you load any necessary packages, you should configure the languages for which to load org-babel
support. Note that this does not affect the ability to export/tangle code, but allows you to execute snippets of code from within the Org buffer by pressing C-c C-c
on them, and have the results automatically inserted into the buffer.
Beautifying Org mode
Org mode allows extensive configuration of its fonts and colors, and doing so can significantly improve the experience of editing literate code with Emacs. In the end, you can have an Emacs setup for editing org documents which looks very nice, with proportional fonts for text and monospaced fonts for code blocks, examples and other elements. For example, here is what a fragment of my Emacs config file looks like:
This is the look of Org in Doom Emacs with the Spacemacs Light theme and my favorite fonts, ET Book for text and Fira Code for code:
Of course, if you use plain Emacs, you can also achieve similar results. Instead of describing the configuration here, I’ll just point you to the Beautifying Org mode section of my Old Emacs config - you can find all the details there.
Structure of a literate config file
A literate config file is simply an org file which has source code blocks in it with the :tangle
option to specify the file to which they should be extracted. If more than one source block specifies the same file, they will be appended in the order they appear. For example, a very simple bash config file can be produced as follows:
If your whole org file gets tangled to a single config file, you can specify the :tangle
header globally to avoid having to specify it for every code block. To do this, you specify a header-args
global property corresponding to the language of the source blocks you want to export. For example, I have the following line at the top my init.org
Hammerspoon config source file:
If the basename of your org file is the same as the exported source file (for example, init.org
produces init.lua
), you can even use a bit of Emacs Lisp magic to avoid having to specify the output filename by hand, just the extension (shown here in two lines, make sure you type everything in a single line):
Even if you specify the tangle filename globally, you can still change its value for individual code blocks. For example, you can prevent a code block from being tangled by specifying :tangle no
, or specify a different filename to write it to a file different from the global one.
3 Tips and tricks
We now look at some useful tips that will make your life easier when writing literate config files.
Converting your existing config files
You probably already have a number of long and complex config files. The beauty of literate config is that you don’t have to do a lot of work to start using it. As a first step, you can simply include your whole file into a single source block. For example, if you already have a /.profile
file, you can create /.profile.org
with the following:
You can then start breaking it up in logical blocks, adding commentary and other structure as you see fit. For this, you may find useful the org-babel-demarcate-block
command, bound by default to C-c C-v C-d
. When invoked within a source block, this command splits the block at that point, creating two source blocks with the same language and any other header arguments as the original one.
Using noweb references to structure your code
Exporting source blocks in the sequence in which they appear in the Org file is useful, but ultimately little more than extensively documenting your file with comments. The real power of literate programming comes from being able to express the logic of your code in a way that makes sense to a human reading it. This does not necessarily match the order or structure in which the code needs to be read by the computer.
This is possible in Org by using noweb references, so called because they follow the same convention introduced by the early noweb literate programming tool. In this convention, a code block can have an arbitrary name, and be referenced (included) in other blocks by specifying its name inside double angle brackets: <<code block name>>
.
Processing of noweb references is enabled by the :noweb
header argument in source blocks. This argument can have multiple values, but I have found the following to be the most useful:
-
:noweb yes
enables Noweb processing in the block, both for tangling and for exporting (i.e. when you export the code block to HTML, LaTeX or some other format, the noweb references will be expanded as well); -
:noweb no-export
enables Noweb processing in the block when tangling, but not when exporting. This is useful for leaving the noweb indicators in the human-readable exports of your file, which can make it easier to understand.
The name of an individual code block can be specified with the :noweb-ref
header argument. If multiple blocks have the same :noweb-ref
value, they will be concatenated when referenced. You can also specify the name of a block using a #+name:
property line before the source block, but in this case there can be only one block with the given name.
Consider the following org code:
Here we can see that the first block references the two other by their names, and they get combined to produce the final tangled output. This is of course a very simple example, but in complex config files, this technique can make it much easier to see the overall structure of the code before delving in the details. For a more realistic example, see the Org mode section of my old Emacs config. Here you can see a top-level block as follows:
The different noweb references get “filled in” throughout the rest of the configuration by assigning them to the corresponding :noweb-ref
values. This makes it possible to specify keybindings, variables, faces, hooks and custom config code in the sections where it makes logical sense, rather than where they need to be included in the code.
Multiple config files per org file
Most Literate Config Org files will probably generate a single config file. However, as illustrated in How it works, you can easily produce multiple config files from a single org file. For this, you can use one of the following techniques:
- Manually specify the file to which each source block should be tangled, using the
:tangle
header argument to specify it; - Specify a main tangle target globally, as described in Structure of a literate config file, and specify the
:tangle
argument for the blocks that should be written to a different file; - If the different files are produced from within different sections of the Org document, you can specify the
:tangle
argument per section, by specifying it within a:PROPERTIES
drawer of the corresponding headline. For example, in the following source, each section gets tangled to a different output file:
Literate config examples
In this part of the book you will find three real-world examples of literate configuration files created, maintained and used by me. These are the real thing—what you see here is included directly from the Org files that contain the configuration I use for Emacs (with Doom Emacs), Hammerspoon and Elvish, respectively. You can find the latest version of these files, both in Org and in their corresponding tangled output, in Github at the locations specified in each of the sections.
Note also that each section links to a post in my blog containing the corresponding config file. This illustrates another advantage of literate configuration: that the same Org file can be used to produce the output in multiple formats. Each one of the literate config files you see below are rendered in the following places:
- This book, produced by exporting the Org files in Leanpub’s Markua format using my own
ox-leanpub
package; - The corresponding blog posts in my blog, produced by exporting in Hugo Markdown format using the excellent
ox-hugo
package; - Directly in GitHub and GitLab, thanks to their support for rendering Org files.
4 Emacs
This is my Doom Emacs configuration. From this org file, all the necessary Doom Emacs config files are generated.
This file is written in literate programming style using org-mode. See init.el, packages.el and config.el for the generated files. You can see this in a nicer format on my blog post My Doom Emacs configuration, with commentary.
References
Emacs config is an art, and I have learned a lot by reading through other people’s config files, and from many other resources. These are some of the best ones (several are also written in org mode). You will find snippets from all of these (and possibly others) throughout my config.
- Sacha Chua’s Emacs Configuration
- Uncle Dave’s Emacs config
- PythonNut’s Emacs config
- Mastering Emacs
- Tecosaur’s Emacs config
Note: a lot of manual configuration has been rendered moot by using Emacs Doom, which aggregates a well-maintained and organized collection of common configuration settings for performance optimization, package management, commonly used packages (e.g. Org) and much more.
Doom config file overview
Doom Emacs uses three config files:
-
init.el
defines which of the existing Doom modules are loaded. A Doom module is a bundle of packages, configuration and commands, organized into a unit that can be toggled easily from this file. -
packages.el
defines which packages should be installed, beyond those that are installed and loaded as part of the enabled modules. -
config.el
contains all custom configuration and code.
There are other files that can be loaded, but theses are the main ones. The load order of different files is defined depending on the type of session being started.
All the config files are generated from this Org file, to try and make its meaning as clear as possible. All package!
declarations are written to packages.el
, all other LISP code is written to config.el
.
Config file headers
We start by simply defining the standard headers used by the three files. These headers come from the initial files generated by doom install
, and contain either some Emacs-LISP relevant indicators like lexical-binding
, or instructions about the contents of the file.
Customized variables
Doom does not recommend the Emacs customize
mechanism:
Note: do not use M-x customize or the customize API in general. Doom is designed to be configured programmatically from your config.el, which can conflict with Customize’s way of modifying variables.
All necessary settings are therefore set by hand as part of this configuration file. The only exceptions are “safe variable” and “safe theme” settings, which are automatically saved by Emacs in custom.el
, but this is OK as they don’t conflict with anything else from the config.
Doom modules
This code is written to the init.el
to select which modules to load. Written here as-is for now, as it is quite well structured and clear.
General configuration
My user information.
Change the Mac modifiers to my liking
When at the beginning of the line, make Ctrl-K
remove the whole line, instead of just emptying it.
Disable line numbers.
For some reason Doom disables auto-save and backup files by default. Let’s reenable them.
Disable exit confirmation.
Visual, session and window settings
I made a super simple set of Doom-Emacs custom splash screens by combining a Doom logo with the word “Emacs” rendered in the Doom Font. You can see them at https://gitlab.com/zzamboni/dot-doom/-/tree/master/splash (you can also see one of them at the top of this file). I configure it to be used instead of the default splash screen. It took me all of 5 minutes to make, so improvements are welcome!
I like two of the images, so I select one at random.
Set base and variable-pitch fonts. I currently like Fira Code and ET Book.
Allow mixed fonts in a buffer. This is particularly useful for Org mode, so I can mix source and prose blocks in the same document.
Set the theme to use. I like the Spacemacs-Light, which does not come with Doom, so we need to install it from package.el
:
And then from config.el
we specify the theme to use.
In my previous configuration, I used to automatically restore the previous session upon startup. Doom Emacs starts up so fast that it does not feel right to do it automatically. In any case, from the Doom dashboard I can simply press Enter to invoke the first item, which is “Reload Last Session”. So this code is commented out now.
Maximize the window upon startup. The (fullscreen . maximized)
value suggested in the Doom FAQ works, but results in a window that cannot be resized. For now I just manually set it to a large-enough window size by hand.
Key bindings
Doom Emacs has an extensive keybinding system, and most module functions are already bound. I modify some keybindings for simplicity of to match the muscle memory I have from my previous Emacs configuration.
Note: I do not use VI-style keybindings (which are the default for Doom) because I have decades of muscle memory with Emacs-style keybindings. You may need to adjust these if you want to use them.
Miscellaneous keybindings
Use counsel-buffer-or-recentf
for C-x b
. I like being able to see all recently opened files, instead of just the current ones. This makes it possible to use C-x b
almost as a replacement for C-c C-f
, for files that I edit often. Similarly, for switching between non-file buffers I use counsel-switch-buffer
, mapped to C-x C-b
.
The counsel-buffer-or-recentf
function by default shows duplicated entries because it does not abbreviate the paths of the open buffers. The function below fixes this, I have submitted this change to the counsel
library (https://github.com/abo-abo/swiper/pull/2687), in the meantime I define it here and integrate it via advice-add
.
Use +default/search-buffer
for searching by default, I like the Swiper interface.
Map C-c C-g
to magit-status
- I have too ingrained muscle memory for this keybinding.
Interactive search key bindings - visual-regexp-steroids provides sane regular expressions and visual incremental search. I use the pcre2el
package to support PCRE-style regular expressions.
The Doom undo
package introduces the use of undo-fu
, which makes undo/redo more “lineal”. I normally use C-/
for undo and Emacs doesn’t have a separate “redo” action, so I map C-?
(in my keyboard, the same combination + Shift
) for redo.
Emulating vi’s %
key
One of the few things I missed in Emacs from vi was the %
key, which jumps to the parenthesis, bracket or brace which matches the one below the cursor. This function implements this functionality, bound to the same key. Inspired by NavigatingParentheses, but modified to use smartparens
instead of the default commands, and to work on brackets and braces.
Org mode
Org mode has become my primary tool for writing, blogging, coding, presentations and more. I am duly impressed. I have been a fan of the idea of literate programming for many years, and I have tried other tools before (most notably noweb, which I used during grad school for homeworks and projects), but Org is the first tool I have encountered which makes it practical. Here are some of the resources I have found useful in learning it:
- Howard Abrams’ Introduction to Literate Programming, which got me jumpstarted into writing code documented with org-mode.
- Nick Anderson’s Level up your notes with Org, which contains many useful tips and configuration tricks. Nick’s recommendation also got me to start looking into Org-mode in the first place!
- Sacha Chua’s Some tips for learning Org Mode for Emacs, her Emacs configuration and many of her other articles.
- Rainer König’s OrgMode Tutorial video series.
Doom’s Org module provides a lot of sane configuration settings, so I don’t have to configure so much as in my previous hand-crafted config.
General Org Configuration
Default directory for Org files.
Hide Org markup indicators.
Insert Org headings at point, not after the current subtree (this is enabled by default by Doom).
Enable logging of done tasks, and log stuff into the LOGBOOK drawer by default
Use the special C-a
, C-e
and C-k
definitions for Org, which enable some special behavior in headings.
Enable Speed Keys, which allows quick single-key commands when the cursor is placed on a heading. Usually the cursor needs to be at the beginning of a headline line, but defining it with this function makes them active on any of the asterisks at the beginning of the line.
Org visual settings
Enable variable and visual line mode in Org mode by default.
Capturing and note taking
First, I define where all my Org-captured things can be found.
I define some global keybindings to open my frequently-used org files (original tip from Learn how to take notes more efficiently in Org Mode).
First, I define a helper function to define keybindings that open files. Note that this requires lexical binding to be enabled, so that the lambda
creates a closure, otherwise the keybindings don’t work.
Now I define keybindings to access my commonly-used org files.
I’m still trying out org-roam
, although I have not figured out very well how it works for my setup. For now I configure it to include my whole Org directory.
Capturing images
Using org-download
to make it easier to insert images into my org notes. I don’t like the configuration provided by Doom as part of the (org +dragndrop)
module, so I install the package by hand and configure it to my liking. I also define a new keybinding to paste an image from the clipboard, asking for the filename first.
Capturing links
org-mac-link
implements the ability to grab links from different Mac apps and insert them in the file. Bind C-c g
to call org-mac-grab-link
to choose an application and insert a link.
Tasks and agenda
Customize the agenda display to indent todo items by level to show nesting, and enable showing holidays in the Org agenda display.
Install and load some custom local holiday lists I’m interested in.
org-super-agenda provides great grouping and customization features to make agenda mode easier to use.
I configure org-archive
to archive completed TODOs by default to the archive.org
file in the same directory as the source file, under the “date tree” corresponding to the task’s CLOSED date - this allows me to easily separate work from non-work stuff. Note that this can be overridden for specific files by specifying the desired value of org-archive-location
in the #+archive:
property at the top of the file.
I have started using org-clock
to track time I spend on tasks. Often I restart Emacs for different reasons in the middle of a session, so I want to persist all the running clocks and their history.
GTD
I am trying out Trevoke’s org-gtd. I haven’t figured out my perfect workflow for tracking GTD with Org yet, but this looks like a very promising approach.
We define the corresponding Org-GTD capture templates.
I set up an advice before org-capture
to make sure org-gtd
and org-capture
are loaded, which triggers the setup of the templates above.
Exporting a Curriculum Vitae
I use ox-awesomecv
from Org-CV, to export my Curriculum Vitæ.
My ox-awesomecv
package is not yet merged into the main Org-CV distribution, so I install from my branch for now.
Publishing to LeanPub
I use LeanPub for self-publishing my books. Fortunately, it is possible to export from org-mode to both LeanPub-flavored Markdown and Markua, so I can use Org for writing the text and simply export it in the correct format and structure needed by Leanpub.
When I decided to use org-mode to write my books, I looked around for existing modules and code. Here are some of the resources I found:
- Description of ox-leanpub.el (GitHub repo) by Juan Reyero;
- Publishing a book using org-mode by Lakshmi Narasimhan;
- Publishing a Book with Leanpub and Org Mode by Jon Snader (from where I found the links to the above).
Building upon these, I developed a new ox-leanpub
package which you can find in MELPA (source at https://github.com/zzamboni/ox-leanpub), and which I load and configure below.
The ox-leanpub
module sets up Markua export automatically. I add the code for setting up the Markdown exporter too (I don’t use it, but just to keep an eye on any breakage):
I highly recommend using Markua rather than Markdown, as it is the format that Leanpub is guaranteed to support in the future, and where most of the new features are being developed.
With this setup, I can write my book in org-mode (I usually keep a single book.org
file at the top of my repository), and then call the corresponding “Book” export commands. The manuscript
directory, as well as the corresponding Book.txt
and other necessary files are created and populated automatically.
If you are interested in learning more about publishing to Leanpub with Org-mode, check out my book Publishing with Emacs, Org-mode and Leanpub.
Blogging with Hugo
ox-hugo is an awesome way to blog from org-mode. It makes it possible for posts in org-mode format to be kept separate, and it generates the Markdown files for Hugo. Hugo supports org files, but using ox-hugo has multiple advantages:
- Parsing is done by org-mode natively, not by an external library. Although goorgeous (used by Hugo) is very good, it still lacks in many areas, which leads to text being interpreted differently as by org-mode.
- Hugo is left to parse a native Markdown file, which means that many of its features such as shortcodes, TOC generation, etc., can still be used on the generated file.
Doom Emacs includes and configures ox-hugo
as part of its (:lang org +hugo)
module, so all that’s left is to configure some parameters to my liking.
I set org-hugo-use-code-for-kbd
so that I can apply a custom style to keyboard bindings in my blog.
Code for org-mode macros
Here I define functions which get used in some of my org-mode macros
The first is a support function which gets used in some of the following, to return a string (or an optional custom string) only if it is a non-zero, non-whitespace string, and nil
otherwise.
This function receives three arguments, and returns the org-mode code for a link to the Hammerspoon API documentation for the link
module, optionally to a specific function
. If desc
is passed, it is used as the display text, otherwise section.function
is used.
Split STR at spaces and wrap each element with the ~
char, separated by +
. Zero-width spaces are inserted around the plus signs so that they get formatted correctly. Envisioned use is for formatting keybinding descriptions. There are two versions of this function: “outer” wraps each element in ~
, the “inner” wraps the whole sequence in them.
Links to a specific section/function of the Lua manual.
Reformatting an Org buffer
I picked up this little gem in the org mailing list. A function that reformats the current buffer by regenerating the text from its internal parsed representation. Quite amazing.
Avoiding non-Org mode files
org-pandoc-import is a mode that automates conversions to/from Org mode as much as possible.
Reveal.js presentations
I use org-re-reveal
to make presentations. The functions below help me improve my workflow by automatically exporting the slides whenever I save the file, refreshing the presentation in my browser, and moving it to the slide where the cursor was when I saved the file. This helps keeping a “live” rendering of the presentation next to my Emacs window.
The first function is a modified version of the org-num--number-region
function of the org-num
package, but modified to only return the numbering of the innermost headline in which the cursor is currently placed.
The zz/refresh-reveal-prez
function makes use of the above to perform the presentation export, refresh and update. You can use it by adding an after-save hook like this (add at the end of the file):
Note #1: This is specific to my OS (macOS) and the browser I use (Brave). I will make it more generic in the future, but for now feel free to change it to your needs.
Note #2: the presentation must be already open in the browser, so you must run “Export to reveal.js -> To file and browse” (C-c C-e v b
) once by hand.
Coding
Tangle-on-save has revolutionized my literate programming workflow. It automatically runs org-babel-tangle
upon saving any org-mode buffer, which means the resulting files will be automatically kept up to date.
Some useful settings for LISP coding - smartparens-strict-mode
to enforce parenthesis to match. I map M-(
to enclose the next expression as in paredit
using a custom function. Prefix argument can be used to indicate how many expressions to enclose instead of just 1. E.g. C-u 3 M-(
will enclose the next 3 sexps.
Adding keybindings for some useful functions:
-
find-function-at-point
gets bound toC-c l g p
(grouped together with other “go to” functions bound by Doom) and toC-c C-f
(analog to the existingC-c f
) for faster access.
Some other languages I use.
- Elvish shell, with support for org-babel.
-
CFEngine policy files. The
cfengine3-mode
package is included with Emacs, but I also install org-babel support. - Graphviz for graph generation.
Other tools
- Trying out Magit’s multi-repository abilities. This stays in sync with the git repo list used by my chain:summary-status Elvish shell function by reading the file every time
magit-list-repositories
is called, usingdefadvice!
. I also customize the display to add theStatus
column. - I prefer to use the GPG graphical PIN entry utility. This is achieved by setting
epg-pinentry-mode
(epa-pinentry-mode
before Emacs 27) tonil
instead of the default'loopback
. - I find
iedit
absolutely indispensable when coding. In short: when you hitCtrl-;
, all occurrences of the symbol under the cursor (or the current selection) are highlighted, and any changes you make on one of them will be automatically applied to all others. It’s great for renaming variables in code, but it needs to be used with care, as it has no idea of semantics, it’s a plain string replacement, so it can inadvertently modify unintended parts of the code. - A useful macro (sometimes) for timing the execution of things. From StackOverflow.
- I’m still not fully convinced of running a terminal inside Emacs, but
vterm
is much nicer than any of the previous terminal emulators, so I’m giving it a try. I configure it so that it runs my favorite shell. Vterm runs Elvish flawlessly! - Add “unfill” commands to parallel the “fill” ones, bind
A-q
tounfill-paragraph
and rebindM-q
to theunfill-toggle
command, which fills/unfills paragraphs alternatively.
Experiments
Some experimental code to list functions which are not native-compiled. Sort of works but its very slow. This does not get tangled to my config.el, I just keep it here for reference.
5 Hammerspoon
This is my Hammerspoon configuration file.
This file is written in literate programming style using org-mode. See init.lua for the generated file. You can see this in a nicer format on my blog post My Hammerspoon Configuration, With Commentary.
If you want to learn more about Hammerspoon, check out my book Learning Hammerspoon!
General variables and configuration
Global log level. Per-spoon log level can be configured in each Install:andUse
block below.
I use hyper
, shift_hyper
and ctrl_cmd
as the modifiers for most of my key bindings, so I define them as variables here for easier use.
Set up an abbreviation for hs.drawing.color.x11
since I use it repeatedly later on.
Work’s logo, which I use in some of my Seal shortcuts later on.
Spoon Management
Set up SpoonInstall - this is the only spoon that needs to be manually installed (it is already there if you check out this repository), all the others are installed and configured automatically.
Configuration of my personal spoon repository, which contains Spoons that have not been merged in the main repo. See the descriptions at https://zzamboni.github.io/zzSpoons/.
I prefer sync notifications, makes them easier to read.
This is just a shortcut to make the declarations below look more readable, i.e. Install:andUse
instead of spoon.SpoonInstall:andUse
.
BetterTouchTool integration (experimental)
I’m currently working on a new BetterTouchTool.spoon which provides integration with the BetterTouchTool AppleScript API. This is in heavy development! See the configuration for the Hammer spoon in System and UI for an example of how to use it.
URL dispatching to site-specific browsers
The URLDispatcher spoon makes it possible to open URLs with different browsers. I have created different site-specific browsers using Epichrome, which allows me to keep site-specific bookmarks, search settings, etc. I also use the url_redir_decoders
parameter to rewrite some URLs before they are opened, both to redirect certain URLs directly to their corresponding applications (instead of going through the web browser) and to fix a bug I have experienced in opening URLs from PDF documents using Preview.
Window and screen manipulation
The WindowHalfsAndThirds spoon sets up multiple key bindings for manipulating the size and position of windows.
This was one of the first spoons I wrote, and I used it for window resizing until I discovered MiroWindowsManager (see below), which I started using now.
MiroWindowsManager allows more granular window resizing and movement. One thing to keep in mind is that this spoon uses the hs.grid
module internally. If you also use the WindowGrid
spoon (see below), make sure both spoons use the same grid size to avoid conflicts.
The WindowGrid spoon sets up a key binding (Hyper-g
here) to overlay a grid that allows resizing windows by specifying their opposite corners.
The WindowScreenLeftAndRight spoon sets up key bindings for moving windows between multiple screens.
The ToggleScreenRotation spoon sets up a key binding to rotate the external screen (the spoon can set up keys for multiple screens if needed, but by default it rotates the first external screen).
Organization and Productivity
Universal Archiving
The UniversalArchive spoon sets up a single key binding (Ctrl-Cmd-a
) to archive the current item in Evernote, Mail and Outlook.
Filing to Omnifocus
Note: I no longer use OmniFocus so the Spoon below is diabled, but this section is still here as an example.
The SendToOmniFocus spoon sets up a single key binding (Hyper-t
) to send the current item to OmniFocus from multiple applications. We use the fn
attribute of Install:andUse
to call a function which registers some of the Epichrome site-specific-browsers I use, so that the Spoon knows how to collect items from them.
Evernote filing and tagging
The EvernoteOpenAndTag spoon sets up some missing key bindings for note manipulation in Evernote. I no longer use Evernote for GTD, so I have it disabled for now.
Clipboard history
The TextClipboardHistory spoon implements a clipboard history, only for text items. It is invoked with Cmd-Shift-v
.
Note: This is disabled for the moment as I experiment with BetterTouchTool’s built-in clipboard history, which I have bound to the same key combination for consistency in my workflow.
System and UI
General Hammerspoon utilities
The BTT_restart_Hammerspoon
function sets up a BetterTouchTool widget which also executes the config_reload
action from the spoon. This gets assigned to the fn
config parameter in the configuration of the Hammer spoon below, which has the effect of calling the function with the Spoon object as its parameter.
This is still very manual - the uuid
parameter contains the ID of the BTT widget to configure, and for now you have to get it by hand from BTT and paste it here.
The Hammer spoon (get it? hehe) is a simple wrapper around some common Hammerspoon configuration variables. Note that this gets loaded from my personal repo, since it’s not in the official repository.
Caffeine: Control system/display sleep
The Caffeine spoon allows preventing the display and the machine from sleeping. I use it frequently when playing music from my machine, to avoid having to unlock the screen whenever I want to change the music. In this case we also create a function BTT_caffeine_widget
to configure the widget to both execute the corresponding function, and to set its icon according to the current state.
Colorize menubar according to keyboard layout
The MenubarFlag spoon colorizes the menubar according to the selected keyboard language or layout (functionality inspired by ShowyEdge). I use English, Spanish and German, so those are the colors I have defined.
Locating the mouse
The MouseCircle spoon shows a circle around the mouse pointer when triggered. I have it disabled for now because I have the macOS shake-to-grow feature enabled.
Finding colors
One of my original bits of Hammerspoon code, now made into a spoon (although I keep it disabled, since I don’t really use it). The ColorPicker spoon shows a menu of the available color palettes, and when you select one, it draws swatches in all the colors in that palette, covering the whole screen. You can click on any of them to copy its name to the clipboard, or cmd-click to copy its RGB code.
Homebrew information popups
I use Homebrew, and when I run brew update
, I often wonder about what some of the formulas shown are (names are not always obvious). The BrewInfo spoon allows me to point at a Formula or Cask name and press Hyper-b
or Hyper-c
(for Casks) to have the output of the info
command in a popup window, or the same key with Shift-Hyper
to open the URL of the Formula/Cask.
Displaying keyboard shortcuts
The KSheet spoon traverses the current application’s menus and builds a cheatsheet of the keyboard shortcuts, showing it in a nice popup window.
TimeMachine backup monitoring
The TimeMachineProgress spoon shows an indicator about the progress of the ongoing Time Machine backup. The indicator disappears when there is no backup going on.
Disabling Turbo Boost
The TurboBoost spoon shows an indicator of the CPU’s Turbo Boost status, and allows disabling/enabling. This requires Turbo Boost Switcher to be installed.
Unmounting external disks on sleep
The EjectMenu
spoon automatically ejects all external disks before the system goes to sleep. I use this to avoid warnings from macOS when I close my laptop and disconnect it from my hub without explicitly unmounting my backup disk before. I disable the menubar icon, which is shown by default by the Spoon.
Other applications
The HeadphoneAutoPause spoon implements auto-pause/resume for iTunes, Spotify and others when the headphones are unplugged. Note that this goes unused since I started using wireless headphones.
Seal application launcher/controller
The Seal spoon is a powerhouse. It implements a Spotlight-like launcher, but which allows for infinite configurability of what can be done or searched from the launcher window. I use Seal as my default launcher, triggered with Cmd-space
, although I still keep Spotlight around under Hyper-space
, mainly for its search capabilities.
We start by loading the spoon, and specifying which plugins we want.
The useractions
Seal plugin allows me to define my own shortcuts. For example, a bookmark to the Hammerspoon documentation page:
Or to manually trigger my work/non-work transition scripts (see below):
Or to translate things using dict.leo.org:
Network transitions
The WiFiTransitions spoon allows triggering arbitrary actions when the SSID changes. I am interested in the change from my work network (corpnet01) to other networks, mainly because at work I need a proxy for all connections to the Internet. I have two applications which don’t handle these transitions gracefully on their own: Spotify and Adium. So I have written a couple of functions for helping them along.
The reconfigSpotifyProxy
function quits Spotify, updates the proxy settings in its config file, and restarts it.
The reconfigAdiumProxy
function uses AppleScript to tell Adium about the change without having to restart it - only if Adium is already running.
Functions to stop applications that are disallowed in the work network.
The configuration for the WiFiTransitions spoon invoked these functions with the appropriate parameters.
Pop-up translation
I live in Switzerland, and my German is far from perfect, so the PopupTranslateSelection spoon helps me a lot. It allows me to select some text and, with a keystroke, translate it to any of three languages using Google Translate. Super useful! Usually, Google’s auto-detect feature works fine, so the translate_to_<lang>
keys are sufficient. I have some translate_<from>_<to>
keys set up for certain language pairs for when this doesn’t quite work (I don’t think I’ve ever needed them).
I am now testing DeepLTranslate, based on PopupTranslateSelection but which uses the DeepL translator (this is disabled because I have the DeepL app installed, which binds its own global hotkeys).
Leanpub integration
The Leanpub spoon provides monitoring of book build jobs. You can read more about how I use this in my blog post Automating Leanpub book publishing with Hammerspoon and CircleCI.
Showing application keybindings
The KSheet spoon provides for showing the keybindings for the currently active application.
Loading private configuration
In init-local.lua
I keep experimental or private stuff (like API tokens) that I don’t want to publish in my main config. This file is not committed to any publicly-accessible git repositories.
End-of-config animation
The FadeLogo spoon simply shows an animation of the Hammerspoon logo to signal the end of the config load.
If you don’t want to use FadeLogo, you can have a regular notification.
6 Elvish
This is my main config file for Elvish.
This file is written in literate programming style using org-mode. See rc.elv for the generated file. You can see this in a nicer format on my blog post My Elvish Configuration With Commentary.
Paths
First we set up the executable paths. We set the GOPATH
environment variable while we are at it, since we need to use it as part of the path.
I have a quick sanity check because sometimes certain paths disappear depending on new versions, etc. This prints a warning when opening a new shell, if there are any non-existing directories in $paths
.
Package installation
The bundled epm module allows us to install and manage Elvish packages.
For now I use these packages:
- github.com/zzamboni/elvish-modules contains all my modules except completions and themes. Maybe these should be separated eventually, but for now this works fine.
- github.com/zzamboni/elvish-themes contains my prompt themes (only chain for now).
- github.com/zzamboni/elvish-completions contains my completer definitions.
-
github.com/xiaq/edit.elv, which includes the
smart-matcher
module used below. - github.com/muesli/elvish-libs for the git utilities module.
- github.com/iwoloschin/elvish-packages for the update.elv package.
The modules within each package get loaded individually below.
Automatic proxy settings
When I am in the office, I need to use a proxy to access the Internet. For macOS applications, the proxy is set automatically using a company-provided PAC file. For the environment variables http_proxy
and https_proxy
, commonly used by command-line programs, the proxy module allows me to define a test which determines when the proxy should be used, so that the change is done automatically. We load this early on so that other modules which need to access the network get the correct settings already.
First, we load the module and set the proxy host.
Next, we set the test function to enable proxy auto-setting. In my case, the /etc/resolv.conf
file contains the corproot.net
domain (set through DHCP) when I’m in the corporate network, so I can check for that.
We run an initial check so that other commands in rc.org get the correctd settings already, even before the first prompt.
General modules and settings
Load the bundled re module to have access to regular expression functions.
The bundled readline-binding module associates some Emacs-like keybindings for manipulation of the command line.
I add a couple of keybindings which are missing from the default readline-binding
module:
-
Alt-backspace
to delete small-word -
Alt-d
to delete the small-word under the cursor - I also bind “instant preview mode” to
Alt-m
. This is useful to see the results of a command while you are typing it. - Limit the height of location and history mode so that they don’t cover the whole screen.
Aliases and miscellaneous functions
Elvish does not have built-in alias functionality, but this is implemented easily using the alias module, which stores the alias definitions as functions under ~/.elvish/aliases/ and loads them automatically.
For reference, I define here a few of my commonly-used aliases:
bat
man
(using bat
as the pager for man
pages).
Open man pages as PDF, I gathered this tip from https://twitter.com/MrAhmadAwais/status/1279066968981635075. Neat but not very useful for daily use, particularly with the bat
integration above.
Completions
The smart-matcher module tries prefix match, smart-case prefix match, substring match, smart-case substring match, subsequence match and smart-case subsequence match automatically.
Other possible values for edit:completion:matcher
are [p]{ edit:match-prefix &smart-case $p }
for smart-case completion (if your pattern is entirely lower case it ignores case, otherwise it’s case sensitive). &smart-case
can be replaced with &ignore-case
to make it always case-insensitive.
I also configure Tab
to trigger completion mode, but also to automatically enter “filter mode”, so I can keep typing the filename I want, without having to use the arrow keys. Disabled as this is the default behavior starting with commit b24e4a7, but you may need it if you are running an older version for any reason and want this behavior.
I load some command-specific completions from the elvish-completions package:
I configure the git completer to use hub
instead of git
(if you use plain git, you don’t need to call git:init
)
This is not usually necessary, but I load the comp
library specifically since I do a lot of tests and development of completions.
Prompt theme
I use the chain prompt theme, ported from the fish theme at https://github.com/oh-my-fish/theme-chain.
I set the color of the directory segment, the prompt chains and the prompt arrow in my prompt to a session-identifying color (a different color for each session).
Customize some of the glyphs for the font I use in my terminal. I use the Fira Code font which includes ligatures, so I disable the last chain, and set the arrow
segment to a combination of characters which shows up as a nice arrow.
Elvish has a comprehensive mechanism for displaying prompts with useful information while avoiding getting blocked by prompt functions which take too long to finish. For the most part the defaults work well. One change I like to make is to change the stale prompt transformer function to make the prompt dim when stale (the default is to show the prompt in inverse video):
Another possibility is to make the prompt stay the same when stale - useful to avoid distractions (disabled for now):
I also like the continuous update of the prompt as I type (by default it only updates on Enter and on $pwd
changes, but I like also git status changes to be updated automatically), so I increase its eagerness.
iTerm2 shell integration support
The iterm2
module provides support for iTerm2’s Shell Integration features. Note that iterm2:init
must be called after setting up the prompt, hence this is done after loading the chain
module above.
Long-running-command notifications
The long-running-notifications module allows for producing a notification when a command takes longer than a certain time to finish (by default the period is 10 seconds). The module automatically detects when terminal-notifier is available on macOS and uses it to produce Mac-style notifications, otherwise it prints a notification on the terminal.
Directory and command navigation and history
Elvish comes with built-in location and command history modes, and these are the main mechanism for accessing prior directories and commands. The weight-keeping in location mode makes the most-used directories automatically raise to the top of the list over time.
I have decades of muscle memory using !!
and !$
to insert the last command and its last argument, respectively. The bang-bang module allows me to keep using them.
The dir module implements a directory history and some related functions. I alias the cd
command to dir:cd
so that any directory changes are kept in the history. I also alias cdb
to dir:cdb
function, which allows changing to the base directory of the argument.
dir
also implements a custom directory history chooser, which I bind to Alt-i
(I have found I don’t use this as much as I thought I would - the built-in location mode works nicely).
I bind Alt-b/f
to dir:left-small-word-or-prev-dir
and dir:right-small-word-or-next-dir
respectively, which “do the right thing” depending on the current content of the command prompt: if it’s empty, they move back/forward in the directory history, otherwise they move through the words of the current command. In my Terminal.app setup, Alt-left/right
also produce Alt-b/f
, so these bindings work for those keys as well.
The following makes the location and history modes be case-insensitive by default:
I use exa as a replacement for the ls
command, so I alias ls
to it. Unfortunately, exa
does not understand the -t
option to sort files by modification time, so I explicitly look for the -lrt
option combination (which I use very often, and it always trips me off) and replace them with the correct options for exa
. All other options are passed as-is.
Dynamic terminal title
The terminal-title module handles setting the terminal title dynamically according to the current directory or the current command being executed.
Loading private settings
The private
module sets up some private settings such as authentication tokens. This is not on github :) The $private-loaded
variable gets set to $ok
if the module was loaded correctly.
O’Reilly Atlas
I sometimes use the O’Reilly Atlas publishing platform. The atlas module contains some useful functions for triggering and accessing document builds.
OpsGenie
I used OpsGenie at work for a while, so I put together the opsgenie library to make API operations easier. I don’t actively use or maintain this anymore.
LeanPub
I use LeanPub for publishing my books, so I have written a few utility functions. I don’t use this regularly, I have much better integration using Hammerspoon and CircleCI, I wrote about it in my blog: Automating Leanpub book publishing with Hammerspoon and CircleCI.
TinyTeX
Tiny module with some utility functions for using TinyTeX.
Environment variables
Default options to less
.
Use vim as the editor from the command line (although I am an Emacs fan, I still sometimes use vim for quick editing).
Locale setting.
PKGCONFIG configuration
Utility functions
The util module includes various utility functions.
I use muesli’s git utilities module.
The update.elv package prints a message if there are new commits in Elvish after the running version.
Set up electric delimiters in the command line.
ASCII spinners and TTY escape code generation.
Work-specific stuff
I have a private library which contains some work-specific functions.
Exporting aliases
We populate $-exports-
with the alias definitions so that they become available in the interactive namespace.