Aaron Bedra's Emacs 26 Configuration
Table of Contents
Configuration
Emacs is a special beast. Taming it takes a lot of care. In an attempt to document/explain/share with the rest of the world, this is my attempt at configuration as a literate program. It also shows off the awesome power of org-mode, which makes all of this possible.
User details
Emacs will normally pick this up automatically, but this way I can be sure the right information is always present.
(setq user-full-name "Aaron Bedra") (setq user-mail-address "aaron@aaronbedra.com")
Environment
There are plenty of things installed outside of the default
PATH
. This allows me to establish additional PATH
information. At
the moment, the only things that added are /usr/local/bin
for
homebrew on OS X and .cabal/bin
for Haskell package binaries.
Emacs lisp is really only a subset of common lisp, and I need to have some of the additional functionality to make the configuration and its dependencies work properly, which we get by requiring Common Lisp for Emacs.
(setenv "PATH" (concat "/usr/local/bin:/opt/local/bin:/usr/bin:/bin" (getenv "PATH"))) (setq exec-path (append exec-path '("/usr/local/bin"))) (require 'cl)
Package Management
Since Emacs 24, Emacs includes the Emacs Lisp Package Archive (ELPA) by default. This provides a nice way to install additional packages. Since the default package archive doesn't include everything necessary, the and melpa repositories are also added.
(load "package") (package-initialize) (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)
Define default packages
This is the list of packages used in this configuration.
(defvar abedra/packages '(ac-slime auto-complete autopair cider clojure-mode elpy f feature-mode flycheck graphviz-dot-mode htmlize magit markdown-mode org paredit powerline rvm smex solarized-theme web-mode writegood-mode yaml-mode) "Default packages")
Install default packages
When Emacs boots, check to make sure all of the packages defined
in abedra/packages
are installed. If not, have ELPA take care of
it.
(defun abedra/packages-installed-p () (loop for pkg in abedra/packages when (not (package-installed-p pkg)) do (return nil) finally (return t))) (unless (abedra/packages-installed-p) (message "%s" "Refreshing package database...") (package-refresh-contents) (dolist (pkg abedra/packages) (when (not (package-installed-p pkg)) (package-install pkg))))
Start-up options
Splash Screen
I want to skip straight to the scratch buffer. This turns off the
splash screen and puts me straight into the scratch buffer. I
don't really care to have anything in there either, so turn off
the message while we're at it. Since I end up using org-mode
most of the time, set the default mode accordingly.
(setq inhibit-splash-screen t
initial-scratch-message nil
initial-major-mode 'org-mode)
Marking text
There are some behaviors in Emacs that aren't intuitive. Since I pair with others that don't know how Emacs handles highlighting, treat regions like other text editors. This means typing when the mark is active will write over the marked region. Also, make the common highlighting keystrokes work the way most people expect them to. This saves a lot of time explaining how to highlight areas of text. Emacs also has it's own clipboard and doesn't respond to the system clipboard by default, so tell Emacs that we're all friends and can get along.
(delete-selection-mode t)
(transient-mark-mode t)
(setq x-select-enable-clipboard t)
Display Settings
I have some modifications to the default display. First, a minor tweak to the frame title. It's also nice to be able to see when a file actually ends. This will put empty line markers into the left hand side.
(setq-default indicate-empty-lines t) (when (not indicate-empty-lines) (toggle-indicate-empty-lines))
Indentation
There's nothing I dislike more than tabs in my files. Make sure I don't share that discomfort with others.
(setq tab-width 2
indent-tabs-mode nil)
Backup files
Some people like to have them. I don't. Rather than pushing them to a folder, never to be used, just turn the whole thing off.
(setq make-backup-files nil)
Yes and No
Nobody likes to have to type out the full yes or no when Emacs asks. Which it does often. Make it one character.
(defalias 'yes-or-no-p 'y-or-n-p)
Key bindings
Miscellaneous key binding stuff that doesn't fit anywhere else.
(global-set-key (kbd "RET") 'newline-and-indent) (global-set-key (kbd "C-;") 'comment-or-uncomment-region) (global-set-key (kbd "M-/") 'hippie-expand) (global-set-key (kbd "C-+") 'text-scale-increase) (global-set-key (kbd "C--") 'text-scale-decrease) (global-set-key (kbd "C-c C-k") 'compile) (global-set-key (kbd "C-x g") 'magit-status)
Misc
Turn down the time to echo keystrokes so I don't have to wait around for things to happen. Dialog boxes are also a bit annoying, so just have Emacs use the echo area for everything. Beeping is for robots, and I am not a robot. Use a visual indicator instead of making horrible noises. Oh, and always highlight parentheses. A person could go insane without that.
(setq echo-keystrokes 0.1
use-dialog-box nil
visible-bell t)
(show-paren-mode t)
Vendor directory
I have a couple of things that don't come from package managers. This includes the directory for use.
(defvar abedra/vendor-dir (expand-file-name "vendor" user-emacs-directory)) (add-to-list 'load-path abedra/vendor-dir) (dolist (project (directory-files abedra/vendor-dir t "\\w+")) (when (file-directory-p project) (add-to-list 'load-path project)))
Org
org-mode
is one of the most powerful and amazing features of
Emacs. I mostly use it for task/day organization and generating
code snippets in HTML. Just a few tweaks here to make the
experience better.
Settings
Enable logging when tasks are complete. This puts a time-stamp on
the completed task. Since I usually am doing quite a few things at
once, I added the INPROGRESS
keyword and made the color
blue. Finally, enable flyspell-mode
and writegood-mode
when
org-mode
is active.
(setq org-log-done t org-todo-keywords '((sequence "TODO" "INPROGRESS" "DONE")) org-todo-keyword-faces '(("INPROGRESS" . (:foreground "blue" :weight bold)))) (add-hook 'org-mode-hook (lambda () (flyspell-mode))) (add-hook 'org-mode-hook (lambda () (writegood-mode)))
org-agenda
First, create the global binding for org-agenda
. This allows it
to be quickly accessed. The agenda view requires that org files be
added to it. The personal.org
file is my daily file for review. I
have a habit to plan the next day. I do this by assessing my
calendar and my list of todo items. If a todo item is already
scheduled or has a deadline, don't show it in the global todo list.
(global-set-key (kbd "C-c a") 'org-agenda) (setq org-agenda-show-log t org-agenda-todo-ignore-scheduled t org-agenda-todo-ignore-deadlines t) (setq org-agenda-files (list "~/Dropbox/org/personal.org"))
org-habit
I have several habits that I also track. In order to take
full advantage of this feature org-habit
has to be required and
added to org-modules
. A few settings are also tweaked for habit
mode to make the tracking a little more palatable. The most
significant of these is org-habit-graph-column
. This specifies
where the graph should start. The default is too low and cuts off a
lot, so I start it at 80 characters.
(require 'org) (require 'org-install) (require 'org-habit) (add-to-list 'org-modules "org-habit") (setq org-habit-preceding-days 7 org-habit-following-days 1 org-habit-graph-column 80 org-habit-show-habits-only-for-today t org-habit-show-all-today t)
org-babel
org-babel
is a feature inside of org-mode
that makes this
document possible. It allows for embedding languages inside of an
org-mode
document with all the proper font-locking. It also
allows you to extract and execute code. It isn't aware of
Clojure
by default, so the following sets that up.
(require 'ob) (org-babel-do-load-languages 'org-babel-load-languages '((shell . t) (ditaa . t) (plantuml . t) (dot . t) (ruby . t) (js . t) (C . t))) (add-to-list 'org-src-lang-modes (quote ("dot". graphviz-dot))) (add-to-list 'org-src-lang-modes (quote ("plantuml" . fundamental))) (add-to-list 'org-babel-tangle-lang-exts '("clojure" . "clj")) (defvar org-babel-default-header-args:clojure '((:results . "silent") (:tangle . "yes"))) (defun org-babel-execute:clojure (body params) (lisp-eval-string body) "Done!") (provide 'ob-clojure) (setq org-src-fontify-natively t org-confirm-babel-evaluate nil) (add-hook 'org-babel-after-execute-hook (lambda () (condition-case nil (org-display-inline-images) (error nil))) 'append)
Utilities
Smex
smex
is a necessity. It provides history and searching on top of M-x
.
(setq smex-save-file (expand-file-name ".smex-items" user-emacs-directory)) (smex-initialize) (global-set-key (kbd "M-x") 'smex) (global-set-key (kbd "M-X") 'smex-major-mode-commands)
Ido
Ido
mode provides a nice way to navigate the filesystem. This is
mostly just turning it on.
(ido-mode t)
(setq ido-enable-flex-matching t
ido-use-virtual-buffers t)
Column number mode
Turn on column numbers.
(setq column-number-mode t)
Temporary file management
Deal with temporary files. I don't care about them and this makes them go away.
(setq backup-directory-alist `((".*" . ,temporary-file-directory))) (setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))
autopair-mode
This makes sure that brace structures (), [], {}
, etc. are closed
as soon as the opening character is typed.
(require 'autopair)
Power lisp
A bunch of tweaks for programming in LISP dialects. It defines the
modes that I want to apply these hooks to. To add more just add
them to lisp-modes
. This also creates its own minor mode to
properly capture the behavior. It remaps some keys to make paredit
work a little easier as well. It also sets clisp
as the default
lisp program and racket
as the default scheme program.
(setq lisp-modes '(lisp-mode emacs-lisp-mode common-lisp-mode scheme-mode clojure-mode)) (defvar lisp-power-map (make-keymap)) (define-minor-mode lisp-power-mode "Fix keybindings; add power." :lighter " (power)" :keymap lisp-power-map (paredit-mode t)) (define-key lisp-power-map [delete] 'paredit-forward-delete) (define-key lisp-power-map [backspace] 'paredit-backward-delete) (defun abedra/engage-lisp-power () (lisp-power-mode t)) (dolist (mode lisp-modes) (add-hook (intern (format "%s-hook" mode)) #'abedra/engage-lisp-power)) (setq inferior-lisp-program "clisp") (setq scheme-program-name "racket")
auto-complete
Turn on auto complete.
(require 'auto-complete-config) (ac-config-default)
Indentation and buffer cleanup
This re-indents, untabifies, and cleans up whitespace. It is stolen directly from the emacs-starter-kit.
(defun untabify-buffer () (interactive) (untabify (point-min) (point-max))) (defun indent-buffer () (interactive) (indent-region (point-min) (point-max))) (defun cleanup-buffer () "Perform a bunch of operations on the whitespace content of a buffer." (interactive) (indent-buffer) (untabify-buffer) (delete-trailing-whitespace)) (defun cleanup-region (beg end) "Remove tmux artifacts from region." (interactive "r") (dolist (re '("\\\\│\·*\n" "\W*│\·*")) (replace-regexp re "" nil beg end))) (global-set-key (kbd "C-x M-t") 'cleanup-region) (global-set-key (kbd "C-c n") 'cleanup-buffer) (setq-default show-trailing-whitespace t)
flyspell
The built-in Emacs spell checker. Turn off the welcome flag because it is annoying and breaks on quite a few systems. Specify the location of the spell check program so it loads properly.
(setq flyspell-issue-welcome-flag nil) (if (eq system-type 'darwin) (setq-default ispell-program-name "/usr/local/bin/aspell") (setq-default ispell-program-name "/usr/bin/aspell")) (setq-default ispell-list-command "list")
eshell
Customize eshell
(require 'f) (setq eshell-visual-commands '("less" "tmux" "htop" "top" "bash" "zsh" "fish")) (setq eshell-visual-subcommands '(("git" "log" "l" "diff" "show"))) ;; Prompt with a bit of help from http://www.emacswiki.org/emacs/EshellPrompt (defmacro with-face (str &rest properties) `(propertize ,str 'face (list ,@properties))) (defun eshell/abbr-pwd () (let ((home (getenv "HOME")) (path (eshell/pwd))) (cond ((string-equal home path) "~") ((f-ancestor-of? home path) (concat "~/" (f-relative path home))) (path)))) (defun eshell/my-prompt () (let ((header-bg "#161616")) (concat (with-face (eshell/abbr-pwd) :foreground "#008700") (if (= (user-uid) 0) (with-face "#" :foreground "red") (with-face "$" :foreground "#2345ba")) " "))) (setq eshell-prompt-function 'eshell/my-prompt) (setq eshell-highlight-prompt nil) (setq eshell-prompt-regexp "^[^#$\n]+[#$] ") (setq eshell-cmpl-cycle-completions nil)
powerline
(require 'powerline) (powerline-default-theme)
gnus
TODO: grumpy rant about the state of GPG email
(setq user-email-address "aaron@aaronbedra.com" gnus-select-method '(nnimap "personal" (nnimap-address "imap.gmail.com") (nnimap-server-port 993) (nnimap-stream ssl)) smtpmail-smtp-server "smtp.gmail.com" smtpmail-smtp-service 587 message-send-mail-function 'smtpmail-send-it nntp-authinfo-file "~/.authinfo.gpg" gnus-ignored-newsgroups "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]\"[#'()]" gnus-agent nil gnus-message-archive-group nil gnus-fetch-old-headers 'some) (add-hook 'gnus-summary-mode-hook 'my-gnus-summary-keys) (defun my-gnus-summary-keys () (local-set-key "y" 'gmail-archive) (local-set-key "$" 'gmail-report-spam)) (defun gmail-archive () (interactive) (gnus-summary-move-article nil "nnimap+imap.gmail.com:[Gmail]/All Mail")) (defun gmail-report-spam () (interactive) (gnus-summary-move-article nil "nnimap+imap.gmail.com:[Gmail]/Spam"))
Language Hooks
shell-script-mode
Use shell-script-mode
for .zsh
files.
(add-to-list 'auto-mode-alist '("\\.zsh$" . shell-script-mode))
Web Mode
(setq web-mode-style-padding 2) (setq web-mode-script-padding 2) (setq web-mode-markup-indent-offset 2) (setq web-mode-css-indent-offset 2) (setq web-mode-code-indent-offset 2) (add-to-list 'auto-mode-alist '("\\.hbs$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.erb$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.html$" . web-mode))
Ruby
Turn on autopair
for Ruby. Identify additional file
names/extensions that will trigger ruby-mode
when loaded.
(add-hook 'ruby-mode-hook (lambda () (autopair-mode))) (add-to-list 'auto-mode-alist '("\\.rake$" . ruby-mode)) (add-to-list 'auto-mode-alist '("\\.gemspec$" . ruby-mode)) (add-to-list 'auto-mode-alist '("\\.ru$" . ruby-mode)) (add-to-list 'auto-mode-alist '("Rakefile" . ruby-mode)) (add-to-list 'auto-mode-alist '("Gemfile" . ruby-mode)) (add-to-list 'auto-mode-alist '("Capfile" . ruby-mode)) (add-to-list 'auto-mode-alist '("Vagrantfile" . ruby-mode)) (add-to-list 'auto-mode-alist '("Guardfile" . ruby-mode))
YAML
Add additional file extensions that trigger yaml-mode
.
(add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode)) (add-to-list 'auto-mode-alist '("\\.yaml$" . yaml-mode))
JavaScript Mode
js-mode
defaults to using 4 spaces for indentation. Change it to 2
(defun js-custom () "js-mode-hook" (setq js-indent-level 2)) (add-hook 'js-mode-hook 'js-custom)
Markdown Mode
Enable Markdown mode and setup additional file extensions. Use pandoc to generate HTML previews from within the mode, and use a custom css file to make it a little prettier.
(add-to-list 'auto-mode-alist '("\\.md$" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.mdown$" . markdown-mode)) (add-hook 'markdown-mode-hook (lambda () (visual-line-mode t) (writegood-mode t) (flyspell-mode t))) (setq markdown-command "pandoc --smart -f markdown -t html") (setq markdown-css-paths `(,(expand-file-name "markdown.css" abedra/vendor-dir)))
Themes
Load solarized-light if in a graphical environment. Load the wombat theme if in a terminal.
(if window-system
(load-theme 'solarized-light t)
(load-theme 'wombat t))
Color Codes
Running things like RSpec in compilation mode produces ansi color codes that aren't properly dealt with by default. This takes care of that and makes sure that the colors that are trying to be presented are rendered correctly.
(require 'ansi-color) (defun colorize-compilation-buffer () (toggle-read-only) (ansi-color-apply-on-region (point-min) (point-max)) (toggle-read-only)) (add-hook 'compilation-filter-hook 'colorize-compilation-buffer)