Xiaojie's Emacs Init File Written in Org-Mode

Table of Contents

1 About

1.1 My Emacs Init File

This is my Emacs configuration file. Inspired by Sacha and Bernt, l written it using Org-babel in literate style, to make it easy to explain.

Author: Xiaojie Feng
Keywords: emacs, dotfile, config

   ___ _ __ ___   __ _  ___ ___
  / _ \ '_ ` _ \ / _` |/ __/ __|
 |  __/ | | | | | (_| | (__\__ \
(_)___|_| |_| |_|\__,_|\___|___/

1.2 License

Copyright (C) 2016-2019 Xiaojie Feng

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

Code in this document is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

1.3 Commentary

Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish. [Neal Stephenson]

People talk about getting used to a new editor, but over time, it is precisely the opposite that should happen — the editor should get used to us. [Vivek Haldar]

"Show me your ~/.emacs and I will tell you who you are." [Bogdan Maryniuk]

"Emacs is like a laser guided missile. It only has to be slightly mis-configured to ruin your whole day." [Sean McGrath]

"While any text editor can save your files, only Emacs can save your soul." [Per Abrahamsen]

1.4 What I Want to Say

If you are not familiar with the Emacs basic usage, I highly recommend you to read some sophisticated Emacs users' Emacs guide, such as Tuhdo's Emacs Mini Manual (PART 1) - THE BASICS and Xah Lee's Practical Emacs Tutorial, instead of the official Emacs Manual. The official manual is too long to read for Emacs newbies.

You can always use my init file as long as you master the basic usage of Emacs, even though my configuration contains something a little advanced to Emacs (and Emacs Lisp) beginners. As I think, the init file was well organized in structural and code blocks (setq, defun, package configuration etc.) were written together with plenty explanation about why and how to use, thanks to the Literate programming paradigm introduced by Donald Knuth.

1.5 How to Use This Document

This document is available as an org file which you can load in Emacs and tangle with M-x org-babel-tangle (C-c C-v C-t) which will create org-mode.el in the same directory as the org-mode.org file. This will extract all of the elisp examples in this document into a file you can include in your $HOME/.emacs.d/init.el file.

If you're new to Emacs Lisp, you probably don't want to copy and paste large chunks of this code. Instead, copy small parts of it (always making sure to copy a complete set of parentheses) into your *scratch* buffer or some other buffer in emacs-lisp-mode. Use M-x eval-buffer to evaluate the code and see if you like the way that Emacs behaves.

If you're viewing the Org file, you can open source code blocks (those are the ones in begin_src) in a separate buffer by moving your point inside them and typing C-c ' (M-x org-edit-special). This opens another buffer in emacs-lisp-mode, so you can use M-x eval-buffer to load the changes.

2 Personal Information

Emacs will normally pick this up automatically, but this way I can be sure the right information is always present.

Note: You should modify this with your personal info.

(setq user-full-name "Xiaojie Feng"
      user-mail-address "fengxiaojie1997@gmail.com")

3 Bootstraping

3.1 Load Multiple Emacs Files

3.1.1 My Directory Location

Conventionally, additional per-user Emacs-specific files are placed in .emacs.d directory in the HOME directory. The variable user-emacs-directory holds that.

Structure the .emacs.d directory by putting some sub-directories in it. Following functions make it easy to get the location (whole path) of these sub-directories (and files):

(defun user-emacs-subdirectory (d) (expand-file-name d user-emacs-directory))
(defun user-emacs-file (f) (expand-file-name f user-emacs-directory))

But, in practice, I didn't use them.

3.1.2 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.

(defconst user-emacs-savefile-dir
  (expand-file-name "savefile" user-emacs-directory)
  "This folder stores all the history and cache files")
(defconst user-emacs-backup-dir
  (expand-file-name "backup" user-emacs-directory)
  "This folder stores all the backup~ files")
(defconst user-emacs-autosave-dir
  (expand-file-name "autosave" user-emacs-directory)
  "This folder stores all the #autosave# files")

(unless (file-exists-p user-emacs-savefile-dir)
  (make-directory user-emacs-savefile-dir))
(unless (file-exists-p user-emacs-backup-dir)
  (make-directory user-emacs-backup-dir))
(unless (file-exists-p user-emacs-autosave-dir)
  (make-directory user-emacs-autosave-dir))

3.1.3 Customization

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" user-emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file))

3.1.4 Setting up the Load Path

Extra packages not available via the package manager go in my personal stash at $HOME/.emacs.d/elisp. As long as they're in a directory in my load-path, Emacs can find them.

(add-to-list 'load-path (expand-file-name "elisp" user-emacs-directory))

3.2 Package Management

3.2.1 ELPA - Packages Installer and Manager

Emacs has become like every other operating system, and now has a package manager called Emacs Lisp Package Archive (ELPA) with its own collection repository. This provides a nice way to install additional packages. But since it is so conservative, we need to add more package source.

(require 'package)
(setq package-archives
      '(("gnu" . "https://elpa.gnu.org/packages/")
        ("org" . "http://orgmode.org/elpa/")
        ("melpa" . "https://melpa.org/packages/")))

(package-initialize)
(setq package-enable-at-startup nil)

Note: As a Chinese user, I use the following mirrors instead. If you want to use my emacs init file directly, you should change the value yes and no after variable :tangle in the org file, then tangle the org file with org-babel to use the official package source.

(require 'package)
(setq package-archives
      '(("gnu"   . "http://elpa.emacs-china.org/gnu/")
        ("melpa" . "http://elpa.emacs-china.org/melpa/")
        ("org"   . "http://elpa.emacs-china.org/org/")))

(package-initialize)
(setq package-enable-at-startup nil)

3.2.2 Use-Package - Package Configuration Macro

This use-package macro provides more concise ways to setup package autoloads, keybindings, and various mode configuration. The focus is on decreasing startup time by autoloading packages instead loading them on startup.

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(setq use-package-verbose t
      use-package-always-ensure t)

(eval-when-compile
  (require 'use-package))

;; Please don't load outdated byte code
(setq load-prefer-newer t)

3.3 Library Dependency

Load up a collection of enhancements to Emacs Lisp, to make the configuration and its dependencies work properly.

(require 'cl)                           ; provides useful things like `loop' and `setf'
(use-package dash :ensure t)            ; a modern list library
(use-package diminish :ensure t)        ; diminish keeps the modeline tidy

Benefiting from org-mode's contrib:

(use-package org
  :ensure org-plus-contrib)

3.4 Environment fixup

On macOS, Emacs doesn't use the shell PATH if it's not started from the shell (started from the GUI). Exec-path-from-shell can fix this problem. It ensure environment variables inside Emacs look the same as in the user's shell.

(use-package exec-path-from-shell
  :defer t
  :init
  (progn
    (when (memq window-system '(mac ns))
      (exec-path-from-shell-initialize))))

3.5 Keyboard settings for macOS users

Set keys for Apple keyboard, for Emacs in macOS.

(when (eq system-type 'darwin)

  (setq mac-command-modifier 'meta)     ; make command key do Meta
  (setq mac-option-modifier 'super)     ; make option key do Super
  (setq mac-control-modifier 'control)  ; make control key do Control
  (setq ns-function-modifier 'hyper)    ; make fn key do Hyper
  )

4 Basic settings used everywhere

4.1 General user interface and appearance

4.1.1 Startup screen and errors reporting

Turn off the startup messages when entering Emacs.

(setq inhibit-startup-screen t)         ; disable startup screen

No beep when reporting errors.

(setq ring-bell-function (lambda ()))   ; disable the annoying bell ring

4.1.2 Set frames title with buffer name

Use project and buffer name as frame title.

(setq frame-title-format
      (list "[" '(:eval (projectile-project-name)) "]"
            " ψωETωψ ◎ %b"))

4.1.3 Distinguish buffers with the same file name

Make two buffers with the same file name distinguishable.

(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)
(setq uniquify-separator "/")
(setq uniquify-after-kill-buffer-p t)    ; rename after killing uniquified
(setq uniquify-ignore-buffers-re "^\\*") ; don't muck with special buffers

4.1.4 Menu bar, tool bar, scroll bar. No!

Get rid of the menu bar, tool bar, and scroll bar. Useless!

(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)

4.1.5 Modeline settings

Show current line and cursor column position.

(line-number-mode 1)
(column-number-mode 1)

Indicate current file size.

(size-indication-mode t)

Display the time.

(display-time-mode 1)

4.1.6 Put empty line markers into the left hand side

Put empty line markers into the left hand side to see when a file actually ends.

(setq-default indicate-empty-lines t)
(when (not indicate-empty-lines)
  (toggle-indicate-empty-lines))

4.1.7 Display Line Numbers

According to this article, Emacs now has two line numbers mode: linum-mode and display-line-numbers-mode (Emacs 26). Use the new one :)

(global-display-line-numbers-mode)

With the display-line-numbers-mode turned on, we can use M-g g (goto-line) to go to the desired line quickly.

4.1.8 Cursor and mouse

The blinking cursor is nothing, but an annoyance.

(blink-cursor-mode -1)                  ; No blinking cursor

Hide the mouse while typing.

(setq make-pointer-invisible t)         ; Hide the mouse while typing

4.1.9 Sweet highlight

Hightlight something sweet.

(global-font-lock-mode 1)               ; Syntax highlight
(global-hl-line-mode 1)                 ; Highlight cursor line

Show-paren-mode highlights the matching parenthesis on point.

(show-paren-mode 1)                      ; Highlight parenthesis pairs
(setq show-paren-delay 0)                ; No delay
(setq blink-matching-paren-distance nil) ; Blinking parenthesis
(setq show-paren-style 'expression)      ; Highlight text between parenthesis

Volatile-highlights highlight things like undo, copy, paste, etc.

(use-package volatile-highlights
  :diminish ""
  :init (volatile-highlights-mode))

Hl-todo highlight annotations like TODO in source code.

(use-package hl-todo
  :diminish ""
  :init (global-hl-todo-mode))

4.1.10 Nice scrolling

Scroll the screen in a better way.

(setq scroll-margin 0
      scroll-conservatively 100000
      scroll-preserve-screen-position 1)

4.1.11 No #autosave# and backup~ files

Stop creating backup~ files and #autosave# files.

(setq auto-save-default nil)            ; No #autosave# files
(setq make-backup-files nil)            ; No backup~ files

Emacs backup is so annoying. This will completely stop Emacs from creating temoporary symbolic link files (lock file) named #.something:

(setq create-lockfiles nil)             ; No #.something symbolic link files

Warning: Disable lock file may be a problem if you have situations where a file is being edited by different people or instances of Emacs. (So I didn't disable it.)

4.1.12 Change "yes or no" to "y or n"

(defalias 'yes-or-no-p 'y-or-n-p)       ; y/n instead of yes/no

4.1.13 Confirm before quit

Confirmation is required before exiting Emacs.

(setq confirm-kill-emacs 'yes-or-no-p)

4.2 Commands - useful interactive functions

4.2.1 Reload emacs initialization file

Here are the commands I used frequently when I writing this Emacs configuration in org-mode:

(defun my/reload-emacs-init-el-file ()
  (interactive)
  (load-file (expand-file-name "init.el" user-emacs-directory)))
(defun my/reload-emacs-init-org-file ()
  (interactive)
  (org-babel-load-file (expand-file-name "init.org" user-emacs-directory)))

4.2.2 macOS swap Meta and Super

Swap the mapping of Meta and Super if necessary (taken form Prelude project):

(defun prelude-swap-meta-and-super ()
  "Swap the mapping of Meta and Super.
Very useful for people using their Mac with a
Windows external keyboard from time to time."
  (interactive)
  (if (eq mac-command-modifier 'super)
      (progn
        (setq mac-command-modifier 'meta)
        (setq mac-option-modifier 'super)
        (message "Command is now bound to META and Option is bound to SUPER."))
    (setq mac-command-modifier 'super)
    (setq mac-option-modifier 'meta)
    (message "Command is now bound to SUPER and Option is bound to META.")))

4.2.3 Copy filename to clipboard

Sometimes I need to copy the name of the currently visited file to the clipboard. Emacs does not have a built-in command for that, but cooking one is pretty straightforward:

(defun copy-file-name-to-clipboard ()
  "Copy the current buffer file name to the clipboard."
  (interactive)
  (let ((filename (if (equal major-mode 'dired-mode)
                      default-directory
                    (buffer-file-name))))
    (when filename
      (kill-new filename)
      (message "Copied buffer file name '%s' to the clipboard." filename))))

4.2.4 Open File in external app

Here's a command to open the current file or marked dired files in external app. (as if you double-clicked the file on desktop) It's useful for image files, PDF file, video, audio files.

(defun xah-open-in-external-app (&optional @fname)
  "Open the current file or dired marked files in external app.
The app is chosen from your OS's preference.

When called in emacs lisp, if @fname is given, open that.

URL `http://ergoemacs.org/emacs/emacs_dired_open_file_in_ext_apps.html'
Version 2019-01-18"
  (interactive)
  (let* (
         ($file-list
          (if @fname
              (progn (list @fname))
            (if (string-equal major-mode "dired-mode")
                (dired-get-marked-files)
              (list (buffer-file-name)))))
         ($do-it-p (if (<= (length $file-list) 5)
                       t
                     (y-or-n-p "Open more than 5 files? "))))
    (when $do-it-p
      (cond
       ((string-equal system-type "windows-nt")
        (mapc
         (lambda ($fpath)
           (w32-shell-execute "open" (replace-regexp-in-string "/" "\\" $fpath t t))) $file-list))
       ((string-equal system-type "darwin")
        (mapc
         (lambda ($fpath)
           (shell-command
            (concat "open " (shell-quote-argument $fpath))))  $file-list))
       ((string-equal system-type "gnu/linux")
        (mapc
         (lambda ($fpath) (let ((process-connection-type nil))
                            (start-process "" nil "xdg-open" $fpath))) $file-list))))))

4.3 Navigation

4.3.1 Projectile - project interaction

The Projectile project is a nifty way to run commands and search for files in a particular "project". All projectile keybindings is placed under C-x p. And, the most used commands by myself are C-x p p to switch-project, and C-x p f to find a file and C-x p d to find a directory.

(use-package projectile
  :ensure t
  :diminish projectile-mode
  :init (projectile-global-mode)
  ;; caching can speedup file and directory listings
  ;;(setq projectile-enable-caching t)
  ;; projectile cache file location
  (setq projectile-cache-file (expand-file-name  "projectile.cache" user-emacs-savefile-dir))
  :commands projectile-ag
  :config
  (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
  (define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map)

  (setq projectile-completion-system 'helm ; `ido' or `helm' interface?
        projectile-switch-project-action 'projectile-commander
        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 ?c
    "Run `compile' in the project."
    (projectile-compile-project nil))

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

Much of the section came from this essay. Read it for config details and usage.

4.3.2 Dired - directory editor

Install GNU Coreutils (and add it to your $PATH correctly) to make sure "ls" program support "–dired" option:

brew install coreutils
(setq ls-lisp-use-insert-directory-program nil)

Alternatively, you might want to use Emacs’s own emulation of "ls", by using:

(setq ls-lisp-use-insert-directory-program t)

After dired-x mode turned on. C-x C-j (dired-jump) can jump to the directory of any current buffer:

(use-package direx
  :ensure t
  :bind (("C-x j" . direx:jump-to-directory)
         ("C-x C-j" . direx:jump-to-directory)))

Tell dired to stop asking me whether I want to recursively delete or copy, since I never respond to that question with no.

(setq dired-recursive-copies 'always)   ; recursive copies without asking
(setq dired-recursive-deletes 'top)     ; recursive deletes with asked only once

Enables Do What I Mean mode for dired: If I'm in a split frame with two dired buffers, the default target to copy (and rename) will be the other window.

(setq dired-dwim-target t)

This dired-hide-details-mode enhancement to dired hides the ugly details (owner permission info) until you hit ( and shows the details with ) hit again. I want it always on:

(add-hook 'dired-mode-hook (lambda () (dired-hide-details-mode 1)))
(setq dired-listing-switches "-alh")    ; human readable format

I also want dired to automatically revert, but to be quiet about it. The first line actually enables auto-revert for any buffers.

(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)

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

4.3.3 Neotree - tree layout directory explorer

The NeoTree project provides a nice tree layout file explorer, and it gives an ide-like look and feel.

(use-package neotree
  :ensure t
  :defer t
  :bind ("C-x t n" . neotree-toggle)
  :config
  (setq projectile-switch-project-action 'neotree-projectile-action)
  (setq neo-show-hidden-files t)
  (setq neo-smart-open t))

(global-set-key [f2] 'neotree-toggle)
(global-set-key [f8] 'neotree-toggle)

4.3.4 Winner - undo and redo window configuration

Change window configuration and then return to the old configuration with winner-mode. Use Control-C Arrow keys to cycle through window/frame configurations. This is handy when something has popped up a buffer that you want to look at briefly before returning to whatever you were working on. When you're done, press C-c <left>.

(winner-mode 1)

4.3.5 Windmove - switch between windows

Windmove lets you move between windows with something more natural than cycling through C-x o (other-window).

(use-package windmove
  :bind
  (("s-<right>" . windmove-right)
   ("s-<left>" . windmove-left)
   ("s-<up>" . windmove-up)
   ("s-<down>" . windmove-down)))

4.3.6 Buffer-move - move or swap buffer to other window

Buffer-move is similar to windmove, but more powerful. It can swap the buffers.

(use-package buffer-move
  :config
  (global-set-key (kbd "<C-s-up>")     'buf-move-up)
  (global-set-key (kbd "<C-s-down>")   'buf-move-down)
  (global-set-key (kbd "<C-s-left>")   'buf-move-left)
  (global-set-key (kbd "<C-s-right>")  'buf-move-right))

4.3.7 C-x o : Moving to another window

A visual replacement for C-x o.

(use-package switch-window
  :bind (("C-x o" . switch-window)))

4.3.8 C-x k : Quick kill buffer

I rarely want to kill any buffer but the one I'm looking at. And, I usually want to kill buffers even if modified unconditionally. Code from this discussion.

(defun kill-this-buffer-volatile ()
    "Kill current buffer unconditionally, even if it has been modified."
    (interactive)
    (set-buffer-modified-p nil)
    (kill-this-buffer))

;; Kill the current visible buffer without confirmation unless the buffer has
;; been modified. In this last case, you have to answer y/n.
(global-set-key (kbd "C-x k") 'kill-this-buffer)
;; Unconditionally kill unmodified buffers.
(global-set-key (kbd "C-x K") 'kill-this-buffer-volatile)

4.3.9 Avy - fast cursor movement

Avy enable fast cursor movement to visible text using a char-based decision tree. Avy is fast!

(use-package avy
  :ensure t
  :init (setq avy-background t)
  :config
  (global-set-key (kbd "s-;") 'avy-goto-line)
  (global-set-key (kbd "s-l") 'avy-goto-line)
  (global-set-key (kbd "s-.") 'avy-goto-char-timer)
  (global-set-key (kbd "s-j") 'avy-goto-char-timer)
  (global-set-key (kbd "s-,") 'avy-pop-mark)
  (global-set-key (kbd "s-J") 'avy-pop-mark))

4.3.10 C-a : Smart beginning of line

This Emacs Redux article has a great suggestion for having C-a go to the beginning of the line's content instead of the actual beginning of the line. Hit C-a a second to get to the actual beginning.

(defun my/smarter-move-beginning-of-line (arg)
  "Move point back to indentation of beginning of line.

Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.

If ARG is not nil or 1, move forward ARG - 1 lines first.  If
point reaches the beginning or end of the buffer, stop there."
  (interactive "^p")
  (setq arg (or arg 1))

  ;; Move lines first
  (when (/= arg 1)
    (let ((line-move-visual nil))
      (forward-line (1- arg))))

  (let ((orig-point (point)))
    (back-to-indentation)
    (when (= orig-point (point))
      (move-beginning-of-line 1))))

;; remap C-a to `smarter-move-beginning-of-line'
(global-set-key [remap move-beginning-of-line]
                'my/smarter-move-beginning-of-line)

4.3.11 C-w and M-w : Smart cut and copy

Here's a function to cut current line if there's no text selection:

(defun xah-cut-line-or-region ()
  "Cut current line, or text selection.
When `universal-argument' is called first, cut whole buffer (respects `narrow-to-region').

URL `http://ergoemacs.org/emacs/emacs_copy_cut_current_line.html'
Version 2015-06-10"
  (interactive)
  (if current-prefix-arg
      (progn ; not using kill-region because we don't want to include previous kill
        (kill-new (buffer-string))
        (delete-region (point-min) (point-max)))
    (progn (if (use-region-p)
               (kill-region (region-beginning) (region-end) t)
             (kill-region (line-beginning-position) (line-beginning-position 2))))))

(global-set-key (kbd "C-w") 'xah-cut-line-or-region)

Here is a function copy current line if there is no text selection. If called again, it'll append-copy next line. So you can press a key repeatedly to keep copying lines.

(defun xah-copy-line-or-region ()
  "Copy current line, or text selection.
When called repeatedly, append copy subsequent lines.
When `universal-argument' is called first, copy whole buffer (respects `narrow-to-region').

URL `http://ergoemacs.org/emacs/emacs_copy_cut_current_line.html'
Version 2018-09-10"
  (interactive)
  (if current-prefix-arg
      (progn
        (copy-region-as-kill (point-min) (point-max)))
    (if (use-region-p)
        (progn
          (copy-region-as-kill (region-beginning) (region-end)))
      (if (eq last-command this-command)
          (if (eobp)
              (progn )
            (progn
              (kill-append "\n" nil)
              (kill-append
               (buffer-substring-no-properties (line-beginning-position) (line-end-position))
               nil)
              (progn
                (end-of-line)
                (forward-char))))
        (if (eobp)
            (if (eq (char-before) 10 )
                (progn )
              (progn
                (copy-region-as-kill (line-beginning-position) (line-end-position))
                (end-of-line)))
          (progn
            (copy-region-as-kill (line-beginning-position) (line-end-position))
            (end-of-line)
            (forward-char)))))))

(global-set-key (kbd "M-w") 'xah-copy-line-or-region)
(global-set-key (kbd "M-[") 'xah-copy-line-or-region)

4.3.12 M-\ : Smart delete whitespaces around cursor

Here's a function combine most of the deleting whitespaces around cursor command into single one:

(defun xah-delete-blank-lines ()
  "Delete all newline around cursor.

URL `http://ergoemacs.org/emacs/emacs_shrink_whitespace.html'
Version 2018-04-02"
  (interactive)
  (let ($p3 $p4)
          (skip-chars-backward "\n")
          (setq $p3 (point))
          (skip-chars-forward "\n")
          (setq $p4 (point))
          (delete-region $p3 $p4)))

(defun xah-shrink-whitespaces ()
  "Remove whitespaces around cursor to just one, or none.

Shrink any neighboring space tab newline characters to 1 or none.
If cursor neighbor has space/tab, toggle between 1 or 0 space.
If cursor neighbor are newline, shrink them to just 1.
If already has just 1 whitespace, delete it.

URL `http://ergoemacs.org/emacs/emacs_shrink_whitespace.html'
Version 2018-04-02T14:38:04-07:00"
  (interactive)
  (let* (
         ($eol-count 0)
         ($p0 (point))
         $p1 ; whitespace begin
         $p2 ; whitespace end
         ($charBefore (char-before))
         ($charAfter (char-after ))
         ($space-neighbor-p (or (eq $charBefore 32) (eq $charBefore 9) (eq $charAfter 32) (eq $charAfter 9)))
         $just-1-space-p
         )
    (skip-chars-backward " \n\t")
    (setq $p1 (point))
    (goto-char $p0)
    (skip-chars-forward " \n\t")
    (setq $p2 (point))
    (goto-char $p1)
    (while (search-forward "\n" $p2 t )
      (setq $eol-count (1+ $eol-count)))
    (setq $just-1-space-p (eq (- $p2 $p1) 1))
    (goto-char $p0)
    (cond
     ((eq $eol-count 0)
      (if $just-1-space-p
          (delete-horizontal-space)
        (progn (delete-horizontal-space)
               (insert " "))))
     ((eq $eol-count 1)
      (if $space-neighbor-p
          (delete-horizontal-space)
        (progn (xah-delete-blank-lines) (insert " "))))
     ((eq $eol-count 2)
      (if $space-neighbor-p
          (delete-horizontal-space)
        (progn
          (xah-delete-blank-lines)
          (insert "\n"))))
     ((> $eol-count 2)
      (if $space-neighbor-p
          (delete-horizontal-space)
        (progn
          (goto-char $p2)
          (search-backward "\n" )
          (delete-region $p1 (point))
          (insert "\n"))))
     (t (progn
          (message "nothing done. logic error 40873. shouldn't reach here" ))))))

(global-set-key (kbd "M-\\") 'xah-shrink-whitespaces)

4.3.13 C-k : Smart kill entire lines

According to this article, killing arbitrary number of lines of text can be done with two keystrokes.

This creates a macro that moves to the beginning of the line and then calls a function given to it. Quite an interesting approach:

(defmacro bol-with-prefix (function)
  "Define a new function which calls FUNCTION.
Except it moves to beginning of line before calling FUNCTION when
called with a prefix argument. The FUNCTION still receives the
prefix argument."
  (let ((name (intern (format "endless/%s-BOL" function))))
    `(progn
       (defun ,name (p)
         ,(format
           "Call `%s', but move to the beginning of the line when called with a prefix argument."
           function)
         (interactive "P")
         (when p
           (forward-line 0))
         (call-interactively ',function))
       ',name)))

And we re-bind them to functions that use them.

(global-set-key [remap paredit-kill] (bol-with-prefix paredit-kill))
(global-set-key [remap sp-kill-hybrid-sexp] (bol-with-prefix sp-kill-hybrid-sexp))
(global-set-key [remap org-kill-line] (bol-with-prefix org-kill-line))
(global-set-key [remap kill-line] (bol-with-prefix kill-line))

(global-set-key (kbd "C-k") (bol-with-prefix kill-line))

With this little macro, C-k still kills from point, but C-7 C-k swallows seven whole line. As a bonus, we get the kill-whole-line (C-S-backspace) behavior by doing C-1 C-k (I prefer M-1 to C-1 for easy stroke.).

4.3.14 Expand-region - Smart region selection

Wherever you are in a file, and whatever the type of file, you can slowly increase a region selection by logical segments by using the 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 original expand-region (Taken from here):

(use-package expand-region  
  :ensure t
  :defer 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)     ; wrapped version of `er/expand-region'
         ("C--" . er/contract-region)))

I chose C-0 instead of C-1 as the prefix argument to select the current line, because It's easier to stroke C-0 than C-1.

Give it a try, and you will know what it will do quickly.

4.3.15 Fancy-narrow - Smart narrowing

Narrowing is one of the fascinating features in Emacs, great for code-reviews and other presentations. It works well but still can be better.

This nifty function is a nice replacement for many other narrowing keybindings that I use, it is smart (do what I mean):

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

Unlike narrow-to-region, which completely hides text outside the narrowed region, this fancy-narrow package simply deemphasizes the text, makes it readonly, and makes it unreachable (hightlight instead of narrow). With this fancy-narrow package, we can define a smart hightlight-section function with prefix argument:

(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-x n ." . ha/highlight-section)))
  ;; :bind (("C-M-+" . ha/highlight-section)
  ;;        ("C-<f12>" . ha/highlight-section)))

;; (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, call `fancy-narrow-to-defun', to highlight
;;      current function. If NUM is 1, highlight the current
;;      block (delimited by blank lines). Otherwise, highlight that
;;      number of lines."
;;     (interactive "p")
;;     (cond
;;      ((fancy-narrow-active-p)  (fancy-widen))
;;      ((region-active-p)        (fancy-narrow-to-region (region-beginning) (region-end)))
;;      ((= num 0)                (fancy-narrow-to-defun))
;;      ((= num 1)                (ha/highlight-block))
;;      (t                        (progn (er/expand-region num) ; depend on `er/expand-region'
;;                                       (fancy-narrow-to-region (region-beginning) (region-end))
;;                                       (setq mark-active nil)))))

;;   ;; highlight-section is smart enough
;;   :bind (("C-x n ." . ha/highlight-section)))

4.3.16 M-; : Smart Comment

The smart-comment project has the nice feature of commenting a line without being at the beginning of the line M-; (default comment in the middle of the line is to split it). Also has the ability (with the C-u prefix) to mark comments as things to be deleted.

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

4.3.17 M-p and M-n : 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))

4.3.18 Better Searching and Visual Regular Expressions

The anzu package enhances query-replace and query-replace-regexp by showing total matches and current match position.

(use-package anzu
  :diminish anzu-mode
  :bind (("M-%" . anzu-query-replace)
         ("C-M-%" . anzu-query-replace-regexp))
  :config (global-anzu-mode))

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 RET to accept it.

(use-package visual-regexp
  :ensure t
  :init
  ;; use modern regular expressions instead of Emacs-style regular expressions
  (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)))

4.4 Editing

4.4.1 Coding - always UTF-8

Always, always UTF-8.

(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(setq default-buffer-file-coding-system 'utf-8)

4.4.2 Tabs, spaces, enters

  • Tabs vs. Space

    Tabs are evil! I want spaces instead of tabs, and want exactly 2 spaces instead of a tab. Note to self: Apparently emacs is smart enough to not do this in Python, which is a good thing.

    ;; make indentation commands use space only (never tab character)
    (setq-default indent-tabs-mode nil)
    
    ;; set current buffer's tab char's display width to 2 spaces
    (setq tab-width 2)
    
  • Indent or Complete - smart tab behavior
    (setq-default tab-always-indent 'complete)
    
  • Sentences end with one space
    (setq sentence-end-double-space nil)    ; Sentences end with one space
    
  • Bind ENT to newline-and-indent

    By default, Emacs won't indent when press RET because the command bound to RET is newline. You can enable automatic indentation by binding RET to newline-and-indent.

    (global-set-key (kbd "RET") 'newline-and-indent)
    
  • Always newline at end of file
    (setq require-final-newline t)
    

4.4.3 Delete the selection with a keypress

(delete-selection-mode t)

4.4.4 Whitespace - show and clean devil

We want to show trailing whitespace. Trailing whitespace is the devil.

(use-package whitespace
  :bind ("C-c x w" . whitespace-mode)
  :diminish whitespace-mode
  :init
  ;; Indicate trailing empty lines in the GUI
  (set-default 'indicate-empty-lines t)
  (setq show-trailing-whitespace t)

  ;; limit line length                                                                    
  (setq whitespace-line-column 80)
  ;; Here are the things that whitespace-mode should highlight
  (setq whitespace-style '(face tabs empty trailing lines-tail))

  ;; Display pretty things for newlines and tabs (nothing for spaces)
  (setq whitespace-display-mappings
        ;; all numbers are Unicode codepoint in decimal. e.g. (insert-char 182 1)
        ;; 32 SPACE, 183 MIDDLE DOT
        '((space-mark nil)
          ;; 10 LINE FEED
          ;;(newline-mark 10 [172 10])
          (newline-mark nil)
          ;; 9 TAB, MIDDLE DOT
          (tab-mark 9 [183 9] [92 9])))

  ;; Disable it in certain modes where whitespace doesn't make sense.
  (setq whitespace-global-modes '(not org-mode
                                    eshell-mode
                                    shell-mode
                                    web-mode
                                    log4j-mode
                                    dired-mode
                                    emacs-lisp-mode
                                    clojure-mode
                                    lisp-mode)))

When you press RET to create a newline and got indented by eletric-indent-mode, you have appropriate whitespace for indenting. But, if you leave the line blank and move to the next line, the whitespace becomes useless. Clean-aindent-mode helps clean up unused whitespace. Turn it on in prog-mode by default.

(use-package clean-aindent-mode
  :init
  (add-hook 'prog-mode-hook 'clean-aindent-mode))

4.4.5 Smartparents - Insert closing parens automagically

Smartparents is a minor mode for dealing with pairs, such as automatically insert pairs, wrap, unwrap and rewrap pairs,

(use-package smartparens
  :ensure t
  :defer t
  :diminish ""
  :init
  (smartparens-global-mode)
  (require 'smartparens-config))  

4.4.6 Undo-tree - visualize your undos and branches

Undo-tree-mode lets you use C-x u (undo-tree-visualize) to visually walk through the changes you've made, undo back to a certain point (or redo), and go down different branches.

(use-package undo-tree
  :ensure t
  :diminish undo-tree-mode
  :init (global-undo-tree-mode)
  :config
  (progn
    (setq undo-tree-visualizer-timestamps t)
    (setq undo-tree-visualizer-diff t)))

4.4.7 Auto-fill - warp long lines

Turn on auto-fill-mode to warp long lines automatically, instead of M-q altogether. Sometimes, toggle-truncate-lines would be useful.

(setq-default fill-column 79)
(add-hook 'text-mode-hook 'turn-on-auto-fill)
(add-hook 'prog-mode-hook 'turn-on-auto-fill)

(global-set-key (kbd "C-x t f") 'auto-fill-mode)
(global-set-key (kbd "C-x t t") 'toggle-truncate-lines)

Sometimes, l want to join all the lines in a paragraph into a single line, Emacs does not have a unfill command to do the inverse of fill. Luckly, Xah Lee wrote unfill functions for us.

(defun xah-unfill-paragraph ()
  "Replace newline chars in current paragraph by single spaces.
This command does the inverse of `fill-paragraph'."
  (interactive)
  (let ((fill-column most-positive-fixnum))
    (fill-paragraph)))

(define-key global-map "\M-Q" 'unfill-paragraph)

4.4.8 Hippie-expand - Text Expansion

Hippie-expand looks at the word before point and tries to expand it in various ways including expanding from a fixed list (like `expand-abbrev’), expanding from matching text found in a buffer (like `dabbrev-expand’) or expanding in ways defined by your own functions. Which of these it tries and in what order is controlled by a configurable list of functions.

(setq hippie-expand-try-functions-list
 '(try-expand-dabbrev
   try-expand-dabbrev-all-buffers
   try-expand-dabbrev-from-kill
   try-complete-file-name-partially
   try-complete-file-name
   try-expand-all-abbrevs
   try-expand-list
   try-expand-line
   try-complete-lisp-symbol-partially
   try-complete-lisp-symbol))

(global-set-key (kbd "M-/") 'hippie-expand)

4.4.9 Company - Completion for Anything

Company is a text completion framework for Emacs. The name stands for complete anything. I use company-mode for all my auto completion needs.

Completion will start automatically after you type a few letters. Use M-n and M-p to select, <RET> to complete or <TAB> to complete the common part. Press M-(digit) to quickly complete with one of the first 10 candidates.

(use-package company
  :ensure t
  :diminish company-mode
  :bind ("C-:" . company-complete)  ; In case I don't want to wait
  :init
  (add-hook 'after-init-hook 'global-company-mode)
  :config
  (setq company-idle-delay 0.5)
  (setq company-show-numbers t)
  (setq company-tooltip-limit 10)
  (setq company-minimum-prefix-length 2)
  (setq company-tooltip-align-annotations t)
  ;; invert the navigation direction if the the completion popup-isearch-match
  ;; is displayed on top (happens near the bottom of windows)
  (setq company-tooltip-flip-when-above t))

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.

4.4.10 Yasnippet - Code Templates

YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates, based on the languages. You can view a bunch of predefined snippet from the yasnippet snippets project.

(use-package yasnippet
  :ensure
  :diminish t
  :init 
  (yas-global-mode 1)
  :config
  (add-to-list 'yas-snippet-dirs (expand-file-name "snippets" user-emacs-directory)))

4.4.11 Flyspell - Spelling Checking

I like spell checking with Flyspell which does spell-checking on the fly as you type using the build-in spell-check settings of ispell.

The ASpell program is better supported than ispell. It automatically configures a personal dictionary at ~/.aspell.en.pws, so no need to configure that. Install ASpell with homebrew first:

brew install aspell
(use-package flyspell
  :defer t
  :diminish ""
  :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" ; use aspell instead of ispell
        ispell-dictionary "american"
        ispell-extra-args '("--sug-mode=ultra"
                            "--lang=en_US"
                            "--ignore=3")
        ispell-list-command "--list"))

(global-set-key (kbd "C-x t s") 'flyspell-mode)

4.4.12 Multiple Cursors - edit with multiple cursors

Multiple-cursors is an advanced package enable editing with multiple cursors simultaneously. Multiple-cursors is crazy! 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)))

4.5 Helm - interactive completion and selection

4.5.1 Helm core - amazing utils used everywhere

Helm makes it easy to complete various things. I find it to be easier to configure than ido in order to get completion in as many places as possible, although I prefer ido's way of switching buffers.

This article: A Package in a league of its own: Helm is worth reading. Helm is amazing!

(use-package helm
  :ensure t
  :diminish ""
  :init
  (require 'helm)
  (require 'helm-config)

  ;; The default "C-x c" is quite close to "C-x C-c", which quits Emacs.
  ;; Changed to "C-c h". Note: We must set "C-c h" globally, because we
  ;; cannot change `helm-command-prefix-key' once `helm-config' is loaded.
  (global-set-key (kbd "C-c h") 'helm-command-prefix)
  (global-unset-key (kbd "C-x c"))

  ;; rebind tab to run persistent action
  (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action)
  ;; make TAB works in terminal
  (define-key helm-map (kbd "C-i") 'helm-execute-persistent-action)
  ;; list actions using C-z
  (define-key helm-map (kbd "C-z")  'helm-select-action)

  (when (executable-find "curl")
    (setq helm-google-suggest-use-curl-p t))

  (setq helm-split-window-in-side-p           t
        helm-move-to-line-cycle-in-source     t
        helm-ff-search-library-in-sexp        t
        helm-scroll-amount                    8
        helm-ff-file-name-history-use-recentf t)

  (helm-mode 1)

  :config
  ;; fuzzy matching
  (setq helm-recentf-fuzzy-match t
        helm-locate-fuzzy-match nil ;; locate fuzzy is worthless
        helm-M-x-fuzzy-match t
        helm-buffers-fuzzy-matching t
        helm-semantic-fuzzy-match t
        helm-apropos-fuzzy-match t
        helm-imenu-fuzzy-match t
        helm-lisp-fuzzy-completion t
        helm-completion-in-region-fuzzy-match t)

  :bind (("M-x" . helm-M-x)
         ("M-y" . helm-show-kill-ring)

         ("C-x b" . helm-mini)
         ("C-x C-b" . helm-buffers-list)
         ("C-x C-f" . helm-find-files)
         ("C-x C-r" . helm-recentf)

         ("C-c s" . helm-swoop)
         ("C-c o" . helm-occur)

         ("C-c h a" . helm-apropos)
         ("C-c h y" . helm-yas-complete)
         ("C-c h SPC" . helm-all-mark-rings)
         ("C-c h i" . helm-semantic-or-imenu)
         ("C-c h m" . helm-man-woman)
         ))

4.5.2 Helm-projectile - project maintaining

Read this article Exploring large projects with Projectile and Helm Projectile (or the official Projectile Manual) See some demos and master the usage. First of all, you should keep the command helm-projectile (C-x p h) in mind. Then master the command on directories and files.

  (use-package helm-projectile
    :bind ("C-x p h" . helm-projectile)
    :init (projectile-global-mode)
    :config
    (helm-projectile-on)
    (setq projectile-completion-system 'helm)
    ;(setq projectile-switch-project-action 'helm-projectile-find-file)
)

4.5.3 Helm-swoop - searching tool

This promises to be a fast way to find things.

(use-package helm-swoop
  :bind (("M-i" . helm-swoop)
         ("M-I" . helm-swoop-back-to-last-point)
         ("C-c M-i" . helm-multi-swoop))
  :config
  ;; When doing isearch, hand the word over to helm-swoop
  (define-key isearch-mode-map (kbd "M-i") 'helm-swoop-from-isearch)
  ;; From helm-swoop to helm-multi-swoop-all
  (define-key helm-swoop-map (kbd "M-i") 'helm-multi-swoop-all-from-helm-swoop)
  ;; Save buffer when helm-multi-swoop-edit complete
  (setq helm-multi-swoop-edit-save t
        ;; If this value is t, split window inside the current window
        helm-swoop-split-with-multiple-windows t
        ;; Split direcion. 'split-window-vertically or 'split-window-horizontally
        helm-swoop-split-direction 'split-window-vertically
        ;; If nil, you can slightly boost invoke speed in exchange for text color
        helm-swoop-speed-or-color nil))

4.5.4 Helm-describe - keybings describe

Helm Descbinds provides an interface to emacs’ describe-bindings making the currently active key bindings interactively searchable with helm.

(use-package helm-descbinds
  :bind ("C-h b" . helm-descbinds)
  :init (fset 'describe-bindings 'helm-descbinds)
  :config (require 'helm-config))

4.5.5 Helm-flyspell - flyspell correct

(use-package helm-flyspell
  :defer t
  :config
  (define-key flyspell-mode-map (kbd "M-S") 'helm-flyspell-correct))

4.6 Backup and autosave

Backup is one of the things people usually want to change right away. By default, Emacs saves backup files in the current directory. These are the files ending in ~ that are cluttering up your directory lists. The following code stashes them all in $HOME/.emacs.d/backup, where I can find them with C-x C-f if I really need to.

;; backup in one place. flat, no tree structure
(setq backup-directory-alist `((".*" . ,user-emacs-backup-dir)))
(setq make-backup-files t)

;; store the undo-tree history in one place
(setq undo-tree-history-directory-alist `((".*" . ,user-emacs-backup-dir)))      
(setq undo-tree-auto-save-history t)

;; backup misc settings
(setq backup-by-copying t)
(setq delete-old-versions t)
(setq version-control t)
(setq vc-make-backup-files t)
(setq kept-new-versions 6
      kept-old-versions 2)
(setq auto-save-file-name-transforms `((".*" ,user-emacs-autosave-dir t)))
(setq auto-save-default t)

4.7 Persistence and history

4.7.1 Recentf - recent files

Recentf is a minor mode that builds a list of recently opened files. Turn it on, then call recentf-open-files to open recently opened files fast.

(require 'recentf)
(recentf-mode 1) ; keep a list of recently opened files, for future sessions
(setq recentf-save-file (expand-file-name "recentf" user-emacs-savefile-dir))

(setq recentf-max-saved-items 500
      recentf-max-menu-items 25
      ;; disable recentf-cleanup on Emacs start, because it can cause
      ;; problems with remote files
      recentf-auto-cleanup 'never)

(global-set-key (kbd "C-c f r") 'recentf-open-files)

4.7.2 Saveplace - save file position

You can save the cursor position for every file you opened. So, next time you open the file, the cursor will be at the position you last opened it.

(require 'saveplace)
(save-place-mode 1)
(setq save-place-file (expand-file-name "saveplace" user-emacs-savefile-dir))

4.7.3 Savehist - save history

By default, Savehist saves only your minibuffer histories, but you can optionally save other histories and other variables as well.

(require 'savehist)
(savehist-mode 1)
(setq savehist-file (expand-file-name "savehist" user-emacs-savefile-dir))

(setq history-length t)
(setq history-delete-duplicates t)
(setq savehist-save-minibuffer-history 1)
(setq savehist-additional-variables
      '(kill-ring
        search-ring
        regexp-search-ring))

4.7.4 Desktop - save and restore opened files and windows config

Desktop can save and restore all previously opened files in last Emacs session, and also previous windows configuration.

(require 'desktop)
(desktop-save-mode 1)
(setq desktop-base-file-name (expand-file-name "desktop" user-emacs-savefile-dir))
(setq desktop-base-lock-name (expand-file-name "desktop.lock" user-emacs-savefile-dir))

(setq desktop-save 'ask)
(setq desktop-auto-save-timeout nil)

I don't tangle this block anymore because it slow down the startup speed obviously.

4.7.5 Bookmark - emacs bookmark

Bookmarks lets you easily open frequently needed files. It is similar to browser's bookmark. Read this article about the usage.

(require 'bookmark)
(setq bookmark-default-file (expand-file-name "bookmark" user-emacs-savefile-dir))
(setq bookmark-save-flag 1) ; everytime bookmark is changed, auto save it

4.7.6 Eshell - emacs shell

(require 'eshell)
(setq eshell-directory-name (expand-file-name "eshell" user-emacs-savefile-dir))

4.8 Global Key Bindings

4.8.1 Function keys

(global-set-key [f8] 'other-frame)
(global-set-key [f9] 'enlarge-window)
(global-set-key [f10] 'enlarge-window-horizontally)
(global-set-key [f11] 'toggle-frame-fullscreen)

4.8.2 Easy keys for frequently used commands

Split and unsplit window panes are some of the most frequently used commands, because Emacs often generates output in a splits window. So set easy keys for them:

(global-set-key (kbd "s-0") 'delete-window)
(global-set-key (kbd "s-1") 'delete-other-windows)
(global-set-key (kbd "s-2") 'split-window-below)
(global-set-key (kbd "s-3") 'split-window-right)

Change font size:

(global-set-key (kbd "C-s-=") 'text-scale-increase)
(global-set-key (kbd "C-s--") 'text-scale-decrease)

4.8.3 Toggles

Bind shortcuts to some mode toggle functions. Inspired by this article, I am a wizard now.

(define-prefix-command 'endless/toggle-map)
;; The manual recommends C-c for user keys, but C-x t is
;; always free, whereas C-c t is used by some modes.
(define-key ctl-x-map "t" 'endless/toggle-map)
;; build-in packages
(define-key endless/toggle-map "d" #'toggle-debug-on-error)
(define-key endless/toggle-map "q" #'toggle-debug-on-quit)
(define-key endless/toggle-map "f" #'auto-fill-mode)
(define-key endless/toggle-map "t" #'toggle-truncate-lines)
(define-key endless/toggle-map "l" #'display-line-numbers-mode)
;; packages installed by `use-packages'
(define-key endless/toggle-map "w" #'whitespace-mode)
(define-key endless/toggle-map "s" #'flyspell-mode)
(define-key endless/toggle-map "c" #'flycheck-mode)
(define-key endless/toggle-map "g" #'git-timemachine)

4.8.4 Miscellaneous

Here are some useful global key bindings.

;; Align your code in a pretty way.
(global-set-key (kbd "C-x \\") 'align-regexp)

;; Start eshell or switch to it if it's active.
(global-set-key (kbd "C-x m") 'eshell)

;; Start a new eshell even if one is active.
(global-set-key (kbd "C-x M") (lambda () (interactive) (eshell t)))

;; Start a regular shell if you prefer that.
(global-set-key (kbd "C-x M-m") 'shell)

;; use hippie-expand instead of dabbrev
(global-set-key (kbd "M-/") 'hippie-expand)

;; replace buffer-menu with ibuffer
(global-set-key (kbd "C-x C-b") 'ibuffer)

4.8.5 Which-key - keys cheat sheet

Many command sequences may be logical, but who can remember them all? Don't worry. Which-key can display the key bindings following your currently entered incomplete command (a prefix) in a popup.

(use-package which-key
  :ensure t
  :config (which-key-mode))

5 Aesthetics

5.1 Color Themes

Solarized Theme and Spacemacs Theme are my favorite color themes. And the Zenburn Theme is also really nice. I used these as my default color themes.

According to this Stackoverflow discuss , I can use f12 to switch color themes.

(use-package zenburn-theme :defer t)
(use-package solarized-theme :defer t)
(use-package spacemacs-theme :defer t)

;; color theme cycle list
(setq my-themes (list
                 'zenburn
                 'spacemacs-light
                 'spacemacs-dark
                 ;; 'solarized-light
                 ;; 'solarized-dark
                 ))

(defun my-theme-cycle ()
  (interactive)
  (disable-theme (car curr-theme)) ;;Nee flickeringded to stop even worse
  (setq curr-theme (cdr curr-theme))
  (if (null curr-theme) (setq curr-theme my-themes))
  (load-theme (car curr-theme) t)
  (message "%s" (car curr-theme)))

(global-set-key [f12] 'my-theme-cycle)
(setq curr-theme my-themes)
(load-theme (car curr-theme) t)

5.2 Modeline Theme

(use-package powerline
  :ensure t
  :init
  (setq powerline-default-separator 'curve
        powerline-default-separator-dir (quote (left . right))
        powerline-height 28
        powerline-display-buffer-size nil
        powerline-display-hud nil
        powerline-display-mule-info nil
        powerline-gui-use-vcs-glyph t
        powerline-inactive1 '((t (:background "grey11" :foreground "#c5c8c6")))
        powerline-inactive2 '((t (:background "grey20" :foreground "#c5c8c6")))))

5.3 Icons

According to this, install all-the-icon package for neotree's color theme:

(use-package all-the-icons)

In order for the icons to work it is very important that you install the Resource Fonts included in this package. Call the following command:

M-x all-the-icons-install-fonts

Then use all-the-icons for neotree:

(setq inhibit-compacting-font-caches t)
(setq neo-theme 'icons)

5.4 Fonts

Choosing a nice and comfortable font is quite important in your whole coding life.

I prefer Monaco. And, as a Chinese, l choose WenQuanYi for Chinese charset.

(when (eq system-type 'darwin)

  ;; default Latin font (e.g. Consolas)
  (set-face-attribute 'default nil :family "Monaco")

  ;; default font size (point * 10)
  ;;
  ;; WARNING!  Depending on the default font,
  ;; if the size is not supported very well, the frame will be clipped
  ;; so that the beginning of the buffer may not be visible correctly.
  (set-face-attribute 'default nil :height 150)

  ;; use specific font for Chinese charset.
  ;; if you want to use different font size for specific charset,
  ;; add :size POINT-SIZE in the font-spec.
  (set-fontset-font t 'han (font-spec :name "WenQuanYi Micro Hei Mono"))
  )

M-x eshell, then run the command (print (font-family-list)). You will see a list of fonts can be used in Emacs.

5.5 Nyan-mode - Nyan cat in modeline

Let Nyan Cat show you your buffer position in mode line. You can scroll the buffer by clicking on the Nyan Cat’s rainbow and the space in front of it.

(use-package nyan-mode
  :init (nyan-mode))

5.6 Rainbow-delimiters - color delimiters for LISP

For lisp like languages, I want to witness the full power of colorful rainbow-delimiters! I will even set them to pastel versions of the rainbow colors.

(use-package rainbow-delimiters
  :init (rainbow-delimiters-mode 1))

(set-face-attribute 'rainbow-delimiters-depth-1-face nil
                    :foreground "#78c5d6")
(set-face-attribute 'rainbow-delimiters-depth-2-face nil
                    :foreground "#bf62a6")
(set-face-attribute 'rainbow-delimiters-depth-3-face nil
                    :foreground "#459ba8")
(set-face-attribute 'rainbow-delimiters-depth-4-face nil
                    :foreground "#e868a2")
(set-face-attribute 'rainbow-delimiters-depth-5-face nil
                    :foreground "#79c267")
(set-face-attribute 'rainbow-delimiters-depth-6-face nil
                    :foreground "#f28c33")
(set-face-attribute 'rainbow-delimiters-depth-7-face nil
                    :foreground "#c5d647")
(set-face-attribute 'rainbow-delimiters-depth-8-face nil
                    :foreground "#f5d63d")
(set-face-attribute 'rainbow-delimiters-depth-9-face nil
                    :foreground "#78c5d6")

We also want to make unmatched parens stand out more:

(set-face-attribute 'rainbow-delimiters-unmatched-face nil
                    :foreground 'unspecified
                    :inherit 'show-paren-mismatch
                    :strike-through t)

Now we just need to adjust the hook for lisp-like languages. Possibly have to add clojure, if I ever want to mess with that.

(add-hook 'emacs-lisp-mode-hook 'rainbow-delimiters-mode)
(add-hook 'lisp-mode-hook 'rainbow-delimiters-mode)

5.7 Rainbow-mode - color words for CSS

Rainbow-mode makes "color words" in my programs appear in the colours they describe. Particularly good for CSS and the like.

(use-package rainbow-mode
  :diminish rainbow-mode
  :config
  (add-hook 'emacs-lisp-mode-hook 'rainbow-mode)
  (add-hook 'css-mode-hook 'rainbow-mode)
  (add-hook 'html-mode-hook 'rainbow-mode)
  (add-hook 'js2-mode-hook 'rainbow-mode))

6 Programming Environment

6.1 Editing Support

6.1.1 Aggressive Indent

Automatically indent without use of the tab found in this article, and seems to be quite helpful for many types of programming languages.

(use-package aggressive-indent
  :init (global-aggressive-indent-mode 1))

6.1.2 Indent Guide

Indent Guide project shows vertical lines to guide indentation.

(use-package indent-guide
  :init (indent-guide-global-mode))

6.1.3 Code 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:

(global-set-key (kbd "C-x t h") 'hs-minor-mode)
(global-set-key (kbd "C-c t h") 'ha/hs-hide-all)
(global-set-key (kbd "C-c t s") 'ha/hs-show-all)
(global-set-key (kbd "C-c t c") 'ha/hs-toggle-hiding)

6.2 Document

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

6.3 Error checking

Flycheck seems to be quite superior to Flymake.

(use-package flycheck
  :bind ("C-x t c" . flycheck-mode)
  :ensure t
  :diminish flycheck-mode
  :init
  (add-hook 'after-init-hook 'global-flycheck-mode)
  :config
  (setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc)))

7 Programming Languages

7.1 Shell

Make a shell script executable automatically on save.

(add-hook 'after-save-hook
          'executable-make-buffer-file-executable-if-script-p)

Files with .zsh extension is shell script too.

(add-to-list 'auto-mode-alist '("\\.zsh\\'" . sh-mode))

8 Typesetting Languages

8.1 Markdown

Markdown-mode is a major mode for editing Markdown-formatted text.

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown"))

8.2 LaTeX

9 Git and Magit

9.1 Git basic settings

I like diff-hl. This mode can highlight uncommitted changes on the left side of the window, allows you to jump between and revert them selectively.

(use-package diff-hl
  :ensure t
  :init (global-diff-hl-mode)
  :config
  (add-hook 'dired-mode-hook 'diff-hl-dired-mode)
  (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh))

I want to have special mode for Git's gitconfig and gitignore file:

(use-package gitconfig-mode
  :ensure t)

(use-package gitignore-mode
  :ensure t)

Play with Git Time Machine project for viewing the different version history of a file with p and n.

(use-package git-timemachine
  :bind ("C-x t g" . git-timemachine))

Git Messenger can popup commit message at current line.

(use-package git-messenger
  :bind (("C-x v m" . git-messenger:popup-message)))

9.2 Magit - git interface

Git is already part of Emacs. However, Magit is sweet!

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

10 Org-Mode

10.1 Exporter Setup

I have the following setup for the exporters I use. Alphabetical listing options need to be set before the exporters are loaded for filling to work correctly.

(setq org-alphabetical-lists t)

;; Explicitly load required exporters
(require 'ox-html)
(require 'ox-latex)
(require 'ox-ascii)
(require 'ox-md)

The ox-extra package make it possible to export content of subtrees without their headings.

(require 'ox-extra)
(ox-extras-activate '(ignore-headlines))

11 Acknowledges

  • Inspired by Sacha and Bernt, I began to maintain my Emacs configuration using org-babel in literate style.
  • My configuration taken a lot from Howard Abrams's Emacs initialization code which is well structured and documented with org-mode.
  • Benefit from the Magit project's Themes, I built the pretty nice Emacs Init File Project Page.

Date: 2016-2019

Author: Xiaojie Feng

Email: fengxiaojie1997@gmail.com

Created: 2019-09-01 Sun 22:47

Emacs 26.1 (Org mode 9.2.5)

Validate