Permalink
Find file
a109313 Feb 25, 2017
@hrs @benjamreynolds
1344 lines (1003 sloc) 36.7 KB

Emacs configuration

Use sensible-defaults.el

Use sensible-defaults.el for some basic settings.

(load-file "~/code/personal/sensible-defaults.el/sensible-defaults.el")
(sensible-defaults/use-all-settings)
(sensible-defaults/use-all-keybindings)
(sensible-defaults/backup-to-temp-directory)

Set personal information

(setq user-full-name "Harry R. Schwartz"
      user-mail-address "hello@harryrschwartz.com"
      calendar-latitude 42.2
      calendar-longitude -71.1
      calendar-location-name "Cambridge, MA")

Add resources to load-path

(add-to-list 'load-path "~/.emacs.d/resources/")

Package management

I use cask and pallet for managing packages.

(require 'cask "~/.cask/cask.el")
(cask-initialize)
(require 'pallet)

evil-mode

Use evil.

(evil-mode 1)

Enable surround everywhere.

(global-evil-surround-mode 1)

Bind C-p to fuzzy-finding files in the current project.

(define-key evil-normal-state-map (kbd "C-p") 'projectile-find-file)

Utility functions

Define a big ol’ bunch of handy utility functions.

(defun hrs/view-buffer-name ()
  "Display the filename of the current buffer."
  (interactive)
  (message (buffer-file-name)))

(defun hrs/generate-scratch-buffer ()
  "Create and switch to a temporary scratch buffer with a random
     name."
  (interactive)
  (switch-to-buffer (make-temp-name "scratch-")))

(defun hrs/de-unicode ()
  "Tidy up a buffer by replacing all special Unicode characters
     (smart quotes, etc.) with their more sane cousins"
  (interactive)
  (let ((unicode-map '(("[\u2018\|\u2019\|\u201A\|\uFFFD]" . "'")
                       ("[\u201c\|\u201d\|\u201e]" . "\"")
                       ("\u2013" . "--")
                       ("\u2014" . "---")
                       ("\u2026" . "...")
                       ("\u00A9" . "(c)")
                       ("\u00AE" . "(r)")
                       ("\u2122" . "TM")
                       ("[\u02DC\|\u00A0]" . " "))))
    (save-excursion
      (loop for (key . value) in unicode-map
            do
            (goto-char (point-min))
            (replace-regexp key value)))))

(defun hrs/beautify-json ()
  "Pretty-print the JSON in the marked region. Currently shells
     out to `jsonpp'--be sure that's installed!"
  (interactive)
  (save-excursion
    (shell-command-on-region (mark) (point) "jsonpp" (buffer-name) t)))

(defun hrs/unfill-paragraph ()
  "Takes a multi-line paragraph and makes it into a single line of text."
  (interactive)
  (let ((fill-column (point-max)))
    (fill-paragraph nil)))

(defun hrs/kill-current-buffer ()
  "Kill the current buffer without prompting."
  (interactive)
  (kill-buffer (current-buffer)))

(defun hrs/visit-last-dired-file ()
  "Open the last file in an open dired buffer."
  (end-of-buffer)
  (previous-line)
  (dired-find-file))

(defun hrs/visit-last-migration ()
  "Open the last file in 'db/migrate/'. Relies on projectile. Pretty sloppy."
  (interactive)
  (dired (expand-file-name "db/migrate" (projectile-project-root)))
  (hrs/visit-last-dired-file)
  (kill-buffer "migrate"))

(defun hrs/add-auto-mode (mode &rest patterns)
  "Add entries to `auto-mode-alist' to use `MODE' for all given file `PATTERNS'."
  (dolist (pattern patterns)
    (add-to-list 'auto-mode-alist (cons pattern mode))))

(defun hrs/find-file-as-sudo ()
  (interactive)
  (let ((file-name (buffer-file-name)))
    (when file-name
      (find-alternate-file (concat "/sudo::" file-name)))))

(defun hrs/insert-random-string (len)
  "Insert a random alphanumeric string of length len."
  (interactive)
  (let ((mycharset "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstyvwxyz"))
    (dotimes (i len)
      (insert (elt mycharset (random (length mycharset)))))))

(defun hrs/generate-password ()
  "Insert a good alphanumeric password of length 30."
  (interactive)
  (hrs/insert-random-string 30))

UI preferences

Disable window chrome

I don’t usually use the menu or scroll bar, and they take up useful space.

(tool-bar-mode 0)
(menu-bar-mode 0)
(when window-system
  (scroll-bar-mode -1))

Use fancy lambdas

Why not?

(global-prettify-symbols-mode t)

Custom solarized-dark theme

(when window-system
  (setq solarized-use-variable-pitch nil)
  (setq solarized-height-plus-1 1.0)
  (setq solarized-height-plus-2 1.0)
  (setq solarized-height-plus-3 1.0)
  (setq solarized-height-plus-4 1.0)
  (setq solarized-high-contrast-mode-line t)
  (load-theme 'solarized-dark t))

Disable visual bell

sensible-defaults replaces the audible bell with a visual one, but I really don’t even want that (and my Emacs/Mac pair renders it poorly). This disables the bell altogether.

(setq ring-bell-function 'ignore)

Scroll conservatively

When point goes outside the window, Emacs usually recenters the buffer point. I’m not crazy about that. This changes scrolling behavior to only scroll as far as point goes.

(setq scroll-conservatively 100)

Set default font and configure font resizing

I’m partial to Inconsolata.

The standard text-scale- functions just resize the text in the current buffer; I’d generally like to resize the text in every buffer, and I usually want to change the size of the modeline, too (this is especially helpful when presenting). These functions and bindings let me resize everything all together!

Note that this overrides the default font-related keybindings from sensible-defaults.

(setq hrs/default-font "Inconsolata")
(setq hrs/default-font-size 14)
(setq hrs/current-font-size hrs/default-font-size)

(setq hrs/font-change-increment 1.1)

(defun hrs/set-font-size ()
  "Set the font to `hrs/default-font' at `hrs/current-font-size'."
  (set-frame-font
   (concat hrs/default-font "-" (number-to-string hrs/current-font-size))))

(defun hrs/reset-font-size ()
  "Change font size back to `hrs/default-font-size'."
  (interactive)
  (setq hrs/current-font-size hrs/default-font-size)
  (hrs/set-font-size))

(defun hrs/increase-font-size ()
  "Increase current font size by a factor of `hrs/font-change-increment'."
  (interactive)
  (setq hrs/current-font-size
        (ceiling (* hrs/current-font-size hrs/font-change-increment)))
  (hrs/set-font-size))

(defun hrs/decrease-font-size ()
  "Decrease current font size by a factor of `hrs/font-change-increment', down to a minimum size of 1."
  (interactive)
  (setq hrs/current-font-size
        (max 1
             (floor (/ hrs/current-font-size hrs/font-change-increment))))
  (hrs/set-font-size))

(define-key global-map (kbd "C-)") 'hrs/reset-font-size)
(define-key global-map (kbd "C-+") 'hrs/increase-font-size)
(define-key global-map (kbd "C-=") 'hrs/increase-font-size)
(define-key global-map (kbd "C-_") 'hrs/decrease-font-size)
(define-key global-map (kbd "C--") 'hrs/decrease-font-size)

(hrs/reset-font-size)

Highlight the current line

global-hl-line-mode softly highlights the background color of the line containing point. It makes it a bit easier to find point, and it’s useful when pairing or presenting code.

(when window-system
  (global-hl-line-mode))

Hide certain modes from the modeline

I’d rather have only a few necessary mode identifiers on my modeline. This either hides or “renames” a variety of major or minor modes using the diminish package.

(defmacro diminish-minor-mode (filename mode &optional abbrev)
  `(eval-after-load (symbol-name ,filename)
     '(diminish ,mode ,abbrev)))

(defmacro diminish-major-mode (mode-hook abbrev)
  `(add-hook ,mode-hook
             (lambda () (setq mode-name ,abbrev))))

(diminish-minor-mode 'abbrev 'abbrev-mode)
(diminish-minor-mode 'simple 'auto-fill-function)
(diminish-minor-mode 'company 'company-mode)
(diminish-minor-mode 'eldoc 'eldoc-mode)
(diminish-minor-mode 'flycheck 'flycheck-mode)
(diminish-minor-mode 'flyspell 'flyspell-mode)
(diminish-minor-mode 'global-whitespace 'global-whitespace-mode)
(diminish-minor-mode 'projectile 'projectile-mode)
(diminish-minor-mode 'ruby-end 'ruby-end-mode)
(diminish-minor-mode 'subword 'subword-mode)
(diminish-minor-mode 'undo-tree 'undo-tree-mode)
(diminish-minor-mode 'yard-mode 'yard-mode)
(diminish-minor-mode 'yasnippet 'yas-minor-mode)
(diminish-minor-mode 'wrap-region 'wrap-region-mode)

(diminish-minor-mode 'paredit 'paredit-mode " π")

(diminish-major-mode 'emacs-lisp-mode-hook "el")
(diminish-major-mode 'haskell-mode-hook "λ=")
(diminish-major-mode 'lisp-interaction-mode-hook "λ")
(diminish-major-mode 'python-mode-hook "Py")

Highlight uncommitted changes

Use the diff-hl package to highlight changed-and-uncommitted lines when programming.

(require 'diff-hl)

(add-hook 'prog-mode-hook 'turn-on-diff-hl-mode)
(add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode)

Programming customizations

I like shallow indentation, but tabs are displayed as 8 characters by default. This reduces that.

(setq-default tab-width 2)

Treating terms in CamelCase symbols as separate words makes editing a little easier for me, so I like to use subword-mode everywhere.

(global-subword-mode 1)

Compilation output goes to the *compilation* buffer. I rarely have that window selected, so the compilation output disappears past the bottom of the window. This automatically scrolls the compilation window so I can always see the output.

(setq compilation-scroll-output t)

CSS and Sass

Indent 2 spaces and use rainbow-mode to display color-related words in the color they describe.

(add-hook 'css-mode-hook
          (lambda ()
            (rainbow-mode)
            (setq css-indent-offset 2)))

(add-hook 'scss-mode-hook 'rainbow-mode)

Don’t compile the current file every time I save.

(setq scss-compile-at-save nil)

Haskell

Enable haskell-doc-mode, which displays the type signature of a function, and use smart indentation.

(setq exec-path (append exec-path (list "~/.cabal/bin")))
(add-hook 'haskell-mode-hook
          (lambda ()
            (haskell-doc-mode)
            (turn-on-haskell-indent)
            (ghc-init)))

JavaScript and CoffeeScript

Indent everything by 2 spaces.

(setq js-indent-level 2)

(add-hook 'coffee-mode-hook
          (lambda ()
            (yas-minor-mode 1)
            (setq coffee-tab-width 2)))

Lisps

All the lisps have some shared features, so we want to do the same things for all of them. That includes using paredit-mode to balance parentheses (and more!), rainbow-delimiters to color matching parentheses, and highlighting the whole expression when point is on a paren.

(setq lispy-mode-hooks
      '(clojure-mode-hook
        emacs-lisp-mode-hook
        lisp-mode-hook
        scheme-mode-hook))

(dolist (hook lispy-mode-hooks)
  (add-hook hook (lambda ()
                   (setq show-paren-style 'expression)
                   (paredit-mode)
                   (rainbow-delimiters-mode))))

If I’m writing in Emacs lisp I’d like to use eldoc-mode to display documentation.

(add-hook 'emacs-lisp-mode-hook 'eldoc-mode)

My own silly language (blueprint) is close enough to Scheme that it can use the same mode.

(hrs/add-auto-mode 'scheme-mode "\\.blu$")

Magit

I bring up the status menu with C-x g:

(global-set-key (kbd "C-x g") 'magit-status)

The default behavior of magit is to ask before pushing. I haven’t had any problems with accidentally pushing, so I’d rather not confirm that every time.

(setq magit-push-always-verify nil)

I sometimes use git from the terminal, and I’ll use emacsclient --tty to write commits. I’d like to be in the insert state when my editor pops open for that.

(add-hook 'with-editor-mode-hook 'evil-insert-state)

Prolog

I don’t write a lot of Prolog, but (oddly enough) I write more Prolog than Perl.

(hrs/add-auto-mode 'prolog-mode "\\.pl$")

Projectile

Projectile’s default binding of projectile-ag to C-c p s s is clunky enough that I rarely use it (and forget it when I need it). This binds the easier-to-type C-c C-v and C-c v to useful searches.

(defun hrs/search-project-for-symbol-at-point ()
  "Use `projectile-ag' to search the current project for `symbol-at-point'."
  (interactive)
  (projectile-ag (projectile-symbol-at-point)))

(global-set-key (kbd "C-c v") 'projectile-ag)
(global-set-key (kbd "C-c C-v") 'hrs/search-project-for-symbol-at-point)

When I visit a project with projectile-switch-project, the default action is to search for a file in that project. I’d rather just open up the top-level directory of the project in dired and find (or create) new files from there.

(setq projectile-switch-project-action 'projectile-dired)

Python

Indent 2 spaces.

(setq python-indent 2)

Ruby and RSpec

I use chruby to switch between versions of Ruby. This sets a default version to use within Emacs (for things like xmp or rspec).

(chruby "ruby-2.4.0")

rcodetools provides xmp, which lets me evaluate a Ruby buffer and display the results in “magic” (# =>) comments.

I disable warnings in Ruby because I disagree with a few of them (complaining about private attr_reader, especially) and they gunk up my buffer.

(setq xmpfilter-command-name
      "ruby -S xmpfilter --no-warnings --dev --fork --detect-rbtest")
(require 'rcodetools)

There are a bunch of things I’d like to do when I open a Ruby buffer:

  • I don’t want to insert an encoding comment.
  • I want to enable yas, rspec, yard, flycheck, and projectile-rails.
  • I’d like my RSpec tests to be run in a random order, and I’d like the output to be colored.
  • C-c C-c should run xmp, to do that nifty “eval into comments” trick.
(add-hook 'ruby-mode-hook
          (lambda ()
            (setq ruby-insert-encoding-magic-comment nil)
            (yas-minor-mode)
            (rspec-mode)
            (yard-mode)
            (flycheck-mode)
            (local-set-key "\r" 'newline-and-indent)
            (setq rspec-command-options "--color --order random")
            (define-key ruby-mode-map (kbd "C-c C-c") 'xmp)
            (projectile-rails-mode)))

I associate ruby-mode with Gemfiles, gemspecs, Rakefiles, and Vagrantfiles.

(hrs/add-auto-mode
 'ruby-mode
 "\\Gemfile$"
 "\\.rake$"
 "\\.gemspec$"
 "\\Guardfile$"
 "\\Rakefile$"
 "\\Vagrantfile$"
 "\\Vagrantfile.local$")

When running RSpec tests I’d like to scroll to the first error.

(add-hook 'rspec-compilation-mode-hook
          (lambda ()
            (make-local-variable 'compilation-scroll-output)
            (setq compilation-scroll-output 'first-error)))

sh

Indent with 2 spaces.

(add-hook 'sh-mode-hook
          (lambda ()
            (setq sh-basic-offset 2
                  sh-indentation 2)))

Slim

If I’m editing Slim templates I’m probably in a Rails project. In that case, I’d like to still be able to run my tests from a Slim buffer.

(add-hook 'slim-mode-hook 'rspec-mode)

web-mode

If I’m in web-mode, I’d like to:

  • Color color-related words with rainbow-mode.
  • Still be able to run RSpec tests from web-mode buffers.
  • Indent everything with 2 spaces.
(add-hook 'web-mode-hook
          (lambda ()
            (rainbow-mode)
            (rspec-mode)
            (setq web-mode-markup-indent-offset 2)))

Use web-mode with embedded Ruby files, regular HTML, and PHP.

(hrs/add-auto-mode
 'web-mode
 "\\.erb$"
 "\\.html$"
 "\\.php$"
 "\\.rhtml$")

YAML

If I’m editing YAML I’m usually in a Rails project. I’d like to be able to run the tests from any buffer.

(add-hook 'yaml-mode-hook 'rspec-mode)

Terminal

I use multi-term to manage my shell sessions. It’s bound to C-c s.

(global-set-key (kbd "C-c s") 'multi-term)

Use a login shell:

(setq multi-term-program-switches "--login")

I’d rather not use Evil in the terminal. It’s not especially useful (I don’t use vi bindings in xterm) and it shadows useful keybindings (C-d for EOF, for example).

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

I add a bunch of hooks to term-mode:

  • I’d like links (URLs, etc) to be clickable.
  • Yanking in term-mode doesn’t quite work. The text from the paste appears in the buffer but isn’t sent to the shell process. This correctly binds C-y and middle-click to yank the way we’d expect.
  • I bind M-o to quickly change windows. I’d like that in terminals, too.
  • I don’t want to perform yasnippet expansion when tab-completing.
(defun hrs/term-paste (&optional string)
  (interactive)
  (process-send-string
   (get-buffer-process (current-buffer))
   (if string string (current-kill 0))))

(add-hook 'term-mode-hook
          (lambda ()
            (goto-address-mode)
            (define-key term-raw-map (kbd "C-y") 'hrs/term-paste)
            (define-key term-raw-map (kbd "<mouse-2>") 'hrs/term-paste)
            (define-key term-raw-map (kbd "M-o") 'other-window)
            (setq yas-dont-activate t)))

Publishing and task management with Org-mode

Display preferences

I like to see an outline of pretty bullets instead of a list of asterisks.

(add-hook 'org-mode-hook
          (lambda ()
            (org-bullets-mode t)))

I like seeing a little downward-pointing arrow instead of the usual ellipsis (...) that org displays when there’s stuff under a header.

(setq org-ellipsis "")

Use syntax highlighting in source blocks while editing.

(setq org-src-fontify-natively t)

Make TAB act as if it were issued in a buffer of the language’s major mode.

(setq org-src-tab-acts-natively t)

When editing a code snippet, use the current window rather than popping open a new one (which shows the same information).

(setq org-src-window-setup 'current-window)

Task and org-capture management

Store my org files in ~/org, maintain an inbox in Dropbox, define the location of an index file (my main todo list), and archive finished tasks in ~/org/archive.org.

(setq org-directory "~/org")

(defun org-file-path (filename)
  "Return the absolute address of an org file, given its relative name."
  (concat (file-name-as-directory org-directory) filename))

(setq org-inbox-file "~/Dropbox/inbox.org")
(setq org-index-file (org-file-path "index.org"))
(setq org-archive-location
      (concat (org-file-path "archive.org") "::* From %s"))

I use Drafts to create new tasks, format them according to a template, and append them to an “inbox.org” file in my Dropbox. This function lets me import them easily from that inbox file to my index.

(defun hrs/copy-tasks-from-inbox ()
  (when (file-exists-p org-inbox-file)
    (save-excursion
      (find-file org-index-file)
      (goto-char (point-max))
      (insert-file-contents org-inbox-file)
      (delete-file org-inbox-file))))

I store all my todos in ~/org/index.org, so I’d like to derive my agenda from there.

(setq org-agenda-files (list org-index-file))

Hitting C-c C-x C-s will mark a todo as done and move it to an appropriate place in the archive.

(defun hrs/mark-done-and-archive ()
  "Mark the state of an org-mode item as DONE and archive it."
  (interactive)
  (org-todo 'done)
  (org-archive-subtree))

(define-key org-mode-map (kbd "C-c C-x C-s") 'hrs/mark-done-and-archive)

Record the time that a todo was archived.

(setq org-log-done 'time)

Capturing tasks

Define a few common tasks as capture templates. Specifically, I frequently:

  • Record ideas for future blog posts in ~/org/blog-ideas.org,
  • Keep a running grocery list in ~/org/groceries.org, and
  • Maintain a todo list in ~/org/index.org.
(setq org-capture-templates
      '(("b" "Blog idea"
         entry
         (file (org-file-path "blog-ideas.org"))
         "* TODO %?\n")

        ("g" "Groceries"
         checkitem
         (file (org-file-path "groceries.org")))

        ("l" "Today I Learned..."
         entry
         (file+datetree (org-file-path "til.org"))
         "* %?\n")

        ("r" "Reading"
         checkitem
         (file (org-file-path "to-read.org")))

        ("t" "Todo"
         entry
         (file+headline org-index-file "Tasks")
         "* TODO %?\n")))

When I’m starting an org capture template I’d like to begin in insert mode. I’m opening it up in order to start typing something, so this skips a step.

(add-hook 'org-capture-mode-hook 'evil-insert-state)

Keybindings

Bind a few handy keys.

(define-key global-map "\C-cl" 'org-store-link)
(define-key global-map "\C-ca" 'org-agenda)
(define-key global-map "\C-cc" 'org-capture)

Hit C-c i to quickly open up my todo list.

(defun open-index-file ()
  "Open the master org TODO list."
  (interactive)
  (hrs/copy-tasks-from-inbox)
  (find-file org-index-file)
  (flycheck-mode -1)
  (end-of-buffer))

(global-set-key (kbd "C-c i") 'open-index-file)

Hit M-n to quickly open up a capture template for a new todo.

(defun org-capture-todo ()
  (interactive)
  (org-capture :keys "t"))

(global-set-key (kbd "M-n") 'org-capture-todo)
(add-hook 'gfm-mode-hook
          (lambda () (local-set-key (kbd "M-n") 'org-capture-todo)))
(add-hook 'haskell-mode-hook
          (lambda () (local-set-key (kbd "M-n") 'org-capture-todo)))

Exporting

Allow export to markdown and beamer (for presentations).

(require 'ox-md)
(require 'ox-beamer)

Allow babel to evaluate Emacs lisp, Ruby, dot, or Gnuplot code.

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (ruby . t)
   (dot . t)
   (gnuplot . t)))

Don’t ask before evaluating code blocks.

(setq org-confirm-babel-evaluate nil)

Associate the “dot” language with the graphviz-dot major mode.

(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))

Translate regular ol’ straight quotes to typographically-correct curly quotes when exporting.

(setq org-export-with-smart-quotes t)

Exporting to HTML

Don’t include a footer with my contact and publishing information at the bottom of every exported HTML document.

(setq org-html-postamble nil)

Exporting to PDF

I want to produce PDFs with syntax highlighting in the code. The best way to do that seems to be with the minted package, but that package shells out to pygments to do the actual work. pdflatex usually disallows shell commands; this enables that.

(setq org-latex-pdf-process
      '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

Include the minted package in all of my LaTeX exports.

(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)

Exporting projects

I have a few Org project definitions that I maintain in a separate elisp file.

(load-file ".emacs.d/projects.el")

TeX configuration

I rarely write LaTeX directly any more, but I often export through it with org-mode, so I’m keeping them together.

Automatically parse the file after loading it.

(setq TeX-parse-self t)

Always use pdflatex when compiling LaTeX documents. I don’t really have any use for DVIs.

(setq TeX-PDF-mode t)

Enable a minor mode for dealing with math (it adds a few useful keybindings), and always treat the current file as the “main” file. That’s intentional, since I’m usually actually in an org document.

(add-hook 'LaTeX-mode-hook
          (lambda ()
            (LaTeX-math-mode)
            (setq TeX-master t)))

Blogging

I maintain a blog written in Jekyll. There are plenty of command-line tools to automate creating a new post, but staying in my editor minimizes friction and encourages me to write.

This defines a hrs/new-blog-post function, which prompts the user for a title and creates a new post (with a timestamped and slugged file name) in the blog’s _posts/ directory. The new post includes appropriate YAML header information.

(defvar hrs/jekyll-posts-directory "~/documents/blog/_posts/")
(defvar hrs/jekyll-post-extension ".md")

(defun hrs/replace-unusual-characters (title)
  "Replace characters that aren't alphanumeric with hyphens."
  (replace-regexp-in-string " " "-"
                            (downcase (replace-regexp-in-string "[^A-Za-z0-9 ]" " " title))))

(defun hrs/slug-for (title)
  "Given a blog post title, return a convenient URL slug.
  Downcase letters and remove special characters."
  (let ((slug (hrs/replace-unusual-characters title)))
    (while (string-match "--" slug)
      (setq slug (replace-regexp-in-string "--" "-" slug)))
    slug))

(defun hrs/timestamped-slug-for (title)
  "Turn a string into a slug with a timestamp and title."
  (concat (format-time-string "%Y-%m-%d")
          "-"
          (hrs/slug-for title)))

(defun hrs/jekyll-yaml-template (title)
  "Return the YAML header information appropriate for a blog
  post. Include the title, the current date, the post layout, and
  an empty list of tags."
  (concat
   "---\n"
   "title: " title "\n"
   "date: " (format-time-string "%Y-%m-%d") "\n"
   "layout: post\n"
   "tags: []\n"
   "---\n\n"))

(defun hrs/new-blog-post (title)
  "Create a new blog post in Jekyll."
  (interactive "sPost title: ")
  (let ((post (concat hrs/jekyll-posts-directory
                      (hrs/timestamped-slug-for title)
                      hrs/jekyll-post-extension)))
    (if (file-exists-p post)
        (find-file post)
      (find-file post)
      (insert (hrs/jekyll-yaml-template title)))))

dired

Load up the assorted dired extensions.

(require 'dired-x)
(require 'dired+)
(require 'dired-open)

Open media with the appropriate programs.

(setq dired-open-extensions
      '(("pdf" . "evince")
        ("mkv" . "vlc")
        ("mp4" . "vlc")
        ("avi" . "vlc")))

These are the switches that get passed to ls when dired gets a list of files. We’re using:

  • l: Use the long listing format.
  • h: Use human-readable sizes.
  • v: Sort numbers naturally.
  • A: Almost all. Doesn’t include ”.” or ”..”.
(setq-default dired-listing-switches "-lhvA")

Use “j” and “k” to move around in dired.

(evil-define-key 'normal dired-mode-map (kbd "j") 'dired-next-line)
(evil-define-key 'normal dired-mode-map (kbd "k") 'dired-previous-line)

Kill buffers of files/directories that are deleted in dired.

(setq dired-clean-up-buffers-too t)

Always copy directories recursively instead of asking every time.

(setq dired-recursive-copies 'always)

Ask before recursively deleting a directory, though.

(setq dired-recursive-deletes 'top)

Editing settings

Always kill current buffer

Assume that I always want to kill the current buffer when hitting C-x k.

(global-set-key (kbd "C-x k") 'hrs/kill-current-buffer)

Look for executables in /usr/local/bin.

(setq exec-path (append exec-path '("/usr/local/bin")))

Use company-mode everywhere

(add-hook 'after-init-hook 'global-company-mode)

Always indent with spaces

Never use tabs. Tabs are the devil’s whitespace.

(setq-default indent-tabs-mode nil)

Configure yasnippet

I keep my snippets in ~/.emacs/snippets/text-mode, and I always want yasnippet enabled.

(setq yas-snippet-dirs '("~/.emacs.d/snippets/text-mode"))
(yas-global-mode 1)

I don’t want ido to automatically indent the snippets it inserts. Sometimes this looks pretty bad (when indenting org-mode, for example, or trying to guess at the correct indentation for Python).

(setq yas/indent-line nil)

Configure abbrev-mode

My email address is too long, so I like to keep some personal information as abbreviations.

I’m prefixing them with semicolons to avoid collisions with real words.

(define-abbrev-table 'global-abbrev-table
  '((";name" "Harry R. Schwartz")
    (";email" "hello@harryrschwartz.com")
    (";tb" "harry@thoughtbot.com")
    (";site" "http://harryrschwartz.com")))

Always enable abbrev-mode:

(setq-default abbrev-mode t)

Configure ido

(setq ido-enable-flex-matching t)
(setq ido-everywhere t)
(ido-mode 1)
(ido-ubiquitous)
(flx-ido-mode 1) ; better/faster matching
(setq ido-create-new-buffer 'always) ; don't confirm to create new buffers
(ido-vertical-mode 1)
(setq ido-vertical-define-keys 'C-n-and-C-p-only)

Use smex to handle M-x with ido

(smex-initialize)

(global-set-key (kbd "M-x") 'smex)
(global-set-key (kbd "M-X") 'smex-major-mode-commands)

Editing with Markdown

Because I can’t always use org.

I’d like spell-checking running when editing Markdown.

(add-hook 'gfm-mode-hook 'flyspell-mode)

Associate .md files with GitHub-flavored Markdown.

(hrs/add-auto-mode 'gfm-mode "\\.md$")

Use pandoc to render the results.

(setq markdown-command "pandoc --standalone --mathjax --from=markdown")

Wrap paragraphs automatically

AutoFillMode automatically wraps paragraphs, kinda like hitting M-q. I wrap a lot of paragraphs, so this automatically wraps ‘em when I’m writing text, Markdown, or Org.

(add-hook 'text-mode-hook 'turn-on-auto-fill)
(add-hook 'gfm-mode-hook 'turn-on-auto-fill)
(add-hook 'org-mode-hook 'turn-on-auto-fill)

Sometimes, though, I don’t wanna wrap text. This toggles wrapping with C-c q:

(global-set-key (kbd "C-c q") 'auto-fill-mode)

Linting prose

I use proselint to check my prose for common errors. This creates a flycheck checker that runs proselint in texty buffers and displays my errors.

(require 'flycheck)

(flycheck-define-checker proselint
  "A linter for prose."
  :command ("proselint" source-inplace)
  :error-patterns
  ((warning line-start (file-name) ":" line ":" column ": "
            (id (one-or-more (not (any " "))))
            (message (one-or-more not-newline)
                     (zero-or-more "\n" (any " ") (one-or-more not-newline)))
            line-end))
  :modes (text-mode markdown-mode gfm-mode org-mode))

(add-to-list 'flycheck-checkers 'proselint)

Use flycheck in the appropriate buffers:

(add-hook 'markdown-mode-hook #'flycheck-mode)
(add-hook 'gfm-mode-hook #'flycheck-mode)
(add-hook 'text-mode-hook #'flycheck-mode)
(add-hook 'org-mode-hook #'flycheck-mode)

Enable region case modification

(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)

Switch and rebalance windows when splitting

When splitting a window, I invariably want to switch to the new window. This makes that automatic.

(defun hrs/split-window-below-and-switch ()
  "Split the window horizontally, then switch to the new pane."
  (interactive)
  (split-window-below)
  (balance-windows)
  (other-window 1))

(defun hrs/split-window-right-and-switch ()
  "Split the window vertically, then switch to the new pane."
  (interactive)
  (split-window-right)
  (balance-windows)
  (other-window 1))

(global-set-key (kbd "C-x 2") 'hrs/split-window-below-and-switch)
(global-set-key (kbd "C-x 3") 'hrs/split-window-right-and-switch)

Mass editing of grep results

I like the idea of mass editing grep results the same way I can edit filenames in dired. These keybindings allow me to use C-x C-q to start editing grep results and C-c C-c to stop, just like in dired.

(eval-after-load 'grep
  '(define-key grep-mode-map
    (kbd "C-x C-q") 'wgrep-change-to-wgrep-mode))

(eval-after-load 'wgrep
  '(define-key grep-mode-map
    (kbd "C-c C-c") 'wgrep-finish-edit))

(setq wgrep-auto-save-buffer t)

Configure wrap-region

(wrap-region-global-mode t)
(wrap-region-add-wrapper "/" "/" nil 'ruby-mode)
(wrap-region-add-wrapper "`" "`" nil '(markdown-mode ruby-mode))

Split horizontally for temporary buffers

Horizonal splits are nicer for me, since I usually use a wide monitor. This is handy for handling temporary buffers (like compilation or test output).

(defun hrs/split-horizontally-for-temp-buffers ()
  (when (one-window-p t)
    (split-window-horizontally)))

(add-hook 'temp-buffer-window-setup-hook
          'hrs/split-horizontally-for-temp-buffers)

Use projectile everywhere

(projectile-global-mode)

Add a bunch of engines for engine-mode

Enable engine-mode and define a few useful engines.

(require 'engine-mode)

(defengine duckduckgo
  "https://duckduckgo.com/?q=%s"
  :keybinding "d")

(defengine github
  "https://github.com/search?ref=simplesearch&q=%s"
  :keybinding "g")

(defengine google
  "http://www.google.com/search?ie=utf-8&oe=utf-8&q=%s")

(defengine rfcs
  "http://pretty-rfc.herokuapp.com/search?q=%s")

(defengine stack-overflow
  "https://stackoverflow.com/search?q=%s"
  :keybinding "s")

(defengine wikipedia
  "http://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s"
  :keybinding "w")

(defengine wiktionary
  "https://www.wikipedia.org/search-redirect.php?family=wiktionary&language=en&go=Go&search=%s")

(engine-mode t)

Set custom keybindings

Just a few handy functions.

(global-set-key (kbd "C-w") 'backward-kill-word)
(global-set-key (kbd "M-/") 'hippie-expand)
(global-set-key (kbd "M-o") 'other-window)

Remap when working in terminal Emacs.

(define-key input-decode-map "\e[1;2A" [S-up])