Complete Computing Environment: Core Emacs

Table of Contents

(provide 'cce-emacs-core)

Bootstrapping Emacs

Make sure our customizations, if they exist, aren't made inside of the init file itself, since they'll be wiped out.

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

I have some packages that are vendorized in this repository, make sure they get loaded.

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

package.el Setup

My strategy with regard to packaging is simple – let the system handle it. In the past, I've tried to use things like Cask1 or use-package2, but Emacs bundles a package manager that is, frankly, Good Enough for my tastes. Cask requires a python bootstrap script, and Use Package's declarative syntax doesn't fit with how I want to structure CCE or my previous literate configurations. So I just use package.el, especially since I don't need to support Emacs 23 or anything crazy like that.

I mainly use Melpa for my packages, I am going to experiment with using only Org, ELPA and MELPA, but I probably will end up having to pull in Marmalade as well.

(package-initialize)

(add-to-list 'package-archives
             '("org" . "http://orgmode.org/elpa/") t)
(add-to-list 'package-archives
             '("gnu" . "http://elpa.gnu.org/packages/") t)
(add-to-list 'package-archives
             '("melpa" . "http://melpa.org/packages/") t)

I define cce/did-refresh-packages, which is used as a signal in install-pkgs that we need to refresh the package archives.

(setq cce/did-refresh-packages nil)

install-pkgs is a simple elisp function that will iterate over a list, and install each package in it, if it is not installed. If cce/did-refresh-packages is set to nil, it'll also refresh the package manager.

(defun install-pkgs (list)
  (dolist (pkg list)
    (unless (package-installed-p pkg)
      (unless cce/did-refresh-packages
        (package-refresh-contents)
        (setq cce/did-refresh-packages t))
      (package-install pkg))))

Make sure org always comes from the Org ELPA.

(setq package-pinned-archives '())
(add-to-list 'package-pinned-archives '(org-plus-contrib . "org"))
(add-to-list 'package-pinned-archives '(org . "org"))

el-get Setup

El-get is a recipe-based package manager that can be used to easily install things that aren't in the Package archives, such as random libraries that are only hosted on Emacswiki, or some random github gist or text file somewhere.

(add-to-list 'load-path "~/.emacs.d/el-get/el-get")
(setq el-get-notify-type 'message)

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

(setq cce/el-get-packages nil)

After every other module is loaded, we should install their dependent packages, which is done using (el-get 'sync).

(add-hook 'after-cce-hook (lambda ()
                            (el-get 'sync cce/el-get-packages)) t)

Helper Methods

I define a bunch of weird little helper methods all over the place, these are simple, DRY methods that I can use everywhere in my system.

I am fairly certain there is a chomp equivalent in the emacs standard library, but I've not been able to figure out what it's called yet, so I have my own for now.

(defun cce/str-chomp (str)
  "Chomp leading and trailing whitespace from STR."
  (while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'" str)
    (setq str (replace-match "" t t str)))
  str)
(defmacro cce/with-any-domain (envs &rest body)
  "Run BODY if the current CCE Env is one of ENVS."
  (let ((hostname (with-temp-buffer (shell-command "hostname" (current-buffer)) (cce/str-chomp (buffer-string)))))
    `(when (member ,hostname ,envs)
       ,@body)))
(defun scissors ()
  (interactive)
  (insert "--8<---------------cut here---------------start------------->8---\n")
  (save-excursion
    (insert "\n--8<---------------cut here---------------end--------------->8---\n")))

Long running Emacs jobs

I have a few long running jobs that I leave terminals open for. It's not great, and it's just un-needed extra overhead for things that I don't touch more than twice a day. They're not things that I can easily migrate to SystemD user services, since they require interactive input such as SSH key passphrases and that sort of things.

Instead, we can run them inside of emacs in a comint buffer using async-shell-command. I built a helper function around that to make them run forever in their own buffer.

rpm-install unison240-text
deb-install unison2.40.102
(defun cce/async-forever (command buffer-name)
  "Run a command in an async buffer forever"
  (async-shell-command (concat "while true; do " command "; sleep 5; done")
                       (get-buffer-create buffer-name)))

File Sync using Unison

I use Unison for syncing my files between different machines, mainly my Org-mode files, but also other files that are also stored in git, and things like my private dotfiles such as ~/.authinfo and my ~/News. cce/kick-off-unison is a function that'll start a number of Unison sync programs, including one for work files.

(defun cce/kick-off-unison (profile)
  "Start my unison syncs if they arent already running"
  (interactive "MProfile? ")
  (let ((unison-cmd (cond ((file-exists-p "/usr/bin/unison-2.40") "/usr/bin/unison-2.40")
                          (t "/usr/bin/unison")))
        (delay "30"))
    (if (and (not (get-buffer (concat "*unison-log " profile " *")))
             (file-exists-p unison-cmd))
        (cce/async-forever (concat unison-cmd " " profile " -ui text -auto -batch -terse -repeat " delay)
                           (concat "*unison-log " profile " *")))))

(cce/with-any-domain
 '("work" "main")
 (add-hook 'after-init-hook
           (lambda () (cce/kick-off-unison "org"))))

I have a number of different Unison hosts that I use for different operating systems; my Fedora 22 machines use a different, incompatible version of OCaml than Fedora 21, so I have to have two, and there is a third one for my Nokia n900 which runs a random Unison package that I found on the interbutts, and has to go to a third one-off Unison host.

root = /home/user/sync
root = ssh://rrix@hypervisor01.pss9.kickass.systems//srv/files/files/rrix/org/sync

servercmd = /usr/bin/unison

ignore = Path Uploads/
ignore = Path pictures/
ignore = Regex .*~

merge = Name *.org -> emacsclient -c --eval '(ediff-merge-files "CURRENT1" "CURRENT2" nil "NEW")'
rm ~/.unison/org.prf
link-this $PWD/out/org.prf ~/.unison/org.prf

Core Workflow and Computing Environment

I almost always want Emacs's server running. The ability to be able to attach to Emacs to edit files from the command line, or to rescue a broken X11 session, or to do batch operations using Emacs's text-processing libraries are all super powerful, and it makes little sense to not use this.

(unless (boundp 'server-process)
  (server-start))

Desktop Save Mode3 is the session management system for Emacs; it holds state of open buffers and session variables across instantiation of Emacs, which is super useful in mobile setups like laptops which reboot a lot. To make startup sane, I'm choosing to eagerly restore the 10 most recently used buffers on startup, and then in Idle the system will restore the remaining buffers.

(desktop-save-mode 1)
(setq desktop-restore-eager 10)
(setq desktop-files-not-to-save "\\(^/[^/:]*:\\|(ftp)$\\|KILL\\)")
(setq desktop-restore-frames nil)

Emacs should automatically save my state, and does so every five minutes.

(defun cce/desktop-save ()
  "Write the desktop save file to ~/.emacs.d"
  (desktop-save user-emacs-directory))
(if (not (boundp 'cce/desktop-save-timer))
    (setq cce/desktop-save-timer
          (run-with-idle-timer 300 t 'cce/desktop-save)))

The splashscreen is useful if you've never used Emacs before, but I'd rather just drop straight in to the Scratch buffer.

(setq inhibit-splash-screen t)

Uniquify buffer names; this makes working on multiple codebases more sane. Sometimes a single project can have multiple files with the same name, for example tests/index.js and tests/units/index.js, tests/lib/index.js, etc. The buffers will be listed as index.js:tests, index.js:units, and index.js:lib respectively. You can tweak that by looking at the eldoc fo uniquify-buffer-name-style.

(require 'uniquify)
(setq uniquify-buffer-name-style 'post-forward
      uniquify-separator ":")

Diminish some common modes; Diminishing a mode makes it eat less space in the modeline, by replacing the text on the modeline with something else. The Emacs config I stole these from uses a bunch of unicode characters, so I kind of backfill that, too. I also hide some modes that I don't give any shits about, so.

(install-pkgs '(diminish))
(require 'diminish)
(diminish 'visual-line-mode "")
(diminish 'isearch-mode "?")

By default, my machine drops me in to a *scratch* buffer. Originally designed to be an lisp playground that you could dive right in to on start up, it's sort of eclipsed that for me in to a general purpose buffer, where I will put things like elisp I am prototyping or playtesting, small snippets of code that I want to use in dayjob, etc. But when you kill emacs, or it dies, that buffer disappears. This code will save the Scratch buffer every five minutes and restores it on Emacs startup.

(defun save-persistent-scratch ()
  "Write the contents of *scratch* to the file name
`persistent-scratch-file-name'."
  (with-current-buffer (get-buffer-create "*scratch*")
    (write-region (point-min) (point-max) "~/.emacs.d/persistent-scratch")))

(defun load-persistent-scratch ()
  "Load the contents of `persistent-scratch-file-name' into the
  scratch buffer, clearing its contents first."
  (if (file-exists-p "~/.emacs-persistent-scratch")
      (with-current-buffer (get-buffer "*scratch*")
        (delete-region (point-min) (point-max))
        (insert-file-contents "~/.emacs.d/persistent-scratch"))))

(add-hook 'after-init-hook 'load-persistent-scratch)
(add-hook 'kill-emacs-hook 'save-persistent-scratch)

(if (not (boundp 'cce/save-persistent-scratch-timer))
    (setq cce/save-persistent-scratch-timer
          (run-with-idle-timer 300 t 'save-persistent-scratch)))

I restart emacs a lot, especially on my n900, and it would be nice to have the history of things like M-x saved across those sessions. savehist mode gives us that4.

(require 'savehist)
(setq savehist-file (concat user-emacs-directory "savehist"))
(savehist-mode 1)
(setq savehist-save-minibuffer-history 1)
(setq savehist-additional-variables
      '(kill-ring
        search-ring
        regexp-search-ring))

A lot of systems outside of the Emacs environment will edit files for me, especially Org-mode and Git. global-auto-revert-mode will make that sane.

(global-auto-revert-mode t)

Jump to the *scratch* buffer easily with bh/switch-to-scratch

(defun bh/switch-to-scratch ()
  (interactive)
  (switch-to-buffer "*scratch*"))

Toggle line wrapping with <f7>

(global-set-key (kbd "<f7>") 'bh/set-truncate-lines)
(defun bh/set-truncate-lines ()
  "Toggle value of truncate-lines and refresh window display."
  (interactive)
  (setq truncate-lines (not truncate-lines))
  ;; now refresh window display (an idiom from simple.el):
  (save-excursion
    (set-window-start (selected-window)
                      (window-start (selected-window)))))

recentf gives us a "Recent Files" menu and also some pretty neat Projectile integrations

(recentf-mode 1)

Use ibuffer instead of list-buffers

(global-set-key (kbd "C-x C-b") 'ibuffer)
(defun kill-this-buffer-y-or-n ()
  (interactive)
  (let ((buf (current-buffer)))
    (if (y-or-n-p (format "Kill %s?" (buffer-name buf)))
        (kill-buffer buf))))

Modal Editing with Evil Mode

(install-pkgs '(evil evil-args evil-avy evil-leader evil-lispy evil-magit evil-matchit evil-surround syndicate))
(require 'evil)
(evil-mode 1)
(global-evil-leader-mode 1)
(setq evil-emacs-state-cursor '("red" box))
(setq evil-normal-state-cursor '("red" box))
(setq evil-visual-state-cursor '("orange" box))
(setq evil-insert-state-cursor '("orange" box))
(setq evil-replace-state-cursor '("red" bar))
(setq evil-operator-state-cursor '("red" hollow))
(loop for (mode . state) in '((text-mode . normal)
                              (exwm-mode . emacs)
                              (cider-stacktrace-mode . emacs)
                              (sauron-mode . emacs)
                              (prog-mode . normal)
                              (comint-mode . normal)
                              (shell-mode . insert)
                              (git-commit-mode . insert)
                              (git-rebase-mode . emacs)
                              (grep-mode . emacs)
                              (bc-menu-mode . emacs)
                              (magit-branch-manager-mode . emacs)
                              (mingus-playlist-mode . emacs)
                              (mingus-browse-mode . emacs)
                              (mpc-mode . emacs)
                              (mpc-status-mode . emacs)
                              (mpc-tagbrowser-mode . emacs)
                              (mpc-tagbrowser-dir-mode . emacs)
                              (mpc-songs-mode . emacs)
                              (rdictcc-buffer-mode . emacs)
                              (dired-mode . emacs)
                              (wdired-mode . normal))
      do (evil-set-initial-state mode state))

Set up and configure a Leader key.

(eval-after-load 'evil (lambda ()
                         (add-hook 'evil-normal-state-entry-hook 'evil-leader-mode)
                         (evil-leader/set-leader "SPC")))
(eval-after-load "org"
  (progn
    (load-library "syndicate")
    (add-hook 'org-mode-hook #'syndicate-mode)
    (evil-define-key 'normal syndicate-mode-map
      "gc" 'org-cycle)))

Common Keybindings with Hydra

In the past, I would provide myself with jumps in to other modes and features that I use often using evil-leader. However, in this new brave world that I am working in, I will rarely be in evil-normal-state, which makes Leader inaccessible. This leads me to Hydra5, which allows you to define, basically, easily accessed minor modes such as a "window management" minor-mode6, etc.

It's basically little more than a big-hairy-macro, but it works nicely. I use C-x x as a sort of self-tracked prefix for these, and then a Hydra after that.

I have a set of prefix mnemonics that I try to use for my bindings:

  • b: Things that operate on buffers
  • p: Things that projectile does
  • e: Things that operate on Errors

Let's install and load Hydra.

(install-pkgs '(hydra))
(require 'hydra)

Window Manipulation

First, we define, hydra-window, a hydra that allows you to move between frames and windows, using a combination of windmove, ace-window and winner. We bind that to C-x w.

(install-pkgs '(ace-window
                transpose-frame))
(winner-mode)
(require 'hydra-examples)
(require 'transpose-frame)
(defhydra hydra-window (:color red :hint nil :columns 8)
  ("h" windmove-left "Left")
  ("j" windmove-down "Right")
  ("k" windmove-up "Up")
  ("l" windmove-right "Down")

  ("H" hydra-move-splitter-left "Splitter Left")
  ("J" hydra-move-splitter-down "Splitter Right")
  ("K" hydra-move-splitter-up "Splitter Up")
  ("L" hydra-move-splitter-right "Splitter Right")
  ("|" (lambda ()
         (interactive)
         (split-window-right)
         (windmove-right)) "Split | and Right")
  ("_" (lambda ()
         (interactive)
         (split-window-below)
         (windmove-down))"Split - and Down")
  ("v" split-window-right "Split | and Left")
  ("x" split-window-below "Split - and Up")

  ("u" winner-undo "Undo")
  ("r" winner-redo "Redo")

  ("o" other-window "Other Window")
  ("a" ace-window "Select Window" :exit t)
  ("s" ace-swap-window "Swap Windows")

  ("f" make-frame "New Frame" :exit t)

  ("da" ace-delete-window "Select + Delete Window")
  ("dw" delete-window "Delete Window")
  ("db" kill-this-buffer "Kill Buffer")
  ("df" delete-frame "Delete Frame" :exit t)
  ("i" ace-maximize-window "Select and Maximize")
  ("b" ido-switch-buffer "Buffer")
  ("t" transpose-frame "Transpose")

  ("wi" ivy-push-view "Push View")
  ("wo" ivy-pop-view "Pop View")

  ("p" previous-buffer "Previous Buffer")
  ("n" next-buffer "Next Buffer")

  ("q" nil))

(evil-leader/set-key "w" 'hydra-window/body)

"Show Me" – a Hydra for presenting information.

The showme hydra on C-c s is various things that pop up mini buffers for more information; kill rings, history, and that sort of thing, searching using surfraw7.

(install-pkgs '(sauron))
(defhydra hydra-showme ()
  ("s" sauron-toggle-hide-show "Sauron" :exit t)
  ("k" counsel-yank-pop "Kills" :exit t)
  ("b" ibuffer "Buffers" :exit t)
  ("m" list-marks "Marks" :exit t)
  ("c" calculator "Calculator" :exit t)
  ("g" eww "Browse" :exit t)
  ("C" display-time-world "Times" :exit t)
  ("w" wttrin "Weather" :exit t)
  ("d" calendar "Calendar" :exit t))

(evil-leader/set-key "s" 'hydra-showme/body)

Quick Zoom/Unzoom of text

The zoom hydra allows me to easily decrease and increase text size in a buffer-local fashion.

(defhydra hydra-zoom ()
  ("g" text-scale-increase "in")
  ("l" text-scale-decrease "out"))

(evil-leader/set-key "z" 'hydra-zoom/body)

NEXT Make hydra-zoom have a max/unmax

I want to make a zoom Head that maximizes/unmaximizes a single window, but I haven't got around to that yet.

System Integrations Hydra

(install-pkgs '(gnomenm))
(require 'kv)
(require 'gnomenm)
(defhydra hydra-sysint (:columns 4 :exit t)
  "Systems Integration"
  ("c" gnomenm-connect "Connect to Wifi")
  ("v" (async-shell-command "sudo /usr/sbin/openvpn --config ~/vpn/operator.ovpn --dev tun0" "*openvpn*") "Connect to VPN")
  ("d" gnomenm-disconnect "Go offline")
  ("w" (async-shell-command "sh ~/.screenlayout/work-walking.sh" "*arandr*") "Walking")
  ("p" (async-shell-command "sh ~/.screenlayout/work-pluggedin.sh" "*arandr*") "Plugged In"))

(evil-leader/set-key "i" 'hydra-sysint/body)

Dired

I'm trying to use dired instead of a graphical file manager like Dolphin.

When you have two dired buffers side by side, if you rename (R) or copy (C) in a dired buffer, with another dired buffer next to it, settings dired-dwim-target to non-nil will auto-fill the adjacent buffer in those operations.

(setq dired-dwim-target t)

Emacs Appearance

I choose to hide the tool bar chrome by default because I don't feel like they're very useful. I also disable scrollbars and menu bars; the modeline contains the buffer's progress, and I don't actually use the scrollbar to navigate. This leaves a tiny bit more room for the fringe and the buffers, which I consider a win.

(menu-bar-mode 0)
(when (functionp 'tool-bar-mode) (tool-bar-mode 0))
(scroll-bar-mode 0)
(column-number-mode -1)
(line-number-mode -1)

I don't disable visual-line-mode any more and am using auto-fill-mode in text-mode buffers to make them legible. The way I figure it, in prog-mode I should be keeping my code concise enough to not need a wrap, and if it does I should be thinking better about it.

I wrap somewhat arbitrarily at 100 columns; 80 is too wide for vertical-splits and is too narrow to be useful. 100 provides a good, legible length and is not too narrow.

(setq-default fill-column 100)
(add-hook 'text-mode-hook 'auto-fill-mode)
(diminish 'auto-fill-function "F")

I have my own color theme called "fireside" which used to be a red and white on black theme, but has slowly morphed in to a low-contrast grayscale theme which I waffle on quite a bit. Right now, I'm actually using Alect Black.

(install-pkgs '(alect-themes))
(add-hook 'after-cce-hook (lambda ()
                            (when (file-exists-p custom-file)
                              (load custom-file))
                            (load-theme 'alect-black)))
(set-frame-parameter (selected-frame) 'alpha '(100 100))
(add-to-list 'default-frame-alist '(alpha 100 100))

We put some properties to disable warning about certain obvious features, namely narrowing, which I use when refactoring code.

(add-hook 'after-cce-hook (lambda ()
                            (put 'narrow-to-page 'disabled nil)
                            (put 'narrow-to-region 'disabled nil)))

Right now, I am using Input Mono for my code, with a fallback set for Deja Vu Sans if Input isn't installed. I find Input to be a nice font, easy on the eyes and scales up and down well. In terms of scaling, I have my font set for text-mode things to be derived from Input Sans instead of Input Mono, which I make a bit larger than the normal, because it doesn't quite feel as heavy, especially in the whitespace.

Furthermore, I do some stupid shit with xrandr output to detect whether I am on my work machine, and to scale up the font in my Emacs appropriately.

(add-hook 'prog-mode-hook (lambda () (variable-pitch-mode -1)))
(add-hook 'text-mode-hook (lambda () (variable-pitch-mode 1)))

(defun cce/set-font-scale (size)
  (interactive "nWhat font size do you want? ")
  (set-face-attribute 'mode-line nil :inherit 'default :height (+ 10 size))
  (eval-after-load "org"
    (set-face-attribute 'org-block nil :inherit 'fixed-pitch))
  (eval-after-load "linum"
    (lambda () (set-face-attribute 'linum nil :inherit 'default :height size)))
  (set-face-attribute 'variable-pitch nil :family "Input Sans Regular" :height (+ 10 size))
  (cond
   ((find-font (font-spec :family "Input Mono"))
    (progn
      (set-face-attribute 'default nil :family "Input Mono" :weight 'light :height size)
      (set-face-attribute 'fixed-pitch nil :family "Input Mono" :inherit 'default)))
   ((find-font (font-spec :family "DejaVu Sans Mono"))
    (progn
      (set-face-attribute 'default nil :family "DejaVu Sans Mono" :weight 'light :height size)
      (set-face-attribute 'fixed-pitch nil :family "DejaVu Sans Mono" :inherit 'default)))))

(defun cce/external-display-connected ()
  (let ((xrandr-buffer (get-buffer-create " *xrandr*")))
    (shell-command "xrandr | grep ^DP | grep '\sconnected'" xrandr-buffer)
    (not (string-blank-p
          (with-current-buffer xrandr-buffer
            (buffer-string))))))

(if (or (cce/external-display-connected)
        (equal (getenv "HOSTNAME") "work"))
    (add-hook 'after-cce-hook (lambda () (cce/set-font-scale 150)))
  (add-hook 'after-cce-hook (lambda () (cce/set-font-scale 105))))

I explicitely disable some common modes that I don't think should always be enabled. Prose has no use for line numbers. Whitespace mode should only turn on for certain buffers. I do this explicitely because it seems like Emacs turns some things on by default.

(global-linum-mode 0)
(global-whitespace-mode 0)
(global-hl-line-mode 0)
(hl-line-mode 0)
(add-hook 'before-save-hook 'delete-trailing-whitespace)

Enable hl-line-mode for certain modes, primarly ones where I am doing read-only actions such as moving through Gnus folders or Messages. Interestingly, I also like having it turned on in Eshell and ERC modes because I find that these are sorts of buffers which I do a lot of reading in, and being able to move along logfile and keep track of where I am like that is quite nice.

(defun rrix/enable-hl-line ()
       (hl-line-mode 1))

(mapc (function (lambda (mode)
                 (message (symbol-name mode))
                 (add-hook mode 'rrix/enable-hl-line)))
      '(erc-mode-hook
        mingus-mode-hook
        gnus-group-mode-hook
        gnus-summary-mode-hook
        org-agenda-mode-hook
        eshell-mode-hook))

I'm starting to turn off transient-mark mode8 and move towards a model of actually treating the Mark as more than just a selection tool, and towards treating it as a navigation tool. In light of that, I'm going to start disabling transient-mark-mode and only relying on visible-mark-mode for showing the mark, which alleviates most of the issues you see with disabling transient-mark-mode

(install-pkgs '(visible-mark))
(require 'visible-mark)
(defun rrix/disable-transient-mark ()
  ""
  (transient-mark-mode -1)
  (visible-mark-mode))
(add-hook 'text-mode-hook 'rrix/disable-transient-mark)
(add-hook 'prog-mode-hook 'rrix/disable-transient-mark)

(setq visible-mark-max 1)
(setq visible-mark-faces '(visible-mark-active))

(set-face-attribute 'visible-mark-active nil :background nil)
(set-face-attribute 'visible-mark-active nil :foreground nil)
(set-face-attribute 'visible-mark-active nil :inherit 'cursor)

mark-tools provides a buffer that allows you to view the global and local mark rings and navigate to them with RET. This is used in hydra-showme.

(install-pkgs '(mark-tools))

browse-kill-ring+ is the enhanced version of browse-kill-ring, which gives you a buffer to paste in an arbitrary entry in your kill ring to the active buffer. The enhanced version adds some nice keybindings and takes up little more space than the regular version.

(install-pkgs '(browse-kill-ring+))

Ivy and Counsel

Ivy is a completion/selection framework similar to helm, but probablymaybe a bit more performant. It is a lightweight core, with a bunch of nice addons built in to the counsel and swiper packages. My system will have all of them installed, and then bound to useful key bindings.

(install-pkgs '(ivy
                ivy-hydra
                imenu-anywhere
                flyspell-correct-ivy
                counsel-projectile
                counsel
                swiper))
(ivy-mode 1)
(diminish 'ivy-mode)
(setq magit-completing-read-function 'ivy-completing-read)
(setq projectile-completion-system 'ivy)
(global-set-key (kbd "M-u") 'ivy-resume)

(global-set-key (kbd "C-h f") 'counsel-describe-function)
(global-set-key (kbd "C-h v") 'counsel-describe-variable)
(global-set-key (kbd "C-h w") 'counsel-descbinds)

(defhydra hydra-help (:columns 4)
  ("v" counsel-describe-variable "Desc Variable")
  ("w" counsel-descbinds "Desc Bindings")
  ("s" describe-syntax "Desc Syntax")
  ("P" describe-package "Desc Package")
  ("o" describe-symbol "Desc Symbols")
  ("m" describe-mode "Desc Mode")
  ("k" describe-key "Desc Keys")
  ("f" counsel-describe-function "Desc Functions")
  ("d" apropos-documentation "Apropos Docs")
  ("a" apropos-command "Apropos Commands")
  ("j" cce/man-at-point "Man for symbol at point.")
  ("r" info-emacs-manual "Emacs Info Pages")
  ("p" finder-by-keyword "Find Package by Keyword")
  ("l" view-lossage "Recent Commands")
  ("i" info "Info Index")
  ("e" view-echo-area-messages "*Messages*")
  ("b" describe-bindings "List Bindings")
  ("S" info-lookup-symbol "Lookup Symbol in Info")
  ("q" nil))
(evil-leader/set-key "h" 'hydra-help/body)

(global-set-key (kbd "C-x C-f") 'counsel-find-file)

(global-set-key (kbd "C-x b") 'ivy-switch-buffer)
(evil-leader/set-key "b" 'ivy-switch-buffer)

(setq counsel-find-file-at-point t)
(setq counsel-find-file-ignore-regexp
      (concat
       ;; file names beginning with # or .
       "\\(?:\\`[#.]\\)"
       ;; file names ending with # or ~
       "\\|\\(?:[#~]\\'\\)"))

(with-eval-after-load 'org
  (define-key org-mode-map (kbd "C-c C-q") #'counsel-org-tag))
(with-eval-after-load 'org-agenda
  (define-key org-agenda-mode-map (kbd "C-c C-q") #'counsel-org-tag-agenda))
(define-key evil-normal-state-map (kbd "/") 'swiper)
(evil-leader/set-key "SPC" 'counsel-M-x)
    (defun ivy--matcher-desc () ; used in `hydra-ivy'
      (if (eq ivy--regex-function
              'ivy--regex-fuzzy)
          "fuzzy"
"ivy"))

(defhydra hydra-ivy (:hint nil :color pink)
  "
^^_,_        _f_ollow      occ_u_r      _g_o          ^^_c_alling %-7s(if ivy-calling \"on\" \"off\")      _w_/_s_/_a_: %-14s(ivy-action-name)
_p_/_n_      _d_one        ^^           _i_nsert      ^^_m_atcher %-7s(ivy--matcher-desc)^^^^^^^^^^^^      _C_ase-fold: %-10`ivy-case-fold-search
^^_._        _D_o it!      ^^           _q_uit        _<_/_>_ shrink/grow^^^^^^^^^^^^^^^^^^^^^^^^^^^^      _t_runcate: %-11`truncate-lines
"
  ;; arrows
  ("," ivy-beginning-of-buffer) ; default h
  ("p" ivy-previous-line) ; default j
  ("n" ivy-next-line) ; default k
  ("." ivy-end-of-buffer) ; default l
  ;; quit ivy
  ("q" keyboard-escape-quit :exit t) ; default o
  ("C-g" keyboard-escape-quit :exit t)
  ;; quit hydra
  ("i" nil)
  ("C-o" nil)
  ;; actions
  ("f" ivy-alt-done :exit nil)
  ;; Exchange the default bindings for C-j and C-m
  ("C-m" ivy-alt-done :exit nil) ; RET, default C-j
  ("C-j" ivy-done :exit t) ; default C-m
  ("d" ivy-done :exit t)
  ("g" ivy-call)
  ("D" ivy-immediate-done :exit t)
  ("c" ivy-toggle-calling)
  ("m" ivy-toggle-fuzzy)
  (">" ivy-minibuffer-grow)
  ("<" ivy-minibuffer-shrink)
  ("w" ivy-prev-action)
  ("s" ivy-next-action)
  ("a" ivy-read-action)
  ("t" (setq truncate-lines (not truncate-lines)))
  ("C" ivy-toggle-case-fold)
  ("u" ivy-occur :exit t))

(define-key ivy-minibuffer-map  (kbd "C-m")
  'ivy-alt-done)                                        ; RET, default C-j
(define-key ivy-minibuffer-map (kbd "C-j")
  ' ivy-done)                        ; default C-m
(define-key ivy-minibuffer-map (kbd "C-S-m")
  'ivy-immediate-done)
(define-key ivy-minibuffer-map (kbd "C-t") 'ivy-toggle-fuzzy)
(define-key ivy-minibuffer-map (kbd "C-o") 'hydra-ivy/body)

Ivy View is a new thing that lets you save and restore window views, I'm going to configure it and see if I use it.

(setq ivy-use-virtual-buffers t)

Text Manipulation

My rules of choice: 4 space tabstops, no tabs.

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

expand-region is a neat little guy that lets you grow your region just by calling it. Pretty self explanatory, but it's a great tool to have in your belt. I bind it over the mostly-useless M-o.

(install-pkgs '(expand-region))
(global-set-key (kbd "M-o") 'er/expand-region)

Undo Tree

Undo Tree is a neat visualization and navigation tool for walking through a tree history.

(install-pkgs '(undo-tree))
(require 'undo-tree)
(global-undo-tree-mode 1)
(diminish 'undo-tree-mode "")

Pasting and Sharing Code

scpaste9 is a neat package that takes a region or buffer, renders it to HTML, and uses scp to push it somewhere. I have it wired up to Hydra to paste to either work shared HTML space or to my personal site.

(install-pkgs '(scpaste))
(defhydra hydra-scpaste (global-map "C-c j")
  ("p" (let ((scpaste-http-destination "http://whatthefuck.computer/~rrix/p/")
             (scpaste-scp-destination "li01.rix.si:/home/rrix/public_html/p/"))
         (call-interactively 'scpaste-region)))
  ("w" (let ((scpaste-http-destination scpaste-work-http)
             (scpaste-scp-destination scpaste-work-scp))
         (call-interactively 'scpaste-region))))

Buffer Navigation

Moving around with Avy

Avy is the framework that powers ace-jump but it's a hell of a lot more now; it's a generic framework for moving about, and is pretty incredibly powerful.

(install-pkgs '(avy avy-zap ace-link))
(defhydra hydra-avy (global-map "M-i" :color teal)
  ("c" avy-goto-char "char")
  ("w" avy-goto-word-0 "word")
  ("l" avy-goto-line "line")
  ("p" avy-pop-mark "pop")
  ("o" ace-link-info "url"))

(ace-link-setup-default)
(evil-leader/set-key "a" 'hydra-avy/body)

Focusing, Dimming and Highlighting

Focusing on a bit of code or text comes in two varieties for me; one is the highlighting of a symbol, either manually or with auto-highlight-symbol. The other is the opposite, dimming everything but exactly what I'm trying to grok right now.

This little fellow, auto-highlight-symbol, tags all occurrences of the symbol under the cursor.

(install-pkgs '(highlight-symbol auto-highlight-symbol))
(require 'highlight-symbol)

Focus Mode10 provides a way to limit what you're focusing on to a function or sentence at a time. This makes it easy for me to read

(install-pkgs '(focus))

And we define a hydra to make them accessible, via C-c h.

(defhydra hydra-highlight-symbol (global-map "C-c h")
  "
Highlight -----------> Dim
_h_: Highlight at Point _f_: focus-mode
_j_: Previous Symbol    _r_: focus-ro
_k_: Next Symbol
_d_: Clear All Symbols
_a_: Toggle AHS
"
  ("h" highlight-symbol-at-point)
  ("j" highlight-symbol-prev)
  ("k" highlight-symbol-next)
  ("d" (progn (highlight-symbol-remove-all)))
  ("a" auto-highlight-symbol-mode)
  ("f" focus-mode)
  ("r" focus-read-only-mode))

Footnotes:

Author: Ryan Rix

Created: 2017-07-02 Sun 11:27

Validate XHTML 1.0