Last update: April 9, 2018
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 https://github.com/zzamboni/dot_emacs/blob/master/init.el.
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.
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.
(setq custom-file "~/.emacs.d/custom.el")
Here is the current contents of my custom.el file.
;; custom-set-variables 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.
("6ac7c0f959f0d7853915012e78ff70150bfbe2a69a1b703c3ac4184f9ae3ae02" "8e4efc4bed89c4e67167fdabff77102abeb0b1c203953de1e6ab4d2e3a02939a" "a1a966cf2e87be6a148158c79863440ba2e45aa06cc214341feafe5c6deca4f2" "3eb2b5607b41ad8a6da75fe04d5f92a46d1b9a95a202e3f5369e2cdefb7aac5c" "3d0142352ce19c860047ad7402546944f84c270e84ae479beddbc2608268e0e5" "a33858123d3d3ca10c03c657693881b9f8810c9e242a62f1ad6380adf57b031c" "a40eac965142a2057269f8b2abd546b71a0e58e733c6668a62b1ad1aa7669220" "7be789f201ea16242dab84dd5f225a55370dbecae248d4251edbd286fe879cfa" "94dac4d15d12ba671f77a93d84ad9f799808714d4c5d247d5fd944df951b91d6" "4d8fab23f15347bce54eb7137789ab93007010fa47296c2f36757ff84b5b3c8a" default)))
("sh" "bash" "zsh" "fish" "csh" "ash" "dash" "ksh" "mksh" "posh")))
(("a" . "export ascii")
("c" . "center")
("C" . "comment")
("e" . "example")
("E" . "export")
("h" . "export html")
("l" . "export latex")
("q" . "quote")
("s" . "src")
("v" . "verse")
("n" . "note")
("d" . "description"))))
(helm-flx which-key spaceline pretty-mode visual-regexp-steroids ox-hugo adaptive-wrap yankpad smart-mode-line org-plus-contrib ob-cfengine3 org-journal ox-asciidoc org-jira ox-jira org-bullets ox-reveal lispy parinfer uniquify csv all-the-icons toc-org helm cider clojure-mode ido-completing-read+ writeroom-mode crosshairs ox-confluence ox-md inf-ruby ob-plantuml ob-ruby darktooth-theme kaolin-themes htmlize ag col-highlight nix-mode easy-hugo elvish-mode zen-mode racket-mode package-lint scala-mode go-mode wc-mode neotree applescript-mode ack magit clj-refactor yaml-mode visual-fill-column visible-mark use-package unfill typopunct smooth-scrolling smex smartparens rainbow-delimiters projectile markdown-mode magit-popup lua-mode keyfreq imenu-anywhere iedit ido-ubiquitous hl-sexp gruvbox-theme git-commit fish-mode exec-path-from-shell company clojure-mode-extra-font-locking clojure-cheatsheet aggressive-indent adoc-mode 4clojure)))
'(reb-re-syntax (quote string))
(org-edit-src-content-indentation . 2))))
;; 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.
'(default ((t (:inherit nil :stipple nil :background "#282828" :foreground "#FDF4C1" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 160 :width normal :foundry "nil" :family "Inconsolata"))))
'(col-highlight ((t (:background "#3c3836"))))
'(fixed-pitch ((t (:family "Inconsolata"))))
'(linum ((t (:background "#282828" :foreground "#504945" :height 140 :family "Inconsolata"))))
'(markup-meta-face ((t (:foreground "gray40" :height 140 :family "Inconsolata"))))
'(markup-title-0-face ((t (:inherit markup-gen-face :height 1.6))))
'(markup-title-1-face ((t (:inherit markup-gen-face :height 1.5))))
'(markup-title-2-face ((t (:inherit markup-gen-face :height 1.4))))
'(markup-title-3-face ((t (:inherit markup-gen-face :weight bold :height 1.3))))
'(markup-title-5-face ((t (:inherit markup-gen-face :underline t :height 1.1))))
'(org-block ((t (:inherit fixed-pitch))))
'(org-document-info ((t (:foreground "dark orange"))))
'(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
'(org-document-title ((((class color) (min-colors 16777215)) (:foreground "#3FD7E5" :weight bold)) (((class color) (min-colors 255)) (:foreground "#00d7ff" :weight bold))))
'(org-level-1 ((((class color) (min-colors 16777215)) (:foreground "#FE8019")) (((class color) (min-colors 255)) (:foreground "#ff8700"))))
'(org-level-2 ((((class color) (min-colors 16777215)) (:foreground "#B8BB26")) (((class color) (min-colors 255)) (:foreground "#afaf00"))))
'(org-level-3 ((((class color) (min-colors 16777215)) (:foreground "#83A598")) (((class color) (min-colors 255)) (:foreground "#87afaf"))))
'(org-level-4 ((((class color) (min-colors 16777215)) (:foreground "#FABD2F")) (((class color) (min-colors 255)) (:foreground "#ffaf00"))))
'(org-level-5 ((((class color) (min-colors 16777215)) (:foreground "#427B58")) (((class color) (min-colors 255)) (:foreground "#5f8787"))))
'(org-level-6 ((((class color) (min-colors 16777215)) (:foreground "#B8BB26")) (((class color) (min-colors 255)) (:foreground "#afaf00"))))
'(org-level-7 ((((class color) (min-colors 16777215)) (:foreground "#FB4933")) (((class color) (min-colors 255)) (:foreground "#d75f5f"))))
'(org-level-8 ((((class color) (min-colors 16777215)) (:foreground "#83A598")) (((class color) (min-colors 255)) (:foreground "#87afaf"))))
'(org-link ((t (:foreground "royal blue" :underline t))))
'(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
'(org-property-value ((t (:inherit fixed-pitch))) t)
'(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
'(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
'(org-verbatim ((t (:inherit (shadow fixed-pitch)))))
'(variable-pitch ((t (:weight light :height 180 :family "Source Sans Pro")))))
Setting up the package system
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.
(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
("marmalade" . "https://marmalade-repo.org/packages/")
("melpa" . "https://melpa.org/packages/")
;;("org" . "http://orgmode.org/elpa/")
Then we initialize the package system, refresh the list of packages and install
use-package if needed.
(when (not package-archive-contents)
(when (not (package-installed-p 'use-package))
We set some configuration for
(setq use-package-always-ensure t)
(setq use-package-verbose t)
This variable tells Emacs to prefer the
.el file if it’s newer, even if there is a corresponding
.elc file. Also, use
auto-compile to autocompile files as needed.
(setq load-prefer-newer t)
Set the load path to the directories from where I sometimes load things outside the package system. For now I am loading
org-mode from a checkout of its git repository, so I load all its packages and the contrib packages from there.
(add-to-list 'load-path "~/.emacs.d/lisp")
(add-to-list 'load-path "~/.emacs.d/lisp/org-mode/lisp")
(add-to-list 'load-path "~/.emacs.d/lisp/org-mode/contrib/lisp")
Before, I used to manually install the
org-plus-contrib package. The code below is disabled for now, but kept here for reference.
(when (not (package-installed-p 'org-plus-contrib))
org right away, to avoid any interference with the version of
org included with Emacs.
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.
(defun zz/set-proxy ()
(setq url-proxy-services '(("http" . "proxy.corproot.net:8079")
("https" . "proxy.corproot.net:8079"))))
(defun zz/unset-proxy ()
(setq url-proxy-services nil))
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.
(add-hook 'before-save-hook 'time-stamp)
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.
(setq mouse-yank-at-point t)
Make completion case-insensitive.
(setq completion-ignore-case t)
(setq read-file-name-completion-ignore-case t)
Show line numbers (disable for now because it causes performance issues in very large buffers).
Highlight trailing whitespace in red, so it’s easily visible
(setq-default show-trailing-whitespace t)
Highlight matching parenthesis
Don’t use hard tabs
(setq-default indent-tabs-mode nil)
Emacs can automatically create backup files. This tells Emacs to put all backups in ~/.emacs.d/backups.
(setq backup-directory-alist `(("." . ,(concat user-emacs-directory "backups"))))
WinnerMode makes it possible to cycle and undo window configuration changes (i.e. arrangement of panels, etc.)
(when (fboundp 'winner-mode) (winner-mode 1))
Add “unfill” commands to parallel the “fill” ones, bind A-q to
unfill-paragraph and rebind M-q to the
unfill-toggle command, which fills/unfills paragraphs alternatively.
(global-set-key (kbd "M-q") 'unfill-toggle)
(global-set-key (kbd "A-q") 'unfill-paragraph)
Save the place of the cursor in each file, and restore it upon opening it again.
(setq-default save-place t)
(setq save-place-file (concat user-emacs-directory "places")))
Provide mode-specific “bookmarks” - press
M-i and you will be presented with a list of elements to which you can navigate - they can be headers in org-mode, function names in emacs-lisp, etc.
(global-set-key (kbd "M-i") 'helm-imenu-anywhere))
Smooth scrolling (line by line) instead of jumping by half-screens.
Delete trailing whitespace before saving a file.
(add-hook 'before-save-hook 'delete-trailing-whitespace)
M-g interactively asks for a line number and jump to it (
(global-set-key [(meta g)] 'goto-line)
M-` focuses the next frame, if multiple ones are active (emulate the Mac “next app window” keybinding)
(global-set-key [(meta \`)] 'other-frame)
Interactive search key bindings - make regex search the default. By default,
isearch-forward, so this swaps the bindings.
(global-set-key (kbd "C-s") 'isearch-forward-regexp)
(global-set-key (kbd "C-r") 'isearch-backward-regexp)
(global-set-key (kbd "C-M-s") 'isearch-forward)
(global-set-key (kbd "C-M-r") 'isearch-backward)
Key binding to use “hippie expand” for text autocompletion
(global-set-key (kbd "M-/") 'hippie-expand)
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, that’s it, nothing else. It’s beautiful.
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.
(defun zz/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."
(if (not (memq last-command '(set-mark
(self-insert-command (or arg 1))
(cond ((looking-at "\\s\(") (sp-forward-sexp) (backward-char 1))
((looking-at "\\s\)") (forward-char 1) (sp-backward-sexp))
(t (self-insert-command (or arg 1))))))
We bind this function to the
(global-set-key (kbd "%") 'zz/goto-match-paren)
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.
use-package to load the
org package, and put all its Configuration inside the
:config section (
<<org-mode-config>> is replaced with all the org-related configuration blocks below).
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.
(define-key global-map "\C-cl" 'org-store-link)
C-c a to call up agenda mode.
(define-key global-map "\C-ca" 'org-agenda)
The default keybinding for
M-h, which in macOS hides the current application, so I bind it to
(define-key global-map (kbd "A-h") 'org-mark-element)
Default setup and keybinding for
(setq org-default-notes-file (concat org-directory "/notes.org"))
(define-key global-map "\C-cc" 'org-capture)
org-tempo to enable snippets such as
<s<TAB> to insert a source block.
General org-mode configuration
org-directory to a directory inside my Dropbox so that my main files get synchronized automatically.
(setq org-directory "~/Dropbox/org")
Automatically log done times in todo items (I haven’t used this much yet).
Keep the indentation well structured by. OMG 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! I added the
diminish line, which removes the “Ind” indicator from the modeline.
(setq org-startup-indented t)
(eval-after-load 'org-indent '(diminish 'org-indent-mode))
Building presentations with org-mode
org-reveal is an awesome package for building presentations with org-mode.
;; Set this to nil because a bug in ox-reveal otherwise breaks org-structure-template-alist
(setq org-reveal-note-key-char nil)
(setq org-reveal-root "file:///Users/taazadi1/Dropbox/org/reveal.js")
One of the big strengths of org-mode is the ability to export a document in many different formats. Here I load some of the exporters I have found useful.
Jira markup. I also load
org-jira, which provides a full interface to Jira through org-mode.
;; (setq jiralib-url "https://tracker.mender.io:443")
(setq jiralib-url "https://jira.swisscom.com")
(setq org-jira-working-dir "~/.org-jira"))
Confluence markup. This is included in org’s contrib, so we just load it with
require instead of
TexInfo. I have found that the best way to produce a PDF from an org file is to export it to a
.texi file, and then use
texi2pdf to produce the PDF.
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.
Configure a capture template for creating new blog posts, from https://ox-hugo.scripter.co/doc/org-capture-setup.
(defun org-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: ")) ;Prompt to enter the post title
(fname (org-hugo-slug title)))
,(concat "* TODO " title)
,(concat ":EXPORT_HUGO_BUNDLE: " fname)
"%?\n") ;Place the cursor here finally
'("z" ;`org-capture' binding + h
;; It is assumed that below file is present in `org-directory'
;; and that it has a "Blog Ideas" heading. It can even be a
;; symlink pointing to the actual location of all-posts.org!
(file+olp "zzamboni.org" "Ideas")
Avoid auto-generate-on-save for
org-capture’d notes, from https://ox-hugo.scripter.co/doc/auto-export-on-saving/.
;; Do not cause auto Org->Hugo export to happen when saving captures
(defun modi/org-capture--remove-auto-org-to-hugo-export-maybe ()
"Function for `org-capture-before-finalize-hook'.
(setq org-hugo-allow-export-after-save nil))
(defun modi/org-capture--add-auto-org-to-hugo-export-maybe ()
"Function for `org-capture-after-finalize-hook'.
(setq org-hugo-allow-export-after-save t))
(add-hook 'org-capture-before-finalize-hook #'modi/org-capture--remove-auto-org-to-hugo-export-maybe)
(add-hook 'org-capture-after-finalize-hook #'modi/org-capture--add-auto-org-to-hugo-export-maybe))
Keeping a Journal
I use 750words for my personal Journal, and I usually write my entries locally using Scrivener. I have been playing with
org-journal for this, but I am not fully convinced yet.
(setq org-journal-dir "~/Documents/logbook"))
Literate programming using org-babel
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.)
Plain literate programming is built-in, but the
ob-* packages provide the ability to execute code in different languages, beyond those included with org-mode.
'((cfengine3 . t)
(ruby . t)
(latex . t)
(plantuml . t)
(python . t)
(shell . t)
(elvish . t)
(calc . t)))
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.
(setq org-confirm-babel-evaluate nil)
This makes it so that code within
src blocks is fontified according to their corresponding Emacs mode, making the file much more readable.
(setq org-src-fontify-natively t)
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
(setq org-src-tab-acts-natively t)
Automatically show inline images, useful when executing code that produces them, such as PlantUML or Graphviz.
(add-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images)
This little snippet 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.
(lambda () (add-hook 'after-save-hook 'org-babel-tangle
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 by me.
(setq org-hide-emphasis-markers t)
'(("^ *\\([-]\\) "
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))
(cond ((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. Install Source Sans Pro."))))
(base-font-color (face-foreground 'default nil 'default))
(headline `(:inherit default :weight bold :foreground ,base-font-color)))
`(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 :height 1.1))))
`(org-level-3 ((t (,@headline ,@variable-tuple :height 1.25))))
`(org-level-2 ((t (,@headline ,@variable-tuple :height 1.5))))
`(org-level-1 ((t (,@headline ,@variable-tuple :height 1.75))))
`(org-document-title ((t (,@headline ,@variable-tuple :height 2.0 :underline nil))))))
I am experimenting with using proportional fonts in org-mode for the text, while keeping fixed-width fonts for blocks, so that source code, tables, etc. are shown correctly. I am currently playing with these settings, which include:
- 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.
Setting up the
variable-pitch face (I only learned of its existence now while figuring this out) to the proportional font I like to use. I’m currently using Source Sans Pro. Another favorite is Avenir Next.
'(variable-pitch ((t (:family "Source Sans Pro" :height 180 :weight light)))))
Setting up the
fixed-pitch face to be the same as my usual
default face. My current one is Inconsolata.
'(fixed-pitch ((t (:family "Inconsolata")))))
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 a hook that automatically enables
variable-pitch-mode when entering org-mode.
(add-hook 'org-mode-hook 'visual-line-mode)
(add-hook 'org-mode-hook 'variable-pitch-mode)
These two modes produce modeline indicators, which I disable using
(eval-after-load 'face-remap '(diminish 'buffer-face-mode))
(eval-after-load 'simple '(diminish 'visual-line-mode))
Auto-generated table of contents
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.
(add-hook 'org-mode-hook 'toc-org-enable))
Grabbing links from different Mac applications
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.
(add-hook 'org-mode-hook (lambda ()
(define-key org-mode-map (kbd "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.
(defun zz/org-reformat-buffer ()
(when (y-or-n-p "Really format current buffer? ")
(let ((document (org-element-interpret-data (org-element-parse-buffer))))
Snippets and templates
The yankpad package makes it easy to store snippets that can be inserted at arbitrary points. Together with yasnippet it becomes more powerful.
(setq yankpad-file "~/Dropbox/org/yankpad.org")
(bind-key "<f7>" 'yankpad-map)
(bind-key "<f12>" 'yankpad-expand)
;; If you want to expand snippets with hippie-expand
(add-to-list 'hippie-expand-try-functions-list #'yankpad-expand))
Some settings maybe OS-specific, and this is where we set them. For now I only use Emacs on my Mac, so only the Mac section is filled out, but there are sections for Linux and Windows as well.
(cond ((eq system-type 'darwin)
((eq system-type 'windows-nt)
((eq system-type 'gnu/linux)
First, we set the key modifiers correctly to my preferences: Make Command (⌘) act as Meta, Option as Alt, right-Option as Super
(setq mac-command-modifier 'meta)
(setq mac-option-modifier 'alt)
(setq mac-right-option-modifier 'super)
We also make it possible to use the familiar
⌘-- to increase and decrease the font size. ⌘-= is also bound to “increase” because it’s on the same key in an English keyboard.
(global-set-key (kbd "M-+") 'text-scale-increase)
(global-set-key (kbd "M-=") 'text-scale-increase)
(global-set-key (kbd "M--") 'text-scale-decrease)
Somewhat surprisingly, there seems to be no “reset” function, so I define my own and bind it to
(defun zz/text-scale-reset ()
(global-set-key (kbd "M-0") 'zz/text-scale-reset)
We also use the
exec-path-from-shell to make sure the path settings from the shell are loaded into Emacs (usually it starts up with the default system-wide path).
There are no Linux-specific settings for now.
There are no Windows-specific settings for now.
Appearance, buffer/file management and theming
Here we take care of all the visual, UX and desktop-management settings.
diminish package makes it possible to remove clutter from the modeline. Here we just load it, it gets enabled for individual packages in their corresponding declarations.
I have been playing with different themes, and I have settled for now in
gruvbox. Some of my other favorites are also here so I don’t forget about them.
Install smart-mode-line for modeline goodness.
Enable desktop-save mode, which saves the current buffer configuration on exit and reloads it on restart.
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 in Emacs, so I specify
:ensure nil so that
use-package doesn’t try to install it, and just loads and configures it.
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-buffer-name-style 'post-forward)
(setq uniquify-strip-common-suffix nil))
I like to highlight the current line and column. I’m still deciding between two approaches:
- Using the built-in
global-hl-mode to always highlight the current line, together with the
col-highlight package, which highlights the column only after a defined interval has passed
- Using the
crosshairs package, which combines both but 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, I’m only undecided about the always-on column highlighting.
Sometimes I find the always-highlighted column to be distracting, but other times I find it useful. So I have both pieces of code here, I’m still deciding.
;; (use-package crosshairs
It’s one of those things where I genuinely have to wonder why there is no built in functionality for it. Once in a blue moon I need to kill all buffers, and having ~150 of them open would mean I’d need to spend a few too many seconds doing this than I’d like, here’s a solution.
This can be invoked using
C-M-s-k. This keybinding makes sure you don’t hit it unless you really want to.
(defun close-all-buffers ()
"Kill all buffers without regard for their origin."
(mapc 'kill-buffer (buffer-list)))
(global-set-key (kbd "C-M-s-k") 'close-all-buffers)
Completion: IDO or Helm?
The battle rages on - 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.
Should I also look at ivy?
I use IDO mode to get better matching capabilities everywhere in Emacs (disabled while I give helm a try, see below).
(setq ido-use-virtual-buffers t)
(setq ido-enable-flex-matching t)
(setq ido-use-filename-at-point nil)
(setq ido-auto-merge-work-directories-length -1))
This config came originally from Uncle Dave’s Emacs config, thought I have tweaked it a bit.
("C-x C-f" . 'helm-find-files)
("C-x C-b" . 'helm-buffers-list)
("C-x b" . 'helm-multi-files)
("M-x" . 'helm-M-x)
;; (defun daedreth/helm-hide-minibuffer ()
;; (when (with-helm-buffer helm-echo-input-in-header-line)
;; (let ((ov (make-overlay (point-min) (point-max) nil nil t)))
;; (overlay-put ov 'window (selected-window))
;; (overlay-put ov 'face
;; (let ((bg-color (face-background 'default nil)))
;; `(:background ,bg-color :foreground ,bg-color)))
;; (setq-local cursor-type nil))))
;; (add-hook 'helm-minibuffer-set-up-hook 'daedreth/helm-hide-minibuffer)
(setq helm-autoresize-max-height 0
(define-key helm-find-files-map (kbd "C-b") 'helm-find-files-up-one-level)
(define-key helm-find-files-map (kbd "C-f") 'helm-execute-persistent-action)
(setq helm-flx-for-helm-find-files t ;; t by default
helm-flx-for-helm-locate t) ;; nil by default
I also use
recentf to keep a list of recently open buffers, and define a function to trigger recentf with IDO integration, using
C-x C-r as the keybinding (disabled while I figure out how to integrate with helm).
;; (defun ido-recentf-open ()
;; "Use `ido-completing-read' to \\[find-file] a recent file"
;; (if (find-file (ido-completing-read "Find recent file: " recentf-list))
;; (message "Opening file...")
;; (message "Aborting")))
(setq recentf-max-menu-items 50)
;; (global-set-key (kbd "C-x C-r") 'ido-recentf-open)
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
(global-set-key (kbd "C-x C-b") 'ibuffer))
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
:bind (("M-x" . smex))
midnight-mode purges buffers which haven’t been displayed in 3 days. We configure the period so that the cleanup happens every 2 hours (7200 seconds).
(setq midnight-mode 't)
(setq midnight-period 7200))
For distraction-free writing, I’m testing out
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.
(setq neo-theme (if (display-graphic-p) 'icons 'arrow))
(setq neo-smart-open t)
(setq projectile-switch-project-action 'neotree-projectile-action)
(defun neotree-project-dir ()
"Open NeoTree using the git root."
(let ((project-dir (projectile-project-root))
(message "Could not find git project root."))))
(global-set-key [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.
all-the-icons package provides a number of useful icons.
Coding is my main use for Emacs, so it’s understandably the largest section in my Emacs configuration.
General settings and modules
subword allows navigating “sub words” individually in CamelCaseIdentifiers. For now I only enable it in
(add-hook 'clojure-mode-hook #'subword-mode))
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.
(add-hook 'prog-mode-hook #'aggressive-indent-mode))
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.
(add-hook 'after-init-hook #'global-company-mode))
projectile-mode allows us to perform project-relative operations such as searches, navigation, etc.
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.
:config (set-face-background 'iedit-occurrence "Magenta"))
Turn on the online documentation mode for all programming modes (not all of them support it) and for the Clojure REPL
(add-hook 'prog-mode-hook #'turn-on-eldoc-mode)
(add-hook 'cider-repl-mode-hook #'turn-on-eldoc-mode))
On-the-fly spell checking. I enable it for all text modes.
(define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word)
(define-key flyspell-mouse-map [mouse-3] #'undefined)
(add-hook 'text-mode-hook 'flyspell-mode))
Clojure and LISP coding
I dabble in Clojure and Emacs LISP, and Emacs has some fantastic support for them. There’s a number of packages and configuration related to this, so I have a whole section for it.
The centerpiece is of course
clojure-mode. In addition to files ending in
.clj, I bind it automatically to
.boot files (both by extension and by shebang line) and to the Riemann config files.
(add-to-list 'magic-mode-alist '(".* boot" . clojure-mode)))
Enable some additional fontification for Clojure code.
cider package provides a fantastic REPL built into Emacs. We configure a few aspects, including pretty printing, fontification, history size and others.
;; nice pretty printing
(setq cider-repl-use-pretty-printing nil)
;; nicer font lock in REPL
(setq cider-repl-use-clojure-font-lock t)
;; result prefix for the REPL
(setq cider-repl-result-prefix "; => ")
;; never ending REPL history
(setq cider-repl-wrap-history t)
;; looong history
(setq cider-repl-history-size 5000)
;; persistent history
(setq cider-repl-history-file "~/.emacs.d/cider-history")
;; error buffer not popping up
(setq cider-show-error-buffer nil)
;; go right to the REPL buffer when it's finished connecting
(setq cider-repl-pop-to-buffer-on-connect t))
clj-refactor for supporting advanced code refactoring in Clojure.
(defun my-clojure-mode-hook ()
(yas-minor-mode 1) ; for adding require/use/import statements
;; This choice of keybinding leaves cider-macroexpand-1 unbound
(cljr-add-keybindings-with-prefix "C-c C-m"))
(add-hook 'clojure-mode-hook #'my-clojure-mode-hook))
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.
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode)
(add-hook 'cider-repl-mode-hook #'rainbow-delimiters-mode))
Another useful addition for LISP coding -
smartparens enforces parenthesis to match, and adds a number of useful operations for manipulating parenthesized expressions.
(setq sp-base-key-bindings 'paredit)
(add-hook 'clojure-mode-hook #'smartparens-strict-mode)
(add-hook 'emacs-lisp-mode-hook #'smartparens-strict-mode)
(add-hook 'lisp-mode-hook #'smartparens-strict-mode)
(add-hook 'cider-repl-mode-hook #'smartparens-strict-mode))
M-( to enclose the next expression, as in
paredit. 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.
(defun zz/sp-enclose-next-sexp (num) (interactive "p") (insert-parentheses (or num 1)))
(add-hook 'smartparens-mode-hook #'sp-use-paredit-bindings)
(add-hook 'smartparens-mode-hook (lambda () (local-set-key (kbd "M-(") 'zz/sp-enclose-next-sexp)))
Minor mode for highlighting the current sexp in LISP modes.
(add-hook 'clojure-mode-hook #'hl-sexp-mode)
(add-hook 'lisp-mode-hook #'hl-sexp-mode)
(add-hook 'emacs-lisp-mode-hook #'hl-sexp-mode))
Trying out lispy for LISP code editing (disabled for now).
(defun enable-lispy-mode ()
(add-hook 'clojure-mode-hook #'enable-lispy-mode)
(add-hook 'emacs-lisp-mode-hook #'enable-lispy-mode)
(add-hook 'common-lisp-mode-hook #'enable-lispy-mode)
(add-hook 'scheme-mode-hook #'enable-lispy-mode)
(add-hook 'lisp-mode-hook #'enable-lispy-mode))
I am sometimes trying out parinfer (disabled for now).
(("C-," . parinfer-toggle-mode))
'(defaults ; should be included.
pretty-parens ; different paren styles for different modes.
;;evil ; If you use Evil.
lispy ; If you use Lispy. With this extension, you should install Lispy and do not enable lispy-mode directly.
paredit ; Introduce some paredit commands.
smart-tab ; C-b & C-f jump positions and smart shift with tab & S-tab.
smart-yank)) ; Yank behavior depend on mode.
(add-hook 'clojure-mode-hook #'parinfer-mode)
(add-hook 'emacs-lisp-mode-hook #'parinfer-mode)
(add-hook 'common-lisp-mode-hook #'parinfer-mode)
(add-hook 'scheme-mode-hook #'parinfer-mode)
(add-hook 'lisp-mode-hook #'parinfer-mode)))
Other programming languages
Many other programming languages are well served by a single mode, without so much setup as Clojure/LISP.
git interface with some simple configuration I picked up somewhere
(defadvice magit-status (around magit-fullscreen activate)
"Make magit-status run alone in a frame."
(defun magit-quit-session ()
"Restore the previous window configuration and kill the magit buffer."
(define-key magit-status-mode-map (kbd "q") 'magit-quit-session)
(global-set-key (kbd "C-c C-g") 'magit-status))
Interface to use the silver-searcher
Publishing with Hugo
(setq easy-hugo-basedir "~/Personal/devel/zzamboni.org/zzamboni.org/")
(setq easy-hugo-url "http://zzamboni.org/")
(setq easy-hugo-previewtime "300")
(define-key global-map (kbd "C-c C-e") 'easy-hugo))
Function to randomize the order of lines in a region, from https://www.emacswiki.org/emacs/RandomizeBuffer.
(defun my-randomize-region (beg end)
"Randomize lines in region from BEG to END."
(let ((lines (split-string
(delete-and-extract-region beg end) "\n")))
(when (string-equal "" (car (last lines 1)))
(setq lines (butlast lines 1)))
(sort (mapcar (lambda (x) (cons (random) (concat x "\n"))) lines)
(lambda (a b) (< (car a) (car b))))))))
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.
(setq auto-insert-directory "~/Dropbox/emacs-auto-insert")
(add-hook 'find-file-hook 'auto-insert)
visual-regexp-steroids provides sane regular expressions and visual incremental search.
(define-key global-map (kbd "C-c r") 'vr/replace)
(define-key global-map (kbd "C-c q") 'vr/query-replace)
;; if you use multiple-cursors, this is for you:
;; (define-key global-map (kbd "C-c m") 'vr/mc-mark)
;; to use visual-regexp-steroids's isearch instead of the built-in regexp isearch, also include the following lines:
(define-key esc-map (kbd "C-r") 'vr/isearch-backward)
(define-key esc-map (kbd "C-s") 'vr/isearch-forward))
General text editing
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
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.
(add-hook 'adoc-mode-hook 'visual-line-mode)
(add-hook 'adoc-mode-hook 'variable-pitch-mode))
Markdown, generally useful. I also set up variable pitch and visual line mode.
(add-hook 'markdown-mode-hook 'visual-line-mode)
(add-hook 'markdown-mode-hook 'variable-pitch-mode))
When typopunct is enabled (needs to be enabled by hand), automatically inserts “pretty” quotes of the appropriate type.
(typopunct-change-language 'english t))
How to do different things, not necessarily used in my Emacs config but useful sometimes.
This is how we get a global header property in org-mode