/emacs.d

my emacs config

Primary LanguageEmacs Lisp

Emacs Configuration

It’s been said “there are many ways to skin a cat”. The same can be said of Emacs. Probably.

Personal

Don’t be shy - introduce yourself to emacs. If you are copying this config, make sure you use your name. We don’t want to confuse my mother.

(setq user-full-name "Alex Recker"
      user-mail-address "alex@reckerfamily.com")

Packages

All packages are installed with the use-package library. Sadly, this needs to load before org can tangle anything, so all the action is in init.el.

Startup

Path

Ensure that the system PATH is the one used by emacs.

(use-package exec-path-from-shell
  :ensure t
  :config (exec-path-from-shell-initialize))

Scratch

The slash screen displayed on startup is a little too noisy for me. The *scratch* buffer is a lot more low key.

(setq inhibit-startup-message 't)

With this function, we can randomize the *scratch* message eachs time using the output of a program.

(defun recker/get-startup-scratch ()
  (with-temp-buffer
    (lisp-mode)
    (insert-string (shell-command-to-string recker/scratch-message-program))
    (comment-region (point-max) (point-min))
    (buffer-string)))

I like to use my very own wilfred-say, but the classic fortune is a good candidate as well.

(setq recker/scratch-message-program "wilfred-say")
(setq initial-scratch-message (recker/get-startup-scratch))

Make the *scratch* buffer unkillable.

(use-package unkillable-scratch
  :ensure t
  :init (unkillable-scratch))

Server

Start the emacs server if it is not running.

(require 'server)
(unless (server-running-p)
  (server-start))

This allows you to connect to the emacs process from somewhere else - like a terminal session.

Interface

Better Defaults

Emacs comes with some obnixious defaults. “Not on my watch!”, yelled Alex as he disabled them.

(setq make-backup-files nil
      auto-save-default nil
      indent-tabs-mode nil)

(global-auto-revert-mode 1)
(menu-bar-mode 0)
(scroll-bar-mode 0)
(tool-bar-mode 0)
(delete-selection-mode t)

Better Comments

I overwrite the build-in comment-dwim with its superior sequel.

(use-package comment-dwim-2
  :ensure t
  :bind ("M-;" . comment-dwim-2))

Better Modeline

Hide all minor modes from the modeline (since there are usually like a hundred).

(use-package rich-minority
  :ensure t
  :init (rich-minority-mode 1)
  :config (setq rm-blacklist ""))

Better File Manager

By default, hide dot files. They can be shown by disabling dired-omit-mode with C-x M-o.

Another nice side effect of dired-x is suddenly gaining the ability of jumping to the current file in dired with C-x C-j.

(require 'dired-x)
(setq-default dired-omit-files-p t)
(setq dired-omit-files (concat dired-omit-files "\\|^\\..+$"))

Better Text Selection

I use expand-region to incrementally grab larger portions of text based on where the cursor is. It’s a brilliant tool.

(use-package expand-region
  :ensure t
  :bind ("C-=" . er/expand-region))

Better Completion

Company mode.

(use-package company
  :ensure t
  :config (global-company-mode))

Yasnippet - I don’t use this nearly as much as I should be.

(use-package yasnippet
  :ensure t
  :init (yas-global-mode 1))

Completion and filtering with ivy, supported by counsel.

(use-package ivy
  :ensure t
  :config (setq ivy-use-selectable-prompt t)
  :init (ivy-mode 1))

(use-package counsel
  :ensure t
  :bind
  ("C-c i" . counsel-imenu)
  ("C-c s" . swiper)
  ("C-x C-y" . counsel-yank-pop))

Spartparens - because I can’t even be bothered to close my own parentheses and quotes.

(use-package smartparens
  :ensure t
  :init (progn (smartparens-global-mode t)
               (sp-pair "'" nil :actions :rem)))

Better Git

Magit. Seriously. Just try it you heathen.

(use-package magit
  :ensure t
  :bind ("C-x g" . magit-status))

Modes

These are the settings for various editing modes - the top level being text-mode, which is for “editing text written for humans to read”.

(defun recker/text-mode-hook ()
  (auto-fill-mode 1)
  (flyspell-mode 1)
  (flymake-mode-off))
(add-hook 'text-mode-hook 'recker/text-mode-hook)

Flycheck mode.

(use-package flycheck
  :ensure t
  :init
  (global-flycheck-mode))

Globally cleanup white space on save.

(use-package whitespace-cleanup-mode
  :ensure t
  :config (global-whitespace-cleanup-mode))

Use pretty symbols where possible.

(global-prettify-symbols-mode t)

Support for editorconfig.

(use-package editorconfig
  :ensure t
  :config (editorconfig-mode 1))

Commmon Lisp

For this to work, sbcl should be installed and in PATH.

(use-package slime
  :ensure t
  :config (setq inferior-lisp-program (executable-find "sbcl")))

(use-package slime-company
  :ensure t
  :init (slime-setup '(slime-fancy slime-company)))

Csv

(use-package csv-mode
  :ensure t
  :mode "\\.csv\\'")

Dockerfile

(use-package dockerfile-mode
  :ensure t
  :mode "\\Dockerfile\\'")

Elisp

Disable those silly docstring warnings when editing elisp.

(with-eval-after-load 'flycheck
  (setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc)))

Groovy

Pretty much just for Jenkins files.

(use-package groovy-mode
  :ensure t
  :mode "\\Jenkinsfile\\'")

HTML

(use-package web-mode
  :ensure t
  :mode ("\\.html\\'" "\\.jinja\\'")
  :config (setq web-mode-markup-indent-offset 2
                web-mode-code-indent-offset 2))

(use-package emmet-mode
  :ensure t
  :config (add-hook 'web-mode-hook 'emmet-mode))

JavaScript

This is the web-scale portion of my config.

(setq js-indent-level 2)

Markdown

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "pandoc"))

Python

Install these dependencies

pip install --user rope flake8 importmagic autopep8 yapf ipdb ipython virtualenv virtualenvwrapper

Install virtualenvwrapper support.

(use-package virtualenvwrapper
  :ensure t)

Use ipython for running the code in a shell. Evidently, it’s still experimental. I have issues with some of the tab completion, so I’ll end up using *ansi-term* instead.

 (setq python-shell-interpreter "ipython"
	python-shell-interpreter-args "-i --simple-prompt")

Let elpy do its thing.

(use-package elpy
  :ensure t
  :init (elpy-enable))

Ruby

These are very much a work in progress. I know about as much about ruby as I know about scented candles and professional football.

(setq ruby-deep-indent-paren nil)

Terraform

(use-package terraform-mode
  :ensure t
  :mode "\\.tf\\'")

Terminal

I’m a simple man, and I use a simple shell.

(defun recker/ansi-term ()
  (interactive)
  (ansi-term "/bin/bash"))
(global-set-key (kbd "C-c e") 'eshell)
(global-set-key (kbd "C-x t") 'recker/ansi-term)

The terminal buffer should be killed on exit.

(defadvice term-handle-exit
    (after term-kill-buffer-on-exit activate)
  (kill-buffer))

Aliases for eshell

(defalias 'ff #'find-file)

YAML

(use-package indent-guide
  :ensure t
  :init (add-hook 'yaml-mode-hook 'indent-guide-mode))

(use-package yaml-mode
  :ensure t
  :mode ("\\.yml\\'" "\\.sls\\'")
  :init
  (add-hook 'yaml-mode-hook 'turn-off-auto-fill))

Org

Org is love. Org is life.

(use-package ob-http :ensure t)
(use-package ob-browser :ensure t)

(use-package org
  :ensure t
  :config (progn (setq org-hide-emphasis-markers t)
                 (custom-set-faces      ;Get rid of the different font sizes on headers
                  '(org-document-title ((t (:inherit outline-1 :height 1.0 :underline nil))))
                  '(org-level-1 ((t (:inherit outline-1 :height 1.0))))
                  '(org-level-2 ((t (:inherit outline-2 :height 1.0))))
                  '(org-level-3 ((t (:inherit outline-3 :height 1.0))))
                  '(org-level-4 ((t (:inherit outline-4 :height 1.0))))
                  '(org-level-5 ((t (:inherit outline-5 :height 1.0))))))
  :init (org-babel-do-load-languages
         'org-babel-load-languages
         '((awk . t)
           (browser . t)
           (C . t)
           (calc . t)
           (clojure . t)
           (css . t)
           (ditaa . t)
           (ditaa . t)
           (haskell . t)
           (http . t)
           (java . t)
           (js . t)
           (latex . t)
           (lisp . t)
           (makefile . t)
           (perl . t)
           (python . t)
           (ruby . t)
           (scala . t)
           (screen . t)
           (sh . t)
           (sql . t)
           (sqlite . t))))

This package gives the ability to export documents in that wonky fake markdown that Atlassian invented.

(use-package ox-jira :ensure t)

Use this package to make source pretty. Or just leave the CSS classes in case I want to add a theme some day.

(use-package "htmlize"
  :ensure t
  :config (setq org-html-htmlize-output-type 'css))

Gnus

Gnus has a steep learning curve, and learning to incorporate this mysterious program has proven to be an emotional roller coaster. I’m not even sure I know enough about it to say “it’s worth it”, but hopefully this will help you with your own journey.

Better Startup

Gnus requires a “primary method” from which you obtain news. Unfortunately, the program kind of explodes if this isn’t set, which proves to be kind of a pain when you want to poke around and set up things interactively.

Here’s my workaround - set the primary method to a dummy protocol that will immediately come back. In our case, this is a blank nnml stream.

(setq gnus-select-method '(nnml ""))

Default on topic mode, since it’s more helpful.

(add-hook 'gnus-group-mode-hook 'gnus-topic-mode)

Disable saving to a newsrc config file.

(setq gnus-save-newsrc-file nil)

Read the auto save file on startup without asking.

(setq gnus-always-read-dribble-file t)

Better Folders

Gnus creates a bunch of folders in your home directory that, as far as I can tell, are not needed outside of gnus. These settings will hide them all in ~/.gnus, which will serve as our convenient nuke-point if things ever go south while playing around.

Yes - nnfolder-directory is really needed. Whether this is a bug or not, the redundancy is intentional.

(setq gnus-home-directory "~/.gnus"
      nnfolder-directory "~/.gnus/Mail/archive"
      message-directory "~/.gnus/Mail"
      nndraft-directory "~/.gnus/Drafts")

Reading News

Use gmane and gwene to follow news, mailers, and tons of other syndicated things. There are even comics.

(setq gnus-secondary-select-methods '((nntp "news.gmane.org")
                                      (nntp "news.gwene.org")))

Reading Mail

Add a personal IMAP account.

(add-to-list 'gnus-secondary-select-methods
             '(nnimap "personal"
                      (nnimap-address "imap.gmail.com")
                      (nnimap-server-port "imaps")
                      (nnimap-stream ssl)
                      (nnmail-expiry-target "nnimap+gmail:[Gmail]/Trash")
                      (nnmail-expiry-wait immediate)))

Sending Mail

Posting styles for a personal email.

(setq gnus-posting-styles '((".*" (signature (string-join '("Alex Recker" "alex@reckerfamily.com") "\n")))))

Don’t attempt to archive outbound emails to groups.

(setq gnus-message-archive-group nil)

Keep addresses locally using bbdb.

(use-package bbdb
  :ensure t
  :config (setq bbdb-file "~/.bbdb")
  :init
  (bbdb-mua-auto-update-init 'message)
  (setq bbdb-mua-auto-update-p 'query)
  (add-hook 'gnus-startup-hook 'bbdb-insinuate-gnus))

The rest of the configuration can be set up interactively. Just open a message buffer and hit C-c C-c. Emacs will allow you to choose a delivery method, enter your credentials, and even save everything.

To encrypt your credentials, just save it within ~/.authinfo.gpg.

Miscellaneous

Functions

These are miscellaneous functions that I’ve written (or plagiarized).

(defun recker/purge-buffers ()
  "Delete all buffers, except for *scratch*."
  (interactive)
  (mapc #'(lambda (b) (unless (string= (buffer-name b) "*scratch*") (kill-buffer b))) (buffer-list)))

(defun recker/unfill-region (beg end)
  "Unfill the region, joining text paragraphs into a single logical line."
  (interactive "*r")
  (let ((fill-column (point-max)))
    (fill-region beg end)))

(defun recker/org-scratch ()
  "Open a org mode *scratch* pad."
  (interactive)
  (switch-to-buffer "*org scratch*")
  (org-mode)
  (insert "#+TITLE: Org Scratch\n\n"))

Keybindings

(global-set-key (kbd "C-c b") 'browse-url)
(global-set-key (kbd "C-x k") 'kill-this-buffer)
(global-set-key (kbd "C-x C-k k") 'kill-buffer)
(global-set-key (kbd "C-c f") 'project-find-file)
(global-set-key (kbd "C-c l") 'sort-lines)
(global-set-key (kbd "C-c o") 'recker/org-scratch)
(global-set-key (kbd "C-c r") 'replace-string)

Local

Emacs sometimes dumps things in init.el. It means well, but I would rather this be in a different file ignored by git.

(let ((custom (concat (file-name-as-directory user-emacs-directory) "custom.el")))
  (unless (file-exists-p custom)
    (with-temp-buffer
      (write-file custom)))
  (setq custom-file custom))

I also like to keep a file around for miscellaneous elisp that should run on startup. This is for machine specific settings or things I am still tinkering with.

(let ((local (concat (file-name-as-directory user-emacs-directory) "local.el")))
  (unless (file-exists-p local)
    (with-temp-buffer
      (insert ";; This file is for local changes")
      (write-file local)))
  (load local))