Skip to content

Latest commit

 

History

History
772 lines (682 loc) · 28.3 KB

emacs-init.org

File metadata and controls

772 lines (682 loc) · 28.3 KB

Literate Emacs Configuration

Preliminaries

(if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))
(if (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(if (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(defun in-emacs-dir (path)
  (concat user-emacs-directory path))
(defun in-org-dir (path)
  (concat "~/Dropbox/org/" path))

Customizations

Don’t pollute init.el with GUI-made customizations.

This is also used for theme loading, as well. It needs to be loaded early on; otherwise loading themes via el-get mechanisms bites because you have to continually accept their loading.

(setq custom-file (in-emacs-dir "custom.el"))
(load custom-file)

Path

Apparently we need to use --login (instead of -i) to also capture any PATH entries that OS X brings in via it’s /usr/libexec/path_helper utility, which is only invoked for login shells. This will incorporate any settings from ~/.bash_profile as well, so I just source ~/.bashrc from there to get everything at once.

This is needed for (among other things), making sure ispell=/=aspell is available for Flyspell to work properly.

As this sets `exec-path`, it needs to come early on, so we can use `executable-find` to guard el-get recipe includes.

(I swiped this code from Steve Purcell’s emacs.d)

(defun set-exec-path-from-shell-PATH ()
  (let ((path-from-shell (shell-command-to-string "$SHELL --login -c 'echo $PATH'")))
    (setenv "PATH" path-from-shell)
    (setq exec-path (split-string path-from-shell path-separator))))

(if window-system
    (set-exec-path-from-shell-PATH))

el-get

Taken from the instructions in the el-get README file:
(add-to-list 'load-path (in-emacs-dir "el-get/el-get"))

(unless (require 'el-get nil 'noerror)
  (with-current-buffer
      (url-retrieve-synchronously
       "https://raw.github.com/dimitri/el-get/master/el-get-install.el")
    (goto-char (point-max))
    (eval-print-last-sexp)))

(add-to-list 'el-get-recipe-path (in-emacs-dir "recipes"))

;; (Optional) configuration for these packages is found in the
;; "init-<package>.el" files in `el-get-user-package-directory`
(setq el-get-user-package-directory (in-emacs-dir "init"))
(setq my-packages '(
                    diminish
                    flycheck
                    smartparens
                    smex
                    yasnippet
                    ;; gnuplot-mode
                    color-theme-zenburn
                    js2-mode
                    yaml-mode


                    company-mode

                    helm
                    helm-ls-git
                    helm-git-grep
                    helm-company

                    nyan-mode
                    sqitch

                    projectile

                    rainbow-delimiters

                    rust-mode
                    ))

(when (executable-find "git")
  (add-to-list 'my-packages 'magit)
  (add-to-list 'my-packages 'gist))

(when (executable-find "markdown")
  (add-to-list 'my-packages 'markdown-mode))

(when (executable-find "ruby")
  (add-to-list 'my-packages 'ruby-mode)
  (add-to-list 'my-packages 'ruby-tools)
  (add-to-list 'my-packages 'auto-complete-ruby))

(when (executable-find "elixir")
  (add-to-list 'my-packages 'elixir-mode)
  (add-to-list 'my-packages 'alchemist))

(el-get 'sync my-packages)

Default Configurations

Miscellaneous Configurations

(setq initial-major-mode 'org-mode)
(set-face-attribute 'default nil :family "Anonymous Pro" :height 150)
(setq inhibit-startup-screen t ;; There's no good reason to keep the startup screen.
      next-line-add-newlines t
      delete-by-moving-to-trash t ;; use system trash directory
      trash-directory "~/.Trash"
      line-number-mode t
      column-number-mode t
      size-indication-mode t
      require-final-newline t ;; All files end with newlines
      ;; Send backups and autosaves to the temp folder instead of polluting the current directory.
      backup-directory-alist `((".*" . ,temporary-file-directory))
      auto-save-file-name-transforms `((".*" ,temporary-file-directory t))
      create-lockfiles nil
      echo-keystrokes 0.1
      font-lock-maximum-decoration t
      visible-bell t)
(add-hook 'after-save-hook
          'executable-make-buffer-file-executable-if-script-p)

(global-linum-mode 1)

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

(global-auto-revert-mode t)

;; http://whattheemacsd.com/sane-defaults.el-01.html
;; Auto-refresh dired buffers, too
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)

;; From http://eden.rutgers.edu/~mangesh/emacs.html
(setq-default frame-title-format
 (list '((buffer-file-name " %f" (dired-directory
                                  dired-directory
                                  (revert-buffer-function " %b"
                                  ("%b - Dir:  " default-directory)))))))

(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)

(define-key global-map (kbd "C-+") 'text-scale-increase)
;; Set to = so you don't have to hold the Shift key, and
;; increase/decrease are symmetric
(define-key global-map (kbd "C-=") 'text-scale-increase)
(define-key global-map (kbd "C--") 'text-scale-decrease)

;; Also toss in OS X bindings for the hell of it
(define-key global-map (kbd "s-=") 'text-scale-increase)
(define-key global-map (kbd "s--") 'text-scale-decrease)

;; Get back to baseline quickly
;;
;; TODO: It'd be good to find a better keybinding for this, though
(define-key global-map (kbd "s-0")
  (lambda ()
    (interactive)
    (text-scale-set 0)))

;; Apparently this RET and C-j were swapped recently
(define-key global-map (kbd "C-j") 'newline-and-indent)

Why WOULDN’T you have this?!

(nyan-mode)

Server

(server-start)

Ido Mode

(require 'ido)
(ido-mode t)
(setq ido-enable-flex-matching t)

Jump-to-symbol

;; I got this from Seth Falcon
(require 'imenu)

(defun prelude-ido-goto-symbol (&optional symbol-list)
  "Refresh imenu and jump to a place in the buffer using Ido."
  (interactive)
  (unless (featurep 'imenu)
    (require 'imenu nil t))
  (cond
   ((not symbol-list)
    (let ((ido-mode ido-mode)
          (ido-enable-flex-matching
           (if (boundp 'ido-enable-flex-matching)
               ido-enable-flex-matching t))
          name-and-pos symbol-names position)
      (unless ido-mode
        (ido-mode 1)
        (setq ido-enable-flex-matching t))
      (while (progn
               (imenu--cleanup)
               (setq imenu--index-alist nil)
               (prelude-ido-goto-symbol (imenu--make-index-alist))
               (setq selected-symbol
                     (ido-completing-read "Symbol? " symbol-names))
               (string= (car imenu--rescan-item) selected-symbol)))
      (unless (and (boundp 'mark-active) mark-active)
        (push-mark nil t nil))
      (setq position (cdr (assoc selected-symbol name-and-pos)))
      (cond
       ((overlayp position)
        (goto-char (overlay-start position)))
       (t
        (goto-char position)))))
   ((listp symbol-list)
    (dolist (symbol symbol-list)
      (let (name position)
        (cond
         ((and (listp symbol) (imenu--subalist-p symbol))
          (prelude-ido-goto-symbol symbol))
         ((listp symbol)
          (setq name (car symbol))
          (setq position (cdr symbol)))
         ((stringp symbol)
          (setq name symbol)
          (setq position
                (get-text-property 1 'org-imenu-marker symbol))))
        (unless (or (null position) (null name)
                    (string= (car imenu--rescan-item) name))
          (add-to-list 'symbol-names name)
          (add-to-list 'name-and-pos (cons name position))))))))

;; Jump to a definition in the current file. (This is awesome.)
(global-set-key (kbd "M-i") 'prelude-ido-goto-symbol)

SavePlace

(require 'saveplace)
(setq save-place-file (in-emacs-dir "saveplace"))
(setq-default save-place t)

EShell: The Emacs Shell

(global-set-key "\C-xt" 'eshell)

Tabs

(setq-default indent-tabs-mode nil)
(setq tab-width 4)

Whitespace

(global-set-key [f5] 'whitespace-mode)
(add-hook 'before-save-hook 'whitespace-cleanup)
(add-hook 'makefile-mode-hook
          (lambda ()
            (whitespace-mode t)))
;; http://xahlee.org/emacs/whitespace-mode.html
(setq whitespace-display-mappings
      '((space-mark 32 [183] [46]) ; normal space, ·
        (space-mark 160 [164] [95])
        (space-mark 2208 [2212] [95])
        (space-mark 2336 [2340] [95])
        (space-mark 3616 [3620] [95])
        (space-mark 3872 [3876] [95])
        (newline-mark 10 [182 10]) ; newlne, ¶
        (tab-mark 9 [9655 9] [92 9]) ; tab, ▷
        ))

Sizing

(global-set-key (kbd "S-C-<left>") 'shrink-window-horizontally)
(global-set-key (kbd "S-C-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "S-C-<down>") 'shrink-window)
(global-set-key (kbd "S-C-<up>") 'enlarge-window)

Auto-fill

(defun cwm-auto-fill-comments ()
  (setq comment-auto-fill-only-comments t)
  (auto-fill-mode))
  

Spelling

Install aspell on Macs using Homebrew:
brew install aspell --with-lang-en
  
(defun cwm-flyspell-comments ()
  (flyspell-prog-mode))
  

Helm

Helm is kinda awesome.

I’m going to try replacing Emacs’ built-in file finder with Helm’s.

(global-set-key (kbd "C-x C-f") 'helm-find-files)
  

This is from helm-ls-git. Another fun thing is C-], which toggles full paths on and off.

(global-set-key (kbd "C-x C-d") 'helm-browse-project)
  

This sets up Helm in the M-x prompt! SQUEE!

(global-set-key (kbd "M-x") 'helm-M-x)
  

Holy crap, this is a much better way to interact with the kill ring!

(global-set-key (kbd "M-y") 'helm-show-kill-ring)
  

Waaaaay better way to interact with currently-open buffers!

(global-set-key (kbd "C-x b") 'helm-mini)
  

Custom Functions

cwm-insert-date

Inspired by Xah Lee’s work at http://ergoemacs.org/emacs/elisp_datetime.html, along with the source code to calendar-day-of-year-string

(defun cwm-insert-date ()
  "Insert current date string"
  (interactive)
  (when (use-region-p)
    (delete-region (region-beginning)
                   (region-end)))
  (let* ((today     (calendar-current-date))
         (year      (calendar-extract-year today))
         (month     (calendar-extract-month today))
         (date      (calendar-extract-day today))
         (day       (calendar-day-number today))
         (day-name  (calendar-day-name today))
         (eoy       (list 12 31 year))
         (num-days  (calendar-day-number eoy))
         (remaining (- (calendar-day-number eoy) day)))
    (insert (format "%d-%02d-%02d, %s; Day %d of %d (%d remaining in %d)"
                    year month date day-name day num-days remaining year))))
(defun cwm-today ()
  "Insert current date string"
  (interactive)
  (when (use-region-p)
    (delete-region (region-beginning)
                   (region-end)))
  (insert (format-time-string "%Y-%m-%d")))
(global-set-key (kbd "<f6>") 'cwm-insert-date)
(global-set-key (kbd "C-<f6>") 'cwm-today)

cwm-work-payday

Adapted from a reply to an org-mode mailing list post of mine from 4 years ago (which I forgot I had even posted!)

Intended to be used as an org-mode sexp recurring date entry (e.g., SCHEDULED: <%%(cwm-work-payday date>)

(defun cwm-work-payday (date)
  "Returns TRUE if DATE is one of my bi-monthly paydays: the
15th (or nearest preceeding workday) and the last workday of the
month. This doesn't take into account company holidays"
  (let* ((dayname (calendar-day-of-week date))
         (is-weekday (memq dayname '(1 2 3 4 5)))
         (is-friday (= dayname 5))
         (day (calendar-extract-day date))
         (month (calendar-extract-month date))
         (year (calendar-extract-year date))
         (lastday (calendar-last-day-of-month month year))
         (is-last-day (= day lastday))
         (last-two-days-before-last-day (list (- lastday 2) (- lastday 1))))
    (or (and (= day 15) is-weekday)
        (and (memq day '(13 14)) is-friday)
        (and is-last-day is-weekday)
        (and (memq day last-two-days-before-last-day) is-friday))))

Multi-hook

Based (sort of) on http://stackoverflow.com/a/7400476

This allows you to add a function as a hook to multiple modes at once. It’s handy for setting up, say, all your programming modes with flyspell and autofill enabled in comment sections.

(defun cwm-add-to-hooks (function modes)
  "Adds FUNCTION to the hooks of each one of MODES"
  (mapc (lambda (mode)
          (add-hook (intern (concat (symbol-name mode) "-hook"))
                    function))
        modes))

Languages

Erlang

On OS X, I install Erlang from source; a vanilla install goes here by default:
     (setq erlang-root-dir "/Users/cm/src/erlang/otp_src_R16B03")
;;     (setq erlang-root-dir (getenv "ERL_TOP"))

The rest of this configuration is taken from the Erlang documentation.

     (add-to-list 'load-path (concat erlang-root-dir "/lib/tools/emacs"))
     (add-to-list 'exec-path (concat erlang-root-dir "/bin"))

     (add-to-list 'auto-mode-alist '("\\.erl?$" . erlang-mode))
     (add-to-list 'auto-mode-alist '("\\.hrl?$" . erlang-mode))

     (require 'erlang-start)
;;     (require 'erlang-flymake)

Lisps

SLIME

(global-set-key "\C-cs" 'slime-selector)

AutoComplete in SLIME

Steve Purcell made this snazzy add-on for AutoComplete to use SLIME symbols.

(load-vendor-package "ac-slime")

(require 'ac-slime)
(add-hook 'slime-mode-hook 'set-up-slime-ac)
(add-hook 'slime-repl-mode-hook 'set-up-slime-ac)

Emacs Lisp

It’s nice to have Paredit in Emacs Lisp, no?

(add-hook 'emacs-lisp-mode-hook
          'enable-paredit-mode)

Clojure

All The Modes

(require 'clojure-test-mode)
(require 'clojurescript-mode)

Durendal

(require 'durendal)
(add-hook 'clojure-mode-hook 'durendal-enable-auto-compile)
(add-hook 'slime-repl-mode-hook 'durendal-slime-repl-paredit)
(add-hook 'sldb-mode-hook 'durendal-dim-sldb-font-lock)
;;  (add-hook 'slime-compilation-finished-hook 'durendal-hide-successful-compile)

SLIME

Make it pretty.

(add-hook 'slime-repl-mode-hook
          'clojure-mode-font-lock-setup)

Miscellaneous

align-cljlet is a neat little package that allows you to neatly align entries in Clojure bindings and literal hashes.

(load-vendor-package "align-cljlet")
(require 'align-cljlet)

(define-key clojure-mode-map (kbd "C-c C-c a") 'align-cljlet)

Scheme

Instructions for running Petite Chez Scheme through Emacs from Indiana University (and they should know!).

(I’m using Petite Chez Scheme, because that’s what Dan Friedman and William Byrd used at Clojure Conj, 2011; if it’s good enough for them, it’s good enough for me.)

(autoload 'scheme-mode "cmuscheme"
  "Major mode for Scheme." t)
(autoload 'run-scheme "cmuscheme"
  "Switch to interactive Scheme buffer." t)
(add-to-list 'auto-mode-alist
             '("\\.ss" . scheme-mode)
             '("\\.scm" . scheme-mode))

Make Emacs invoke Petite Chez Scheme when running M-x run-scheme (assumes petite is on your path):

(custom-set-variables '(scheme-program-name "petite"))

Add some special indentation rules for Kanren / miniKanren function calls.

(put 'fresh 'scheme-indent-function 1)
(put 'run 'scheme-indent-function 2)

Can’t forget Paredit!

(add-hook 'scheme-mode-hook
          'enable-paredit-mode)

Cross-mode Configuration

(setq my-programming-modes
      '(ruby-mode
        erlang-mode))
(cwm-add-to-hooks 'cwm-flyspell-comments
                  my-programming-modes)
(cwm-add-to-hooks 'cwm-auto-fill-comments
                  my-programming-modes)

Org Mode

(require 'org-mobile)
(require 'org-habit)

(defun org-file (filename-without-extension)
  (concat org-directory "/" filename-without-extension ".org"))

;; Stole this next bit from the INFO pages
(defun org-summary-todo (n-done n-not-done)
  "Switch entry to DONE when all subentries are done, to TODO otherwise."
  (let (org-log-done org-log-states)   ; turn off logging
    (org-todo (if (= n-not-done 0) "DONE" "TODO"))))

(add-hook 'org-mode-hook 'turn-on-visual-line-mode)
(add-hook 'org-mode-hook 'turn-on-flyspell 'append)
(add-hook 'org-mode-hook (lambda ()
                           (auto-fill-mode 1)))

(add-hook 'org-after-todo-statistics-hook 'org-summary-todo)

(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cb" 'org-iswitchb)
(global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-cl" 'org-store-link)

(global-set-key (kbd "<f9>") 'org-mobile-push)
(global-set-key (kbd "S-<f9>") 'org-mobile-pull)
(global-set-key (kbd "<f11>") 'org-agenda-clock-in)
(global-set-key (kbd "<f12>") 'org-agenda-clock-out)

(setq org-blank-before-new-entry nil
      org-directory "~/Dropbox/org"
      org-mobile-files `(,org-directory)
      org-mobile-directory "~/Dropbox/MobileOrg"
      org-mobile-inbox-for-pull (org-file "from-inbox")
      org-agenda-files `(,org-directory)
      org-agenda-span 'day
      org-agenda-skip-deadline-if-done t
      org-agenda-skip-scheduled-if-done t
      org-agenda-skip-scheduled-if-deadline-is-shown 'not-today
      org-agenda-include-diary nil
      org-agenda-log-mode-items '(closed clock)
      org-agenda-custom-commands '(("p" . "Priorities")
                                   ("pa" "A items" tags-todo "+PRIORITY=\"A\""
                                    ((org-agenda-todo-ignore-scheduled 'future)
                                     (org-agenda-tags-todo-honor-ignore-options t)))
                                   ("pb" "B items" tags-todo "+PRIORITY=\"B\""
                                    ((org-agenda-todo-ignore-scheduled 'future)
                                     (org-agenda-tags-todo-honor-ignore-options t)))
                                   ("pc" "C items" tags-todo "+PRIORITY=\"C\""
                                    ((org-agenda-todo-ignore-scheduled 'future)
                                     (org-agenda-tags-todo-honor-ignore-options t)))
                                   ("w" "Things I'm Waiting On" todo "WAITING")
                                   ("e" "Errands" tags-todo "errands|shopping"
                                    ((org-agenda-todo-ignore-scheduled 'future)
                                     (org-agenda-tags-todo-honor-ignore-options t)))
                                   ("r" "Refile" tags "+REFILE")
                                   ("z" "By Date"
                                    ((agenda "Dead" ((org-agenda-entry-types '(:deadline))
                                                     (org-agenda-sorting-strategy '(priority-down category-keep))))
                                     (agenda "Do" ((org-agenda-entry-types '(:scheduled))
                                                   (org-agenda-sorting-strategy '(priority-down category-keep))))))
                                   ("f" "Financial Work" agenda ""
                                    ((org-agenda-files `(,(org-file "financial")))))
                                   ("W" . "Work Projects")
                                   ("We" "Work" agenda ""
                                    ((org-agenda-files `(,(org-file "opscode")))
                                     (org-agenda-sorting-strategy '(priority-down effort-down)))))
      org-default-notes-file (org-file "inbox")
      org-capture-templates '(("s" "Shopping")
                              ("sg" "Groceries" entry
                               (file+headline (org-file "shopping") "Groceries")
                               "* TODO %? %^G\n")
                              ("ss" "General Shopping" entry
                               (file+headline (org-file "shopping") "Other Things To Buy")
                               "* TODO %? %^G\n")
                              ("t" "General TODO" entry
                               (file org-default-notes-file)
                               "* TODO %?\n%U\n%a" :clock-in t :clock-resume t)
                              ("w" "Work Tasks" entry
                               (file+headline (org-file "opscode") "Daily Catch-All")
                               "* TODO %?"))
      org-enforce-todo-dependencies t
      org-todo-keywords '((sequence "TODO(t)"
                                    "STARTED(s!)"
                                    "WAITING(w@/!)"
                                    "APPT(a)"
                                    "|"
                                    "DONE(d!)"
                                    "CANCELLED(c@)"
                                    "DEFERRED(f@)"))
      org-treat-S-cursor-todo-selection-as-state-change nil
      org-use-fast-todo-selection t
      org-use-property-inheritance t
      org-refile-use-outline-path 'file
      org-refile-allow-creating-parent-nodes 'confirm
      org-refile-targets '((org-agenda-files . (:maxlevel . 5))
                           (nil . (:maxlevel . 5)))
      org-clock-out-remove-zero-time-clocks t
      org-clock-persist t
      org-completion-use-ido t
      org-deadline-warning-days 3
      org-hide-leading-stars t
      org-log-done 'note
      org-log-into-drawer t
      org-outline-path-complete-in-steps t
      org-hierarchical-todo-statistics nil
      org-src-fontify-natively t
      org-src-tab-acts-natively t
      org-src-window-setup 'current-window
      org-use-sub-superscripts '{}
      org-habit-graph-column 60)

(org-clock-persistence-insinuate)

(defun cwm-work-tasks-done-last-workday ()
  "Produces an org agenda tags view list of the work tasks
    completed on the last work day (i.e., yesterday, or last Friday
    if today is Monday or Sunday).  Good for daily stand-up meetings."
  (interactive)
  (let* ((day-of-week (calendar-day-of-week (calendar-current-date)))
         (start-day (calendar-current-date (cond ((= day-of-week 1) -3)     ;; if Monday, go back 3 days to Friday
                                                 ((= day-of-week 0) -2)     ;; if Sunday, go back 2 days to Friday
                                                 (t                 -1))))  ;; otherwise, use yesterday
         (end-day (calendar-current-date (cond ((= day-of-week 1) -2)       ;; if Monday, go back 2 days to Saturday
                                               ((= day-of-week 0) -1)       ;; if Sunday, go back 1 day to Saturday
                                               (t                  0)))))   ;; otherwise, use today
    (org-tags-view nil
                   (concat "CATEGORY=\"work\"+TODO=\"DONE\""
                           (format "+CLOSED>=\"[%d-%02d-%02d]\""
                                   (calendar-extract-year start-day)
                                   (calendar-extract-month start-day)
                                   (calendar-extract-day start-day))
                           (format "+CLOSED<=\"[%d-%02d-%02d]\""
                                   (calendar-extract-year end-day)
                                   (calendar-extract-month end-day)
                                   (calendar-extract-day end-day))))))

(org-babel-do-load-languages
 'org-babel-load-languages
 '((clojure . t)
   (sh . t)
   (dot . t)
   (js . t)))

Registers

Jump to files with a simple C-x r j <register>.

(set-register ?i `(file . ,(in-emacs-dir "emacs-init.org")))
(set-register ?z `(file . "~/.zshrc"))
(set-register ?w `(file . ,(in-org-dir "work_log.org")))
(set-register ?t `(file . ,(in-org-dir "todo.org")))
(set-register ?d `(file . ,(in-org-dir "daily.org")))