my emacs config
Switch branches/tags
Nothing to show
Clone or download
Latest commit 03ba310 Oct 12, 2018
Permalink
Failed to load latest commit information.
screenshots Add gnus screenshot Sep 5, 2017
signatures Lol what happened to my signatures Sep 14, 2018
snippets Little somethin Jul 27, 2018
.gitignore gitignore Oct 8, 2018
README.org Mingus Oct 12, 2018
init.el Lol jenkins Apr 18, 2018

README.org

Emacs Configuration

screenshots/scratch.png

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 (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
      ns-confirm-quit 1)

(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 Bookmarks

Automatically save the bookmark file each time it is modified. This prevents losing bookmarks created in separate emacs clients.

(setq bookmark-save-flag 1)

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 "\\|^\\..+$"))

Add the -h switch to the dired output to show prettier filenames.

(setq dired-listing-switches "-alh")

Better Music

OK, so there’s no music in Emacs to begin with. But check out mingus, it’s pretty awesome. This works, assuming you have an local mpd server running on the default port.

(use-package mingus
  :ensure t
  :bind (("C-x m" . mingus)))

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-c g" . counsel-git-grep)
  ("C-x C-y" . counsel-yank-pop))

Better Git

Magit. Seriously. Just try it you heathen.

(use-package magit
  :ensure t
  :bind
  ("C-x g" . magit-status)
  ("C-c m" . magit-blame)
  :config (magit-add-section-hook 'magit-status-sections-hook
                                'magit-insert-unpushed-to-upstream
                                'magit-insert-unpushed-to-upstream-or-recent
                                'replace))

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))

Support for editorconfig.

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

C

Taken from The Linux Kernel Coding Style, which was a way better read than you’d think.

I slightly modified the provided snippet so that all of my C would obey these rules by default.

(defun c-lineup-arglist-tabs-only (ignored)
  "Line up argument lists by tabs, not spaces"
  (let* ((anchor (c-langelem-pos c-syntactic-element))
         (column (c-langelem-2nd-pos c-syntactic-element))
         (offset (- (1+ column) anchor))
         (steps (floor offset c-basic-offset)))
    (* (max steps 1)
       c-basic-offset)))

(add-hook 'c-mode-common-hook
          (lambda ()
            ;; Add kernel style
            (c-add-style
             "linux-tabs-only"
             '("linux" (c-offsets-alist
                        (arglist-cont-nonempty
                         c-lineup-gcc-asm-reg
                         c-lineup-arglist-tabs-only))))))

(add-hook 'c-mode-hook (lambda ()
                         (setq indent-tabs-mode t)
                         (setq show-trailing-whitespace t)
                         (c-set-style "linux-tabs-only")))

Clojure

(use-package cider
  :ensure t)

(use-package clojure-mode
  :ensure t)

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\\'")

D

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

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)))

Go

Here is the really trendy part of my config.

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

Groovy

Pretty much just for Jenkins files.

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

Haskell

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

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)

Log

Taken from Working with Log Files in Emacs.

(use-package vlf :ensure t)

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

Lua

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

Markdown

Nothing! Nothing for markdown! Just treat it like stupid plain text!

Nginx

(use-package nginx-mode
  :ensure t)

Python

Install these dependencies

pip install 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)

Rust

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

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)

Typescript

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

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 org
  :ensure t
  :config (progn (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))))))
  :bind (("C-c a" . org-agenda))
  :init (org-babel-do-load-languages
	   'org-babel-load-languages
	   '((awk . t)
	     (C . t)
	     (calc . t)
	     (clojure . t)
	     (css . t)
	     (ditaa . t)
	     (ditaa . t)
	     (haskell . 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))))

Shims

Fix this broken function. Thanks, Howard.

(defun org-babel-temp-file (prefix &optional suffix)
  "Create a temporary file in the `org-babel-temporary-directory'.
Passes PREFIX and SUFFIX directly to `make-temp-file' with the
value of `temporary-file-directory' temporarily set to the value
of `org-babel-temporary-directory'."
  (if (file-remote-p default-directory)
      (let ((prefix
             ;; We cannot use `temporary-file-directory' as local part
             ;; on the remote host, because it might be another OS
             ;; there.  So we assume "/tmp", which ought to exist on
             ;; relevant architectures.
             (concat (file-remote-p default-directory)
                     ;; REPLACE temporary-file-directory with /tmp:
                     (expand-file-name prefix "/tmp/"))))
        (make-temp-file prefix nil suffix))
    (let ((temporary-file-directory
           (or (and (boundp 'org-babel-temporary-directory)
                    (file-exists-p org-babel-temporary-directory)
                    org-babel-temporary-directory)
               temporary-file-directory)))
      (make-temp-file prefix nil suffix))))

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 'inline-css))

Gnus

screenshots/gnus.png

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)

Enable the asynchronous flag.

(setq gnus-asynchronous 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))

SMTP settings.

(setq smtpmail-smtp-service 587
      smtpmail-smtp-user "alex@reckerfamily.com"
      smtpmail-smtp-server "smtp.gmail.com"
      send-mail-function 'smtpmail-send-it)

For this to work out of the box, be sure to fill out your ~/.authinfo like so (or name the file ~/.authinfo.gpg if you want to encrypt it).

machine imap.gmail.com login alex@reckerfamily.com password <password> port imaps
machine smtp.gmail.com login alex@reckerfamily.com password <password> port 587

Miscellaneous

Tools

(use-package pass
  :ensure t
  :bind (("C-x p" . pass)))

(use-package request
  :ensure t)

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"))

(defun recker/sudo (file-name)
  "find-file, as sudo."
  (interactive "Fsudo Find file:")
  (let ((tramp-file-name (concat "/sudo::" (expand-file-name file-name))))
    (find-file tramp-file-name)))

(defun recker/do-fancy-equal-thingy (beg end)
  (interactive "r")
  (align-regexp beg end "\\(\\s-*\\)\\ =" 1 0 t))

(defun recker/pass-to-string (entry)
  "Read an entry from `pass` as a string."
  (with-temp-buffer
    (password-store-copy entry)
    (progn (yank) (buffer-string))))

Keybindings

(global-set-key (kbd "C-c b") 'browse-url)
(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)
(global-set-key (kbd "C-x C-k k") 'kill-buffer)
(global-set-key (kbd "C-x k") 'kill-this-buffer)
(global-set-key (kbd "C-x |") 'recker/do-fancy-equal-thingy)

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))