Skip to content

Latest commit

 

History

History
2342 lines (1858 loc) · 80.9 KB

init.org

File metadata and controls

2342 lines (1858 loc) · 80.9 KB

Emacs Configuration File

Table of Contents

Introduction

This is an Emacs configuration file written in Org-mode. It is adapted from Lars Tveito’s excellent configuration file on GitHub.

Meta

Tangling

After cloning from GitHub, there is no init.el file, only an init.org file (this file).

To produce an init.el file, either:

  • Open init.org and use C-c C-v t to call org-babel-tangle, which extracts code blocks from the current file into init.el, then restart Emacs, or
  • Run the following command in a shell before starting Emacs:
    emacs --batch \
          --load "/usr/local/share/emacs/25.1/lisp/org/ob-tangle.elc" \
          --eval "(org-babel-tangle-file \"~/.emacs.d/init.org\")"
        

    This assumes Emacs 25.1 built from source on GNU/Linux. Otherwise, swap out the load path of ob-tangle.elc for whatever is appropriate.

Automatic tangling

To avoid having to tangle manually each time a change is made, we can add a function to after-save-hook to tangle the init.org after saving.

(defun *-tangle-init-file ()
  "Tangle the current buffer if it is the init.org file."
  (when (equal (buffer-file-name)
               (expand-file-name (concat user-emacs-directory "init.org")))
    (org-babel-tangle)))

(add-hook 'after-save-hook #'*-tangle-init-file)

Package Management

Managing extensions in Emacs is simplified using package, which is built into Emacs 24 and newer. To load downloaded packages, we need to initialize package.

  • First, we list the package archives we’d like to install packages from.
    (customize-set-variable 'package-archives
                            '(("gnu" . "http://elpa.gnu.org/packages/")
                              ("melpa" . "https://melpa.org/packages/")))
        
  • If the variable package-enable-at-startup is non-nil, package initialization occurs after the init file is loaded, but before after-init-hook. We want to load packages before the init file is loaded, because we’ll be referencing packages in the init file. Therefore, we need to initialize our packages manually.
    (customize-set-variable 'package-enable-at-startup nil)
    (package-initialize)
        
  • Make sure that we have the list of packages available.
    (unless package-archive-contents
      (package-refresh-contents))
        
  • Define a list of packages that we want to install. Package is smart enough to install dependencies automatically.
    (setq *-package-list '(aggressive-indent
                           auctex
                           cider
                           clojure-mode
                           company
                           company-auctex
                           company-quickhelp
                           discover-my-major
                           dockerfile-mode
                           edit-server
                           elpy
                           emmet-mode
                           eros
                           expand-region
                           flycheck
                           flycheck-color-mode-line
                           haskell-mode
                           helm
                           helm-cider
                           helm-dash
                           helm-descbinds
                           helm-describe-modes
                           helm-org
                           helm-pages
                           helm-projectile
                           htmlize
                           hydra
                           js2-mode
                           json-mode
                           key-chord
                           less-css-mode
                           macrostep
                           magit
                           markdown-mode
                           mediawiki
                           multiple-cursors
                           page-break-lines
                           pdf-tools
                           popwin
                           projectile
                           quick-peek
                           rainbow-mode
                           scss-mode
                           slime
                           slime-company
                           smartparens
                           toc-org
                           use-package
                           yaml-mode
                           yasnippet
                           zenburn-theme))
        
  • Install the missing packages.
    (dolist (package *-package-list)
      (unless (package-installed-p package)
        (package-install package)))
        
  • Define a function to easily upgrade all packages and delete obsolete ones. Thanks to @basil for pointers on package-menu-async!
    (defun *-package-upgrade ()
      "Refresh, upgrade and delete obsolete packages synchronously."
      (interactive)
      (save-window-excursion
        (let (package-menu-async)
          (package-list-packages)))
      (with-current-buffer "*Packages*"
        (package-menu-mark-upgrades)
        (package-menu-mark-obsolete-for-deletion)
        (condition-case err
            (package-menu-execute t)
          ;; Don't barf if there is nothing to do
          (user-error (message "Nothing to do"))
          ;; But allow other errors through
          (error (signal (car err) (cdr err))))))
        
  • Use a hydra, along with helm, to create a package-related menu.
    (defhydra hydra-package (:color blue)
      "
    Packages                                                           [_q_] quit
    ^^---------------------------------------------------------------------------
    [_d_] describe
    [_i_] install
    [_l_] list
    [_L_] list (no fetch)
    [_U_] upgrade all
    "
    ("d" describe-package nil)
    ("i" package-install nil)
    ("l" package-list-packages nil)
    ("L" package-list-packages-no-fetch nil)
    ("U" *-package-upgrade nil)
    ("q" nil nil))
    
    (define-key help-map "p" 'hydra-package/body)
        

Utils

Utility functions, etc. that need to be fined ahead of time.

Requires

Require various libraries.

(require 'cl-lib)
(require 'subr-x)

Doc pop-up

Macro to create function that provides documentation in a pop-up window.

(require 'quick-peek)
(set-face-attribute 'quick-peek-border-face nil :height 20)

(defmacro *-make-doc-command (doc-fun sym-fun)
  "Return an command that uses `quick-peek' to preview docs.

DOC-FUN is a unary function that takes a loop-up string and
returns the doc string.

SYM-FUN is a nullary function that gets the symbol at point as a
string."
  `(lambda ()
     (interactive)
     (let ((doc (funcall ,doc-fun (funcall ,sym-fun))))
       (if (string-empty-p doc)
           (message "Unknown symbol, or no documentation available.")
         (let ((map (make-sparse-keymap)))
           (set-transient-map map
                              (lambda ()
                                (eq #'mwheel-scroll this-command))
                              (lambda ()
                                (quick-peek-hide)
                                (setq this-command #'ignore))))
         (let ((pos (save-excursion
                      (beginning-of-line)
                      (point))))
           (quick-peek-show doc pos nil (frame-height)))))))

Configurations

About Emacs

  • Replace the *About GNU Emacs* buffer with Emacs and user info.
(defhydra hydra-about-emacs ()
  "
About Emacs                                                        [_q_] quit
^^---------------------------------------------------------------------------
         PID:    %s(emacs-pid)
      Uptime:    %s(emacs-uptime)
   Init time:    %s(emacs-init-time)
   Directory:    %s(identity user-emacs-directory)
Invoked from:    %s(concat invocation-directory invocation-name)
     Version:    %s(identity emacs-version)

User Info
^^---------------------------------------------------------------------------
   User name:    %s(user-full-name)
Login (real):    %s(user-login-name) (%s(user-real-login-name))
  UID (real):    %s(user-uid) (%s(user-real-uid))
  GID (real):    %s(group-gid) (%s(group-real-gid))
Mail address:    %s(identity user-mail-address)
"
  ("q" nil nil))

(global-set-key (kbd "C-h C-a") #'hydra-about-emacs/body)
  • System info
    (defhydra hydra-system-info ()
      "
    System Info                                                        [_q_] quit
    ^^---------------------------------------------------------------------------
        System name:    %s(system-name)
        System type:    %s(identity system-type)
      System config:    %s(identity system-configuration)
    
    Memory
    ^^---------------------------------------------------------------------------
               Used:    %s(format \"%0.0f percent\"
                                  (* 100 (- 1 (/ (cl-second (memory-info))
                                                 (float (cl-first (memory-info)))))))
           Free RAM:    %s(format \"%0.1f GB (of %0.1f GB)\"
                                  (/ (float (cl-second (memory-info))) 1048576)
                                  (/ (float (cl-first (memory-info))) 1048576))
          Free swap:    %s(format \"%0.1f GB (of %0.1f GB)\"
                                  (/ (float (cl-fourth (memory-info))) 1048576)
                                  (/ (float (cl-third (memory-info))) 1048576))
        Pure memory:    %s(format \"%0.1f GB\" (/ (float pure-bytes-used) 1048576))
    
    Garbage Collection
    ^^---------------------------------------------------------------------------
           GCs done:    %`gcs-done
        GCs elapsed:    %s(format-seconds \"%M, %S\" gc-elapsed)
     Cons threshold:    %`gc-cons-threshold
    Cons percentage:    %`gc-cons-percentage
    "
      ("q" nil nil))
    
    (global-set-key (kbd "C-h C-s") #'hydra-system-info/body)
        

About Me

  • Set personal information, like name and e-mail.
    (customize-set-variable 'user-full-name "Tianxiang Xiong")
    (customize-set-variable 'user-mail-address "[email protected]")
        

Aggressive Indent

  • Use aggressive-indent to keep source code aligned.
    (aggressive-indent-global-mode 1)
        
  • Disable aggressive-indent-mode in shells, REPLs, etc.
    (dolist (mode '(cider-repl-mode
                    comint-mode
                    eshell-mode
                    slime-repl-mode
                    term-mode))
      (add-to-list 'aggressive-indent-excluded-modes mode))
        

Blog

  • Use org-publish to manage my GitHub blog. We need to set the org-publish-project-alist variable to publish projects.
    (customize-set-variable
     'org-publish-project-alist
     '(("blog posts"
        :base-directory "~/github/xiongtx.github.io/_org/"
        :base-extension "org"
        :body-only t
        :html-extension "html"
        :publishing-directory "~/github/xiongtx.github.io/"
        :publishing-function org-html-publish-to-html
        :recursive t)
    
       ("blog static"
        :base-directory "~/github/xiongtx.github.io/_org/"
        :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg"
        :publishing-directory "~/github/xiongtx.github.io/"
        :publishing-function org-publish-attachment
        :recursive t)
    
       ("blog" :components ("blog posts" "blog static"))))
        

Buffer

  • Kill current buffer.
    (global-set-key (kbd "C-c k") #'kill-this-buffer)
        
  • Use unique buffer names. The post-forward style displays the buffer name as buffer|dir1/dir2.
    (customize-set-variable 'uniquify-buffer-name-style 'post-forward)
        
  • Revert buffer with <f5> (refresh).
    (global-set-key (kbd "<f5>") #'revert-buffer)
        

Clojure

  • Recognize boot files as Clojure.
    (add-to-list 'auto-mode-alist '("\\.boot\\'" . clojure-mode))
    
    ;; Boot script files
    (add-to-list 'magic-mode-alist '(".* boot" . clojure-mode))
        
  • Custom indent special forms and macros.
    (with-eval-after-load 'clojure-mode
      (define-clojure-indent
        (defrecord '(2 nil nil (1)))
        (deftype   '(2 nil nil (1)))
        (implement '(1 (1)))
        (letfn     '(1 ((:defn)) nil))
        (match 1)
        (proxy     '(2 nil nil (1)))
        (reify     '(:defn (1)))
        (specify   '(1 (1)))
        (specify   '(1 (1)))))
        
  • Use CIDER when visiting Clojure files.
    (with-eval-after-load 'clojure-mode
      (add-hook 'clojure-mode-hook #'cider-mode))
    
    (customize-set-variable 'cider-prompt-for-symbol nil)
    (customize-set-variable 'cider-repl-display-help-banner nil)
    (customize-set-variable 'cider-repl-use-pretty-printing t)
        
  • Bind keys for browsing Clojure namespaces.
    (with-eval-after-load 'cider-doc
      (define-key cider-doc-map (kbd "n") #'cider-browse-ns-all)
      (define-key cider-doc-map (kbd "C-n") #'cider-browse-ns-all))
        
  • Use helm-cider.
    (with-eval-after-load 'cider-mode
      (add-hook 'cider-mode-hook #'helm-cider-mode))
        
  • Look up Clojure documentation in a pop-up with CIDER.
    (defun *-cider-symbol-full-doc (symbol)
      "Return a string of the full documentation of SYMBOL, as given by
    `cider-create-doc-buffer'."
      (let ((buf (cider-create-doc-buffer symbol)))
        (when buf
          (with-current-buffer buf
            (buffer-substring (point-min)
                              ;; `-10' to exclude "[source]" line
                              (- (point-max) 10))))))
    
    (defun *-cider-doc-popup ()
      "Display CIDER documentation in a popup."
      (interactive)
      (funcall (*-make-doc-command #'*-cider-symbol-full-doc #'cider-symbol-at-point)))
    
    (defun *-cider-doc-popup-on ()
      "Turn `*-cider-doc-popup' by binding it to an appropriate key."
      (define-key cider-mode-map (kbd "C-h j") #'*-cider-doc-popup)
      (define-key cider-mode-map (kbd "C-h C-j") #'*-cider-doc-popup))
    
    ;; Only use pop-up documentation when CIDER is connected
    (add-hook 'cider-connected-hook #'*-cider-doc-popup-on)
        

ClojureScript

  • Use Figwheel when after cider-jack-in-clojurescript. Thanks to Mark Hudnall.
    (customize-set-variable 'cider-cljs-lein-repl
                            "(do (require 'figwheel-sidecar.repl-api)
                                 (figwheel-sidecar.repl-api/start-figwheel!)
                                 (figwheel-sidecar.repl-api/cljs-repl))")
        

Comint

  • Make prompt read-only.
    (customize-set-variable 'comint-prompt-read-only t)
        
  • Use comint-bol instead of move-beginning-of-line
    (with-eval-after-load 'comint-mode
      (define-key comint-mode-map (kbd "C-a") #'comint-bol))
        

Comment

  • Comment or uncomment a region or line in a “do what I mean” fashion.
    (defun *-comment-or-uncomment-region-or-line ()
      "Comments or uncomments the region or the current line if there's no active region."
      (interactive)
      (let (beg end)
        (if (region-active-p)
            (setq beg (region-beginning) end (region-end))
          (setq beg (line-beginning-position) end (line-end-position)))
        (comment-or-uncomment-region beg end)
        (forward-line)))
        
  • Set convenient key binding to comment/uncomment line.
    (global-set-key (kbd "C-;") #'*-comment-or-uncomment-region-or-line)
        

Common Lisp

  • Open files in lisp-mode.
    ;; The SBCL configuration file is in Common Lisp
    (add-to-list 'auto-mode-alist '("\\.sbclrc\\'" . lisp-mode))
    
    ;; Open files with .cl extension in lisp-mode
    (add-to-list 'auto-mode-alist '("\\.cl\\'" . lisp-mode))
        
  • Settings for SLIME (Superior Lisp Interaction Mode for Emacs).
    (setq inferior-lisp-program "/usr/bin/sbcl --noinform")
    (setq slime-contribs '(slime-fancy))
        
  • Use slime-company, which integrates company-mode with SLIME.
    (add-to-list 'slime-contribs 'slime-company)
        
  • Set up SLIME contribs.
    (slime-setup)
        
  • Create SLIME REPL when visiting Common Lisp file.
    (defun *-slime-create-or-switch-to ()
      "Start an inferior Lisp process and connect to its Swank
    server if none exists, or switch to existing one.
    
    This is always done in another window. If there is only one
    window, it is split horizontally.
    
    Do not switch to SLIME window if Helm is active; this allows
    previewing files in Helm without trouble."
      (interactive)
      (save-selected-window
        (if (not (slime-connected-p))
            (slime)
          (if (> (length (window-list)) 1)
              (other-window 1)
            (split-window-horizontally)
            (other-window 1)
            (*-rotate-buffers-in-windows))
          (unless (helm-alive-p)
            (set-window-buffer (selected-window)
                               (slime-output-buffer))))))
    
    ;; (add-hook 'lisp-mode-hook #'*-slime-create-or-switch-to)
        
  • Use hyperspec commands with SLIME.
    (with-eval-after-load 'slime
      (define-key slime-mode-map (kbd "C-c C-d C-s") #'common-lisp-hyperspec))
        
  • Look up Common Lisp documentation in a pop-up with SLIME.
    (defun *-slime-symbol-full-doc (symbol)
      "Return a string of the full documentation of SYMBOL.
    
    First `slime-documentation' is tried.  If there is no
    documentation, `slime-describe-symbol' is tried."
      (let ((symbol (if (stringp symbol) symbol (symbol-name symbol)))
            (package (slime-current-package)))
        (let ((doc (slime-eval `(swank:documentation-symbol ,symbol) package)))
          (if (string-prefix-p "No such symbol" doc)
              ""
            (if (string-suffix-p "Not documented." doc)
                (slime-eval `(swank:describe-symbol ,symbol) package)
              doc)))))
    
    (defun *-slime-doc-popup ()
      "Display CIDER documentation in a popup."
      (interactive)
      (funcall (*-make-doc-command #'*-slime-symbol-full-doc #'slime-symbol-at-point)))
    
    (defun *-slime-doc-popup-on ()
      "Use `*-slime-doc-popup' by binding it to an appropriate key."
      (define-key slime-mode-map (kbd "C-h j") #'*-slime-doc-popup)
      (define-key slime-mode-map (kbd "C-h C-j") #'*-slime-doc-popup))
    
    (add-hook 'slime-connected-hook #'*-slime-doc-popup-on)
        

Company

  • Use company-mode, a modular in-buffer completion framework for Emacs.
    (add-hook 'after-init-hook #'global-company-mode)
        
  • Reduce completion menu time delay to minimum.
    (customize-set-variable 'company-idle-delay 0)
        
  • More easily navigate the completion menu.
    (with-eval-after-load 'company
      (define-key company-active-map (kbd "C-n") #'company-select-next)
      (define-key company-active-map (kbd "C-p") #'company-select-previous))
        
  • Show quick-access numbers of completion options.
    (customize-set-variable 'company-show-numbers t)
        
  • Align annotations.
    (customize-set-variable 'company-tooltip-align-annotations t)
        
  • Use company-quickhelp, which shows a documentation pop-up during company-mode completion.
    (company-quickhelp-mode t)
    
    ;; Do not show pop-up automatically
    (customize-set-variable 'company-quickhelp-delay nil)
    
    ;; Define binding for showing pop-up manually in company-active-map instead of
    ;; company-quickhelp-mode-map; this activates it only when we want completion.
    (with-eval-after-load 'company
      (define-key company-active-map (kbd "C-j") #'company-quickhelp-manual-begin))
        

    There’s also company-show-doc-buffer, which is built into company-mode-map, but the selections disappear when the doc buffer appears (maybe it just doesn’t play nice with popwin?). Furthermore, it’s easier to read the documentation when it’s right next to the candidate.

Compilation

  • Use more convenient bindings for previous-error and next-error.
    (dolist (key (append (where-is-internal #'previous-error)
                         (where-is-internal #'next-error)))
      (global-unset-key key))
    (global-set-key (kbd "M-N") #'next-error)
    (global-set-key (kbd "M-P") #'previous-error)
        
  • Quickly bring up last compilation buffer.
    (defun *-last-compilation-buffer ()
      "Display last compilation buffer in current window."
      (interactive)
      (if (buffer-live-p compilation-last-buffer)
          (set-window-buffer (get-buffer-window) compilation-last-buffer)
        (message "Last compilation buffer is killed.")))
    
    (global-set-key (kbd "C-x c") #'*-last-compilation-buffer)
        

CSS

  • Use emmet-mode.
    (add-hook 'css-mode-hook #'emmet-mode)
        
  • Use rainbow-mode.
    (add-hook 'css-mode-hook #'rainbow-mode)
        

Cursor

  • Do not blink the cursor.
    (blink-cursor-mode 0)
        

Default Functionality

  • Some functionality is turned off by default to avoid confusing new users. Turn them back on.
    (put 'erase-buffer 'disabled nil)
    (put 'narrow-to-defun 'disabled nil)
    (put 'narrow-to-page 'disabled nil)
    (put 'narrow-to-region 'disabled nil)
        

Default Major Mode

  • Set the default major mode to emacs-lisp-mode.
    (customize-set-variable 'initial-major-mode #'emacs-lisp-mode)
        

Dired

  • Omit uninteresting files; see dired-omit-files.
    (require 'dired-x)
    (add-hook 'dired-mode-hook #'dired-omit-mode)
        

Discover My Major

  • Use discover-my-major, which displays key bindings and their meaning for the current Emacs major mode.
    (define-key help-map (kbd "C-k") #'discover-my-major)
        

Ediff

  • Better defaults for ediff.
    ;; Show control panel in single frame
    (customize-set-variable 'ediff-window-setup-function 'ediff-setup-windows-plain)
    
    ;; Split windows horizontally
    (customize-set-variable 'ediff-split-window-function 'split-window-horizontally)
    
    ;; Only highlight one diff
    (customize-set-variable 'ediff-highlight-all-diffs nil)
        
  • Useful diff commands.
    (global-set-key (kbd "C-x d") #'ediff-current-file)
        
  • Expand outline-mode (including org-mode) buffers when diffing.
    (add-hook 'ediff-prepare-buffer-hook #'outline-show-all)
        

Edit Server

  • Use emacs-chrome, which allows editing of text areas and other editable text elements of a page with Emacs.

    Obviously, we need to install the Edit with Emacs extension for Chrome.

    For this to work, we need to run an “edit server” on our machine. This is because, as a security measure, extensions in Chrome(ium) cannot spawn new processes.

    ;; Start edit-server
    (when (require 'edit-server nil t)
      ;; Edit in a new buffer instead of a new frame
      (customize-set-variable 'edit-server-new-frame nil)
      (edit-server-start))
        
  • Associated common websites with major modes.
    (customize-set-variable 'edit-server-url-major-mode-alist
                            '(("github\\.com" . markdown-mode)
                              ("reddit\\.com" . markdown-mode)
                              ("stackexchange\\.com" . markdown-mode)
                              ("stackoverflow\\.com" . markdown-mode)))
        

Elisp

  • Use eros, which displays evaluation result overlays.
    (eros-mode 1)
        
  • Use eldoc-mode, which shows function signatures in the echo area.
;; Don't delay `eldoc' display
(customize-set-variable 'eldoc-idle-delay 0)

;; Activate `eldoc' for certain modes
(add-hook 'emacs-lisp-mode-hook #'eldoc-mode)
(add-hook 'lisp-interaction-mode-hook #'eldoc-mode)
  • Use quick-peek to create documentation pop-up for Emacs Lisp. Heavily inspired by Popup Help in Emacs Lisp and clippy.
    (defmacro *-symbol-full-doc (symbol)
      "Return a string of the full documentation of SYMBOL, as given by
    `help-xref-interned'.
    
    If `popwin-mode' is active, disable it temporarily. The value of
    `popwin-mode' is thus always nil, even when the mode is active.'"
      (let* ((sym (cl-gensym "doc"))
             (form `(let ((,sym ,symbol))
                      (save-window-excursion
                        (with-temp-buffer
                          (let ((help-xref-following t))
                            (help-mode)
                            (help-xref-interned ,sym)
                            (buffer-string)))))))
        (if (fboundp #'popwin-mode)
            `(let ((old-popwin popwin-mode))
               (popwin-mode -1)
               (unwind-protect
                   ,form
                 (popwin-mode (or old-popwin -1))))
          ,form)))
    
    (defun *-describe-symbol-popup ()
      "Display full documentation of symbol in a pop-up window."
      (interactive)
      (funcall (*-make-doc-command (lambda (symbol)
                                     (*-symbol-full-doc symbol))
                                   #'symbol-at-point)))
    
    (define-key help-map "j" #'*-describe-symbol-popup)
    (define-key help-map (kbd "C-j") #'*-describe-symbol-popup)
        
  • Switch to IELM buffer in other window.
    (defun *-switch-to-ielm-buffer ()
      "Interactively evaluate Emacs Lisp expressions.
    
    Switches to the buffer `*ielm*' in another window."
      (interactive)
      (require 'ielm)
      (let (old-point)
        (unless (comint-check-proc "*ielm*")
          (with-current-buffer (get-buffer-create "*ielm*")
            (unless (zerop (buffer-size)) (setq old-point (point)))
            (inferior-emacs-lisp-mode)))
        (pop-to-buffer "*ielm*")
        (when old-point (push-mark old-point))))
    
    (define-key emacs-lisp-mode-map (kbd "C-c C-z") #'*-switch-to-ielm-buffer)
        
  • Eval and pretty print in other buffer.
    (define-minor-mode *-pp-buffer-mode
      "Mode for `pp' buffers."
      :init-value nil
      :keymap '(("q" . (lambda () (interactive) (quit-restore-window)))))
    
    (defun *-pp-eval-last-sexp ()
      "Like `pp-eval-last-sexp', but better handles output buffer."
      (interactive)
      (pp-eval-expression (pp-last-sexp))
      (pop-to-buffer "*Pp Eval Output*")
      (goto-char (point-min))
      (setq buffer-read-only t)
      (*-pp-buffer-mode))
    
    (define-key emacs-lisp-mode-map (kbd "C-c C-p") #'*-pp-eval-last-sexp)
        

Emmet

  • Move cursor between first pair of quotes after expansion.
    (customize-set-variable 'emmet-move-cursor-between-quotes t)
        
  • Do not use certain keybindings.
    (with-eval-after-load 'emmet-mode
      (define-key emmet-mode-keymap (kbd "C-j") nil)
      (define-key emmet-mode-keymap (kbd "C-M-<left>") nil)
      (define-key emmet-mode-keymap (kbd "C-M-<right>") nil))
        

Eshell

  • Remove banner.
    (customize-set-variable 'eshell-banner-message "")
        
  • Function to switch Eshell directory to other window’s buffer’s default directory.
    (defun *-eshell-next-window-default-directory (&optional previous)
      "Return next window's buffer's `default-directory', if it
    exists.  Nil otherwise.
    
    If optional argument PREVIOUS is non-nil, use previous instead of
    next window."
      (let ((window (if previous
                        (previous-window nil 'no-minibuf)
                      (next-window nil 'no-minibuf))))
        (with-current-buffer (window-buffer window)
          default-directory)))
        
  • Clear Eshell buffer, preserving current input.
    (defun *-eshell-clear ()
      "Clear `eshell' buffer, comint-style."
      (interactive)
      (goto-char (point-max))
      (let ((input (eshell-get-old-input)))
        (eshell/clear-scrollback)
        (eshell-emit-prompt)
        (insert input)))
        
  • Start Eshell in other window, switching to useful directory.
    (defun *-eshell-other-window ()
      "Start Eshell in other window's buffer's `default-directory'.
    
    If current buffer is in `eshell-mode', `cd' to previous window's
    buffer's default directory."
      (interactive)
      (cl-flet ((switch-dir (dir)
                            (cd dir)
                            (*-eshell-clear)))
        (if (eq 'eshell-mode major-mode)
            (switch-dir (*-eshell-next-window-default-directory 'previous))
          (let ((dir default-directory))
            (other-window 1)
            (eshell)
            (switch-dir dir)))))
    
    (global-set-key (kbd "M-$") #'*-eshell-other-window)
        
  • Eshell initializations.
    (defun *-eshell-init ()
      "Init forms to run as part of `eshell-mode-hook'."
      (yas-minor-mode -1)
      (toggle-truncate-lines 1)
      (smartparens-mode t)
      (define-key eshell-mode-map (kbd "C-c M-o") #'*-eshell-clear)
      (define-key eshell-mode-map [remap eshell-list-history] #'helm-eshell-history)
      (define-key eshell-mode-map [remap eshell-pcomplete] #'company-manual-begin))
    
    (add-hook 'eshell-mode-hook #'*-eshell-init)
        

Expand Region

  • Use expand-region, which increases selected region by semantic units.
    (global-set-key (kbd "M-[") #'er/expand-region)
    (global-set-key (kbd "M-]") #'er/contract-region)
        

Font

  • Change the default font.
    (when (member "DejaVu Sans Mono" (font-family-list))
        (let ((height (if (string= system-type "darwin") 120 110)))
          (set-face-attribute 'default nil :font "DejaVu Sans Mono" :height height)))
        

Fill Column

  • Fill column to 78 chars.

    The width of 78 is chosen so that text centered on an 80-char-wide interface will have a column of white space on each side.

    (customize-set-variable 'fill-column 78)
        

File Files

  • Bind C-x C-l to find-file-literally instead of downcase-region, which is rarely used.
    (define-key ctl-x-map (kbd "C-l") #'find-file-literally)
        
  • Bind C-x f to find-file instead of set-fill-column, which is rarely used. Also, I hit C-x f instead of C-x C-f way too often.
    (define-key ctl-x-map "f" #'find-file)
        

Frame

  • Make frame full-screen. We cannot use toggle-frame-fullscreen because no frame has been created when the init file is loaded.
    (add-to-list 'default-frame-alist '(fullscreen . fullboth))
        
  • Set keybinding to iconify (minimize) or de-iconify frame.
    (global-set-key (kbd "s--") #'iconify-or-deiconify-frame)
        
  • Resize pixel-wise. This is particularly useful on Mac.
    (customize-set-variable 'frame-resize-pixelwise t)
        

Fringe

  • Hide fringes.
    (require 'fringe)
    (fringe-mode 0)
        
  • Give fringe same color as default face.

    This is useful for centered-window-mode.

    (set-face-background 'fringe (face-background 'default))
        

Helm

  • Configure helm, an incremental completion and selection narrowing framework. See the author’s config file as a reference.
    (helm-mode t)
    
    ;;; Let helm use current window
    (customize-set-variable 'helm-split-window-default-side 'same)
    
    ;; Auto-complete file names when finding files
    (customize-set-variable 'helm-ff-auto-update-initial-value t)
        
  • Change the prefix key for helm from C-x c to something else, because C-x c is too close to C-x C-c, which is bound to the command save-buffers-kill-terminal.
    (global-set-key (kbd "C-c h") 'helm-command-prefix)
    (global-unset-key (kbd "C-x c"))
        
  • Bind common helm commands to more accessible key sequences.
    ;; Resume helm
    (global-set-key (kbd "M-r") #'helm-resume)
    
    ;; Navigate between sources
    (define-key helm-map (kbd "C-o") #'helm-previous-source)
    (define-key helm-map (kbd "C-l") #'helm-next-source)
    
    ;; Commands
    (global-set-key (kbd "<menu>") #'helm-M-x)
    (global-set-key (kbd "M-<menu>") #'helm-run-external-command)
    (global-set-key (kbd "M-x") #'helm-M-x)
    (global-set-key (kbd "M-X") #'helm-run-external-command)
    
    ;; Buffers
    (define-key ctl-x-map "b"  #'helm-mini)
    (global-set-key [remap list-buffers] #'helm-buffers-list)
    
    ;; Files
    (global-set-key [remap find-file] #'helm-find-files)
    (define-key ctl-x-map (kbd "C-r") #'helm-recentf)
    
    ;; Search
    (global-set-key (kbd "M-i") #'helm-semantic-or-imenu)
    (global-set-key (kbd "M-I") #'helm-imenu-in-all-buffers)
    (global-set-key (kbd "M-o") #'helm-occur)
    (global-set-key (kbd "M-s f") #'helm-find)
    (global-set-key (kbd "M-s g") #'helm-do-grep-ag)
    
    ;; Apropos
    (global-set-key [remap apropos-command] #'helm-apropos)
    (global-set-key [remap describe-function] #'helm-apropos)
    (global-set-key [remap describe-variable] #'helm-apropos)
    
    ;; Regexp
    (define-key help-map "r" #'helm-regexp)
    
    ;; Source code
    (global-set-key [remap find-library] #'helm-locate-library)
    (define-key help-map "l" #'helm-locate-library)
    
    ;; Kill ring
    (global-set-key (kbd "M-y") #'helm-show-kill-ring)
    
    ;; Mark rings
    (define-key help-map (kbd "SPC") #'helm-all-mark-rings)
    
    ;; Registers
    (global-set-key [remap insert-register] #'helm-register)
    
    ;; Bookmarks
    (define-key ctl-x-r-map "b" #'helm-filtered-bookmarks)
    
    ;; Chars
    (global-set-key [remap insert-char] #'helm-ucs)
    
    ;; Evaluation
    (define-key (current-global-map) [remap eval-expression]
      #'helm-eval-expression-with-eldoc)
    
    ;; Processes
    (define-key help-map "t" #'helm-top)
        
  • Reduce sources for helm-apropos, which should improve speed.
    (customize-set-variable 'helm-apropos-function-list
                            '(helm-def-source--emacs-commands
                              helm-def-source--emacs-functions
                              helm-def-source--emacs-variables
                              helm-def-source--emacs-faces))
        
  • Turn off popwin for Help buffers when using Helm and restore it after existing Helm.

    In a two-window configuration, Helm will display the Help buffer from persistent action in the other window. Having popwin for Help buffers brings up another, pop-up Help buffer.

    (defun *-popwin-help-mode-off ()
      "Turn `popwin-mode' off for *Help* buffers."
      (when (boundp 'popwin:special-display-config)
        (customize-set-variable 'popwin:special-display-config
                                (delq 'help-mode popwin:special-display-config))))
    
    (defun *-popwin-help-mode-on ()
      "Turn `popwin-mode' on for *Help* buffers."
      (when (boundp 'popwin:special-display-config)
        (customize-set-variable 'popwin:special-display-config
                                (add-to-list 'popwin:special-display-config 'help-mode nil #'eq))))
    
    (add-hook 'helm-minibuffer-set-up-hook #'*-popwin-help-mode-off)
    (add-hook 'helm-cleanup-hook #'*-popwin-help-mode-on)
        

Helm Dash

  • Use helm-dash, a Helm interface for Dash docsets.

    To use helm-dash, first install docsets via helm-dash-install-docset. Docsets are installed to helm-dash-docsets-path, which is ~/.docsets by default.

    ;; Set `helm-dash' to `C-h d', which is normally `apropos-documentation'
    (define-key help-map "d" 'helm-dash)
    
    ;; Set minimum entry length to display docset elements
    ;; 0 facilitates discoverability, but can be slow with too many docsets
    (customize-set-variable 'helm-dash-min-length 0)
        

Helm Descbinds

  • Use helm-descbinds to present keybindings.
    (helm-descbinds-mode)
    
    ;; Show binding descriptions in same window
    (customize-set-variable 'helm-descbinds-window-style 'split-window)
        

Image

  • Fit image to window width.
    (customize-set-variable 'image-auto-resize 'fit-width)
        
  • Don’t cache images. The cache often causes Emacs to show an older version, leading to confusion.
    (customize-set-variable 'image-cache-eviction-delay 0)
        

Info

  • Use better faces.
    (set-face-attribute 'Info-quoted nil
                        :family "Monospace"
                        :inherit 'org-special-keyword
                        :slant 'italic)
        
  • Access commonly-used Info files through a hydra.
    (defhydra hydra-helm-info (:color blue)
      "
    Common Lisp                                    [_a_] all  [_d_] dir  [_r_] resume
    ^^^^^^---------------------------------------------------------------------------
    [_s_] slime (SLIME)
    
    Emacs
    ^^^^^^---------------------------------------------------------------------------
    [_e_] emacs (The Emacs Editor)
    [_i_] eintr (An Introduction to Programming in Emacs Lisp)
    [_l_] elisp (Emacs Lisp)
    [_o_] org   (Org Mode Manual)
    
    Scheme
    ^^^^^^---------------------------------------------------------------------------
    [_g_] guile (The Guile Reference Manual)
    "
      ("a" helm-info nil)
      ("d" Info-directory nil)
      ("e" helm-info-emacs nil)
      ("g" helm-info-guile nil)
      ("i" helm-info-eintr nil)
      ("l" helm-info-elisp nil)
      ("o" helm-info-org nil)
      ("r" info nil)
      ("s" helm-info-slime nil))
    
    (define-key help-map "i" #'hydra-helm-info/body)
        

JavaScript

  • Use js2-mode instead of the default js-mode.
    (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
        
  • Style configs.
    (with-eval-after-load 'js
      (customize-set-value 'js-indent-level 2))
        

Keyboard Macro

  • Use helm for keyboard macros.
    (define-key kmacro-keymap "h" #'helm-execute-kmacro)
        

LaTeX

Line and Column Numbers

  • Display column numbers.
    (column-number-mode t)
        

Macros

  • Use macrostep, an interactive macro expanding tool. Great for checking what your macro is expanding to!
    ;; `C-x e' is usually kmacro-end-and-call-macro, which we never use
    (define-key ctl-x-map "e" #'macrostep-expand)
    
    ;; Exit macrostep via macrostep-collapse-all with `C-g'
    (with-eval-after-load 'macrostep
      (define-key macrostep-keymap (kbd "C-g") #'macrostep-collapse-all))
        

Magit

  • Bind the magit-status command, which is Magit’s entry point.
    (global-set-key (kbd "C-c m") #'magit-status)
        
  • Don’t ask to save repository buffers.
    (customize-set-variable 'magit-save-repository-buffers 'dontask)
        
  • Bind magit-section-toggle to more convenient key.
    (defvar *-magit-spc-toggle-maps
      '(magit-diff-mode-map
        magit-revision-mode-map
        magit-status-mode-map
        magit-stash-mode-map)
      "`magit'-related mode maps in which `SPC' should be bound to toggle.")
    
    (with-eval-after-load 'magit
      (dolist (map *-magit-spc-toggle-maps)
        (define-key (symbol-value map) (kbd "SPC") #'magit-section-toggle)))
        

Markdown

  • Use pandoc as Markdown converter.
    (if (executable-find "pandoc")
        (customize-set-variable 'markdown-command "pandoc"))
        
  • Don’t hide URLs.
    (customize-set-variable 'markdown-hide-urls nil)
        

Menubar and Toolbar

  • Disable the menu bar, but make it toggleable, since menus can be useful for exploring new modes.
    (menu-bar-mode -1)
    
    ;; Toggle menu bar
    (global-set-key (kbd "<f10>") #'menu-bar-mode)
    
    ;; F10 was originally `menu-bar-open'; bind that to C-<f10> instead
    (global-set-key (kbd "C-<f10>") #'menu-bar-open)
        
  • Disable the toolbar.
    (tool-bar-mode 0)
        

Mode Line

  • Show time in the mode line.
    ;; Display time
    (display-time-mode t)
    
    ;; Time format
    (customize-set-variable 'display-time-string-forms
                            '((propertize (concat dayname
                                                  " " 12-hours ":" minutes " " (upcase am-pm))
                                          'help-echo (format-time-string "%a, %b %e %Y" now))))
    
    ;; Update display-time-string
    (display-time-update)
    
    ;; Remove display-time-string from global-mode-string
    (setq global-mode-string (delq 'display-time-string global-mode-string))
        
  • Show battery information in the mode line.
    (display-battery-mode t)
    
    ;; Remove battery-mode-line-string from global-mode-string
    (setq global-mode-string (delq 'battery-mode-line-string global-mode-string))
        
  • Modify mode line.

    To get the battery and time information to align on the right end of the mode line, we fill the mode line with spaces. This is adapted from Nicolas Rougier’s StackOverflow Answer on this issue.

    (defun *-mode-line-fill (reserve)
      "Return empty space using FACE and leaving RESERVE space on the right."
      (unless reserve
        (setq reserve 20))
      (when (and window-system
                 (eq 'right (get-scroll-bar-mode)))
        (setq reserve (- reserve 3)))
      (propertize " "
                  'display `((space :align-to (- (+ right right-fringe right-margin) ,reserve)))))
    
    (customize-set-variable 'mode-line-format
                            '("%e"
                              mode-line-front-space
                              mode-line-client
                              mode-line-remote
                              mode-line-mule-info
                              mode-line-modified
                              "  "
                              ;; Buffer name
                              (:propertize mode-line-buffer-identification
                                           face font-lock-builtin-face)
                              "  "
                              ;; Position
                              "%p (%l,%c)"
                              "  "
                              ;; Mode, recursive editing, and narrowing information
                              "("
                              (:propertize "%["
                                           face font-lock-warning-face)
                              mode-name
                              (:propertize "%]"
                                           face font-lock-warning-face)
                              (:eval (if (buffer-narrowed-p)
                                         (concat " "
                                                 (propertize "Narrow"
                                                             'face 'font-lock-warning-face))))
                              ")"
                              ;; Version control
                              (:eval (when vc-mode
                                       (concat " "
                                               vc-mode)))
                              ;; Miscellaneous information
                              "  "
                              mode-line-misc-info
    
                              (:eval (*-mode-line-fill (+ (length battery-mode-line-string)
                                                          1
                                                          (length display-time-string))))
                              battery-mode-line-string
                              " "
                              display-time-string))
        

Modes

  • Use helm-describe-modes, which provides a Helm interface to Emacs’s describe-mode.
    (global-set-key [remap describe-mode] #'helm-describe-modes)
        

Multiple Cursors

  • Use multiple-cursors to easily mark things.
    ;; Define C-x m prefix (C-x m is usually `compose-mail')
    (define-prefix-command '*-mc-map)
    (define-key ctl-x-map "m" '*-mc-map)
    
    ;; Globally useful
    (define-key *-mc-map "m" #'mc/mark-all-dwim)
    
    ;; Sometimes useful
    (define-key *-mc-map "i" #'mc/insert-numbers)
    (define-key *-mc-map "h" #'mc-hide-unmatched-lines-mode)
    (define-key *-mc-map "a" #'mc/mark-all-like-this)
    
    ;; Rarely useful
    (define-key *-mc-map "d" #'mc/mark-all-symbols-like-this-in-defun)
    (define-key *-mc-map "r" #'mc/reverse-regions)
    (define-key *-mc-map "s" #'mc/sort-regions)
    (define-key *-mc-map "l" #'mc/edit-lines)
    (define-key *-mc-map "\C-a" #'mc/edit-beginnings-of-lines)
    (define-key *-mc-map "\C-e" #'mc/edit-ends-of-lines)
        

Octave

  • Open files in octave-mode.
    (add-to-list 'auto-mode-alist '("\\.m\\'" . octave-mode))
        
  • Change keybindings.
    (with-eval-after-load 'octave
      (define-key octave-mode-map (kbd "C-h a") nil)
      (define-key octave-mode-map (kbd "C-c C-z") #'inferior-octave)
      (define-key inferior-octave-mode-map (kbd "C-h a") nil)
      (define-key inferior-octave-mode-map [remap inferior-octave-dynamic-list-input-ring] #'helm-comint-input-ring))
        
  • Look up Octave documentation in a pop-up.
    (defun *-octave-help (f)
      "Return docs of function F as string."
      (inferior-octave-send-list-and-digest (list (format "help ('%s');\n" f)))
      (string-join inferior-octave-output-list "\n"))
    
    (defun *-octave-doc-popup ()
      "Display documentation of an Octave function in a pop-up
    window."
      (interactive)
      (funcall (*-make-doc-command #'octave-help #'symbol-at-point)))
    
    (defun *-octave-doc-popup-on ()
      "Turn `*-octave-doc-popup' by binding it to an appropriate
    key."
      (dolist (map (list octave-mode-map inferior-octave-mode-map))
        (define-key map (kbd "C-h j") #'*-octave-doc-popup)
        (define-key map (kbd "C-h C-j") #'*-octave-doc-popup)))
    
    (add-hook 'octave-mode-hook #'*-octave-doc-popup-on)
    (add-hook 'inferior-octave-mode-hook #'*-octave-doc-popup-on)
        

Open

  • Universal open command.
    (defun *-open-at-point ()
      (interactive)
      (condition-case nil (org-open-at-point-global)
        (user-error (helm-find-files nil))))
    
    (global-set-key (kbd "C-c o") #'*-open-at-point)
        

Org

  • Use Org-suggested keybindings for global Org commands.
    (global-set-key (kbd "C-c a") #'org-agenda)
    (global-set-key (kbd "C-c c") #'org-capture)
    (global-set-key (kbd "C-c l") #'org-store-link)
    (global-set-key (kbd "C-c L") #'org-insert-link-global)
        
  • Get back old structured templates behavior, which was removed in Orgmode 9.2.
    (require 'org-tempo)
        
  • Use Helm to jump to headlines.
    (with-eval-after-load 'org
      (customize-set-variable 'helm-org-headings-fontify t)
      (define-key org-mode-map (kbd "M-i") #'helm-org-in-buffer-headings)
      (define-key org-mode-map (kbd "C-M-i") #'helm-org-parent-headings))
        
  • Move across headlines more easily.
    (with-eval-after-load 'org
      (define-key org-mode-map (kbd "M-p") #'outline-previous-visible-heading)
      (define-key org-mode-map (kbd "M-n") #'outline-next-visible-heading)
      (define-key org-mode-map (kbd "M-P") #'org-backward-heading-same-level)
      (define-key org-mode-map (kbd "M-N") #'org-forward-heading-same-level)
      (define-key org-mode-map (kbd "M-U") #'outline-up-heading))
        
  • Automatically adjust footnotes after insert or delete. Simple fn:N will be renumbered, and all footnotes will be sorted.
    (customize-set-variable 'org-footnote-auto-adjust t)
        
  • Set Org babel languages.
    (org-babel-do-load-languages 'org-babel-load-languages
                                 '((clojure . t)
                                   (emacs-lisp . t)
                                   (lisp . t)
                                   (scheme . t)))
        
  • Set Org export backends. This determines what options are available when the export framework is used.
    (customize-set-variable 'org-export-backends '(ascii html icalendar latex md))
        
  • Replace ellipsis with less annoying symbol.
    (customize-set-variable 'org-ellipsis "")
        
  • Use toc-org to automatically generate a table of contents for Org files. Useful mainly for GitHub.
    (defun *-org-insert-toc ()
      "Create table of contents (TOC) if current buffer is in
    `org-mode'."
      (when (eq major-mode 'org-mode)
        (toc-org-insert-toc)))
    
    (when (require 'toc-org nil t)
      (add-hook 'org-mode-hook #'toc-org-enable)
      (add-hook 'before-save-hook #'*-org-insert-toc))
        
  • Don’t fontify source blocks natively.
    (customize-set-variable 'org-src-fontify-natively nil)
        

Pages

  • Use page-break-lines to convert form feed characters into horizontal rules.
    (with-eval-after-load 'page-break-lines
      (dolist (mode '(clojure-mode clojurec-mode clojurescript-mode clojurex-mode))
        (cl-pushnew mode page-break-lines-modes)))
    
    (global-page-break-lines-mode)
        
  • Use helm-pages to navigate pages.
    (global-set-key (kbd "M-p") #'helm-pages)
        

PDF Tools

  • Use pdf-tools, a support library for PDF files that features fast rendering, etc. Need to install external dependencies first. Linux-only.
    (if (string= system-type "gnu/linux")
        (pdf-tools-install t nil t))
        

Performance

  • Increase garbage collection (GC) threshold to reduce frequency.

    Taken from Bozhidar Batsov’s config file.

    ;; Reduce the frequency of garbage collection by making it happen on each 50MB
    ;; of allocated data (the default is on every 0.76MB)
    (customize-set-variable 'gc-cons-threshold 50000000)
    
        

Popwin

  • Use popwin, a popup window manager for Emacs which makes you free from annoying buffers such like *Help*, *Completions*, *compilation*, etc.
    (require 'popwin)
    (popwin-mode t)
    
    ;; Set popup window height to 1/2 of frame height
    (customize-set-variable 'popwin:popup-window-height 0.5)
        

Projectile

  • Use projectile, a project interaction library, with helm-projectile.
    (projectile-global-mode t)
    (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
    
    ;; Use helm-projectile
    (customize-set-variable 'projectile-completion-system 'helm)
    (helm-projectile-on)
    
    ;; Truncate long lines with helm
    ;; Only seems to affect helm-projectile
    (customize-set-variable 'helm-truncate-lines t)
        

Python

  • Use python3 when available.
    (let ((python3-cmd (if (string= system-type "windows-nt")
                           "py"
                         "python3")))
      (when (executable-find python3-cmd)
        (customize-set-variable 'elpy-rpc-python-command python3-cmd)
        (customize-set-variable 'python-shell-interpreter python3-cmd)))
        
  • Use ELPY.
    (elpy-enable)
        
  • Remap some ELPY commands.
    (define-key elpy-mode-map (kbd "C-x C-e") #'elpy-shell-send-statement)
    (define-key elpy-mode-map (kbd "C-M-x") #'elpy-shell-send-top-statement)
    (define-key elpy-refactor-map (kbd "f") (cons (format "%sormat" (propertize "f" 'face 'bold))
                                                  #'elpy-format-code))
        
  • Use the Black formatter when available.
    (with-eval-after-load 'elpy
      (when (elpy-config--package-available-p 'black)
        (customize-set-variable 'elpy-formatter 'black)))
        

Quit

  • Unbind C-z, C-x C-z, which by default are bound to suspend-frame. No need to waste a precious C- key on this.
    (global-unset-key (kbd "C-z"))
    (global-unset-key (kbd "C-x C-z"))
        
  • Unbind C-x C-c, which is save-buffers-kill-terminal. Why would we ever want to quit Emacs?
    (global-unset-key (kbd "C-x C-c"))
        

Sass

  • Define a function to clean up a Sass file using the sass-convert tool.
    (defun *-cleanup-sass ()
      "Clean up Sass file"
      (interactive)
      (shell-command
       (format "/usr/local/bin/sass-convert %s %s"
               (shell-quote-argument (buffer-file-name))
               (shell-quote-argument (buffer-file-name))))
      (revert-buffer t t t))
        

Scheme

  • Specify Scheme program name.
    (customize-set-variable 'scheme-program-name "guile")
        
  • Function to run inferior Scheme process in other window.
    (defun *-run-scheme-other-window ()
      "Run Scheme inferior process in other window"
      (interactive)
      (other-window 1)
      (run-scheme scheme-program-name))
        
  • Run Inferior Scheme in other window immediately after entering scheme-mode.
    (add-hook 'scheme-mode-hook (lambda () (save-selected-window
                                             (*-run-scheme-other-window))))
    
    ;; Replace switch-to-scheme with *-run-scheme-other-window
    (with-eval-after-load 'scheme
      (define-key scheme-mode-map (kbd "C-c C-z") #'*-run-scheme-other-window))
        

Scratch Buffer

  • Inhibit scratch buffer text.
    (customize-set-variable 'initial-scratch-message "")
        

Scroll

  • Disable the scrollbar.
    (require 'scroll-bar)
    (scroll-bar-mode 0)
        
  • Use convenient binding for scroll-other-window-down.
    (global-set-key (kbd "C-M-y") #'scroll-other-window-down)
        

Search and Replace

  • Prefer regexp versions of search and replace functions.
    ;; Search
    (global-set-key (kbd "C-s") #'isearch-forward-regexp)
    (global-set-key (kbd "C-r") #'isearch-backward-regexp)
    
    ;; Replace
    (global-set-key (kbd "M-%") #'query-replace-regexp)
        

Sexp

  • Bind C-M-2 to mark-sexp, because C-M-@ can be hard to reach on certain keyboards.
    (global-set-key (kbd "C-M-2") #'mark-sexp)
        
  • Use custom pair insertion commands.
    ;; Without an argument, `insert-parentheses' inserts a pair of parentheses at
    ;; point. We don't need that since we use `smartparens', so make wrapping next
    ;; sexp the default behavior.
    
    (defun *-insert-pair (&optional arg open close)
      (interactive "p")
      (setq arg (or arg 1))
      (save-excursion
        (condition-case nil
            (forward-sexp)
          (scan-error (setq arg nil))))
      (insert-pair arg open close))
    
    (defun *-insert-parentheses (&optional arg)
      (interactive "p")
      (*-insert-pair arg ?\( ?\)))
    
    (defun *-insert-brackets (&optional arg)
      (interactive "p")
      (*-insert-pair arg ?\[ ?\]))
    
    (defun *-insert-braces (&optional arg)
      (interactive "p")
      (let ((parens-require-spaces nil))
        (*-insert-pair arg ?\{ ?\})))
    
    (global-set-key (kbd "C-M-9") #'*-insert-parentheses)
    (global-set-key (kbd "M-ESC [") #'*-insert-brackets)
    (global-set-key (kbd "C-M-{") #'*-insert-braces)
        
  • Use custom move-past-close-and-reindent
    (defun *-move-past-close-and-reindent ()
      "Like `move-past-close-and-reindent', but insert a pair of
    parentheses."
      (interactive)
      (move-past-close-and-reindent)
      (insert-parentheses))
    
    (defun *-move-past-close-and-reindent-brackets ()
      "Like `move-past-close-and-reindent', but inserts a pair of
      brackets."
      (interactive)
      (move-past-close-and-reindent)
      (*-insert-brackets))
    
    (defun *-move-past-close-and-reindent-braces ()
      "Like `move-past-close-and-reindent', but inserts a pair of
      braces."
      (interactive)
      (move-past-close-and-reindent)
      (*-insert-braces))
    
    (global-set-key (kbd "C-M-0") #'*-move-past-close-and-reindent)
    (global-set-key (kbd "C-M-]") #'*-move-past-close-and-reindent-brackets)
    (global-set-key (kbd "C-M-}") #'*-move-past-close-and-reindent-braces)
        

SGML

  • Use emmet-mode for all markup.
    (add-hook 'sgml-mode-hook #'emmet-mode)
        
  • View markup buffer in browser.
    (with-eval-after-load 'sgml-mode
      (define-key sgml-mode-map (kbd "C-c w u") #'browse-url-of-buffer))
        

Shell

  • In shell-mode, use helm-comint-input-ring for comint-dynamic-list-input-ring, which lists the input history.
    (define-key comint-mode-map
      [remap comint-dynamic-list-input-ring] #'helm-comint-input-ring)
        

Smartparens

  • Use smartparens, a minor mode for Emacs that deals with parens pairs and tries to be smart about it.
    (require 'smartparens-config)
    
    ;; Use smartparens
    (smartparens-global-mode t)
    
    ;; Use show-smartparens, which highlights matched pairs
    (show-smartparens-global-mode)
    
    ;; Use smartparens-strict-mode for certain modes
    (mapc (lambda (hook)
              (add-hook hook #'smartparens-strict-mode))
            '(markdown-mode-hook
              prog-mode-hook
              scss-mode-hook))
        
  • Keybinding for smartparens, taken from the author’s config file.
    ;; Foward/backward
    (defun *-forward-sexp ()
      "Move forward sexp, depending on major mode."
      (interactive)
      (pcase major-mode
        (`clojure-mode (clojure-forward-logical-sexp))
        (_ (sp-forward-sexp))))
    
    (defun *-backward-sexp ()
      "Move backward sexp, depending on major mode."
      (interactive)
      (pcase major-mode
        (`clojure-mode (clojure-backward-logical-sexp))
        (_ (sp-backward-sexp))))
    
    (define-key smartparens-mode-map (kbd "C-M-f") #'*-forward-sexp)
    (define-key smartparens-mode-map (kbd "C-M-b") #'*-backward-sexp)
    
    ;; Up/down
    (define-key smartparens-mode-map (kbd "C-M-d") #'sp-down-sexp)
    (define-key smartparens-mode-map (kbd "C-M-e") #'sp-up-sexp)
    (define-key smartparens-mode-map (kbd "C-M-a") #'sp-backward-down-sexp)
    (define-key smartparens-mode-map (kbd "C-M-q") #'sp-backward-up-sexp)
    (define-key smartparens-mode-map (kbd "C-M-`") #'beginning-of-defun)
    
    ;; Transpose
    (define-key smartparens-mode-map (kbd "C-M-t") 'sp-transpose-sexp)
    
    ;; Mark/kill/copy
    (global-set-key [remap mark-sexp] #'sp-mark-sexp)
    (define-key smartparens-mode-map (kbd "C-M-k") #'sp-kill-sexp)
    (define-key smartparens-mode-map (kbd "C-M-w") #'sp-copy-sexp)
    
    ;; Unwrap
    (define-key smartparens-mode-map (kbd "M-<delete>") #'sp-unwrap-sexp)
    (define-key smartparens-mode-map (kbd "M-<backspace>") #'sp-backward-unwrap-sexp)
    
    ;; Slurp/barf
    (define-key smartparens-mode-map (kbd "C-<right>") #'sp-forward-slurp-sexp)
    (define-key smartparens-mode-map (kbd "C-<left>") #'sp-forward-barf-sexp)
    (define-key smartparens-mode-map (kbd "C-S-<left>") #'sp-backward-slurp-sexp)
    (define-key smartparens-mode-map (kbd "C-S-<right>") #'sp-backward-barf-sexp)
    
    (define-key smartparens-mode-map (kbd "C-M-<backspace>") #'sp-splice-sexp-killing-backward)
    (define-key smartparens-mode-map (kbd "C-S-<backspace>") #'sp-splice-sexp-killing-around)
    
    ;; Indent
    (define-key smartparens-mode-map (kbd "C-M-<tab>") #'sp-indent-defun)
        

Spelling

  • Use flyspell with text-mode and its derivatives, and flyspell-prog-mode with programming modes.
    (defun *-flyspell-mode-setup ()
      "Hook function for `flyspell-mode'."
      (customize-set-variable 'flyspell-auto-correct-binding (kbd "C-;"))
      (flyspell-mode)
      (with-eval-after-load 'flyspell
        (define-key flyspell-mode-map (kbd "C-M-i") nil)))
    
    (defun *-flyspell-prog-mode-setup ()
      "Hook function for `flyspell-prog-mode'."
      (customize-set-variable 'flyspell-auto-correct-binding (kbd "C-'"))
      (flyspell-prog-mode)
      (with-eval-after-load 'flyspell
        (define-key flyspell-mode-map (kbd "C-M-i") nil)))
    
    (add-hook 'text-mode-hook #'*-flyspell-mode-setup)
    (add-hook 'prog-mode-hook #'*-flyspell-prog-mode-setup)
        

Splash Screen

  • Inhibit the splash screen.
    (customize-set-variable 'inhibit-splash-screen t)
        

Tabs

  • Use 4 spaces by default.
    (setq-default tab-width 4)
        

Terminal Mode

  • Do not use yasnippet in term-mode.
    (add-hook 'term-mode-hook (lambda ()
                                (yas-minor-mode -1)))
        

Theme

  • Use theme.
    (load-theme 'zenburn t)
        
  • Eliminate vertical-border by giving it the same color as default face.
    (set-face-foreground 'vertical-border (face-background 'default))
        
  • Use a better face for eldoc-highlight-function-argument.
    (custom-set-faces '(eldoc-highlight-function-argument ((t (:inherit font-lock-variable-name-face)))))
        

Toggle

  • Keys for toggling modes. Inspired by article from Endless Parentheses.
    ;; Toggle common modes
    (defhydra hydra-toggle (:color amaranth)
      "
    Appearance                                                         [_q_] quit
    ^^---------------------------------------------------------------------------
    [_r_] rainbow-mode:               %s(if (boundp 'rainbow-mode) rainbow-mode 'nil)
    [_w_] whitespace-mode:            %s(if (boundp 'whitespace-mode) whitespace-mode 'nil)
    
    Debug
    ^^---------------------------------------------------------------------------
    [_d_] debug-on-error:             %`debug-on-error
    
    Editing
    ^---------------------------------------------------------------------------
    [_%_] read-only-mode:             %`buffer-read-only
    [_f_] auto-fill-mode:             %`auto-fill-function
    [_s_] smartparens-mode:           %`smartparens-mode
    [_S_] smartparens-strict-mode:    %`smartparens-strict-mode
    [_t_] toggle-truncate-lines:      %`truncate-lines
    [_v_] visual-line-mode:           %`visual-line-mode
    
    Expansion
    ^^---------------------------------------------------------------------------
    [_y_] yas-global-mode:            %`yas-global-mode
    
    Spelling and Syntax
    ^^---------------------------------------------------------------------------
    [_c_] flycheck-mode:              %s(if (boundp 'flycheck-mode) flycheck-mode 'nil)
    [_p_] flyspell-mode:              %s(if (boundp 'flyspell-mode) flyspell-mode 'nil)
    [_P_] flyspell-prog-mode:         %s(if (boundp 'flyspell-prog-mode) flyspell-prog-mode 'nil)
    "
      ("%" read-only-mode nil)
      ("c" flycheck-mode nil)
      ("d" toggle-debug-on-error nil)
      ("f" auto-fill-mode nil)
      ("p" flyspell-mode nil)
      ("P" flyspell-prog-mode nil)
      ("r" rainbow-mode nil)
      ("s" smartparens-mode nil)
      ("S" smartparens-strict-mode nil)
      ("t" toggle-truncate-lines nil)
      ("v" visual-line-mode nil)
      ("w" whitespace-mode nil)
      ("q" nil nil :color blue)
      ("y" yas-global-mode nil))
    
    (define-key ctl-x-map "t" #'hydra-toggle/body)
        

Whitespace Cleanup

  • Use spaces, not tabs, by default.
    (customize-set-variable 'indent-tabs-mode nil)
        
  • Clean up whitespace in the buffer before saving.
    (add-hook 'before-save-hook #'whitespace-cleanup)
        

Window Management

  • Split the frame into two windows, left and right, if the frame is maximized or fullscreen.

    For some reason, simply adding our function to after-make-frame-functions does not work for the intial frame. It does work when using emacsclient, because the Emacs daemon always creates an invisible frame. We therefore make an explicit call to the function to make sure it gets called.

    See this article from EmacsNinja for more information.

    (defun *-split-window (&optional frame)
      "Split the current frame into two windows horizontally."
      (with-selected-frame (or frame
                               (selected-frame))
        (split-window-horizontally)
        (other-window 1)))
    
    ;; Add function to after-make-frame-functions, which works only when using
    ;; emacsclient
    (add-hook 'after-make-frame-functions #'*-split-window)
    
    ;; Call function to split window explicitly if not using emacsclient
    (if (not (daemonp))
        (*-split-window))
        
  • Function to rotate buffers between windows in the current frame. When there are two windows (which is most of the time), swap the buffers in the windows. The cursor remains in the same buffer.
    (defun *-rotate-buffers-in-windows (&optional arg)
      "Rotate buffers displayed in windows by ARG windows.
    
    If ARG is positive, rotate ARG windows clockwise. If ARG is negative,
    rotate -ARG windows counterclockwise. Nil defaults to 1 window.
    Raw prefix argumnt \\[universal-argument] defaults to -1.
    
    By 'clockwise', we mean that if windows W1, W2, W3 are displaying
    buffers B1, B2, and B3 respectively, then the result of calling
    this function without an argument is that W1 <- B3, W2 <- B1, W3
    <- B2."
      (interactive "P")
      (require 'dash)
      (unless (minibufferp)
        (let* ((windows (window-list))
               (numrot (cond ((consp arg) -1)
                             (t (mod (prefix-numeric-value arg)
                                     (length windows))))))
          (cl-mapcar (lambda (window buffer)
                       (set-window-buffer window buffer))
                     windows
                     (mapcar #'window-buffer
                             (-rotate numrot windows)))
          (select-window (nth numrot windows)))))
        
  • Hydra for working with windows.
    (defhydra hydra-window (:color pink)
      "
    Windows                                                             [_q_] quit
    ^------------------------------------------------------------------------------
    [_c_]     ^^center
    [_-_]/[_=_] vertical shrink / enlarge
    [_[_]/[_]_] horizontal shrink / enlarge
    [_b_]     ^^balance
    [_o_]/[_O_] point to other window / reverse
    [_r_]/[_R_] rotate buffers / counterclockwise
    [_v_]/[_V_] vertical split / delete
    "
    ("[" (shrink-window-horizontally 5) nil)
    ("]" (enlarge-window-horizontally 5) nil)
    ("-" (shrink-window 3) nil)
    ("=" (enlarge-window 3) nil)
    ("b" balance-windows nil)
    ("c" (centered-window-mode-toggle) nil)
    ("o" (other-window 1) nil)
    ("O" (other-window -1) nil)
    ("r" *-rotate-buffers-in-windows nil)
    ("R" (*-rotate-buffers-in-windows -1) nil)
    ("v" split-window-vertically nil)
    ("V" delete-other-windows-vertically nil)
    ("q" nil nil))
    
    (define-key ctl-x-map "w" #'hydra-window/body)
        

YASnippet

  • Use yasnippet, a template system.
    (yas-global-mode t)
        
  • Use whitespace-mode when writting snippets for YASnippet.
    (add-hook 'snippet-mode-hook #'whitespace-mode)
        

Microsoft Windows

Microsoft Windows specific configurations. These should only apply when Emacs is being used on a Windows system.

Keybindings

  • The <menu> key on Linux is referred to as the <apps> key on Windows. We bind helm-M-x to this key.
    (if (string= system-type "windows-nt")
        (global-set-key (kbd "<apps>") #'helm-M-x))
        

Mac OSX

Keybindings

  • Use Command keys as Meta instead of Super.
    (when (string= system-type "darwin")
      (customize-set-variable 'mac-command-modifier 'meta)
      (customize-set-variable 'mac-option-modifier nil))
        

License

My Emacs configurations written in Org mode.

Copyright (c) 2014-2017 Tianxiang Xiong

This program 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 program 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.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.