I have enjoyed slowly converting my configuration files to literate programming style style using org-mode in Emacs. I previously posted my Elvish configuration, and now it’s the turn of my Emacs configuration file. The text below is included directly from my init.org file. Please note that the text below is a snapshot as the file stands as of the date shown above, but it is always evolving. See the init.org file in GitHub for my current, live configuration, and the generated file at init.el.
If you are interested in writing your own Literate Config files, check out my new book Literate Config on Leanpub!
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.
First, we wrap the whole init file in a block that sets file-name-handler-alist to nil to prevent any special-filename parsing of files loaded from the init file (e.g. remote files loaded through tramp, etc.). The let block gets closed in the Epilogue.
Next, a hook that reports how long and how many garbage collections the startup took. We use a hook to run it at the very end, so the message doesn’t get clobbered by other messages during startup.
(add-hook'emacs-startup-hook(lambda()(message"Emacs ready in %s with %d garbage collections."(format"%.2f seconds"(float-time(time-subtractafter-init-timebefore-init-time)))gcs-done)))
Optionally enable debug-on-error - I do this only when I’m trying to figure out some problem in my config.
;;(setq debug-on-error t)
If the gcmh package is already installed, load and enable it early. If not, this gets installed a bit later in the Package Management section. This package manages the garbage collection thresholds and scheduling to improve performance. DISABLED for now because it seems to cause performance degradation when inserting text in long files.
;; (when (require 'gcmh nil t);; (gcmh-mode 1))
We set gc-cons-threshold to its maximum value, to prevent any garbage collection from happening during load time. We also reset this value in the Epilogue.
Emacs has its own Customization mechanism for easily customizing many parameters. To make it easier to manage, I keep the customized variables and faces in a separate file and load it from the main file. A lot of my custom settings are configured from this init file as well, but there are always some which I change by hand for added flexibility.
I use the wonderful use-package to manage most of the packages in my installation (one exception is org-mode, see below). As this is not bundled yet with Emacs, the first thing we do is install it by hand. All other packages are then declaratively installed and configured with use-package. This makes it possible to fully bootstrap Emacs using only this config file, everything else is downloaded, installed and configured automatically.
First, we declare the package repositories to use.
The use-package-always-ensure variable indicates that use-package should always try to install missing packages. For some libraries this is not appropriate, and in those cases you see the :ensure nil declaration as part of the use-package statement. This applies mainly to libraries which are installed as part of some other package (happens mostly with some libraries that come with org-mode).
The use-package-always-defer sets :defer true as the default for all package declarations. This makes Emacs startup much faster by preventing packages from being loaded when Emacs starts, and only doing so when they are needed. Some packages don’t work well with this, so you’ll see some declarations when I explicitly set :defer nil to force the package to be loaded at startup, or :defer n to load the package, but only n seconds after startup.
The use-package-verbose variable enables verbose loading of packages, useful for debugging. I set/unset this according to need.
Testing quelpa and to install packages directly from their github repositories (and other places). I install quelpa using use-package first, and then install quelpa-use-package to allow using quelpa from within use-package declarations. Very recursive.
Set the load path to the directories from where I sometimes load things outside the package system. Note that the path for specific packages like org-mode (which I load from a checkout of its git repository) is set as part of their use-package declarations, so they don’t appear here.
Giving a try to Paradox for an enhanced package management interface. We set paradox-github-token to t to disable GitHub integration (I don’t want to star repos).
Password management using auth-sources and pass (I normally use 1Password, but I have not found a good command-line/Emacs interface for it, so I am using pass for now for some items I need to add to my Emacs config file).
These are two short functions I wrote to be able to set/unset proxy settings within Emacs. I haven’t bothered to improve or automate this, as I pretty much only need it to be able to install packages sometimes when I’m at work. For now I just call them manually with M-x zz/(un)set-proxy when I need to.
Load the cl library to enable some additional macros (e.g. lexical-let).
Install and load the async package to enable asynchronous operations (this gets loaded by some other packages, but I use it explicitly in zz/org-babel-async-tangle below, so I load it explicitly).
Start the Emacs server
This is probably one of my oldest settings - I remember adding it around 1993 when I started learning Emacs, and it has been in my config ever since. When time-stamp is run before every save, the string Time-stamp: <> in the first 8 lines of the file will be updated with the current timestamp.
When at the beginning of the line, make Ctrl-K remove the whole line, instead of just emptying it.
Paste text where the cursor is, not where the mouse is.
Show line numbers. I used linum-mode before, but it caused severe performance issues on large files. Emacs 26 introduces display-line-numbers-mode, which has no perceivable performance impact even on very large files. Disabled for now.
The which-key package makes Emacs functionality much easier to discover and explore: in short, after you start the input of a command and stop, pondering what key must follow, it will automatically open a non-intrusive buffer at the bottom of the screen offering you suggestions for completing the command. Extremely useful.
I use the bind-key package to more easily keep track and manage user keybindings. bind-key comes with use-package so we just load it.
The main advantage of using this over define-key or global-set-key is that you can use M-xdescribe-personal-keybindings to see a list of all the customized keybindings you have defined.
M-g interactively asks for a line number and jump to it (goto-line).
M-` focuses the next frame, if multiple ones are active (emulate the Mac “next app window” keybinding)
Interactive search key bindings - visual-regexp-steroids provides sane regular expressions and visual incremental search. We make C-s and C-r run the visual-regexp functions. We leave C-M-s and C-M-r to run the default isearch-forward/backward functions, as a fallback. I use the pcre2el package to support PCRE-style regular expressions.
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 the functionality. Inspired by http://www.emacswiki.org/emacs/NavigatingParentheses, but modified to use smartparens instead of the default commands, and to work on brackets and braces.
(defunzz/goto-match-paren(arg)"Go to the matching paren/bracket, otherwise (or if ARG is not
nil) insert %. vi style of % jumping to matching brace."(interactive"p")(if(not(memqlast-command'(set-markcua-set-markzz/goto-match-parendown-listup-listend-of-defunbeginning-of-defunbackward-sexpforward-sexpbackward-up-listforward-paragraphbackward-paragraphend-of-bufferbeginning-of-bufferbackward-wordforward-wordmwheel-scrollbackward-wordforward-wordmouse-start-secondarymouse-yank-secondarymouse-secondary-save-then-killmove-end-of-linemove-beginning-of-linebackward-charforward-charscroll-upscroll-downscroll-leftscroll-rightmouse-set-pointnext-bufferprevious-bufferprevious-linenext-lineback-to-indentation)))(self-insert-command(orarg1))(cond((looking-at"\\s\(")(sp-forward-sexp)(backward-char1))((looking-at"\\s\)")(forward-char1)(sp-backward-sexp))(t(self-insert-command(orarg1))))))
We bind this function to the % key.
I have started using org-mode to writing, blogging, coding, presentations and more, thanks to the hearty recommendations and information from Nick and many others. 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 many of my homeworks and projects), but org-mode is the first tool I have encountered which seems to make it practical. Here are some of the resources I have found useful in learning it:
This is the newest and most-in-flux section of my Emacs config, since I’m still learning org-mode myself.
I use use-package to load the org package, and put its configuration inside the corresponding sections for keybindings (:bind), custom variables (:custom), custom faces (:custom-face), hooks (:hook) and general configuration code (:config), respectively. The contents of each section is populated with the corresponding snippets that follow. See the sections below for the details on what goes into each configuration section, and some other configuration code that ends up outside this declaration.
Note that mode-specific configuration variables are defined under their corresponding packages, this section defines only global org-mode configuration variables, which are inserted in the main use-package declaration for org-mode.
Default directory for org files (not all are stored here).
Automatically log done times in todo items.
Keep the indentation well structured by setting org-startup-indented to t. This is a must have. Makes it feel less like editing a big text file and more like a purpose built editor for org-mode that forces the indentation. Thanks Nick for the tip!
By default, org-indent produces an indicator "Ind" in the modeline. We use diminish to hide it. I also like to increase the indentation a bit so that the levels are more visible.
Note that other keybindings are configured under their corresponding packages, this section defines only global org-mode keybindings, which are inserted in the main use-package declaration for org-mode.
Use the special C-a, C-e and C-k definitions for Org, which enable some special behavior in headings.
Set up C-c l to store a link to the current org object, in counterpart to the default C-c C-l to insert a link.
The default keybinding for org-mark-element is M-h, which in macOS hides the current application, so I bind it to A-h.
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 (useful with the font highlighting I use, as all but the last asterisk are sometimes not visible).
I define a helper function to define keybindings that open files. Since I use the which-key package, it also defines the description of the key that will appear in the which-key menu. Note the use of lexical-let so that the lambda creates a closure, otherwise the keybindings don’t work.
Now I define keybindings to access my commonly-used org files, and add them to org-agenda-files
(custom-set-variables'(org-agenda-files'("~/Work/work.org.gpg""~/org/ideas.org""~/org/projects.org""~/org/diary.org")))(zz/add-file-keybinding"C-c f w""~/Work/work.org.gpg""work.org")(zz/add-file-keybinding"C-c f i""~/org/ideas.org""ideas.org")(zz/add-file-keybinding"C-c f p""~/org/projects.org""projects.org")(zz/add-file-keybinding"C-c f d""~/org/diary.org""diary.org")
org-capture provides a generic and extensible interface to capturing things into org-mode in different formats. I set up C-c c as the default keybinding for triggering org-capture. Usually setting up a new capture template requires some custom code, which gets defined in the corresponding package config sections and included in the :config section below.
Define all my org agenda files as targets for refiling.
(setqorg-refile-targets'((nil:maxlevel.9)(org-agenda-files:maxlevel.9)))(setqorg-outline-path-complete-in-stepsnil); Refile in a single go(setqorg-refile-use-outline-patht); Show full paths for refiling
Org-Agenda is the umbrella for all todo, journal, calendar, and other views. I set up C-c a to call up agenda mode.
(use-packageorg-agenda:ensurenil:afterorg:bind("C-c a".org-agenda):custom(org-agenda-include-diaryt)(org-agenda-prefix-format'((agenda." %i %-12:c%?-12t% s");; Indent todo items by level to show nesting(todo." %i %-12:c%l")(tags." %i %-12:c")(search." %i %-12:c")))(org-agenda-start-on-weekdaynil))
I also provide some customization for the holidays package, since its entries are included in the Org Agenda through the org-agenda-include-diary integration.
(quelpa'(swiss-holidays:fetchergithub:repo"egli/swiss-holidays"))(require'swiss-holidays)(setqswiss-holidays-zh-city-holidays'((holiday-float413"Sechseläuten");; meistens dritter Montag im April(holiday-float913"Knabenschiessen")));; zweites Wochenende im September
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.
(use-packageorg-edna:defernil)(use-packageorg-gtd:defernil:afterorg:load-path"lisp/org-gtd.el":config(require'org-gtd);; these are the interactive functions you're likely to want to use as you go about GTD.(global-set-key(kbd"C-c d c")'org-gtd-capture);; add item to inbox(global-set-key(kbd"C-c d p")'org-gtd-process-inbox);; process entire inbox(global-set-key(kbd"C-c d a")'org-agenda-list);; see what's on your plate today(global-set-key(kbd"C-c d n")'org-gtd-show-all-next);; see all NEXT items(global-set-key(kbd"C-c d s")'org-gtd-show-stuck-projects);; see projects that don't have a NEXT item(add-to-list'org-agenda-filesorg-gtd-directory);; package: https://www.nongnu.org/org-edna-el/;; org-edna is used to make sure that when a project task gets DONE,;; the next TODO is automatically changed to NEXT.(setqorg-edna-use-inheritancet)(org-edna-load);; package: https://github.com/Malabarba/org-agenda-property;; this is so you can see who an item was delegated to in the agenda(setqorg-agenda-property-list'("DELEGATED_TO"));; I think this makes the agenda easier to read(setqorg-agenda-property-position'next-line))
(add-to-list'org-capture-templates'("i""GTD item"entry(file(lambda()(org-gtd--pathorg-gtd-inbox-file-basename)))"* %?\n%U\n\n %i":kill-buffert))(add-to-list'org-capture-templates'("l""GTD item with link to where you are in emacs now"entry(file(lambda()(org-gtd--pathorg-gtd-inbox-file-basename)))"* %?\n%U\n\n %i\n %a":kill-buffert))
I’m testing a new library called org-roam for non-hierarchical note taking.
(use-packageorg-roam:after(orgemacsqlemacsql-sqlite):load-path"lisp/org-roam":diminish:hook((org-mode.org-roam-mode)(after-init.org-roam--build-cache-async)):custom(org-roam-directory"~/org"):bind("C-c n l".org-roam)("C-c n t".org-roam-today)("C-c n f".org-roam-find-file)("C-c n i".org-roam-insert)("C-c n g".org-roam-show-graph))
(use-packagedeft:afterorg:bind("C-c n d".deft):custom(deft-directoryorg-directory)(deft-recursivet)(deft-use-filename-as-titlenil)(deft-use-filter-string-for-filenamet)(deft-file-naming-rules'((noslash."-")(nospace."-")(case-fn.downcase)))(deft-org-mode-title-prefixt)(deft-extensions'("org""txt""text""md""markdown""org.gpg"))(deft-default-extension"org"))
Using org-download to make it easier to insert images into my org notes.
org-reveal is an awesome package for building presentations with org-mode. The MELPA version of the package gives me a conflict with my hand-installed version of org-mode, so I also install it by hand and load it directly from its checked-out repository.
Some customizations for the LaTeX exporter. ox-latex gets loaded automatically, but we use use-package anyway so that the config code is only executed after the package is loaded. I add a pseudo-class which uses the document class book but without parts (only chapters at the top level).
I use ox-org to generate an org file from another. For example, the README.org file for my elvish-modules package is generated by exporting from README-src.org, to automatically extract summaries from the different module files.
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.
I am intrigued by ox-hugo’s “one post per org subtree” proposed structure. So far I’ve always had one file per post, but with org-mode’s structuring features, it might make sense to give it a try.
(use-packageox-hugo:defer3:afterorg;; Testing hooks to automatically set the filename on an ox-hugo;; blog entry when it gets marked as DONE;; :hook;; (org-mode . (lambda ();; (add-hook 'org-after-todo-state-change-hook;; (lambda ();; (org-set-property;; "testprop";; (concat "org-state: " org-state;; " prev-state: " (org-get-todo-state))));; 'run-at-end 'only-in-org-mode))):custom(org-hugo-use-code-for-kbdt))
(defunorg-hugo-new-subtree-post-capture-template()"Returns `org-capture' template string for new Hugo post.
See `org-capture-templates' for more information."(let*((title(read-from-minibuffer"Post Title: "))(fname(org-hugo-slugtitle)))(mapconcat#'identity`(,(concat"* TODO "title)":PROPERTIES:",(concat":EXPORT_HUGO_BUNDLE: "fname)":EXPORT_FILE_NAME: index"":END:""%?\n"); Place the cursor here finally"\n")))(add-to-list'org-capture-templates'("z";`org-capture' binding + z"zzamboni.org post"entry;; It is assumed that below file is present in `org-directory';; and that it has an "Ideas" heading. It can even be a;; symlink pointing to the actual location of all-posts.org!(file+olp"zzamboni.org""Ideas")(functionorg-hugo-new-subtree-post-capture-template)))
First, load the built-in EasyPG support. By calling (epa-file-enable), Emacs automatically encrypts/decrypts files with a .gpg extension. By default it asks about the key to use, but I configure it to always use my own GPG key.
(use-packageepa-file:ensurenil;; included with Emacs:config(setqepa-file-encrypt-to'("firstname.lastname@example.org")); (epa-file-enable):custom(epa-file-select-keys'silent))
Then, load org-crypt to enable selective encryption/decryption using GPG within org-mode.
(use-packageorg-crypt:ensurenil;; included with org-mode:afterorg:config(org-crypt-use-before-save-magic)(setqorg-tags-exclude-from-inheritance(quote("crypt"))):custom(org-crypt-key"email@example.com"))
Keeping a Journal
I use 750words for my personal Journal, and I used to write my entries locally using Scrivener. Now I am using org-journal for this, works quite well together with wc-mode to keep a count of how many words I have written.
In order to keep my journal entries encrypted there are two separate but confusingly named mechanisms:
org-journal-encrypt-journal, if set to t has the effect of transparently encrypting/decrypting the journal files as they are written to disk. This is what I use.
org-journal-enable-encryption, if set to t, enables integration with org-crypt (see above), so it automatically adds a :crypt: tag to new journal entries. This has the effect of automatically encrypting those entries upon save, replacing them with a blob of gpg-encrypted text which has to be further decrypted with org-decrypt-entry in order to read or edit them again. I have disabled it for now to make it more transparent to work with my journal entries while I am editing them.
Org-mode is the first literate programming tool that seems practical and useful, since it’s easy to edit, execute and document code from within the same tool (Emacs) using all of its existing capabilities (i.e. each code block can be edited in its native Emacs mode, taking full advantage of indentation, completion, etc.)
First, we load the necessary programming language support. The base features and literate programming for Emacs LISP is built-in, but the ob-* packages provide the ability to execute code in different languages directly from within the Org buffer, beyond those included with org-mode. I load the modules for some of the languages I use frequently:
We determine the location of the PlantUML jar file automatically from the installed Homebrew formula.
brew list plantuml | grep jar
Which in my current setup results in the following:
The command defined above is used to define the value of the homebrew-plantuml-jar-path variable. If you don’t use Homebrew of have installed PlantUML some other way, you need to modify this command, or hard-code the path.
(require'subr-x)(setqhomebrew-plantuml-jar-path(expand-file-name(string-trim(shell-command-to-string"brew list plantuml | grep jar"))))
Finally, we use this value to configure both plantuml-mode (for syntax highlighting) and ob-plantuml (for evaluating PlantUML code and inserting the results in exported Org documents).
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. For a long time I simply had the following hook:
This is simple and it works, the only disadvantage is that it runs the tangle process synchronously, so Emacs freezes until the org-babel-tangle command is done. For large files (such as this one), the delay is noticeable, so I also had some hooks to measure and report the tangle time:
(defunzz/report-tangle-time(start-time)(message"org-babel-tangle took %s"(format"%.2f seconds"(float-time(time-sincestart-time)))))
Thanks to the kind help of Ihor in the emacs-orgmode mailing list, I now have an asynchronous version of this, which dispatches the tangle function to a subprocess, so that the main Emacs is not blocked while it runs. The zz/org-babel-tangle-async function uses the emacs-async package to start the tangle operation in a child process. Note that the child Emacs started by async-start is empty, without any configuration, so we need to load org before tangling. Depending on your setup, you may need to load more configuration.
(defunzz/org-babel-tangle-async(file)"Invoke `org-babel-tangle-file' asynchronously."(message"Tangling %s..."(buffer-file-name))(async-start(let((args(listfile)))`(lambda()(require'org);;(load "~/.emacs.d/init.el")(let((start-time(current-time)))(apply#'org-babel-tangle-file',args)(format"%.2f"(float-time(time-sincestart-time))))))(let((message-string(format"Tangling %S completed after "file)))`(lambda(tangle-time)(message(concat,message-string(format"%s seconds"tangle-time)))))))(defunzz/org-babel-tangle-current-buffer-async()"Tangle current buffer asynchronously."(zz/org-babel-tangle-async(buffer-file-name)))
Finally, we set up an org-mode hook which adds the async tangle function to the after-save-hook, so that it happens automatically after every save. Disabled for now because the tangle is getting interrupted sometimes when I move the cursor before the async tangle finishes, leaving files incomplete.
This is potentially dangerous: it suppresses the query before executing code from within org-mode. I use it because I am very careful and only press C-c C-c on blocks I absolutely understand.
This makes it so that code within src blocks is fontified according to their corresponding Emacs mode, making the file much more readable.
In principle this makes it so that indentation in src blocks works as in their native mode, but in my experience it does not always work reliably. For full proper indentation, always edit the code in a native buffer by pressing C-c '.
Automatically show inline images, useful when executing code that produces them, such as PlantUML or Graphviz.
These settings make org-mode much more readable by using different fonts for headings, hiding some of the markup, etc. This was taken originally from Howard Abrams’ Org as a Word Processor, and subsequently tweaked and broken up in the different parts of the use-package declaration by me.
First, we set org-hid-emphasis-markers so that the markup indicators are not shown.
We add an entry to the org-mode font-lock table so that list markers are shown with a middle dot instead of the original character.
Show symbols when the cursor is over of right after them.
Second, we define a special face for checked items.
(deffaceorg-checkbox-done-text'((t(:foreground"#71696A":strike-throught)))"Face for the text part of a checked org-mode checkbox.")(font-lock-add-keywords'org-mode`(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\[\\(?:X\\|\\([0-9]+\\)/\\2\\)\\][^\n]*\n\\)"1'org-checkbox-done-textprepend))'append)
We choose a nice font for the document title and the section headings. The first one found in the system from the list below is used, and the same font is used for the different levels, in varying sizes.
(let*((variable-tuple(cond((x-list-fonts"ETBembo")'(:font"ETBembo"))((x-list-fonts"Source Sans Pro")'(:font"Source Sans Pro"))((x-list-fonts"Lucida Grande")'(:font"Lucida Grande"))((x-list-fonts"Verdana")'(:font"Verdana"))((x-family-fonts"Sans Serif")'(:family"Sans Serif"))(nil(warn"Cannot find a Sans Serif Font."))))(base-font-color(face-foreground'defaultnil'default))(headline`(:inheritdefault:weightbold:foreground,base-font-color)))(custom-theme-set-faces'user`(org-level-8((t(,@headline,@variable-tuple))))`(org-level-7((t(,@headline,@variable-tuple))))`(org-level-6((t(,@headline,@variable-tuple))))`(org-level-5((t(,@headline,@variable-tuple))))`(org-level-4((t(,@headline,@variable-tuple:height1.1))))`(org-level-3((t(,@headline,@variable-tuple:height1.25))))`(org-level-2((t(,@headline,@variable-tuple:height1.5))))`(org-level-1((t(,@headline,@variable-tuple:height1.75))))`(org-headline-done((t(,@headline,@variable-tuple:strike-throught))))`(org-document-title((t(,@headline,@variable-tuple:height2.0:underlinenil))))))
Fonts and wrapping
I use proportional fonts in org-mode for the text, while keeping fixed-width fonts for blocks, so that source code, tables, etc. are shown correctly. These settings include:
Configure org-indent to inherit from fixed-pitch to fix the vertical spacing in code blocks. Thanks to Ben for the tip!
Configure org-fontify-done-headline to apply a special face to DONE items in org-mode, and configure the org-done face to be used. Note that org-done only applies to the “DONE” keyword itself, the face for the rest of a “done” headline is defined above as the org-headline-done face.
Configuring the corresponding org-mode faces for blocks, verbatim code, and maybe a couple of other things. As these change more frequently, I do them directly from the customize-face interface, you can see their current settings in the Customized variables section.
Setting up visual-line-mode and making all my paragraphs one single line, so that the lines wrap around nicely in the window according to their proportional-font size, instead of at a fixed character count, which does not work so nicely when characters have varying widths. I set up a hook that automatically enables visual-line-mode and variable-pitch-mode when entering org-mode.
Turns out visual-line-mode also remaps the C-a and C-e keybindings (of course, which breaks the behavior enabled by the org-special-ctrl-a/e/k variables. To counter this, I also add some bindings that set those keys to their Org functions. These functions know how to deal with visual mode anyway.
In variable-pitch mode, the default right-alignment for headline tags doesn’t work, and results in the tags being misaligned (as it uses character positions to do the alignment). This setting positions the tags right after the last character of the headline, so at least they are more consistent.
I also set org-todo-keyword-faces to highlight different types of org-mode TODO items with different colors.
I’m experimenting with some settings based on Ricing up Org Mode, particularly for using when writing, to avoid distractions. For now these are contained within a function that I can call to enable them, to give me a chance to experiment.
(defunzz/write()(interactive);; Line spacing(setqline-spacing0.1);; Top padding(setqheader-line-format" ");; Hide modeline(hide-mode-line-mode);;(setq mode-line-format nil);; Side padding(setqleft-margin-width2)(setqright-margin-width2)(set-window-buffernil(current-buffer)))
The following code (by Rasmus) prettifies org-mode’s source blocks by replacing the #+begin/end_src keywords and the header arguments with symbols. In my config, the following code:
(defvarzzamboni/test-symbol?✎"This is a test symbol")
Looks like this:
When the cursor is over or next to one of the symbols, it gets expanded into its text representation to make editing easier. This is enabled by setting prettify-symbols-unprettify-at-point to 'right-edge:
(with-eval-after-load'org(defvar-localrasmus/org-at-src-begin-1"Variable that holds whether last position was a ")(defvarrasmus/ob-header-symbol?☰"Symbol used for babel headers")(defunrasmus/org-prettify-src--update()(let((case-fold-searcht)(re"^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")found)(save-excursion(goto-char(point-min))(while(re-search-forwardrenilt)(goto-char(match-end0))(let((args(org-trim(buffer-substring-no-properties(point)(line-end-position)))))(when(org-string-nw-pargs)(let((new-cell(consargsrasmus/ob-header-symbol)))(cl-pushnewnew-cellprettify-symbols-alist:test#'equal)(cl-pushnewnew-cellfound:test#'equal)))))(setqprettify-symbols-alist(cl-set-differenceprettify-symbols-alist(cl-set-difference(cl-remove-if-not(lambda(elm)(eq(cdrelm)rasmus/ob-header-symbol))prettify-symbols-alist)found:test#'equal)));; Clean up old font-lock-keywords.(font-lock-remove-keywordsnilprettify-symbols--keywords)(setqprettify-symbols--keywords(prettify-symbols--make-keywords))(font-lock-add-keywordsnilprettify-symbols--keywords)(while(re-search-forwardrenilt)(font-lock-flush(line-beginning-position)(line-end-position))))))(defunrasmus/org-prettify-src()"Hide src options via `prettify-symbols-mode'.
`prettify-symbols-mode' is used because it has uncollpasing. It's
may not be efficient."(let*((case-fold-searcht)(at-src-block(save-excursion(beginning-of-line)(looking-at"^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*"))));; Test if we moved out of a block.(when(or(andrasmus/org-at-src-begin(notat-src-block));; File was just opened.(eqrasmus/org-at-src-begin-1))(rasmus/org-prettify-src--update));; Remove composition if at line; doesn't work properly.;; (when at-src-block;; (with-silent-modifications;; (remove-text-properties (match-end 0);; (1+ (line-end-position));; '(composition))))(setqrasmus/org-at-src-beginat-src-block)));; This function helps to produce a single glyph out of a;; string. The glyph can then be used in prettify-symbols-alist.;; This function was provided by Ihor in the org-mode mailing list.(defunyant/str-to-glyph(str)"Transform string into glyph, displayed correctly."(let((compositionnil))(dolist(char(string-to-liststr)(nreverse(cdrcomposition)))(pushcharcomposition)(push'(Br.Bl)composition))))(defunrasmus/org-prettify-symbols()(mapc(apply-partially'add-to-list'prettify-symbols-alist)(cl-reduce'append(mapcar(lambda(x)(listx(cons(upcase(carx))(cdrx))))`(("#+begin_src".?⎡);; ⎡ ➤ 🖝 ➟ ➤ ✎;; multi-character strings can be used with something like this:;; ("#+begin_src" . ,(yant/str-to-glyph "```"))("#+end_src".?⎣);; ⎣ ✐("#+header:".,rasmus/ob-header-symbol)("#+begin_quote".?«)("#+end_quote".?»)))))(turn-on-prettify-symbols-mode)(add-hook'post-command-hook'rasmus/org-prettify-srctt))(add-hook'org-mode-hook#'rasmus/org-prettify-symbols))
Auto-generated table of contents
The toc-org package allows us to insert a table of contents in headings marked with :TOC:. This is useful for org files that are to be viewed directly on GitHub, which renders org files correctly, but does not generate a table of contents at the top. For an example, see this file on GitHub.
Note that this breaks HTML export by default, as the links generated by toc-org cannot be parsed properly by the html exporter. The workaround is to use :TOC:noexport: as the marker, which removed the generated TOC from the export, but still allows ox-html to insert its own TOC at the top.
org-mac-link (included in contrib) 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.
(use-packageorg-mac-link:ensurenil:load-path"lisp/org-mode/contrib/lisp":afterorg:custom(org-mac-grab-Acrobat-app-pnil"Disable grabbing from Adobe Acrobat")(org-mac-grab-devonthink-app-pnil"Disable grabbinb from DevonThink"):bind(:maporg-mode-map("C-c g".org-mac-grab-link)))
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.
(defunzz/org-reformat-buffer()(interactive)(when(y-or-n-p"Really format current buffer? ")(let((document(org-element-interpret-data(org-element-parse-buffer))))(erase-buffer)(insertdocument)(goto-char(point-min)))))
(defunafs/org-remove-link()"Replace an org link by its description or if empty its address"(interactive)(if(org-in-regexporg-bracket-link-regexp1)(let((remove(list(match-beginning0)(match-end0)))(description(if(match-end3)(org-match-string-no-properties3)(org-match-string-no-properties1))))(apply'delete-regionremove)(insertdescription))))(bind-key"C-c C-M-u"'afs/org-remove-link)
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.
I use LeanPub for self-publishing my books. Fortunately, it is possible to export from org-mode to both LeanPub-flavored Markdown and Markua, the new preferred Leanpub markup format, 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:
I highly recommend using Markua rather than Markdown, as it is the future 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.
Miscellaneous org functions and configuration
Utility org-get-keyword function (from the org-mode mailing list) to get the value of file-level properties.
org-sidebar provides a configurable sidebar to org buffers, showing the agenda, headlines, etc.
Appearance, buffer/file management and theming
Here we take care of all the visual, UX and desktop-management settings.
You’ll notice that many of the packages in this section have :defer nil. This is because some of these package are never called explicitly because they operate in the background, but I want them loaded when Emacs starts so they can perform their necessary customization.
Emacs 26 (which I am trying now) introduces pixel-level scrolling.
Enable desktop-save mode, which saves the current buffer configuration on exit and reloads it on restart.
Desktop mode also includes the desktop-clear function, which can be used to kill all open buffers. I bind it to Control-Meta-super-k.
(use-packagedesktop:defernil:custom(desktop-restore-eager1"Restore the first buffer right away")(desktop-lazy-idle-delay1"Restore the other buffers 1 second later")(desktop-lazy-verbosenil"Be silent about lazily opening buffers"):bind("C-M-s-k".desktop-clear):config(desktop-save-mode))
The uniquify package makes it much easier to identify different open files with the same name by prepending/appending their directory or some other information to them. I configure it to add the directory name after the filename. uniquify is included with Emacs, so I specify :ensure nil so that use-package doesn’t try to install it, and just loads and configures it.
I also provide a custom value for hl-line-range-function (thanks to Eric on the org-mode mailing list for the tip) which highlights only the current visual line in visual-line-mode, which I use for Org-mode files (see Beautifying org-mode).
I have also experimented with highlighting the current column. At the moment the code below is all disabled because I find it too distracting, but I’m leaving it here for reference. I found two options to achieve this:
The col-highlight package, which highlights the column only after a defined interval has passed
The crosshairs package, which always highlights both the column and the line. It also has a “highlight crosshairs when idle” mode, but I prefer to have the current line always highlighted.
The ibuffer package allows all sort of useful operations on the list of open buffers. I haven’t customized it yet, but I have a keybinding to open it. (Disabled for now as I am using helm’s helm-buffer-list).
The smex package is incredibly useful, adding IDO integration and some other very nice features to M-x, which make it easier to discover and use Emacs commands. Highly recommended. (Disabled for now as I’m using helm’s helm-M-x).
For distraction-free writing, I’m testing out writeroom-mode.
NeoTree shows a navigation tree on a sidebar, and allows a number of operations on the files and directories. I’m not much of a fan of this type of interface in Emacs, but I have set it up to check it out.
(use-packageneotree:custom(neo-theme(if(display-graphic-p)'icons'arrow))(neo-smart-opent)(projectile-switch-project-action'neotree-projectile-action):config(defunneotree-project-dir()"Open NeoTree using the git root."(interactive)(let((project-dir(projectile-project-root))(file-name(buffer-file-name)))(neotree-toggle)(ifproject-dir(if(neo-global--window-exists-p)(progn(neotree-dirproject-dir)(neotree-findfile-name)))(message"Could not find git project root.")))):bind([f8].neotree-project-dir))
wc-mode allows counting characters and words, both on demand and continuously. It also allows setting up a word/character goal.
The all-the-icons package provides a number of useful icons.
Completion: IDO or Helm?
The battlerageson - helm or IDO? Both are nice completion frameworks for Emacs, and both integrate nicely with most main Emacs functions, including file opening, command and buffer selection, etc. I was using IDO for some time but are now giving helm a try. Both my configs are shown below, but only Helm is enabled at the moment.
With aggressive-indent, indentation is always kept up to date in the whole buffer. Sometimes it gets in the way, but in general it’s nice and saves a lot of work, so I enable it for all programming modes except for Python mode, where I explicitly disable as it often gets the indentation wrong and messes up existing code.
Disabled for now while I test how much I miss it (I often find it gets in the way, but I’m not sure how often it helps and I don’t even notice it)
With company-mode, we get automatic completion - when there are completions available, a popup menu will appear when you stop typing for a moment, and you can either continue typing or accept the completion using the Enter key. I enable it globally.
I find iedit absolutely indispensable when coding. In short: when you hit Ctrl-:, 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.
Enable some additional fontification for Clojure code.
The cider package provides a fantastic REPL built into Emacs. We configure a few aspects, including pretty printing, fontification, history size and others.
(use-packagecider:custom;; nice pretty printing(cider-repl-use-pretty-printingnil);; nicer font lock in REPL(cider-repl-use-clojure-font-lockt);; result prefix for the REPL(cider-repl-result-prefix"; => ");; never ending REPL history(cider-repl-wrap-historyt);; looong history(cider-repl-history-size5000);; persistent history(cider-repl-history-file"~/.emacs.d/cider-history");; error buffer not popping up(cider-show-error-buffernil);; go right to the REPL buffer when it's finished connecting(cider-repl-pop-to-buffer-on-connectt))
We use clj-refactor for supporting advanced code refactoring in Clojure.
(use-packageclj-refactor:config(defunmy-clojure-mode-hook()(clj-refactor-mode1)(yas-minor-mode1); for adding require/use/import statements;; This choice of keybinding leaves cider-macroexpand-1 unbound(cljr-add-keybindings-with-prefix"C-c C-m")):hook(clojure-mode.my-clojure-mode-hook))
Use emr for supporting refactoring in Emacs LISP and some other languages.
When coding in LISP-like languages, rainbow-delimiters is a must-have - it marks each concentric pair of parenthesis with different colors, which makes it much easier to understand expressions and spot mistakes.
Another useful addition for LISP coding - smartparens enforces parenthesis to match, and adds a number of useful operations for manipulating parenthesized expressions. 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.
The rx library makes it easier to express regular expressions (I know regex syntax, but in Emacs the quoting makes them very hard to read). The xr library is the inverse - can be used to convert regex strings to rx syntax, which makes it easier to learn by example.
Use helm-pass as an interface to pass.
git interface with some simple configuration I picked up somewhere. When you press C-c C-g, magit-status runs full-screen, but when you press q, it restores your previous window setup. Very handy.
(use-packagemagit:diminishauto-revert-mode:bind(("C-c C-g".magit-status):mapmagit-status-mode-map("q".magit-quit-session)):config(defadvicemagit-status(aroundmagit-fullscreenactivate)"Make magit-status run alone in a frame."(window-configuration-to-register:magit-fullscreen)ad-do-it(delete-other-windows))(defunmagit-quit-session()"Restore the previous window configuration and kill the magit buffer."(interactive)(kill-buffer)(jump-to-register:magit-fullscreen)))
Publishing with Hugo. I don’t use this anymore since I started blogging with ox-hugo. I keep it loaded, but without its keybinding, because it makes it easy sometimes to see the history of my Markdown posts.
(defunmy-randomize-region(begend)"Randomize lines in region from BEG to END."(interactive"*r")(let((lines(split-string(delete-and-extract-regionbegend)"\n")))(when(string-equal""(car(lastlines1)))(setqlines(butlastlines1)))(apply'insert(mapcar'cdr(sort(mapcar(lambda(x)(cons(random)(concatx"\n")))lines)(lambda(ab)(<(cara)(carb))))))))
auto-insert mode for automatically inserting user-defined templates for certain file types. It’s included with Emacs, so I just configure its directory to one inside my Dropbox, and set the hook to run it automatically when opening a file.
In addition to coding, I configure some modes that can be used for text editing.
AsciiDoc, which I use for my book and some other text. I also set up visual-line-mode and variable-pitch-mode here. adoc-mode is not so granular as org-mode with respect to face assignments, so the variable/fixed distinction does not always work, but it’s still pretty good for long-text editing.
;; Call this function with "M-x org-multi-file-md-export"(defunorg-multi-file-md-export()"Export current buffer to multiple Markdown files."(interactive);; Loop over all entries in the file(org-map-entries(lambda()(let*((level(nth1(org-heading-components)))(title(or(nth4(org-heading-components))""));; Export filename is the EXPORT_FILE_NAME property, or the;; lower-cased section title if it's not set.(filename(or(org-entry-get(point)"EXPORT_FILE_NAME")(concat(replace-regexp-in-string" ""-"(downcasetitle))".md"))))(when(=level1);; export only first level entries;; Mark the subtree so that the title also gets exported(org-mark-subtree);; Call the export function. This is one of the base org;; functions, the 'md defines the backend to use for the;; conversion. For exporting to other formats, simply use the;; correct backend name, and also change the file extension;; above.(org-export-to-file'mdfilenameniltnil))));; skip headlines tagged with "noexport" (this is an argument to;; org-map-entries above)"-noexport")nilnil)
This is how we get a global header property in org-mode
(setqhelp/default-gc-cons-thresholdgc-cons-threshold)(defunhelp/set-gc-cons-threshold(&optionalmultipliernotify)"Set `gc-cons-threshold' either to its default value or a
`multiplier' thereof."(let*((new-multiplier(ormultiplier1))(new-threshold(*help/default-gc-cons-thresholdnew-multiplier)))(setqgc-cons-thresholdnew-threshold)(whennotify(message"Setting `gc-cons-threshold' to %s"new-threshold))))(defunhelp/double-gc-cons-threshold()"Double `gc-cons-threshold'."(help/set-gc-cons-threshold10))(add-hook'org-babel-pre-tangle-hook#'help/double-gc-cons-threshold)(add-hook'org-babel-post-tangle-hook#'help/set-gc-cons-threshold)
A work-in-progress Hammerspoon shell for Emacs, posted on the Hammerspoon mailing list.