Michael's Emacs Config

1 Preamble

This is my Emacs config, which is almost a single file thanks to org-babel.

Although GitHub does render .org files nicely, I recommend viewing it in Emacs, as GitHub isn't fully compatible. Using org-mode gives a nice overview and makes it quick and easy to navigate and make changes.

2 Beginning config

2.1 Personal information

(setq user-full-name "Michael Englehorn")
(setq user-mail-address "michael@englehorn.com")

3 Package related

Setup general package related settings.

3.1 package.el

Initialize package.el, the built-in package manager.

(package-initialize)

These are the package repositories I use:

(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))

3.2 use-package

Install/activate use-package, basically a wrapper for package.el.

  • makes installing and configuring other packages nicer and easier.
  • helps with autoloading packages to greatly reduce initial startup time.
    (unless (package-installed-p 'use-package)
        (package-refresh-contents)
        (package-install 'use-package))
    (require 'use-package)
    (setq use-package-always-ensure t)
    

3.3 Quelpa

(unless (package-installed-p 'quelpa)
  (with-temp-buffer
    (url-insert-file-contents "https://raw.githubusercontent.com/quelpa/quelpa/master/quelpa.el")
    (eval-buffer)
    (quelpa-self-upgrade)))

;; Install and load `quelpa-use-package'.
(quelpa
 '(quelpa-use-package
   :fetcher git
   :url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package)

3.4 Emacs String Manipulation Library

This is required for some plugins.

(use-package s)

4 Appearance related

Setup appearance early so that if something goes wrong with the init, Emacs is still looking good.

4.1 Maximize Emacs on startup

This snippet works in *nix and all (relevant) versions of Windows.

(if (eq system-type 'windows-nt)
    (with-no-warnings
      (defun w32-maximize-frame ()
        "Maximize the current frame (windows only)"
        (interactive)
        (w32-send-sys-command 61488))
      (add-hook 'windows-setup-hook 'w32-maximize-frame t))
  (set-frame-parameter nil 'fullscreen 'maximized))

4.2 Minimal GUI

Remove unnecessary GUI elements: menu-bar, tool-bar, and scroll-bar.

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

4.3 No splash screen

I prefer loading up to the scratch buffer on initial startup.

(setq inhibit-splash-screen t)

4.4 Initial scratch buffer message

(setq initial-scratch-message "")

4.5 Theme

4.5.1 Hook make-frame to detect if we're in a TTY or GUI

(load-theme 'wheatgrass t)
(enable-theme 'wheatgrass)

4.6 RainbowDelimiters

Highlight matching delimiters with unique colors. It's virtually necessary with emacs-lisp.

(use-package rainbow-delimiters
:commands (rainbow-delimiters-mode)
:init
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode))

4.7 Highlight matching parenthesis

Specifically under the cursor

(defvar show-paren-delay)
(setq show-paren-delay 0.0)
(show-paren-mode t)

4.8 Highlight trailing whitespace

Trailing whitespace is really annoying, especially when pasting from certain terminals.

(defun my/buf-show-trailing-whitespace ()
  (interactive)
    (setq show-trailing-whitespace t))
(add-hook 'prog-mode-hook 'my/buf-show-trailing-whitespace)
(custom-set-faces '(trailing-whitespace ((t (:background "dim gray")))))

4.9 Prettify symbols

Convert certain words into symbols. Prime example: lambda becomes λ.

(global-prettify-symbols-mode)

4.10 Show column number in modeline

(column-number-mode t)

4.11 Prettify source code in org-mode

Org mode should have pretty highlighting for source code.

(setq org-src-fontify-natively t)
(setq org-src-tab-acts-natively t)
(setq org-edit-src-content-indentation 0)
(use-package htmlize)

5 General settings

These settings don't belong to any specific mode or package. Some packages are exceptional for being very simple.

5.1 Optimize org-babel config for faster startup

Running org-babel-load-file increases startup time, so only do it when there are changes to update. Only run it when config.el is missing, and delete config.el when config.org changes. (see init.el)

(require 's)
(defun my/delete-config-el ()
    "Delete ~/.emacs.d/config.el when the current buffer is ~/.emacs.d/config.org"
    (defvar my-configel)
    (setq my-configel "~/git/menglehorn-linux-dotfiles/configs/emacs/config.el")
    (if (s-suffix? "emacs/config.org" buffer-file-name)
        (if (file-exists-p my-configel)
            (progn (delete-file "~/git/menglehorn-linux-dotfiles/configs/emacs/config.el")
                   (delete-file "~/git/menglehorn-linux-dotfiles/configs/emacs/config.elc")))))

(add-hook 'after-save-hook 'my/delete-config-el)

5.2 Set up org-babel

We need to allow SH scripts to be executed by org-babel.

;(require 'ob-sh)

5.3 Start server if it isn't started

Turn the first Emacs process into a server, which allows reuse of a that process through the shell command emacsclient.

(require 'server)
(if (not (server-running-p)) (server-start))

5.4 Backup & auto-save files in one place

These files are useful if something goes wrong, but they're also annoying in how they clutter directories. Put them in ~/.emacs.d/tmp to remedy this.

(setq backup-directory-alist `((".*" . "~/.emacs.d/tmp"))
        auto-save-file-name-transforms `((".*" , "~/.emacs.d/tmp" t)))

5.5 Scroll smoothly

(setq scroll-margin 0)
(setq scroll-conservatively 10000)
(setq scroll-preserve-screen-position t)

5.6 Sentences end with a single period

(setq sentence-end-double-space nil)

5.7 y/n instead of yes/no

(fset 'yes-or-no-p 'y-or-n-p)

5.8 Wrap text at 80 characters

(setq-default fill-column 80)

5.9 Auto-detect indent settings

I prefer to follow a file's indenting style instead of enforcing my own, if possible. dtrt-indent does this and works for most mainstream languages.

(use-package dtrt-indent)

5.10 Auto-update changed files

If a file is changed outside of Emacs, automatically load those changes.

(global-auto-revert-mode t)

5.11 Auto-executable scripts in *nix

When saving a file that starts with #!, make it executable.

(add-hook 'after-save-hook
        'executable-make-buffer-file-executable-if-script-p)

5.12 Enable HideShow in programming modes

Useful for getting an overview of the code. It works better in some languages and layouts than others.

(defun my/enable-hideshow ()
    (interactive)
    (hs-minor-mode t))
(add-hook 'prog-mode-hook 'my/enable-hideshow)

5.13 Recent Files

Enable recentf-mode and remember a lot of files.

(recentf-mode 1)
(defvar recentf-max-saved-items)
(setq recentf-max-saved-items 200)

5.14 Better same-name buffer distinction

When two buffers are open with the same name, this makes it easier to tell them apart.

(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)

5.15 Remember last position for reopened files

(if (version< emacs-version "25.0")
    (progn (require 'saveplace)
        (setq-default save-place t))
(save-place-mode 1))

5.16 Disable garbage collection in minibuffer

See this article for more info.

(defun my/minibuffer-setup-hook ()
(setq gc-cons-threshold most-positive-fixnum))
(defun my/minibuffer-exit-hook ()
(setq gc-cons-threshold 800000))
(add-hook 'minibuffer-setup-hook #'my/minibuffer-setup-hook)
(add-hook 'minibuffer-exit-hook #'my/minibuffer-exit-hook)

5.17 Configure default web browser

I use Chrome as my default browser.

(defvar browse-url-generic-program)
(if (eq system-type 'darwin)
    (setq browse-url-browser-function 'browse-url-generic
          browse-url-generic-program "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"))
(if (eq system-type 'gnu/linux)
    (setq browse-url-browser-function 'w3m-browse-url))

5.18 Use plink on windows

Windows doesn't have the ssh command.

(when (eq window-system 'w32)
  (setq tramp-default-method "plink"))

6 Install and Set Up packages

6.1 yaml-mode

(use-package yaml-mode
  :config
  (progn
    (require 'yaml-mode)
    (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))))

6.2 Ansible (ansible) minor mode

Install the Ansible minor mode

(use-package ansible)

6.3 BBDB

Install the Big Brother Database

(use-package bbdb
  :config
  (bbdb-initialize 'gnus 'message))
(require 'bbdb)

6.4 request.el

This is an HTTP library for emacs.

(use-package request)

6.5 git

An Elisp API for programmatically using Git.

(use-package git)

6.6 el-get

I use this to grab arbitrary lisp from github.

(use-package el-get
  :init
  (require 'el-get)
  (add-to-list 'load-path "~/.emacs.d/el-get"))

6.7 ERC IRC Client

IRC Client for Emacs

(use-package erc-colorize)
(use-package erc-crypt)
(use-package erc-hl-nicks)
(use-package erc-image)
;(use-package erc-social-graph)
(use-package erc-youtube)
;(require 'tls)
(make-variable-buffer-local 'erc-fill-column)
(add-hook 'window-configuration-change-hook 
          '(lambda ()
             (save-excursion
               (walk-windows
                (lambda (w)
                  (let ((buffer (window-buffer w)))
                    (set-buffer buffer)
                    (when (eq major-mode 'erc-mode)
                      (setq erc-fill-column (- (window-width w) 2)))))))))

6.8 Magit

Easy Git management

(use-package magit)
(use-package magit-popup)

6.9 Smex

Smart M-x for Emacs

(use-package smex
  :init
  (global-set-key (kbd "M-x") 'smex))

6.10 Git Commit Mode

Mode for Git Commits

(use-package git-commit)

6.11 JSON Formatter

Command to clean up and prettify json.

(use-package json-mode)

6.12 EMMS

Emacs Multimedia System

(use-package emms)
(use-package emms-info-mediainfo)

(require 'emms-setup)
(emms-all)
(emms-default-players)

;; After loaded
;(require 'emms-info-mediainfo)
;(add-to-list 'emms-info-functions 'emms-info-mediainfo)
(require 'emms-info-metaflac)
(add-to-list 'emms-info-functions 'emms-info-metaflac)

(require 'emms-player-simple)
(require 'emms-source-file)
(require 'emms-source-playlist)
(setq emms-source-file-default-directory "~/Music/")

6.13 w3m

Web browser for Emacs

(cond
 ((string-equal system-type "windows-nt")
  (progn
    ))
 ((string-equal system-type "gnu/linux")
  (progn
    (use-package w3m
      :ensure t
      :init
      (setenv "PATH" (concat (getenv "PATH") ":/usr/local/bin"))
      (setq exec-path (append exec-path '("/usr/local/bin")))
      (autoload 'w3m-browse-url "w3m")
      (global-set-key "\C-xm" 'browse-url-at-point)
      (setq w3m-use-cookies t)
      (setq w3m-default-display-inline-images t)))))

6.14 multi-term

Multiple terminal manager for Emacs

(use-package multi-term
  :init
  (setq multi-term-program "/bin/bash"))
(require 'multi-term)

6.15 web-mode

web-mode.el is an emacs major mode for editing web templates aka HTML files embedding parts (CSS/JavaScript) and blocks (pre rendered by client/server side engines).

(use-package web-mode
  :init
    (require 'web-mode)
    (add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.php\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . 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)))

6.16 exec-path-from-shell

This is used to grab envirtonment variables from the shell.

(use-package exec-path-from-shell)
(require 'exec-path-from-shell)

6.17 Company-mode

Company is a text completion framework for Emacs. The name stands for "complete anything". It uses pluggable back-ends and front-ends to retrieve and display completion candidates.

(use-package company
  :init
  (add-hook 'after-init-hook 'global-company-mode))

6.18 govc VMware Integration

A VMware integration script

(use-package govc)

6.19 helm-spotify-plus

I enjoy listening to spotify, bonus points for emacs integration.

(use-package helm-spotify-plus
  :init
  (global-set-key (kbd "C-c s s") 'helm-spotify-plus))

6.20 helm-bbdb

Using helm to access and use the BBDB is nice too.

(use-package helm-bbdb
  :init
  (global-set-key (kbd "C-c s b") 'helm-bbdb))

6.21 helm-pass

Pass is pretty nice password manager

(use-package helm-pass
  :init
  (global-set-key (kbd "C-c s p") 'helm-pass))

6.22 helm-emms

This package makes using emms super easy!

(use-package helm-emms
  :init
  (global-set-key (kbd "C-c s e") 'helm-emms))

6.23 vagrant

(use-package vagrant)

6.24 Golang

All the packages associated with golang.

(use-package go-mode)
(use-package go-stacktracer)
(use-package go-complete)
(use-package go-eldoc)

6.25 markdown-mode

Package for editing markdown.

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

6.26 blockdiag-mode

I need blockdiag-mode for ob-blockdiag

(use-package blockdiag-mode)

6.27 cmake-mode

Provides syntax highlighting and indentation for CMakeLists.txt and *.cmake source files.

(use-package cmake-mode)

6.28 Rust cargo

(use-package cargo-mode)

6.29 Rust rustic

(use-package rustic
  :config
  (setq rustic-format-on-save t)
  (setq rustic-lsp-server 'rls))

6.30 Matrix client

ement.el Github

;; Install `plz' HTTP library (not on MELPA yet).
(use-package plz
  :quelpa (plz :fetcher github :repo "alphapapa/plz.el"))

;; Install Ement.
(use-package ement
  :quelpa (ement :fetcher github :repo "alphapapa/ement.el"))

(setq ement-save-sessions 't)
(setq ement-sessions-file "~/.emacs.d/ement-sessions.el")

;(ement-connect :uri-prefix "http://localhost:8008")

7 Org-mode

7.1 Install org-mode

(use-package org)
(require 'org)

7.2 Todo Keywords

Here are my TODO states and color settings.

(setq org-todo-keywords
      (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
              (sequence "WAITING(W@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING" "EVENT"))))

(setq org-todo-keyword-faces
      (quote (("TODO" :foreground "red" :weight bold)
              ("NEXT" :foreground "blue" :weight bold)
              ("DONE" :foreground "forest green" :weight bold)
              ("WAITING" :foreground "orange" :weight bold)
              ("HOLD" :foreground "magenta" :weight bold)
              ("CANCELLED" :foreground "forest green" :weight bold)
              ("MEETING" :foreground "forest green" :weight bold)
              ("PHONE" :foreground "forest green" :weight bold)
              ("EVENT" :foreground "goldenrod" :weight bold))))

7.3 Todo Selection

(setq org-use-fast-todo-selection t)

Changing a task state is done with C-c C-t KEY

7.4 Todo State Triggers

Trigger breakdown

  • Moving a task to CANCELLED adds a CANCELLED tag
  • Moving a task to WAITING adds a WAITING tag
  • Moving a task to HOLD adds WAITING and HOLD tags
  • Moving a task to a done state removes WAITING and HOLD tags
  • Moving a task to TODO removes WAITING, CANCELLED, and HOLD tags
  • Moving a task to NEXT removes WAITING, CANCELLED, and HOLD tags
  • Moving a task to DONE removes WAITING, CANCELLED, and HOLD tags
(setq org-todo-state-tags-triggers
      (quote (("CANCELLED" ("CANCELLED" . t))
              ("WAITING" ("WAITING" . t))
              ("HOLD" ("WAITING") ("HOLD" . t))
              (done ("WAITING") ("HOLD"))
              ("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
              ("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
              ("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))

7.5 Remove empty LOGBOOK drawers

;; Remove empty LOGBOOK drawers on clock out
(defun bh/remove-empty-drawer-on-clock-out ()
  (interactive)
  (save-excursion
    (beginning-of-line 0)
    (org-remove-empty-drawer-at "LOGBOOK" (point))))

(add-hook 'org-clock-out-hook 'bh/remove-empty-drawer-on-clock-out 'append)

7.6 Refile captured

Any file in org-agenda-files are valid refile targets.

; Targets include any file contributing to the agenda - up to 9 levels deep
(setq org-refile-targets (quote ((org-agenda-files :maxlevel . 9))))

; Use full outline paths for refile targets - we file directly with IDO
(setq org-refile-use-outline-path t)

; Targets complete directly with IDO
(setq org-outline-path-complete-in-steps nil)

; Allow refile to create parent tasks with confirmation
(setq org-refile-allow-creating-parent-nodes (quote confirm))

;;;; Refile settings
; Exclude DONE state tasks from refile targets
(defun bh/verify-refile-target ()
  "Exclude todo keywords with a done state from refile targets"
  (not (member (nth 2 (org-heading-components)) org-done-keywords)))

(setq org-refile-target-verify-function 'bh/verify-refile-target)

7.7 Org-bullets

I don't see a reason why you wouldn't want this.

(use-package org-bullets
  :commands (org-bullets-mode)
  :init
    (setq org-bullets-bullet-list '("●"))
    (add-hook 'org-mode-hook 'org-bullets-mode))

7.8 Set up notes and todos

Org-mode is great for project management, and I use it quite a bit for that.

(setq org-default-notes-file "~/Nextcloud/org-mode/personal.org")
(setq org-agenda-files '("~/Nextcloud/org-mode/personal.org"
                         "~/Nextcloud/org-mode/jblc.org"))

;; Org-Mode Capture Templates
(defvar org-capture-templates)
(setq org-capture-templates
      '(("w" "Work")
        ("wt" "Todo" entry (file+headline "~/Nextcloud/org-mode/jblc.org" "Tasks")
         "* TODO %?\n%U\n%a\n" :clock-in nil :clock-resume nil)
        ("wr" "Respond" entry (file "~/Nextcloud/org-mode/jblc.org")
         "* NEXT Respond to %:from %:subject\nSCHEDULED: %t\n%U\n%a\n" :clock-in t :clock-resume t :immediate-finish t)
        ("wj" "Journal" entry (file+datetree "~/Nextcloud/org-mode/jblc.org")
         "* %? %?\n%U\n" :clock-in t :clock-resume t)
        ("wm" "Meeting" entry (file "~/Nextcloud/org-mode/jblc.org")
         "* MEETING with %? :MEETING:\n%U" :clock-in t :clock-resume t)
        ("wp" "Phone call" entry (file "~/Nextcloud/org-mode/jblc.org")
         "* PHONE %? :PHONE:\n%U" :clock-in t :clock-resume t)
        ("p" "Personal")
        ("pt" "Todo" entry (file+headline "~/Nextcloud/org-mode/personal.org" "Tasks")
         "* TODO %?\n%U\n%a\n" :clock-in nil :clock-resume nil)
        ("pr" "Respond" entry (file "~/Nextcloud/org-mode/personal.org")
         "* NEXT Respond to %:from %:subject\nSCHEDULED: %t\n%U\n%a\n" :clock-in t :clock-resume t :immediate-finish t)
        ("pj" "Journal" entry (file+datetree "~/Nextcloud/org-mode/personal.org")
         "* %? %?\n%U\n" :clock-in t :clock-resume t)
        ("pm" "Meeting" entry (file "~/Nextcloud/org-mode/personal.org")
         "* MEETING with %? :MEETING:\n%U" :clock-in t :clock-resume t)
        ("pp" "Phone call" entry (file "~/Nextcloud/org-mode/personal.org")
         "* PHONE %? :PHONE:\n%U" :clock-in t :clock-resume t)))

(global-set-key (kbd "C-c c") 'org-capture)
(global-set-key (kbd "C-c a") 'org-agenda)
(global-set-key (kbd "C-c l") 'org-store-link)
(setq org-log-done 'time)

7.9 ox-twbs

Twitter Bootstrap or just Bootstrap is prettier than out of the box Org-mode. Let's get it installed!

(use-package ox-twbs)

7.10 ox-latex

ox-latex allows syntax highlighted output. Just make sure to run the following code.

sudo pip install pygments

And set up ox-latex

(require 'ox-latex)
(add-to-list 'org-latex-packages-alist '("" "minted"))
(add-to-list 'org-latex-packages-alist '("" "tabularx"))
(setq org-latex-minted-options '(("breaklines" "true")
                                 ("breakanywhere" "true")))
(setq org-latex-listings 'minted)
(setq org-latex-pdf-process
      '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

7.11 ox-mediawiki

ox-mediawiki allows exporting org-mode documents to Mediawiki

(use-package ox-mediawiki
  :init
  (require 'ox-mediawiki))

7.12 ox-gfm

ox-gfm allows exporting org-mode documents to GitHub Markdown

(use-package ox-gfm
  :init
  (require 'ox-gfm))

7.13 ob-dot

Let's set up capacity to create pretty diagrams

(require 'ob-dot)

7.14 ob-ledger

Org Babel Ledger Blocks!

(use-package ob-ledger
  :quelpa (ob-ledger :fetcher url
                     :url "https://git.sr.ht/~bzg/org-contrib/blob/master/lisp/ob-ledger.el"))
(require 'ob-ledger)

7.15 ob-sql

ob-sql allows me to add MySQL queries to org-mode and execute them.

(use-package sql
  :init
  (require 'sql))
(require 'ob-sql)

7.16 ob-blockdiag

Create diagrams in org-mode.

(use-package ob-blockdiag
  :init
  (require 'ob-blockdiag))

7.17 ob-http

Perform HTTP requests in org-mode source blocks.

(use-package ob-http
  :init
  (require 'ob-http))

7.18 ob-restclient

Perform REST operations in org-mode source blocks.

(use-package ob-restclient
  :init
  (require 'ob-restclient))

7.19 Disable org-confirm-babel-evaluate for some code types

  • dot
(defun my-org-confirm-babel-evaluate (lang body)
  (not (member lang '("dot"))))  ;don't ask for dot
(setq org-confirm-babel-evaluate #'my-org-confirm-babel-evaluate)

7.20 org-publish

I have a website to publish!

(setq org-publish-project-alist
      '(
        ("org-notes"
         :base-directory "~/Nextcloud/org/"
         :base-extension "org"
         :publishing-directory "/ssh:michael@websites01.lan.productionservers.net:/mnt/sites/michael.englehorn.com/public_html/"
         :recursive t
         :publishing-function org-twbs-publish-to-html
         :headline-levels 4
         :auto-preamble t)
        ("org-static"
         :base-directory "~/Nextcloud/org/"
         :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|php\\|txt\\|asc"
         :publishing-directory "/ssh:michael@websites01.lan.productionservers.net:/mnt/sites/michael.englehorn.com/public_html/"
         :recursive t
         :publishing-function org-publish-attachment)
        ("org" :components ("org-notes" "org-static"))))

(require 's)
(defun my/publish-work ()
  "Publish ~/Nextcloud/org-mode/work/ whenever work.org is saved."
  (if (equal "~/Nextcloud/org-mode/work/work.org" buffer-file-truename)
      (progn (setq org-confirm-babel-evaluate nil)
             (setq org-export-use-babel t)
             (org-publish "org-work")
             (org-publish "org-work-static")
             (setq org-confirm-babel-evaluate t))))

(add-hook 'org-mode-hook
          (lambda ()
            (add-hook 'after-save-hook 'my/publish-work nil nil)))

8 Customizations

8.1 Load SSH Agent from environment

(when (not (eq window-system 'w32))
    (exec-path-from-shell-copy-env "SSH_AGENT_PID")
    (exec-path-from-shell-copy-env "PATH")
    (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))

8.2 Powerline

Initialize the Powerline.

(require 'powerline)

8.3 Powerline theme

Set up the powerline theme

(powerline-default-theme)

8.4 Load secrets

I keep slightly more sensitive information in a separate file so that I can easily publish my main configuration.

(el-get-bundle menglehorn-emacs-secret
  :url "git@gitlab.com:K0HAX/menglehorn-emacs-secret.git"
  :feature "menglehorn-emacs-secret"
  :submodule nil
  (load-file "~/.emacs.d/el-get/menglehorn-emacs-secret/menglehorn-emacs-secret.el"))

8.5 Restart Emacs

It's useful to be able to restart emacs from inside emacs. Configure restart-emacs to allow this.

(use-package restart-emacs)

8.6 Ledger Reports

Some custom and shorter keywords for ledger reports (C-r r in ledger-mode)

(setq ledger-reports
      (quote
       (("register" "ledger ")
        ("bal" "ledger -f %(ledger-file) --price-db ./prices.db -V bal Assets Liabilities")
        ("bigbal" "ledger -f %(ledger-file) bal Assets Liabilities")
        ("reg" "ledger -f %(ledger-file) reg")
        ("payee" "ledger -f %(ledger-file) reg @%(payee)")
        ("account" "ledger -f %(ledger-file) reg %(account)"))))

8.7 Newsticker List

Set up RSS feeds

(setq newsticker-url-list-defaults
(quote
    (("NY Times" "http://partners.userland.com/nytRss/nytHomepage.xml")
    ("The Register" "http://www.theregister.co.uk/tonys/slashdot.rdf")
    ("slashdot" "http://slashdot.org/index.rss" nil 3600))))

8.8 Mail Processing

Set up the mail sending function

(setq send-mail-function 'smtpmail-send-it)

8.9 Customizations file

Move the customizations file out from init.el so we don't break the Git repo.

(setq custom-file "~/.emacs.d/emacs-custom.el")
(load custom-file)

8.10 Disable blinking and flashing

Disable the annoying bell

(setq ring-bell-function 'ignore)

8.11 M-s s to SSH to a host.

I wanted to by able to easily SSH from Emacs, so I wrote some elisp.

(defun ssh-to-host (x)
  "Ask for host."
  (interactive "sHost: ")
  (let* ((buffer-name (format "*SSH %s*" x))
         (buffer (get-buffer buffer-name)))
    (if buffer
        (switch-to-buffer buffer)
      (multi-term)
      (term-send-string
       (get-buffer-process (rename-buffer buffer-name))
       (format "ssh %s\r" x)))))

(global-set-key (kbd "M-s s") 'ssh-to-host)

8.12 Ask to open as root if I lack permission to edit

Very useful. If I try to open a file I don't have write permissions to, ask if I want to open it as root using tramp.

Note: if you're experiencing problems using this (like tramp hanging), check that you can open them "manually" in the first place, C-x C-f /sudo::/path/to/file. Check the tramp troubleshooting section at emacs wiki.

(defun th-rename-tramp-buffer ()
  (when (file-remote-p (buffer-file-name))
    (rename-buffer
     (format "%s:%s"
             (file-remote-p (buffer-file-name) 'method)
             (buffer-name)))))

(add-hook 'find-file-hook
          'th-rename-tramp-buffer)

(defadvice find-file (around th-find-file activate)
  "Open FILENAME using tramp's sudo method if it's read-only."
  (if (and (not (file-writable-p (ad-get-arg 0)))
           (not (file-remote-p default-directory))
           (y-or-n-p (concat "File "
                             (ad-get-arg 0)
                             " is read-only.  Open it as root? ")))
      (th-find-file-sudo (ad-get-arg 0))
    ad-do-it))

(defun th-find-file-sudo (file)
  "Opens FILE with root privileges."
  (interactive "F")
  (set-buffer (find-file (concat "/sudo::" file))))

8.13 Dired customizations

  • Human readable sizes in Dired
  • Sort by size
(setq dired-listing-switches "-alh")

8.14 Key bindings

Set up custom global key bindings.

(global-set-key (kbd "<f12>") 'org-agenda)
(global-set-key (kbd "<f8>") 'org-cycle-agenda-files)
(global-set-key (kbd "<f9> b") 'bbdb)
(global-set-key (kbd "<f9> c") 'my-open-calendar)
(global-set-key (kbd "<f9> g") 'gnus)
(global-set-key (kbd "<f11>") 'org-clock-goto)
(global-set-key (kbd "C-c c") 'org-capture)

8.15 Cisco Router Mode

;;; cisco-router-mode.el --- Major mode for editing Cisco router configuration files
;;
;; Copyright (C) 2022 Michael Englehorn <michael at englehorn stop com>
;; Copyright (C) 2004 Noufal Ibrahim <nkv at hcoop period net>
;;
;; This program is not part of Gnu Emacs
;;
;; cisco-router-mode.el is free software; you can
;; redistribute it and/or modify it under the terms of the GNU General
;; Public License as published by the Free Software Foundation; either
;; version 2 of the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

(defgroup cisco-router nil
  "Cisco Router Mode"
  :group 'text)

(defcustom cisco-router-mode-hook nil
  "Hook called by \"cisco-router-mode\""
  :type 'hook
  :group 'cisco-router)

(defcustom cisco-router-mode-map
  (let
      ((cisco-router-mode-map (make-keymap)))
    (define-key cisco-router-mode-map "\C-j" 'newline-and-indent)
    cisco-router-mode-map)
  "Keymap for Cisco router configuration major mode"
  :group 'cisco-router)

;; Font locking definitions.
(defcustom cisco-router-command-face 'cisco-router-command-face
  "Face for basic router commands"
  :group 'cisco-router)

(defcustom cisco-router-toplevel-face 'cisco-router-toplevel-face
  "Face for top level commands"
  :group 'cisco-router)

(defcustom cisco-router-no-face 'cisco-router-no-face
  "Face for \"no\""
  :group 'cisco-router)

(defcustom cisco-router-ipadd-face 'cisco-router-ipadd-face
  "Face for IP addresses"
  :group 'cisco-router)
;; End Font locking definitions.

(defface cisco-router-ipadd-face
  '(
    (((type tty) (class color)) (:foreground "yellow"))
    (((type graphic) (class color)) (:foreground "LightGoldenrod"))
    (t (:foreground "LightGoldenrod" ))
    )
  "Face for IP addresses")

(defface cisco-router-command-face
  '(
    (((type tty) (class color)) (:foreground "cyan"))
    (((type graphic) (class color)) (:foreground "cyan"))
    (t (:foreground "cyan" ))
    )
  "Face for basic router commands")

(defface cisco-router-toplevel-face
  '(
    (((type tty) (class color)) (:foreground "blue"))
    (((type graphic) (class color)) (:foreground "lightsteelblue"))
    (t (:foreground "lightsteelblue" ))
    )
  "Face for basic router commands")

(defface cisco-router-no-face
  '(
    (t (:underline t))
    )
  "Face for \"no\"")


;; (regexp-opt '("interface" "ip vrf" "controller" "class-map" "redundancy" "line" "policy-map" "router" "access-list" "route-map") t)
;; (regexp-opt '("diagnostic" "hostname" "logging" "service" "alias" "snmp-server" "boot" "card" "vtp" "version" "enable") t)

(defconst cisco-router-font-lock-keywords
  (list
   '( "\\<\\(access-list\\|c\\(?:lass-map\\|ontroller\\)\\|i\\(?:nterface\\|p vrf\\)\\|line\\|policy-map\\|r\\(?:edundancy\\|oute\\(?:-map\\|r\\)\\)\\)\\>". cisco-router-toplevel-face)
   '( "\\<\\(alias\\|boot\\|card\\|diagnostic\\|^enable\\|hostname\\|logging\\|s\\(?:ervice\\|nmp-server\\)\\|v\\(?:ersion\\|tp\\)\\)\\>" . cisco-router-command-face)
   '("\\<\\(no\\)\\>" . cisco-router-no-face)
   '("\\<\\([0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\)\\>" . cisco-router-ipadd-face)
   )
  "Font locking definitions for cisco router mode")

;; Imenu definitions.
(defcustom cisco-router-imenu-expression
  '(
    ("Interfaces"        "^[\t ]*interface *\\(.*\\)" 1)
    ("VRFs"              "^ip vrf *\\(.*\\)" 1)
    ("Controllers"       "^[\t ]*controller *\\(.*\\)" 1)
    ("Routing protocols" "^router *\\(.*\\)" 1)
    ("Class maps"        "^class-map *\\(.*\\)" 1)
    ("Policy maps"       "^policy-map *\\(.*\\)" 1)
    )
  "Cisco Router imenu Expression"
  :group 'cisco-router)

;; Indentation definitions.
(defun cisco-router-indent-line ()
  "Indent current line as cisco router config line"
  (let ((indent0 "^interface\\|redundancy\\|^line\\|^ip vrf \\|^controller\\|^class-map\\|^policy-map\\|router\\|access-list\\|route-map")
        (indent1 " *main-cpu\\| *class\\W"))
    (beginning-of-line)
    (let ((not-indented t)
          (cur-indent 0))
      (cond ((or (bobp) (looking-at indent0) (looking-at "!")) ; Handles the indent0 and indent1 lines
;            (message "Indent0")
             (setq not-indented nil
                   cur-indent 0))
            ((looking-at indent1)
;            (message "Indent1")
             (setq not-indented nil
                   cur-indent 1)))
      (save-excursion ; Indents regular lines depending on the block they're in.
        (while not-indented
          (forward-line -1)
          (cond ((looking-at indent1)
;                (message "Indent1 block")
                 (setq cur-indent 2
                       not-indented nil))
                ((looking-at indent0)
;                (message "Indent0 block")
                 (setq cur-indent 1
                       not-indented nil))
                ((looking-at "!")
;                (message "Reached !")
                 (setq cur-indent 0
                       not-indented nil))
                ((bobp)
;                (message "Buffer beginning reached")
                 (setq cur-indent 0
                       not-indented nil)))))
      (indent-line-to cur-indent))))


;; Custom syntax table
(defcustom cisco-router-mode-syntax-table (make-syntax-table)
  "Syntax table for cisco router mode"
  :group 'cisco-router)

(modify-syntax-entry ?_ "w" cisco-router-mode-syntax-table) ;All _'s are part of words.
(modify-syntax-entry ?- "w" cisco-router-mode-syntax-table) ;All -'s are part of words.
(modify-syntax-entry ?! "<" cisco-router-mode-syntax-table) ;All !'s start comments.
(modify-syntax-entry ?\n ">" cisco-router-mode-syntax-table) ;All newlines end comments.
(modify-syntax-entry ?\r ">" cisco-router-mode-syntax-table) ;All linefeeds end comments.

;; Entry point
(defun cisco-router-mode  ()
  "Major mode for editing Cisco router configuration files"
  (interactive)
  (kill-all-local-variables)
  (set-syntax-table cisco-router-mode-syntax-table)
  (use-local-map cisco-router-mode-map)
  (set (make-local-variable 'font-lock-defaults) '(cisco-router-font-lock-keywords))
  (set (make-local-variable 'indent-line-function) 'cisco-router-indent-line)
  (set (make-local-variable 'comment-start) "!")
  (set (make-local-variable 'comment-start-skip) "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)!+ *")
  (setq imenu-case-fold-search nil)
  (set (make-local-variable 'imenu-generic-expression) cisco-router-imenu-expression)
  (imenu-add-to-menubar "Imenu")
  (setq major-mode 'cisco-router-mode
        mode-name "Cisco router configuration")
  (run-hooks cisco-router-mode-hook))

(add-to-list 'auto-mode-alist '("\\.cfg\\'" . cisco-router-mode))

(provide 'cisco-router-mode)

;;; cisco-router-mode.el ends here

8.16 Fix gnus large groups

(setq gnus-large-newsgroup nil)

8.17 Fix tramp for ZSH

(add-to-list 'tramp-connection-properties
      (list (regexp-quote "/ssh:michael@asterisk:")
            "remote-shell" "/bin/zsh"))

8.18 nws-weather custom function

(defun nws-weather (station)
  (interactive "sNWS Station? ")
  (let ((current-observation (car (with-current-buffer (url-retrieve-synchronously (format "https://w1.weather.gov/xml/current_obs/%s.xml" station))
                                    (xml-parse-region (point-min) (point-max))))))
    (message "Location: %s\nTemperature: %s℉\nRelative Humidity: %s%%\nWind: %s\nDew Point: %s\nVisibility: %s miles"
            (car (xml-node-children (car (xml-get-children current-observation 'location))))
            (car (xml-node-children (car (xml-get-children current-observation 'temp_f))))
            (car (xml-node-children (car (xml-get-children current-observation 'relative_humidity))))
            (car (xml-node-children (car (xml-get-children current-observation 'wind_string))))
            (car (xml-node-children (car (xml-get-children current-observation 'dewpoint_string))))
            (car (xml-node-children (car (xml-get-children current-observation 'visibility_mi)))))))

9 BBDB

9.1 Automatically add people to the bbdb

(bbdb-initialize 'gnus 'message)
(bbdb-mua-auto-update-init 'message)
(setq bbdb-mua-auto-update-p 'query)

10 Mutt

10.1 Emacs as external editor

(add-to-list 'auto-mode-alist '("/mutt" . mail-mode))

10.2 Autofill for Mail

(add-hook 'mail-mode-hook 'turn-on-auto-fill)

10.3 Replace C-x # with C-x k

(defun my-mail-mode-hook ()
  (auto-fill-mode 1)
  (abbrev-mode 1)
  (local-set-key "\C-Xk" 'server-edit))
(add-hook 'mail-mode-hook 'my-mail-mode-hook)

11 Evil Mode

11.1 Enable evil mode

(setq evil-want-keybinding nil)
(use-package evil)
(use-package evil-leader)
(use-package evil-org)
(require 'evil)
(evil-mode 1)

11.2 Set up global key-bindings, and make evil my leader.

(eval-after-load "evil"
'(progn
    (define-key evil-normal-state-map (kbd "M-h") 'evil-window-left)
    (define-key evil-normal-state-map (kbd "M-j") 'evil-window-down)
    (define-key evil-normal-state-map (kbd "M-k") 'evil-window-up)
    (define-key evil-normal-state-map (kbd "M-l") 'evil-window-right)
    (define-key evil-motion-state-map ";" 'smex)
    (define-key evil-motion-state-map ":" 'evil-ex)))

11.3 Evil Powerline

Powerline for Evil mode

(use-package powerline)
(use-package powerline-evil)

11.4 Ledger Mode

I use ledger-cli for my personal finances, here I make it evil friendly.

(use-package ledger-mode
  :ensure t
  :init
  (setq ledger-clear-whole-transactions 1)

  :config
  (add-to-list 'evil-emacs-state-modes 'ledger-report-mode)
  :mode "\\.ldg\\'")

11.5 Set up key-bindings for ledger-mode

(with-eval-after-load 'ledger-mode
  (define-key ledger-mode-map (kbd "C-c n") 'ledger-add-transaction)
  (define-key ledger-mode-map (kbd "C-c c") 'ledger-mode-clean-buffer)
  (define-key ledger-mode-map (kbd "C-c r") 'ledger-report))

11.6 Set up key-bindings for BBDB

Set up the Key Bindings for the Big Brother Database. This code was pulled from github/tarleb

(evil-define-key 'motion bbdb-mode-map
  "\C-k"       'bbdb-delete-field-or-record
  "\C-x\C-s"   'bbdb-save
  "\C-x\C-t"   'bbdb-transpose-fields
  "\d"         'bbdb-prev-field ; DEL
  "\M-d"       'bbdb-dial
  "\t"         'bbdb-next-field ; TAB
  "+"          'bbdb-append-display
  "*"          'bbdb-do-all-records
  ";"          'bbdb-edit-foo
  "?"          'bbdb-help
  "!"          'bbdb-search-invert
  "="          'delete-other-windows
  "a"          'bbdb-add-mail-alias
  "A"          'bbdb-mail-aliases
  "C"          'bbdb-copy-records-as-kill
  "c"          'bbdb-create
  "d"          'bbdb-delete-field-or-record
  "e"          'bbdb-edit-field
  "h"          'bbdb-info
  "i"          'bbdb-insert-field
  "J"          'bbdb-next-field
  "j"          'bbdb-next-record
  "K"          'bbdb-prev-field
  "k"          'bbdb-prev-record
  "m"          'bbdb-mail
  "M"          'bbdb-mail-address
  "N"          'bbdb-next-field
  "n"          'bbdb-next-record
  "o"          'bbdb-omit-record
  "P"          'bbdb-prev-field
  "p"          'bbdb-prev-record
  "s"          'bbdb-save
  "T"          'bbdb-display-records-completely
  "t"          'bbdb-toggle-records-layout
  "u"          'bbdb-browse-url

  ;; Search keys
  "b"          'bbdb
  "/1"         'bbdb-display-records
  "/n"         'bbdb-search-name
  "/o"         'bbdb-search-organization
  "/p"         'bbdb-search-phone
  "/a"         'bbdb-search-address
  "/m"         'bbdb-search-mail
  "/N"         'bbdb-search-xfields
  "/x"         'bbdb-search-xfields
  "/c"         'bbdb-search-changed
  "/d"         'bbdb-search-duplicates
  "\C-xnw"     'bbdb-display-all-records
  "\C-xnd"     'bbdb-display-current-record
  )

(evil-set-initial-state 'bbdb-mode 'motion)

11.7 Fix term-mode

Term-mode shouldn't have evil bindings.

(evil-set-initial-state 'term-mode 'emacs)

11.8 Fix govc-mode

Govc shouldn't have evil bindings.

(evil-set-initial-state 'govc-mode 'emacs)
(evil-set-initial-state 'govc-mode-major-mode 'emacs)

11.9 Fix twittering-mode

Twittering should have some evil bindings.

(eval-after-load 'twittering-mode
  '(progn
     (evil-define-key 'motion twittering-mode-map
                                  "g" nil
                                  "g g" 'twittering-goto-first-status
                                  "c" 'twittering-current-timeline
                                  "C-u" 'twittering-scroll-down
                                  "/" 'evil-search-forward
                                  "?" 'evil-search-backward
                                  "n" 'evil-search-next
                                  "N" 'evil-search-previous
                                  [escape] 'twittering-edit-cancel-status
                                  "<tab>" 'twittering-goto-next-uri)
     (evil-set-initial-state 'twittering-mode 'motion)))

11.10 evil-collection

evil-collection is a collection of Evil mode bindings for parts of Emacs that Evil does not cover properly by default.

(use-package evil-collection
  :init (evil-collection-init))

12 Disabled Configs

This section is where bad sections of this config can go if they break Emacs. It's only used for debugging purposes.

12.1 graphviz-dot-mode

(use-package graphviz-dot-mode
  :ensure t
  :config
  (setq graphviz-dot-indent-width 4))

(use-package company-graphviz-dot)

12.2 calfw

Package for org-mode calendar

(use-package calfw
  :custom-face
  (cfw:face-toolbar ((t (:background "midnight blue" :foreground "Steelblue4"))))
  (cfw:face-toolbar-button-off ((t (:foreground "gray30" :weight bold)))))
(use-package calfw-org)
(use-package calfw-ical)
(use-package calfw-cal)

12.3 Twitter Mode

Browse Twitter from Emacs

(use-package twittering-mode
  :init
  (if (executable-find "convert")
      (setq twittering-convert-fix-size 32))
  (if (executable-find "gzip")
      (setq twittering-use-icon-storage t)))

12.4 nnreddit

A Reddit backend for the Gnus newsreader.

;(el-get-bundle nnredit
;  :url "https://github.com/paul-issartel/nnreddit.git"
;  (add-to-list 'gnus-secondary-select-methods '(nnreddit "")))