UP | HOME

Gareth's Emacs Config

Table of Contents

This is my emacs config. Or at least, this is the main bit. All the org stuff is in org-stuff.org, and there are a couple of other little auxiliary files. At the moment the main one that's not published is my gnus file.

I keep my configs in org files and load them using org-babel-load-file. These webpage are automatically generated from those files using org's export functionality. Of course, all my config files are in version-control, but since my configs contain some personal information, I'm not going to put my repo up on github. During the publishing process I call a filter function (which you can see here) which redacts that personal information from the published html.

I'm not yet happy with these configs. They're messy, and they don't yet do everything I want. You'll see some todo items scattered through them, and there's plenty of poorly documented and legacy code. I hope these things will tend to improve over time.

TODO Things to look in to

Integrate a desktop search?

Documentation

Quite a lot of the GNU/Linux ecology is beautifully documented. Unforunately many people seem unaware of the wonderful manuals and info pages on their machines, and end up hunting down the answers to simple questions in reams of blog posts and stackoverflow questions. Even worse, users of debian and debian-based systems often don't have their documentation installed for ideological reasons. If you can't figure out how to install the docs on your distro-of-choice, I tarred up the /usr/info/ dir from my slackware box, and put it here.

I highly recommend learning to use the GNU info system, especially if you're an emacs user. Emacs users can get started by hitting C-h i h, while console-users can type "info info".

A lot of emacs packages provide their own info files. If, like me, you tend to install emacs packages in your home directory (rather than site-wide) it's easy for that documentation to get lost. Here's how I avoid that.

Whenever I install a new emacs package in my home directory, I check for info files:

find -name "*.info" -print

I might see output that looks something like:

3034466    4 -rw-r--r--   1 gds      users        3503 Oct 29  2014 auctex-11.88/doc/auctex.info
3034445  120 -rw-r--r--   1 gds      users      120617 Oct 29  2014 auctex-11.88/doc/preview-latex.info

If one or more info files exist, then I need to do two things to plumb it in to my system. First, check to see if there's a dir file in the same directory as the info file(s). If not, then we need to create it:

install-info auctex-11.88/doc/auctex.info auctex-11.88/doc/dir
install-info auctex-11.88/doc/preview-latex.info auctex-11.88/doc/dir

The install-info command will create the dir file if it doesn't exist, and update it if it does. Now I just need to tell emacs where it is. I do this by adding the directory in question to the variable Info-default-directory-list. You can see this happening in various places throughout this file. Notice that I always update Info-default-directory-list outside of the use-package expressions. This is because the operation is risk free (if the directory doesn't exist, it has no effect) and fast; and we need that variable to be ready the first time we call M-x info, otherwise some of our docs will be missing. For this reason I don't want any of the updates to be caught in a deferred package.

Bootstraping

How to call this file

This org-file is loaded by my init.el. That file just contains the following:

(menu-bar-mode -1)
(tool-bar-mode -1)
(if (fboundp 'scroll-bar-mode)
    (scroll-bar-mode -1))

(add-to-list 'load-path "~/elisp/use-package/")
(require 'use-package)
; (setq use-package-verbose t)
(add-to-list 'Info-default-directory-list "~/elisp/org-8.3/doc/")
(use-package org-install
  :load-path "~/elisp/org-8.2.10/lisp" )

(org-babel-load-file "~/Documents/configs/emacs/gds.org")

We load packages with use-package, inspired by this Sachachua / John Wigley interview. Once we've loaded the org package, we can call the main config file. The commented out line:

; (setq use-package-verbose t)

…is in case we want to measure the performance of the emacs startup. If we were to uncomment that line before starting emacs, then the *Messages* buffer would fill with information on how long each package took to load. Based on this information I have chosen to "defer" or "lazy-load" some of the packages below. The line about Info-default-directory-list is to ensure I have access to the org-mode manual, as described here.

Other requirements.

We use diminish in conjunction with use-package to keep the modeline tidy.

(use-package diminish
  :load-path "~/elisp/")

Global Editing/Navigation Gadgets

Evil

I used to switch between using emacs and using vim every 6-18 months or so. But my last switch to emacs was easily longer ago than that, and seems unlikely to abate any time soon (this is probably the fault of org-mode and especially the way it integrates with gnus).

Still, vi-like compositional keys are pretty cool, and an easy way to get them is with the evil package. At least two people seem to be enjoying it, ranging from this simple config to this more complex one. I've been using it for a little while myself, and am pretty comfortable.

(use-package evil
  :load-path "~/elisp/evil/"
  :config
  (use-package evil-leader
    :load-path "~/elisp/evil-leader/"
    :config
    (global-evil-leader-mode 1)
    (evil-leader/set-leader "<SPC>")
    (setq evil-leader/in-all-states 1)
    (evil-leader/set-key "b" 'helm-mini
      "e" 'helm-find-files
      "k" 'kill-buffer
      "y" 'helm-show-kill-ring
      "j" 'avy-goto-word-1
      "l" 'avy-goto-line
      "u" 'undo-tree-visualize
      "s" 'helm-swoop
      "n" 'company-yasnippet))
  (evil-mode))

(use-package evil-anzu
  :load-path "~/elisp/emacs-evil-anzu/"
  :init
  (use-package anzu
    :load-path "~/elisp/emacs-anzu/")
  :config
  (anzu-mode))
(use-package evil-org
  :diminish evil-org-mode
  :load-path "~/elisp/evil-org-mode")


(define-minor-mode gds-erc-normal-mode
  "My personal keybindings for ERC normal mode."
  :keymap (make-sparse-keymap))
(evil-define-key 'normal gds-erc-normal-mode-map (kbd "RET") 'erc-send-current-line)
(add-hook 'erc-mode-hook 'gds-erc-normal-mode)

(define-minor-mode gds-gnus-group-normal-mode
  "My personal keybindings for the Group buffer."
  :keymap (make-sparse-keymap))
(add-hook 'gnus-group-mode-hook 'gds-gnus-group-normal-mode)
; Either I can make RET do the right thing in normal mode...
(evil-define-key 'normal gds-gnus-group-normal-mode-map (kbd "RET") 'gnus-topic-select-group)
; Or I can make " b" "j" "k" and " l" do the right thing in gnus-group-mode
(define-key gds-gnus-group-normal-mode-map (kbd "<SPC> b") 'helm-mini)
(define-key gds-gnus-group-normal-mode-map (kbd "<SPC> l") 'avy-goto-line)
(define-key gds-gnus-group-normal-mode-map (kbd "j") 'evil-next-line)
(define-key gds-gnus-group-normal-mode-map (kbd "J J") 'gnus-group-jump-to-group)
(define-key gds-gnus-group-normal-mode-map (kbd "k") 'evil-previous-line)
(defun gds/gnus-topic-hack ()
  "Hack because gnus-topic-mode has been overriding my <SPC> keymaps."
  (define-key gnus-topic-mode-map (kbd "<SPC>") nil)
  (define-key gnus-topic-mode-map (kbd "<SPC> b") 'helm-mini)
  (define-key gnus-topic-mode-map (kbd "<SPC> l") 'avy-goto-line))
(add-hook 'gnus-topic-mode-hook 'gds/gnus-topic-hack)
(define-minor-mode gds-gnus-summary-normal-mode
  "My personal keybindings for summary buffers."
  :keymap (make-sparse-keymap))
(define-key gds-gnus-summary-normal-mode-map (kbd "j") 'evil-next-line)
(define-key gds-gnus-summary-normal-mode-map (kbd "k") 'evil-previous-line)
(add-hook 'gnus-summary-mode-hook 'gds-gnus-summary-normal-mode)
(define-minor-mode gds-gnus-article-normal-mode
  "My personal keybindings for summary buffers."
  :keymap (make-sparse-keymap))
(define-key gds-gnus-article-normal-mode-map (kbd "j") 'evil-next-line)
(define-key gds-gnus-article-normal-mode-map (kbd "k") 'evil-previous-line)
(add-hook 'gnus-article-mode-hook 'gds-gnus-article-normal-mode)

(use-package evil-args
  :load-path "~/elisp/evil-args/"
  :config
  ;; bind evil-args text objects
  (define-key evil-inner-text-objects-map "a" 'evil-inner-arg)
  (define-key evil-outer-text-objects-map "a" 'evil-outer-arg)

  ;; bind evil-forward/backward-args
  (define-key evil-normal-state-map "L" 'evil-forward-arg)
  (define-key evil-normal-state-map "H" 'evil-backward-arg)
  (define-key evil-motion-state-map "L" 'evil-forward-arg)
  (define-key evil-motion-state-map "H" 'evil-backward-arg)

  ;; bind evil-jump-out-args
  (define-key evil-normal-state-map "K" 'evil-jump-out-args))
(use-package evil-jumper
  :load-path "~/elisp/evil-jumper/"
  :config
  (global-evil-jumper-mode))
(use-package evil-matchit
  :load-path "~/elisp/evil-matchit/"
  :config
  (global-evil-matchit-mode 1))
(use-package evil-surround
  :load-path "~/elisp/evil-surround/"
  :config (global-evil-surround-mode 1))

Make escape work the way it does in vim

Hint from here.

;; esc quits
(defun minibuffer-keyboard-quit ()
  "Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
  (interactive)
  (if (and delete-selection-mode transient-mark-mode mark-active)
      (setq deactivate-mark  t)
    (when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
    (abort-recursive-edit)))
(define-key evil-normal-state-map [escape] 'keyboard-quit)
(define-key evil-visual-state-map [escape] 'keyboard-quit)
(define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
(global-set-key [escape] 'evil-exit-emacs-state)

TODO Evil-numbers: Am I happy with these keys?

C-a makes sense for inc, but usually we'd have C-x for dec, and that's used a fair bit in emacs already. C-c C-z is a horrible compromise.

(use-package evil-numbers
  :bind (("C-a" . evil-numbers/inc-at-pt)
         ("C-c C-z" . evil-numbers/dec-at-pt))
  :load-path "~/elisp/evil-numbers")

TODO fix this.

the problem here is that searches always seem to result in an error "invalid function: evil-without-input-method-hooks". but then if i ignore that error, everything seems to work fine :s

use highlight mode like :set hlsearch in vim.

(use-package highlight
  :load-path "~/elisp/")
(use-package evil-search-highlight-persist
  :load-path "~/elisp/evil-search-highlight-persist/"
  :config
  (global-evil-search-highlight-persist t))

now the issue is understanding "evil-search-incrementally: invalid function: evil-without-input-method-hooks "

multiple cursors

Sometimes you just want to edit with lots of cursors at once. I haven't quite got it interacting with evil-mode the way I want yet, but it's awesome in plain-emacs buffers.

(use-package multiple-cursors
  :load-path "~/elisp/multiple-cursors"
  :bind (("C-S-c C-S-c" . mc/edit-lines)
         ("C->" . mc/mark-next-like-this)
         ("C-<" . mc/mark-previous-like-this)
         ("C-c C-<" . mc/mark-all-like-this)))

Spelling

Use a british dictionary for spelling.

(define-key text-mode-map (kbd "M-TAB") 'flyspell-auto-correct-word)
(unless (or (string= "example.com" (system-name))
            (string-prefix-p "example.com" (system-name)))
  (ispell-change-dictionary "en_GB" 1))

Old school keys

When I started using emacs, this is what the home and end keys did by default.

(bind-key "<home>" 'beginning-of-buffer)
(bind-key "<end>" 'end-of-buffer)
(cua-mode -1)
(delete-selection-mode -1)

Enabling "advanced" commands

(put 'scroll-left 'disabled nil)
(put 'scroll-right 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'set-goal-column 'disabled nil)
(put 'narrow-to-region 'disabled nil)

Search and Replace stuff

Re-builder is now included in emacs by default, but you still have to enable it.

(setq-default case-fold-search t)

(use-package re-builder
  :bind ("<F8>" . re-builder)
  :config
  ;; From http://emacs-journey.blogspot.co.uk/2012/06/re-builder-query-replace-this.html
  (defun gds/reb-query-replace-this-regxp (replace)
    "Uses the regexp built with re-builder to query the target buffer.
This function must be run from within the re-builder buffer, not the target
buffer.

Argument REPLACE String used to replace the matched strings in the buffer.
 Subexpression references can be used (\1, \2, etc)."
    (interactive "sReplace with: ")
    (if (eq major-mode 'reb-mode)
        (let ((reg (reb-read-regexp)))
          (select-window reb-target-window)
          (save-excursion
            (beginning-of-buffer)
            (query-replace-regexp reg replace)))
      (message "Not in a re-builder buffer!")))

  (bind-key "\C-c\M-%" 'gds/reb-query-replace-this-regxp reb-mode-map))

Fill Stuff

I'm not currently using fci mode by default, but sometimes it's handy. When I do use it, I want to have a sensible fill column.

Note FCI mode does add extra characters to the end of each line in org-export!

(use-package fill-column-indicator
  :load-path "~/elisp/Fill-Column-Indicator"
  :defer 5
  :commands fci-mode)
;; (add-hook 'tuareg-mode-hook 'fci-mode)
(add-hook 'tuareg-mode-hook '(lambda () (setq fill-column 80)))
;(remove-hook 'LaTeX-mode-hook 'fci-mode)
(add-hook 'LaTeX-mode-hook (lambda ()  (setq fill-column 80)))
(add-hook 'message-mode-hook (lambda ()  (setq fci-rule-column 80)))
;; (add-hook 'message-mode-hook 'fci-mode)
;(remove-hook 'haskell-mode-hook 'fci-mode)
;(remove-hook 'haskell-mode-hook (lambda ()  (setq fill-column 80)))
;(remove-hook 'text-mode-hook 'fci-mode)
(add-hook 'text-mode-hook (lambda ()  (setq fci-rule-column 80)))
;; (add-hook 'python-mode-hook 'fci-mode)
(add-hook 'python-mode-hook (lambda ()  (setq fill-column 80)))

(setq sentence-end-double-space nil)

Yasnippet stuff

Snippets make editing stuff with boilerplate a whole lot faster. I use them in combination with Company Mode. Note the binding to company-yasnippet here.

(use-package yasnippet
  :load-path "~/elisp/yasnippet/"
  :defer 5
  :commands yas-global-mode
  :diminish yas-minor-mode
  :config
  (yas-global-mode)
  (setq yas-prompt-functions
        '( yas-ido-prompt
           yas-dropdown-prompt
           yas-completing-prompt
           yas-no-prompt
           yas-x-prompt)))

Whitespace

Don't ever insert tabs. Seriously.

;(global-whitespace-mode)
(setq-default indent-tabs-mode nil)

Rainbow Mode

This makes "colour words" in my programs appear in the colours they describe. Particularly good for CSS and the like.

(use-package rainbow-mode
  :load-path "~/elisp/rainbow-mode.el/"
  :unless (string-prefix-p "example.com" (system-name))
  :diminish rainbow-mode
  :config
  (add-hook 'emacs-lisp-mode-hook 'rainbow-mode)
  (add-hook 'css-mode-hook 'rainbow-mode)
  (add-hook 'html-mode-hook 'rainbow-mode)
  (add-hook 'js2-mode-hook 'rainbow-mode))

Avy (ace-jump's spiritual successor?)

Avy gives you keyboard shortcuts for jumping to arbitrary words or lines.

(use-package avy
  :load-path "~/elisp/avy/")

Ace Link

https://github.com/abo-abo/ace-link

Like avy or ace-jump mode, but for links in things like help buffers. The magic key is "o".

(use-package ace-link
  :load-path "~/elisp/ace-link/"
  :config
  (ace-link-setup-default))

Undo tree, and undo in region

If version control has taught us anything, it's that undoing and redoing things naturally gives rise to a tree. This package visualises that tree in emacs, and lets you bring back any state in that tree.

(use-package undo-tree
  :diminish undo-tree-mode
  :load-path "~/elisp/undo-tree/"
  :config
  (defun gds/undo ()
    "Just like regular undo-tree-undo, but without the cursor jumping around if
the region is active"
    (interactive)
    (if (region-active-p)
        (save-excursion
          (undo-tree-undo))
      (undo-tree-undo)))

  (bind-key "C-/" 'gds/undo undo-tree-map)
  (global-undo-tree-mode))

Browse Kill Ring

This is a gadget that makes it a bit easier to browse through all the things that used to be in your clipboard, and pick one to paste.

(use-package browse-kill-ring
  :load-path "~/elisp/browse-kill-ring"
  :commands browse-kill-ring)

Helm Stuff

Helm is the best menu widget I've come across. When I ask emacs to do a thing, helm shows me all possible options. I can type a regex to (incrementally) narrow that list, then hit enter to make it happen.

Use Helm for almost everything.

Occasionally there's something helm is rubbish for. For those things I turn it off.

(use-package helm-mode
  :demand
  :load-path "~/elisp/helm/"
  :diminish helm-mode
  :bind (("C-x b" . helm-mini))
  :config
  (helm-mode)
  (setq helm-quick-update t)
  (setq helm-bookmark-show-location t)
  (setq helm-buffers-fuzzy-matching t)
  ;; Don't use helm for LaTeX commands, since they usually guess right anyway.
  (add-to-list 'helm-completing-read-handlers-alist '(TeX-command-master))
  (add-to-list 'helm-completing-read-handlers-alist '(LaTeX-environment))
  (add-to-list 'helm-completing-read-handlers-alist '(TeX-insert-macro))
  (add-to-list 'helm-completing-read-handlers-alist '(LaTeX-section))
  (add-to-list 'helm-completing-read-handlers-alist '(TeX-master-file-ask))
  ;; Don't use helm for adding fields to BBDB, since helm cocks up the address business.
  (add-to-list 'helm-completing-read-handlers-alist '(bbdb-insert-field))
  (add-to-list 'helm-completing-read-handlers-alist '(bbdb-create))
  ;; Don't use it for org tags, 'cause other completion is better there.
  (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command))
  (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags))
  (add-to-list 'helm-completing-read-handlers-alist '(org-match-sparse-tree)))

Use Swoop

Swoop gives you a helm-like interface for searching buffers. It's an easy way to see all search-matches at once, and bounce between them.

(use-package helm-swoop
  :load-path "~/elisp/helm-swoop"
  :bind (("C-s" . helm-swoop)
         ("C-S-s" . helm-multi-swoop-all))
  :config
  ;; Hopefully temporary, because my version of helm-swoop requires it
  ; (setq helm-match-plugin-mode helm-mode)
  )
  • Turn off the pre-input serach query

    By default, helm-swoop tries to guess what you're going to search for, based on whatever's under your cursor. For my usual usage I found it did a poor job of guessing, and the guess got in the way of what I actually wanted to do.

    ;; disable pre-input
    (setq helm-swoop-pre-input-function
          (lambda () ""))
    

TODO Learn more about helm

It's clearly way more powerful that the trival use I'm putting it to.

Discover

Discover gives you magit-like context menus in other parts of emacs. This makes it easier to discover and learn new bits of the editor/environment.

(use-package discover
  :load-path "~/elisp/discover.el/"
  :init
  (use-package makey
    :load-path "~/elisp/makey/")
  :config
  (discover-mode))

Input Methods

Emacs has lots of input methods, for typing various lanugages. Since I'm familiar with LaTeX already, I like to use the TeX input method to enter unicode characters that aren't on my keyboard.

(setq default-input-method "TeX")

Now I can use C-\ to switch to the TeX input mode, whereupon typing things like ∖lambda results in the unicode character λ.

Messing with the Mouse

Sometimes my trackpad annoys me, so I want to turn it off.

(defun gds/mouse-off ()
  "Turn off the trackpad"
  (interactive)
  (shell-command "synclient AreaBottomEdge=1"))

(defun gds/mouse-on ()
  "Turn on the trackpad"
  (interactive)
  (shell-command "synclient AreaBottomEdge=0"))

Projectile

Projectile gives you a bunch of nice functions for interacting with projects as a whole. Things like "search all the files in this project for this regex". It guesses which files are in which projects by looking at your git, hg, etc files.

(add-to-list 'Info-default-directory-list "~/elisp/dash.el/")
(use-package projectile
  :load-path "~/elisp/projectile"
  :diminish projectile-mode
  :init
  (use-package dash
    :load-path "~/elisp/dash.el")
  :config
  (projectile-global-mode))

Boxquote

A handy trick for drawing a box around things. Good for quotes in emails and docs.

(use-package boxquote
  :load-path "~/elisp/")

Company Mode – "complete anything"

Give me pretty guified auto-completion of everything I ever want to type.

(use-package company
  :load-path "~/elisp/company-mode"
  :defer 5
  :diminish company-mode
  :config
  (use-package company-yasnippet)
  (global-company-mode))

Gui things

Colors for the terminal

Makes the terminal less ugly.

(use-package color-theme-approximate
  :load-path "~/elisp/color-theme-approximate/"
  :config
  (color-theme-approximate-on))

Themes

We can get lots of themes from here:

git clone https://github.com/emacs-jp/replace-colorthemes.git
(add-to-list 'custom-theme-load-path
             (file-name-as-directory "~/elisp/replace-colorthemes/"))

Display stuff

Tell me what column I'm on, as well as what line. Show the name of the file in the window title. Use a cursor that looks like it belongs on an old BBC Micro.

(column-number-mode)
(setq frame-title-format '(buffer-file-name "%b (%f)" ("%f")))
(setq-default cursor-type 'hbar)

I don't currently use the distraction-free stuff.

; (org-babel-load-file "~/Documents/configs/emacs/distraction-free.org")

Time display

Show a clock.

(setq display-time-24hr-format t)
(display-time-mode 1)

Minibuffer

A quick trick for keeping the cursor out of the minibuffer prompt.

; Used to be:  (setq minibuffer-prompt-properties '(read-only t face minibuffer-prompt))
(setq minibuffer-prompt-properties (quote (read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt)))
  • TODO Do this with something like add-to-list

    …rather than nuking any other properties that may have been in there.

Make ediff single-frame

The default behaviour with multiple frames is really annoying in xmonad.

(setq  ediff-window-setup-function 'ediff-setup-windows-plain)

Fonts

Sometimes I like to use terminus. Sometimes I use a hi-res screen and don't want to strap binoculars to my head.

(defun gds/set-fonts ()
  "Set up the fonts that I like."
  (interactive)
  (set-face-font 'menu "-*-terminus-*-*-*-*-18-*-*-*-*-*-*-*")
  (set-face-font 'default "-*-terminus-*-*-*-*-22-*-*-*-*-*-*-*")
  )

(defun gds/xinerama-fonts ()
  "Set up the fonts that I like for my desktop."
  (interactive)
  (set-face-font 'menu "-*-terminus-*-*-*-*-12-*-*-*-*-*-*-*")
  (set-face-font 'default "-*-terminus-*-*-*-*-14-*-*-*-*-*-*-*"))
(defun gds/default-fonts ()
  "Set up the fonts that I like for my desktop."
  (interactive)
  (set-face-font 'menu "-unknown-DejaVu Sans Mono-normal-normal-normal-*-13-*-*-*-m-0-iso10646-1")
  (set-face-font 'default "-unknown-DejaVu Sans Mono-normal-normal-normal-*-13-*-*-*-m-0-iso10646-1"))
(defun gds/default-but-larger-fonts ()
  "Set up the fonts that I like for my desktop."
  (interactive)
  (set-face-font 'menu "-unknown-DejaVu Sans Mono-normal-normal-normal-*-18-*-*-*-m-0-iso10646-1")
  (set-face-font 'default "-unknown-DejaVu Sans Mono-normal-normal-normal-*-22-*-*-*-m-0-iso10646-1"))
(defun gds/tree-fonts ()
  "Set up the fonts that I like for my desktop."
  (interactive)
  (set-face-font 'menu "-unknown-DejaVu Sans Mono-normal-normal-normal-*-14-*-*-*-m-0-iso10646-1")
  (set-face-font 'default "-unknown-DejaVu Sans Mono-normal-normal-normal-*-16-*-*-*-m-0-iso10646-1"))

Volatile Highlights

From github:

This library provides minor mode `volatile-highlights-mode’, which brings visual feedback to some operations by highlighting portions relating to the operations.

All of highlights made by this library will be removed when any new operation is executed.

(use-package volatile-highlights
  :load-path "~/elisp/volatile-highlights.el/"
  :diminish volatile-highlights-mode
  :config
  (volatile-highlights-mode t))

Pretty Calendar Library

From here.

(use-package calfw
  :load-path "~/elisp/emacs-calfw"
  :bind ("<f12>" . cfw:open-org-calendar)
  :config
  (use-package calfw-org
    :load-path "~/elisp/emacs-calfw"))

Typesetting Languages

Markdown

From here.

(use-package markdown-mode
  :load-path "~/elisp/markdown-mode"
  :commands markdown-mode)

LaTeX Stuff

AUCTeX is by far the best way to edit LaTeX documents.

(add-to-list 'Info-default-directory-list "~/elisp/auctex-11.88/doc/")
(use-package tex
  :load-path "~/elisp/auctex-11.88/"
  :defer 5
  :commands latex-mode
  :config
  (TeX-global-PDF-mode t)
  (setq  LaTeX-command-style '(("" "%(PDF)%(latex) -file-line-error %S%(PDFout)")))

  ;; (setq TeX-view-program-list '(("xpdf" ("xpdf"
  ;;                                           (mode-io-correlate " -p %(outpage)")
  ;;                                           " %o"))
  ;;                            ("zathura" ("zathura"
  ;;                                           (mode-io-correlate " -p %(outpage)")
  ;;                                           " %o"))
  ;;                               ("mupdf" ("mupdf"
  ;;                                         (mode-io-correlate " -p %(outpage)")
  ;;                                         " %o"))))

  ;; (defun gds/output-pdf-debug ()
  ;;   (message "Testing if pdf")
  ;;   (output-pdf))

  (setq TeX-view-program-selection '((output-pdf "xpdf")
                                     ((output-dvi style-pstricks)
                                      "dvips and gv")
                                     (output-dvi "xdvi")

                                     (output-html "xdg-open")))
  (setq TeX-auto-save t)
  (setq TeX-parse-self t)
  (setq-default TeX-master nil)
  (setq TeX-math-close-double-dollar t)
  (setq TeX-electric-sub-and-superscript t)
  (setq TeX-electric-macro t)
  (setq TeX-insert-braces nil)

  ;; Track changes http://sourceforge.net/projects/latextrack/
                                        ;(load-file "~/elisp/ltc-mode.el")

  (add-hook 'LaTeX-mode-hook 'auto-fill-mode))

(defun gds/make-auctex-use-X ()
  "Stop auctex from attempting to call a console DVI viewer.

For some reason, when my emacs is started as a server, the
`TeX-command-list` variable is overwritten to make View do
something weird. This function puts it back the way it was."
  (interactive)
  (setq TeX-command-list '(("TeX" "%(PDF)%(tex) %`%S%(PDFout)%(mode)%' %t" TeX-run-TeX nil
                            (plain-tex-mode ams-tex-mode texinfo-mode)
                            :help "Run plain TeX")
                           ("LaTeX" "%`%l%(mode)%' %t" TeX-run-TeX nil
                            (latex-mode doctex-mode)
                            :help "Run LaTeX")
                           ("Makeinfo" "makeinfo %t" TeX-run-compile nil
                            (texinfo-mode)
                            :help "Run Makeinfo with Info output")
                           ("Makeinfo HTML" "makeinfo --html %t" TeX-run-compile nil
                            (texinfo-mode)
                            :help "Run Makeinfo with HTML output")
                           ("AmSTeX" "%(PDF)amstex %`%S%(PDFout)%(mode)%' %t" TeX-run-TeX nil
                            (ams-tex-mode)
                            :help "Run AMSTeX")
                           ("ConTeXt" "texexec --once --texutil %(execopts)%t" TeX-run-TeX nil
                            (context-mode)
                            :help "Run ConTeXt once")
                           ("ConTeXt Full" "texexec %(execopts)%t" TeX-run-TeX nil
                            (context-mode)
                            :help "Run ConTeXt until completion")
                           ("BibTeX" "bibtex %s" TeX-run-BibTeX nil t :help "Run BibTeX")
                           ("Biber" "biber %s" TeX-run-Biber nil t :help "Run Biber")
                           ("View" "%V" TeX-run-discard-or-function t t :help "Run Viewer")
                           ("Print" "%p" TeX-run-command t t :help "Print the file")
                           ("Queue" "%q" TeX-run-background nil t :help "View the printer queue" :visible TeX-queue-command)
                           ("File" "%(o?)dvips %d -o %f " TeX-run-command t t :help "Generate PostScript file")
                           ("Index" "makeindex %s" TeX-run-command nil t :help "Create index file")
                           ("Check" "lacheck %s" TeX-run-compile nil
                            (latex-mode)
                            :help "Check LaTeX file for correctness")
                           ("Spell" "(TeX-ispell-document \"\")" TeX-run-function nil t :help "Spell-check the document")
                           ("Clean" "TeX-clean" TeX-run-function nil t :help "Delete generated intermediate files")
                           ("Clean All" "(TeX-clean t)" TeX-run-function nil t :help "Delete generated intermediate and output files")
                           ("Other" "" TeX-run-command t t :help "Run an arbitrary command"))))

Programming Languages

Go

(use-package go-mode-autoloads
  :load-path "~/elisp/go-mode.el"
  :config
  (add-hook 'before-save-hook 'gofmt-before-save))

Flycheck

This is a mode which uses external tools (like the GHC type checker or whatever) to check your code as you type it, and give you feedback. It works for many lanuages.

(use-package flycheck
  :load-path "~/elisp/flycheck"
  :diminish flycheck-mode
  :if (not (string= (system-name) "fire"))
  :config
  (require 'go-mode)
  (add-to-list 'go-mode-hook 'flycheck-mode))

Coq

Proof General could also handle various other proof assistants if I used them.

(use-package proof-site
  :load-path "~/elisp/ProofGeneral-4.2/generic/"
  :config
  (progn (setq  coq-dependency-analyzer "~/.opam/4.01.0/bin/coqdep")
         (setq coq-compiler "~/.opam/4.01.0/bin/coqc")
         (setq coq-prog-name "~/.opam/4.01.0/bin/coqtop")))

Nix things

The nix-mode package is managed by nixos, not by elpa or similar.

(if (string-prefix-p "example.com" (system-name))
    (progn
      (require 'nix-mode)
      ))

Haskell stuff

(use-package haskell
  :load-path "~/elisp/haskell-mode"
  :config
  ;; Show types and things in the modeline
  (require 'haskell-doc)
  (add-hook 'haskell-mode-hook 'haskell-doc-mode)

  ;; Magic indentation.
  (add-hook 'haskell-mode-hook 'haskell-indentation-mode)

  ;; REPL control
  (add-hook 'haskell-mode-hook 'interactive-haskell-mode)
  (setq haskell-process-suggest-remove-import-lines t)
  (setq haskell-process-auto-import-loaded-modules t)
  (setq haskell-process-log t)
  (setq haskell-process-type 'cabal-repl)
  )

TODO IDE features – particularly flychecking.

At some point I'd like to make scion work.

Web stuff

I've played a little web mode, but I'm switching back to html mode, because of the awesomeness that is skewer.

(use-package skewer-mode
  :load-path "~/elisp/skewer-mode"
  :defer 5
  :commands (skewer-mode run-skewer)
  :init
  (use-package skewer-html
    :load-path "~/elisp/skewer-mode")
  (use-package simple-httpd
    :load-path "~/elisp/emacs-web-server")
  (use-package js2-mode
    :defer 5
    :load-path "~/elisp/js2-mode")
  (use-package html-mode
    :mode ("\\.html?\\'" . html-mode))
  :config
  (add-hook 'js2-mode-hook 'skewer-mode)
  (add-hook 'css-mode-hook 'skewer-css-mode)
  (add-hook 'html-mode-hook 'skewer-html-mode))
(use-package web-mode
  :disabled
  :config
  (use-package skewer-html
    :load-path "~/elisp/skewer-mode")
  (use-package skewer-css
    :load-path "~/elisp/skewer-mode")
  (add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.jsp\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.as[cp]x\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.tmpl\\'" . web-mode))

  (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
  ;; (setq web-mode-engines-alist
  ;;       '(("php" . "\\.phtml\\'") 
  ;;         ("blade" . "\\.blade\\.")))
  )

ELisp

Notice when elisp is lexical

A trick to let you know which elisp semantics are currently in play.

(use-package lexbind-mode
  :load-path "~/elisp/lexbind-mode/"
  :config
  (add-hook 'emacs-lisp-mode-hook 'lexbind-mode))

Use paredit for elisp

ParEdit is the best way to edit lisp. Keeps your parens ballenced all the time, and handles your indendataion for you on the way.

(use-package paredit
  :load-path "~/elisp/"
  :diminish paredit-mode
  :config
  (add-hook 'emacs-lisp-mode-hook 'paredit-mode))
(add-hook 'emacs-lisp-mode-hook 'show-paren-mode)

ESS

Emacs Speaks Statistics is a mode for working with R programs.

To install, you'll want:

cd ~/elisp/ 
wget http://ess.r-project.org/downloads/ess/ess-15.03-1.tgz
tar -xzvf ess-15.03-1.tgz
cd ess-15.03-1
make

And to use in emacs:

(add-to-list 'Info-default-directory-list "~/elisp/ess-15.03-1/doc/info/")
(use-package ess-site
  :load-path "~/elisp/ess-15.03-1/lisp/"
  :defer 5
  :config)

Crontab Mode

Harley Gorell's mode for editing cron files.

(use-package crontab-mode
  :load-path "~/elisp"
  :mode ("\\.cron\\(tab\\)?\\'" . crontab-mode)
  :mode ("cron\\(tab\\)?\\."    . crontab-mode))

OCaml

The best mode for OCaml editing in emacs is Tuareg mode.

(use-package tuareg
  :load-path "~/elisp/tuareg"
  :defer 5)

Rust

Rust is mozilla's new language for systems programming. They provide an emacs mode for it.

(use-package rust-mode
  :load-path "~/elisp/rust-mode"
  :mode ("\\.rs\\'" . rust-mode))

…and there's a third-party flymake mode:

(use-package flymake-rust
  :load-path "~/elisp/flymake-rust"
  :init
  (use-package flymake-easy
    :load-path "~/elisp/flymake-easy"))

Ledger

Ledger is an accounting program, for keeping track of finances. It comes with an emacs mode.

(use-package ledger-mode
  :load-path "~/elisp/ledger/lisp"
  :commands ledger-mode)

Email Things

Use Gnus for email. It comes with emacs.

(use-package gnus
  :bind ("C-x C-m" . gnus-group-mail)
  :commands gnus
  :defer 5
  :config
  (add-hook 'message-mode-hook 'flyspell-mode)
  (gnus-registry-initialize)
  (setq gnus-treat-from-gravatar t)
  (setq mail-user-agent 'gnus-user-agent)
  (setq read-mail-command 'gnus-user-agent))

If I haven't set up a sensible email system for this machine, I want to know it.

(setq sendmail-program "/bin/false")

Optionally use tog mode for writing emails using org.

(use-package tog-mode
  :load-path "~/elisp/tog-mode"
  :config
  (add-hook 'message-mode-hook
            (lambda () (tog-setup (list 'org-mode 'message-mode)))))

But better to use an indirect buffer!

(defun gds/email-org-editing ()
  "Make an indirect buffer for this email, which is in
  org-mode."
  (interactive)
  (switch-to-buffer (make-indirect-buffer 
                     (current-buffer) 
                     (generate-new-buffer-name "org-email")))
  (org-mode))

Handle FF email links.

(defun gds/external-email (address)
  "Email a given address.
Most useful when called by Firefox"
  (gnus-group-mail)
  (insert address)
  (save-excursion
    (beginning-of-buffer)
    (while (search-forward "mailto:" nil t)
      (replace-match "" nil t)))
  (search-forward-regexp "^Subject: ")
  (end-of-line))

Use offlineimap where available – not all my machines have it, or want it.

(use-package offlineimap
  :load-path "~/elisp/offlineimap.el/"
  :unless (or (string-prefix-p "example.com.doc" (system-name))
              (string-prefix-p "example.com" (system-name))))

Identity management.

I use the same instance of gnus to handle both personal email and work email. I also use gnus on several different machines, with different ways of sending mail. The following code is for switching between these identities: setting things like "From" lines and send methods. The functions gds/email-almaz and gds/email-college can be called interactively, and are also called automatically when I enter certain mailboxes (gnus groups).

(defun gds/change-email-address (new-address)
  "Change my email address
         If I'm in message mode, replace the From line too."
  (setq user-mail-address new-address)
  (when (string= major-mode "message-mode")
    (save-excursion
      (beginning-of-buffer)
      (search-forward-regexp "^From: ")
      (search-forward "<")
      ;; Kill current email
      (let ((left (point)))
        (search-forward ">")
        (delete-region left (point)))
      ;; replace with toth
      (insert new-address ">")
      )))

(defun gds/email-almaz ()
  "Switch to home email"
  (interactive)
  (setq message-sendmail-extra-arguments '("-a" "zenit"))
  (setq user-full-name "My Name")
  (gds/change-email-address "example@example.com")
  (if (or (string-prefix-p "example.com.totherme" (system-name))
          (string-prefix-p "example.com" (system-name))
          (string-prefix-p "example.com" (system-name)))
      (progn
        (setq send-mail-function 'smtpmail-send-it)
        (setq message-send-mail-function 'smtpmail-send-it)
        (setq smtpmail-smtp-server "example.com")
        (setq smtpmail-smtp-service 57043)
        (setq smtpmail-smtp-user nil)))
  (if (string-prefix-p "example.com" (system-name))
      (progn
        (setq send-mail-function 'sendmail-send-it))))

(defun gds/email-college ()
  "Switch to college email"
  (interactive)
  (setq message-sendmail-extra-arguments '())
  (setq user-full-name "My Name")
  (gds/change-email-address "example@example.com")
  (if (or (string-prefix-p "tree.totherme" (system-name))
          (string-prefix-p "example.com" (system-name))
          (string-prefix-p "example.com" (system-name)))
      (progn
        (setq send-mail-function 'smtpmail-send-it)
        (setq message-send-mail-function 'smtpmail-send-it)
        (setq smtpmail-smtp-server "example.com")
        (setq smtpmail-smtp-service 587)
        (setq smtpmail-smtp-user "name")
        (setq smtpmail-auth-credentials
              (list (list "example.com" 587
                          "name" (gds/get-irc-password "example.com" "name"))))))
  (if (string-prefix-p "example.com.doc" (system-name))
      (progn
        (setq send-mail-function 'sendmail-send-it)
        (setq message-send-mail-function 'sendmail-send-it)
        (setq sendmail-program "/usr/sbin/sendmail")
        ;; (setq send-mail-function 'smtpmail-send-it)
        ;; (setq message-send-mail-function 'smtpmail-send-it)
        ;; (setq smtpmail-smtp-server "example.com")
        ;; (setq smtpmail-smtp-service 587)
        ;; (setq smtpmail-auth-credentials
        ;;    (list (list "example.com" 587 "name" (gds/get-irc-password "example.com" "name"))))
        ))
  (if (string-prefix-p "example.com" (system-name))
      (progn
        (setq send-mail-function 'sendmail-send-it))))

(gds/email-almaz)

Gnus spam trick

I have a cron job on my server which runs every night, and trains spamassassin from whatever it finds in the spamtraining mailbox. Then it deletes the mailbox.

(defun gdsspam ()
  (interactive)
  (gnus-summary-move-article nil "spamtraining" nil))

Mutt email interface

If you want to use emacs(client?) to edit mutt emails, this is the thing you need.

(or (assoc "mutt-" auto-mode-alist) (setq auto-mode-alist (cons '("mutt-" . mail-mode) auto-mode-alist)))
(or (assoc "\\.eml$" auto-mode-alist) (setq auto-mode-alist (cons '("\\.eml$" . mail-mode) auto-mode-alist)))

Gnus notify

This is a widget that displays on my modeline how many emails I haven't dealt with yet. Read how to use it here. There are other options.

(use-package gnus-notify
  :load-path "~/elisp/"
  :defer 5
  :unless (string-prefix-p "example.com" (system-name)))

BBDB Stuff

I use BBDB3 to track everyone I've ever communicated with. It stores email addresses, phone numbers, home addresses, birthdays, and so on. I keep my bbdb file with my org files, and I have git keep it synced across my various machines. I use bbdb-vcard to sync it with my phone.

BBDB-ext provides some extra functionality. Things like displaying addresses on google maps.

(add-to-list 'Info-default-directory-list "~/elisp/bbdb/doc/")
(use-package bbdb
  :load-path "~/elisp/bbdb/lisp"
  :defer 5
  :config
  ;(use-package bbdb-mua) ; I shouldn't really need this should I?
  (use-package bbdb-gnus)
  (use-package bbdb-message)
  (use-package bbdb-anniv)
  (setq bbdb-file "/path/to/org/bbdb")
  (setq bbdb-phone-style nil)
  (setq bbdb-update-records-p 'create)
  (bbdb-initialize 'gnus 'message 'anniv)
  (bbdb-mua-auto-update-init 'gnus 'message 'w3)
  (setq bbdb-pop-up-window-size 6)
  (setq bbdb-mua-pop-up-window-size 6)
  (setq bbdb-ignore-message-alist '(("From" . "@postmaster.twitter.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "example@example.com")
                                    ("From" . "Mail Delivery System")
                                    ("From" . "mailer-daemon")
                                    ("From" . "facebookmail.com")
                                    ("From" . "noreply")
                                    ("From" . "no-reply")
                                    ("From" . "donotreply")
                                    ("Subject" . "SPAM")
                                    ("X-Spam-Level" . "*")
                                    ("From" . "example@example.com")))

  (setq bbdb-complete-mail-allow-cycling t)

  (use-package bbdb-vcard
    :unless (string-prefix-p "example.com" (system-name))
    :load-path "~/elisp/bbdb-vcard/")

  (use-package bbdb-ext
    :disabled ;; key maps clash with evil mode
    :init (require 'bbdb)
    :load-path "~/elisp/bbdb-ext"))

Connectivity Gadgets

Web Stuff

Default browsers

On many linux machines xdg-open (or similar) does the right thing, so we can just have emacs use the default browser. But if that doesn't work, emacs falls back to trying mozilla, then firefox. I'd rather fall straight to firefox.

(if (memq system-type '(gnu/linux))
    (unless (browse-url-can-use-xdg-open)
      (setq browse-url-browser-function 'browse-url-firefox)))

eww browser

Emacs comes with its own web browser now.

(use-package eww
  :bind ("C-c 3" . eww)
  :config
  (setq eww-search-prefix "https://startpage.com/do/metasearch.pl?q="))

Gadgets for following FB links

I browse facebook using a different browser profile to the one I do my regular browsing in. This is because I'm paranoid, and don't want FB tracking me all over the web. When people post links in facebook, instead of following them by clicking on them, I use the vimperator addon to copy the link (keyboard shortcut: ;y), then in emacs I hit M-x gds/visit-fb-url. This pulls the actual link out of the facebook redirection spaghetti, and opens it in my main browser.

To make this happen, we need a simple url-decoder. There might be a better one in eww these days, but this seems to work for me.

(defun gds/decode-url-region (beginning end)
  "Decodes the URL in the region."
  (interactive "*@r")
  (replace-string "%0D%0A" "\n" nil beginning end)
  (replace-string "%0D" "\n" nil beginning end)
  (replace-string "%0A" "\n" nil beginning end)
  (replace-string "%20" " " nil beginning end)
  (replace-string "%21" "!" nil beginning end)
  (replace-string "%22" "\"" nil beginning end)
  (replace-string "%23" "#" nil beginning end)
  (replace-string "%24" "$" nil beginning end)
  (replace-string "%26" "&" nil beginning end)
  (replace-string "%27" "'" nil beginning end)
  (replace-string "%28" "(" nil beginning end)
  (replace-string "%29" ")" nil beginning end)
  (replace-string "%2A" "*" nil beginning end)
  (replace-string "%2B" "+" nil beginning end)
  (replace-string "%2C" "," nil beginning end)
  (replace-string "%2D" "-" nil beginning end)
  (replace-string "%2E" "." nil beginning end)
  (replace-string "%2F" "/" nil beginning end)
  (replace-string "%3A" ":" nil beginning end)
  (replace-string "%3B" ";" nil beginning end)
  (replace-string "%3C" "<" nil beginning end)
  (replace-string "%3D" "=" nil beginning end)
  (replace-string "%3E" ">" nil beginning end)
  (replace-string "%3F" "?" nil beginning end)
  (replace-string "%40" "@" nil beginning end)
  (replace-string "%5B" "[" nil beginning end)
  (replace-string "%5C" "\\" nil beginning end)
  (replace-string "%5D" "]" nil beginning end)
  (replace-string "%5E" "^" nil beginning end)
  (replace-string "%5F" "_" nil beginning end)
  (replace-string "%60" "`" nil beginning end)
  (replace-string "%7B" "{" nil beginning end)
  (replace-string "%7C" "|" nil beginning end)
  (replace-string "%7D" "}" nil beginning end)
  (replace-string "%7E" "~" nil beginning end)
  (replace-string "%25" "%" nil beginning end))

Now we can use that to decode facebook-mangled URLs

(defun gds/visit-fb-url ()
  "Take a facebook-mangled URL from clipboard, and open it in your browser."
  (interactive)
  (with-temp-buffer
    (yank)
    (beginning-of-buffer)
    (zap-to-char 1 ?=)
    (search-forward "&")
    (backward-char)
    (kill-line)
    (gds/decode-url-region (point-min) (point-max))
    (browse-url (buffer-string))))

Git Stuff

Magit

This is the best way I've found to interact with git. Git is awesome, but the UI kinda sucks.

(add-to-list 'Info-default-directory-list "~/elisp/magit/Documentation")
(use-package magit
  :load-path "~/elisp/magit/lisp/"
  :bind ("\C-xg" . magit-status)
  :config)

Git Gutter

This persistently shows which lines in the current file differ from the repo. And lets you commit changes directly.

Irritatingly, it can cause a big slowdown if it's enabled on very large files. Such as some of my org-files, for example. For this reason I want to enable global-git-gutter+-mode, but disable it for certain files and directories. I don't want to just enable it for certain modes – since I need it disabled for some of my (large) org files, and I want it enabled for others. I can't use file-local or directory-local variables to solve the problem, because global-git-gutter+-mode adds git=gutter+-turn-on to the file load and revert hooks.

The solution seems to be to advise the internal functions git=gutter+-turn-on and git-gutter+-reenable-buffers. Now I can set gds/git-gutter+-okp to nil in the file- or directory-local variables of any file that would otherwise cause problems.

(use-package git-gutter+
  :load-path "~/elisp/git-gutter-plus"
  :defer 5
  :diminish git-gutter+-mode
  :commands global-git-gutter+-mode
  :bind (("C-x C" . git-gutter+-stage-and-commit)
         ("C-x c" . git-gutter+-commit)
         ("C-x t" . git-gutter+-stage-hunks))
  :config
  (defvar-local gds/git-gutter+-okp t
    "Is it ok to enable `git-gutter+-mode' on this file?")

  (advice-add 'git-gutter+-turn-on :before-while (lambda () gds/git-gutter+-okp))
  (advice-add 'git-gutter+-reenable-buffers :before-while (lambda () gds/git-gutter+-okp))

  (global-git-gutter+-mode))

Git Timemachine

This is a gadget that lets you step a file backward and forward through its git-history.

(use-package git-timemachine
  :load-path "~/elisp/git-timemachine")

Dired stuff

Dired details annoyed me more than it was worth.

;; Make dired less verbose
;; (require 'dired-details)
;; (setq-default dired-details-hidden-string "--- ")
;; (dired-details-install)

Make dired suggest sensible (mc-like) targets for copies and renames, and get human readable file sizes. Also: Wdired is awesome. Everyone should be able to change file names and permissions using keyboard macros and multiple-cursors.

(use-package dired
  :config
  (setq dired-dwim-target t)
  (setq dired-listing-switches "-alh")
  (setq wdired-allow-to-change-permissions t)
  (bind-key "C-c w" 'wdired-change-to-wdired-mode dired-mode-map))

Online offline functions

When I open my laptop, I like to be able to do something like type M-x online, and have emacs take care of the business of actually connecting my SSH tunnels firing up email and IRC clients and so on.

The actual online-offline functions need a clean (factoring out common bits, and so on), but they work.

(defun gds/online-home ()
  "Do the stuff I like to do when I just got online."
  (interactive)
  (save-some-buffers)
  (async-shell-command "~/bin/pipes.sh" "*pipes output*")    ;For some reason, it locks up without & ?
  (org-save-all-org-buffers)
  (gds/git-org-sync)
  (gds/ss-text-update)
  (org-revert-all-org-buffers)
  (gds/ircnet2)
  (jabber-connect-all)
  (jabber-send-presence "" "mucking about" 900)
  (erc-netsplit-mode -1)
  (gnus)
  )

(defun gds/online-almaz ()
  "Do the stuff I like to do when I just got online."
  (interactive)
  (save-some-buffers)
  (org-save-all-org-buffers)
  (gds/git-org-sync)
  (org-revert-all-org-buffers)
  (gds/ircnet6)
  (gds/ircnet7)
  (erc-netsplit-mode -1)
  (org-mobile-pull)
  (gnus)
)

(defun gds/offline ()
  "Do the stuff I like to do just before going offline."
  (interactive)
  (gnus-group-exit)  
  (org-save-all-org-buffers)
  (save-some-buffers)
  (jabber-disconnect)
  (gds/quit-irc-buffers)
  (gds/kill-online-buffers)
  (gds/git-org-sync)
  (org-revert-all-org-buffers)
  (if gds/online-at-work
      (progn 
        (setq gds/online-at-work nil)
        (gds/irc-log-msg "logged off from work"))))

;; Are we online and at work?
(setq gds/online-at-work nil)

Sometimes this generates a fair bit of info that need to be glanced at, and then ignored. I have a mode for this.

(defun gds/clean-and-close ()
  "Clean online status buffers, leave net mode, kill frame."
  (interactive)
  (gds/clean-online-status-buffers)
  (gds/net-mode -1)
  (delete-frame))

(define-minor-mode gds/net-mode
  "A global minor mode for managing my net connection.

This mode provides keybindings for going online at home or at
  work."
  nil                                   ; Not on by default
  " net"                                ; modeline indicator
  '(([?\C-c ?h] . gds/online-home)      ; keymap
    ([?\C-c ?w] . gds/online-work)
    ([?\C-c ?f] . gds/offline)
    ([?\C-c ?s] . gds/show-online-status-buffers)
    ([?\C-c ?c] . gds/clean-online-status-buffers)
    ([?\C-c ?k] . gds/clean-and-close))
  :group 'gdsnet
  :global t)

These functions help show me this ephermeral information, then clean it up when I'm satisfied nothing horrible happened.

(defun gds/online-status-temp-buffers ()
  "Get a list of temporary online-status buffers.

The sort of things I want to look at after going online, then kill."
  (append
          (-filter (lambda (buf) (search "SERVER" (buffer-name buf))) (buffer-list))
          (list "*pipes output*" "*git org-output*" "*news update output*")))

(defun gds/get-online-status-buffers ()
  "Get a list of buffer names that I might want to look at after going online."
  (append (gds/online-status-temp-buffers)
          (-filter (lambda (buf) (search "example.com" (buffer-name buf))) (buffer-list))
          (-filter (lambda (buf) (search "example.com" (buffer-name buf))) (buffer-list))
          (-filter (lambda (buf) (search "example.com" (buffer-name buf))) (buffer-list))))

(defun gds/show-online-status-buffers ()
  "Show the buffers I like to check after going online."
  (interactive)
  (delete-other-windows)
  (let* ((buffs (gds/get-online-status-buffers))
        (newsize (/ (window-height) (length buffs))))
    (switch-to-buffer (car buffs))
    (cl-loop for buf in (cdr buffs) do
             (split-window nil newsize)
             (switch-to-buffer buf)
             (other-window 1))))

(defun gds/clean-online-status-buffers ()
  "Kill the temporary buffers that set up the pipes, etc."
  (interactive)
  (cl-loop for buf in (gds/online-status-temp-buffers) do
           (kill-buffer buf)))

These are useful for killing all my network tentacles when I want to go offline

;; Neat trick, stolen from http://stackoverflow.com/a/5098163
;; Ported from cl to cl-lib for emacs 24.3
(defun gds/is-online-buffer? (buffer)
  "Is the buffer one that stops making sense if I lose network?"
  (let ((name (buffer-name buffer)))
    (or (string-prefix-p "example.com:6667" name)
        (string-prefix-p "example.com:6667" name)
        (string-prefix-p "example.com" name)
        (string-prefix-p "example.com" name)
        (string-prefix-p "example.com" name)
        (string-prefix-p "*tramp/ssh" name))))

(defun gds/kill-online-buffers ()
  "Kill buffers that stop making sense if I lose network."
  (interactive)
  (cl-loop for buffer being the buffers
           do (and (gds/is-online-buffer? buffer) (kill-buffer buffer))))

(defun gds/quit-irc-buffers ()
  "Kill buffers that stop making sense if I lose network."
  (interactive)
  (cl-loop for buffer being the buffers
           do (and (gds/is-online-buffer? buffer) (progn (switch-to-buffer buffer)
                                                         (erc-quit-server "Going offline")))))

For syncing the things that need syncing with git.

(defun gds/git-org-sync ()
  "Sync my org files using git"
  (interactive)
  (shell-command "~/bin/org-git-sync &" "*git org-output*"))

I like to have xscreensaver set to phosphor mode, reading recent news from a text file in my home directory. This command updates that text file.

(defun gds/ss-text-update ()
  "Update my screensaver news file."
  (interactive)
  (shell-command "~/bin/getSSNews &" "*news update output*"))

This function just calls the following shell script:

#!/usr/bin/env bash
curl http://planet.emacsen.org > /tmp/gdsSSdump && (cat /tmp/gdsSSdump | w3m -T text/html -cols 74 -O ASCII -dump) > ~/.SSnews.txt

For a while, I had unreliable wifi at work. I used these functions to keep logs of whenever the wifi caused my IRC connections to die.

(defun gds/irc-log-msg (msg)
  "Append a dated record to my IRC log.

This will write MSG into the log file /path/to/org/ircbounce.org
UNSAVED CHANGES WILL BE LOST

This function will save any changes it makes. If you interact
with the log file only though this function, you should be ok"
  (save-current-buffer
    (find-file "/path/to/org/ircbounce.org")
    (save-excursion
      (revert-buffer nil t)
      (end-of-buffer)
      (unless (= 0 (current-column)) (insert "\n"))
      (insert "* [" (time-stamp-string "%:y-%02m-%02d %3a %02H:%02M:%02S")"] " msg)
      (save-buffer))))

(defun gds/log-wifi-bounce ()
  "Keep a log of having been dropped from the wifi.

The log is kept in /path/to/org/ircbounce.org"
  (interactive)
  (gds/irc-log-msg "Bounce"))

IRC Things

I run most of my IRC sessions through SSH pipes. If the connection dies, blindly reconnecting won't work, and will slow emacs to a crawl.

(use-package erc
  ;; This syntax should be allowed soon.
  ;; :bind (:map erc-mode-map
  ;;             ("M-TAB" . flyspell-auto-correct-word))
  :defer 5
  :config
  (setq erc-server-auto-reconnect nil)
  (add-to-list 'erc-modules 'services)
  (add-to-list 'erc-modules 'spelling)
  (erc-update-modules)
  ;; Since we get passwords from authinfo, we don't need to ask for it.
  (setq erc-prompt-for-nickserv-password nil)
  (define-key erc-mode-map (kbd "M-TAB") 'flyspell-auto-correct-word)
  (setq erc-autojoin-channels-alist '(("example.com" "#channel" "&bitlbee")
                                      ("example.com" )
                                      ("example.com" "#channel" "#channel" "#channel")
                                      ("example.com" "#channel" "#channel")
                                      ("example.com" "#channel"))))

TODO Think about Filtering

This chap has a neat solution to his overwhelming ERC issues. Can I steal any of it to get what I want? What do I want? :S

Networks I often connect to

(defun gds/ircnet1 ()
  (interactive)
  (erc :server "example.com"
       :port 6665
       :nick "name"))

(defun gds/ircnet2 ()
  (interactive)
  (erc-tls :server "example.com"
           :port 6697
           :nick "name"))

(defun gds/bitlbee ()
  "Connect to local bitlbee server."
  (interactive)
  ;; Set nickserv password from authinfo for BitlBee.
  (setq erc-nickserv-passwords
        `((BitlBee (("name" . ,(gds/get-irc-password "BitlBee" "BitlBee"))))))
  ;; Actually connect
  (erc :server "example.com"
       :port 6667
       :nick "name"
       :full-name "My Name"))

(defun gds/ircnet3 ()
  "Connect to ircnet3 over local ssh pipe."
  (interactive)
  (erc :server "example.com"
       :port 6667
       :nick "name"
       ;; Get server password from authinfo
       :password (gds/get-irc-password "ircnet3" "ircnet3")
       :full-name "My Name"))

(defun gds/ircnet4 ()
  "Connect to ircnet4 over local ssh pipe."
  (interactive)
  (erc :server "example.com"
       :port 6667
       :nick "name"
       ;; Get server password from authinfo
       :password (gds/get-irc-password "ircnet4" "ircnet4")))

(defun gds/ircnet5 ()
  "Connect to ircnet5 over local ssh pipe."
  (interactive)
  (erc :server "example.com"
       :port 6667
       :nick "name"
       ;; Get server password from authinfo
       :password (gds/get-irc-password "ircnet5" "ircnet5")))

(defun gds/ircnet6 ()
  "Connect to ircnet6."
  (interactive)
  (erc :server "example.com" ;; "example.com"
       :port 6667
       :nick "name"
       :password (gds/get-irc-password "ircnet6" "ircnet6")
       :full-name "My Name"))

(defun gds/myirc ()
  "Connect to various IRC networks."
  (interactive)
  (gds/ircnet3)
  (gds/ircnet4)
  (gds/ircnet5)
  (gds/bitlbee)
  (gds/ircnet7))

(defun gds/ircnet7 ()
  "Connect to ircnet7 network."
  (interactive)
  (erc :server "example.com"
       :nick "name"
       :password (gds/get-irc-password "ircnet7" "name")
       :full-name "My Name"))
(defun gds/ircnet7_gravel ()
  "Connect to ircnet7 using a slightly different server."
  (interactive)
  (setq erc-autojoin-channels-alist (append erc-autojoin-channels-alist '(("example.com" "#channel"))))
  (erc :server "example.com"
       :nick "name"
       :password (gds/get-irc-password "ircnet7" "name")
       :full-name "My Name"))

Manage IRC passwords

(defun gds/get-irc-password (host login)
  "Get an IRC password from authinfo.

Look up the password associated with server HOST and username LOGIN."
  (let* ((found (auth-source-search :host host :login login))
         (secret (plist-get (nth 0 found) :secret)))
    (if (functionp secret)
        (funcall secret)
      secret)))

Jabber Things

(add-to-list 'Info-default-directory-list "~/elisp/jabber/")
(use-package jabber-autoloads
  :load-path "~/elisp/emacs-jabber-0.8.92/"
  :defer
  :init
  (unbind-key "C-x C-j")
  :commands jabber-connect-all
  :bind (("C-x C-j C-c" . jabber-connect-all))
  :config
  (add-hook 'jabber-chat-mode-hook 'flyspell-mode)
  (setq jabber-alert-presence-message-function 'jabber-presence-only-chat-open-message)
  (setq jabber-account-list `(("totherme@gmail.com"
                               (:network-server . "talk.google.com")
                               (:connection-type . ssl)
                               (:password . ,(gds/get-irc-password "google" "totherme"))))))

Twitter Things

(use-package twittering-mode
  :commands twit
  :load-path "~/elisp/twittering-mode")

Meedja Things

EMMS

Play music from within emacs.

(add-to-list 'Info-default-directory-list "~/elisp/emms-4.0/doc/")
(use-package emms
  :unless (string-prefix-p "example.com" (system-name))
  :load-path "~/elisp/emms-4.0/lisp/"
  :config
  ;; (emms-standard)
  ;; (emms-default-players)
  )
  • TODO fix emms

    my setup worked with an old version, and doesn't seem to work any more.

TODO Set up youtube-playing

Follow the direction of this blog and use mps-youtube.

Proced

Like top or htop, but in emacs.

(bind-key "C-x p" 'proced)

Shell Stuff

I actually mostly use dired as my shell these days, but sometimes you just want to have a terminal. And sometimes you want that terminal to be in emacs. Eshell is more tightly integrated, but if you want actual bash (and still in emacs), you'll need the regular shell.


Bash stuff

Make sure bash starts with the args I like.

(use-package shell
  :defer 5
  :bind ("C-x M-m" . shell)
  :config
  (setq explicit-bash-args '("--noediting" "-i" "-l")))

Eshell stuff

Most of the settings I liked best are taken from Mastering Eshell.

First, basic settings. Keep the cursor above the executing command, but if I start typing a new command, then take me to the end.

(use-package eshell
  :defer 5
  :bind ("C-x m" . eshell)
  :config
  (use-package em-smart)
  (setq eshell-where-to-jump 'begin)
  (setq eshell-review-quick-commands nil)
  (setq eshell-smart-space-goes-to-end t)
  ;; (add-to-list 'eshell-visual-commands "opam --help")
  (add-hook 'eshell-mode-hook 'eshell-smart-initialize))

My xmonad is set to use a fresh eshell as the default terminal. Often I want to just cd to some place, open some file and forget about the eshell. To do that, alias gds/find-file-instead-eshell as fi in the eshell, and let it kill the temporary eshell for you.

(defun gds/find-file-instead (filename)
  "Like `find-file', but opens FILENAME /instead/ of the current one.

Kills the current buffer, and leaves you with the one you asked for."
  (interactive)
  (let ((buf (current-buffer)))
    (find-file filename)
    (kill-buffer buf)
    ))

(defun gds/find-file-instead-eshell (filename)
  "Opens FILENAME like `gds/find-file-instead', but works in eshell.

This function mustn't actually kill the eshell buffer, or the
eshell alias will print the return value to whatever buffer you
just opened."
  (interactive)
  (let ((buf (current-buffer)))
    (find-file filename)
    (switch-to-buffer buf)
    (end-of-buffer)
    (insert "exit")))

(defun gds/exit-eshell-close-frame ()
  "Exits the current eshell, then closes this frame."
  (execute-kbd-macro (kbd "exit
"))
  (delete-frame))

(defun gds/really-kill-this-buffer ()
  "Like `kill-this-buffer`, but ignoring read-only constraints.

Particularly useful when eshell messes up."
  (interactive)
  (let ((inhibit-read-only t)) (kill-this-buffer)))

Authentication Stuff

Emacs can keep your passwords and things in a gpged file, and load them into memory when it needs to access something remote. You can use this to make tramp remote file accesses more seemless, but you're probably better off using ssh keys. I use it for IRC passwords.

(setq auth-sources '("~/.authinfo.gpg" "~/.authinfo" "~/.netrc"))

Make my encryption strong by default

This is more secure, and also prevents the warnings I was getting that say:

gnutls.c: Note that the security level of the Diffie-Hellman key exchange has been lowered to 256 bits and this may allow decryption of the session data

(setq gnutls-min-prime-bits 1024)

Jabber

I'm currently using bitlbee instead. You can get jabber mode from here.

(use-package emacs-jabber
  :load-path "~/elisp/emacs-jabber"
  :disabled
  :defer 5)

Org stuff

This almost all now lives in its own file.

(message "About to load org bits")
(org-babel-load-file "/path/to/org-config.org")
(message "loaded org-bits")

…except for:

Gnorb Stuff

Gnorb is glue that sticks org-mode, gnus, and BBDB together. It means I can schedule tasks from my email client (which is often where I hear about them), see which emails are associated with a given task, and seemlessly reply to an email when I've completed a task.

Now that I've been using this stuff for a while, it amazes me that gmail and google calendar don't seem to do this yet.

(add-to-list 'Info-default-directory-list "~/elisp/gnorb/")
(use-package gnorb
  :load-path "~/elisp/gnorb"
  :defer 5
  :init
  :config
  (eval-after-load "gnorb-bbdb"
    '(progn
       (define-key bbdb-mode-map (kbd "O") 'gnorb-bbdb-tag-agenda)
       (define-key bbdb-mode-map (kbd "S") 'gnorb-bbdb-mail-search)
       (define-key bbdb-mode-map [remap bbdb-mail] 'gnorb-bbdb-mail)
       (define-key bbdb-mode-map (kbd "l") 'gnorb-bbdb-open-link)
       (global-set-key (kbd "C-c C") 'gnorb-bbdb-cite-contact)))

  (eval-after-load "gnorb-org"
    '(progn
       (org-defkey org-mode-map (kbd "C-c C") 'gnorb-org-contact-link)
       (org-defkey org-mode-map (kbd "C-c t") 'gnorb-org-handle-mail)
       (org-defkey org-mode-map (kbd "C-c e") 'gnorb-org-view)
       (org-defkey org-mode-map (kbd "C-c E") 'gnorb-org-email-subtree)
       (org-defkey org-mode-map (kbd "C-c V") 'gnorb-org-popup-bbdb)
       (setq gnorb-org-agenda-popup-bbdb t)
       (eval-after-load "org-agenda"
         '(progn (org-defkey org-agenda-mode-map (kbd "H") 'gnorb-org-handle-mail)
                 (org-defkey org-agenda-mode-map (kbd "V") 'gnorb-org-popup-bbdb)))))

  (eval-after-load "gnorb-gnus"
    '(progn
       (define-key gnus-summary-mime-map "a" 'gnorb-gnus-article-org-attach)
       (define-key gnus-summary-mode-map (kbd "C-c t") 'gnorb-gnus-incoming-do-todo)
       (push '("attach to org heading" . gnorb-gnus-mime-org-attach)
             gnus-mime-action-alist)
       ;; The only way to add mime button command keys is by redefining
       ;; gnus-mime-button-map, possibly not ideal. Ideal would be a
       ;; setter function in gnus itself.
       (push '(gnorb-gnus-mime-org-attach "a" "Attach to Org heading")
             gnus-mime-button-commands)
       (setq gnus-mime-button-map
             (let ((map (make-sparse-keymap)))
               (define-key map gnus-mouse-2 'gnus-article-push-button)
               (define-key map gnus-down-mouse-3 'gnus-mime-button-menu)
               (dolist (c gnus-mime-button-commands)
                 (define-key map (cadr c) (car c)))
               map))))

  (eval-after-load "message"
    '(progn
       (define-key message-mode-map (kbd "C-c t") 'gnorb-gnus-outgoing-do-todo)))

  (setq gnorb-gnus-new-todo-capture-key "t"))

…and finally, we start the server

(server-start)
(message "Finished main config")

Unused

Various things that were handy once, but which I'm not currently using. This is a good place to look if you want to read things that seemed like a good idea at the time, but which can be made much better/neater.

Smart Mode Line

(setq sml/theme 'dark)
(setq sml/mode-width 10)                ; Was 30
(setq sml/name-width 15)                ; Was 44
(require 'smart-mode-line)
(sml/setup)

(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") nil) ; front of list
(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") t)    ; back of list
(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") t)
(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") t)
(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") t)
(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") t)
(add-to-list 'sml/replacer-regexp-list '("path\\([^/]+\\)/" "abbrev(\\1):") t)
(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") t)
(add-to-list 'sml/replacer-regexp-list '("path" "abbrev") t)

Emacs Live

Emacs Live is cool and you should check it out. It's what Meta Ex use at their their live-coding gigs. For a while, I imported some bits of it wholesale into my config.

(load-file "/path/to/my/emacslive/hack")

Some keys nicked from live
(global-set-key (kbd "C-c j p") 'quick-jump-go-back)
(global-set-key (kbd "C-c j b") 'quick-jump-go-back)
(global-set-key (kbd "C-c j m") 'quick-jump-push-marker)
(global-set-key (kbd "C-c j n") 'quick-jump-go-forward)
(global-set-key (kbd "C-c j f") 'quick-jump-go-forward)
(global-set-key (kbd "C-c j c") 'quick-jump-clear-all-marker)
(define-key nrepl-mode-map (kbd "M-RET") 'nrepl-doc)
(define-key nrepl-interaction-mode-map (kbd "M-RET") 'nrepl-doc)
(global-unset-key (kbd "C-<"))
(global-unset-key (kbd "C->"))
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)

Browsing URLs.

If the default browser doesn't work, you can set it manually. At the moment all my machines get this right without help.

(setq browse-url-browser-function 'browse-url-firefox)

Darcsum

A mode for using darcs from emacs. Actually, darcsum may not be worth it atm… Darcs has a good command-line interface, and darcsum seems to me to not have beaten it yet.

(require 'darcsum)

Mouse messing

These days I use xmonad to move the cursor out of the way.

(mouse-avoidance-mode 'none)

Making prelude sensible

When using bbatsov's Prelude (I'm currently not – but this config should co-exist with it fine) it's a good idea to turn off "guru mode". That way you can use arrow keys.

(setq prelude-guru nil)

Jabber

I mostly use bitlbee atm, because it seems to deal better with the weirdnesses of facebook's jabber implementation.

(add-hook 'jabber-chat-mode-hook 'flyspell-mode)
(setq jabber-account-list '(("totherme@gmail.com"
                          (:network-server . "talk.google.com")
                          (:connection-type . ssl))))

Find file as root

;; Find file as root from http://emacs-fu.blogspot.co.uk/2013/03/editing-with-root-privileges-once-more.html
;; Prelude now seems to do this by default.
(defun gds/djcb-find-file-as-root ()
  "Like `ido-find-file, but automatically edit the file with
 root-privileges (using tramp/sudo), if the file is not writable by
 user."
  (interactive)
  (let ((file (ido-read-file-name "Edit as root: ")))
    (unless (file-writable-p file)
      (setq file (concat "/sudo:root@example.com:" file)))
    (find-file file)))
;; or some other keybinding...
(global-set-key (kbd "C-x F") 'gds/djcb-find-file-as-root)

Codings

There was a time when I needed to poke things to make UTF work.

(prefer-coding-system 'utf-8)

Fixing prelude

;; Prelude keys that get on my nerves
(if (boundp 'prelude-mode-map)
    (progn
      (define-key prelude-mode-map (kbd "C-a") nil)
      (global-set-key (kbd "C-a") 'beginning-of-line)
      (define-key prelude-mode-map (kbd "M-o") nil)
      (define-key prelude-mode-map (kbd "s-o") nil)
      (global-set-key (kbd "s-o") (lambda () (interactive) (other-window -1)))
      (define-key prelude-mode-map (kbd "C-c k") nil)))

highlight current line

Sometimes handy, but mostly just annoying :/

(global-hl-line-mode 0)

God Mode

(require 'god-mode)
(god-mode)
(global-set-key (kbd "<escape>") (lambda () (interactive) (god-local-mode 1)))
(global-set-key (kbd "<f1>") (lambda () (interactive) (god-local-mode 1)))
;(global-set-key (kbd "<home>") (lambda () (interactive) (god-local-mode 1))) ;; In non-x ecape doesn't work. In X I don't have a home key.I do now.
(defun my-update-cursor ()
  (setq cursor-type (if (or god-local-mode buffer-read-only)
                        'box
                      'bar)))

(add-hook 'god-mode-enabled-hook 'my-update-cursor)
(add-hook 'god-mode-disabled-hook 'my-update-cursor)
(define-key god-local-mode-map (kbd "i") 'god-local-mode)
(define-key god-local-mode-map (kbd ".") 'repeat)

Ed

Sometimes you just want the zen of the standard text editor.

(require 'ed)

Magit

;; full screen magit-status
(defadvice magit-status (around magit-fullscreen activate)
  (window-configuration-to-register :magit-fullscreen)
  ad-do-it
  (delete-other-windows))

(defun gds/magit-quit-session ()
  "Restores the previous window configuration and kills the magit buffer"
  (interactive)
  (kill-buffer)
  (jump-to-register :magit-fullscreen))
(define-key magit-status-mode-map (kbd "q") 'gds/magit-quit-session)

JavaScript Stuff

(autoload 'moz-minor-mode "moz" "Mozilla Minor and Inferior Mozilla Modes" t)

(add-hook 'javascript-mode-hook 'javascript-custom-setup)
(defun javascript-custom-setup ()
  (moz-minor-mode 1))

w3m stuff

Mostly obsolete now that we have eww – but I still sometimes use older machines.

(defun gds/w3m-rename-buffer (url)
  "base buffer name on title"
  (let* ((size 32)
         (title w3m-current-title)
         (name (truncate-string-to-width
                (replace-regexp-in-string " " "_" title)
                size)))
    (rename-buffer name t)))
;;(add-hook 'w3m-display-hook 'gds/w3m-rename-buffer)
;; (defadvice w3m-modeline-title (around my-w3m-modeline-title)
;;   "prevent original function from running; cleanup remnants"
;;   (setq w3m-modeline-separator ""
;;         w3m-modeline-title-string ""))
;; (ad-activate 'w3m-modeline-title)

Window management

(defun gds/split-windows-with-function (splitter)
  "Switch this two-window layout to the one specified by SPLITTER.

Assuming the current frame has two windows in it, close one, and
re-split using the window-splitting function SPLITTER."
  (let ((bname (buffer-name)))
    (delete-window)
    (funcall splitter)
    (switch-to-buffer bname)))

(defun gds/rotate-window-arrangement ()
  "Toggle windows between a horizontal split and a vertical split.

Seems to work, so long as you only have two windows open."
  (interactive)
  (if (eq (window-width) (frame-width))
      ;; We have a horizontal split
      (gds/split-windows-with-function 'split-window-right)
    ;; We have a vertical split
    (gds/split-windows-with-function 'split-window-below)))

(global-set-key (kbd "C-c ~") 'gds/rotate-window-arrangement)

;; Faster bindings for common window commands
(defun gds/previous-window ()
  "Like other-window, only backwards."
  (interactive)
  (other-window -1))

(global-set-key (kbd "M-1") 'digit-argument)
(global-set-key (kbd "M-2") 'digit-argument)
(global-set-key (kbd "M-3") 'digit-argument)
(global-set-key (kbd "M-0") 'digit-argument)
(global-set-key (kbd "M-o") 'other-window)
(global-set-key (kbd "s-o") 'gds/previous-window)
(global-set-key (kbd "M-O") 'gds/previous-window)
(global-set-key (kbd "C-x C-1") 'delete-other-windows)
(global-set-key (kbd "C-x C-2") 'split-window-below)
(global-set-key (kbd "C-x C-3") 'split-window-right)
(global-set-key (kbd "C-x C-0") 'delete-window)

(defun gds/switch-to-previous-buffer ()
  "Switch to previously open buffer.
Repeated invocations toggle between the two most recently open buffers.
Nicked with thanks from the emacs prelude."
  (interactive)
  (switch-to-buffer (other-buffer (current-buffer) 1)))

Some personal global key chords

There's less point to these while I'm using evil-mode

Key chords are awesome anti-RSI tricks. Choosing keychords that won't get in the way is hard. Here I have a list of current and previous chords, to remind me what not to pick, and why.

(key-chord-define-global "vv" 'nil) ; savvy
(key-chord-define-global "gg" nil) ; Goggles
(key-chord-define-global "qq" nil) ; \qquad
(key-chord-define-global "]]" 'forward-paragraph)
(key-chord-define-global "[[" 'backward-paragraph)
(key-chord-define-global "oo" nil) ; Cool
(key-chord-define-global "ss" nil) ; Guess
(key-chord-define-global "rr" nil) ; Horrible
(key-chord-define-global "bb" nil) ; Wobble.
(key-chord-define-global "ff" nil) ; Off
(key-chord-define-global "nn" nil) ; beginning, dinner
(key-chord-define-global "pp" nil) ; uppers
(key-chord-define-global "ww" nil)      ;www...
(key-chord-define-global "kk" 'nil) ; nekkid
(key-chord-define-global "yy" 'browse-kill-ring)
(key-chord-define-global "qw" 'kill-ring-save)
(key-chord-define-global "//" nil)      ; http://...
(key-chord-define-global "uu" nil) ; vacuum.
(key-chord-define-global "xf" nil)      ;Oxford
(key-chord-define-global "xs" nil)      ; haskell
(key-chord-define-global "dd" nil) ; Address
(key-chord-define-global "xo" 'nil) ; firefox.
(key-chord-define-global "xg" nil) ; gets in the way of god mode
(key-chord-define-global "xb" 'helm-mini)
(key-chord-define-global "JJ" (lambda () (interactive) (message "NOPE!"))) ; Gnus toggle plugged
(key-chord-define-global "jj" nil) ; gets in the way of god mode
(global-set-key (kbd "C-0") 'ace-jump-mode)
(key-chord-define-global "jl" 'ace-jump-line-mode)
(key-chord-define-global "jk" 'ace-jump-char-mode)
(key-chord-mode 0)

DONE Ditch key-chord for evil.

Instead of xb for helm-mini, use space-space in normal mode, as recommended here. Find some vimish replacement for ace-jump.

Diminish

Irritatingly, we can only diminish things that have been loaded ;)

;; Diminish modeline clutter
(require 'diminish)
(require 'paredit)
(unless (string-prefix-p "example.com" (system-name))
  (require 'rainbow-mode))
;; (require 'ruby-block)
(require 'skewer-mode)
(require 'git-gutter)
(require 'projectile)
(require 'whitespace)
(require 'volatile-highlights)
(require 'flyspell)
(require 'abbrev)
(require 'org)
;; (require 'ruby-block)
(require 'js2-mode)

Some things we want to diminish away completely. Others we want to rename.

;; stolen from http://whattheemacsd.com//appearance.el-01.html
(defmacro rename-modeline (package-name mode new-name)
  `(eval-after-load ,package-name
     '(defadvice ,mode (after rename-modeline activate)
        (setq mode-name ,new-name))))

(defun gds/cleanup ()
  "Diminish all the clutter in my modeline."
  (interactive)
  ;; (diminish 'wrap-region-mode)
  (size-indication-mode 0)
  (setq display-time-mail-file t)
  (setq display-time-load-average-threshold 4.0)
  (diminish 'yas-minor-mode)
  (diminish 'paredit-mode)
  (unless (string-prefix-p "example.com" (system-name))
    (diminish 'rainbow-mode))
  (diminish 'projectile-mode)
  (diminish 'whitespace-mode)
  ;; (diminish 'guru-mode)
  (diminish 'eldoc-mode)
  (diminish 'volatile-highlights-mode)
  (if (boundp 'prelude-mode) (diminish 'prelude-mode))
  (diminish 'flyspell-mode)
  (diminish 'git-gutter-mode)
  ;; (diminish 'auto-complete-mode)
  (diminish 'abbrev-mode)
  (diminish 'orgstruct-mode)
  (diminish 'auto-fill-function)
  ;; (diminish 'ruby-block-mode)
  (diminish 'skewer-mode)
  (diminish 'undo-tree-mode)
  (rename-modeline "lisp-mode" emacs-lisp-mode "EL")
  (rename-modeline "tex-mode" LaTeX-mode "Lt")
  (rename-modeline "js2-mode" js2-mode "JS2")
  (rename-modeline "clojure-mode" clojure-mode "Clj")
  (org-expiry-deinsinuate))
(gds/cleanup)

TODO use use-package instead of calling diminish directly

Author: Gareth Smith

Created: 2015-08-29 Sat 22:18

Validate