Emacs Configuration

1-29-2015

About

This File

This is an emacs org-mode file which generates my personal emacs configuration. These settings are used along side Emacs Prelude. I share my config across multiple machines and use git to version it. Here is my configuration on github.

This Emacs Config

My workflow involves running mainly one window in a frame. And running many frames at once (all in daemon mode). Many settings are a consequence of this.

Why?

I run emacs in a tiling window manager, it takes care of tabing and window management needs. Its nicer when all my programs operate tabs/windowing consistently.

My config will be periodically updated on my blog, and since I often piece things together quickly and refine it later, there's likely to be mess from time to time

Emacs initialization

Personal Information

Set my name and email

(setq user-full-name "Mike McFarland"
      user-mail-address "mikedmcfarland@gmail.com")

Prelude Modules

Prelude includes many packages you can toggle. Here's the list I enabled.

Packages

My required packages, prelude will make sure these are downloaded and required at startup.

(add-to-list 'load-path "~/.emacs.d/vendor/use-package")
(require 'use-package)

(add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))

(prelude-require-packages
 '(s
   restclient
   powerline-evil
   yasnippet
   multiple-cursors
   expand-region
   evil-nerd-commenter
   js2-refactor
   key-chord
   aggressive-indent
   req-package
   ))

(require 'req-package)

Backups

store backups in one folder instead next to the file

(setq backup-directory-alist `((".*" . "~/.emacs.d/.backups")))

(setq make-backup-files t               ; backup of a file the first time it is saved.
      backup-by-copying t               ; don't clobber symlinks
      version-control t                 ; version numbers for backup files
      delete-old-versions t             ; delete excess backup files silently
      delete-by-moving-to-trash t
      kept-old-versions 6               ; oldest versions to keep when a new numbered backup is made (default: 2)
      kept-new-versions 9               ; newest versions to keep when a new numbered backup is made (default: 2)
      auto-save-default t               ; auto-save every buffer that visits a file
      auto-save-timeout 20              ; number of seconds idle time before auto-save (default: 30)
      auto-save-interval 200            ; number of keystrokes between auto-saves (default: 300)
      )

Auto Save

Auto save is fine, just don't do it in my working directories

(defvar my-auto-save-folder "~/.emacs.d/auto-save"); folder for auto-saves
(setq auto-save-list-file-prefix "~/.emacs.d/auto-save/.saves-"); set prefix for auto-saves
(setq auto-save-file-name-transforms `((".*" ,my-auto-save-folder t))); location for all auto-save files
(setq tramp-auto-save-directory my-auto-save-folder); auto-save tramp files in local directory

Save Place

Save cursor position when reopening a file

(setq save-place-file "~/.emacs.d/saveplace")
(setq-default save-place t)
(require 'saveplace)

Aesthetics

Load the monokai theme (Sublime Text's default colors)

(add-to-list 'custom-theme-load-path "~/.emacs.d/vendor/monokai-emacs")
(load-theme 'monokai t)

Make sure we edit using visual lines, instead of actual line breaks. Helps when word wrapping is on.

(global-visual-line-mode t)

set the font, this seems to work for the emacs daemon, other methods were requiring me to re-run expressions after connecting another client

(add-to-list 'default-frame-alist '(font .   "Source Code Pro 12" ))

Configure whitespace mode

(setq whitespace-style
      '(face tabs empty trailing))

A few miscallaneious things

(display-time-mode t)

;;smooth scrolling
(setq scroll-margin 5
      scroll-conservatively 9999
      scroll-step 1)


(defun set-my-margins ()
  (interactive)
  (setq left-margin-width 1)
  ;; (setq right-margin-width 1)
  (setq left-fringe-width 5)
  (setq right-fringe-width 10)
  )

(add-hook 'text-mode-hook 'set-my-margins)
(add-hook 'prog-mode-hook 'set-my-margins)



(setq linum-format " %d ")

(use-package hlinum
  :ensure
  :config
  (progn
    (hlinum-activate)
    ))

(add-hook 'prog-mode-hook 'linum-mode)

(powerline-evil-vim-color-theme)

(setq-default tab-width 2)

(add-hook 'prog-mode-hook
	  (lambda () (setq prettify-symbols-alist
		      '(
			("=>" . ?⇒)
			("<-" . ?←)
			("->" . ?→)
			("<<<" . ?⋘)
			(">>>" . ?⋙)
			("lambda" . ?λ)
			("function" . ?λ)
			))))


(global-prettify-symbols-mode t)

Configuration helpers

I map evil shortcuts allot, these are those mappings shorter.

;;Unmap prelude evils ace jump
(define-key evil-normal-state-map (kbd "SPC") nil)

(defun map-all-evil-states (keys action)
  "maps key combination to action for all evil modes"
  (define-key evil-normal-state-map keys action)
  (define-key evil-insert-state-map keys action)
  (define-key evil-visual-state-map keys action))
(defun map-n-v-evil-states (keys action)
  (define-key evil-normal-state-map keys action)
  (define-key evil-visual-state-map keys action))

(defun map-n-i-evil-states (keys action)
  (define-key evil-normal-state-map keys action)
  (define-key evil-insert-state-map keys action))

Javascript

js2 refactor

set js refactors prefix

(js2r-add-keybindings-with-prefix (kbd "C-c C-r"))

Indentation

Two spaces is nice. Set it as default, and set evils shift width when in js mode as well.

(setq-default js-indent-level 2)
(add-hook 'prelude-js-mode-hook
	  (lambda() (setq evil-shift-width 2)))

(setq-default js2-basic-offset 2)

js2 settings

Highlighting/style settings. Don't worry about semicolons. And use harmony.

(setq-default js2-highlight-level 3)
(setq-default js2-highlight-external-variables t)

(setq-default js2-concat-multiline-strings t)
(setq-default js2-strict-missing-semi-warning nil)
(setq-default js2-pretty-multiline-declarations nil)

(setq-default js2-language-version 200)

configuration so we use jslint, and allow node externs

(setq-default js2-include-jslint-globals t)
(setq-default js2-include-node-externs t)

Jump to test or implementation

Little script for jumping between tests and implementations. Made with a little help. Expects A structure with a lib directory for implementations, and a test directory for specs, Like so:

lib/foo.js
lib/someDir/bar.js
test/fooSpec.js
test/someDir/barSpec.js
(defun js-jump-to (current from to format-name)
  (find-file
   (cl-loop with parts = (reverse current)
	    with fname = (file-name-sans-extension (cl-first parts))
	    for (name . rest) on (cl-rest parts)
	    until (string-equal name from)
	    collect name into names
	    finally (cl-return
		     (mapconcat 'identity
				(nconc (reverse rest)
				       (list to)
				       (reverse names)
				       (list (funcall format-name fname) )) "/" )))))

(defun js-format-impl-name (fname)
  (format "%s.js" (replace-regexp-in-string "Spec" "" fname)))

(defun js-format-test-name (fname)
  (format "%sSpec.js" fname))

(defun js-jump-to-implementation-or-test ()
  (interactive)
  (let ((current (split-string (buffer-file-name) "/")))
    (cond
     ((member "test" current) (js-jump-to current "test" "lib" 'js-format-impl-name))
     ((member "lib" current)  (js-jump-to current "lib" "test" 'js-format-test-name))
     (t (error "not within a test or lib directory"))
  )))

align declarations

Aligning require statements at the top, or variable literal assignments is something that can make code more readable, but its too painful to do manually all the time. This seeks out '=' or ':' in lines, and aligns them.

(defun js-align-assignments (&optional NUM)
  (interactive "p")
  ;;Keep them separate align calls, otherwise colons align with spaces
  ;;if they're in the same region
  (align-regexp (region-beginning) (region-end) "\\(\\s-*\\)=")
  (align-regexp (region-beginning) (region-end) "\\(\\s-*\\):"))

(evil-define-key `normal js2-mode-map (kbd "SPC \\") 'js-align-assignments)
(evil-define-key `visual js2-mode-map (kbd "SPC \\") 'js-align-assignments)

convert module imports

(defun convert-cmjs-to-es6 ()
  (interactive)
  (let ((from (concat
	       "\\(const\\|var\\|let\\)[[:space:]]+"
	       "\\([A-Za-z0-9_\$]+\\)[[:space:]]+=[[:space:]]+"
	       "require([\"|']\\([^'|^\"]+\\)[\"|'])"))
	(to "import %s from '%s'"))
    (beginning-of-buffer)
    (while (re-search-forward from nil t)
      (replace-match (format to (match-string 2) (match-string 3))))))

misc

(add-to-list 'auto-mode-alist '("\\.jsx\\'" . js2-mode))

Python

(setq python-guess-indent nil)
(setq python-indent 2)

C#

(use-package csharp-mode
  :ensure
  :config
  (progn
    ))

(use-package omnisharp
  :load-path "~/.emacs.d/vendor/omnisharp-emacs"
  :disabled t
  :config
  (progn
    (add-hook 'csharp-mode-hook 'omnisharp-mode)
    (setq omnisharp-debug t)
    ))

Scala

(use-package ensime
  :ensure
  :commands ensime-scala-mode-hook
  ;; :load-path "~/.emacs.d/vendor/ensime"
  :init
  (add-hook 'scala-mode-hook 'ensime-scala-mode-hook)
  :config
  (progn
    (evil-define-key `normal ensime-mode-map (kbd "SPC ee") 'ensime-print-errors-at-point)
    (evil-define-key `normal ensime-mode-map (kbd "SPC et") 'ensime-print-type-at-point)
    (evil-define-key `normal ensime-mode-map (kbd "SPC ei") 'ensime-import-type-at-point)
    (evil-define-key `normal ensime-mode-map (kbd "SPC ed") 'ensime-edit-definition)

    (evil-define-key `normal ensime-mode-map (kbd "SPC er") 'ensime-refactor-rename)
    (evil-define-key `normal ensime-mode-map (kbd "SPC eo") 'ensime-refactor-organize-imports)
    (evil-define-key `normal ensime-mode-map (kbd "SPC en") 'ensime-reload)))

Jade

(use-package jade-mode
  :load-path "~/.emacs.d/vendor/jade-mode"
  :mode "\\.jade$")

elisp

Emacs has warnings about documenting elisp code. I don't follow these rules, so disable them

(setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc))

dot diagrams

(use-package graphviz-dot-mode
  :load-path "~/.emacs.d/vendor/graphviz-dot-mode"
  :mode "\\.dot$")

Snippets And Autocompletion

Yasnippet

Load YASnippet configuration

(setq yas-snippet-dirs '("~/.emacs.d/snippets"))
(yas-reload-all)
(add-hook 'prog-mode-hook 'yas-minor-mode)
(add-hook 'text-mode-hook 'yas-minor-mode)
(add-hook 'org-mode-hook  'yas-minor-mode)

Company

Using mainly companys default prelude settings. Except I don't want any delay in suggestions.

(setq company-idle-delay 0)

Tab

So, Company mode and YASnippet step on each other toes. So These functions are to help TAB have the behaviour I expect. Attempt these actions, and do the first one that works.

  1. expand yas snippet
  2. auto complete with company
  3. indent
(defun check-expansion ()
  (save-excursion
    (if (looking-at "\\_>") t
      (backward-char 1)
      (if (looking-at "\\.") t
	(backward-char 1)
	(if (looking-at "->") t nil)))))

(defun do-yas-expand ()
  (let ((yas/fallback-behavior 'return-nil))
    (yas/expand)))

(defun tab-indent-or-complete ()
  (interactive)
  (if (minibufferp)
      (minibuffer-complete)
    (if (or (not yas/minor-mode)
	    (null (do-yas-expand)))
	(if (check-expansion)
	    (company-complete-common)
	  (indent-for-tab-command)))))

I became fairly frustrated trying to bind tab, so this is a fairly dirty way of altering the keymaps of company and yas-minor modes.

(defun bind-tab-properly ()
  "Binds tab to tab-indent-or-complete, overwritting yas and company bindings"
  (interactive)
  ;;overwrite yas and company tab mappings
  (define-key yas-minor-mode-map (kbd "<tab>") 'tab-indent-or-complete)
  (define-key yas-minor-mode-map (kbd "TAB") 'tab-indent-or-complete)
  (define-key company-active-map [tab] 'tab-indent-or-complete)
  (define-key company-active-map (kbd "TAB") 'tab-indent-or-complete))

(add-hook 'company-mode-hook 'bind-tab-properly)

Custom Commands

create-new-empty-buffer

(defun create-new-empty-buffer ()
  "Open a new empty buffer."
  (interactive)
  (let ((buf (generate-new-buffer "untitled")))
    (switch-to-buffer buf)
    (funcall (and initial-major-mode))
    (setq buffer-offer-save t)))

just-my-main-window

This is something I created with some help. The main window is defined per frame, and at any point I may wish to close all windows besides the main (to get back to work).

(defun mark-this-window-as-main ()
  "Mark the current window as the main window."
  (interactive)
  (mapc (lambda (win) (set-window-parameter win 'main nil))
    (window-list))
  (set-window-parameter nil 'main t))

(defun get-main-window()
  "Find and return the main window or nil if non exists."
  (cl-find-if (lambda (win) (window-parameter win 'main)) (window-list)))

(defun just-my-main-window ()
  "Show only the main window"
  (interactive)
  (delete-other-windows (get-main-window)))

Add some hooks to make sure the appropriate window is marked as main. That'll be code and document windows.

(add-hook 'prog-mode-hook 'mark-this-window-as-main)
(add-hook 'text-mode-hook 'mark-this-window-as-main)
(add-hook 'org-mode-hook 'mark-this-window-as-main)

kill file and buffer

(defun delete-this-buffer-and-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)))
	(error "Buffer '%s' is not visiting a file!" name)
      (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)))))

(map-n-v-evil-states " df" 'delete-this-buffer-and-file)

Insert todays date

(defun put-todays-date ()
  (interactive)
  (insert (shell-command-to-string "todays-date")))

Org-mode

Misc

Automatically put us into org-indent-mode when editing org files

(add-hook 'org-mode-hook 'org-indent-mode)

some custom shortcuts

(evil-define-key 'normal org-mode-map " ol" 'org-toggle-link-display)
;; (setq org-src-fontify-natively t)
(setq org-src-fontify-natively nil)

magit-ediff

When using magit-ediff org files sometimes have collapsed headings. This makes it fairly hard to resolve any conflicts. This makes everything visible when using ediff.

(add-hook 'ediff-prepare-buffer-hook 'f-ediff-prepare-buffer-hook-setup)
(defun f-ediff-prepare-buffer-hook-setup ()
  ;; specific modes
  (cond ((eq major-mode 'org-mode)
	 (f-org-vis-mod-maximum))
	;; room for more modes
	)
  ;; all modes
  (setq truncate-lines nil))
(defun f-org-vis-mod-maximum ()
  "Visibility: Show the most possible."
  (cond
   ((eq major-mode 'org-mode)
    (visible-mode 1)  ; default 0
    (setq truncate-lines nil)  ; no `org-startup-truncated' in hook
    (setq org-hide-leading-stars t))  ; default nil
   (t
    (message "ERR: not in Org mode")
    (ding))))

org-babel

language definitions for org babel

(org-babel-do-load-languages
 'org-babel-load-languages
 '((js . t)
   (emacs-lisp . t)
   (scala . t)
   (sh . t)
   (dot . t)
   (python . t)
   (gnuplot . t)
   ))

open source code blocks in new frames.

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

org mode maps the language mode by name, remap to proper modes when the language name doesn't match exactly

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

lets use babel-node instead of node (for es6 features). This needs babel installed globally to work.

(setq org-babel-js-cmd "babel-node")

use relative file links when tangling/detangling

(setq org-babel-tangle-use-relative-file-links t)

org-agenda

Pull in all org files for my agenda from:

  • my ~/notes directory
  • recursively through my ~/projects
    (defun update-org-agenda-files ()
      (interactive)
      (let*
          ((entries
    	'("~/notes"))
           (rec-entries
    	'("~/projects"))
           (rec-files
    	(mapcan
    	 'find-org-file-recursively
    	 rec-entries))
    
           (all-org-files (append entries rec-files)))
        (setq org-agenda-files all-org-files)))
    
    (defun find-org-file-filter (name)
      (not (s-contains? "node_modules" name)))
    
    ;; recursively find .org files in provided directory
    ;; modified from an Emacs Lisp Intro example
    (defun find-org-file-recursively (directory &optional filext)
      "Return .org and .org_archive files recursively from DIRECTORY.
    If FILEXT is provided, return files with extension FILEXT instead."
      ;; FIXME: interactively prompting for directory and file extension
      (let* (org-file-list
    	 (case-fold-search t)           ; filesystems are case sensitive
    	 (file-name-regex "^[^.#].*")   ; exclude .*
    	 (filext (if filext filext "org$\\\|org_archive"))
    	 (fileregex (format "%s\\.\\(%s$\\)" file-name-regex filext))
    	 (cur-dir-list (directory-files directory t file-name-regex)))
        ;; loop over directory listing
        (dolist (file-or-dir cur-dir-list org-file-list) ; returns org-file-list
          (cond
           ((file-regular-p file-or-dir) ; regular files
    	(if (string-match fileregex file-or-dir) ; org files
    	    (add-to-list 'org-file-list file-or-dir)))
           ((file-directory-p file-or-dir)
    	(if (find-org-file-filter file-or-dir)
    	    (dolist (org-file (find-org-file-recursively file-or-dir filext)
    			      org-file-list) ; add files found to result
    	      (add-to-list 'org-file-list org-file))))))))
    
    (update-org-agenda-files)
    

Configure shortcuts and settings for org agenda

(use-package org-agenda
  :bind ("C-c a" . org-agenda)
  :config
  (progn
    ;;we dont use evil in agenda, but j/k should be vim style
    (define-key org-agenda-mode-map "j" 'org-agenda-next-line)
    (define-key org-agenda-mode-map "k" 'org-agenda-previous-line)
    ;; (setq org-agenda-start-with-follow-mode t)
    ))

org-capture

Set up capture templates and shortcuts.

(use-package org-capture
  :config
  (progn
    (setq org-capture-templates
	  '(
	    ("t" "Todo" entry (file+headline "~/notes/tasks.org" "Tasks")
	     "* TODO %?\n  %i\n  %a")
	    ("e" "Emacs Todo" entry (file+headline "~/.emacs.d/personal/settings.org" "Tasks")
	     "* TODO %?\n  %i\n  %a")
	    ("p" "Project Todo" entry (function bear-org-project-jump-to-tasks)
	     "* TODO %?\n  %i\n  %a")
	    ("j" "Journal" entry (file+datetree "~/notes/journal.org")
	     "* %?\nEntered on %U\n  %i\n  %a")))

    (map-n-v-evil-states " oc" 'org-capture)
    ))

These functions allow the project template to work. They add tasks to the appropriate org file (and section) based on the current project.

(defun bear-org-jump-to-tasks()
  "Jumps to the tasks within the current org file. If no task section
exists, one is created"
  (interactive)
  (goto-char (point-min))
  (unless
      (re-search-forward
       "^[[:space:]]*\\*.[[:space:]]*\\(tasks\\|planned\\|todos\\)[[:space:]]*"
       nil t)
    (progn
      (goto-char (point-max))
      (newline)
      (insert "* Tasks")
      (newline))))

(defun bear-org-project-jump ()
  "Jumps to the projects org file (dominating file named readme.org or
todo.org in)"
  (interactive)
  (let* ((dir (file-name-directory (buffer-file-name)))
	 (match "^\\([rR][eE][aA][dD][mM][dE]\\|[tT][oO][dD][oO]\\)\\.[oO][rR][gG]$")
	 (get-matched (lambda(parent) (car (directory-files parent nil match))))
	 (matched-dir (locate-dominating-file dir get-matched))
	 (matched-file (funcall get-matched matched-dir)))
    (find-file (concat matched-dir matched-file))))


(defun bear-org-project-jump-to-tasks ()
  "Finds the org file and location associated with project tasks, and jumps to it"
  (interactive)
  (if (string= (file-name-extension (buffer-file-name)) "org")
      (bear-org-jump-to-tasks)
    (progn
      (bear-org-project-jump)
      (bear-org-jump-to-tasks))))

org-clock (time-tracking)

set up org clocking shortcuts and settings

(setq org-clock-persist 'history)
(org-clock-persistence-insinuate)

(use-package org-clock
  :config
  (progn
    (map-n-v-evil-states " oki" 'org-clock-in)
    (map-n-v-evil-states " oko" 'org-clock-out)
    (map-n-v-evil-states " okx" 'org-clock-in-last)
    (map-n-v-evil-states " oke" 'org-clock-out)
    (add-hook 'org-clock-in-prepare-hook
	      'my-org-mode-ask-effort)

    (defun my-org-mode-ask-effort ()
      "Ask for an effort estimate when clocking in."
      (unless (org-entry-get (point) org-effort-property)
	(let ((effort
	       (completing-read
		(format "%s: " org-effort-property)
		(org-entry-get-multivalued-property (point) org-effort-property))))
	  (unless (equal effort "")
	    (org-set-property org-effort-property effort)))))
    ))

org-sync

 (add-to-list 'load-path "~/.emacs.d/vendor/org-sync")
 (require 'os)
;; (require 'os-bb)
;; (require 'os-github)
;; (require 'os-rmine)

org-scrum

(use-package gnuplot
  :ensure
  :config
  (progn
    ))

(use-package gnuplot-mode
  :ensure
  :config
  (progn
    ))

(use-package scrum
  :load-path "~/.emacs.d/vendor/emacs-scrum"
  :config
  (progn
    (setq org-effort-property "ESTIMATED")

    ))

Web Mode

(use-package web-mode
  :ensure
  :config
  (progn
    (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
    ;; (add-to-list 'auto-mode-alist '("\\.jsx\\'" . web-mode))
    ))

Key Bindings

A bunch of keybindings that really should be put into appropriate sections.

;;remove forward motion from evil motion ma
(define-key evil-motion-state-map " " nil)

(define-key evil-normal-state-map (kbd "g t") 'js-jump-to-implementation-or-test)

;;map control s p to finding a file with projectile
(global-set-key (kbd "C-S-p") 'projectile-find-file)

;;have j and k go down to next visual line
(define-key evil-normal-state-map (kbd "j") 'evil-next-visual-line)
(define-key evil-normal-state-map (kbd "k") 'evil-previous-visual-line)

(global-set-key (kbd "C-c C-n") 'create-new-empty-buffer)
(global-set-key (kbd "C-c C-o") 'just-my-main-window)
(define-key org-mode-map (kbd "C-c C-o") 'just-my-main-window)

(global-set-key (kbd "RET") 'newline-and-indent)

;;Remap alt p to switching a project with projectile
(global-set-key (kbd "M-p") 'projectile-switch-project)

(global-set-key (kbd "C-h") 'previous-buffer)
(global-set-key (kbd "C-l") 'next-buffer)
(global-set-key (kbd "C-S-h") 'winner-undo)
(global-set-key (kbd "C-S-l") 'winner-redo)
(global-set-key (kbd "C-c C-q") 'kill-this-buffer)

(global-set-key (kbd "C-S-m") 'mc/mark-all-like-this-dwim)
(map-n-v-evil-states (kbd "SPC m") 'mc/mark-next-like-this)
(define-key mc/keymap (kbd "C-n") 'mc/mark-next-like-this)
(define-key mc/keymap (kbd "C-S-n") 'mc/skip-to-next-like-this)
(define-key mc/keymap (kbd "C-p") 'mc/unmark-next-like-this)
(define-key mc/keymap (kbd "C-S-p") 'mc/unmark-previous-like-this)

(global-set-key (kbd "C-S-SPC") 'er/contract-region)
(global-set-key (kbd "C-SPC") 'er/expand-region)

(map-n-v-evil-states (kbd "SPC j") 'evil-ace-jump-two-chars-mode)
(map-n-v-evil-states (kbd "SPC r") 'js2r-rename-var)
(map-n-v-evil-states (kbd "SPC l") 'js2r-log-this)
(map-n-v-evil-states (kbd "SPC c") 'evilnc-comment-or-uncomment-lines)
(map-n-v-evil-states (kbd "SPC p") 'projectile-find-file)

(global-set-key (kbd "C-s") 'save-buffer)
(map-n-v-evil-states (kbd "SPC s") 'save-buffer)

(define-key evil-normal-state-map (kbd "gp") 'evil-select-last-yanked-text)

(setq evil-default-state `normal)
(evil-define-key `normal org-src-mode-map (kbd "SPC '") 'org-edit-src-save-and-exit)
(evil-define-key `normal org-src-mode-map (kbd "SPC s") 'org-edit-src-save)
(define-key org-src-mode-map (kbd "C-s") 'org-edit-src-save)
(evil-define-key `normal org-mode-map (kbd "SPC '") 'org-edit-special)


(defun org-edit-src-save-and-exit()
  (interactive)
  (org-edit-src-save)
  (org-edit-src-exit 'exit))

;; (map-n-v-evil-states  (kbd "SPC i") 'helm-swoop)
(global-set-key (kbd "C-S-i") 'helm-projectile-ack)

;; (define-key isearch-mode-map (kbd "M-i") 'helm-swoop-from-isearch)
;; (require 'helm-swoop)
;; (define-key helm-swoop-map (kbd "M-i") 'helm-multi-swoop-all-from-helm-swoop)

(map-n-v-evil-states (kbd "go") 'find-file-other-frame)
(define-key company-active-map (kbd "C-n") 'company-select-next-or-abort)
(define-key company-active-map (kbd "C-p") 'company-select-previous-or-abort)

(define-key js2-mode-map (kbd "C-c C-o") nil)

(define-key evil-normal-state-map (kbd "C-u") 'evil-scroll-up)
(define-key evil-visual-state-map (kbd "C-u") 'evil-scroll-up)
(define-key evil-insert-state-map (kbd "C-u")
  (lambda ()
    (interactive)
    (evil-delete (point-at-bol) (point))))

(map-n-i-evil-states (kbd "C-S-k") 'move-text-up)
(map-n-i-evil-states (kbd "C-S-j") 'move-text-down)

;; (evil-define-key `normal js2-mode-map (kbd "C-S-k") 'js2r-move-line-up)
;; (evil-define-key `insert js2-mode-map (kbd "C-S-k") 'js2r-move-line-up)
;; (evil-define-key `normal js2-mode-map (kbd "C-S-j") 'js2r-move-line-down)
;; (evil-define-key `insert js2-mode-map (kbd "C-S-j") 'js2r-line-move-down)

;; (define-key evil-normal-state-local-map (kbd "<right>") 'hannesr/evil-normal-move-character-forward)
;; (define-key evil-normal-state-local-map (kbd "<left>") 'hannesr/evil-normal-move-character-backward)
;; (define-key evil-visual-state-local-map (kbd "<right>") 'hannesr/evil-visual-move-selection-forward)
;; (define-key evil-visual-state-local-map (kbd "<left>") 'hannesr/evil-visual-move-selection-backward)

Key chords

kj is a convenient exit compared to escape

(setq key-chord-two-keys-delay 0.5)
(key-chord-define evil-insert-state-map "kj" 'evil-normal-state)
(key-chord-mode +1)

Evil

Evil is an emulation layer for Vim ontop of emacs. It's a very faithful emulation.

Select last yanked text

This function will select whatever texted you pasted last (in the buffer).

(defun evil-select-last-yanked-text ()
  "uses evils markers to select the last yanked text"
  (interactive)
  (evil-goto-mark ?\[)
  (evil-visual-char)
  (evil-goto-mark ?\]))

;; (defun evil-yank-and-indent-text ()
;;   (interactive)
;;   (evil-paste-after)
;;   (evil-indent)
;;   (evil-goto-mark ?\[))

Escape should exit things

Stole this from somewhere, attempts to map escape to various exits. It isn't perfect however, and I still need ctrl g from time to time.

(defun minibuffer-keyboard-quit ()
  "Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
  (interactive)
  (if (and delete-selection-mode transient-mark-mode mark-active)
      (setq deactivate-mark  t)
    (when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
    (abort-recursive-edit)))

(define-key evil-normal-state-map [escape] 'keyboard-quit)
(define-key evil-visual-state-map [escape] 'keyboard-quit)
(define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
(define-key mc/keymap [escape] 'mc/keyboard-quit)
(global-set-key [escape] 'evil-exit-emacs-state)

Miscellaneous

Evil star allows you to use the * shortcut on visually selected text. Should be default vim behavior if you ask me.

(require 'evil-visualstar)
(global-evil-visualstar-mode t)

Let motions (like f and F) cross lines.

(setq evil-cross-lines t)

Evil vs mutliple-cursors

Multiple cursors and evil don't play well together. I ripped this solution from these configs. It disables evil while multiple-cursors is running. This allows js2-refactor to still work nicely for me. I can still use multiple cursors too, but it's less awesome without vim keybindings.

(defun user-utils/evil-visual-or-normal-p ()
  "True if evil mode is enabled, and we are in normal or visual mode."
  (and (bound-and-true-p evil-mode)
       (not (memq evil-state '(insert emacs)))))

(defun mc-evil-compat/switch-to-emacs-state ()
  (when (user-utils/evil-visual-or-normal-p)

    (setq mc-evil-compat/evil-prev-state evil-state)

    (when (region-active-p)
      (setq mc-evil-compat/mark-was-active t))

    (let ((mark-before (mark))
	  (point-before (point)))

      (evil-emacs-state 1)

      (when (or mc-evil-compat/mark-was-active (region-active-p))
	(goto-char point-before)
	(set-mark mark-before)))))

(defun mc-evil-compat/back-to-previous-state ()
  (when mc-evil-compat/evil-prev-state
    (unwind-protect
	(case mc-evil-compat/evil-prev-state
	  ((normal visual) (evil-force-normal-state))
	  (t (message "Don't know how to handle previous state: %S"
		      mc-evil-compat/evil-prev-state)))
      (setq mc-evil-compat/evil-prev-state nil)
      (setq mc-evil-compat/mark-was-active nil))))

(add-hook 'multiple-cursors-mode-enabled-hook
	  'mc-evil-compat/switch-to-emacs-state)
(add-hook 'multiple-cursors-mode-disabled-hook
	  'mc-evil-compat/back-to-previous-state)

(defun mc-evil-compat/rectangular-switch-state ()
  (if rectangular-region-mode
      (mc-evil-compat/switch-to-emacs-state)
    (setq mc-evil-compat/evil-prev-state nil)))

;; When running edit-lines, point will return (position + 1) as a
;; result of how evil deals with regions
(defadvice mc/edit-lines (before change-point-by-1 activate)
  (when (user-utils/evil-visual-or-normal-p)
    (if (> (point) (mark))
	(goto-char (1- (point)))
      (push-mark (1- (mark))))))

(add-hook 'rectangular-region-mode-hook 'mc-evil-compat/rectangular-switch-state)

(defvar mc--default-cmds-to-run-once nil)

Multiple Cursors

mc's fast key expansion was getting in the way of my other shortcuts. So Disable it.

(require 'multiple-cursors)
;; Don't use expand-region fast keys
(setq expand-region-fast-keys-enabled nil)

Ace Jump

I thought i'd use this, I really don't. It's cool but less functional then I expected (for my workflow)

(setq ace-jump-mode-scope 'frame)
(setq ace-jump-mode-move-keys
      (loop for i from ?a to ?z collect i))

(require 'ace-jump-mode)
(defun evil-ace-jump-two-chars-mode (query-char query-char-2)
  "evil AceJump two chars mode"
  (interactive (list (read-char "First Char:")
		     (read-char "Second:")))

  (if (eq (ace-jump-char-category query-char) 'other)
      (error "[AceJump] Non-printable character"))

  ;; others : digit , alpha, punc
  (setq ace-jump-query-char query-char)
  (setq ace-jump-current-mode 'evil-ace-jump-char-mode)
  (ace-jump-do (regexp-quote (concat (char-to-string query-char)
				     (char-to-string query-char-2)))))

(defun point-greater-then (v)
  (lexical-let ((v v))
    (lambda () (> (point) v))))

(defun point-less-then (v)
  (lexical-let ((v v))
    (lambda () (< (point) v))))

(defun evil-ace-jump-word-mode-backward ()
  (interactive)
  (let ((ace-jump-search-filter (point-less-then (point))))
    (evil-ace-jump-word-mode)))

(defun evil-ace-jump-word-mode-forward ()
  (interactive)
  (let ((ace-jump-search-filter (point-greater-then (point))))
    (evil-ace-jump-word-mode)))

(map-n-v-evil-states " j" 'evil-ace-jump-word-mode-forward)
(map-n-v-evil-states " k" 'evil-ace-jump-word-mode-backward)

Avy Jump

(use-package avy
  :load-path "~/.emacs.d/vendor/avy"
  :commands avy-goto-char
  :config
  (progn
    (map-n-v-evil-states (kbd "SPC g") 'avy-goto-char)

    ))

;; (defun bear-avy-goto-char (char)
;;   "Jump to the currently visible CHAR.
;; The window scope is determined by `avy-all-windows' (ARG negates it)."
;;   (interactive (list (read-char "char: ")
;;                      current-prefix-arg))
;;   (cl-flet
;;       ((pred (mf (x)(> x 0)))

;;        (avy-goto-char char))
;;     ))

wgrep-ag

Still playing with this. Haven't used it enough times to figure out how it should be.

(use-package wgrep
  :load-path "~/.emacs.d/vendor/Emacs-wgrep"
  :config
  (progn
    (use-package wgrep-helm)
    (use-package wgrep-ack)
    ;; (use-package wgrep-ag)

    ;; (use-package helm-ag
    ;;   :ensure t
    ;;   :load-path "~/.emacs.d/vendor/emacs-helm-ag"
    ;;   :config
    ;;   (define-key helm-ag-map (kbd "C-c C-s") 'helm-grep-save-results))
    ;; )
    ))

Drag Stuff

Useful to drag lines up and down.

(use-package drag-stuff
    :load-path "~/.emacs.d/vendor/drag-stuff.el"
    :bind
    (("C-S-j" . drag-stuff-down)
     ("C-S-k" . drag-stuff-up))
    :config
    (drag-stuff-mode t))

Erc

I use erc for chatting with bitlbee.

(setq erc-auto-query 'frame
      erc-query-display 'frame

      erc-interpret-mirc-color t

      erc-hide-list '("JOIN" "PART" "QUIT")

      erc-kill-buffer-on-part t
      erc-kill-queries-on-quit t
      erc-kill-server-buffer-on-quit t)


(evil-define-key `normal erc-mode-map (kbd "RET") 'erc-send-current-line)
(evil-define-key `visual erc-mode-map (kbd "RET") 'erc-send-current-line)

Automatically put us into flyspell-mode when using erc

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

Notify on message

;; (defun bear-erc-privmsg-notify (proc res)
;;   (flet ((rtrim-string (s) (replace-regexp-in-string "\\([[:space:]\n]*$\\)" "" s)))
;;     (let ((channel-buffers     (erc-channel-list proc))
;;           (sender              (or (car (split-string (erc-response.sender res) "!"))
;;                                    (erc-response.sender res)))
;;           (target-channel-name (car (erc-response.command-args res)))
;;           (xwindow-class       (rtrim-string (shell-command-to-string "stumpish current-window-class"))))
;;       (unless (or (string= xwindow-class "Emacs") ; we are in an emacs frame
;;                   (member (get-buffer target-channel-name) channel-buffers)) ; this is a channel message
;;         (progn (notify "Instant message!"
;;                        (format "Direct message from %s" sender)
;;                        :icon     "/home/ysph/.emacs.d/emacs.png"
;;                        :timeout  120000
;;                        :app "ERC")
;;                nil        ; we never want this to interrupt processing
;;                )))))
;; (add-hook 'erc-server-PRIVMSG-functions 'bear-erc-privmsg-notify)

Aggressive indent

Has dependencies so I used prelude-require for now, should refactor to use use package instead.

Enable it general, with the exception of a few modes where it gets in the way.

(defvar aggressive-indent-mode-exceptions
  '(scala-mode,jade-mode,python-mode))

(defun maybe-aggressive-indent-mode ()
  (unless (member major-mode aggressive-indent-mode-exceptions)
    (aggressive-indent-mode t)))

(add-hook 'prog-mode-hook 'maybe-aggressive-indent-mode)
(add-hook 'text-mode-hook 'maybe-aggressive-indent-mode)

Relative line numbers

Some customizations that allow relative line numbers to be described as alternating letters. This way I can jump to those lines via shortcut. This has replace ace-jump-line for me since I don't have to wait for feedback.

(use-package stripe-buffer
  :load-path "~/.emacs.d/vendor/stripe-buffer"
  :config
  (progn
    (add-hook 'prog-mode-hook 'stripe-buffer-mode)
    (add-hook 'org-mode-hook 'stripe-buffer-mode)

    (set-face-background stripe-highlight-face "#2A2B25")
    ))

(use-package hl-line
  :ensure
  :config
  (progn
    (add-hook 'prog-mode-hook 'hl-line-mode)
    (add-hook 'org-mode-hook 'hl-line-mode)
    ))


(use-package relative-line-numbers
  :ensure
  :config
  :disabled
  (progn
    (defun relative-jump-chars-list ()
      (list
       ?f ?d ?s ?a ?v
       ?t ?b ?c ?e ?g
       ?u ?h ?i ?y ?j
       ?k ?l ?m ?n ?o
       ?p ?q ?r ?w ?x
       ?z
       ))

    (defun relative-jump-chars-permutations-default()
      (cl-mapcar
       'char-to-string
       (relative-jump-chars-list)
       ))

    (defvar relative-jump-chars-permutations
      (relative-jump-chars-permutations-default))

    ;; (defun permutations-up-to (size elements)
    ;;   (if (= size 0)
    ;;       nil
    ;;     (append  (permutations-up-to (- size 1) elements) (permutations size elements))
    ;;     ))

    ;; (defun permutations (size elements)
    ;;   (if (= size 0)
    ;;       '(())
    ;;     (cl-mapcan (lambda (p)
    ;;                  (cl-mapcar (lambda (e)
    ;;                               (cons e p))
    ;;                             elements))
    ;;                (permutations (- size 1) elements))))

    (defun relative-jump-chars-line-format (offset)
      (let ((rel (abs offset))
	    (pad 3)
	    (div 2))
	(cond
	 ((= rel 0)
	  (let ((str (number-to-string (line-number-at-pos))))
	    (if (> 3 (length str))
		(concat " " str)
	      str)))
	 ((or
	   (< rel pad)
	   (> (mod rel div) 0)) "")
	 (t (let ((char (nth
			 (/ (- rel pad) div)
			 relative-jump-chars-permutations)))
	      (if char
		  (concat " " char " " )
		""))))))

    (defun relative-jump-chars (char)
      (let ((pad 3)
	    (div 2)
	    (i (cl-position
		(char-to-string char)
		relative-jump-chars-permutations
		:test 'cl-equalp)))
	(+
	 (* i div)
	 (+ pad 1))))

    (defun relative-jump-chars-next ()
      (interactive)
      (evil-next-visual-line (relative-jump-chars (read-char)) ))

    (defun relative-jump-chars-previous()
      (interactive)
      (evil-previous-visual-line (relative-jump-chars (read-char)) ))

    (setq relative-line-numbers-format 'relative-jump-chars-line-format)
    (setq relative-line-numbers-motion-function 'forward-visible-line)
    ;; (map-n-v-evil-states " j" 'relative-jump-chars-next)
    ;; (map-n-v-evil-states " k" 'relative-jump-chars-previous)

    (add-hook 'prog-mode-hook 'relative-line-numbers-mode)
    (add-hook 'org-mode-hook 'relative-line-numbers-mode)
    ))

Git Gutter Fringe

(req-package git-gutter-fringe
  :config
  (progn
    (global-diff-hl-mode 0)

    (setq git-gutter-fr:side 'right-fringe)

    (add-hook 'text-mode-hook 'git-gutter-mode)
    (add-hook 'prog-mode-hook 'git-gutter-mode)
    ))

Finish config

(req-package-finish)

Tasks