An Annotated Spacemacs

For an org-mode workflow

I started using org-mode on SpacemacsI use this Emacs Mac-port version of Emacs since the normal one had issues like screen flickering and crashing on quitting a frame. EDIT Jan 30 2020: Using the latest and greatest homebrew-emacs-head now - finally doom-wilmersdorf works out the box something like two years ago to track projects for my PhD. Still using it now but it felt like a good time to standardize some stuff and remove redundant or unnecessary cruft, based on what I’ve actually used till now. First off, I’m not really an emacs user, and I don’t know a lot of lisp (except for our tiny scheme experiments from way back). I’ve gotten pretty used to vim keyboard shortcuts for text editing and even use the IdeaVim plugin in PyCharm. But vim doesn’t have something like org-mode (vim-wiki was nice and all but this is an entirely different beast) and a knowledge-base + project tracker comes in extremely handy for a PhD. So Spacemacs, with the motto

The best editor is neither Emacs nor Vim, it’s Emacs and Vim!”

fit the bill perfectly. Org-mode is seriously vast though, and can be almost infinitely extended with additional packages. Even cursory searches lead to all kinds of guides and configs of people who seem to be fiercely productive and really get things done. But I like building up from scratch, so I have time to get used to actually using bits and pieces of functionality before adding more, so my settings are going to start out a bit more minimal.

Since org-mode is amazing, it has this thing called org-babel which lets you write source code in any language in between text. You can run these code blocks, store, and manipulate the outputs too - kind of like a multi-lingual Jupyter notebook. That’s not all though, with org-babel-tangle you can extract all the source code into a separate file (since an org-mode file is seen to weave together text and code, the code can then be (un?)-tangled out).

This means that this post, written in org-mode, is actually my entire .spacemacs configuration file with plenty of annotations, descriptions, and examples of how I use each functionality. Every time I want to add something new or get rid of something I don’t use, I can make the change here in this post, with some notes on why and how, and then extract the code out into my new .spacemacs.

Added this to the beginning of the file and then called org-babel-tangle (ALT-x org-babel-tangle).

#+PROPERTY: header-args :tangle ".spacemacs"

Also, pelican doesn’t seem to like #BEGIN_EXAMPLE blocks - used #BEGIN_SRC blocks (with “:tangle no”) and ‘,’s inside them instead (like so).

So here it is, a living, breathing, changing Spacemacs configuration. It has everything I’ve been using for the past two years and I’ll keep it up to date with my future work flow as I figure it out. Also, before diving into the code, here’s what my typical Spacemacs window looks like:

org_mode.png

Figure 1: While editing this post

Edits

dotspacemacs / layers

To make things easier on the user, Spacemacs works with the concept of layers. Here’s a full list of them. Some emacs and org packages haven’t been neatly encapsulated into a layer yet but as long as it’s a MELPA package you can use them with pretty much the same amount of ease. The .spacemacs file starts out with defun dotspacemacs/layers which is where both these kinds of things go. I haven’t yet come across a must-have functionality that doesn’t fall into one of these two categories, maybe later.

;; -*- mode: emacs-lisp -*-
;; This file is loaded by Spacemacs at startup.
;; It must be stored in your home directory.
(defun dotspacemacs/layers ()
  "Configuration Layers declaration.
You should not put any user code in this function besides modifying the variable
values."
  (setq-default
   ;; Base distribution to use. This is a layer contained in the directory
   ;; `+distribution'. For now available distributions are `spacemacs-base'
   ;; or `spacemacs'. (default 'spacemacs)
   dotspacemacs-distribution 'spacemacs
   ;; Lazy installation of layers (i.e. layers  are installed only when a file
   ;; with a supported type is opened). Possible values are `all', `unused'
   ;; and `nil'. `unused' will lazy install only unused layers (i.e. layers
   ;; not listed in variable `dotspacemacs-configuration-layers'), `all' will
   ;; lazy install any layer that s  (set-face-attribute 'variable-pitch nil :family "ETBembo" :size 15) upport lazy installation even the layers
   ;; listed in `dotspacemacs-configuration-layers'. `nil' disable the lazy
   ;; installation feature and you have to explicitly list a layer in the
   ;; variable `dotspacemacs-configuration-layers' to install it.
   ;; (default 'unused)
   dotspacemacs-enable-lazy-installation 'unused
   ;; If non-nil then Spacemacs will ask for confirmation before installing
   ;; a layer lazily. (default t)
   dotspacemacs-ask-for-lazy-installation t
   ;; If non-nil layers with lazy install support are lazy installed.
   ;; List of additional paths where to look for configuration layers.
   ;; Paths must have a trailing slash (i.e. `~/.mycontribs/')
   dotspacemacs-configuration-layer-path '()
   ;; List of configuration layers to load.
   dotspacemacs-configuration-layers
   '(
     ;; ----------------------------------------------------------------
     ;; Example of useful layers you may want to use right away.
     ;; Uncomment some layer names and press <SPC f e R> (Vim style) or
     ;; <M-m f e R> (Emacs style) to install them.
     ;; ----------------------------------------------------------------
     ;;
     ;; org-mode
     org
     ;; Typing-related
     auto-completion
     (spell-checking :variables    spell-checking-enable-by-default nil)
     syntax-checking
     ;; Search-related
     ivy
     deft
     ;; Language support
     python
     html
     rust
     emacs-lisp
     latex
     markdown
     yaml
     ;; For papers and PDFs
     bibtex
     pdf-tools
     ;; Other
     osx
     git
     )
 ;; List of additional packages that will be installed without being
 ;; wrapped in a layer. If you need some configuration for these
 ;; packages, then consider creating a layer. You can also put the
 ;; configuration in `dotspacemacs/user-config'.
   dotspacemacs-additional-packages
   '( 
     ;; Aesthetics
     doom-themes
     org-bullets
     olivetti
     ;; For writing
     writegood-mode
     langtool
     ;; For org-drill
     org-plus-contrib
     ;; For defining nice org-capture templates
     org-starter
     dash
     dash-functional
     org-reverse-datetree
     ;; For annotating PDFs
     org-noter
     ;; For making a kanban from TODO entries
     org-kanban
     )
 ;; A list of packages that cannot be updated.
 dotspacemacs-frozen-packages '()
 ;; A list of packages that will not be installed and loaded.
 dotspacemacs-excluded-packages '(vi-tilde-fringe org-projectile doom-wilmersdorf-theme)
 ;; Defines the behaviour of Spacemacs when installing packages.
 ;; Possible values are `used-only', `used-but-keep-unused' and `all'.
 ;; `used-only' installs only explicitly used packages and uninstall any
 ;; unused packages as well as their unused dependencies.
 ;; `used-but-keep-unused' installs only the used packages but won't uninstall
 ;; them if they become unused. `all' installs *all* packages supported by
 ;; Spacemacs and never uninstall them. (default is `used-only')
 dotspacemacs-install-packages 'used-only))

The Org Layer

This is the layer that gives you org-mode functionality. There’ll be a lot more on this in other parts of the config. The Spacemacs org layer page has a crazy amount of keyboard shortcuts but here are the ones that I actually use. (For commands not starting with ALT, SHIFT, or CTRL, you have to press ESC first)

Tasks, scheduling, and agenda

  • Current week agenda - SPC m a * a

    The buffer window that you run this in shows everything you have scheduled for the week (top right in the picture above). The asterisk makes it a “sticky” agenda view, meaning you can call a different agenda command (such as the one below) in another window and still see this one.

  • List of tasks - SPC m a t

    The buffer window you run this in shows a list of tasks identified by keywords like “TODO” (bottom right in the picture above). I’ve added a couple of my own keywords to this list.

  • Refresh agenda - r

    You can run this inside an agenda buffer to make it reflect any changes you’ve made to your file(s).

  • Make a heading into a task - t

    Running this on a heading gives you a choice of TODO keywords that you can assign to it.

  • Tick a checkbox - CTRL-c CTRL-c
  • Schedule something - SPC m s
  • Give something a deadline - SPC m d

Adding text and moving it around

  • Add a heading - SPC m h i
  • Add a sub-heading - SPC m h s
  • Add a link - CTRL-c CTRL-l
  • Add a new list item - RET
  • Add tags to a heading - SPC m :
  • Re-organize headings / items - ALT--UP, ALT-DOWN
  • Demote a heading - ALT-SHIFT-RIGHT
  • Fold a heading’s subtree - TAB
  • Archive a subtree when you’re done with it - SPC m A

    This saves the heading and everything under it into “<filename>.orgarchive“.

  • Cut a subtree - CTRL-c CTRL-x CTRL-w
  • Paste a cut subtree - CTRL-c CTRL-x CTRL-y

Other [4/5]

  • DONE Showing entries related to a tag CTRL-c / m <tag name>
  • DONE Displaying statistics - CTRL-c #

    You can run this on a heading with [/] (or [%]) next to it to see how many (or what percentage of) that heading’s checkboxes you’ve ticked or TODOs you’ve DONE’d. This line’s parent heading demonstrates this.

  • DONE Edit source code (in a dedicated buffer) - CTRL-c '
  • DONE org-capture - SPC m c

    A lot more on this one here.

  • TODO Export - CTRL-c CTRL-e

Layers for typing

I use the auto-completion, spell-checking, and syntax-checking layers to make typing text and code more efficient. I’ve set spell-checking off by default though since I only need it for typing long-form articles like this one and you can enable it easily on a file by calling ALT-x-flyspell-mode.

Search-related

Ivy is supposed to be faster than Helm for searching within files, I haven’t really noticed or compared though. Deft is a neat package that lets you define a folder and gives you fast search within the files in that folder. You call it using SPC a n to get a window with a list of files that you can search through. You can just type in the search bar to make a new file. I’ve also used it for renaming ( SPC m r ) and deleting files ( SPC m d ). Refresh with CTRL-c CTRL-g.

Aesthetics

I occasionally try out different doom themes (with SPC T s) - I’m partial to the doom-wilmersdorf and doom-spacegrey themes. org-bullets is to make the heading bullet points look better than asterisks, and Olivetti mode (ALT-x olivetti mode) centers the part of the buffer with the text, which looks nicer when you have a lot of text on the screen.

Org-plus-contrib

This has a lot of packages but I just have it so I can use org-drill for learning Turkish. More on that in the next post.

Org-reverse-datetree

I use this one in some of my org-capture templates which need stored in reverse date order. The Dailies page of this blog is an example, you get the latest notes first.

Org-kanban

Having a giant list of TODOs was pretty daunting and I found myself procrastinating a lot. A kanban board is meant to alleviate this by giving you a visual idea of how much you’ve taken on your plate and how much you can actually work on at a given moment. The org-kanban package accomplishes this in org-mode by pulling in active TODOs from a file into a table, divided into the different TODO states. I have this linked to the Task capture template.

The keyboard shortcuts related to this are quite simple:

  • Initialize the kanban - ALT-x org-kanban/initialize-at-beginning
  • Refresh after adding a new TODO - CTRL-c CTRL-c on the kanban block
  • Shift from TODO -> RUNNING, RUNNING -> DONE - ALT-x org-kanban/shift a
  • Shift from DONE -> RUNNING, RUNNING -> TODO - ALT-x org-kanban/shift d

Handling papers and PDFs

The bibtex layer lets you cite papers from a bibliography, and the org-noter + pdf-tools combo lets you view and annotate (even huge) PDFs in emacs buffers. I think this topic deserves its own post so I’ll keep it short here and add a link later.

dotspacemacs / init

Spacemacs is crazy customizable. I didn’t really change many of the defaults, except making the default font Jetbrains Mono and make ripgrep the default search tool. You can probably skip this section.

By the by, since this section was so long and mostly pointless to readers I dug around to find ways to make it fold and expand with a click, as it is now. Used this reddit answer to do it. Here’s how:

#+BEGIN_EXPORT html
  <details>
  <summary>Click to expand</summary>
#+BEGIN_SRC emacs-lisp
  ;; your code goes here
#+END_SRC
#+BEGIN_EXPORT html
  </details>
#+END_EXPORT
Click to expand
(defun dotspacemacs/init ()
  "Initialization function.
This function is called at the very startup of Spacemacs initialization
before layers configuration.
You should not put any user code in there besides modifying the variable
values."
  ;; This setq-default sexp is an exhaustive list of all the supported
  ;; spacemacs settings.
  (setq-default
   ;; If non nil ELPA repositories are contacted via HTTPS whenever it's
   ;; possible. Set it to nil if you have no way to use HTTPS in your
   ;; environment, otherwise it is strongly recommended to let it set to t.
   ;; This variable has no effect if Emacs is launched with the parameter
   ;; `--insecure' which forces the value of this variable to nil.
   ;; (default t)
   dotspacemacs-elpa-https t
   ;; Maximum allowed time in seconds to contact an ELPA repository.
   dotspacemacs-elpa-timeout 5
   ;; If non nil then spacemacs will check for updates at startup
   ;; when the current branch is not `develop'. Note that checking for
   ;; new versions works via git commands, thus it calls GitHub services
   ;; whenever you start Emacs. (default nil)
   dotspacemacs-check-for-update nil
   ;; If non-nil, a form that evaluates to a package directory. For example, to
   ;; use different package directories for different Emacs versions, set this
   ;; to `emacs-version'.
   dotspacemacs-elpa-subdirectory nil
   ;; One of `vim', `emacs' or `hybrid'.
   ;; `hybrid' is like `vim' except that `insert state' is replaced by the
   ;; `hybrid state' with `emacs' key bindings. The value can also be a list
   ;; with `:variables' keyword (similar to layers). Check the editing styles
   ;; section of the documentation for details on available variables.
   ;; (default 'vim)
   dotspacemacs-editing-style 'vim
   ;; If non nil output loading progress in `*Messages*' buffer. (default nil)
   dotspacemacs-verbose-loading nil
   ;; Specify the startup banner. Default value is `official', it displays
   ;; the official spacemacs logo. An integer value is the index of text
   ;; banner, `random' chooses a random text banner in `core/banners'
   ;; directory. A string value must be a path to an image format supported
   ;; by your Emacs build.
   ;; If the value is nil then no banner is displayed. (default 'official)
   dotspacemacs-startup-banner 'official
   ;; List of items to show in startup buffer or an association list of
   ;; the form `(list-type . list-size)`. If nil then it is disabled.
   ;; Possible values for list-type are:
   ;; `recents' `bookmarks' `projects' `agenda' `todos'."
   ;; List sizes may be nil, in which case
   ;; `spacemacs-buffer-startup-lists-length' takes effect.
   dotspacemacs-startup-lists '((recents . 5)
				(projects . 7))
   ;; True if the home buffer should respond to resize events.
   dotspacemacs-startup-buffer-responsive t
   ;; Default major mode of the scratch buffer (default `text-mode')
   dotspacemacs-scratch-mode 'text-mode
   ;; List of themes, the first of the list is loaded when spacemacs starts.
   ;; Press <SPC> T n to cycle to the next theme in the list (works great
   ;; with 2 themes variants, one dark and one light)
   dotspacemacs-themes '(doom-wilmersdorf
			 doom-spacegrey
			 spacemacs-dark
			 spacemacs-light)
   ;; If non nil the cursor color matches the state color in GUI Emacs.
   dotspacemacs-colorize-cursor-according-to-state t
   ;; Default font, or prioritized list of fonts. `powerline-scale' allows to
   ;; quickly tweak the mode-line size to make separators look not too crappy.
   dotspacemacs-default-font '("Jetbrains Mono"
			       :size 15
			       :weight normal
			       :width normal
			       :powerline-scale 1.1)
   ;; The leader key
   dotspacemacs-leader-key "SPC"
   ;; The key used for Emacs commands (M-x) (after pressing on the leader key).
   ;; (default "SPC")
   dotspacemacs-emacs-command-key "SPC"
   ;; The key used for Vim Ex commands (default ":")
   dotspacemacs-ex-command-key ":"
   ;; The leader key accessible in `emacs state' and `insert state'
   ;; (default "M-m")
   dotspacemacs-emacs-leader-key "M-m"
   ;; Major mode leader key is a shortcut key which is the equivalent of
   ;; pressing `<leader> m`. Set it to `nil` to disable it. (default ",")
   dotspacemacs-major-mode-leader-key ","
   ;; Major mode leader key accessible in `emacs state' and `insert state'.
   ;; (default "C-M-m")
   dotspacemacs-major-mode-emacs-leader-key "C-M-m"
   ;; These variables control whether separate commands are bound in the GUI to
   ;; the key pairs C-i, TAB and C-m, RET.
   ;; Setting it to a non-nil value, allows for separate commands under <C-i>
   ;; and TAB or <C-m> and RET.
   ;; In the terminal, these pairs are generally indistinguishable, so this only
   ;; works in the GUI. (default nil)
   dotspacemacs-distinguish-gui-tab nil
   ;; If non nil `Y' is remapped to `y$' in Evil states. (default nil)
   dotspacemacs-remap-Y-to-y$ nil
   ;; If non-nil, the shift mappings `<' and `>' retain visual state if used
   ;; there. (default t)
   dotspacemacs-retain-visual-state-on-shift t
   ;; If non-nil, J and K move lines up and down when in visual mode.
   ;; (default nil)
   dotspacemacs-visual-line-move-text nil
   ;; If non nil, inverse the meaning of `g' in `:substitute' Evil ex-command.
   ;; (default nil)
   dotspacemacs-ex-substitute-global nil
   ;; Name of the default layout (default "Default")
   dotspacemacs-default-layout-name "Default"
   ;; If non nil the default layout name is displayed in the mode-line.
   ;; (default nil)
   dotspacemacs-display-default-layout nil
   ;; If non nil then the last auto saved layouts are resume automatically upon
   ;; start. (default nil)
   dotspacemacs-auto-resume-layouts nil
   ;; Size (in MB) above which spacemacs will prompt to open the large file
   ;; literally to avoid performance issues. Opening a file literally means that
   ;; no major mode or minor modes are active. (default is 1)
   dotspacemacs-large-file-size 1
   ;; Location where to auto-save files. Possible values are `original' to
   ;; auto-save the file in-place, `cache' to auto-save the file to another
   ;; file stored in the cache directory and `nil' to disable auto-saving.
   ;; (default 'cache)
   dotspacemacs-auto-save-file-location 'cache
   ;; Maximum number of rollback slots to keep in the cache. (default 5)
   dotspacemacs-max-rollback-slots 5
   ;; If non nil, `helm' will try to minimize the space it uses. (default nil)
   dotspacemacs-helm-resize nil
   ;; if non nil, the helm header is hidden when there is only one source.
   ;; (default nil)
   dotspacemacs-helm-no-header nil
   ;; define the position to display `helm', options are `bottom', `top',
   ;; `left', or `right'. (default 'bottom)
   dotspacemacs-helm-position 'bottom
   ;; Controls fuzzy matching in helm. If set to `always', force fuzzy matching
   ;; in all non-asynchronous sources. If set to `source', preserve individual
   ;; source settings. Else, disable fuzzy matching in all sources.
   ;; (default 'always)
   dotspacemacs-helm-use-fuzzy 'always
   ;; If non nil the paste micro-state is enabled. When enabled pressing `p`
   ;; several times cycle between the kill ring content. (default nil)
   dotspacemacs-enable-paste-transient-state nil
   ;; Which-key delay in seconds. The which-key buffer is the popup listing
   ;; the commands bound to the current keystroke sequence. (default 0.4)
   dotspacemacs-which-key-delay 0.4
   ;; Which-key frame position. Possible values are `right', `bottom' and
   ;; `right-then-bottom'. right-then-bottom tries to display the frame to the
   ;; right; if there is insufficient space it displays it at the bottom.
   ;; (default 'bottom)
   dotspacemacs-which-key-position 'bottom
   ;; If non nil a progress bar is displayed when spacemacs is loading. This
   ;; may increase the boot time on some systems and emacs builds, set it to
   ;; nil to boost the loading time. (default t)
   dotspacemacs-loading-progress-bar t
   ;; If non nil the frame is fullscreen when Emacs starts up. (default nil)
   ;; (Emacs 24.4+ only)
   dotspacemacs-fullscreen-at-startup nil
   ;; If non nil `spacemacs/toggle-fullscreen' will not use native fullscreen.
   ;; Use to disable fullscreen animations in OSX. (default nil)
   dotspacemacs-fullscreen-use-non-native nil
   ;; If non nil the frame is maximized when Emacs starts up.
   ;; Takes effect only if `dotspacemacs-fullscreen-at-startup' is nil.
   ;; (default nil) (Emacs 24.4+ only)
   dotspacemacs-maximized-at-startup nil
   ;; A value from the range (0..100), in increasing opacity, which describes
   ;; the transparency level of a frame when it's active or selected.
   ;; Transparency can be toggled through `toggle-transparency'. (default 90)
   dotspacemacs-active-transparency 90
   ;; A value from the range (0..100), in increasing opacity, which describes
   ;; the transparency level of a frame when it's inactive or deselected.
   ;; Transparency can be toggled through `toggle-transparency'. (default 90)
   dotspacemacs-inactive-transparency 90
   ;; If non nil show the titles of transient states. (default t)
   dotspacemacs-show-transient-state-title t
   ;; If non nil show the color guide hint for transient state keys. (default t)
   dotspacemacs-show-transient-state-color-guide t
   ;; If non nil unicode symbols are displayed in the mode line. (default t)
   dotspacemacs-mode-line-unicode-symbols t
   ;; If non nil smooth scrolling (native-scrolling) is enabled. Smooth
   ;; scrolling overrides the default behavior of Emacs which recenters point
   ;; when it reaches the top or bottom of the screen. (default t)
   dotspacemacs-smooth-scrolling t
   ;; Control line numbers activation.
   ;; If set to `t' or `relative' line numbers are turned on in all `prog-mode' and
   ;; `text-mode' derivatives. If set to `relative', line numbers are relative.
   ;; This variable can also be set to a property list for finer control:
   ;; '(:relative nil
   ;;   :disabled-for-modes dired-mode
   ;;                       doc-view-mode
   ;;                       markdown-mode
   ;;                       org-mode
   ;;                       pdf-view-mode
   ;;                       text-mode
   ;;   :size-limit-kb 1000)
   ;; (default nil)
   dotspacemacs-line-numbers nil
   ;; Code folding method. Possible values are `evil' and `origami'.
   ;; (default 'evil)
   dotspacemacs-folding-method 'evil
   ;; If non-nil smartparens-strict-mode will be enabled in programming modes.
   ;; (default nil)
   dotspacemacs-smartparens-strict-mode nil
   ;; If non-nil pressing the closing parenthesis `)' key in insert mode passes
   ;; over any automatically added closing parenthesis, bracket, quote, etc…
   ;; This can be temporary disabled by pressing `C-q' before `)'. (default nil)
   dotspacemacs-smart-closing-parenthesis nil
   ;; Select a scope to highlight delimiters. Possible values are `any',
   ;; `current', `all' or `nil'. Default is `all' (highlight any scope and
   ;; emphasis the current one). (default 'all)
   dotspacemacs-highlight-delimiters 'all
   ;; If non nil, advise quit functions to keep server open when quitting.
   ;; (default nil)
   dotspacemacs-persistent-server nil
   ;; List of search tool executable names. Spacemacs uses the first installed
   ;; tool of the list. Supported tools are `ag', `pt', `ack' and `grep'.
   ;; (default '("ag" "pt" "ack" "grep"))
   dotspacemacs-search-tools '("rg" "ag" "pt" "ack" "grep")
   ;; The default package repository used if no explicit repository has been
   ;; specified with an installed package.
   ;; Not used for now. (default nil)
   dotspacemacs-default-package-repository nil
   ;; Delete whitespace while saving buffer. Possible values are `all'
   ;; to aggressively delete empty line and long sequences of whitespace,
   ;; `trailing' to delete only the whitespace at end of lines, `changed'to
   ;; delete only whitespace for changed lines or `nil' to disable cleanup.
   ;; (default nil)
   dotspacemacs-whitespace-cleanup nil
   ))

dotspacemacs / user-init

Nothing to see here, move along.

Click to expand
(defun dotspacemacs/user-init ()
  "Initialization function for user code.
It is called immediately after `dotspacemacs/init', before layer configuration
executes.
 This function is mostly useful for variables that need to be set
before packages are loaded. If you are unsure, you should try in setting them in
`dotspacemacs/user-config' first."
 )

dotspacemacs / user-config

This is where the meat of the customizations reside so I’ll go through it bit by bit. The remaining code blocks all belong to the same functionI used org-babel-demarcate-block (CTRL-c CTRL-v d) to quickly split up the source code block of this function into smaller blocks. .

(defun dotspacemacs/user-config ()
  "Configuration function for user code.
    This function is called at the very end of Spacemacs initialization after
    layers configuration.
    This is the place where most of your configurations should be done. Unless it is
    explicitly specified that a variable should be set before a package is loaded,
    you should place your code here."
    (require 'org-drill)
    (setq org-drill-scope (quote directory))
    (setq org-link-frame-setup (quote (file . find-file)))
    (setq org-link-frame-setup
     (quote
      ((file . find-file)
       (vm . vm-visit-folder-other-frame)
       (vm-imap . vm-visit-imap-folder-other-frame)
       (gnus . org-gnus-no-new-news)
       (wl . wl-other-frame))))
  • The first two lines are for my org-drill setup, I’ll post about it next.
  • The org-link-frame-setup lines (I only changed the file, the rest are the default) make it so that if I click on a file link it opens the file in the current buffer window, instead of another one - this ensures that nothing messes with my agenda views.

Org Agenda and Todo

UPDATE 07-01-2021: made it so that the agenda starts with the current day instead of Monday

(setq org-agenda-window-setup (quote current-window))
;; To add all org files in a repository to the agenda
(setq org-agenda-files (directory-files-recursively "~/Dropbox/organizer/" "\.org$"))
;; Set task-related keywords
(setq org-todo-keywords
      '((sequence "IDEA(i)" "TODO(t)" "RUNNING(r)" "|" "DONE(d)" "CANCELLED(c)" "DEFERRED(f)")
	(sequence "MEETING(m)" "|" "MET(M)")))
;; Start agenda on current day instead of Monday
(setq org-agenda-start-on-weekday nil)
;; Ignore scheduled tasks and tasks with a deadline in task list view (SPC m a t)
(setq org-agenda-todo-ignore-with-date t)
;; Skip finished items
(setq org-agenda-skip-deadline-if-done t)
(setq org-agenda-skip-scheduled-if-done t)
(setq org-agenda-skip-timestamp-if-done t)
;; Skip deleted files
(setq org-agenda-skip-unavailable-files t)
  • The org-agenda-files line makes agenda check recursively in my Dropbox organizer folder for org files. This comes in handy since I tend to group files into folders when it makes sense - e.g. a folder for conferences with one file of notes per conference etc. The window-setup makes it so that when I call an agenda command in a buffer then it opens in that buffer instead of any old one it finds lying around (I feel like this should be the default somehow).
  • org-todo-keywords defines extra TODO-like keywords. The | between the two sets of words is the difference between TODO and DONE. So MET is the closed state of MEETING. Each keyword has a letter in brackets next to it that I can use to quickly add it to a heading / change an existing one (by calling t on the heading). Other people seem to have a lot more keywords here but I mostly find myself using these.
  • Then I make it so that the org-agenda-todo view (SPC m a t) doesn’t show TODO items which have a scheduled date attached to them. This fits my typical setup (in the Figure at the top) with two vertically stacked windows, one with the week agenda and one that lists unscheduled TODOs, since this way the same item isn’t repeated in both views. I mostly use unscheduled TODOs for ideas (with the IDEA keyword) and more generic/vague statements that don’t really have a deadline (like “Think about projects related to X” or “Look into topic X”). I also use it for meetings/appointments which I still have to set up (with the MEETING keyword).
  • The org-agenda-skip lines make the agenda stop showing DONE’d TODO items.

Deft

;; Deft-related
(setq deft-directory "~/Dropbox/organizer")
(setq deft-extensions '("org"))
(setq deft-recursive t)

These should be self-explanatory - sets the folder for Deft to index and makes it look recursively for org files.

Org-capture templates

This is my favorite part - capture templates!

;; Org-Capture templates
(setq org-capture-templates
	  (quote (

These are basically endlessly code-able templates that let you add specific content of any kind anywhere with a couple of keypresses. I shall demonstrate.

Blog-related

  • Making a draft blog post
    ("b" "Blog-related templates")
      ("bn" "Blog Entry" plain
       (file
        (lambda nil
          (interactive)
          (let ((name (read-string "Filename: ")))
    	(expand-file-name (format "%s.org" name)
    			  "~/Dropbox/organizer/pelican_blog/drafts"))))
       "#+TITLE: %^{title}
      #+DATE: %t
      #+CATEGORY: %^{category}
      #+AUTHOR: Hex
      #+PROPERTY: LANGUAGE en
      #+PROPERTY: SUMMARY %^{summary}
      #+PROPERTY: SUBTITLE %^{subtitle}
      #+PROPERTY: TAGS %^{comma-separated tags}
      #+OPTIONS: num:nil
      #+OPTIONS: toc:nil
      %?
      " :jump-to-captured t)
    

    Suppose one of us comes up with an idea for a blog post. Wherever I am in Spacemacs, I can type SPC m c bn and type in the title of the post, then it asks me for the category, summary, subtitle, and any tags we may have in mind (each of these can be skipped and filled in later of course). I press Enter and have a fully set up skeleton file inside the drafts folder, and now we can concentrate on writing the content. The whole lambda part makes an anonymous function (just like a Python lambda function) that asks the user for a slug/filename instead of using a fixed one. You can ask the user for text with a prompt using %^{prompt} and get today’s date using %t. %? gives you the option to type in content in the capture buffer itself (you exit the capture buffer and save it to the file using CTRL-c CTRL-c). And :jump-to-captured t makes it open the new file.

  • Saving a link
    ("bu" "URL" entry
     (file+function "~/Dropbox/organizer/pelican_blog/content/pages/dailies.org"
    		org-reverse-datetree-goto-date-in-file)
     "* [[%^{URL}][%^{Description}]] %^g %?")
    

    This is what we use to make the Dailies page. Any time I come across a website that’s interesting I hit SPC m c bu in a Spacemacs buffer and file it away along with a description and some tags (with %^g). We still haven’t figured out a nice way to show the tags on Pelican though. This is also where the org-reverse-datetree package comes in handy. The file+function part says to put this captured note in dailies.org under today’s date. If the heading doesn’t exist it makes it (starting from a top-level heading for the year, second-level for month and third-level for the day) and then puts your note as a subheading under it. You can do the same thing with the built-in datetree (using file+datetree instead) but then the top of the file would be your oldest note and the bottom would be the latest which is a bit inconvenient, especially for a blog page. The org-reverse-datetree package also lets you specify the format of your dates. So this is what I have on top of the dailies.org file:

    #+TITLE: Dailies
    #+AUTHOR: Hex
    #+DATE: 2020-01-05
    #+PROPERTY: LANGUAGE en
    #+OPTIONS: toc:nil
    #+OPTIONS: tags:nil
    #+OPTIONS: num:nil
    #+PROPERTY: SUBTITLE Some links and resources found during breaks at work
    #+PROPERTY: META_ROBOTS noindex
    #+REVERSE_DATETREE_USE_WEEK_TREE: nil
    #+REVERSE_DATETREE_DATE_FORMAT: %d %A
    #+REVERSE_DATETREE_MONTH_FORMAT: %B
    #+REVERSE_DATETREE_YEAR_FORMAT: %Y
    
    • USE_WEEK_TREE - Setting this to true would make it store weeks instead of months and days
    • DATE_FORMAT - “%d” = day of the month, “%A” = day of the week
    • MONTH_FORMAT - “%B” = January, February, etc.
    • YEAR_FORMAT - “%Y” = 2020
  • Margin notes, side notes and collapsed notes

    The notes you see on the right (or if you click on the arrows from a phone) are side notes (and very rarely margin notesLook, a margin note ) ala Edward Tufte and some CSS taken from Tufte-CSS. Using capture templates make it infinitely easy to add them into a blog post. There’s also a template for making collapsed notes that you can then click on to expand, such as the the code for these templates below. Notice that the file argument is empty - that’s because we want these templates to be inserted into the file from which we called org-capture at the location we called it from. To do this we call capture with CTRL-0 SPC m c instead of just SPC m c.

    Click to expand
    ("bm" "Margin note (single-line)" plain
      (file )
      "@@html:<label for=\"mn-%^{name}\" class=\"margin-toggle\">&raquo;</label><input type=\"checkbox\" id=\"mn-%\\1\" class=\"margin-toggle\"/><span class=\"marginnote\">@@%^{Note}@@html: </span>@@" 
      :immediate-finish t)
    
    ("bs" "Sidenote (single-line)" plain
      (file )
      "@@html:<label for=\"sn-%^{name}\" class=\"margin-toggle sidenote-number\"></label><input type=\"checkbox\" id=\"sn-%\\1\" class=\"margin-toggle\"/><span class=\"sidenote\">@@%^{Note}@@html: </span>@@" 
      :immediate-finish t)
    
    ("bM" "Margin note (multi-line)" plain
      (file )
      "\#+BEGIN_EXPORT html
       <label for=\"mn-%^{name}\" class=\"margin-toggle\">&raquo;</label><input type=\"checkbox\" id=\"mn-%\\1\" class=\"margin-toggle\"/>
        <span class=\"marginnote\">
        \#+END_EXPORT
        %?
        \#+BEGIN_EXPORT html
        </span>
        \#+END_EXPORT" )
    
    ("bS" "Side note (multi-line)" plain
      (file )
      "\#+BEGIN_EXPORT html
       <label for=\"sn-%^{name}\" class=\"margin-toggle sidenote-number\"></label><input type=\"checkbox\" id=\"sn-%\\1\" class=\"margin-toggle\"/>
        <span class=\"sidenote\">
        \#+END_EXPORT
        %?
        \#+BEGIN_EXPORT html
        </span>
        \#+END_EXPORT" )
    
    ("be" "Collapsed block" plain
     (file )
     "\#+BEGIN_EXPORT html
      <details>
      <summary>%^{Note}</summary>
      \#+END_EXPORT
      %?
      \#+BEGIN_EXPORT html
      </details>
      \#+END_EXPORT")
    

Notes and Tasks

("n" "Notes" entry
 (file+function "~/Dropbox/organizer/phd.org" org-reverse-datetree-goto-date-in-file)
 "* %^{Description} %^g
  Added: %t
  %?

 ")

("t" "Task" entry
 (file+function "~/Dropbox/organizer/phd.org" org-reverse-datetree-goto-date-in-file)
 "* TODO %^{Description} %^gkanban:
  Added: %t
  %?

 ")

SPC m c n and SPC m c t add a note and a TODO respectively, to a reverse-datetree entry in my phd.org file, with a description, some tags, and a line that says the date it was added. I use tags to sort through different projects, with CTRL-c / m <tag name> - this folds everything and highlights only headings matching the tag. The phd.org file also has an org-kanban entry at the beginning, initialized via ALT-x-org-kanban/initialize-at-beginning, and configured with kanban :mirrored t :match "kanban" :range ("TODO" . "DONE"). This ensures I don’t go overboard with my scheduling and can easily shift between TODO, RUNNING and DONE. You’ll notice that “kanban” is automatically added as a tag to a task and only this tag is considered in the board, so at the end of the day I can get rid of this tag for the finished tasks and they won’t show up anymore in my board. Needed an extra newline at the end of the templates otherwise Spacemacs started messing up the formatting everywhere.

Schedule meetings

("m" "Meeting" entry
 (file+headline "~/Dropbox/organizer/phd.org" "Meetings/People-related")
 "** MEETING %^{Description} %^g
  SCHEDULED: %^{Date}T
  %?

")

SPC m c m adds a new MEETING heading under the “Meetings/People-related” heading in the phd.org file (you specify this with “file+headline”). It asks for a description, tags, and a date (with %^{Date}T) that you can just click on in a tiny calendar popup.

Log activities

("l" "Log Time" entry
 (file+function "~/Dropbox/organizer/daily_notes.org" org-reverse-datetree-goto-date-in-file)
 "** %U - %^{Activity}  %^g"
 :immediate-finish t)
)))

A lot of org-enthusiasts use something called Clocking where you clock in / punch in when you start working on something and then clock out when you’re done. This can come in handy if you have to bill people based on how much time you spent on their project, or if you just want to see how you spend your time. I couldn’t really get into it though - too lazy I guess. So this is kind of my compromise - SPC m c l logs a single-sentence activity with tags and time to a daily notes file. I can see what days I worked on which projects with tag search, and keep track of small steps without worrying too much about forgetting to clock in/out. the “:immediate-finish t” means I just click Enter after typing in my Activity and entering tags, and it saves it to the file without any fuss.

Aesthetics

Olivetti mode

;; Text takes up 85% of the buffer
(setq olivetti-body-width 0.85)
;; Starts text files (like .org .txt .md) in olivetti mode
(add-hook 'text-mode-hook 'olivetti-mode)

Olivetti puts all the buffer text in the middle, so that there’s some breathing space around the edges, like a book. body-width makes the text take up 85% of the buffer and the add-hook makes all text-based files like .org, .md, and .txt open automatically in olivetti mode. You can also set this per buffer with ALT-x-olivetti-mode and ALT-x-olivetti-set-width.

Checkboxes

Got this piece of awesomeness from this post at JFT’s notes.

(add-hook 'org-mode-hook (lambda ()
			   "Beautify Org Checkbox Symbol"
			   (push '("[ ]" .  "☐") prettify-symbols-alist)
			   (push '("[X]" . "☑" ) prettify-symbols-alist)
			   (push '("[-]" . "❍" ) prettify-symbols-alist)
			   (prettify-symbols-mode)))
(defface org-checkbox-done-text
'((t (:foreground "#71696A")))
"Face for the text part of a checked org-mode checkbox.")

Look!

org-checkbox.png

Variable pitch

The default Spacemacs font should be set to something that’s fixed-pitch or fixed-width or monospace - i.e. each letter takes the same amount of horizontal space. This is pretty necessary for code and tables and looks better for things like TODO and SCHEDULED etc. But the normal text in an org file doesn’t have any restrictions and (to me) looks better with the kinds of fonts you find in books and newspapers and blog posts.

So I downloaded ETBembo, a font by Edward Tufte, the go to guy for visualization, and set it as my variable-pitch font (i.e. not fixed-pitch). Then I used ALT-x customize-group org-faces to decide which parts of an org-file should be fixed-pitch (by typing “fixed-pitch” in the Inherit box) and which parts should be variable-pitch. I went a bit crazy here and made some things italics and bold and purple etc. but this highlights one of the nice things about Spacemacs - you don’t have to type things in to your config file by hand. All of the settings so far could just as easily have been set via ALT-x customize-variable or ALT-x customize-face which opens up a buffer with options and dropdown boxes like a GUI tool would have. I like using it for things where I want to experiment with how different options look before setting them down in stone in .spacemacs.

(custom-set-faces
     ;; custom-set-faces was added by Custom.
     ;; If you edit it by hand, you could mess it up, so be careful.
     ;; Your init file should contain only one such instance.
     ;; If there is more than one, they won't work right.
     '(variable-pitch ((t (:weight normal :height 1.1 :width normal :family "ETBembo"))))
     '(fixed-pitch ((t (:family "Jetbrains Mono"))))
     '(org-block ((t (:inherit (shadow fixed-pitch)))))
     '(org-checkbox ((t (:foreground "Pink" :inherit (bold fixed-pitch)))))
     '(org-checkbox-statistics-done ((t (:inherit org-done))))
     '(org-code ((t (:inherit (shadow fixed-pitch)))))
     '(org-default ((t (:inherit variable-pitch :family "ETBembo"))))
     '(org-document-info ((t (:inherit fixed-pitch :foreground "pale turquoise"))))
     '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
     '(org-document-title ((t (:inherit default :weight bold :foreground "#c0c5ce" :font "ETBembo" :height 1.5 :underline nil))))
     '(org-done ((t (:inherit fixed-pitch :foreground "PaleGreen" :weight bold))))
     '(org-footnote ((t (:inherit variable-pitch :foreground "Cyan" :underline t))))
     '(org-indent ((t (:inherit (org-hide fixed-pitch)))))
     '(org-level-1 ((t (:inherit variable-pitch :foreground "#c2c2b0" :slant italic :weight semi-light :height 1.75 :width normal :foundry "nil" :family "ETBembo"))))
     '(org-level-2 ((t (:inherit variable-pitch :foreground "#c2c2b0" :height 1.5 :width normal :foundry "nil" :family "ETBembo"))))
     '(org-level-3 ((t (:inherit variable-pitch :foreground "#b0a2e7" :slant italic :weight normal :height 1.25 :width normal :foundry "nil" :family "ETBembo"))))
     '(org-level-4 ((t (:inherit variable-pitch))))
     '(org-level-5 ((t (:inherit default :weight bold :foreground "#c0c5ce" :font "ETBembo"))))
     '(org-level-6 ((t (:inherit default :weight bold :foreground "#c0c5ce" :font "ETBembo"))))
     '(org-level-7 ((t (:inherit default :weight bold :foreground "#c0c5ce" :font "ETBembo"))))
     '(org-level-8 ((t (:inherit default :weight bold :foreground "#c0c5ce" :font "ETBembo"))))
     '(org-link ((t (:inherit (link variable-pitch) :slant normal))))
     '(org-list-dt ((t (:inherit fixed-pitch :foreground "#819cd6" :weight bold))))
     '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
     '(org-property-value ((t (:inherit fixed-pitch))) t)
     '(org-quote ((t (:background "#32353f" :family "ETBembo"))))
     '(org-todo ((t (:inherit fixed-pitch :foreground "Pink" :weight bold))))
     '(org-verbatim ((t (:inherit (shadow fixed-pitch)))))
     '(org-verse ((t (:inherit fixed-pitch))))
     )
)

This part still has some minor issues though. By itself it looks like this:

org-variable-pitch-off.png

So, the headings look good but the font for text under headings and in lists is still fixed-width. But when I turn on variable-pitch mode (ALT-x variable-pitch-mode) then it looks like this:

org-variable-pitch-on.png

The text is variable-pitch but the TODOs and SCHEDULED and stuff are suddenly variable-pitch too. No idea why this happens but I’ve grown fond of the first option for phd.org (since it’s mostly headings with short notes) and the second option for blog posts (since they usually don’t have TODOs) and both still look good so this is a problem for another day.

Other

Some settings I found here and there I think I got most of the aesthetics stuff from this post, but I did a lot of random searching when I was beautifying org so it’s very likely I’m forgetting to credit some people. that makes Org mode look better.

;; Makes some things look nicer
(setq org-startup-indented t
      org-pretty-entities t
      ;; show actually italicized text instead of /italicized text/
      org-hide-emphasis-markers t
      org-agenda-block-separator ""
      org-fontify-whole-heading-line t
      org-fontify-done-headline t
      org-fontify-quote-and-verse-blocks t)
  • org-startup-indented - Vertically aligns text under headings to the heading itself (like in the example here)
  • org-pretty-entities - For LaTeX symbols like α (typed as alpha with a “\” in front) and ∑. I thought this was pretty much only for math symbols but turns out there’s a ginormous list of entities you can use (run ALT-x-org-entities-help in a buffer). There’s even smileys ☻. You can even add your own!
  • The org-fontifys make it so that if you change the font / background color etc. of the heading / done / quote block / verse block then the change shows up for the whole line (across the buffer) instead of just the part with the text.

Next Steps

These options have made for a good workflow so far. There are plenty more packages to check out though. There’s things like grasp (a Firefox/Chrome add-on which lets you capture content from the web with capture templates) and org-download (which lets you drag and drop images from your computer or your browser into an org file) for making it easier to deal with the world outside of orgSpeaking of, I also use Orgzly synced to Dropbox for seeing my org files on my phone - it’s quite nice, let’s me make an agenda widget so I can track things I need to do while at lunch or somewhere without my laptop (though tbh I’m much more likely to have my laptop on me these days than my phone). . I’d also love to get something set up that downloads PDFs of publications from journal RSS feeds (with elfeed?) like Bioinformatics, Nature, eLife etc. so that it’s easier to read (pdf-tools) and annotate (org-noter) them right away.



For comments, click the arrow at the top right corner.