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-package
2, 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"))
Getting Org to install from the Orgmode repository is … really really hard if there is an org-mode 8 which loads from the system. I have an interactive script which makes sure that Emacs loads Org-mode 9 in to an environment which does not have org-mode 8 installed:
- name: org-mode installed become: yes become_user: "{{local_account}}" shell: emacs -q --eval "(progn (package-initialize) (setq package-archives '((\"org\" . \"http://orgmode.org/elpa/\"))) (setq package-pinned-archives '((org . \"org\") (org-plus-contrib . \"org\"))) (package-refresh-contents) (list-packages))" register: org_install changed_when: not org_install.stdout == ""
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.
(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)))
I use this, for example, for this script which sets a blink(1) my roommate gave me to a certain color based on my computer's load:
import os import sys import subprocess hostname = subprocess.check_output('hostname').rstrip() if hostname != "kusanagi": sys.exit(1) upper_thresh=10 value = os.getloadavg()[0] if value > 5.0: hue = 64-int(value*1.5 / upper_thresh * 64) sat = 255 elif value > 3.0: hue = 64-int(value / upper_thresh * 64 + 64) sat = 255 else: hue = int(value / upper_thresh * 64 + 64) sat = int(value / upper_thresh * 255) print "load at {load}, setting to hue {hue} and sat {sat}".format(load=value, hue=hue, sat=sat) os.system("sudo ~/Code/blink1/commandline/blink1-tool --hsb={hue},{sat},255".format(hue=hue, sat=sat))
- name: blink wrapper installed become: yes become_user: "{{local_account}}" copy: src: out/blink-load dest: ~/bin/blink-load mode: 0750
(defun blink-on-load () (interactive) (cce/async-forever "blink-load" "*blink-load*"))
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.d/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))))
Prevent Emacs from litering backup files all over the place; this means that they won't get sync'd and written to other hosts on Syncthing, but I'm not sure if that's actually a problem.
(defvar backup-dir (expand-file-name "~/.emacs.d/emacs_backup/")) (defvar autosave-dir (expand-file-name "~/.emacs.d/autosave/")) (setq backup-directory-alist (list (cons ".*" backup-dir)) auto-save-list-file-prefix autosave-dir auto-save-file-name-transforms `((".*" ,autosave-dir t)) tramp-backup-directory-alist backup-directory-alist tramp-auto-save-directory autosave-dir)
Emacs ships with save-place-mode
, which writes the location you had a file open to when you close
it. I think desktop-mode
does this as well, across sessions.
(setq-default save-place t) (setq save-place-file (expand-file-name ".places" user-emacs-directory)) (save-place-mode 1)
Modal Editing with Evil Mode
(setq evil-want-keybinding nil) (install-pkgs '(evil evil-args evil-avy evil-collection evil-leader evil-lispy evil-magit evil-matchit evil-surround syndicate)) (require 'evil) (evil-mode 1) (global-evil-leader-mode 1) (require 'evil-collection) (evil-collection-init)
(setq evil-emacs-state-cursor '("firebrick" box)) (setq evil-normal-state-cursor '("firebrick" box)) (setq evil-visual-state-cursor '("cyan" box)) (setq evil-insert-state-cursor '("cyan" 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) (sauron-mode . emacs) (shell-mode . insert) (git-commit-mode . insert) (git-rebase-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)) 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 buffersp
: Things that projectile doese
: 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)
(defun vsplit-last-buffer () (interactive) (split-window-vertically) (other-window 1 nil) (switch-to-next-buffer)) (defun hsplit-last-buffer () (interactive) (split-window-horizontally) (other-window 1 nil) (switch-to-next-buffer)) (global-set-key (kbd "C-x 2") 'vsplit-last-buffer) (global-set-key (kbd "C-x 3") 'hsplit-last-buffer)
"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)
DIP Switches
(defun tasshin/read-write-toggle () "Toggles read-only in any relevant mode: ag-mode, Dired, or just any file at all." (interactive) (if (equal major-mode 'ag-mode) ;; wgrep-ag can support ag-mode (wgrep-change-to-wgrep-mode) ;; dired-toggle-read-only has its own conditional: ;; if the mode is Dired, it will make the directory writable ;; if it is not, it will just toggle read only, as desired (dired-toggle-read-only)))
(defhydra hydra-dipswitch () "Toggles." ("e" toggle-debug-on-error "Debug on Error") ("q" toggle-debug-on-quit "Debug on Quit") ("r" tasshin/read-write-toggle "Read/Write") ("w" whitespace-mode "Whitespace") ("l" lsp-ui-mode "LSP UI") ("t" bh/set-truncate-lines "Truncate") ("v" variable-pitch-mode "Variable Pitch")) (evil-leader/set-key "t" 'hydra-dipswitch/body) (global-set-key (kbd "<f8>") 'hydra-dipswitch/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)
Rebind RET
from dired-advertised-find-file
to dired-find-alternate-file
so that we don't have
dired buffer bloat.
(define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)
Rebind ^
to use find-alternate-file
to go up a directory, again to prevent dired buffer bloat.
(define-key dired-mode-map (kbd "^") (lambda () (interactive) (find-alternate-file "..")))
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) (setq x-stretch-cursor t)
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 Light.
(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 "Vulf Mono")) (progn (set-face-attribute 'default nil :family "Vulf Mono" :height size) (set-face-attribute 'fixed-pitch nil :family "Vulf Mono" :inherit 'default) (set-face-attribute 'variable-pitch nil :slant 'oblique :family "Vulf Mono" :height (+ 10 size)))) ((find-font (font-spec :family "Input Mono")) (progn (set-face-attribute 'default nil :family "Input Mono" :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)))))) (add-hook 'after-cce-hook #'cce/refresh-display-scale) (defun cce/refresh-display-scale () (interactive) (cond ((cce/external-display-connected) (cce/set-font-scale 175)) ((equal (system-name) "work") (cce/set-font-scale 175)) ((equal (system-name) "beebs") (cce/set-font-scale 125)) ((equal (system-name) "tres-ebow") (cce/set-font-scale 125)) ((equal (system-name) "solitary-living") (cce/set-font-scale 105)) ((equal (system-name) "penguin") (cce/set-font-scale 125)) ((equal (system-name) "pocket") (cce/set-font-scale 175)) (t (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))
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) (setq ivy-height 15)
(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) (define-key evil-normal-state-map (kbd "?") 'swiper) (evil-leader/set-key "SPC" 'counsel-M-x) (global-set-key (kbd "M-x") '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 "fontkeming.fail:/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" counsel-ace-link "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)) (eval-after-load 'focus (lambda () (define-key focus-mode-map (kbd "j") #'focus-next-thing) (define-key focus-mode-map (kbd "k") #'focus-prev-thing) (evil-define-key 'normal focus-mode-map (kbd "k") #'focus-prev-thing) (evil-define-key 'normal focus-mode-map (kbd "j") #'focus-next-thing)))
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" highlight-symbol-remove-all) ("a" auto-highlight-symbol-mode) ("f" focus-mode) ("r" focus-read-only-mode))
This function is from Tasshin Fogleman's .emacs.d and is an easy keybinding to switch between narrow and wide views of a buffer, iteratively between region, source block, subtree, and function.
(defun narrow-or-widen-dwim (p) "If the buffer is narrowed, it widens. Otherwise, it narrows intelligently. Intelligently means: region, org-src-block, org-subtree, or defun, whichever applies first. Narrowing to org-src-block actually calls `org-edit-src-code'. With prefix P, don't widen, just narrow even if buffer is already narrowed." (interactive "P") (declare (interactive-only)) (cond ((and (buffer-narrowed-p) (not p)) (widen)) ((and (boundp 'org-src-mode) org-src-mode (not p)) (org-edit-src-exit)) ((region-active-p) (narrow-to-region (region-beginning) (region-end))) ((derived-mode-p 'org-mode) (cond ((ignore-errors (org-edit-src-code)) (delete-other-windows)) ((org-at-block-p) (org-narrow-to-block)) (t (org-narrow-to-subtree)))) ((derived-mode-p 'prog-mode) (narrow-to-defun)) (t (error "Please select a region to narrow to")))) (evil-leader/set-key (kbd "n") #'narrow-or-widen-dwim)