This is my .emacs file … well, the top-level of it anyway.
I’ve written it in a literate style, to make it easy to explain.
The notes also include hyperlinks to where I stole it. ;-)
While I often use Emacs for Mac, lately, I’ve been building from Homebrew with the following dependency:
brew cask install xquartzAnd then built from source especially for the Mac:
brew install emacs --HEAD --use-git-head --with-cocoa \
--with-gnutls --srgb --with-librsvg --with-ns
brew linkapps emacsNot only does this install the latest version of Emacs in
/usr/local/bin/emacs, but it also links a GUI version in
/Application/Emacs.app.
All Homebrew options for Emacs can be seen with the command:
brew options emacsTo “load” the contents of this file, add the following to $HOME/.emacs:
;; Load our Literate Programming version of our Dot Emacs
;; file, from file: ~/Work/dot-files/emacs.org
(unless (boundp 'aquamacs-version)
(load-file "~/.emacs.d/elisp/init-main.el")
(if (window-system)
(require 'init-client)
(require 'init-server)
(server-start))I only load this from a “normal” Emacs distribution, which allows me to play around with Aquamacs and Starter Kits. You know, to see what I may be missing.
Normally, the user-emacs-directory stores everything in a
.emacs.d directory in the home directory, however, Aquamacs
overrides that, and since I now feel the need to use these settings
for both editors (sure feels like XEmacs all over again).
Any way, I have a new global variable for that:
(defconst ha/emacs-directory (concat (getenv "HOME") "/.emacs.d/"))
(defun ha/emacs-subdirectory (d) (expand-file-name d ha/emacs-directory))In case this is the first time running this on a computer, we need to make sure the following directories have been created.
(let* ((subdirs '("elisp" "backups" "snippets" "ac-dict"))
(fulldirs (mapcar (lambda (d) (ha/emacs-subdirectory d)) subdirs)))
(dolist (dir fulldirs)
(when (not (file-exists-p dir))
(message "Make directory: %s" dir)
(make-directory dir))))While I would rather program my configurations, sometimes the Emacs menu system is “good enough”, but I want it in its own file:
(setq custom-file (expand-file-name "custom.el" ha/emacs-directory))
(when (file-exists-p custom-file)
(load custom-file))Extra packages not available via the package manager go in my
personal stash at: $HOME/.emacs.d/elisp
(add-to-list 'load-path (ha/emacs-subdirectory "elisp"))Load up my special collection of enhancements to Emacs Lisp:
(require 'cl)
(require 'init-support)Emacs has become like every other operating system, and now has a package manager with its own collection repository, but since it is so conservative, we need to add more repositories to get all the sweet goodness, I demand.
(require 'package)
(setq package-archives '(("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")
("melpa" . "http://melpa.milkbox.net/packages/")
("marmalade" . "http://marmalade-repo.org/packages/")))
(package-initialize)The package management system doesn’t come with a programmatic way to specify what packages should be installed. Here is my solution until I convert to use-package.
(defun packages-install (packages)
"Given a list of packages, this will install them from the standard locations."
(let ((to-install (inverse-filter 'package-installed-p packages)))
(when to-install
(package-refresh-contents)
(dolist (it to-install)
(package-install it)
(delete-other-windows)))))Using use-package to automatically install certain packages, as well as the ease of lazily loading them.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(require 'use-package)General settings about me that other packages can use. The biggest problem is guessing my email address based on what computer I am using:
(if (equal "howard.abrams" user-login-name)
(setq user-mail-address "howard.abrams@workday.com")
(setq user-mail-address "howard.abrams@gmail.com"))I have learned to distrust tabs in my source code, so let’s make sure that we only have spaces. See this discussion for details.
(setq-default indent-tabs-mode nil)
(setq tab-width 2)Make tab key do indent first then completion.
(setq-default tab-always-indent 'complete)Synchronize notes formatted in org-mode across multiple computers with cloud storage services, like Dropbox? Those files are cached in various other storage facilities… so, I use symmetric key encryption with PGP.
To get started on the Mac, install the goodies:
brew install gpgNow, any file loaded with a gpg extension, e.g. some.org.gpg,
will prompt for a password (and then use org-mode). Since these
files are for my eyes only, I don’t need the key-ring prompt:
(setq epa-file-select-keys 2)If you trust your Emacs session on your computer, you can have Emacs cache the password. Not sure I do…
(setq epa-file-cache-passphrase-for-symmetric-encryption t)I’ve been using Emacs for many years, and appreciate a certain minimalist approach to its display. While you can turn these off with the menu items now, it is just as easy to set them here.
(setq initial-scratch-message "") ;; Uh, I know what Scratch is for
(setq visible-bell t) ;; Get rid of the beeps
(when (window-system)
(tool-bar-mode 0) ;; Toolbars were only cool with XEmacs
(when (fboundp 'horizontal-scroll-bar-mode)
(horizontal-scroll-bar-mode -1))
(scroll-bar-mode -1)) ;; Scrollbars are waste screen estateMost of the display settings actually come from the Mac initialization file.
My mode-line code is now more complex in order to make it more simpler.
(require 'init-mode-line)You don’t want this on all the time, but nice to turn it on every now and then:
(use-package whitespace
:bind ("C-c T w" . whitespace-mode)
:init
(setq whitespace-line-column nil
whitespace-display-mappings '((space-mark 32 [183] [46])
(newline-mark 10 [9166 10])
(tab-mark 9 [9654 9] [92 9])))
:config
(set-face-attribute 'whitespace-space nil :foreground "#666666" :background nil)
(set-face-attribute 'whitespace-newline nil :foreground "#666666" :background nil)
(set-face-attribute 'whitespace-indentation nil :foreground "#666666" :background nil)
:diminish whitespace-mode)Automatically wrapping when you get to the end of a line (or the fill-region):
(use-package fill
:bind ("C-c T f" . auto-fill-mode)
:init (add-hook 'org-mode-hook 'turn-on-auto-fill)
:diminish auto-fill-mode)I’m starting to appreciate the Hydra project.
(use-package hydra
:ensure t
:config
(hydra-add-font-lock))Easily manipulate the size of the windows using the arrow keys in a particular buffer window.
(require 'windmove)
(defun hydra-move-splitter-left (arg)
"Move window splitter left."
(interactive "p")
(if (let ((windmove-wrap-around))
(windmove-find-other-window 'right))
(shrink-window-horizontally arg)
(enlarge-window-horizontally arg)))
(defun hydra-move-splitter-right (arg)
"Move window splitter right."
(interactive "p")
(if (let ((windmove-wrap-around))
(windmove-find-other-window 'right))
(enlarge-window-horizontally arg)
(shrink-window-horizontally arg)))
(defun hydra-move-splitter-up (arg)
"Move window splitter up."
(interactive "p")
(if (let ((windmove-wrap-around))
(windmove-find-other-window 'up))
(enlarge-window arg)
(shrink-window arg)))
(defun hydra-move-splitter-down (arg)
"Move window splitter down."
(interactive "p")
(if (let ((windmove-wrap-around))
(windmove-find-other-window 'up))
(shrink-window arg)
(enlarge-window arg)))
(defhydra hydra-splitter (global-map "<f9>")
"splitter"
("<left>" hydra-move-splitter-left)
("<down>" hydra-move-splitter-down)
("<up>" hydra-move-splitter-up)
("<right>" hydra-move-splitter-right))Other Hydra sequences are associated with the package they control.
Many command sequences may be logical, but who can remember them all? Use guide-key to display the final function name. This isn’t as nice as Hydra, but useful for built-in key sequences:
(use-package guide-key
:ensure t
:init (setq guide-key/guide-key-sequence
'("C-x r" ; rectanges and registers
"C-x 4" ; window commands
"M-s h" ; hi-lock highlighting
"C-x w" ; alternative to M-s ...
"C-c @" ; hs-hide-show mode
"C-c p" ; projectile
"<f2>"
"<f9>"
(org-mode "C-c C-x")))
:config (guide-key-mode 1)
:diminish guide-key-mode)May think about using the following commands:
(setq guide-key/guide-key-sequence '("C-x"))
(setq guide-key/recursive-key-sequence-flag t)Emacs has never seen a need for function keys, and I agree…for the most part. For things really away from the flow, they don’t seem to bad. But what are those?
- F1 - Help? Isn’t Control-H good enough?
- F2 - Special odd, little-used characters that I have to think about before remembering what its binding.
- F3 - Define a keyboard macro
- F4 - Replay a keyboard macro
- F5 - Use org-mode’s Mark Ring feature globally
- F6 - Open to temporary, changeable commands…
- F7 - Switch to another window … Control goes the other way.
- F8 - Switch to buffer
- F9 - My extension (replacement?) for
C-cfor changing colors and other odd bindings that I actually don’t use that often.
(global-set-key (kbd "<f5>") 'org-mark-ring-push)
(global-set-key (kbd "C-<f5>") 'org-mark-ring-goto)
(global-set-key (kbd "<f7>") 'other-window)
(global-set-key (kbd "C-<f7>") (lambda () (interactive) (other-window -1)))The F9 prefix is scattered about my config files.
(define-prefix-command 'personal-global-map)
(global-set-key (kbd "<f9>") 'personal-global-map)Unlike the F9 bindings, all the F2 key-bindings happen in a single library file:
(require 'init-f2)I like the ability to highlight random text.
(global-set-key (kbd "C-c i") 'highlight-regexp)
(global-set-key (kbd "C-c u") 'unhighlight-regexp)May get specific highlights automatically for certain files. We begin by highlighting lines in *.log files.
(defun ha/highlite-logs ()
"Highlight certain lines in specific files. Currently, only log files are supported."
(interactive)
(when (equal "log" (file-name-extension (buffer-file-name)))
(hi-lock-mode 1)
(highlight-lines-matching-regexp "ERROR:" 'hi-red-b)
(highlight-lines-matching-regexp "NOTE:" 'hi-blue-b)))The condition in that code allows me to hook it to all files:
(add-hook 'find-file-hook 'highlite-it)Turn on specific word groupings for specific occasions. We begin with highlighting keywords I use during note-taking sessions at the end of a sprint.
(defun ha/sprint-retrospective-highlighting ()
"Highlights the good, the bad and the improvements to make when taking notes."
(interactive)
(hi-lock-mode t)
(highlight-lines-matching-regexp "^ [-*] " 'hi-black-b)
(highlight-phrase "TODO:?" 'hi-black-b)
(highlight-regexp "(?Good)?:?" 'hi-green-b)
(highlight-regexp "(?Bad)?:?" 'hi-red-b)
(highlight-regexp "Imp\\(rove\\)?:" 'hi-blue-b))This works really well with other commands, including fancy-narrow, where I can visually high-light a section of a buffer. Great for code-reviews and other presentations.
(use-package fancy-narrow
:ensure t
:config
(defun ha/highlight-block ()
"Highlights a 'block' in a buffer defined by the first blank
line before and after the current cursor position. Uses the
'fancy-narrow' mode to high-light the block."
(interactive)
(let (cur beg end)
(setq cur (point))
(setq end (or (re-search-forward "^\s*$" nil t) (point-max)))
(goto-char cur)
(setq beg (or (re-search-backward "^\s*$" nil t) (point-min)))
(fancy-narrow-to-region beg end)
(goto-char cur)))
(defun ha/highlight-section (num)
"If some of the buffer is highlighted with the `fancy-narrow'
mode, then un-highlight it by calling `fancy-widen'.
If region is active, call `fancy-narrow-to-region'.
If NUM is 0, highlight the current block (delimited by blank
lines). If NUM is positive or negative, highlight that number
of lines. Otherwise, called `fancy-narrow-to-defun', to
highlight current function."
(interactive "p")
(cond
((fancy-narrow-active-p) (fancy-widen))
((region-active-p) (fancy-narrow-to-region (region-beginning) (region-end)))
((= num 0) (ha/highlight-block))
((= num 1) (fancy-narrow-to-defun))
(t (progn (ha/expand-region num)
(fancy-narrow-to-region (region-beginning) (region-end))))))
:bind ("C-M-+" . ha/highlight-section))Set up ace-window mode:
(use-package ace-window
:ensure t
:init
(setq aw-keys '(?a ?s ?d ?f ?j ?k ?l ?o))
(global-set-key (kbd "C-x o") 'ace-window)
:diminish ace-window-mode)I like IDO for switching buffers since I typically know what I’m after:
(global-set-key (kbd "<f8>") 'ido-switch-buffer)
(global-set-key (kbd "S-<f8>") 'ibuffer)I like kpm-list a bit better than ibuffer, but I really don’t use
either more than ido-switch-buffer. Still:
(use-package kpm-list
:ensure t
:bind ("S-<f8>" . kpm-list)
("C-x C-b" . kpm-list))Often, while on my laptop, I want the current window to be ‘large
enough for work’, and this is bound to <f9> . (period).
(global-unset-key (kbd "C-c w"))
(global-set-key (kbd "C-c w r") 'ha/window-standard-size)If I’ve enlarged the window, I can restore that window to its original size, so this requires a buffer local variable:
(make-variable-buffer-local 'window-width-original)Now a function that either changes the width to 80, or back to the original size if already at 80.
(defun ha/window-standard-size (arg)
"Sets the size of the current window to 80 characters, unless
it already is 80 characters, in which case, set it back to its
previous size. A prefix ARG can be given to set the window to a
particular width."
(interactive "p")
;; If not already set, let's store the current window width in our
;; buffer-local variable.
(if (not (local-variable-p 'window-width-original))
(setq window-width-original (window-width)))
;; The 'goal' is 80 unless we get a better argument, C-u 60 ...
(let* ((goal-width (if (> arg 8) arg 80))
(new-width (- goal-width (window-width))))
(if (= new-width 0) ; Already enlarged? Restore:
(enlarge-window-horizontally (- window-width-original goal-width))
(enlarge-window-horizontally new-width))))Change window configuration and then return to the old
configuration with winner-mode. Use Control-C Arrow keys to
cycle through window/frame configurations.
(use-package winner
:ensure t
:init (winner-mode 1)
:config (global-set-key
(kbd "C-c p")
(defhydra hydra-winner ()
"Winner"
("p" winner-undo "back")
("o" winner-redo "forward" :exit t))))Note: Can’t use the :bind symbol in a use-package with
defhydra, since that needs to be expanded (as a macro), and
evaluated. Calling global-set-key seems to work, though.
Replaced the deprecated ace-jump-mode with the avy project.
Since I use the =avy-goto-word-1= function the most, I bind it to a
simple sequence. Other options (that require more of my memory),
are bound to C-c J (shift J). Perhaps that mnemonic will stick.
(use-package avy
:ensure t
:bind
("C-c j" . avy-goto-word-1)
("C-c J J" . avy-goto-word-1)
("C-c J j" . avy-goto-word-1)
("C-c J c" . avy-goto-char-1)
("C-c J C" . avy-goto-char-1)
("C-c J l" . avy-goto-line)
("C-c J L" . avy-goto-line))Unfilling a paragraph joins all the lines in a paragraph into a single line. Taken from here.
(defun unfill-paragraph ()
"Takes a multi-line paragraph and makes it into a single line of text."
(interactive)
(let ((fill-column (point-max)))
(fill-paragraph nil)))
;; Handy key definition
(define-key global-map "\M-Q" 'unfill-paragraph)The subtle changes I’ve been making to Emacs behavior has grown until I felt I should move it into its own source file.
(require 'init-fixes)While I’m not sure how often I will use multiple-cursors project, I’m going to try to remember it is there. It doesn’t have any default keybindings, so I set up the suggested:
(use-package multiple-cursors
:ensure t
:config
(global-set-key
(kbd "C-c C-.")
(defhydra hydra-multiple-cursors ()
"multiple-cursors"
("." mc/mark-all-dwim "all-dwim")
("C-." mc/mark-all-like-this-dwim "all-like-dwim")
("n" mc/mark-next-like-this "next")
("p" mc/mark-previous-like-this "previous")
("a" mc/mark-all-like-this "mark-all")
("N" mc/mark-next-symbol-like-this "next-symbol")
("P" mc/mark-previous-symbol-like-this "previous-symbol")
("A" mc/mark-all-symbols-like-this "all-symbols")
("f" mc/mark-all-like-this-in-defun "in-func")
("l" mc/edit-lines "all-lines")
("e" mc/edit-ends-of-lines "end-lines"))))Wherever you are in a file, and whatever the type of file, you can slowly increase a region selection by logical segments by using Magnar’s expand-region project.
However, the normal experience for expand-region is interactive,
expected to be called repeatedly to expand and contract the regions
based on syntax, and whatnot. Since I am seldom sure what I will
select if I give this function a numeric prefix, I created a
wrapper function that will (when given a number), just select the
number of lines for the region. Select the current line with a 0
argument. No argument (well, lines is given 1 with no argument),
then it just calls expand-region:
(use-package expand-region
:ensure t
:config
(defun ha/expand-region (lines)
"Prefix-oriented wrapper around Magnar's `er/expand-region'.
Call with LINES equal to 1 (given no prefix), it expands the
region as normal. When LINES given a positive number, selects
the current line and number of lines specified. When LINES is a
negative number, selects the current line and the previous lines
specified. Select the current line if the LINES prefix is zero."
(interactive "p")
(cond ((= lines 1) (er/expand-region 1))
((< lines 0) (ha/expand-previous-line-as-region lines))
(t (ha/expand-next-line-as-region (1+ lines)))))
(defun ha/expand-next-line-as-region (lines)
(message "lines = %d" lines)
(beginning-of-line)
(set-mark (point))
(end-of-line lines))
(defun ha/expand-previous-line-as-region (lines)
(end-of-line)
(set-mark (point))
(beginning-of-line (1+ lines)))
:bind ("C-=" . ha/expand-region))While the M-( binding to insert-pair is great, I often need to
wrap with other characters:
(global-set-key (kbd "M-[") 'insert-pair)
(global-set-key (kbd "M-{") 'insert-pair)
(global-set-key (kbd "M-<") 'insert-pair)
(global-set-key (kbd "M-'") 'insert-pair)
(global-set-key (kbd "M-`") 'insert-pair)
(global-set-key (kbd "M-\"") 'insert-pair)But wrap-region is even more flexible. In most editors, selecting text and typing anything replaces the selected text (see the delete-selection-mode), but in this case, we can do something different… like wrapping:
(use-package wrap-region
:ensure t
:config
(wrap-region-global-mode t)
(wrap-region-add-wrappers
'(("(" ")")
("[" "]")
("{" "}")
("<" ">")
("'" "'")
("\"" "\"")
("‘" "’" "q")
("“" "”" "Q")
("*" "*" "b" org-mode) ; bolden
("*" "*" "*" org-mode) ; bolden
("/" "/" "i" org-mode) ; italics
("/" "/" "/" org-mode) ; italics
("~" "~" "c" org-mode) ; code
("~" "~" "~" org-mode) ; code
("=" "=" "v" org-mode) ; verbatim
("=" "=" "=" org-mode) ; verbatim
("_" "_" "u" '(org-mode markdown-mode)) ; underline
("**" "**" "b" markdown-mode) ; bolden
("*" "*" "i" markdown-mode) ; italics
("`" "`" "c" '(markdown-mode ruby-mode)) ; code
("`" "'" "c" lisp-mode) ; code
))
:diminish wrap-region-mode)But in order to wrap text in a more general way (with just about
any textual string), we need something more. Especially with the
expand-region command, wrapping a logical block of text with a
beginning and ending string really makes sense.
(defun surround (start end txt)
"Wraps the specified region (or the current 'symbol / word'
with some textual markers that this function requests from the
user. Opening-type text, like parens and angle-brackets will
insert the matching closing symbol.
This function also supports some org-mode wrappers:
- `#s` wraps the region in a source code block
- `#e` wraps it in an example block
- `#q` wraps it in an quote block"
(interactive "r\nsEnter text to surround: " start end txt)
;; If the region is not active, we use the 'thing-at-point' function
;; to get a "symbol" (often a variable or a single word in text),
;; and use that as our region.
(if (not (region-active-p))
(let ((new-region (bounds-of-thing-at-point 'symbol)))
(setq start (car new-region))
(setq end (cdr new-region))))
;; We create a table of "odd balls" where the front and the end are
;; not the same string.
(let* ((s-table '(("#e" . ("#+BEGIN_EXAMPLE\n" "\n#+END_EXAMPLE") )
("#s" . ("#+BEGIN_SRC \n" "\n#+END_SRC") )
("#q" . ("#+BEGIN_QUOTE\n" "\n#+END_QUOTE"))
("<" . ("<" ">"))
("(" . ("(" ")"))
("{" . ("{" "}"))
("[" . ("[" "]")))) ; Why yes, we'll add more
(s-pair (assoc-default txt s-table)))
;; If txt doesn't match a table entry, then the pair will just be
;; the text for both the front and the back...
(unless s-pair
(setq s-pair (list txt txt)))
(save-excursion
(narrow-to-region start end)
(goto-char (point-min))
(insert (car s-pair))
(goto-char (point-max))
(insert (cadr s-pair))
(widen))))
(global-set-key (kbd "C-+") 'surround)To make it easier to call from other functions, let’s wrap that wrapper:
(defun surround-text (txt)
(if (region-active-p)
(surround (region-beginning) (region-end) txt)
(surround nil nil txt)))This function returns an interactive lambda expression, suitable for adding to a key-binding:
(defun surround-text-with (surr-str)
"Returns an interactive function that when called, will surround the region (or word) with the SURR-STR string."
(lexical-let ((text surr-str))
(lambda ()
(interactive)
(surround-text text))))The Projectile project is a nifty way to run commands and search for files in a particular “project”. Its necessity is less now that IDO with flexible matching seems to always just find what I need.
(use-package projectile
:ensure t
:init (projectile-global-mode 1)
:commands projectile-ag
:config
(progn
(setq projectile-completion-system 'ido)
(add-to-list 'projectile-globally-ignored-files ".DS_Store")))Between M-! and starting Eshell, comes dired (C-x d).
(setq ls-lisp-use-insert-directory-program nil)This enhancement to dired hides the ugly details until you hit ‘(’ and shows the details with ‘)’. I also change the […] to a simple asterisk.
(use-package dired-details
:ensure t
:init (setq dired-details-hidden-string "* ")
:config (dired-details-install))The ability to create a dired buffer based on searching for files
in a directory tree with find-name-dired is fantastic. The
following magic optimizes this approach:
(use-package find-dired
:ensure t
:init (setq find-ls-option '("-print0 | xargs -0 ls -od" . "-od")))The dired-x project seems useful:
(require 'dired-x)The ability to edit files on remote systems is a wonderful win, since it means I don’t need to have my Emacs environment running on remote machines (still a possibility, just not a requirement).
According to the manual, I can access a file over SSH, via:
/ssh:10.52.224.67:blah
If I set the default method to SSH, I can do this:
/10.52.224.67:blah
So, let’s do it…
(setq tramp-default-method "ssh")According to Emacs Fu, we can use the wonderful Tramp to edit Root-owned files, as in:
(defun ha/find-file-as-root ()
"Like `ido-find-file, but automatically edit the file with
root-privileges (using tramp/sudo), if the file is not writable by
user."
(interactive)
(let ((file (ido-read-file-name "Edit as root: ")))
(unless (file-writable-p file)
(setq file (concat "/sudo:root@localhost:" file)))
(find-file file)))The trick, as always, is finding the correct keybinding… but I
have the C-c f as prefix for loading all sorts of files…
(global-set-key (kbd "C-c f r") 'ha/find-file-as-root)According to Mickey, IDO is the greatest thing.
(use-package ido
:ensure t
:init (setq ido-enable-flex-matching t
ido-everywhere t)
:config (ido-mode 1)
(ido-everywhere 1))Add to IDO, the FLX package:
(use-package flx-ido
:ensure t
:init (setq ido-enable-flex-matching t
ido-use-faces nil)
:config (flx-ido-mode 1))According to Ryan Kneufeld, we could make IDO work vertically, which is much easier to read. For this, I use ido-vertically:
(use-package ido-vertical-mode
:ensure t
:init ; I like up and down arrow keys:
(setq ido-vertical-define-keys 'C-n-C-p-up-and-down)
:config
(ido-vertical-mode 1))Built using IDO to do something similar but with M-x commands:
(use-package smex
:ensure t
:init (smex-initialize)
:bind ("M-x" . smex)
("M-X" . smex-major-mode-commands))Obviously, Helm would be helpful if I can learn all the bindings, so:
(use-package helm
:ensure t
:init (add-to-list 'guide-key/guide-key-sequence "C-x c")
(use-package helm-config)) ;; Binds C-x c to the helm bidness.Re-read this essay on Helm.
I have a voluminous amount of org-mode text files I routinely need search and filter.
I use the standard grep package in Emacs, but need a later version of Gnu Grep. On Mac OS X, run these two commands:
brew tap homebrew/dupes
brew install homebrew/dupes/grepWith Wilfred Hughes’ fancy ag package, I’ve switch from ack to the Silver Searcher:
brew install agBest part about the ag package, is not needing any configuration (as all functions are load-on demand).
ag-project-at-point- sets the query with the word at point, use:
C-c p s s ag-regexp- searches for regular expressions in a chosen
directory (Note: the
agcommand prompts withregexp, but it adds a--literaloption to the command) C-u- Adding a prefix adds command line options, like
-sor-ito specify case-sensitivity.
Create collection of ignorable files so it doesn’t look in backup files:
#.*
Using the latest version of ag? Highlight the keywords:
(use-package ag
:ensure t
:commands ag
:init (setq ag-highlight-search t)
:config (add-to-list 'ag-arguments "--word-regexp"))Personally, I’m almost always looking for full words:
However, I also need a global indexing approach to searching through my notes, and since I’m usually on a Mac, I might as well use the Spotlight service that is already running:
(setq locate-command "mdfind") ;; Use Mac OS X's Spotlight
(global-set-key (kbd "C-c f l") 'locate)The following function wraps locate-with-filter to only grab
org-mode files:
(defun locate-org-files (search-string)
(interactive "sSearch string: ")
(locate-with-filter search-string ".org$"))
(global-set-key (kbd "C-c f o") 'locate-org-files)We could limit the location that Spotlight request searches:
(defun locate-my-org-files (search-string)
(let ((tech (concat (getenv "HOME") "/technical"))
(pers (concat (getenv "HOME") "/personal"))
(note (concat (getenv "HOME") "/notes"))
(jrnl (concat (getenv "HOME") "/journal")))
(-flatten (list "mdfind"
(if (file-exists-p tech) (list "-onlyin" tech))
(if (file-exists-p pers) (list "-onlyin" pers))
(if (file-exists-p note) (list "-onlyin" note))
(if (file-exists-p jrnl) (list "-onlyin" jrnl))
"-interpret" search-string))))
(setq locate-make-command-line 'locate-my-org-files)However, the problem with locate, is it doesn’t show me any
context. My find-notes script uses both mdfind and grep to both
better search and display some useful context.
Just need to wrap that in a function:
(defun find-notes (words)
"Uses my 'find-notes' shell script as a better grep
utility. Not only does it show the results in a clickable list,
it also highlights the result, allowing us to put more context in
the output."
(interactive "sSearch for words:")
(let ((program (concat (getenv "HOME") "/bin/find-notes"))
(buffer-name (concat "*find-notes: " words "*")))
(call-process program nil buffer-name t words)
(switch-to-buffer buffer-name)
(read-only-mode 1)
(grep-mode)
(toggle-truncate-lines)
(beginning-of-buffer)
(dolist (word (split-string words))
(highlight-regexp word))))
(global-set-key (kbd "C-x C-n") 'find-notes)
(global-set-key (kbd "C-c f n") 'find-notes)According to this article, Emacs already has the recent file listing available, just not turned on.
(use-package recentf
:init
(setq recentf-max-menu-items 25)
(recentf-mode 1)
:bind ("C-c f f" . recentf-open-files))This setting moves all backup files to a central location. Got it from this page.
(setq backup-directory-alist
`(("." . ,(expand-file-name
(ha/emacs-subdirectory "backups")))))Make backups of files, even when they’re in version control
(setq vc-make-backup-files t)And let’s make sure our files are saved if we wander off and defocus the Emacs application:
(defun save-all ()
"Saves all dirty buffers without asking for confirmation."
(interactive)
(save-some-buffers t))
(add-hook 'focus-out-hook 'save-all)Just beginning to get a collection of templates to automatically insert if a blank file is loaded.
(use-package autoinsert
:init
(setq auto-insert-directory (ha/emacs-subdirectory "templates/"))
;; Don't want to be prompted before insertion:
(setq auto-insert-query nil)
(add-hook 'find-file-hook 'auto-insert)
(auto-insert-mode 1))Add a :config section to configure static insertion, and add:
(define-auto-insert "\\.html?$" "default-html.html")However, auto insertion requires entering data for particular fields, and for that Yasnippet is better, so in this case, we combine them:
(defun ha/autoinsert-yas-expand()
"Replace text in yasnippet template."
(yas-expand-snippet (buffer-string) (point-min) (point-max)))Now bind many of the templates for auto-insert and field expansion:
(use-package autoinsert
:config
(define-auto-insert "\\.el$" ["default-lisp.el" ha/autoinsert-yas-expand])
(define-auto-insert "\\.sh$" ["default-sh.sh" ha/autoinsert-yas-expand])
(define-auto-insert "/bin/" ["default-sh.sh" ha/autoinsert-yas-expand])
(define-auto-insert "\\.html?$" ["default-html.html" ha/autoinsert-yas-expand]))Using company-mode for all my auto completion needs.
Like this idea of being able to easily insert math symbols based on LaTeX keywords. Start typing a backslash.
(use-package company
:ensure t
:init
(add-hook 'after-init-hook 'global-company-mode)
:config
(add-to-list 'company-backends 'company-math-symbols-unicode)
:diminish company-mode)Take advantage of idle time by displaying some documentation using company-quickhelp project.
(use-package company-quickhelp
:ensure t
:config
(company-quickhelp-mode 1))This also requires pos-tip.
The yasnippet project allows me to create snippets of code that can be brought into a file, based on the language.
(use-package yasnippet
:ensure t
:init
(yas-global-mode 1)
:config
(add-to-list 'yas-snippet-dirs (ha/emacs-subdirectory "snippets")))Note:: the snippets directory contains directories for each
mode, e.g. clojure-mode and org-mode.
According to this discussion, we can correct a misspelled word
with C-x C-i and it will use the abbreviation mode to
automatically correct that word…as long as you misspell it the
same way each time.
(defun ha/ispell-word-then-abbrev (p)
"Call `ispell-word'. Then create an abbrev for the correction made.
With prefix P, create local abbrev. Otherwise it will be global."
(interactive "P")
(let ((bef (downcase (or (thing-at-point 'word) ""))) aft)
(call-interactively 'ispell-word)
(setq aft (downcase (or (thing-at-point 'word) "")))
(unless (string= aft bef)
(message "\"%s\" now expands to \"%s\" %sally"
bef aft (if p "loc" "glob"))
(define-abbrev
(if p global-abbrev-table local-abbrev-table)
bef aft))))
(global-set-key (kbd "C-x C-i") 'ha/ispell-word-then-abbrev)Need to turn on the mode, but not necessarily show it:
(use-package abbrev
:bind ("C-c T a" . abbrev-mode)
:init (setq save-abbrevs t)
(setq-default abbrev-mode t)
:diminish abbrev-mode)I like spell checking with FlySpell, which uses the built-in spell-check settings of ispell.
The ASpell project is better supported than ISpell.
brew install aspellStart for all text modes (but not for log files):
(use-package flyspell
:ensure t
:diminish flyspell-mode
:init
(add-hook 'prog-mode-hook 'flyspell-prog-mode)
(dolist (hook '(text-mode-hook org-mode-hook))
(add-hook hook (lambda () (flyspell-mode 1))))
(dolist (hook '(change-log-mode-hook log-edit-mode-hook org-agenda-mode-hook))
(add-hook hook (lambda () (flyspell-mode -1))))
:config
(setq ispell-program-name "/usr/local/bin/aspell"
ispell-dictionary "american" ; better for aspell
ispell-extra-args '("--sug-mode=ultra" "--lang=en_US")
ispell-list-command "--list")
(add-to-list 'ispell-local-dictionary-alist '(nil
"[[:alpha:]]"
"[^[:alpha:]]"
"['‘’]"
t
("-d" "en_US")
nil
utf-8)))ASpell automatically configures a personal dictionary
at ~/.aspell.en.pws, so no need to configure that.
Turn linum-mode on/off with Command-K (see the Macintosh
section above). However, I turn this on automatically for
programming modes.
(add-hook 'prog-mode-hook 'linum-mode)If we make the line numbers a fixed size, then increasing or decreasing the font size doesn’t truncate the numbers:
(defun fix-linum-size ()
(interactive)
(set-face-attribute 'linum nil :height 110))
(add-hook 'linum-mode-hook 'fix-linum-size)If we alternate between line numbers and no-line numbers, I also have to turn on/off the fringe. Actually, this is really only useful when giving presentations.
(defun linum-off-mode ()
"Toggles the line numbers as well as the fringe. This allows me
to maximize the screen estate."
(interactive)
(if linum-mode
(progn
(fringe-mode '(0 . 0))
(linum-mode -1))
(fringe-mode '(8 . 0))
(linum-mode 1)))
(global-set-key (kbd "A-C-K") 'linum-off-mode)
(global-set-key (kbd "s-C-K") 'linum-off-mode) ;; For LinuxI’m intrigued with the linum-relative mode (especially since I can
toggle between them). The idea is that I can see the line that I
want to jump to (like one 9 lines away), and then C-9 C-n to
quickly pop to it.
(use-package linum-relative
:ensure t
:config
;; Otherwise, let's take advantage of the relative line numbering:
(defun linum-new-mode ()
"If line numbers aren't displayed, then display them.
Otherwise, toggle between absolute and relative numbers."
(interactive)
(if linum-mode
(linum-relative-toggle)
(linum-mode 1)))
:bind ("A-k" . linum-new-mode)
("s-k" . linum-new-mode)) ;; For LinuxUse the M-n to search the buffer for the word the cursor is
currently pointing. M-p to go backwards. See this essay for
details.
(use-package smartscan
:ensure t
:bind ("M-n" . smartscan-symbol-go-forward)
("M-p" . smartscan-symbol-go-backward))When I save, I want to always, and I do mean always strip all trailing whitespace from the file.
(add-hook 'before-save-hook 'delete-trailing-whitespace)The Visual Regular Expressions project highlights the matches while you try to remember the differences between Perl’s regular expressions and Emacs’…
Begin with C-c r then type the regexp. To see the highlighted
matches, type C-c a before you hit ‘Return’ to accept it.
(use-package visual-regexp
:ensure t
:init
(use-package visual-regexp-steroids :ensure t)
:bind (("C-c r" . vr/replace)
("C-c q" . vr/query-replace))
;; if you use multiple-cursors, this is for you:
:config (use-package multiple-cursors
:bind ("C-c m" . vr/mc-mark)))Flycheck seems to be quite superior to good ol’ Flymake.
(use-package flycheck
:ensure t
:init
(add-hook 'after-init-hook 'global-flycheck-mode)
:config
(setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc)))The Hungry Delete project is a free feature, where deleting any space, deletes ALL spaces.
This is already built into Emacs with the following:
M-\- Removes all spaces
M-SPC- Removes extra spaces, leaving just one
M-^- Joins current line with previous line (doesn’t matter where the point is on the line)
M-- M-1 M-SPC- Joins next line to this one (if point at end of line) separated by a space … quite the chording, eh?
Many programming language environments can benefit from this section.
I like ElDoc support (when I can get it), but not needed in the mode line:
(use-package eldoc
:diminish eldoc-mode)All programming languages require some sort of tagging. but after thirty years, we are still using good ol’ ctags…well, Exuberant Ctags. Install with Homebrew:
brew install --HEAD ctagsOn Ubuntu Linux, do:
sudo apt-get install -y exuberant-ctagsNote: for every project, run the following command:
ctags -e -R .I want to be able to add headers from my org-mode files as
a language option:
--langdef=org
--langmap=org:.org
--regex-org=/^\*+[ \t]+([a-zA-Z0-9_ ]+)/\1/d,definition/We access stuff by loading the etags package:
(require 'etags)Now, use the following keys:
- M-.
- To find the tag at point to jump to the function’s definition when the point is over a function call. It is a dwim-type function.
- M-,
- jump back to where you were.
- M-?
- find a tag, that is, use the Tags file to look up a definition. If there are multiple tags in the project with the same name, use `C-u M-.’ to go to the next match.
M-x tags-search- regexp-search through the source files
indexed by a tags file (a bit like
grep) M-x tags-query-replace- query-replace through the source files indexed by a tags file
M-x tags-apropos- list all tags in a tags file that match a regexp
M-x list-tags- list all tags defined in a source file
With the fancy new ctags-update package, we can update the tags file whenever we save a file:
(use-package ctags-update
:ensure t
:config
(add-hook 'prog-mode-hook 'turn-on-ctags-auto-update-mode)
:diminish ctags-auto-update-mode)While, I like imenu, combining it with an IDO interface nicely
lists the headings/functions in the current buffer:
(use-package idomenu
:ensure t
:bind ("C-c i" . idomenu))And if I’m lazy and willing to use the mouse:
(use-package imenu+
:ensure t
:init (add-hook 'prog-mode-hook 'imenup-add-defs-to-menubar)
(add-hook 'org-mode-hook 'imenup-add-defs-to-menubar))If I don’t know what I’m after, Helm is better:
(use-package helm
:bind (("C-c M-i" . helm-imenu)))Notice that I don’t set Helm for everything. Obviously, Helm would be helpful if I can learn all the bindings, so:
(use-package guide-key
:init (add-to-list 'guide-key/guide-key-sequence "C-x c"))However, I need to use this function to use IDO in conjunctions with the TAGS file for all functions in the project:
(use-package ido
:config
(defun ido-find-tag ()
"Find a tag using ido"
(interactive)
(tags-completion-table)
(let (tag-names)
(mapatoms (lambda (x)
(push (prin1-to-string x t) tag-names))
tags-completion-table)
(find-tag (ido-completing-read "Tag: " tag-names))))
(global-set-key (kbd "C-c I") 'ido-find-tag))Emacs 25 changed has now deprecated the famous Tags and Friends,
like find-tags for xref. Some point, I will have to learn how
to configure it, but until then, I’ll just rebind to my old mates:
(global-set-key (kbd "M-.") 'find-tag)
(global-set-key (kbd "C-M-.") 'find-tag-regexp)
(global-set-key (kbd "M-,") 'pop-tag-mark)
(global-set-key (kbd "M-i") 'imenu-anywhere)Note: This prompt needs to go away:
(setq tags-add-tables nil)The Hide Show Minor mode allows us to fold all functions (hidden), showing only the header lines. We need to turn on the mode, so wrappers are in order:
(defun ha/hs-show-all ()
(interactive)
(hs-minor-mode 1)
(hs-show-all))
(defun ha/hs-hide-all ()
(interactive)
(hs-minor-mode 1)
(hs-hide-all))
(defun ha/hs-toggle-hiding ()
(interactive)
(hs-minor-mode 1)
(hs-toggle-hiding))Seems that C-c @ is too obnoxious to use, so I’ll put my
favorite on the C-c h prefix:
(use-package hs-minor-mode
:bind
("C-c T h" . hs-minor-mode)
("C-c h a" . ha/hs-hide-all)
("C-c h s" . ha/hs-show-all)
("C-c h h" . ha/hs-toggle-hiding))See the online resources.
Automatically indent without use of the tab found in this article, and seems to be quite helpful for many types of programming languages.
To begin, we create a function that can indent a function by
calling indent-region on the beginning and ending points of a
function.
(defun indent-defun ()
"Indent current defun.
Do nothing if mark is active (to avoid deactivaing it), or if
buffer is not modified (to avoid creating accidental
modifications)."
(interactive)
(unless (or (region-active-p)
buffer-read-only
(null (buffer-modified-p)))
(let ((l (save-excursion (beginning-of-defun 1) (point)))
(r (save-excursion (end-of-defun 1) (point))))
(cl-letf (((symbol-function 'message) #'ignore))
(indent-region l r)))))Next, create a hook that will call the indent-defun with every
command call:
(defun activate-aggressive-indent ()
"Locally add `ha/indent-defun' to `post-command-hook'."
(add-hook 'post-command-hook
'indent-defun nil 'local))Various keywords (in comments) are now flagged in a Red Error font:
(add-hook 'prog-common-hook
(lambda ()
(font-lock-add-keywords nil
'(("\\<\\(FIX\\|FIXME\\|TODO\\|BUG\\|HACK\\):" 1 font-lock-warning-face t)))))Files in my bin directory (but only if it doesn’t have any
other extension), should start in sh-mode:
(add-to-list 'auto-mode-alist '("/bin/" . sh-mode))Sure, everything here is in Emacs Lisp, but this section helps me write more of that… like making snazzy symbols and colorizing the variables.
The color-identifiers project (unlike others), downplay the keywords, and increase the colorizing of the variables.
(use-package color-identifiers-mode
:ensure t
:init
(add-hook 'emacs-lisp-mode-hook 'color-identifiers-mode)
:diminish color-identifiers-mode)The only real snazzy symbol that I like is replacing the lambda
with λ:
(use-package lisp-mode
:init
(defconst lisp--prettify-symbols-alist
'(("lambda" . ?λ)
("." . ?•)))
:config
(add-hook 'emacs-lisp-mode-hook 'global-prettify-symbols-mode)
(add-hook 'emacs-lisp-mode-hook 'activate-aggressive-indent))One of the cooler features of Emacs is the ParEdit mode which keeps all parenthesis balanced in Lisp-oriented languages. See this cheatsheet.
(use-package paredit
:ensure t
:diminish paredit-mode
:init
(add-hook 'emacs-lisp-mode-hook 'paredit-mode))Color each variable, and downplay standard key words:
(use-package color-identifiers-mode
:ensure t
:init
(add-hook 'emacs-lisp-mode-hook 'color-identifiers-mode))The reverse mode of the default parenthesis matching doesn’t match as well, so this code just makes it bold and more obvious:
(use-package paren
:init
(set-face-background 'show-paren-match (face-background 'default))
(set-face-foreground 'show-paren-match "#afa")
(set-face-attribute 'show-paren-match nil :weight 'black)
(set-face-background 'show-paren-mismatch (face-background 'default))
(set-face-foreground 'show-paren-mismatch "#c66")
(set-face-attribute 'show-paren-mismatch nil :weight 'black))While we are at it, let’s dim the parens:
(use-package paren-face
:ensure t
:init
(global-paren-face-mode))While writing and documenting Emacs Lisp code, it would be helpful to insert the results of evaluation of an s-expression directly into the code as a comment:
(use-package lisp-mode
:config (defun eval-and-comment-output ()
"Add the output of the sexp as a comment after the sexp"
(interactive)
(save-excursion
(end-of-line)
(condition-case nil
(princ (concat " ; -> " (pp-to-string (eval (preceding-sexp))))
(current-buffer))
(error (message "Invalid expression")))))
:bind ("C-x e" . eval-and-comment-output))See emacs-clojure.el for details on working with Clojure. Not sure if I should just load it directly, like:
(require 'init-clojure)As soon as a I have a project that requires Java (and doesn’t allow me to work on either Clojure or Scala, I’ll update my old Java initialization section.
(defun my-c-mode-hook ()
(setq c-basic-offset 4)
(c-set-offset 'substatement-open 0) ; Curly braces alignment
(c-set-offset 'case-label 4)) ; Switch case statements alignment
(add-hook 'c-mode-hook 'my-c-mode-hook)
(add-hook 'java-mode-hook 'my-c-mode-hook)See my emacs-ruby.el file for details on working with Ruby. Typically, my emacs-local.el file would do the work of requiring this for particular hosts or projects.
(require 'init-ruby)See emacs-python.el for details on working with Python. Not sure if I should just load it directly, like:
(require 'init-python)See emacs-javascript.el for details on working with JavaScript.
(require 'init-javascript)See emacs-web.el for details on working with HTML and its ilk.
(require 'init-web)See emacs-org-mode.el for details on my Org-Mode settings.
(require 'init-org-mode)I like git-gutter-fringe:
(use-package git-gutter-fringe
:ensure t
:config (git-gutter-mode 1))I want to have special mode for Git’s configuration file:
(use-package gitconfig-mode
:ensure t)
(use-package gitignore-mode
:ensure t)What about being able to see the Git blame in a buffer?
(use-package mo-git-blame
:ensure t)Run mo-git-blame-current to see the goodies.
Git is already part of Emacs. However, Magit is sweet. Don’t believe me? Check out this video.
(use-package magit
:ensure t
:commands magit-status magit-blame
:init
(defadvice magit-status (around magit-fullscreen activate)
(window-configuration-to-register :magit-fullscreen)
ad-do-it
(delete-other-windows))
:config
(setq magit-branch-arguments nil
;; use ido to look for branches
magit-completing-read-function 'magit-ido-completing-read
;; don't put "origin-" in front of new branch names by default
magit-default-tracking-name-function 'magit-default-tracking-name-branch-only
magit-push-always-verify nil
;; Get rid of the previous advice to go into fullscreen
magit-restore-window-configuration t)
:bind ("C-x g" . magit-status))I like having Magit to run in a full screen mode, and add the
above defadvice idea from Sven Magnars.
Don’t use Markdown nearly as much as I used to, but I’m surprised that the following extension-associations aren’t the default:
(use-package markdown-mode
:ensure t
:mode ("\\.\\(m\\(ark\\)?down\\|md\\)$" . markdown-mode)
:config
(bind-key "A-b" (surround-text-with "+*") markdown-mode-map)
(bind-key "s-b" (surround-text-with "**") markdown-mode-map)
(bind-key "A-i" (surround-text-with "*") markdown-mode-map)
(bind-key "s-i" (surround-text-with "*") markdown-mode-map)
(bind-key "A-=" (surround-text-with "`") markdown-mode-map)
(bind-key "s-=" (surround-text-with "`") markdown-mode-map))Install the Graphviz and PlantUML projects using Homebrew:
brew install graphviz
brew link graphviz
brew install plantumlLoad the mode for PlantUML and reference its jar:
(let ((plantuml-jar (car (file-expand-wildcards "/usr/local/Cellar/plantuml/*/plantuml*.jar"))))
(if plantuml-jar
(use-package plantuml-mode
:ensure t
:init
(setq plantuml-jar-path plantuml-jar
org-plantuml-jar-path plantuml-jar))))And the mode for Graphviz:
(use-package graphviz-dot-mode
:ensure t)This section became involved, and has moved on to emacs-browser file.
(require 'init-browser)See emacs-eshell.el for details of configuring and using EShell.
(require 'init-eshell)Using the jabber.el project to connect up to Google Talk and what
not. To begin, make sure you brew install gnutls
(use-package jabber
:ensure t
:commands jabber-connect-all jabber-chat-with
:init
(define-key personal-global-map (kbd "a") 'jabber-connect-all)
(define-key personal-global-map (kbd "j") 'jabber-chat-with)
:config
(setq starttls-use-gnutls t
starttls-gnutls-program "gnutls-cli"
starttls-extra-arguments '("--starttls" "--insecure")
jabber-history-enabled t
jabber-use-global-history nil
jabber-backlog-number 40
jabber-backlog-days 30)
(defun my-jabber-chat-delete-or-bury ()
(interactive)
(if (eq 'jabber-chat-mode major-mode)
(condition-case e
(delete-frame)
(error
(if (string= "Attempt to delete the sole visible or iconified frame"
(cadr e))
(bury-buffer))))))
(define-key jabber-chat-mode-map [escape] 'my-jabber-chat-delete-or-bury))Make sure that PATH variable for finding binary files can is the
same as what Emacs will look for binary files. This little magic,
starts up a shell, gets its path, and then uses that for the
exec-path:
(when window-system
(let ((path-from-shell (shell-command-to-string "/bin/bash -l -c 'echo $PATH'")))
(setenv "PATH" path-from-shell)
(setq exec-path (split-string path-from-shell path-separator))))If we are running in a windowed environment where we can set up fonts and whatnot, call the ‘mac’ stuff… which will still work for Linux too.
(if (window-system)
(require 'init-client)
(require 'init-server))Before we finish, we need to check if there is a local file for us
to load and evaluate. We assume the local file has been tangled
and provides the init-local key:
(require 'init-local nil t)After the first load, we can reload this with a require:
(provide 'init-main)Before you can build this on a new system, make sure that you put
the cursor over any of these properties, and hit: C-c C-c