dotfiles/emacs/.emacs.d/init.org

853 lines
24 KiB
Org Mode

#+TITLE: Tom-Emacs Interface
#+STARTUP: showall
This is my personal Emacs configuration. The name was inspired by
"Ghost in the Shell 2: Man-Machine Interface and Ryan Rix's "Complete
Computing Environment".
To start off, first I need to enable lexical binding.
#+BEGIN_SRC emacs-lisp :padline no
;; -*- lexical-binding: t; -*-
#+END_SRC
* Package configuration
Require package.el since I immediately start using its variables and
functions anyway, no need to delay loading.
#+BEGIN_SRC emacs-lisp
(require 'package)
#+END_SRC
Add the MELPA and org package archives because I like living on the
bleeding edge. This should be done both at run-time and compile-time
so I can install packages at compile time.
#+BEGIN_SRC emacs-lisp
(eval-and-compile
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/")))
#+END_SRC
Initialize package.el so that packages can be loaded and used. This
also needs to be done at both run-time and compile-time so packages
can be installed at compile-time.
#+BEGIN_SRC emacs-lisp
(eval-and-compile (package-initialize))
#+END_SRC
Refresh the package contents so packages can be installed from all
configured archives. Don't do this at run-time because it slows down
the process too much.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (package-refresh-contents))
#+END_SRC
This macro is inspired by use-package, but I want to maintain some
control of the syntax I use to configure my settings.
#+BEGIN_SRC emacs-lisp
(defmacro ensure-library (library &rest args)
"Make sure LIBRARY is installed.
ARGS should be a plist which may contain one of the following options:
- :package
Specify which package should actually be installed to ensure
the library named in LIBRARY exists.
- :path
Specify a path to add to the load path to be able to load this
package."
(declare (indent 1))
(let ((library-symbol (cl-gensym))
(package-symbol (cl-gensym))
(path-symbol (cl-gensym))
(package (or (plist-get args :package) library))
(path (plist-get args :path)))
`(progn
(eval-and-compile
(let ((,path-symbol ,path))
(if ,path-symbol
(add-to-list 'load-path
(if (file-name-absolute-p ,path-symbol)
,path-symbol
(concat user-emacs-directory ,path-symbol))))))
(eval-when-compile
(let ((,library-symbol ',library)
(,package-symbol ',package))
(unless (require ,library-symbol nil :noerror)
(package-install ,package-symbol)
(require ,library-symbol)))))))
#+END_SRC
* Helper functions
I have noticed that I refer to the combination of
=user-emacs-directory= and "data/" a lot, so I wrote this function
to make referencing it cleaner. Also useful if I ever want to move
my data directory.
#+BEGIN_SRC emacs-lisp
(defun oni:data-location (file-name)
"Return the location of FILE-NAME within my data directory.
This is currently the data directory under the
`user-emacs-directory'."
(concat user-emacs-directory "data/" file-name))
#+END_SRC
I also wrote a test for it.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'ert
(ert-deftest oni:data-location ()
"Test that `oni:data-location' returns the correct locations."
(should (string= "~/.emacs.d/data/backup-files/"
(oni:data-location "backup-files/")))
(should (string= "~/.emacs.d/data/auto-save-files/"
(oni:data-location "auto-save-files/")))
(should (string= "~/.emacs.d/data/auto-save-list/.saves-"
(oni:data-location "auto-save-list/.saves-")))))
#+END_SRC
* Backups
I don't like having every directory filled with "filename~"
files. So instead of saving backup files to the same directory, save
them to a special one instead.
#+BEGIN_SRC emacs-lisp
(setq backup-directory-alist `((".*" . ,(oni:data-location "backup-files/"))))
#+END_SRC
* Auto saves
I prefer to keep all autosave files in a single directory so they
don't clog up my filesystem so much. Usually these files get
deleted, but sometimes they don't, and I don't think they look
pretty. Add it to the end of the list because the default value
stores auto-saves for remote files in /tmp, which is fine by me.
#+BEGIN_SRC emacs-lisp
(add-to-list 'auto-save-file-name-transforms
`(".*" ,(oni:data-location "auto-save-files/") t) :append)
#+END_SRC
Place the files which contain the auto save files in a similar
directory.
#+BEGIN_SRC emacs-lisp
(setq auto-save-list-file-prefix (oni:data-location "auto-save-list/.saves-"))
#+END_SRC
* Tabs
Generally I prefer using spaces over tabs. Especially for lisp-like
languages.
#+BEGIN_SRC emacs-lisp
(setq-default indent-tabs-mode nil)
#+END_SRC
A tab-width of 8 is too wide for me, and 2 is too narrow. 4 is just
right.
#+BEGIN_SRC emacs-lisp
(setq-default tab-width 4)
#+END_SRC
* Font
Set the default font to a more pleasing one, in my opinion, with a
better size as well.
#+BEGIN_SRC emacs-lisp
(add-to-list 'default-frame-alist '(font . "Fantasque Sans Mono-13"))
#+END_SRC
* Menu bar
I don't use the menu bar, so it just takes up space.
#+BEGIN_SRC emacs-lisp
(menu-bar-mode -1)
#+END_SRC
* Tool bar
I don't use the tool bar, so it just takes up space.
#+BEGIN_SRC emacs-lisp
(tool-bar-mode -1)
#+END_SRC
* Scroll bar
I don't use the scroll bar to either navigate my buffers or see
whereabouts I am, so they just take up space.
#+BEGIN_SRC emacs-lisp
(scroll-bar-mode -1)
#+END_SRC
* Whitespace
I hate it when trailing whitespace is left around a file. I've been
using this for years, and apart from having some trouble working
with people who don't pay attention to it, it has worked flawlessly.
#+BEGIN_SRC emacs-lisp
(ensure-library destroy-trailing-whitespace
:path "vendor-lisp/destroy-trailing-whitespace")
(require 'destroy-trailing-whitespace)
(global-destroy-trailing-whitespace-mode)
#+END_SRC
Having a final newline at the end of the file is always a good
idea. Some programs just don't work without it and others produce
some strange results. Github diffs are an example.
#+BEGIN_SRC emacs-lisp
(setq require-final-newline t)
#+END_SRC
* Long lines
By default Emacs wraps long lines around to the next line when they
reach the far end of the window. However I prefer to have them
truncated instead.
#+BEGIN_SRC emacs-lisp
(setq-default truncate-lines t)
#+END_SRC
* Theme
#+BEGIN_SRC emacs-lisp
(ensure-library yoshi-theme
:path "vendor-lisp/yoshi-theme")
(add-to-list 'custom-theme-load-path
(concat user-emacs-directory "vendor-lisp/yoshi-theme"))
(load-theme 'yoshi :no-confirm)
#+END_SRC
* Diminish
I really don't need to see some of the minor modes.
#+BEGIN_SRC emacs-lisp
(ensure-library diminish)
(require 'diminish)
#+END_SRC
* Ivy
Ivy is a completing read implementation that offers choises
vertically. I'm surprised how much I like it. I've tried Swiper
before and I didn't like that so much.
#+BEGIN_SRC emacs-lisp
(ensure-library ivy)
#+END_SRC
Also install the =flx= package to allow ivy to use fuzzy matching.
#+BEGIN_SRC emacs-lisp
(ensure-library flx)
#+END_SRC
Since I immediately use and enable Ivy, there's no need to autoload
it, so require it to keep the byte-compiler quiet.
#+BEGIN_SRC emacs-lisp
(require 'ivy)
#+END_SRC
Don't show that ivy is enabled in the mode-line. It's enabled
globally and I'll notice it from other things anyway (like it
showing up).
#+BEGIN_SRC emacs-lisp
(diminish 'ivy-mode)
#+END_SRC
Enable fuzzy matching in Ivy.
#+BEGIN_SRC emacs-lisp
(setq ivy-re-builders-alist '((t . ivy--regex-fuzzy))
ivy-initial-inputs-alist nil)
#+END_SRC
Enable Ivy.
#+BEGIN_SRC emacs-lisp
(ivy-mode)
#+END_SRC
* Counsel
Counsel is a group of functions that use Ivy to specialize on
certain built-in commands, such as M-x.
#+BEGIN_SRC emacs-lisp
(ensure-library counsel)
#+END_SRC
Since I enable Counsel mode immediately, there's no point in leaving
it to be autoloaded. Requiring it keeps the byte-compiler happy.
#+BEGIN_SRC emacs-lisp
(require 'counsel)
#+END_SRC
Don't show that counsel is enabled in the mode-line. It's enabled
globally and I'll notice whenever I press M-x for example.
#+BEGIN_SRC emacs-lisp
(diminish 'counsel-mode)
#+END_SRC
Enable Counsel.
#+BEGIN_SRC emacs-lisp
(counsel-mode)
#+END_SRC
* Bookmarks
Save bookmarks in my data directory so my =user-emacs-directory= is less cluttered.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'bookmark))
(setq bookmark-default-file (oni:data-location "bookmarks"))
#+END_SRC
* Personal info
Set some personal info for, for example, Gnus to use.
#+BEGIN_SRC emacs-lisp
(setq user-full-name "Tom Willemse"
user-mail-address "tom@ryuslash.org")
#+END_SRC
* Minor modes
** Paredit
Paredit is an awesome minor-mode to have when you write in any
lisp-like languages. It can feel rather strict and uncomfortable at
first, but once you get the hang of using it, you won't want to
live without it.
#+BEGIN_SRC emacs-lisp
(ensure-library paredit)
#+END_SRC
** Electric indent mode
By default `electric-indent-mode' is enabled globally, but I prefer
to enable it locally where I need it.
#+BEGIN_SRC emacs-lisp
(electric-indent-mode -1)
#+END_SRC
Since Emacs 24 `electric-indent-mode' switches the behavior of the
C-j and RET keys. I prefer the original situation because my
muscle-memory still remembers to use C-j for newline-and-indent
behaviour.
#+BEGIN_SRC emacs-lisp
(defun oni:switch-newline-keys ()
"Switch the C-j and RET keys in the local buffer."
(if electric-indent-mode
(progn
(local-set-key (kbd "C-j") 'newline)
(local-set-key (kbd "RET") 'electric-newline-and-maybe-indent))
(local-unset-key (kbd "C-j"))
(local-unset-key (kbd "RET"))))
(add-hook 'electric-indent-local-mode-hook #'oni:switch-newline-keys)
#+END_SRC
** Flycheck
Flycheck lets me see (compiler) errors, warnings and info messages
while writing code.
#+BEGIN_SRC emacs-lisp
(ensure-library flycheck)
#+END_SRC
When developing packages with Cask, some special care needs to be
taken to ensure the checkers work correctly.
#+BEGIN_SRC emacs-lisp
(ensure-library flycheck-cask)
(add-hook 'flycheck-mode-hook 'flycheck-cask-setup)
#+END_SRC
I disable the pylint and pyflakes checkers because they don't seem
to add much except noise when used together with flake8. Also
pylint seems hell-bent on making Python written like a
statically-typed langauge.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'flycheck
(mapc (lambda (c) (delq c flycheck-checkers))
'(python-pylint python-pyflakes)))
#+END_SRC
Also show which columns messages appear in.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'flycheck
(setq flycheck-highlighting-mode 'columns))
#+END_SRC
Show the error message at point in a tooltip.
#+BEGIN_SRC emacs-lisp
(ensure-library flycheck-pos-tip)
(with-eval-after-load 'flycheck
(require 'flycheck-pos-tip)
(flycheck-pos-tip-mode))
#+END_SRC
** Auto revert mode
ARev isn't very descriptive, and fairly wide. Use a font-awesome icon instead.
#+BEGIN_SRC emacs-lisp
(diminish 'auto-revert-mode
(propertize (concat " " (char-to-string #xf021))
'face '(:family "Font Awesome" :height 0.75)))
#+END_SRC
** Auto fill mode
"Fill" is fine as a mode-line lighter, but I prefer something
shorter.
#+BEGIN_SRC emacs-lisp
(diminish 'auto-fill-function
(propertize (concat " " (char-to-string #xf149))
'face '(:family "Font Awesome" :height 0.75)))
#+END_SRC
* Major modes
** Emacs lisp mode
Enable paredit mode.
#+BEGIN_SRC emacs-lisp
(add-hook 'emacs-lisp-mode-hook 'paredit-mode)
#+END_SRC
** Scheme mode
Enable paredit mode.
#+BEGIN_SRC emacs-lisp
(add-hook 'scheme-mode-hook 'paredit-mode)
#+END_SRC
Add scsh to the list of known interpreters for scheme mode. This
way shell-scripts that don't have a file extension but specify scsh
as the interpreter are opened in scheme mode.
#+BEGIN_SRC emacs-lisp
(add-to-list 'interpreter-mode-alist '("scsh" . scheme-mode))
#+END_SRC
** Inferior Emacs lisp mode (ielm)
Enable paredit mode.
#+BEGIN_SRC emacs-lisp
(add-hook 'inferior-emacs-lisp-mode-hook 'paredit-mode)
#+END_SRC
** Mbsync configuration mode
I wrote a simple major-mode for editing my =.mbsyncrc= file. I
might release it as a package, but for now I keep it with the rest
of my configuration.
#+BEGIN_SRC emacs-lisp
(ensure-library mbsync-conf-mode
:path "vendor-lisp/mbsync-conf-mode")
#+END_SRC
Since it isn't installed by package.el, I need to specify the
autoload myself.
#+BEGIN_SRC emacs-lisp
(autoload 'mbsync-conf-mode "mbsync-conf-mode"
"Major mode for editing mbsync configuration files."
:interactive)
#+END_SRC
I also need to add it to the =auto-mode-alist= so ~.mbsyncrc~ is
opened with mbsync conf mode.
#+BEGIN_SRC emacs-lisp
(add-to-list 'auto-mode-alist '("\\.mbsyncrc\\'" . mbsync-conf-mode))
#+END_SRC
** Msmtprc mode
I wrote a simple major-mode for editing my =.msmtprc= file. I might
release it as a package, but for now I keep it with the rest of my
configuration.
#+BEGIN_SRC emacs-lisp
(ensure-library msmtprc-mode
:path "vendor-lisp/msmtprc-mode")
#+END_SRC
Since it isn't installed by package.el, I need to specify the
autoload myself.
#+BEGIN_SRC emacs-lisp
(autoload 'msmtprc-mode "msmtprc-mode"
"Major mode for editing msmtp configuration files."
:interactive)
#+END_SRC
I also need to add it to the =auto-mode-alist= so ~.msmtprc~ is
opened with msmtprc mode.
#+BEGIN_SRC emacs-lisp
(add-to-list 'auto-mode-alist '("\\.msmtprc\\'" . msmtprc-mode))
#+END_SRC
** Git commit mode
Enable =electric-quote-local-mode= to easily type nice-looking
quotes while writing commits.
#+BEGIN_SRC emacs-lisp
(add-hook 'git-commit-mode-hook 'electric-quote-local-mode)
#+END_SRC
** Python mode
Enable electric pair mode.
#+BEGIN_SRC emacs-lisp
(add-hook 'python-mode-hook 'electric-pair-local-mode)
#+END_SRC
Enable syntax and style checking with flycheck.
#+BEGIN_SRC emacs-lisp
(add-hook 'python-mode-hook 'flycheck-mode)
#+END_SRC
* Applications
** Magit
Magit is a very nice interface to Git for Emacs. It allows you to
do just about anything with Git without leaving the comfort of your
Emacs session.
#+BEGIN_SRC emacs-lisp
(ensure-library magit)
#+END_SRC
Show refined diffs in magit. This makes it much easier to see
/what/ has changed on a line.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'magit))
(with-eval-after-load 'magit
(setq magit-diff-refine-hunk 'all))
#+END_SRC
** Gnus
Gnus is one of the most extensible Email programs on the
planet. And it's not even made for email but NNTP.
Store all Gnus-related data in my data directory.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'gnus))
(with-eval-after-load 'gnus
(setq gnus-directory (oni:data-location "News")
gnus-article-save-directory gnus-directory
gnus-cache-directory gnus-directory
gnus-kill-files-directory gnus-directory))
#+END_SRC
Store all Mail source-related data in my data directory.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'mail-source))
(with-eval-after-load 'mail-source
(setq mail-source-directory (oni:data-location "Mail")))
#+END_SRC
Store all message-related data in the same place as the Mail source
data.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'message))
(with-eval-after-load 'message
(setq message-directory (oni:data-location "Mail")))
#+END_SRC
Store all nnfolder-related data in the same place as the Mail
source data.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'nnfolder))
(with-eval-after-load 'nnfolder
(setq nnfolder-directory (oni:data-location "Mail")))
#+END_SRC
Use msmtp to send mail.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'sendmail))
(with-eval-after-load 'sendmail
(setq send-mail-function 'sendmail-send-it)
(setq sendmail-program "/usr/bin/msmtp"))
#+END_SRC
Tell Gnus I'm not a novice anymore. One of the features of Gnus I
use a lot is deleting messages and as long as Gnus thinks I'm a
novice it will ask me if I'm sure every single time.
#+BEGIN_SRC emacs-lisp
(setq gnus-novice-user nil)
#+END_SRC
Add a keybinding to the Gnus summary mode to easily delete
messages.
#+BEGIN_SRC emacs-lisp
(defun oni:gnus-delete-forward ()
"Delete the article under point and move to the next one."
(interactive)
(gnus-summary-delete-article)
(gnus-summary-next-subject 1))
(with-eval-after-load 'gnus
(define-key gnus-summary-mode-map (kbd "M-d") #'oni:gnus-delete-forward))
#+END_SRC
*** ryuslash.org
Set my main email address as the primary select method for Gnus.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'gnus
(setq gnus-select-method
'(nnmaildir "ryuslash" (directory "~/documents/mail/ryuslash/"))))
#+END_SRC
When sending mail from the ryuslash inbox, use the ryuslash msmtp
account.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'gnus-msg))
(with-eval-after-load 'gnus-msg
(add-to-list 'gnus-posting-styles
'(".*"
(address "tom@ryuslash.org")
(eval (setq message-sendmail-extra-arguments
'("-a" "ryuslash"))))))
#+END_SRC
*** picturefix
Add my work email account as a secondary select method.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'gnus
(add-to-list 'gnus-secondary-select-methods
'(nnmaildir "picturefix"
(directory "~/documents/mail/picturefix/"))))
#+END_SRC
When sending mail from the picturefix account, use the picturefix
msmtp account and set the proper name and email address.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'gnus-msg
(add-to-list 'gnus-posting-styles
'("picturefix:"
(name "Tom Willemsen")
(address "tom@picturefix.nl")
(eval (setq message-sendmail-extra-arguments
'("-a" "picturefix"))))))
#+END_SRC
** Linewise user-interface
This is the library used by Circe and Slack to display messages.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'lui))
#+END_SRC
Put the time stamp in lui buffers in the right margin. This gives
the text some extra room.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'lui
(setq lui-time-stamp-position 'right-margin))
#+END_SRC
Remove the "[]" from the time stamp, it's not really necessary.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'lui
(setq lui-time-stamp-format "%H:%M"))
#+END_SRC
Give the right margin just enough room to show the time-stamps, no
more, no less.
#+BEGIN_SRC emacs-lisp
(defun oni:set-circe-margin-width ()
(setq right-margin-width 5))
(add-hook 'lui-mode-hook #'oni:set-circe-margin-width)
#+END_SRC
Fix the wrap prefix so that text at the prompt is aligned properly.
#+BEGIN_SRC emacs-lisp
(defun oni:set-lui-prompt-wrap-prefix ()
(setq wrap-prefix " "))
(add-hook 'lui-mode-hook #'oni:set-lui-prompt-wrap-prefix)
#+END_SRC
Enable visual line mode in lui buffers so my text doesn't go
off-screen.
#+BEGIN_SRC emacs-lisp
(add-hook 'lui-mode-hook 'visual-line-mode)
#+END_SRC
** Circe
I switched to Circe from ERC because I couldn't make the
customizations I wanted to, Circe seems much better at this.
#+BEGIN_SRC emacs-lisp
(ensure-library circe)
#+END_SRC
I prefer storing my passwords in a GPG encrypted authinfo
file. Circe doesn't look at this out-of-the-box, but it's easy
enough to get this. This function returns a function that will get
a stored password for the given host.
#+BEGIN_SRC emacs-lisp
(defun oni:circe-get-password-for (host)
(lambda (_)
(let ((found (nth 0 (auth-source-search :max 1
:host host
:require '(:secret)))))
(when found
(let ((secret (plist-get found :secret)))
(if (functionp secret)
(funcall secret)
secret))))))
#+END_SRC
I spend most of my time on IRC on Freenode.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'circe))
(with-eval-after-load 'circe
(add-to-list 'circe-network-options
`("Freenode"
:nick "ryuslash"
:channels ("#emacs"
"#mowedline"
"#ninthfloor"
"#dispass"
"#linuxvoice"
"#conkeror")
:nickserv-password
,(oni:circe-get-password-for "irc.freenode.net"))))
#+END_SRC
Sometimes I watch some Twitch streams as well.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'circe
(add-to-list 'circe-network-options
`("Twitch"
:use-tls nil
:nick "ryuslash"
:host "irc.twitch.tv"
:pass ,(oni:circe-get-password-for "irc.twitch.tv")
:port 6667)))
#+END_SRC
Enable coloring of nicks.
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'circe
(require 'circe-color-nicks)
(enable-circe-color-nicks))
#+END_SRC
Align all nicks.
#+BEGIN_SRC emacs-lisp
(ensure-library circe-aligned-nicks
:path "vendor-lisp/circe-aligned-nicks")
(with-eval-after-load 'circe
(require 'circe-aligned-nicks)
(enable-circe-aligned-nicks))
#+END_SRC
** Org
Tell org-mode to fontify code blocks in their specified languages.
#+BEGIN_SRC emacs-lisp
(eval-when-compile (require 'org))
(with-eval-after-load 'org
(setq org-src-fontify-natively t))
#+END_SRC
* Custom
Put the customize settings in a different file so that Emacs doesn't
have to modify this file whenever something changes through
customize. I put this into my init file last so any settings made in
there *can* overwrite the ones in the rest of the file, not that I
usually like to do that.
#+BEGIN_SRC emacs-lisp
(setq custom-file (concat user-emacs-directory "custom.el"))
(load custom-file)
#+END_SRC