Alain Leufroy avatar Alain Leufroy committed 85bd3dd

initial sources from emacsrocks

https://github.com/magnars/.emacs.d/

Comments (0)

Files changed (265)

+# My emacs settings
+
+An ever-changing set of emacs settings. Micro-optimizations are super fun.
+These are used in the [Emacs Rocks](http://emacsrocks.com) screencasts.
+You can also see some thoughts behind the settings on my [What the .emacs.d!?](http://whattheemacsd.com)-blog.
+
+## Setup
+
+To grab all the dependencies:
+
+    git clone --recursive git://github.com/magnars/.emacs.d.git
+
+The first time you start emacs, it will install some additional packages
+that are best handled by the package manager.
+
+## Install emacs on mac
+
+I use Cocoa Emacs, installed like this:
+
+    brew install emacs --cocoa
+
+To open it with Alfred or Quicksilver, you have to copy `Emacs.app` into
+`/Applications` instead of the symlink that brew places there.
+
+## Tips for using these emacs settings
+
+If you want to use my settings straight out of the box, here are some things to note:
+
+ * I recommend starting with a blank emacs +
+   [Technomancy's better-defaults package](https://github.com/technomancy/better-defaults),
+   and then dig through this repo for useful nuggets, instead of forking it directly.
+
+ * The key bindings are optimized for a norwegian keyboard layout.
+
+ * Start by reading up on all the cool stuff in key-bindings.el.
+
+ * You quit emacs with `C-x r q`, mnemonic *Really Quit*.
+
+ * Find file in project with `C-x o`, in dir with `C-x C-f`, recent with `C-x f`
+
+ * Add your user- and project-specific stuff in .emacs.d/users/[machine name]/*.el
+
+ * `C-h` is rebound to backspace, like in the shell. Get help on `F1` instead.
+
+ * Autocomplete with `C-.` (autocomplete entire lines with `C-:`)
+
+ * expand-region is your friend. Find its bound key by doing `F1 f er/expand-region`
+
+ * Undo with `C-_` and redo with `M-_`. Watch the undo-tree with `C-x u`
+
+ * Quickly jump anywhere in the buffer with `C-ø` then the starting letter of a word.
+
+ * Indent and clean up white space in the entire buffer with `C-c n`
+
+ * On a mac, the Meta key `M` is bound to Command.
+
+ * I recommend rebinding Caps Lock to Ctrl and use that instead of the often badly placed Ctrl-key.
+
+ * Watch [emacsrocks.com](http://emacsrocks.com)
+
+## Survival guide for the first week of emacs
+
+When you start using emacs for the first time, your habits fight you every inch
+of the way. Your fingers long for the good old familiar keybindings. Here's an
+overview of the most commonly used shortcuts to get you through this pain:
+
+* `C      ` Shorthand for the ctrl-key
+* `M      ` Shorthand for the meta-key (bound to cmd on my mac settings)
+* `S      ` Shorthand for the shift-key
+
+### Files
+
+* `C-x C-f` Open a file. Starts in the current directory
+* `C-x f  ` Open a recently visited file
+* `C-x o  ` Open a file in the current project (based on .git ++)
+* `C-x C-s` Save this file
+* `C-x C-w` Save as ...
+* `C-x C-j` Jump to this files' current directory
+* `C-x b  ` Switch to another open file (buffer)
+* `C-x C-b` List all open files (buffers)
+
+### Cut copy and paste
+
+* `C-space` Start marking stuff. C-g to cancel.
+* `C-w    ` Cut (aka kill)
+* `C-k    ` Cut till end of line
+* `M-w    ` Copy
+* `C-y    ` Paste (aka yank)
+* `M-y    ` Cycle last paste through previous kills
+* `C-x C-y` Choose what to paste from previous kills
+* `C-@    ` Mark stuff quickly. Press multiple times
+
+### General
+
+* `C-g    ` Quit out of whatever mess you've gotten yourself into
+* `M-x    ` Run a command by name
+* `C-.    ` Autocomplete
+* `C-_    ` Undo
+* `M-_    ` Redo
+* `C-x u  ` Show the undo-tree
+* `C-x m  ` Open magit. It's a magical git interface for emacs
+
+### Navigation
+
+* `C-arrow` Move past words/paragraphs
+* `C-a    ` Go to start of line
+* `C-e    ` Go to end of line
+* `M-g M-g` Go to line number
+* `C-x C-i` Go to symbol
+* `C-s    ` Search forward. Press `C-s` again to go further.
+* `C-r    ` Search backward. Press `C-r` again to go further.
+
+### Window management
+
+* `C-x 0  ` Close this window
+* `C-x 1  ` Close other windows
+* `C-x 2  ` Split window horizontally
+* `C-x 3  ` Split window vertically
+* `S-arrow` Jump to window to the left/right/up/down
+
+### Help
+
+* `F1 t   ` Basic tutorial
+* `F1 k   ` Help for a keybinding
+* `F1 r   ` Emacs' extensive documentation
+(setq visible-bell t
+      font-lock-maximum-decoration t
+      color-theme-is-global t
+      truncate-partial-width-windows nil)
+
+;; Highlight current line
+(global-hl-line-mode 1)
+
+;; Set custom theme path
+(setq custom-theme-directory (concat user-emacs-directory "themes"))
+
+(dolist
+    (path (directory-files custom-theme-directory t "\\w+"))
+  (when (file-directory-p path)
+    (add-to-list 'custom-theme-load-path path)))
+
+;; Default theme
+(defun use-presentation-theme ()
+  (interactive)
+  (disable-theme 'default-black)
+  (load-theme 'prez)
+  (when (boundp 'magnars/presentation-font)
+    (set-face-attribute 'default nil :font magnars/presentation-font)))
+
+(defun use-default-theme ()
+  (interactive)
+  (disable-theme 'prez)
+  (load-theme 'default-black)
+  (when (boundp 'magnars/default-font)
+    (set-face-attribute 'default nil :font magnars/default-font)))
+
+(defun toggle-presentation-mode ()
+  (interactive)
+  (if (string= (frame-parameter nil 'font) magnars/default-font)
+      (use-presentation-theme)
+    (use-default-theme)))
+
+(global-set-key (kbd "C-<f9>") 'toggle-presentation-mode)
+
+(use-default-theme)
+
+;; Don't defer screen updates when performing operations
+(setq redisplay-dont-pause t)
+
+;; org-mode colors
+(setq org-todo-keyword-faces
+      '(
+        ("INPR" . (:foreground "yellow" :weight bold))
+        ("DONE" . (:foreground "green" :weight bold))
+        ("IMPEDED" . (:foreground "red" :weight bold))
+        ))
+
+;; Highlight matching parentheses when the point is on them.
+(show-paren-mode 1)
+
+(when window-system
+  (setq frame-title-format '(buffer-file-name "%f" ("%b")))
+  (tooltip-mode -1)
+  (blink-cursor-mode -1))
+
+;; Make zooming affect frame instead of buffers
+(require 'zoom-frm)
+
+;; Unclutter the modeline
+(require 'diminish)
+(eval-after-load "yasnippet" '(diminish 'yas-minor-mode))
+(eval-after-load "eldoc" '(diminish 'eldoc-mode))
+(eval-after-load "paredit" '(diminish 'paredit-mode))
+(eval-after-load "tagedit" '(diminish 'tagedit-mode))
+(eval-after-load "elisp-slime-nav" '(diminish 'elisp-slime-nav-mode))
+(eval-after-load "skewer-mode" '(diminish 'skewer-mode))
+(eval-after-load "skewer-css" '(diminish 'skewer-css-mode))
+(eval-after-load "skewer-html" '(diminish 'skewer-html-mode))
+(eval-after-load "smartparens" '(diminish 'smartparens-mode))
+(eval-after-load "guide-key" '(diminish 'guide-key-mode))
+
+(defmacro rename-modeline (package-name mode new-name)
+  `(eval-after-load ,package-name
+     '(defadvice ,mode (after rename-modeline activate)
+        (setq mode-name ,new-name))))
+
+(rename-modeline "js2-mode" js2-mode "JS2")
+(rename-modeline "clojure-mode" clojure-mode "Clj")
+
+(provide 'appearance)
+(custom-set-variables
+ ;; custom-set-variables was added by Custom.
+ ;; If you edit it by hand, you could mess it up, so be careful.
+ ;; Your init file should contain only one such instance.
+ ;; If there is more than one, they won't work right.
+ '(custom-safe-themes (quote ("9527feeeec43970b1d725bdc04e97eb2b03b15be982ac50089ad223d3c6f2920" "c03d60937e814932cd707a487676875457e0b564a615c1edfd453f23b06fe879" "f3ec2da81c2b1f66f911fe47843a09055754b40fafaddcce79bbd4d781161329" "30c6aef3025cd6f05ccb611ec8838a448a14a6784987ed98b24f78916d63b388" "84ff07913c6109d12bfda40644daeaaa8f4665afb5f04e13e422bd98b02ee88b" "cf33119622dd833e4d8f904f34c5e3ff95d1d3d45bada72dd44648b3470bdebe" "f5776f3da6117901f29405fe52edb2bcba6a687629b4cbd5923d1a642484f2f9" "d56e289b10204629ac5c35b9621a650a534ef3baf183a1c601b4936482321df1" "50ceca952b37826e860867d939f879921fac3f2032d8767d646dd4139564c68a" "ff73e1b0216feca9e041dcb3196938442cc6aa8319f97eedbc2a3e38c8ca9825" "a18dd0a954ac63a80e62c8cb1b550ffcf5d8461189c7c672555faadf2facfcf3" "cb36f8e44d41595010baa23737984c4ecb2d8cc2e363ec15fbfa0408c2f8ea9f" "ea0c5df0f067d2e3c0f048c1f8795af7b873f5014837feb0a7c8317f34417b04" "9f42bccce1e13fa5017eb8718574db099e85358b9f424db78e7318f86d1be08f" default)))
+ '(ido-use-filename-at-point nil)
+ '(safe-local-variable-values (quote ((eval font-lock-add-keywords nil (quote (("defexamples\\|def-example-group\\| => " (0 (quote font-lock-keyword-face)))))) (eval when (and (buffer-file-name) (file-regular-p (buffer-file-name)) (string-match-p "^[^.]" (buffer-file-name))) (emacs-lisp-mode)) (eval font-lock-add-keywords nil (quote (("defexamples\\| => " (0 (quote font-lock-keyword-face)))))) (encoding . utf-8)))))
+
+(custom-set-faces
+ ;; custom-set-faces was added by Custom.
+ ;; If you edit it by hand, you could mess it up, so be careful.
+ ;; Your init file should contain only one such instance.
+ ;; If there is more than one, they won't work right.
+ '(js2-error-face ((t nil)) t)
+ '(js2-warning-face ((t nil)) t))

defuns/buffer-defuns.el

+;; Buffer-related defuns
+
+(require 'imenu)
+
+(defvar buffer-local-mode nil)
+(make-variable-buffer-local 'buffer-local-mode)
+
+(defun mode-keymap (mode-sym)
+  (symbol-value (intern (concat (symbol-name mode-sym) "-map"))))
+
+(defun* buffer-local-set-key (key action)
+  (when buffer-local-mode
+    (define-key (mode-keymap buffer-local-mode)
+      key action)
+    (return-from set-key-buffer-local))
+  (let* ((mode-name-loc (gensym "-blm")))
+    (eval `(define-minor-mode ,mode-name-loc nil nil nil (make-sparse-keymap)))
+    (setq buffer-local-mode mode-name-loc)
+    (funcall mode-name-loc 1)
+    (define-key (mode-keymap mode-name-loc) key action)))
+
+(defun create-scratch-buffer nil
+  "create a new scratch buffer to work in. (could be *scratch* - *scratchX*)"
+  (interactive)
+  (let ((n 0)
+        bufname)
+    (while (progn
+             (setq bufname (concat "*scratch"
+                                   (if (= n 0) "" (int-to-string n))
+                                   "*"))
+             (setq n (1+ n))
+             (get-buffer bufname)))
+    (switch-to-buffer (get-buffer-create bufname))
+    (emacs-lisp-mode)
+    ))
+
+(defun split-window-right-and-move-there-dammit ()
+  (interactive)
+  (split-window-right)
+  (windmove-right))
+
+(defun toggle-window-split ()
+  (interactive)
+  (if (= (count-windows) 2)
+      (let* ((this-win-buffer (window-buffer))
+             (next-win-buffer (window-buffer (next-window)))
+             (this-win-edges (window-edges (selected-window)))
+             (next-win-edges (window-edges (next-window)))
+             (this-win-2nd (not (and (<= (car this-win-edges)
+                                         (car next-win-edges))
+                                     (<= (cadr this-win-edges)
+                                         (cadr next-win-edges)))))
+             (splitter
+              (if (= (car this-win-edges)
+                     (car (window-edges (next-window))))
+                  'split-window-horizontally
+                'split-window-vertically)))
+        (delete-other-windows)
+        (let ((first-win (selected-window)))
+          (funcall splitter)
+          (if this-win-2nd (other-window 1))
+          (set-window-buffer (selected-window) this-win-buffer)
+          (set-window-buffer (next-window) next-win-buffer)
+          (select-window first-win)
+          (if this-win-2nd (other-window 1))))))
+
+(defun rotate-windows ()
+  "Rotate your windows"
+  (interactive)
+  (cond ((not (> (count-windows)1))
+         (message "You can't rotate a single window!"))
+        (t
+         (setq i 1)
+         (setq numWindows (count-windows))
+         (while  (< i numWindows)
+           (let* (
+                  (w1 (elt (window-list) i))
+                  (w2 (elt (window-list) (+ (% i numWindows) 1)))
+
+                  (b1 (window-buffer w1))
+                  (b2 (window-buffer w2))
+
+                  (s1 (window-start w1))
+                  (s2 (window-start w2))
+                  )
+             (set-window-buffer w1  b2)
+             (set-window-buffer w2 b1)
+             (set-window-start w1 s2)
+             (set-window-start w2 s1)
+             (setq i (1+ i)))))))
+
+(defun ido-imenu ()
+  "Update the imenu index and then use ido to select a symbol to navigate to.
+Symbols matching the text at point are put first in the completion list."
+  (interactive)
+  (imenu--make-index-alist)
+  (let ((name-and-pos '())
+        (symbol-names '()))
+    (flet ((addsymbols (symbol-list)
+                       (when (listp symbol-list)
+                         (dolist (symbol symbol-list)
+                           (let ((name nil) (position nil))
+                             (cond
+                              ((and (listp symbol) (imenu--subalist-p symbol))
+                               (addsymbols 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))
+                               (add-to-list 'symbol-names name)
+                               (add-to-list 'name-and-pos (cons name position))))))))
+      (addsymbols imenu--index-alist))
+    ;; If there are matching symbols at point, put them at the beginning of `symbol-names'.
+    (let ((symbol-at-point (thing-at-point 'symbol)))
+      (when symbol-at-point
+        (let* ((regexp (concat (regexp-quote symbol-at-point) "$"))
+               (matching-symbols (delq nil (mapcar (lambda (symbol)
+                                                     (if (string-match regexp symbol) symbol))
+                                                   symbol-names))))
+          (when matching-symbols
+            (sort matching-symbols (lambda (a b) (> (length a) (length b))))
+            (mapc (lambda (symbol) (setq symbol-names (cons symbol (delete symbol symbol-names))))
+                  matching-symbols)))))
+    (let* ((selected-symbol (ido-completing-read "Symbol? " symbol-names))
+           (position (cdr (assoc selected-symbol name-and-pos))))
+      (push-mark (point))
+      (goto-char position))))
+
+(defun untabify-buffer ()
+  (interactive)
+  (untabify (point-min) (point-max)))
+
+(defun indent-buffer ()
+  (interactive)
+  (indent-region (point-min) (point-max)))
+
+(defun cleanup-buffer ()
+  "Perform a bunch of operations on the whitespace content of a buffer.
+Including indent-buffer, which should not be called automatically on save."
+  (interactive)
+  (untabify-buffer)
+  (delete-trailing-whitespace)
+  (indent-buffer))
+
+(defun file-name-with-one-directory (file-name)
+  (concat (cadr (reverse (split-string file-name "/"))) "/"
+          (file-name-nondirectory file-name)))
+
+(require 's)
+
+(defvar user-home-directory (concat (expand-file-name "~") "/"))
+
+(defun shorter-file-name (file-name)
+  (s-chop-prefix user-home-directory file-name))
+
+(defun recentf--file-cons (file-name)
+  (cons (shorter-file-name file-name) file-name))
+
+(defun recentf-ido-find-file ()
+  "Find a recent file using ido."
+  (interactive)
+  (let* ((recent-files (mapcar 'recentf--file-cons recentf-list))
+         (files (mapcar 'car recent-files))
+         (file (completing-read "Choose recent file: " files)))
+    (find-file (cdr (assoc file recent-files)))))

defuns/clj-defuns.el

+(require 's)
+
+(defun clj--src-file-name-from-test (name)
+  (s-with name
+    (s-replace "/test/" "/src/")
+    (s-replace "_test.clj" ".clj")))
+
+(defun clj--test-file-name-from-src (name)
+  (s-with name
+    (s-replace "/src/" "/test/")
+    (s-replace ".clj" "_test.clj")))
+
+(defun clj-other-file-name ()
+  (let ((name (buffer-file-name)))
+    (if (string-match-p "/test/" name)
+        (clj--src-file-name-from-test name)
+      (clj--test-file-name-from-src name))))
+
+(defun clj-jump-to-other-file (arg)
+  (interactive "P")
+  (let ((file (clj-other-file-name)))
+    (if (or (file-exists-p file) arg)
+        (find-file file)
+      (error "%s not found." file))))
+
+(defun clj-jump-to-other-file-other-window (arg)
+  (interactive "P")
+  (let ((file (clj-other-file-name)))
+    (if (or (file-exists-p file) arg)
+        (find-file-other-window file)
+      (error "%s not found." file))))

defuns/editing-defuns.el

+;;; editing-defuns.el --- Basic text editing defuns -*- lexical-binding: t; -*-
+
+(defun open-line-below ()
+  (interactive)
+  (end-of-line)
+  (newline)
+  (indent-for-tab-command))
+
+(defun open-line-above ()
+  (interactive)
+  (beginning-of-line)
+  (newline)
+  (forward-line -1)
+  (indent-for-tab-command))
+
+(defun new-line-in-between ()
+  (interactive)
+  (newline)
+  (save-excursion
+    (newline)
+    (indent-for-tab-command))
+  (indent-for-tab-command))
+
+(defun new-line-dwim ()
+  (interactive)
+  (let ((break-open-pair (or (and (looking-back "{") (looking-at "}"))
+                             (and (looking-back ">") (looking-at "<"))
+                             (and (looking-back "\\[") (looking-at "\\]")))))
+    (newline)
+    (when break-open-pair
+      (save-excursion
+        (newline)
+        (indent-for-tab-command)))
+    (indent-for-tab-command)))
+
+(defun duplicate-current-line-or-region (arg)
+  "Duplicates the current line or region ARG times.
+If there's no region, the current line will be duplicated."
+  (interactive "p")
+  (if (region-active-p)
+      (let ((beg (region-beginning))
+            (end (region-end)))
+        (duplicate-region arg beg end)
+        (one-shot-keybinding "d" (λ (duplicate-region 1 beg end))))
+    (duplicate-current-line arg)
+    (one-shot-keybinding "d" 'duplicate-current-line)))
+
+(defun one-shot-keybinding (key command)
+  (set-temporary-overlay-map
+   (let ((map (make-sparse-keymap)))
+     (define-key map (kbd key) command)
+     map) t))
+
+(defun replace-region-by (fn)
+  (let* ((beg (region-beginning))
+         (end (region-end))
+         (contents (buffer-substring beg end)))
+    (delete-region beg end)
+    (insert (funcall fn contents))))
+
+(defun duplicate-region (&optional num start end)
+  "Duplicates the region bounded by START and END NUM times.
+If no START and END is provided, the current region-beginning and
+region-end is used."
+  (interactive "p")
+  (save-excursion
+   (let* ((start (or start (region-beginning)))
+          (end (or end (region-end)))
+          (region (buffer-substring start end)))
+     (goto-char end)
+     (dotimes (i num)
+       (insert region)))))
+
+(defun duplicate-current-line (&optional num)
+  "Duplicate the current line NUM times."
+  (interactive "p")
+  (save-excursion
+   (when (eq (point-at-eol) (point-max))
+     (goto-char (point-max))
+     (newline)
+     (forward-char -1))
+   (duplicate-region num (point-at-bol) (1+ (point-at-eol)))))
+
+;; automatically indenting yanked text if in programming-modes
+
+(require 'dash)
+
+(defvar yank-indent-modes '(prog-mode
+                            sgml-mode
+                            js2-mode)
+  "Modes in which to indent regions that are yanked (or yank-popped)")
+
+(defvar yank-advised-indent-threshold 1000
+  "Threshold (# chars) over which indentation does not automatically occur.")
+
+(defun yank-advised-indent-function (beg end)
+  "Do indentation, as long as the region isn't too large."
+  (if (<= (- end beg) yank-advised-indent-threshold)
+      (indent-region beg end nil)))
+
+(defadvice yank (after yank-indent activate)
+  "If current mode is one of 'yank-indent-modes, indent yanked text (with prefix arg don't indent)."
+  (if (and (not (ad-get-arg 0))
+           (--any? (derived-mode-p it) yank-indent-modes))
+      (let ((transient-mark-mode nil))
+        (yank-advised-indent-function (region-beginning) (region-end)))))
+
+(defadvice yank-pop (after yank-pop-indent activate)
+  "If current mode is one of 'yank-indent-modes, indent yanked text (with prefix arg don't indent)."
+  (if (and (not (ad-get-arg 0))
+           (member major-mode yank-indent-modes))
+      (let ((transient-mark-mode nil))
+        (yank-advised-indent-function (region-beginning) (region-end)))))
+
+(defun yank-unindented ()
+  (interactive)
+  (yank 1))
+
+;; toggle quotes
+
+(defun current-quotes-char ()
+  (nth 3 (syntax-ppss)))
+
+(defalias 'point-is-in-string-p 'current-quotes-char)
+
+(defun move-point-forward-out-of-string ()
+  (while (point-is-in-string-p) (forward-char)))
+
+(defun move-point-backward-out-of-string ()
+  (while (point-is-in-string-p) (backward-char)))
+
+(defun alternate-quotes-char ()
+  (if (eq ?' (current-quotes-char)) ?\" ?'))
+
+(defun toggle-quotes ()
+  (interactive)
+  (if (point-is-in-string-p)
+      (let ((old-quotes (char-to-string (current-quotes-char)))
+            (new-quotes (char-to-string (alternate-quotes-char)))
+            (start (make-marker))
+            (end (make-marker)))
+        (save-excursion
+          (move-point-forward-out-of-string)
+          (backward-delete-char 1)
+          (set-marker end (point))
+          (insert new-quotes)
+          (move-point-backward-out-of-string)
+          (delete-char 1)
+          (insert new-quotes)
+          (set-marker start (point))
+          (replace-string new-quotes (concat "\\" new-quotes) nil start end)
+          (replace-string (concat "\\" old-quotes) old-quotes nil start end)))
+    (error "Point isn't in a string")))
+
+;; kill region if active, otherwise kill backward word
+
+(defun kill-region-or-backward-word ()
+  (interactive)
+  (if (region-active-p)
+      (kill-region (region-beginning) (region-end))
+    (backward-kill-word 1)))
+
+(defun kill-to-beginning-of-line ()
+  (interactive)
+  (kill-region (save-excursion (beginning-of-line) (point))
+               (point)))
+
+;; copy region if active
+;; otherwise copy to end of current line
+;;   * with prefix, copy N whole lines
+
+(defun copy-to-end-of-line ()
+  (interactive)
+  (kill-ring-save (point)
+                  (line-end-position))
+  (message "Copied to end of line"))
+
+(defun copy-whole-lines (arg)
+  "Copy lines (as many as prefix argument) in the kill ring"
+  (interactive "p")
+  (kill-ring-save (line-beginning-position)
+                  (line-beginning-position (+ 1 arg)))
+  (message "%d line%s copied" arg (if (= 1 arg) "" "s")))
+
+(defun copy-line (arg)
+  "Copy to end of line, or as many lines as prefix argument"
+  (interactive "P")
+  (if (null arg)
+      (copy-to-end-of-line)
+    (copy-whole-lines (prefix-numeric-value arg))))
+
+(defun save-region-or-current-line (arg)
+  (interactive "P")
+  (if (region-active-p)
+      (kill-ring-save (region-beginning) (region-end))
+    (copy-line arg)))
+
+(defun kill-and-retry-line ()
+  "Kill the entire current line and reposition point at indentation"
+  (interactive)
+  (back-to-indentation)
+  (kill-line))
+
+(defun camelize-buffer ()
+  (interactive)
+  (goto-char 0)
+  (ignore-errors
+    (replace-next-underscore-with-camel 0))
+  (goto-char 0))
+
+;; kill all comments in buffer
+(defun comment-kill-all ()
+  (interactive)
+  (save-excursion
+    (goto-char (point-min))
+    (comment-kill (save-excursion
+                    (goto-char (point-max))
+                    (line-number-at-pos)))))
+
+(require 's)
+
+(defun incs (s &optional num)
+  (let* ((inc (or num 1))
+         (new-number (number-to-string (+ inc (string-to-number s))))
+         (zero-padded? (s-starts-with? "0" s)))
+    (if zero-padded?
+        (s-pad-left (length s) "0" new-number)
+      new-number)))
+
+(defun change-number-at-point (arg)
+  (interactive "p")
+  (unless (or (looking-at "[0-9]")
+              (looking-back "[0-9]"))
+    (error "No number to change at point"))
+  (save-excursion
+    (while (looking-back "[0-9]")
+      (forward-char -1))
+    (re-search-forward "[0-9]+" nil)
+    (replace-match (incs (match-string 0) arg) nil nil)))
+
+(defun subtract-number-at-point (arg)
+  (interactive "p")
+  (change-number-at-point (- arg)))
+
+(defun replace-next-underscore-with-camel (arg)
+  (interactive "p")
+  (if (> arg 0)
+      (setq arg (1+ arg))) ; 1-based index to get eternal loop with 0
+  (let ((case-fold-search nil))
+    (while (not (= arg 1))
+      (search-forward-regexp "\\b_[a-z]")
+      (forward-char -2)
+      (delete-char 1)
+      (capitalize-word 1)
+      (setq arg (1- arg)))))
+
+(defun snakeify-current-word ()
+  (interactive)
+  (er/mark-word)
+  (let* ((beg (region-beginning))
+         (end (region-end))
+         (current-word (buffer-substring-no-properties beg end))
+         (snakified (snake-case current-word)))
+    (replace-string current-word snakified nil beg end)))
+
+(defun transpose-params ()
+  "Presumes that params are in the form (p, p, p) or {p, p, p} or [p, p, p]"
+  (interactive)
+  (let* ((end-of-first (cond
+                        ((looking-at ", ") (point))
+                        ((and (looking-back ",") (looking-at " ")) (- (point) 1))
+                        ((looking-back ", ") (- (point) 2))
+                        (t (error "Place point between params to transpose."))))
+         (start-of-first (save-excursion
+                           (goto-char end-of-first)
+                           (move-backward-out-of-param)
+                           (point)))
+         (start-of-last (+ end-of-first 2))
+         (end-of-last (save-excursion
+                        (goto-char start-of-last)
+                        (move-forward-out-of-param)
+                        (point))))
+    (transpose-regions start-of-first end-of-first start-of-last end-of-last)))
+
+(defun move-forward-out-of-param ()
+  (while (not (looking-at ")\\|, \\| ?}\\| ?\\]"))
+    (cond
+     ((point-is-in-string-p) (move-point-forward-out-of-string))
+     ((looking-at "(\\|{\\|\\[") (forward-list))
+     (t (forward-char)))))
+
+(defun move-backward-out-of-param ()
+  (while (not (looking-back "(\\|, \\|{ ?\\|\\[ ?"))
+    (cond
+     ((point-is-in-string-p) (move-point-backward-out-of-string))
+     ((looking-back ")\\|}\\|\\]") (backward-list))
+     (t (backward-char)))))
+
+(autoload 'zap-up-to-char "misc"
+  "Kill up to, but not including ARGth occurrence of CHAR.")
+
+(defun css-expand-statement ()
+  (interactive)
+  (save-excursion
+    (end-of-line)
+    (search-backward "{")
+    (forward-char 1)
+    (let ((beg (point)))
+      (newline)
+      (er/mark-inside-pairs)
+      (replace-regexp ";" ";\n" nil (region-beginning) (region-end))
+      (indent-region beg (point)))))
+
+(defun css-contract-statement ()
+  (interactive)
+  (end-of-line)
+  (search-backward "{")
+  (while (not (looking-at "}"))
+    (join-line -1))
+  (back-to-indentation))

defuns/file-defuns.el

+;; Defuns for working with files
+
+(defun rename-current-buffer-file ()
+  "Renames current buffer and file it is visiting."
+  (interactive)
+  (let ((name (buffer-name))
+        (filename (buffer-file-name)))
+    (if (not (and filename (file-exists-p filename)))
+        (error "Buffer '%s' is not visiting a file!" name)
+      (let ((new-name (read-file-name "New name: " filename)))
+        (if (get-buffer new-name)
+            (error "A buffer named '%s' already exists!" new-name)
+          (rename-file filename new-name 1)
+          (rename-buffer new-name)
+          (set-visited-file-name new-name)
+          (set-buffer-modified-p nil)
+          (message "File '%s' successfully renamed to '%s'"
+                   name (file-name-nondirectory new-name)))))))
+
+(defun delete-current-buffer-file ()
+  "Removes file connected to current buffer and kills buffer."
+  (interactive)
+  (let ((filename (buffer-file-name))
+        (buffer (current-buffer))
+        (name (buffer-name)))
+    (if (not (and filename (file-exists-p filename)))
+        (ido-kill-buffer)
+      (when (yes-or-no-p "Are you sure you want to remove this file? ")
+        (delete-file filename)
+        (kill-buffer buffer)
+        (message "File '%s' successfully removed" filename)))))
+
+(defun copy-current-file-path ()
+  "Add current file path to kill ring. Limits the filename to project root if possible."
+  (interactive)
+  (let ((filename (buffer-file-name)))
+    (kill-new (if eproject-mode
+                  (s-chop-prefix (eproject-root) filename)
+                filename))))
+
+(defun find-or-create-file-at-point ()
+  "Guesses what parts of the buffer under point is a file name and opens it."
+  (interactive)
+  (find-file (file-name-at-point)))
+
+(defun find-or-create-file-at-point-other-window ()
+  "Guesses what parts of the buffer under point is a file name and opens it."
+  (interactive)
+  (find-file-other-window (file-name-at-point)))
+
+(defun file-name-at-point ()
+  (save-excursion
+    (let* ((file-name-regexp "[./a-zA-Z0-9\-_~]")
+           (start (progn
+                    (while (looking-back file-name-regexp)
+                      (forward-char -1))
+                    (point)))
+           (end (progn
+                  (while (looking-at file-name-regexp)
+                    (forward-char 1))
+                  (point))))
+      (buffer-substring start end))))
+
+(defun touch-buffer-file ()
+  (interactive)
+  (insert " ")
+  (backward-delete-char 1)
+  (save-buffer))
+
+(provide 'file-defuns)

defuns/js2r-defuns.el

+(require 'cl)
+(require 's)
+(require 'dash)
+
+(defvar js2r-path-to-tests "/test/"
+  "Path to tests from a root shared with sources")
+
+(defvar js2r-path-to-sources "/lib/"
+  "Path to sources from a root shared with tests")
+
+(defvar js2r-test-suffix "-test"
+  "The suffix added to test files")
+
+(make-variable-buffer-local 'js2r-path-to-tests)
+(make-variable-buffer-local 'js2r-path-to-sources)
+(make-variable-buffer-local 'js2r-test-suffix)
+
+;; Toggle between source and test
+(defun jump-between-source-and-test-files (arg)
+  (interactive "P")
+  (if (looks-like-test-file-name (buffer-file-name))
+      (jump-to-source-file arg)
+    (jump-to-test-file arg)))
+
+(defun jump-between-source-and-test-files-other-window (arg)
+  (interactive "P")
+  (if (looks-like-test-file-name (buffer-file-name))
+      (jump-to-source-file-other-window arg)
+    (jump-to-test-file-other-window arg)))
+
+;; Duplicate object property node
+
+(defun js2r-duplicate-object-property-node ()
+  (interactive)
+  (js2r--guard)
+  (let ((node (js2r--closest 'js2-object-prop-node-p)))
+    (goto-char (js2-node-abs-pos node))
+    (skip-syntax-backward " >")
+    (insert (buffer-substring (point) (js2-node-abs-end node)) ",")
+    (skip-syntax-forward " >")))
+
+;; Rename tests and sources
+
+(defun js2r--rename-file (old-name new-name)
+  (let ((modified-p (buffer-modified-p)))
+    (rename-file old-name new-name 1)
+    (rename-buffer new-name)
+    (set-visited-file-name new-name)
+    (set-buffer-modified-p modified-p)))
+
+(defun also-rename-other (old-name new-name)
+  (let (old-other new-other)
+    (condition-case nil
+        (if (and (looks-like-test-file-name old-name)
+                 (looks-like-test-file-name new-name))
+            (setq old-other (guess-source-file old-name)
+                  new-other (guess-source-file new-name))
+          (setq old-other (guess-test-file old-name)
+                new-other (guess-test-file new-name)))
+      (error nil))
+
+    (when (and old-other new-other
+               (file-exists-p old-other)
+               (not (file-exists-p new-other))
+               (yes-or-no-p (format "Also rename %S to %S?" old-other new-other)))
+
+      (let ((b (find-buffer-visiting old-other)))
+        (if b
+            (with-current-buffer b
+              (js2r--rename-file old-other new-other))
+          (rename-file old-other new-other 1))))))
+
+(defun js2r-rename-current-buffer-file ()
+  "Renames current buffer and file it is visiting."
+  (interactive)
+  (let ((name (buffer-name))
+        (filename (buffer-file-name)))
+    (if (not (and filename (file-exists-p filename)))
+        (error "Buffer '%s' is not visiting a file!" name)
+      (let ((new-name (read-file-name "New name: " filename)))
+        (cond ((get-buffer new-name)
+               (error "A buffer named '%s' already exists!" new-name))
+              (t
+               (js2r--rename-file filename new-name)
+               (also-rename-other filename new-name)
+               (message "File '%s' successfully renamed to '%s'" name (file-name-nondirectory new-name))))))))
+
+;; Delete tests and sources
+
+(defun also-delete-other (file-name)
+  (let (other-name)
+    (condition-case nil
+        (setq other-name
+              (if (looks-like-test-file-name file-name)
+                  (guess-source-file file-name)
+                (guess-test-file file-name)))
+      (error nil))
+
+    (when (and other-name
+               (file-exists-p other-name)
+               (yes-or-no-p (format "Also delete %S?" other-name)))
+
+      (let ((b (find-buffer-visiting other-name)))
+        (when b (kill-buffer b)))
+
+      (delete-file other-name))))
+
+(defun js2r-delete-current-buffer-file ()
+  "Removes file connected to current buffer and kills buffer."
+  (interactive)
+  (let ((filename (buffer-file-name))
+        (buffer (current-buffer))
+        (name (buffer-name)))
+    (if (not (and filename (file-exists-p filename)))
+        (ido-kill-buffer)
+      (when (yes-or-no-p "Are you sure you want to remove this file? ")
+        (delete-file filename)
+        (also-delete-other filename)
+        (kill-buffer buffer)
+        (message "File '%s' successfully removed" filename)))))
+
+;; Jump to source-file
+
+(defun jump-to-source-file (arg)
+  (interactive "P")
+  (let ((file (guess-source-file (buffer-file-name))))
+    (if (or (file-exists-p file) arg)
+        (find-file file)
+      (error "%s not found." file))))
+
+(defun possible-test-file-suffixes ()
+  (cons (concat js2r-test-suffix ".js")
+        '("Test.js" "_test.js" "-test.js")))
+
+(defun looks-like-test-file-name (file-name)
+  (--any? (s-ends-with-p it file-name) (possible-test-file-suffixes)))
+
+(defun jump-to-source-file-other-window (arg)
+  (interactive "P")
+  (let ((file (guess-source-file (buffer-file-name))))
+    (if (or (file-exists-p file) arg)
+        (find-file-other-window file)
+      (error "%s not found." file))))
+
+(defun guess-source-file (file-name)
+  (unless (looks-like-test-file-name file-name)
+    (error "This doesn't look like a test file."))
+  (format "%s/%s.js" (s-chop-suffix "/" (guess-source-folder file-name)) (guess-source-file-name file-name)))
+
+(defun guess-source-file-name (file-name)
+  (s-chop-suffixes (possible-test-file-suffixes) (file-name-nondirectory file-name)))
+
+(defun guess-source-folder (file-name)
+  (let ((test-dir (file-name-directory file-name)))
+    (when (not (string-match-p js2r-path-to-tests test-dir))
+      (error "Unable to locate source folder. Set js2r-path-to-tests and -sources."))
+    (let ((source-dir (replace-regexp-in-string
+                       js2r-path-to-tests
+                       js2r-path-to-sources
+                       test-dir)))
+      (if (file-exists-p source-dir)
+          source-dir
+        (error "Unable to locate source folder. Verify js2r-path-to-tests and -sources")))))
+
+
+;; Jump to test-file
+
+(defun jump-to-test-file (arg)
+  (interactive "P")
+  (let ((file (guess-test-file (buffer-file-name))))
+    (if (or (file-exists-p file) arg)
+        (find-file file)
+      (error "%s not found." file))))
+
+(defun jump-to-test-file-other-window (arg)
+  (interactive "P")
+  (let ((file (guess-test-file (buffer-file-name))))
+    (if (or (file-exists-p file) arg)
+        (find-file-other-window file)
+      (error "%s not found." file))))
+
+(defun guess-test-file (file-name)
+  (when (looks-like-test-file-name file-name)
+    (error "Looks like you're already in the test file."))
+  (or (test-file-that-exists file-name "-test")
+      (test-file-that-exists file-name "_test")
+      (test-file-that-exists file-name "Test")
+      (test-file-name file-name js2r-test-suffix)))
+
+(defun test-file-that-exists (file-name suffix)
+  (let ((file (test-file-name file-name suffix)))
+    (if (file-exists-p file) file nil)))
+
+(defun test-file-name (file-name suffix)
+  (format "%s/%s%s.js" (s-chop-suffix "/" (guess-test-folder file-name)) (test-file-name-stub file-name) suffix))
+
+(defun test-file-name-stub (file-name)
+  (s-chop-suffix ".js" (file-name-nondirectory file-name)))
+
+(defun guess-test-folder (file-name)
+  (let ((source-dir (file-name-directory file-name)))
+    (when (not (string-match-p js2r-path-to-sources source-dir))
+      (error "Unable to locate test folder. Set js2r-path-to-tests and -sources."))
+    (let ((test-dir (replace-regexp-in-string
+                     js2r-path-to-sources
+                     js2r-path-to-tests
+                     source-dir)))
+      (if (file-exists-p test-dir)
+          test-dir
+        (error "Unable to locate test folder. Verify js2r-path-to-tests")))))
+
+;; Toggle assert/refute
+
+(defun toggle-assert-refute ()
+  (interactive)
+  (save-excursion
+    (end-of-line)
+    (re-search-backward "\\(assert\\|refute\\)")
+    (if (looking-at "assert")
+        (progn
+          (kill-word 1)
+          (insert "refute"))
+      (kill-word 1)
+      (insert "assert"))))
+
+;; Mark a js2-node in right window
+
+(defun remove-js2-mark-overlay ()
+  (interactive)
+  (mapc #'(lambda (o)
+            (when (eq (overlay-get o 'type) 'mark-js2-in-right-window)
+              (delete-overlay o)))
+        (overlays-in (point-min) (point-max))))
+
+(defmacro mark-js2-in-right-window (func)
+  `(progn
+     (kill-comment nil)
+     (windmove-right)
+     (remove-js2-mark-overlay)
+     (let* ((node ,func)
+            (beg (js2-node-abs-pos node))
+            (end (js2-node-abs-end node))
+            (o (make-overlay beg end nil nil t)))
+       (overlay-put o 'face 'region)
+       (overlay-put o 'type 'mark-js2-in-right-window)
+       (windmove-left)
+       (save-excursion
+         (insert (format " ;; %s" (js2-node-short-name node)))))))
+
+;;(mark-js2-in-right-window
+;;  (js2-node-at-point)) ;; js2-name-node

defuns/lisp-defuns.el

+;; Lisp specific defuns
+
+(defun eval-and-replace ()
+  "Replace the preceding sexp with its value."
+  (interactive)
+  (backward-kill-sexp)
+  (condition-case nil
+      (prin1 (eval (read (current-kill 0)))
+             (current-buffer))
+    (error (message "Invalid expression")
+           (insert (current-kill 0)))))

defuns/misc-defuns.el

+;; Misc defuns go here
+;; It wouldn't hurt to look for patterns and extract once in a while
+
+(defmacro create-simple-keybinding-command (name key)
+  `(defmacro ,name (&rest fns)
+     (list 'global-set-key (kbd ,key) `(lambda ()
+                                         (interactive)
+                                         ,@fns))))
+
+(create-simple-keybinding-command f2 "<f2>")
+(create-simple-keybinding-command f5 "<f5>")
+(create-simple-keybinding-command f6 "<f6>")
+(create-simple-keybinding-command f7 "<f7>")
+(create-simple-keybinding-command f8 "<f8>")
+(create-simple-keybinding-command f9 "<f9>")
+(create-simple-keybinding-command f10 "<f10>")
+(create-simple-keybinding-command f11 "<f11>")
+(create-simple-keybinding-command f12 "<f12>")
+
+(defun goto-line-with-feedback ()
+  "Show line numbers temporarily, while prompting for the line number input"
+  (interactive)
+  (unwind-protect
+      (progn
+        (linum-mode 1)
+        (call-interactively 'goto-line))
+    (linum-mode -1)))
+
+(defun open-line-and-indent ()
+  (interactive)
+  (newline-and-indent)
+  (end-of-line 0))
+
+;; start a httpd-server in current directory
+(defun httpd-start-here (directory port)
+  (interactive (list (read-directory-name "Root directory: " default-directory nil t)
+                     (read-number "Port: " 8017)))
+  (setq httpd-root directory)
+  (setq httpd-port port)
+  (httpd-start)
+  (browse-url (concat "http://localhost:" (number-to-string port) "/")))
+
+;; shorthand for interactive lambdas
+(defmacro λ (&rest body)
+  `(lambda ()
+     (interactive)
+     ,@body))
+
+(global-set-key (kbd "s-l") (λ (insert "\u03bb")))
+
+;; command to help set up magit-gh-pulls
+(defun magit-gh-pulls-setup (repoid)
+  (interactive "suser/repo: ")
+  (shell-command "git config --add magit.extension gh-pulls")
+  (shell-command (concat "git config magit.gh-pulls-repo " repoid)))
+
+;; Increase/decrease selective display
+(defun inc-selective-display (arg)
+  (interactive "P")
+  (if (numberp arg)
+      (set-selective-display arg)
+    (if (numberp selective-display)
+        (set-selective-display (+ 2 selective-display))
+      (set-selective-display 2)))
+  (create-temp-selective-display-keymap))
+
+(defun dec-selective-display ()
+  (interactive)
+  (when (and (numberp selective-display)
+             (> selective-display 2))
+    (set-selective-display (- selective-display 2)))
+  (create-temp-selective-display-keymap))
+
+(defun clear-selective-display ()
+  (interactive)
+  (when (numberp selective-display)
+    (set-selective-display nil)))
+
+(defun create-temp-selective-display-keymap ()
+  (set-temporary-overlay-map
+   (let ((map (make-sparse-keymap)))
+     (define-key map (kbd "+") 'inc-selective-display)
+     (define-key map (kbd "-") 'dec-selective-display)
+     (define-key map (kbd "0") 'clear-selective-display)
+     map))
+  (message "Type + to reveal more, - for less, 0 to reset."))
+
+;; Add spaces and proper formatting to linum-mode. It uses more room than
+;; necessary, but that's not a problem since it's only in use when going to
+;; lines.
+(setq linum-format (lambda (line)
+                     (propertize
+                      (format (concat " %"
+                                      (number-to-string
+                                       (length (number-to-string
+                                                (line-number-at-pos (point-max)))))
+                                      "d ")
+                              line)
+                      'face 'linum)))
+
+(defun isearch-yank-selection ()
+  "Put selection from buffer into search string."
+  (interactive)
+  (when (region-active-p)
+    (deactivate-mark))
+  (isearch-yank-internal (lambda () (mark))))
+
+(defun region-as-string ()
+  (buffer-substring (region-beginning)
+                    (region-end)))
+
+(defun isearch-forward-use-region ()
+  (interactive)
+  (when (region-active-p)
+    (add-to-history 'search-ring (region-as-string))
+    (deactivate-mark))
+  (call-interactively 'isearch-forward))
+
+(defun isearch-backward-use-region ()
+  (interactive)
+  (when (region-active-p)
+    (add-to-history 'search-ring (region-as-string))
+    (deactivate-mark))
+  (call-interactively 'isearch-backward))
+
+(eval-after-load "multiple-cursors"
+  '(progn
+     (unsupported-cmd isearch-forward-use-region ".")
+     (unsupported-cmd isearch-backward-use-region ".")))
+
+(defun view-url ()
+  "Open a new buffer containing the contents of URL."
+  (interactive)
+  (let* ((default (thing-at-point-url-at-point))
+         (url (read-from-minibuffer "URL: " default)))
+    (switch-to-buffer (url-retrieve-synchronously url))
+    (rename-buffer url t)
+    ;; TODO: switch to nxml/nxhtml mode
+    (cond ((search-forward "<?xml" nil t) (xml-mode))
+          ((search-forward "<html" nil t) (html-mode)))))
+
+(defun linkify-region-from-kill-ring (start end)
+  (interactive "r")
+  (let ((text (buffer-substring start end)))
+    (delete-region start end)
+    (insert "<a href=\"")
+    (yank)
+    (insert (concat "\">" text "</a>"))))
+
+(defun buffer-to-html (buffer)
+  (with-current-buffer (htmlize-buffer buffer)
+    (buffer-string)))
+
+(defun sudo-edit (&optional arg)
+  (interactive "p")
+  (if (or arg (not buffer-file-name))
+      (find-file (concat "/sudo:root@localhost:" (ido-read-file-name "File: ")))
+    (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
+
+(defun add-file-find-hook-with-pattern (pattern fn &optional contents)
+  "Add a find-file-hook that calls FN for files where PATTERN
+matches the file name, and optionally, where CONTENT matches file contents.
+Both PATTERN and CONTENTS are matched as regular expressions."
+  (lexical-let ((re-pattern pattern)
+                (fun fn)
+                (re-content contents))
+    (add-hook 'find-file-hook
+              (lambda ()
+                (if (and
+                     (string-match re-pattern (buffer-file-name))
+                     (or (null re-content)
+                         (string-match re-content
+                                       (buffer-substring (point-min) (point-max)))))
+                    (apply fun ()))))))
+
+;; Fix kmacro-edit-lossage, it's normal implementation
+;; is bound tightly to C-h
+(defun kmacro-edit-lossage ()
+  "Edit most recent 300 keystrokes as a keyboard macro."
+  (interactive)
+  (kmacro-push-ring)
+  (edit-kbd-macro 'view-lossage))

defuns/project-defuns.el

+(defmacro project-specifics (name &rest body)
+  (declare (indent 1))
+  `(progn
+     (add-hook 'find-file-hook
+               (lambda ()
+                 (when (string-match-p ,name (buffer-file-name))
+                   ,@body)))
+     (add-hook 'dired-after-readin-hook
+               (lambda ()
+                 (when (string-match-p ,name (dired-current-directory))
+                   ,@body)))))

defuns/snippet-helpers.el

+;;; javascript
+
+(defun js-method-p ()
+  (save-excursion
+    (word-search-backward "function")
+    (looking-back ": ")))
+
+(defun js-function-declaration-p ()
+  (save-excursion
+    (word-search-backward "function")
+    (looking-back "^\\s *")))
+
+(defun snippet--function-punctuation ()
+  (if (js-method-p)
+      (when (not (looking-at "[ \n\t\r]*[},]"))
+        (insert ","))
+    (unless (js-function-declaration-p)
+      (if (looking-at "$") (insert ";")))))
+
+(defun snippet--function-name ()
+  (if (js-function-declaration-p) "name" ""))
+
+;;; clojure
+
+(defun snippet--clojure-namespace-from-buffer-file-name ()
+  (replace-regexp-in-string "_" "-"
+   (replace-regexp-in-string "/" "."
+    (chop-prefix "test/"
+    (chop-prefix "src/"
+    (chop-suffix ".clj"
+     (substring (buffer-file-name) (length eproject-root))))))))
+
+(defun snippet--clojure-namespace-under-test ()
+  (replace-regexp-in-string "-test" "" (snippet--clojure-namespace-from-buffer-file-name)))
+
+;; snippet-helper-helpers
+
+(defun chop-suffix (suffix s)
+  "Remove string 'suffix' if it is at end of string 's'"
+  (let ((pos (- (length suffix))))
+    (if (and (>= (length s) (length suffix))
+             (string= suffix (substring s pos)))
+        (substring s 0 pos)
+      s)))
+
+(defun chop-prefix (prefix s)
+  "Remove string 'prefix' if it is at start of string 's'"
+  (let ((pos (length prefix)))
+    (if (and (>= (length s) (length prefix))
+             (string= prefix (substring s 0 pos)))
+        (substring s pos)
+      s)))
+;; Turn off mouse interface early in startup to avoid momentary display
+(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))
+
+;; No splash screen please ... jeez
+(setq inhibit-startup-message t)
+
+;; Set path to dependencies
+(setq site-lisp-dir
+      (expand-file-name "site-lisp" user-emacs-directory))
+
+;; Set up load path
+(add-to-list 'load-path user-emacs-directory)
+(add-to-list 'load-path site-lisp-dir)
+
+;; Keep emacs Custom-settings in separate file
+(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
+(load custom-file)
+
+;; Set up appearance early
+(require 'appearance)
+
+;; Settings for currently logged in user
+(setq user-settings-dir
+      (concat user-emacs-directory "users/" user-login-name))
+(add-to-list 'load-path user-settings-dir)
+
+;; Add external projects to load path
+(dolist (project (directory-files site-lisp-dir t "\\w+"))
+  (when (file-directory-p project)
+    (add-to-list 'load-path project)))
+
+;; Write backup files to own directory
+(setq backup-directory-alist
+      `(("." . ,(expand-file-name
+                 (concat user-emacs-directory "backups")))))
+
+;; Make backups of files, even when they're in version control
+(setq vc-make-backup-files t)
+
+;; Save point position between sessions
+(require 'saveplace)
+(setq-default save-place t)
+(setq save-place-file (expand-file-name ".places" user-emacs-directory))
+
+;; Are we on a mac?
+(setq is-mac (equal system-type 'darwin))
+
+;; Setup packages
+(require 'setup-package)
+
+;; Install extensions if they're missing
+(defun init--install-packages ()
+  (packages-install
+   '(magit
+     paredit
+     move-text
+     god-mode
+     gist
+     htmlize
+     visual-regexp
+     flycheck
+     flx
+     flx-ido
+     css-eldoc
+     yasnippet
+     smartparens
+     ido-vertical-mode
+     ido-at-point
+     simple-httpd
+     guide-key
+     nodejs-repl
+     restclient
+     highlight-escape-sequences
+     whitespace-cleanup-mode
+     elisp-slime-nav
+     git-commit-mode
+     gitconfig-mode
+     gitignore-mode
+     clojure-mode
+     nrepl)))
+
+(condition-case nil
+    (init--install-packages)
+  (error
+   (package-refresh-contents)
+   (init--install-packages)))
+
+;; Lets start with a smattering of sanity
+(require 'sane-defaults)
+
+;; Setup environment variables from the user's shell.
+(when is-mac
+  (require-package 'exec-path-from-shell)
+  (exec-path-from-shell-initialize))
+
+;; guide-key
+(require 'guide-key)
+(setq guide-key/guide-key-sequence '("C-x r" "C-x 4" "C-x v" "C-x 8"))
+(guide-key-mode 1)
+(setq guide-key/recursive-key-sequence-flag t)
+(setq guide-key/popup-window-position 'bottom)
+
+;; god-mode
+(require 'god-mode)
+(global-set-key (kbd "<escape>") 'god-local-mode)
+
+;; Setup extensions
+(eval-after-load 'ido '(require 'setup-ido))
+(eval-after-load 'org '(require 'setup-org))
+(eval-after-load 'dired '(require 'setup-dired))
+(eval-after-load 'magit '(require 'setup-magit))
+(eval-after-load 'grep '(require 'setup-rgrep))
+(eval-after-load 'shell '(require 'setup-shell))
+(require 'setup-hippie)
+(require 'setup-yasnippet)
+(require 'setup-perspective)
+(require 'setup-ffip)
+(require 'setup-html-mode)
+(require 'setup-paredit)
+
+;; Default setup of smartparens
+(require 'smartparens-config)
+(setq sp-autoescape-string-quote nil)
+(--each '(css-mode-hook
+          restclient-mode-hook
+          js-mode-hook
+          ruby-mode
+          markdown-mode)
+  (add-hook it 'turn-on-smartparens-mode))
+
+;; Language specific setup files
+(eval-after-load 'js2-mode '(require 'setup-js2-mode))
+(eval-after-load 'ruby-mode '(require 'setup-ruby-mode))
+(eval-after-load 'clojure-mode '(require 'setup-clojure-mode))
+(eval-after-load 'markdown-mode '(require 'setup-markdown-mode))
+
+;; Load stuff on demand
+(autoload 'skewer-start "setup-skewer" nil t)
+(autoload 'skewer-demo "setup-skewer" nil t)
+(autoload 'flycheck-mode "setup-flycheck" nil t)
+(autoload 'auto-complete-mode "auto-complete" nil t)
+
+;; Map files to modes
+(require 'mode-mappings)
+
+;; Highlight escape sequences
+(require 'highlight-escape-sequences)
+(hes-mode)
+(put 'font-lock-regexp-grouping-backslash 'face-alias 'font-lock-builtin-face)
+
+;; Visual regexp
+(require 'visual-regexp)
+(define-key global-map (kbd "M-&") 'vr/query-replace)
+(define-key global-map (kbd "M-/") 'vr/replace)
+
+;; Functions (load all files in defuns-dir)
+(setq defuns-dir (expand-file-name "defuns" user-emacs-directory))
+(dolist (file (directory-files defuns-dir t "\\w+"))
+  (when (file-regular-p file)
+    (load file)))
+
+(require 'expand-region)
+(require 'multiple-cursors)
+(require 'delsel)
+(require 'jump-char)
+(require 'eproject)
+(require 'wgrep)
+(require 'smart-forward)
+(require 'change-inner)
+(require 'multifiles)
+
+;; Fill column indicator
+(require 'fill-column-indicator)
+(setq fci-rule-color "#111122")
+
+;; Browse kill ring
+(require 'browse-kill-ring)
+(setq browse-kill-ring-quit-action 'save-and-restore)
+
+;; Smart M-x is smart
+(require 'smex)
+(smex-initialize)
+
+;; Setup key bindings
+(require 'key-bindings)
+
+;; Misc
+(require 'project-archetypes)
+(require 'my-misc)
+(when is-mac (require 'mac))
+
+;; Elisp go-to-definition with M-. and back again with M-,
+(autoload 'elisp-slime-nav-mode "elisp-slime-nav")
+(add-hook 'emacs-lisp-mode-hook (lambda () (elisp-slime-nav-mode t) (eldoc-mode 1)))
+
+;; Email, baby
+(require 'setup-mu4e)
+
+;; Emacs server
+(require 'server)
+(unless (server-running-p)
+  (server-start))
+
+;; Run at full power please
+(put 'downcase-region 'disabled nil)
+(put 'upcase-region 'disabled nil)
+(put 'narrow-to-region 'disabled nil)
+
+;; Conclude init by setting up specifics for the current user
+(when (file-exists-p user-settings-dir)
+  (mapc 'load (directory-files user-settings-dir nil "^[^#].*el$")))
+;; I don't need to kill emacs that easily
+;; the mnemonic is C-x REALLY QUIT
+(global-set-key (kbd "C-x r q") 'save-buffers-kill-terminal)
+(global-set-key (kbd "C-x C-c") 'delete-frame)
+
+;; Completion that uses many different methods to find options.
+(global-set-key (kbd "C-.") 'hippie-expand-no-case-fold)
+(global-set-key (kbd "C-:") 'hippie-expand-lines)
+(global-set-key (kbd "C-,") 'completion-at-point)
+
+(require 'misc)
+(global-set-key (kbd "s-.") 'copy-from-above-command)
+
+;; Smart M-x
+(global-set-key (kbd "M-x") 'smex)
+(global-set-key (kbd "M-X") 'smex-major-mode-commands)
+(global-set-key (kbd "C-c C-c M-x") 'execute-extended-command)
+
+;; Use C-x C-m to do M-x per Steve Yegge's advice
+(global-set-key (kbd "C-x C-m") 'smex)
+
+;; Expand region (increases selected region by semantic units)
+(global-set-key (if is-mac (kbd "C-@") (kbd "C-'")) 'er/expand-region)
+
+;; Experimental multiple-cursors
+(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
+(global-set-key (kbd "C-S-c C-e") 'mc/edit-ends-of-lines)
+(global-set-key (kbd "C-S-c C-a") 'mc/edit-beginnings-of-lines)
+
+;; Mark additional regions matching current region
+(global-set-key (kbd "M-æ") 'mc/mark-all-dwim)
+(global-set-key (kbd "C-å") 'mc/mark-previous-like-this)
+(global-set-key (kbd "C-æ") 'mc/mark-next-like-this)
+(global-set-key (kbd "C-Æ") 'mc/mark-more-like-this-extended)
+(global-set-key (kbd "M-å") 'mc/mark-all-in-region)
+
+;; Symbol and word specific mark-more
+(global-set-key (kbd "s-æ") 'mc/mark-next-word-like-this)
+(global-set-key (kbd "s-å") 'mc/mark-previous-word-like-this)
+(global-set-key (kbd "M-s-æ") 'mc/mark-all-words-like-this)
+(global-set-key (kbd "s-Æ") 'mc/mark-next-symbol-like-this)
+(global-set-key (kbd "s-Å") 'mc/mark-previous-symbol-like-this)
+(global-set-key (kbd "M-s-Æ") 'mc/mark-all-symbols-like-this)
+
+;; Extra multiple cursors stuff
+(global-set-key (kbd "C-~") 'mc/reverse-regions)
+(global-set-key (kbd "M-~") 'mc/sort-regions)
+(global-set-key (kbd "H-~") 'mc/insert-numbers)
+
+(global-set-key (kbd "C-S-<mouse-1>") 'mc/add-cursor-on-click)
+
+;; Set anchor to start rectangular-region-mode
+(global-set-key (kbd "H-SPC") 'set-rectangular-region-anchor)
+
+;; Replace rectangle-text with inline-string-rectangle
+(global-set-key (kbd "C-x r t") 'inline-string-rectangle)
+
+;; Quickly jump in document with ace-jump-mode
+(define-key global-map (kbd "C-ø") 'ace-jump-mode)
+
+;; Perform general cleanup.
+(global-set-key (kbd "C-c n") 'cleanup-buffer)
+(global-set-key (kbd "C-c C-n") 'cleanup-buffer)
+(global-set-key (kbd "C-c C-<return>") 'delete-blank-lines)
+
+;; M-i for back-to-indentation
+(global-set-key (kbd "M-i") 'back-to-indentation)
+
+;; Turn on the menu bar for exploring new modes
+(global-set-key (kbd "C-<f10>") 'menu-bar-mode)
+
+;; Use shell-like backspace C-h, rebind help to F1
+(define-key key-translation-map [?\C-h] [?\C-?])
+(global-set-key (kbd "<f1>") 'help-command)
+(define-key god-local-mode-map (kbd "h") 'backward-delete-char)
+
+(global-set-key (kbd "M-h") 'kill-region-or-backward-word)
+
+;; Transpose stuff with M-t
+(global-unset-key (kbd "M-t")) ;; which used to be transpose-words
+(global-set-key (kbd "M-t l") 'transpose-lines)
+(global-set-key (kbd "M-t w") 'transpose-words)
+(global-set-key (kbd "M-t s") 'transpose-sexps)
+(global-set-key (kbd "M-t p") 'transpose-params)
+
+;; Interactive selective display
+(global-set-key (kbd "C-x $") 'inc-selective-display)
+
+;; Change next underscore with a camel case
+(global-set-key (kbd "C-c C--") 'replace-next-underscore-with-camel)
+(global-set-key (kbd "M-s M--") 'snakeify-current-word)
+
+;; Killing text
+(global-set-key (kbd "C-S-k") 'kill-and-retry-line)
+(global-set-key (kbd "C-w") 'kill-region-or-backward-word)
+(global-set-key (kbd "C-c C-w") 'kill-to-beginning-of-line)
+
+;; Use M-w for copy-line if no active region
+(global-set-key (kbd "M-w") 'save-region-or-current-line)
+(global-set-key (kbd "M-W") '(λ (save-region-or-current-line 1)))
+
+;; Make shell more convenient, and suspend-frame less
+(global-set-key (kbd "C-z") 'shell)
+(global-set-key (kbd "C-x M-z") 'suspend-frame)
+
+;; Zap to char
+(global-set-key (kbd "M-z") 'zap-up-to-char)
+(global-set-key (kbd "s-z") (lambda (char) (interactive "cZap up to char backwards: ") (zap-up-to-char -1 char)))
+
+(global-set-key (kbd "M-Z") (lambda (char) (interactive "cZap to char: ") (zap-to-char 1 char)))
+(global-set-key (kbd "s-Z") (lambda (char) (interactive "cZap to char backwards: ") (zap-to-char -1 char)))
+
+;; iy-go-to-char - like f in Vim
+(global-set-key (kbd "M-m") 'jump-char-forward)
+(global-set-key (kbd "M-M") 'jump-char-backward)
+(global-set-key (kbd "s-m") 'jump-char-backward)
+
+;; vim's ci and co commands
+(global-set-key (kbd "M-I") 'change-inner)
+(global-set-key (kbd "M-O") 'change-outer)
+
+(global-set-key (kbd "s-i") 'copy-inner)
+(global-set-key (kbd "s-o") 'copy-outer)
+
+;; Create new frame
+(define-key global-map (kbd "C-x C-n") 'make-frame-command)
+
+;; Jump to a definition in the current file. (This is awesome)
+(global-set-key (kbd "C-x C-i") 'ido-imenu)
+
+;; File finding
+(global-set-key (kbd "C-x M-f") 'ido-find-file-other-window)
+(global-set-key (kbd "C-x f") 'recentf-ido-find-file)
+(global-set-key (kbd "C-x C-p") 'find-or-create-file-at-point)
+(global-set-key (kbd "C-x M-p") 'find-or-create-file-at-point-other-window)
+(global-set-key (kbd "C-c y") 'bury-buffer)
+(global-set-key (kbd "C-c r") 'revert-buffer)
+(global-set-key (kbd "M-`") 'file-cache-minibuffer-complete)
+(global-set-key (kbd "C-x C-b") 'ibuffer)
+
+;; toggle two most recent buffers
+(fset 'quick-switch-buffer [?\C-x ?b return])
+(global-set-key (kbd "s-b") 'quick-switch-buffer)
+
+;; Revert without any fuss
+(global-set-key (kbd "M-<escape>") (λ (revert-buffer t t)))
+
+;; Edit file with sudo
+(global-set-key (kbd "M-s e") 'sudo-edit)
+
+;; Copy file path to kill ring
+(global-set-key (kbd "C-x M-w") 'copy-current-file-path)
+
+;; Window switching
+(windmove-default-keybindings) ;; Shift+direction
+(global-set-key (kbd "C-x -") 'toggle-window-split)
+(global-set-key (kbd "C-x C--") 'rotate-windows)
+(global-unset-key (kbd "C-x C-+")) ;; don't zoom like this
+
+(global-set-key (kbd "C-x 3") 'split-window-right-and-move-there-dammit)
+
+;; Add region to *multifile*
+(global-set-key (kbd "C-!") 'mf/mirror-region-in-multifile)
+
+;; Indentation help
+(global-set-key (kbd "M-j") (λ (join-line -1)))
+
+;; Help should search more than just commands
+(global-set-key (kbd "<f1> a") 'apropos)
+
+;; Should be able to eval-and-replace anywhere.
+(global-set-key (kbd "C-c C-e") 'eval-and-replace)
+
+;; Navigation bindings
+(global-set-key [remap goto-line] 'goto-line-with-feedback)
+
+(global-set-key (kbd "<prior>") 'beginning-of-buffer)
+(global-set-key (kbd "<home>") 'beginning-of-buffer)
+(global-set-key (kbd "<next>") 'end-of-buffer)
+(global-set-key (kbd "<end>") 'end-of-buffer)
+(global-set-key (kbd "M-p") 'backward-paragraph)
+(global-set-key (kbd "M-n") 'forward-paragraph)
+
+(global-set-key (kbd "M-<up>") 'smart-up)
+(global-set-key (kbd "M-<down>") 'smart-down)
+(global-set-key (kbd "M-<left>") 'smart-backward)
+(global-set-key (kbd "M-<right>") 'smart-forward)
+
+;; Webjump let's you quickly search google, wikipedia, emacs wiki
+(global-set-key (kbd "C-x g") 'webjump)
+(global-set-key (kbd "C-x M-g") 'browse-url-at-point)
+
+;; Completion at point
+(global-set-key (kbd "C-<tab>") 'completion-at-point)
+
+;; Like isearch, but adds region (if any) to history and deactivates mark
+(global-set-key (kbd "C-s") 'isearch-forward-use-region)
+(global-set-key (kbd "C-r") 'isearch-backward-use-region)
+
+;; Like isearch-*-use-region, but doesn't fuck with the active region
+(global-set-key (kbd "C-S-s") 'isearch-forward)
+(global-set-key (kbd "C-S-r") 'isearch-backward)
+
+;; Move more quickly
+(global-set-key (kbd "C-S-n") (λ (ignore-errors (next-line 5))))
+(global-set-key (kbd "C-S-p") (λ (ignore-errors (previous-line 5))))
+(global-set-key (kbd "C-S-f") (λ (ignore-errors (forward-char 5))))
+(global-set-key (kbd "C-S-b") (λ (ignore-errors (backward-char 5))))
+
+(global-set-key (kbd "H-*") 'beginning-of-buffer) ;; H-p
+(global-set-key (kbd "H-n") 'end-of-buffer)
+
+;; Convenience on ThinkPad Keyboard: Use back/forward as pg up/down
+(global-set-key (kbd "<XF86Back>") 'scroll-down)
+(global-set-key (kbd "<XF86Forward>") 'scroll-up)
+(global-set-key (kbd "<XF86WakeUp>") 'beginning-of-buffer)
+
+;; Query replace regex key binding
+(global-set-key (kbd "M-&") 'query-replace-regexp)
+
+;; Yank selection in isearch
+(define-key isearch-mode-map (kbd "C-o") 'isearch-yank-selection)
+
+;; Comment/uncomment block
+(global-set-key (kbd "C-c c") 'comment-or-uncomment-region)
+(global-set-key (kbd "C-c u") 'uncomment-region)
+
+;; Eval buffer
+(global-set-key (kbd "C-c C-k") 'eval-buffer)
+
+;; Create scratch buffer
+(global-set-key (kbd "C-c b") 'create-scratch-buffer)
+
+;; Move windows, even in org-mode
+(global-set-key (kbd "<s-right>") 'windmove-right)
+(global-set-key (kbd "<s-left>") 'windmove-left)
+(global-set-key (kbd "<s-up>") 'windmove-up)
+(global-set-key (kbd "<s-down>") 'windmove-down)
+
+;; Magit
+(global-set-key (kbd "C-x m") 'magit-status)
+(autoload 'magit-status "magit")
+
+;; Mu4e
+(global-set-key (kbd "C-x M") 'mu4e-up-to-date-status)
+
+;; Clever newlines
+(global-set-key (kbd "C-o") 'open-line-and-indent)
+(global-set-key (kbd "<C-return>") 'open-line-below)
+(global-set-key (kbd "<C-S-return>") 'open-line-above)
+(global-set-key (kbd "<M-return>") 'new-line-dwim)
+
+;; Duplicate region
+(global-set-key (kbd "C-c d") 'duplicate-current-line-or-region)
+
+;; Line movement
+(global-set-key (kbd "<C-S-down>") 'move-text-down)
+(global-set-key (kbd "<C-S-up>") 'move-text-up)
+
+;; Fold the active region
+(global-set-key (kbd "C-c C-f") 'fold-this-all)
+(global-set-key (kbd "C-c C-F") 'fold-this)
+(global-set-key (kbd "C-c M-f") 'fold-this-unfold-all)
+
+;; Yank and indent
+(global-set-key (kbd "C-S-y") 'yank-unindented)
+
+;; Toggle quotes
+(global-set-key (kbd "C-\"") 'toggle-quotes)
+
+;; Sorting
+(global-set-key (kbd "M-s l") 'sort-lines)
+
+;; Increase number at point (or other change based on prefix arg)
+(global-set-key (kbd "C-+") 'change-number-at-point)
+(global-set-key (kbd "C-?") 'subtract-number-at-point)
+(eval-after-load 'undo-tree '(define-key undo-tree-map (kbd "C-?") nil))
+
+;; Browse the kill ring
+(global-set-key (kbd "C-x C-y") 'browse-kill-ring)
+
+;; Buffer file functions
+(global-set-key (kbd "C-x t") 'touch-buffer-file)
+(global-set-key (kbd "C-x C-r") 'rename-current-buffer-file)
+(global-set-key (kbd "C-x C-k") 'delete-current-buffer-file)
+
+;; Jump from file to containing directory
+(global-set-key (kbd "C-x C-j") 'dired-jump) (autoload 'dired-jump "dired")
+(global-set-key (kbd "C-x M-j") '(λ (dired-jump 1)))
+
+;; Easy-mode fullscreen rgrep
+(global-set-key (kbd "M-s s") 'git-grep-fullscreen)
+(global-set-key (kbd "M-s S") 'rgrep-fullscreen)
+
+;; Multi-occur
+(global-set-key (kbd "M-s m") 'multi-occur)
+(global-set-key (kbd "M-s M") 'multi-occur-in-matching-buffers)
+
+;; Display and edit occurances of regexp in buffer
+(global-set-key (kbd "C-c o") 'occur)
+
+;; Find files by name and display results in dired
+(global-set-key (kbd "M-s f") 'find-name-dired)
+
+;; Find file in project
+(global-set-key (kbd "C-x o") 'find-file-in-project)
+
+;; Find file in project, with specific patterns
+(global-unset-key (kbd "C-x C-o")) ;; which used to be delete-blank-lines (also bound to C-c C-<return>)
+(global-set-key (kbd "C-x C-o ja") (ffip-create-pattern-file-finder "*.java"))
+(global-set-key (kbd "C-x C-o js") (ffip-create-pattern-file-finder "*.js"))
+(global-set-key (kbd "C-x C-o ht") (ffip-create-pattern-file-finder "*.html"))
+(global-set-key (kbd "C-x C-o jp") (ffip-create-pattern-file-finder "*.jsp"))
+(global-set-key (kbd "C-x C-o cs") (ffip-create-pattern-file-finder "*.css"))
+(global-set-key (kbd "C-x C-o cl") (ffip-create-pattern-file-finder "*.clj"))
+(global-set-key (kbd "C-x C-o el") (ffip-create-pattern-file-finder "*.el"))
+(global-set-key (kbd "C-x C-o md") (ffip-create-pattern-file-finder "*.md"))
+(global-set-key (kbd "C-x C-o rb") (ffip-create-pattern-file-finder "*.rb"))
+(global-set-key (kbd "C-x C-o or") (ffip-create-pattern-file-finder "*.org"))
+(global-set-key (kbd "C-x C-o ph") (ffip-create-pattern-file-finder "*.php"))
+(global-set-key (kbd "C-x C-o tx") (ffip-create-pattern-file-finder "*.txt"))
+(global-set-key (kbd "C-x C-o vm") (ffip-create-pattern-file-finder "*.vm"))
+(global-set-key (kbd "C-x C-o xm") (ffip-create-pattern-file-finder "*.xml"))
+(global-set-key (kbd "C-x C-o pr") (ffip-create-pattern-file-finder "*.properties"))
+(global-set-key (kbd "C-x C-o in") (ffip-create-pattern-file-finder "*.ini"))
+(global-set-key (kbd "C-x C-o gr") (ffip-create-pattern-file-finder "*.groovy"))
+(global-set-key (kbd "C-x C-o ga") (ffip-create-pattern-file-finder "*.gradle"))
+(global-set-key (kbd "C-x C-o !") (ffip-create-pattern-file-finder "*"))
+
+;; View occurrence in occur mode
+(define-key occur-mode-map (kbd "v") 'occur-mode-display-occurrence)
+(define-key occur-mode-map (kbd "n") 'next-line)
+(define-