#+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 Enable Counsel. #+BEGIN_SRC emacs-lisp (counsel-mode) #+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 * 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 ** Diff highlight mode Show the state of lines added, changed and removed since the last commit. #+BEGIN_SRC emacs-lisp (ensure-library diff-hl) (require 'diff-hl) (global-diff-hl-mode) #+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 Enable automatic text filling for org-mode. #+BEGIN_SRC emacs-lisp (add-hook 'org-mode-hook 'auto-fill-mode) #+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