Skip to content

Latest commit

 

History

History
2484 lines (1954 loc) · 85.6 KB

emacs.org

File metadata and controls

2484 lines (1954 loc) · 85.6 KB

Emacs Configuration File

Introduction

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. ;-)

Emacs Executable

While I often use Emacs for Mac, lately, I’ve been building Emacs from source using Homebrew. I start by adding the following dependency:

brew cask install xquartz

Note: I’ve been running into an issue where I want Emacs to display ligatures as well as work with the visual regular expression package. To get the both to work, I find I need to build from source after applying this patch. According to the Homebrew documentation (see the Patches section), we first:

brew edit emacs

And add the following section

patch do
  url "https://github.com/minimal/emacs/commit/812dd5119645a09bc025a9dddedad9474d12ecb6.diff"
end

And then build from source especially for the Mac:

brew install emacs --HEAD --use-git-head --with-cocoa --with-gnutls --with-librsvg --with-ns --with-imagemagick

brew linkapps emacs

Not 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 emacs

Loading this File

To “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")
  (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.

General Settings

My Directory Location

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))

Directory Structure

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"))
       (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))))

Customization Section

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))

Setting up the Load Path

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"))

Modernizing Emacs

With a long history of working on small machines without gigabytes of RAM, we might as well let Emacs be the beast it has always dreamed.

First, let’s increase the cache before starting garbage collection:

(setq gc-cons-threshold 50000000)

Found here how to remove the warnings from the GnuTLS library when using HTTPS… increase the minimum prime bits size:

(setq gnutls-min-prime-bits 4096)

Package Initialization

Package Manager

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.org/packages/")
                         ("marmalade" . "http://marmalade-repo.org/packages/")))

(package-initialize)
(package-refresh-contents)

Use-Package

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)

Init File Support

Load up a collection of enhancements to Emacs Lisp, including dash, s for string manipulation, and f for file manipulation.

(require 'cl)

(use-package dash
  :ensure t
  :config (eval-after-load "dash" '(dash-enable-font-lock)))

(use-package s
  :ensure t)

(use-package f
  :ensure t)

Variables

Tabs vs Spaces

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)

Encrypting Files

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 gpg

Now, 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.

(setq epa-file-cache-passphrase-for-symmetric-encryption t)

Misc Variable Settings

Does anyone type yes anymore?

(fset 'yes-or-no-p 'y-or-n-p)

Fix the scrolling to keep point in the center:

(setq scroll-conservatively 10000
      scroll-preserve-screen-position t)

I’ve been using Emacs for too long to need to re-enable each feature bit-by-bit:

(setq disabled-command-function nil)

Display Settings

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 estate

My mode line has become quite complicated, so I’ve pulled it out into its own file:

(require 'my-powerline)

And the best colored highlighting of selected text needs to be both bright, but not obscure the white text in the foreground (see list-colors-display). Favorites so far are purple4 and DarkOrange3:

(set-face-background 'region "blue3")

Most of the display settings actually come from the Mac initialization file.

Whitespace Mode

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)

Fill 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)
         ("C-c T t" . toggle-truncate-lines))
  :init (add-hook 'org-mode-hook 'turn-on-auto-fill)
  :diminish auto-fill-mode)

Key Bindings

Displaying Command Sequences

Many command sequences may be logical, but who can remember them all? While I used to use guide-key to display the final function name, it isn’t as nice as which-key.

(use-package which-key
  :ensure t
  :defer 10
  :diminish which-key-mode
  :config

  ;; Replacements for how KEY is replaced when which-key displays
  ;;   KEY → FUNCTION
  ;; Eg: After "C-c", display "right → winner-redo" as "▶ → winner-redo"
  (setq which-key-key-replacement-alist
        '(("<\\([[:alnum:]-]+\\)>" . "\\1")
          ("left"                  . "")
          ("right"                 . "")
          ("up"                    . "")
          ("down"                  . "")
          ("delete"                . "DEL") ; delete key
          ("\\`DEL\\'"             . "BS") ; backspace key
          ("next"                  . "PgDn")
          ("prior"                 . "PgUp"))

        ;; List of "special" keys for which a KEY is displayed as just
        ;; K but with "inverted video" face... not sure I like this.
        which-key-special-keys '("RET" "DEL" ; delete key
                                 "ESC" "BS" ; backspace key
                                 "SPC" "TAB")

        ;; Replacements for how part or whole of FUNCTION is replaced:
        which-key-description-replacement-alist
        '(("Prefix Command" . "prefix")
          ("\\`calc-"       . "") ; Hide "calc-" prefixes when listing M-x calc keys
          ("\\`projectile-" . "𝓟/")
          ("\\`org-babel-"  . "ob/"))

        ;; Underlines commands to emphasize some functions:
        which-key-highlighted-command-list
        '("\\(rectangle-\\)\\|\\(-rectangle\\)"
          "\\`org-"))

  ;; Change what string to display for a given *complete* key binding
  ;; Eg: After "C-x", display "8 → +unicode" instead of "8 → +prefix"
  (which-key-add-key-based-replacements
    "C-x 8"   "unicode"
    "C-c T"   "toggles-"
    "C-c p s" "projectile-search"
    "C-c p 4" "projectile-other-buffer-"
    "C-x a"   "abbrev/expand"
    "C-x r"   "rect/reg"
    "C-c /"   "engine-mode-map"
    "C-c C-v" "org-babel")

  (which-key-mode 1))

Function Key Definitions

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 - Move/Drop/Delete a visual ‘mark’
  • 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-c for changing colors and other odd bindings that I actually don’t use that often.
(global-set-key (kbd "<f7>") 'other-window)
(global-set-key (kbd "C-<f7>") (lambda () (interactive) (other-window -1)))

F2 and F9 Helpers

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)

Undo and Redo

According to this article, I get better functionality than the redo+ plugin (which I can’t seem to get working well).

(use-package undo-tree
  :ensure t
  :diminish undo-tree-mode
  :init
  (global-undo-tree-mode 1)
  :config
  (defalias 'redo 'undo-tree-redo)
  :bind (("C-z" . undo)     ; Zap to character isn't helpful
         ("C-S-z" . redo)))

Highlighting and Narrowing

I like the ability to highlight random text.

M-s h .
highlight-symbol-at-point
M-s h l
highlight-lines-matching-regexp
M-s h p
highlight-phrase
M-s h r
highlight-regexp
M-s h 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 this function that checks for the log extension, allows me to hook it to the loading of any file:

(add-hook 'find-file-hook 'ha/highlite-logs)

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 ()
  "Highlight 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))
                                      (setq mark-active nil)))))

  :bind (("C-M-+" . ha/highlight-section)
         ("C-<f12>" . ha/highlight-section)))

This nifty function from Endless Parenthesis is a nice replacement for many other narrowing keybindings that I use:

(defun narrow-or-widen-dwim (p)
  "If the buffer is narrowed, it widens.  Otherwise, it narrows intelligently.
Intelligently means: region, subtree, or defun, whichever applies
first.

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))
        ((region-active-p)
         (narrow-to-region (region-beginning) (region-end)))
        ((derived-mode-p 'org-mode) (org-narrow-to-subtree))
        (t (narrow-to-defun))))

(global-set-key (kbd "C-x n x") 'narrow-or-widen-dwim)

Jumping to Windows

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)

Selecting a Buffer

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))

Better Jumping

Mostly using the avy project’s avy-goto-word-1 function, so I bind that to C-c j, but the recent update to include a timer feature, seems awful sweet:

(use-package avy
  :ensure t
  :init (setq avy-background t))

Shame that the :bind option for use-package doesn’t actually work. Let’s do a temporary fix:

(global-set-key (kbd "s-h") 'avy-goto-char-timer)
(global-set-key (kbd "s-j") 'avy-goto-char-timer)
(global-set-key (kbd "s-H") 'avy-pop-mark)
(global-set-key (kbd "s-J") 'avy-pop-mark)
(global-set-key (kbd "A-h") 'avy-goto-char-timer)
(global-set-key (kbd "A-j") 'avy-goto-char-timer)
(global-set-key (kbd "A-H") 'avy-pop-mark)
(global-set-key (kbd "A-J") 'avy-pop-mark)

Unfill Paragraph

Unfilling a paragraph joins all the lines in a paragraph into a single line. Taken from here.

(defun unfill-paragraph ()
  "Convert a multi-line paragraph 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)

General Behavior Fixes

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)

Multiple Cursors

I’m intrigued with Magmar’s multiple-cursors project. It doesn’t have any default keybindings, so I set up these:

(use-package multiple-cursors
  :ensure t
  :bind (("C-c C-. ."   . mc/mark-all-dwim)
         ("C-c C-. C-." . mc/mark-all-like-this-dwim)
         ("C-c C-. n"   . mc/mark-next-like-this)
         ("C-c C-. C-n" . mc/mark-next-like-this)
         ("C-c C-. p"   . mc/mark-previous-like-this)
         ("C-c C-. C-p" . mc/mark-previous-like-this)
         ("C-c C-. a"   . mc/mark-all-like-this)
         ("C-c C-. C-a" . mc/mark-all-like-this)
         ("C-c C-. N"   . mc/mark-next-symbol-like-this)
         ("C-c C-. C-N" . mc/mark-next-symbol-like-this)
         ("C-c C-. P"   . mc/mark-previous-symbol-like-this)
         ("C-c C-. C-P" . mc/mark-previous-symbol-like-this)
         ("C-c C-. A"   . mc/mark-all-symbols-like-this)
         ("C-c C-. C-A" . mc/mark-all-symbols-like-this)
         ("C-c C-. f"   . mc/mark-all-like-this-in-defun)
         ("C-c C-. C-f" . mc/mark-all-like-this-in-defun)
         ("C-c C-. l"   . mc/edit-lines)
         ("C-c C-. C-l" . mc/edit-lines)
         ("C-c C-. e"   . mc/edit-ends-of-lines)
         ("C-c C-. C-e" . mc/edit-ends-of-lines)
         ("C-M-<mouse-1>" . mc/add-cursor-on-click)))

However, Like Chris Wellons said:

I’ve come to the conclusion that multiple cursors is all hat and no cattle. It doesn’t compose well with other editing commands, it doesn’t scale up to large operations, and it’s got all sorts of flaky edge cases (off-screen cursors). Nearly anything you can do with multiple cursors, you can do better with old, well-established editing paradigms.

That said, I’m still going to impress my friends with it.

Expand Region

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))

Block Wrappers

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)
  "Wrap region with textual markers.

 Without active region (START and END), use the current 'symbol /
word' at point instead of TXT.

Useful for wrapping parens and angle-brackets to also
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)

This function returns an interactive lambda expression, suitable for adding to a key-binding:

(defun surround-text-with (surr-str)
  "Return an interactive function that when called, surrounds region (or word) with string, SURR-STR."
  (lexical-let ((text surr-str))
      (lambda ()
        (interactive)
        (if (region-active-p)
            (surround (region-beginning) (region-end) text)
          (surround nil nil text)))))

Loading and Finding Files

Projectile

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.

However, I really like the ability to search (with ag or grep) that is limited to the project:

(use-package projectile
  :ensure t
  :init (projectile-global-mode 0)
  :bind (("C-c p s" . projectile-ag)
         ("C-c p g" . projectile-grep)
         ("C-c p R" . projectile-regenerate-tags)))

Projectile is currently causing grief to the rest of my system, and while trying to debug it, let’s turn it off:

(use-package projectile
  :ensure t
  :diminish projectile-mode
  :init (projectile-global-mode 1)
  :commands projectile-ag
  :config
  (setq projectile-switch-project-action 'projectile-commander
        projectile-completion-system 'ido
        projectile-create-missing-test-files t)
  (add-to-list 'projectile-globally-ignored-files ".DS_Store")

  (def-projectile-commander-method ?d
    "Open project root in dired."
    (projectile-dired))

  (def-projectile-commander-method ?s
    "Open a *shell* buffer for the project."
    (projectile-run-shell))

  (def-projectile-commander-method ?X
    "Open a Direx buffer on the side."
    (call-interactively #'ha/projectile-direx))

  (def-projectile-commander-method ?F
    "Git fetch."
    (magit-status)
    (call-interactively #'magit-fetch-current))

  (def-projectile-commander-method ?j
    "Jack-in with Cider."
    (let* ((opts (projectile-current-project-files))
           (file (ido-completing-read
                  "Find file: "
                  opts
                  nil nil nil nil
                  (car (cl-member-if
                        (lambda (f)
                          (string-match "core\\.clj\\'" f))
                        opts)))))
      (find-file (expand-file-name
                  file (projectile-project-root)))
      (run-hooks 'projectile-find-file-hook)
      (cider-jack-in))))

Much of the previous section came from this essay.

Direx

The direx package is a tree-based variation of dired, and it gives an ide-like look and feel. Not sure of its useful-ness.

(use-package direx
  :ensure t
  :bind (("C-c p X" . ha/projectile-direx)
         :map direx:direx-mode-map
         ("q" . kill-buffer-and-window))
  :init
  (defun kill-buffer-and-window (&optional buffer)
    "Kills the buffer and closes the window it is in."
    (interactive)
    (kill-buffer buffer)
    (delete-window))

  (defun ha/projectile-direx (prefix)
    "Start direx in the top-level of a project in a buffer window
     that spans the entire left side of the frame."
    (interactive "P")
    (let ((file-name (file-name-nondirectory (buffer-file-name)))
          (buffer (direx:find-directory-reuse-noselect (projectile-project-root)))
          (window (ha/split-main-window 'left 30)))
      (select-window window)
      (direx:maybe-goto-current-buffer-item buffer)
      (switch-to-buffer buffer)
      (search-forward file-name))))

The following helper function creates a window at the top-level, ignoring other windows in the frame.

(defun ha/split-main-window (direction size)
  "Split the main window in the DIRECTION where DIRECTION is a
symbol with possible values of 'right, 'left, 'above or 'below
and SIZE is the final size of the windows, if the window is split
horizontally (i.e. DIRECTION 'below or 'above) SIZE is assumed to
be the target height otherwise SIZE is assumed to be target width."
  (let* ((new-window (split-window (frame-root-window) nil direction))
         (horizontal (member direction '(right left))))
    (save-excursion
      (select-window new-window)
      (enlarge-window (- size (if horizontal
                                  (window-width)
                                (window-height)))
                      horizontal))
    new-window))

Controlling Window Placement

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))

But I would like to jump between different layout configurations based on purpose or project. I started with Eyebrowse, but I’m currently experimenting with other options.

Perspective

The perspective project and Batsov’s projectile project are now joined together which means that each project has its own perspective.

(use-package perspective
  :ensure t
  :bind ("C-x x x" . persp-switch-last)
  :init (persp-mode +1)

  (use-package persp-projectile
    :ensure t
    :bind ("C-x x P" . projectile-persp-switch-project))

  :config
    (setq persp-interactive-completion-function #'ido-completing-read)
    (persp-turn-off-modestring))

My workflow consists of:

  • C-x x P to investigate a new project with its new perspective (this also saves off whatever I was doing)
  • C-x x x switches to whatever I was doing before
  • C-x x s switches to a project’s perspective based on its name

Workgroups

While winner-mode is easy to keep the current window configuration clean, the workgroups project has some nice features.

; (load-file "~/.emacs.d/elpa/workgroups-20110726.941/workgroups.el")

(use-package workgroups
  :ensure t
  :diminish workgroups-mode
  :config
  (setq wg-prefix-key (kbd "C-c a"))
  (workgroups-mode 1)
  (wg-load "~/.emacs.d/workgroups"))

Short answer for using it:

  • C-c a c to create and name a new view
  • Configure the screen as you like it
  • C-c a u to have that view as the base for that name
  • C-c a v to switch to a particular workgroup view.
  • C-c a C-s to save all workgroup views to the file.

I’m tempted to switch to workgroups2 (but it has its own bugs):

(use-package workgroups2
  :ensure t
  :diminish workgroups-mode
  :init
  (setq wg-prefix-key (kbd "C-c a")
        wg-session-file "~/.emacs.d/workgroups2"
        wg-mode-line-display-on nil
        ;; What to do on Emacs exit / workgroups-mode exit?
        wg-emacs-exit-save-behavior           'save      ; Options: 'save 'ask nil
        wg-workgroups-mode-exit-save-behavior 'save)
  (workgroups-mode 1))

Dired Options

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 peep project allows you to preview files before loading them into a dedicated buffer:

(use-package peep-dired
  :defer t ; don't access `dired-mode-map' until `peep-dired' is loaded
  :bind (:map dired-mode-map
              ("P" . peep-dired)))

The dired-x project seems useful:

(use-package dired-x)

IDO (Interactively DO Things)

According to Mickey, IDO is the greatest thing.

(use-package ido
  :ensure t
  :init  (setq ido-enable-flex-matching t
               ido-ignore-extensions t
               ido-use-virtual-buffers t
               ido-everywhere t)
  :config
  (ido-mode 1)
  (ido-everywhere 1)
  (add-to-list 'completion-ignored-extensions ".pyc"))

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 Neufeld, 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))

IDO File Listing by Modified Time

This sorts an IDO filelist by mtime instead of alphabetically.

(defun ido-sort-mtime ()
  "Reorder the IDO file list to sort from most recently modified."
  (setq ido-temp-list
        (sort ido-temp-list
              (lambda (a b)
                (ignore-errors
                  (time-less-p
                   (sixth (file-attributes (concat ido-current-directory b)))
                   (sixth (file-attributes (concat ido-current-directory a))))))))
  (ido-to-end  ;; move . files to end (again)
   (delq nil (mapcar
              (lambda (x) (and (char-equal (string-to-char x) ?.) x))
              ido-temp-list))))

(add-hook 'ido-make-file-list-hook 'ido-sort-mtime)
(add-hook 'ido-make-dir-list-hook 'ido-sort-mtime)

Editing Root Files

Once I wrote a find-file-as-root function (graciously borrowed from Emacs Fu), however, bbatsov gave me a better idea to lend some advice to find-file, so that non-writable files would be automatically re-opened using the sudo feature of Tramp.

My version works with both local and remotely access files:

(defadvice ido-find-file (after find-file-sudo activate)
  "Find file as root if necessary."
  (unless (and buffer-file-name
               (file-writable-p buffer-file-name))
    (let* ((file-name (buffer-file-name))
           (file-root (if (string-match "/ssh:\\([^:]+\\):\\(.*\\)" file-name)
                          (concat "/ssh:"  (match-string 1 file-name)
                                  "|sudo:" (match-string 1 file-name)
                                  ":"      (match-string 2 file-name))
                        (concat "/sudo:localhost:" file-name))))
      (find-alternate-file file-root))))

No special key-bindings, just load up a file, and if I can’t write it, it will automatically ask me for my credentials, and away I go.

SMEX

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))

Helm

The helm project, while helpful, can be quite intrusive, so I have it available, and try to remember to use its features with C-x c:

(use-package helm
  :ensure t
  :init
  (use-package helm-config))   ;; Binds C-x c to the helm bidness.

Re-read this essay on Helm.

The helm-swoop project has a nice DWIM replacement for isearch-forward-symbol-at-point (bound to M-s .):

(use-package helm-swoop
  :ensure t
  :init
  ;; If this value is t, split window inside the current window
  (setq helm-swoop-split-with-multiple-windows t
        ;; If you prefer fuzzy matching
        helm-swoop-use-fuzzy-match t)
  :bind
  (("M-s s" . helm-swoop)  ;; overbind M-i ?
   ("M-s S" . helm-multi-swoop)))

A poor-person’s variable renaming refactoring is to use:

  • M-x helm-multi-swoop-projectile

Note: While in isearch, his M-i to switch over to helm-swoop.

While in a search, the following keys are available:

M-i
Switch to helm-multi-swoop
C-c C-e
Edit the matching buffer (like rename all variables). Save with C-x C-s

Giving it a prefix (with C-u) specifies the number of lines of context.

(define-key helm-swoop-map (kbd "C-s") 'helm-next-line)
(define-key helm-swoop-map (kbd "C-r") 'helm-previous-line)

Grep for my Notes

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/grep

Silver Searcher

With Wilfred Hughes’ fancy ag package, I’ve switch from ack to the Silver Searcher:

brew install ag

Best 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 ag command prompts with regexp, but it adds a --literal option to the command)
C-u
Adding a prefix adds command line options, like -s or -i to 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:

Spotlight

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)
  "Adjust `locate-with-filter' to only search `org-mode' files with 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)
  "Search `org-mode' files in specific directories for WORDS.

Uses `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)

Recent File List

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-auto-cleanup 'never
        recentf-keep '(file-remote-p file-readable-p))
  (recentf-mode 1)
  (let ((last-ido "~/.emacs.d/ido.last"))
    (when (file-exists-p last-ido)
      (delete-file last-ido)))

  :bind ("C-c f r" . recentf-open-files))

We do not want to stat all the files when Emacs starts up because files read by Tramp will slow down the start time.

Backup Settings

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")))))

Tramp should do the same:

(setq tramp-backup-directory-alist backup-directory-alist)

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 ()
  "Save all dirty buffers without asking for confirmation."
  (interactive)
  (save-some-buffers t))

(add-hook 'focus-out-hook 'save-all)

Word Smithing

Auto Insertion

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]))

Auto Complete

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
  (setq company-dabbrev-ignore-case t
        company-show-numbers t)
  (add-hook 'after-init-hook 'global-company-mode)
  :config
  (add-to-list 'company-backends 'company-math-symbols-unicode)
  :bind ("C-:" . company-complete)  ; In case I don't want to wait
  :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.

Yasnippets

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.

Spell Checking

Spell checking with FlySpell, which uses the built-in settings of ispell. The ASpell project is better supported than ISpell, and it seems to be better than Hunspell for programming modes.

brew install aspell

Start 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-local-dictionary "en_US"
        ispell-dictionary "american" ; better for aspell
        ispell-extra-args '("--sug-mode=ultra" "--lang=en_US")
        ispell-list-command "--list"
        ispell-local-dictionary-alist '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "['‘’]"
                                      t ; Many other characters
                                      ("-d" "en_US") nil utf-8))))

ASpell automatically configures a personal dictionary at ~/.aspell.en.pws, so no need to configure that.

A possibly nifty feature of aspell is the ability to spellcheck individual words in CamelCase that is used extensively in some code (for details, see this article).

(use-package flyspell
  :config
  (defun flyspell-detect-ispell-args (&optional run-together)
    "if RUN-TOGETHER is true, spell check the CamelCase words."
    (let (args)
      (setq args (list "--sug-mode=ultra" "--lang=en_US"))
      (if run-together
          (setq args (append args '("--run-together" "--run-together-limit=5" "--run-together-min=2"))))
      args))

  ;; ispell-cmd-args is useless, it's the list of *extra* arguments we will append to the ispell process when "ispell-word" is called.
  ;; ispell-extra-args is the command arguments which will *always* be used when start ispell process
  (setq-default ispell-extra-args (flyspell-detect-ispell-args t))

  (defadvice ispell-word (around my-ispell-word activate)
    (let ((old-ispell-extra-args ispell-extra-args))
      (ispell-kill-ispell t)
      (setq ispell-extra-args (flyspell-detect-ispell-args))
      ad-do-it
      (setq ispell-extra-args old-ispell-extra-args)
      (ispell-kill-ispell t)))

  (defadvice flyspell-auto-correct-word (around my-flyspell-auto-correct-word activate)
    (let ((old-ispell-extra-args ispell-extra-args))
      (ispell-kill-ispell t)
      ;; use emacs original arguments
      (setq ispell-extra-args (flyspell-detect-ispell-args))
      ad-do-it
      ;; restore our own ispell arguments
      (setq ispell-extra-args old-ispell-extra-args)
      (ispell-kill-ispell t)))

  (defun text-mode-hook-setup ()
    ;; Turn off RUN-TOGETHER option when spell check text-mode
    (setq-local ispell-extra-args (flyspell-detect-ispell-args)))

  (add-hook 'text-mode-hook 'text-mode-hook-setup))

According to this essay, we can make a flyspell-goto-previous-error (which really should be added to the official flyspell project):

(defun flyspell-goto-previous-error (arg)
  "Go to ARG previous spelling error."
  (interactive "p")
  (while (not (= 0 arg))
    (let ((pos (point))
          (min (point-min)))
      (when (and (eq (current-buffer) flyspell-old-buffer-error)
                 (eq pos flyspell-old-pos-error))
        (if (= flyspell-old-pos-error min)
            ;; goto beginning of buffer
            (progn
              (message "Restarting from end of buffer")
              (goto-char (point-max)))
          (backward-word 1))
        (setq pos (point)))

      ;; seek the next error
      (while (and (> pos min)
                  (let ((ovs (overlays-at pos))
                        (r '()))
                    (while (and (not r) (consp ovs))
                      (if (flyspell-overlay-p (car ovs))
                          (setq r t)
                        (setq ovs (cdr ovs))))
                    (not r)))
        (backward-word 1)
        (setq pos (point)))
      ;; save the current location for next invocation
      (setq arg (1- arg))
      (setq flyspell-old-pos-error pos)
      (setq flyspell-old-buffer-error (current-buffer))
      (goto-char pos)
      (if (= pos min)
          (progn
            (message "No more miss-spelled words!")
            (setq arg 0))))))

Spell Correction with Abbreviation Mode

According to this discussion, we can correct a misspelled word with Super-; (similar to C-;), but 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'.  After create an abbrev for the correction made.
With prefix P, create local abbrev.  Otherwise it will be
global."
  (interactive "P")
  (flyspell-goto-previous-error 1)
  (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)
      (define-abbrev
        (if p global-abbrev-table local-abbrev-table)
        bef aft)
      (abbrev-edit-save-to-file abbrev-file-name)
      (message "\"%s\" now expands to \"%s\" %s"
               bef aft (if p "locally" "globally")))))

Need to turn on the mode, but not necessarily show it:

(use-package abbrev
  :bind ("C-c T a" . abbrev-mode)
        ("A-;" . ha/ispell-word-then-abbrev)
  :init (setq save-abbrevs t)
        (setq-default abbrev-mode t)
  :diminish abbrev-mode)

Miscellaneous Settings

Line Numbers

Turn linum-mode on/off with Command-K (see the Macintosh section above). However, I turn this on automatically for programming modes.

(use-package linum
  :init
  (add-hook 'prog-mode-hook 'linum-mode)
  (add-hook 'linum-mode-hook (lambda () (set-face-attribute 'linum nil :height 110)))

  :config
  (defun linum-fringe-toggle ()
    "Toggles the line numbers as well as the fringe."    (interactive)
    (cond (linum-mode (fringe-mode '(0 . 0))
                      (linum-mode -1))
          (t          (fringe-mode '(8 . 0))
                      (linum-mode 1))))

  :bind (("A-C-k"   . linum-mode)
         ("s-C-k"   . linum-mode)
         ("A-C-M-k" . linum-fringe-toggle)
         ("s-C-M-k" . linum-fringe-toggle)))

Note: make the line numbers a fixed size, then increasing or decreasing the font size doesn’t truncate the numbers.

The linum-relative mode allows one to see the destination line as a relative distance (like one 9 lines lower), and then C-9 C-n can quickly pop to it.

(use-package linum-relative
  :ensure t
  :config
  (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 Linux

Better Bookmarks

For me, bookmarks serve two functions. First, as a way to jump back to interesting places by name (and annotate those places), and second, as form of bread crumbs while I’m toiling around a large codebase.

For normal bookmarks, I’d rather use Helm:

(use-package bookmark
  :init (setq bookmark-save-flag 1)
  :config
  (defun ha/add-bookmark (name)
    (interactive
     (list (let* ((filename  (file-name-base (buffer-file-name)))
                  (project   (projectile-project-name))
                  (func-name (which-function))
                  (initial   (format "%s::%s:%s " project filename func-name)))
             (read-string "Bookmark: " initial))))
    (bookmark-set name))
  :bind  (("C-c b m" . ha/add-bookmark)
          ("C-x r m" . ha/add-bookmark)
          ("C-x r b" . helm-bookmarks)))

Visual Bookmarks… Breadcrumbs

For dropping visual breadcrumbs throughout a single file or multiple files, use my better-breadcrumbs mode.

; (require 'better-breadcrumbs)
(load-library "better-breadcrumbs")
(better-breadcrumbs-mode +1)

Smart Comments

The smart-comment project has the nice feature of commenting a line without being at the beginning of the line (default comment in the middle of the line is to split it).

(use-package smart-comment
  :bind ("M-;" . smart-comment))

Also has the ability (with the C-u prefix) to mark comments as things to be deleted.

Smart Scan

Use 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))

Strip Whitespace on Save

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)

Save File Position

Save the point position for every file, and restore it when that file is reloaded.

(use-package saveplace
   :init
   (setq-default save-place t)
   (setq save-place-forget-unreadable-files t
         save-place-skip-check-regexp "\\`/\\(?:cdrom\\|floppy\\|mnt\\|/[0-9]\\|\\(?:[^@/:]*@\\)?[^@/:]*[^@/:.]:\\)"))

Better Searching and Visual Regular Expressions

Searching is quite good in Emacs. Let’s add a few extra keys:

(bind-keys :map isearch-mode-map
           ("<left>"  . isearch-repeat-backward)
           ("<right>" . isearch-repeat-forward)
           ("<up>"    . isearch-ring-retreat)
           ("<down>"  . isearch-ring-advance))

Easier replacement of my Smart Scan for searching forward/backward for the current word. This is now bound to M-s . (in Emacs 24.4), but I then have to hit C-s or C-r … nicer to use the period/comma.

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

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)))

Hungry Delete

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?

Table and Column Alignment

While I shouldn’t, I like to line up comma-separated columns (and colon-delimited hashes), and since I can never type the regular expression on the first time, I wrapped it up in a callable function.

(defun align-comma (start end c)
  "Repeat alignment with a character padded with spaces for
comma-separated columns."
  (interactive "r\nsAlign character: ")
  (align-regexp start end
                (concat c "\\(\\s-*\\)") 1 1 t))

Programming Languages

General Language Support

Many programming language environments can benefit from this section.

ElDoc

I like ElDoc support (when I can get it), but not needed in the mode line:

(use-package eldoc
  :diminish eldoc-mode
  :init  (setq eldoc-idle-delay 0.1))

Tag Support

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 ctags

On Ubuntu Linux, do:

sudo apt-get install -y exuberant-ctags

Note: 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/

--exclude=vendor
--exclude=.git

Also, add various directories and filenames that should be ignored. We access stuff by loading the etags package:

(use-package etags
   :init (setq tags-revert-without-query 1))

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))

If I don’t know what I’m after, Helm is better:

(use-package helm
  :bind (("C-c M-i" . helm-imenu)))

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.

Note: This prompt needs to go away:

(setq tags-add-tables nil)

What if the marker stack is empty? M-, returns an error. Let’s do a DWIM function:

(defun ha/xref-pop-marker-stack (arg)
  "Pops the marker stack, unless I haven't searched a tag/xref
with `M-.' and there is nothing to return to, in which case, let's
jump back to the last change."
  (interactive "P")
  (condition-case nil
      (xref-pop-marker-stack)
    (error
     (goto-last-change arg))))

(bind-key "M-," 'ha/xref-pop-marker-stack)

Code Block Folding

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.

Aggressive Auto Indention

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))

Red Warnings

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)))))

XML

Can’t believe we are still dealing with this awful data format.

(setq nxml-slash-auto-complete-flag t)

Remember a couple of bindings:

C-c C-i
Type <p and then this, to have the other tag inserted and the cursor in the middle.
C-c C-f
Finish any opened tag that needs to be completed.
C-c C-s C-a
After putting in the schema, use this to refresh it

Shell Scripts

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))

Emacs Lisp

See emacs-elisp for details of my setup for programming Emacs Lisp.

(require 'init-elisp)

Clojure

See emacs-clojure.el for details on working with Clojure. Not sure if I should just load it directly, like:

(require 'init-clojure)

Java

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)

Ruby

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)

Python

See emacs-python.el for details on working with Python. Not sure if I should just load it directly, like:

(require 'init-python)

JavaScript

See emacs-javascript.el for details on working with JavaScript.

(require 'init-javascript)

HTML, CSS and other Web Programming

See emacs-web.el for details on working with HTML and its ilk.

(require 'init-web)

Org-Mode

See emacs-org-mode.el for details on my Org-Mode settings.

(require 'init-org-mode)

Tools

Git

I like git-gutter-fringe:

(use-package git-gutter-fringe
   :ensure t
   :diminish git-gutter-mode
   :init (setq git-gutter-fr:side 'right-fringe)
   :config (global-git-gutter-mode t))

I want to have special mode for Git’s configuration file:

(use-package gitconfig-mode
  :ensure t)

(use-package gitignore-mode
  :ensure t)

Finally, I want to play with Git Time Machine project for stepping backward through the version history of a file:

(use-package git-timemachine)

Magit

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.

Note: Use the smerge-mode that is now part of Emacs.

Markdown

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))

PlantUML and Graphviz

Install the Graphviz and PlantUML projects using Homebrew:

brew install graphviz
brew link graphviz
brew install plantuml

Load the mode for PlantUML and reference its jar:

(let ((plantuml-jar (car (file-expand-wildcards "/usr/local/Cellar/plantuml/*/plantuml*.jar"))))
  (ignore-errors
    (use-package plantuml-mode
      :if plantuml-jar
      :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)

Applications

Web Browsing

This section became involved, and has moved on to emacs-browser file.

(require 'init-browser)

EShell

See emacs-eshell.el for details of configuring and using EShell.

(require 'init-eshell)

Chatting

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))

Frivolous

Life must be more whimsical. To begin, install the fortune package:

brew install fortune   # Installs in /usr/local/share/games/fortunes

Or, if on Ubuntu:

sudo apt-get install fortune # Installs in /usr/share/games/fortunes

Let’s create a variable for knowing if we have everything installed:

(defvar ha/can-haz-cookie-p nil "Is true if the fortune system has been correctly configured")

Since fortune installs quite a few files (some of which we don’t like), and we may want to run the same code on multiple operating systems, we write a little wrapper function around the cookie function to pick one of our favorite files (if available) at random:

(defun ha/cookie ()
  "Returns a phrase from a random `fortune' file from standard locations."
  (interactive)
  (condition-case nil
      (let* ((favs "computers$\\|definitions$\\|drugs$\\|fortunes$\\|goedel$\\|linuxcookie$\\|magic$")
             (paths '("/usr/share/games/fortunes" "/usr/local/share/games/fortunes"))
             (path (car (-filter 'file-exists-p paths)))
             (files (directory-files path t favs))
             (file (nth (random (length files)) files)))
        (setq ha/can-haz-cookie-p t)
        (message "%s" (cookie file)))
    (error (message "Happy Hacking!"))))

And let’s display a frivolous message each time we return to Emacs:

(when ha/can-haz-cookie-p
  (add-hook 'focus-in-hook 'ha/cookie))

Technical Artifacts

Setting up the Exec Path

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))))

Configure the Graphical Settings

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))

Load up the Local Configuration

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)

Finally, let’s get happy:

(defun display-startup-echo-area-message ()
  (ha/cookie))

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