Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1057 lines (933 sloc) 31.9 KB

Geoff van der Meer’s Emacs configuration

Introduction

Using org-mode for emacs config feels like cheating. Thanks Michael for the inspiration!

Settings

Personal information

(setq user-full-name "Geoff van der Meer"
      user-mail-address "gmwils@gmail.com")

General settings

Debug

(setq debug-on-error t)

Turn of a range of things

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

(setq inhibit-startup-message t
      inhibit-startup-echo-area-message t)

Sentances end with a single space

(setq sentence-end-double-space nil)

Browser setup

(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "open")

Setup directories

Make it easy to reference the dotfiles.

(setq dotfiles-dir (file-name-directory
                    (or (buffer-file-name) load-file-name)))

Custom file

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

Enable package support

I set up packages, Melpa, and use-package bright and early so that I can make use of use-package’s bind-key macro.

When I first copy this set-up into a new machine, I still have to require package, add MELPA, initialize package, and grab use-package, and a couple of others from the package archives and the internet. This could be improved.

(require 'package)

(setq package-archives '(("ELPA" . "http://tromey.com/elpa/")
                         ("gnu" . "http://elpa.gnu.org/packages/")
                         ("org" . "http://orgmode.org/elpa/")
                         ("melpa" . "http://melpa.milkbox.net/packages/")
                         ("marmalade" . "http://marmalade-repo.org/packages/")))
(package-initialize)
(setq load-prefer-newer t)
(require 'use-package) ;; currently you have to evaluate everything up to here, and grab use-package manually :/

Paradox

(use-package paradox
  :ensure t
  :config
  (setq paradox-execute-asynchronously t))

Diminish

Reduce mode line clutter: http://www.emacswiki.org/emacs/DiminishedModes

(use-package diminish
  :ensure t
  :init
  (defmacro rename-modeline (package-name mode new-name)
    `(eval-after-load ,package-name
       '(defadvice ,mode (after rename-modeline activate)
          (setq mode-name ,new-name)))))

Emacs

Org

Babel

http://orgmode.org/org.html#Languages

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (R . t)
   (ruby . t)
   (sh . t)
   (python . t)
   (js . t)))

YouTube links

http://endlessparentheses.com/embedding-youtube-videos-with-org-mode-links.html

Example: yt:A3JAlWM8qRM

[[yt:A3JAlWM8qRM]]
(defvar yt-iframe-format
  ;; You may want to change your width and height.
  (concat "<iframe width=\"440\""
          " height=\"335\""
          " src=\"https://www.youtube.com/embed/%s\""
          " frameborder=\"0\""
          " allowfullscreen>%s</iframe>"))

(org-add-link-type
 "yt"
 (lambda (handle)
   (browse-url
    (concat "https://www.youtube.com/embed/"
            handle)))
 (lambda (path desc backend)
   (cl-case backend
     (html (format yt-iframe-format
                   path (or desc "")))
     (latex (format "\href{%s}{%s}"
                    path (or desc "video"))))))

ispell

Making ispell work in org-mode http://endlessparentheses.com/ispell-and-org-mode.html

(defun endless/org-ispell ()
  "Configure `ispell-skip-region-alist' for `org-mode'."
  (make-local-variable 'ispell-skip-region-alist)
  (add-to-list 'ispell-skip-region-alist '(org-property-drawer-re))
  (add-to-list 'ispell-skip-region-alist '("~" "~"))
  (add-to-list 'ispell-skip-region-alist '("=" "="))
  (add-to-list 'ispell-skip-region-alist '("^#\\+BEGIN_SRC" . "^#\\+END_SRC")))
(add-hook 'org-mode-hook #'endless/org-ispell)

Mac Settings

(when (eq system-type 'darwin)
  (set-default-font "-*-Menlo-normal-normal-normal-*-14-*-*-*-m-0-iso10646-1")
  ;; initial window
  (setq initial-frame-alist '((width . 120) (height . 50)))
  ;; defaults subsequent windows
  (setq default-frame-alist '((width . 120) (height . 50)))

  (load-theme 'tango-dark)
)

Scratch file

See: http://emacsredux.com/blog/2014/07/25/configure-the-scratch-buffers-mode/

(setq initial-scratch-message "")
(setq initial-major-mode 'lisp-interaction-mode)

Highlight trailing whitespace

(setq-default show-trailing-whitespace t)
(global-set-key "\C-c\C-w" 'whitespace-cleanup)

Search for occurance of symbol at point

C-s does normal search, C-u C-s will search for the current symbol that the cursor is on.

See: http://endlessparentheses.com/quickly-search-for-occurrences-of-the-symbol-at-point.html

(defun endless/isearch-symbol-with-prefix (p)
  "Like isearch, unless prefix argument is provided.
With a prefix argument P, isearch for the symbol at point."
  (interactive "P")
  (let ((current-prefix-arg nil))
    (call-interactively
     (if p #'isearch-forward-symbol-at-point
       #'isearch-forward))))

(global-set-key [remap isearch-forward]
                #'endless/isearch-symbol-with-prefix)

Auto fill

Always fill at 78 chars.

(add-hook 'text-mode-hook #'turn-on-auto-fill)
(setq-default fill-column 78)

File encoding

Set default file encoding to utf-8 (http://nakkaya.com/2009/11/29/emacs-and-international-characters/)

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

Dired

See: http://batsov.com/emacsredux/blog/2015/05/09/emacs-on-os-x/

And on Mac, install: brew install coreutils

(if (eq system-type 'darwin)
 (setq insert-directory-program (executable-find "gls"))
)

Tramp mode

http://www.gnu.org/software/tramp/#Connection-types

(setq tramp-default-method "scpx")

Change prompts

Make yes or no prompts be y or n prompts.

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

Backup files

Auto Save files to temp dir: http://emacswiki.org/emacs/AutoSave

(setq backup-directory-alist
      `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

Make backups of files, even when they’re in version control.

(setq vc-make-backup-files t)

Mode line

See: http://www.lunaryorn.com/2014/07/26/make-your-emacs-mode-line-more-useful.html

More compact position display.

(setq-default mode-line-position
              '((-3 "%p") (size-indication-mode ("/" (-4 "%I")))
                " "
                (line-number-mode
                 ("%l" (column-number-mode ":%c")))))

Mode line formating for projectile.

(defvar gmwils-projectile-mode-line
  '(:propertize
    (:eval (when (ignore-errors (projectile-project-root))
             (concat " " (projectile-project-name))))
    face font-lock-constant-face)
  "Mode line format for Projectile.")
(put 'gmwils-projectile-mode-line 'risky-local-variable t)

Shorter version control (VC) mode line.

(defvar gmwils-vc-mode-line
  '(" " (:propertize
         ;; Strip the backend name from the VC status information
         (:eval (let ((backend (symbol-name (vc-backend (buffer-file-name)))))
                  (substring vc-mode (+ (length backend) 2))))
         face font-lock-variable-name-face))
  "Mode line format for VC Mode.")
(put 'gmwils-vc-mode-line 'risky-local-variable t)

Set the actual mode line.

(setq-default mode-line-format
              '("%e" mode-line-front-space
                mode-line-mule-info
                mode-line-client
                mode-line-modified
                mode-line-remote
                mode-line-buffer-identification
                " " mode-line-position
                gmwils-projectile-mode-line
                gmwils-vc-mode-line
                (flycheck-mode flycheck-mode-line) ; Flycheck status
                " "
                mode-line-misc-info
                " "
                mode-line-modes
                battery-mode-line-string
                mode-line-end-spaces))

(display-battery-mode 1)
(setq battery-mode-line-format "%p%%") ; Default: "[%b%p%%]"

(diminish 'isearch-mode)

Packages

Smart tab

(use-package smart-tab
 :ensure t
 :diminish smart-tab-mode
 :init
 (global-smart-tab-mode 1))

String Inflection

Change case styles: https://github.com/akicho8/string-inflection

(use-package string-inflection
 :ensure t)

(global-unset-key (kbd "C-q"))
;; C-q C-u is the key bindings similar to Vz Editor.
(global-set-key (kbd "C-q C-u") 'my-string-inflection-cycle-auto)

(defun my-string-inflection-cycle-auto ()
  "switching by major-mode"
  (interactive)
  (cond
   ;; for emacs-lisp-mode
   ((eq major-mode 'emacs-lisp-mode)
    (string-inflection-all-cycle))
   ;; for java
   ((eq major-mode 'java-mode)
    (string-inflection-java-style-cycle))
   (t
    ;; default
    (string-inflection-ruby-style-cycle))))

Avy - jump within buffers

See: http://emacsredux.com/blog/2015/07/19/ace-jump-mode-is-dead-long-live-avy/

(use-package avy
 :ensure t
 :bind
 (("\C-cj" . avy-goto-word-or-subword-1)
  ("\C-c:" . avy-goto-word-or-subword-1)
  ("\C-cw" . ace-window)))

YAS snippet

Setup Yasnippet - http://github.com/capitaomorte/yasnippet

(use-package yasnippet
 :ensure t
 :diminish yas-minor-mode
 :init
 (yas-global-mode 1))

Save place

(use-package saveplace
  :init
  (setq-default save-place t)
  (setq save-place-file (expand-file-name ".places" user-emacs-directory)))

Markdown

(use-package markdown-mode
 :ensure t)

Git

Setup full screen magit-status: http://whattheemacsd.com/setup-magit.el-01.html

(use-package magit
 :ensure t
 :init
 (defadvice magit-status (around magit-fullscreen activate)
   (window-configuration-to-register :magit-fullscreen)
   ad-do-it
   (delete-other-windows))

 (defun magit-quit-session ()
   "Restores the previous window configuration and kills the magit buffer"
   (interactive)
   (kill-buffer)
   (jump-to-register :magit-fullscreen))

)

ESS

Emacs statistics mode.

(use-package ess
 :ensure t
 :init
 (setq ess-ask-for-ess-directory nil)
 (require 'ess-site))

Projectile mode

https://github.com/bbatsov/projectile

(use-package projectile
 :ensure t
 :diminish projectile-mode
 :bind ("M-p" . projectile-find-file)
 :init
 (projectile-global-mode)
 (setq projectile-enable-caching t))

Asciidoc mode

(use-package adoc-mode
 :ensure t
 :init
 (add-to-list 'auto-mode-alist '("\\.doc$" . adoc-mode))
 (add-to-list 'auto-mode-alist '("\\.asciidoc$" . adoc-mode)))

IDO mode

IDO lets you interactively do things with files and buffers.

(setq ido-enable-flex-matching t
      ido-everywhere t
      ido-use-faces nil ;; disable ido faces to see flx highlights.
      ido-create-new-buffer 'always)

;; suppress  "reference to free variable problems"
(setq ido-cur-item nil
      ido-context-switch-command nil
      ido-cur-list nil
      ido-default-item nil)

(ido-mode 1)

(use-package ido-ubiquitous
  :ensure t
  :init
  (ido-ubiquitous-mode 1))
(use-package flx-ido
  :ensure t
  :init
  (setq flx-ido-threshold 1000)
  (flx-ido-mode 1))

Protobuf

(use-package protobuf-mode
 :ensure t)

Deft

Deft setup - http://jblevins.org/projects/deft/

(use-package deft
 :ensure t
 :init
 (setq
  deft-extension "org"
  deft-directory "~/Notes/"
  deft-text-mode 'org-mode))

Appearance

(use-package color-theme
 :ensure t)

; TODO - (require 'color-theme-ir-black)

Paredit

(use-package paredit
 :ensure t
 :init
(defun conditionally-enable-paredit-mode ()
  "Enable `paredit-mode' in the minibuffer, during `eval-expression'."
  (if (eq this-command 'eval-expression)
      (paredit-mode 1)))
(add-hook 'minibuffer-setup-hook 'conditionally-enable-paredit-mode))

Dash

https://github.com/stanaka/dash-at-point#readme

(use-package dash-at-point
 :ensure t
 :bind (("\C-cd" . dash-at-point)))

Other

(require 'dired-x)

;; Hive / Hadoop
(add-to-list 'auto-mode-alist '("\\.hql$" . sql-mode))

Virtual env mode for Python

Development

General settings

Default to unified diffs

(setq diff-switches "-u -w")

File associations

(add-to-list 'auto-mode-alist '("\\.css$" . css-mode))
(add-to-list 'auto-mode-alist '("\\.ya?ml$" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
(add-to-list 'auto-mode-alist '("Rakefile$" . ruby-mode))
(add-to-list 'auto-mode-alist '("\\.js\\(on\\)?$" . js-mode))
(add-to-list 'auto-mode-alist '("\\.xml$" . nxml-mode))
(add-to-list 'auto-mode-alist '("\\.md$" . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.markdown$" . markdown-mode))

Flycheck

(use-package flycheck
  :ensure t
  :init
  (global-flycheck-mode))

Grep

(eval-after-load 'grep
  '(when (boundp 'grep-find-ignored-files)
    (add-to-list 'grep-find-ignored-files "target")
    (add-to-list 'grep-find-ignored-files "*.class")))

Highlight parens

(show-paren-mode 1)

Open compressed files

(auto-compression-mode t)

Seed the random number generator

(random t)

Unicode

Ensure we’re using UTF8 as a default

(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(ansi-color-for-comint-mode-on)

Extra settings

(set-default 'indent-tabs-mode nil)
(set-default 'indicate-empty-lines t)
(set-default 'imenu-auto-rescan t)

Languages

C family

(use-package google-c-style
  :ensure t
  :config
  (add-hook 'c-mode-common-hook 'google-set-c-style)
  (add-hook 'c-mode-common-hook 'google-make-newline-indent)
  (add-hook 'java-mode-hook 'google-set-c-style)
  (add-hook 'java-mode-hook 'google-make-newline-indent))

(defun flymake-cpplint-init ()
  (list "cpplint" (list (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))))

Haskell

Wiki: https://github.com/haskell/haskell-mode/wiki Tutorial: https://github.com/serras/emacs-haskell-tutorial/blob/master/tutorial.md

(use-package haskell-mode
 :ensure t)

(add-hook 'haskell-mode-hook 'turn-on-haskell-indentation)
(add-hook 'haskell-mode-hook 'interactive-haskell-mode)

;; Setup cabal
(let ((my-cabal-path (expand-file-name "~/.cabal/bin")))
  (setenv "PATH" (concat my-cabal-path ":" (getenv "PATH")))
  (add-to-list 'exec-path my-cabal-path))
(custom-set-variables '(haskell-tags-on-save t))

(custom-set-variables
  '(haskell-process-suggest-remove-import-lines t)
  '(haskell-process-auto-import-loaded-modules t)
  '(haskell-process-log t))

(eval-after-load 'haskell-mode
  '(progn
     (define-key haskell-mode-map (kbd "C-c C-l") 'haskell-process-load-or-reload)
     (define-key haskell-mode-map (kbd "C-c `") 'haskell-interactive-bring)
     (define-key haskell-mode-map (kbd "C-c C-t") 'haskell-process-do-type)
     (define-key haskell-mode-map (kbd "C-c C-i") 'haskell-process-do-info)
     (define-key haskell-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
     (define-key haskell-mode-map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
     (define-key haskell-mode-map (kbd "C-c c") 'haskell-process-cabal)
     (define-key haskell-mode-map (kbd "SPC") 'haskell-mode-contextual-space)
  ))

(eval-after-load 'haskell-cabal
  '(progn
     (define-key haskell-cabal-mode-map (kbd "C-c `") 'haskell-interactive-bring)
     (define-key haskell-cabal-mode-map (kbd "C-c C-z") 'haskell-interactive-switch)
     (define-key haskell-cabal-mode-map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
     (define-key haskell-cabal-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
     (define-key haskell-cabal-mode-map (kbd "C-c c") 'haskell-process-cabal)))

(custom-set-variables '(haskell-process-type 'cabal-repl))

(eval-after-load 'haskell-mode
  '(define-key haskell-mode-map (kbd "C-c C-o") 'haskell-compile))
(eval-after-load 'haskell-cabal
  '(define-key haskell-cabal-mode-map (kbd "C-c C-o") 'haskell-compile))

Javascript

(autoload 'js2-mode "js2-mode" nil t)
(add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))

(use-package json-mode
 :ensure t)

;; Fix the crappy indentation of js2-mode
(defun my-js2-indent-function ()
  (interactive)
  (save-restriction
    (widen)
    (let* ((inhibit-point-motion-hooks t)
           (parse-status (save-excursion (syntax-ppss (point-at-bol))))
           (offset (- (current-column) (current-indentation)))
           (indentation (espresso--proper-indentation parse-status))
           node)

      (save-excursion

        ;; consecutive declarations in a var statement are nice if
        ;; properly aligned, i.e:
        ;;
        ;; var foo = "bar",
        ;;     bar = "foo";
        (setq node (js2-node-at-point))
        (when (and node
                   (= js2-NAME (js2-node-type node))
                   (= js2-VAR (js2-node-type (js2-node-parent node))))
          (setq indentation (+ 4 indentation))))

      (indent-line-to indentation)
      (when (> offset 0) (forward-char offset)))))

(defun my-indent-sexp ()
  (interactive)
  (save-restriction
    (save-excursion
      (widen)
      (let* ((inhibit-point-motion-hooks t)
             (parse-status (syntax-ppss (point)))
             (beg (nth 1 parse-status))
             (end-marker (make-marker))
             (end (progn (goto-char beg) (forward-list) (point)))
             (ovl (make-overlay beg end)))
        (set-marker end-marker end)
        (overlay-put ovl 'face 'highlight)
        (goto-char beg)
        (while (< (point) (marker-position end-marker))
          ;; don't reindent blank lines so we don't set the "buffer
          ;; modified" property for nothing
          (beginning-of-line)
          (unless (looking-at "\\s-*$")
            (indent-according-to-mode))
          (forward-line))
        (run-with-timer 0.5 nil '(lambda(ovl)
                                   (delete-overlay ovl)) ovl)))))


(defun my-js2-mode-hook ()
  (require 'espresso)
  ;; (setq espresso-indent-level javascript-indent
  ;;       indent-tabs-mode nil
  ;;       c-basic-offset javascript-indent)
  (c-toggle-auto-state 0)
  (c-toggle-hungry-state 1)
  (set (make-local-variable 'indent-line-function) 'my-js2-indent-function)
  (define-key js2-mode-map [(meta control |)] 'cperl-lineup)
  (define-key js2-mode-map [(meta control \;)]
    '(lambda()
       (interactive)
       (insert "/* -----[ ")
       (save-excursion
         (insert " ]----- */"))
       ))
  (define-key js2-mode-map [(return)] 'newline-and-indent)
  (define-key js2-mode-map [(backspace)] 'c-electric-backspace)
  (define-key js2-mode-map [(control d)] 'c-electric-delete-forward)
  (define-key js2-mode-map [(control meta q)] 'my-indent-sexp)
  (if (featurep 'js2-highlight-vars)
    (js2-highlight-vars-mode)))

(add-hook 'js2-mode-hook 'my-js2-mode-hook)

Lisp

(define-key read-expression-map (kbd "TAB") 'lisp-complete-symbol)
(define-key lisp-mode-shared-map (kbd "C-c l") "lambda")
(define-key lisp-mode-shared-map (kbd "RET") 'reindent-then-newline-and-indent)
(define-key lisp-mode-shared-map (kbd "C-\\") 'lisp-complete-symbol)
(define-key lisp-mode-shared-map (kbd "C-c v") 'eval-buffer)

;;; Enhance Lisp Modes

(eval-after-load 'paredit
  ;; need a binding that works in the terminal
  '(define-key paredit-mode-map (kbd "M-)") 'paredit-forward-slurp-sexp))

(defun turn-on-paredit ()
  (paredit-mode t))

(dolist (x '(scheme emacs-lisp lisp clojure))
  (when window-system
    (font-lock-add-keywords
     (intern (concat (symbol-name x) "-mode"))
     '(("(\\|)" . 'esk-paren-face))))
  (add-hook
   (intern (concat (symbol-name x) "-mode-hook")) 'turn-on-paredit))

(eval-after-load 'clojure-mode
  '(font-lock-add-keywords
    'clojure-mode `(("(\\(fn\\>\\)"
                     (0 (progn (compose-region (match-beginning 1)
                                               (match-end 1) "ƒ")
                               nil))))))

(setq inferior-lisp-program "browser-repl")

Eldoc

Turn on eldoc - http://emacswiki.org/emacs/ElDoc

(autoload 'turn-on-eldoc-mode "eldoc" nil t)
(diminish 'eldoc-mode)
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
(add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode)
(add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)

PHP

(setq auto-mode-alist (cons '("\\.php$" . php-mode) auto-mode-alist))
(autoload 'php-mode "php-mode" "PHP editing mode." t)

Python

(use-package python-mode
 :ensure t
 :init)

(setq auto-mode-alist (cons '("\\.py$" . python-mode) auto-mode-alist))
(setq auto-mode-alist (cons '("\\.tac$" . python-mode) auto-mode-alist))
(setq interpreter-mode-alist (cons '("python" . python-mode)
				   interpreter-mode-alist))
(autoload 'python-mode "python-mode" "Python editing mode." t)

Ruby

(eval-after-load 'ruby-mode
  '(progn
     ;; work around possible elpa bug
     (ignore-errors (require 'ruby-compilation))
     (setq ruby-use-encoding-map nil)
     (define-key ruby-mode-map (kbd "RET") 'reindent-then-newline-and-indent)
     (define-key ruby-mode-map (kbd "C-M-h") 'backward-kill-word)
     (define-key ruby-mode-map (kbd "C-c l") "lambda")))


;; Rake files are ruby, too, as are gemspecs, rackup files, etc.
(add-to-list 'auto-mode-alist '("\\.rake$" . ruby-mode))
(add-to-list 'auto-mode-alist '("\\.gemspec$" . ruby-mode))
(add-to-list 'auto-mode-alist '("\\.ru$" . ruby-mode))
(add-to-list 'auto-mode-alist '("Rakefile$" . ruby-mode))
(add-to-list 'auto-mode-alist '("Gemfile$" . ruby-mode))
(add-to-list 'auto-mode-alist '("Capfile$" . ruby-mode))
(add-to-list 'auto-mode-alist '("Vagrantfile$" . ruby-mode))

Web mode

(use-package web-mode
  :ensure web-mode
  :init (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
  :config
  (progn
    (setq web-mode-markup-indent-offset 2)
    (setq web-mode-enable-auto-pairing t)
    (setq web-mode-enable-current-element-highlight t)
    (setq web-mode-ac-sources-alist
          '(("css" . (ac-source-css-property))
            ("html" . (ac-source-words-in-buffer ac-source-abbrev)))
          )))

Custom functions

Auto commenting

From: http://endlessparentheses.com/implementing-comment-line.html

(defun gmwils/comment-line (n)
  "Comment or uncomment current line and leave point after it.
   With positive prefix, apply to N lines including current one.
   With negative prefix, apply to -N lines above."
  (interactive "p")
  (let ((range (list (line-beginning-position)
                     (goto-char (line-end-position n)))))
    (comment-or-uncomment-region
     (apply #'min range)
     (apply #'max range)))
  (forward-line 1)
  (back-to-indentation))

(defun gmwils/comment-line-or-region (n)
  "Comment or uncomment current line and leave point after it.
With positive prefix, apply to N lines including current one.
With negative prefix, apply to -N lines above.
If region is active, apply to active region instead."
  (interactive "p")
  (if (use-region-p)
      (comment-or-uncomment-region
       (region-beginning) (region-end))
    (let ((range
           (list (line-beginning-position)
                 (goto-char (line-end-position n)))))
      (comment-or-uncomment-region
       (apply #'min range)
       (apply #'max range)))
    (forward-line 1)
    (back-to-indentation)))
(global-set-key (kbd "C-x /") #'gmwils/comment-line-or-region)

Alternate vesion, lifted from the textmate package.

;;; allow-line-as-region-for-function adds an "-or-line" version of
;;; the given comment function which (un)comments the current line is
;;; the mark is not active.  This code comes from Aquamac's osxkeys.el
;;; and is licensed under the GPL
(defmacro allow-line-as-region-for-function (orig-function)
`(defun ,(intern (concat (symbol-name orig-function) "-or-line"))
   ()
   ,(format "Like `%s', but acts on the current line if mark is not active."
            orig-function)
   (interactive)
   (if mark-active
       (call-interactively (function ,orig-function))
     (save-excursion
       ;; define a region (temporarily) -- so any C-u prefixes etc. are preserved.
       (beginning-of-line)
       (set-mark (point))
       (end-of-line)
       (call-interactively (function ,orig-function))))))

(defun textmate-define-comment-line ()
  "Add or-line (un)comment function if not already defined"
  (unless (fboundp 'comment-or-uncomment-region-or-line)
    (allow-line-as-region-for-function comment-or-uncomment-region)))

(textmate-define-comment-line)
(global-set-key (kbd "C-X /") 'comment-or-uncomment-region-or-line)

Copy filename to clipboard

http://emacsredux.com/blog/2013/03/27/copy-filename-to-the-clipboard/

(defun copy-file-name-to-clipboard ()
  "Copy the current buffer file name to the clipboard."
  (interactive)
  (let ((filename (if (equal major-mode 'dired-mode)
                      default-directory
                    (buffer-file-name))))
    (when filename
      (kill-new filename)
      (message "Copied buffer file name '%s' to the clipboard." filename))))

Open browser with URL

(defun browse-url-default-macosx-browser (url &optional new-window)
  (interactive (browse-url-interactive-arg "URL: "))
  (if (and new-window (>= emacs-major-version 23))
      (ns-do-applescript
       (format (concat "tell application \"Safari\" to make document with properties {URL:\"%s\"}\n"
                       "tell application \"Safari\" to activate") url))
    (start-process (concat "open " url) nil "open" url)))

Run test

A function to run the unit test assocated with the current file.

  • TODO(gmwils): Run cucumber if editing features
  • TODO(gmwils): use different test runners based on extension (eg. rspec, py, etc)
  • TODO(gmwils): if file is ROOT/a/b/file, then try ROOT/test/a/b/test_file
(defun string/ends-with (s ending)
  "return non-nil if string S ends with ENDING."
  (let ((elength (length ending)))
            (string= (substring s (- 0 elength)) ending)))

(defun find-test-file (f)
  "find the equivalent test file in the current project"
  (let ((test-dir (concat (textmate-find-project-root) "/test/"))
        (filename (file-name-nondirectory (file-name-sans-extension f)))
        (ext (file-name-extension f)))
    (concat test-dir filename "_test." ext)))

(defun test-file-p (f)
  "return non-nil if file is a test file"
  (string/ends-with (file-name-sans-extension f) "test"))

(defun test-file-name (f)
  "return a test file or nil if none found."
  (if (or (eq f nil) (test-file-p f))
      f
      (find-test-file f)))

(defun run-test-from-file (f)
  "given a file, run tests on it"
  (let ((base-dir (textmate-find-project-root))
        (virtenv (file-name-nondirectory (getenv "VIRTUAL_ENV"))))
    (if (and (not (eq f nil)) (file-readable-p f))
        (shell-command (concat "("
                               "cd " base-dir "; "
                               "source ~/.virtualenvs/" virtenv "/bin/activate; "
                               "PYTHONPATH=\"" base-dir ";$PYTHONPATH\" py.test " f ")"))
        (message "Unable to run test for file %s" f))))

(defun run-test ()
  "run tests based on the current buffer"
  (interactive)
  (save-buffer)
  (run-test-from-file (test-file-name (buffer-file-name))))

(global-set-key (kbd "C-t") 'run-test)

Unfill paragraph

http://emacswiki.org/emacs/UnfillParagraph

(defun 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)))
(define-key global-map "\M-Q" 'unfill-paragraph)

Rename current file

From: http://emacsredux.com/blog/2013/05/04/rename-file-and-buffer/

(defun rename-file-and-buffer ()
  "Rename the current buffer and file it is visiting."
  (interactive)
  (let ((filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (message "Buffer is not visiting a file!")
      (let ((new-name (read-file-name "New name: " filename)))
        (cond
         ((vc-backend filename) (vc-rename-file filename new-name))
         (t
          (rename-file filename new-name t)
          (set-visited-file-name new-name t t)))))))

(global-set-key (kbd "C-x C-r") 'rename-file-and-buffer)

Correct DOuble CAPS

From: http://emacs.stackexchange.com/questions/13970/fixing-double-capitals-as-i-type/13975#13975

(defun dcaps-to-scaps ()
  "Convert word in DOuble CApitals to Single Capitals."
  (interactive)
  (and (= ?w (char-syntax (char-before)))
       (save-excursion
         (and (if (called-interactively-p)
                  (skip-syntax-backward "w")
                (= -3 (skip-syntax-backward "w")))
              (let (case-fold-search)
                (looking-at "\\b[[:upper:]]\\{2\\}[[:lower:]]"))
              (capitalize-word 1)))))

(add-hook 'post-self-insert-hook #'dcaps-to-scaps nil 'local)

(define-minor-mode dubcaps-mode
  "Toggle `dubcaps-mode'. Converts words in DOuble CApitals to
Single Capitals as you type."
  :init-value nil
  :lighter (" DC")
  (if dubcaps-mode
      (add-hook 'post-self-insert-hook #'dcaps-to-scaps nil 'local)
    (remove-hook 'post-self-insert-hook #'dcaps-to-scaps 'local)))

(add-hook 'text-mode-hook #'dubcaps-mode)

Key bindings

Key bindings that aren’t included elsewhere.

(progn
  ;; Font size
  (define-key global-map (kbd "C-+") 'text-scale-increase)
  (define-key global-map (kbd "C--") 'text-scale-decrease)

  ;; Use regex searches by default.
  (global-set-key (kbd "C-s") 'isearch-forward-regexp)
  (global-set-key (kbd "\C-r") 'isearch-backward-regexp)
  (global-set-key (kbd "C-M-s") 'isearch-forward)
  (global-set-key (kbd "C-M-r") 'isearch-backward)

  ;; M-S-6 is awkward
  (global-set-key (kbd "C-c q") 'join-line)
  (global-set-key (kbd "M-j")
            (lambda ()
                  (interactive)
                  (join-line -1)))


  ;; Cycle between windows - https://github.com/garybernhardt/dotfiles/blob/master/.emacs
  (global-set-key (kbd "C-o") 'other-window)
  (defun prev-window ()
    (interactive)
    (other-window -1))
  (global-set-key "\M-o" 'prev-window)

  ;; Help should search more than just commands
  (global-set-key (kbd "C-h a") 'apropos)

  (global-set-key (kbd "C-x m") 'magit-status)
  (global-set-key (kbd "M-s")   'fixup-whitespace)

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