quinoa42's dotEmacs

7420 words 35 min read

Here is a list of blogs / configurations that I’ve grabbed code from or absorbed ideas from. All of them worth reading.

This file might be out of sync. The newest version is available here.

General Settings

Coding and Languages

Use Utf-8 as the default coding system.

(set-language-environment "UTF-8")
(prefer-coding-system 'utf-8-unix)

Tangle init.el

When there is no init.el tangled yet, run C-c on this block to generate the first init.el.

(let ((src (expand-file-name "readme.org" user-emacs-directory))
      (dst (expand-file-name "init.el" user-emacs-directory)))
  (when (file-newer-than-file-p src dst)
    (call-process (concat invocation-directory invocation-name)
                  nil nil t
                  "-q" "--batch" "--eval" "(require 'ob-tangle)"
                  "--eval" (format "(org-babel-tangle-file \"%s\" \"%s\" 'emacs-lisp)" src dst)))
  ;; (require 'bytecomp)
  ;; (byte-recompile-file dst nil 0 t)
  )

Some helpful defaults

It is reasonable to just turn off menu bar because I want to go mouse-less.

(menu-bar-mode -1)
(column-number-mode 1)

I don’t know if there is any who would like the wierd tab-space-hybird indent mode.

(setq-default indent-tabs-mode nil)

If this is set to nil, hard link to the edited file will point to the backup file after backup process.

(setq backup-by-copying t)

Version-controlled backup:

(setq version-control t
      delete-old-versions t
      kept-new-versions 6
      kept-old-versions 2)

4-space indentation:

(setq-default tab-width 4)
(defvaralias 'cperl-indent-level 'tab-width)

General programming set up:

(add-hook 'prog-mode-hook
          (lambda ()
            (setq truncate-lines t)
            (display-line-numbers-mode 1)))

Turn off lockfiles. They cannot be moved to a different directory, and they consistently screw up with file watchers and version control systems, so it’d be just easier to turn this feature off.

(setq create-lockfiles nil)

A helper function that create a filename from basename and extension:

(defun my/filename-with-extension (base ext)
  "build a filename with basename and extension."
  (let ((base (if base base ""))
        (ext (if ext ext "")))
    (pcase (list base ext)
      (`("" "") (throw 'illegal t))
      (`("" ,e) e)
      (`(,b "") b)
      (`(,b ,e) (format "%s.%s" b e)))))

Ignore more boring files/directories:

(push ".ccls-cache/" completion-ignored-extensions)
(push "tags" completion-ignored-extensions)
(push "GPATH" completion-ignored-extensions)
(push "GRTAGS" completion-ignored-extensions)
(push "GTAGS" completion-ignored-extensions)

Emacs as a server

One of the most disadvantage of Emacs compared with Vim is its startup time. Running Emacs as a server improves this to a really usable level.

(require 'server)
(unless (server-running-p)
  (server-start))

Platform Specific

This part contains code specific to platforms, usually UI or PATH related.

MacOS

I don’t have specific code for Mac yet.

Windows

Said to be a way to make Emacs faster on windows (by running GC less often)

(when (eq system-type 'windows-nt)
  (setq gc-cons-threshold (* 512 1024 1024))
  (setq gc-cons-percentage 0.5)
  (run-with-idle-timer 5 t #'garbage-collect))

Also, the default font for Chinese slow down Emacs terribly on Windows:

(when (eq window-system 'w32)
  (dolist (charset '(kana han cjk-misc bopomofo))
    (set-fontset-font t charset (font-spec :family "Microsoft Yahei"))))

Always prefer msys2 libraries:

(when (eq window-system 'w32)
  (setenv "PATH" (concat "C:\\msys64\\mingw64\\bin;" (getenv "PATH"))))

Linux

I don’t have specific code for Linux yet.

GUI

Turn off blink cursor, scroll bar and tool bar:

(ignore-errors
  (blink-cursor-mode -1)
  (scroll-bar-mode -1)
  (tool-bar-mode -1))

Setting faces. Technically this should be done in the TRUE branch for all cases, but (x-*-fonts) functions are not available in TUI Emacs, including systemd-started Emacs daemon.

(set-face-attribute 'default nil :height 140)
(set-face-attribute 'variable-pitch nil :weight 'normal :inherit 'default)
(if (display-graphic-p)
    (progn ;; TRUE: try fallback
      (cond
       ((x-family-fonts "Latin Modern Math")        (set-face-attribute 'variable-pitch nil :family "Latin Modern Math"))
       ((x-list-fonts   "Lucida Grande")            (set-face-attribute 'variable-pitch nil :font   "Lucida Grande"))
       ((x-list-fonts   "Verdana")                  (set-face-attribute 'variable-pitch nil :font   "Verdana"))
       ((x-family-fonts "Sans Serif")               (set-face-attribute 'variable-pitch nil :family "Sans Serif"))
       (nil (warn "Cannot find a Sans Serif Font.")))
      (cond
       ((x-family-fonts "DejaVuSansMono Nerd Font") (set-face-attribute 'default nil        :family "DejaVuSansMono Nerd Font"))
       ((x-family-fonts "Consolas")                 (set-face-attribute 'default nil        :family "Consolas"))
       ((x-family-fonts "Monospace")                (set-face-attribute 'default nil        :family "Monospace"))
       (nil (warn "Cannot find a Mono Font."))))
  (progn ;; FALSE: hard coded based on system
    (when (eq system-type 'gnu/linux)
      (set-face-attribute 'default nil        :family "DejaVuSansMono Nerd Font")
      (set-face-attribute 'variable-pitch nil :family "Latin Modern Math"))
    (when (eq system-type 'windows-nt)
      (set-face-attribute 'default nil :font "Consolas")
      (set-face-attribute 'variable-pitch nil :family "Microsoft Yahei"))))
(set-face-attribute 'fixed-pitch nil :inherit 'default)

TUI

I don’t have specific code for TUI yet.

Package Management

Proxy, Archives and Mirrors

I use my local proxy to speed up paradox:

(setq url-proxy-services '(("no_proxy" . "^\\(localhost\\|10\\..*\\|192\\.168\\..*\\)")
                           ("http" . "127.0.0.1:8118")
                           ("https" . "127.0.0.1:8118")))

And also, set package archive to include ELPA, MELPA and org’s, with TUNA’s mirror:

(setq package-archives '(("gnu"   . "http://mirrors.tuna.tsinghua.edu.cn/elpa/gnu/")
                         ("melpa" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/melpa/")
                         ("org" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/org/")))

Initialization

From package-initialize help page:

If called as part of loading ‘user-init-file’, set ‘package-enable-at-startup’ to nil, to prevent accidentally loading packages twice.

Thus, I use the following block to load packages:

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

Use-package

use-package is a wonderful package configuration helper (and not a package manager!). If it is not available in the current system, use M-x package-install RET use-package RET to fetch it from MELPA.

Now load use-package:

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

Also, I’d like to turn on use-package's statistic recording to see if my config is correctly set up:

(setq use-package-compute-statistics t)

Paradox

Paradox is a modernized Emacs’ package menu that supports displaying GitHub stars along the entries, filtering and sorting, async installation, etc.

I disabled the GitHub integration because it doesn’t seem to work on my machines.

(use-package paradox
  :ensure t
  :commands paradox-list-packages
  :custom
  (paradox-github-token t)
  (paradox-automatically-star nil)
  :config
  (paradox-enable))

Miscs

Xdg

Start from Emacs 26, we hav buit-in suport for xdg. Then it will be reasonble to put those annoying but extremely helpful stuff into xdg cache:

(use-package xdg
  :config
  (let ((backup-dir (expand-file-name "emacs/backup/" (xdg-data-home)))
        (autosave-dir (expand-file-name "emacs/autosave/" (xdg-cache-home))))
    (mkdir backup-dir t)
    (mkdir autosave-dir t)
    (setq backup-directory-alist
          `(("." . ,backup-dir)))
    (setq auto-save-file-name-transforms
          `((".*" ,autosave-dir t)))))

EasyPG

From EmacsWiki:

EasyPG is an all-in-one GnuPG interface for Emacs.

Note that easy-pg consists of two different kind of modules, one is a library (epg.el) and the others are applications (epa-*.el). Auto-encryption (epa-file.el) is a part of the latter. As the docs says “The EasyPG Library dares to disable passphrase caching”, that is intended behavior. Caveat user, if you start using the library directly. ;)

Helpful references, including the Emacs Wiki page where the above quotes are from:

(use-package epa-file
  :config
  (epa-file-enable))

Bookmark

Emacs has its own bookmark system built-in:

(use-package bookmark
  :defer t
  :init
  (setq bookmark-default-file (expand-file-name "emacs/bookmarks" (xdg-data-home))))

Recentf

recentf (also a built-in) saves recent file list.

(use-package recentf
  :defer t
  :init
  (setq recentf-save-file (expand-file-name "emacs/recentf" (xdg-cache-home))))

Editorconfig

editorconfig is a very handy tool that standardize how different editors should behave according to different language, including tab width, trailing space and so on. It is not only helpful for team to maintain a codestyle standard, but also a handful tool for people use several different editors / computers, like I do.

editorconfig-emacs implements its own editorconfig core, so It’s logical to assume that it works on any platform.

(use-package editorconfig
  :ensure t
  :config
  (editorconfig-mode 1))

Colors

I’m currently using the emacs port of my vim era favorite, gruvbox:

(use-package gruvbox-theme
  :ensure t
  :config
  (load-theme 'gruvbox t))

Add / custom the hi-lock-faces (‘cause #000000 is too hard to read on my screen):

(use-package hi-lock
  :commands hi-lock-mode
  :custom-face
  (hi-black-b               ((t (:background "#fdf4c1"))))
  (hi-black-hb              ((t (:background "#fabd2f" :height 1.0))))
  :init
  (defface hi-purple-b
    '((t (:foreground "#d3869b" :weight bold)))
    "Face for hi-lock mode"
    :group 'hi-lock-faces)
  :config
  (push "hi-purple-b" hi-lock-face-defaults))

Key Bindings

References:

  1. Keybindings strategies in Emacs by Samuel Barreto.

Which-key

which-key is a minor mode that hints you the keybindings prefixed with what you have typed when you get stucked.

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

General

A general is a leader. – onioncheese

General is a, well, general-purpose key-binding interface for emacs.

There is not much config for general yet, besides enable it I simply turn on its Evil integration:

(use-package general
  :ensure t
  :after which-key
  :config
  (general-evil-setup)
  (general-create-definer my/leader-def
    ;; :prefix my-leader
    :prefix "C-c")
  )

Hercules

An auto-magical, which-key based hydra banisher. … If only there was a way to make a hydra without having to list all the bindings explicitly… Kind of like =which-key=…

Did you get the idea what hercules is used for? neither do I (at the first glance)! In short, by using the power of which-key, hercules can build a hydra when given a keymap.

(use-package hercules
  :ensure t
  :after general)

Evil

It’s name tells everything: the Extensible Vi Layer for Emacs, Evil. It works pretty well as a Vim simulation, much better than VsCode’s or Intellij’s. Besides, it is charming combination of Vim’s model-based editing with Emacs’ keymap system, to some extent, as a personal opinion, better than the native Vim on the model-based editing system.

References:

(use-package evil
  :ensure t
  :demand t
  :init
  (general-setq evil-disable-insert-state-bindings t)
  (general-setq evil-respect-visual-line-mode t)
  (general-setq evil-search-module 'evil-search)
  (general-setq evil-search-wrap nil)
  (defmacro my/indented-paste (paste-func)
    `(lambda (count &optional register yank-handler)
       (interactive "*P<x>")
       (evil-with-single-undo
         (,paste-func count register yank-handler)
         (evil-indent (alist-get ?\[ evil-markers-alist)
                      (alist-get ?\] evil-markers-alist)))))
  (general-setq evil-want-integration t)
  (general-setq evil-want-keybinding nil)
  :general
  ([remap evil-emacs-state] 'evil-normal-state)
  (general-nmap "C-j" 'evil-window-down)
  (general-nmap "C-k" 'evil-window-up)
  (general-nmap "C-h" 'evil-window-left)
  (general-nmap "C-l" 'evil-window-right)
  ([remap evil-ex-search-forward] 'swiper)
  ([remap evil-ex-search-backward] 'swiper-backward)
  (when (eq system-type 'gnu/linux)
    (general-nvmap "s-s" (general-simulate-key "M-s"))
    (general-nvmap "s-x" (general-simulate-key "M-x")))
  (general-nvmap "SPC" (general-simulate-key "C-c"))
  (my/leader-def '(normal visual)
    "SPC" (general-simulate-key "C-c C-c"))
  (my/leader-def 'normal
    "o" '(my/insert-line-below :which-key t)
    "O" '(my/insert-line-above :which-key t))
  (general-nmap "C-d" (general-simulate-key "C-x 5"))
  (general-nmap "C-x 5 q" 'delete-frame)
  (general-nmap "C-x 5 o" 'delete-other-frames)
  (general-nmap "C-x 5 C-d" 'other-frame)
  (general-nmap "C-x 5 s" 'make-frame-command)
  (general-nmap "C-x 5 v" 'make-frame-command)
  (general-nmap "C-x 5 g d" 'xref-find-definitions-other-frame)
  (general-vmap "." (general-simulate-key ":normal . RET"))
  (general-vmap "Q" (general-simulate-key ":normal @q RET"))
  (general-nmap "gp" (general-simulate-key "`[v`]"))
  (general-nmap "[p" (my/indented-paste evil-paste-before))
  (general-nmap "]p" (my/indented-paste evil-paste-after))
  :config
  (evil-mode 1))

I don’t want to use Vim’s insert mode bindings in insert state:

(general-setq evil-disable-insert-state-bindings t)

Then, given that I’m using emacs’ bindings in insert state, there is no point to use the emacs state which is so hard to escape from:

([remap evil-emacs-state] 'evil-normal-state)

vim-tmux-navigator-like window control:

(general-nmap "C-j" 'evil-window-down)
(general-nmap "C-k" 'evil-window-up)
(general-nmap "C-h" 'evil-window-left)
(general-nmap "C-l" 'evil-window-right)

It’s okay to rebind C-h because <F1> basically does the same thing.

swiper has a nice evil integration such that / ? can be replaced with 'swiper 'swiper-backward respectively:

([remap evil-ex-search-forward] 'swiper)
([remap evil-ex-search-backward] 'swiper-backward)

My UHK’s keybinding is designed for using with Vim, so my Alt is really far far away from where I would normally position my fingers. Since I did not bind S-s S-x in my xmonad config, it’s reasonable to bind these to their Meta relatives:

(when (eq system-type 'gnu/linux)
  (general-nvmap "s-s" (general-simulate-key "M-s"))
  (general-nvmap "s-x" (general-simulate-key "M-x")))

When visual-line-mode is set (especially in org-mode), I want Vim to behave as visual lines are normal lines (i.e. bind j to gj etc)

(general-setq evil-respect-visual-line-mode t)

Somehow n/N only jump backward with swiper unless this is set:

(general-setq evil-search-module 'evil-search)

Wrapped search is terrible when you want to go through all instances.

(general-setq evil-search-wrap nil)

I use C-c as my leader:

(general-create-definer my/leader-def
  ;; :prefix my-leader
  :prefix "C-c")

This is a trick where I remap SPC to C-c, my leader key. In this way, I wrapped all the mode-defined C-c prefixed keybindings (they are not supposed to do so btw) as if they were my leader key prefixed, so that when I type SPC, which-key will show up all of them.

(general-nvmap "SPC" (general-simulate-key "C-c"))
(my/leader-def '(normal visual)
  "SPC" (general-simulate-key "C-c C-c"))

Bring my two of my old vim keybindings back, which open newline above/below current line without entering insert state:

(defun my/insert-line-below ()
  "Insert an empty line below the current line."
  (interactive)
    (end-of-line)
    (open-line 1)
    (next-line))

(defun my/insert-line-above ()
  "Insert an empty line above the current line."
  (interactive)
    (end-of-line 0)
    (open-line 1)
    (next-line))

I use <leader>o and <leader>O for them because they are close to o and O respectively:

(my/leader-def 'normal
  "o" '(my/insert-line-below :which-key t)
  "O" '(my/insert-line-above :which-key t))

Use C-d as frame key, mimics C-w as window key. d stands for display. Hope this could persuade people and myself. I choose C-d because C-u is (heavily) used by Emacs itself by default and Evil as a result did not bind C-u by default, so it’s reasonable to not have Vim’s C-d functionality alone.

(general-nmap "C-d" (general-simulate-key "C-x 5"))
(general-nmap "C-x 5 q" 'delete-frame)
(general-nmap "C-x 5 o" 'delete-other-frames)
(general-nmap "C-x 5 C-d" 'other-frame)
(general-nmap "C-x 5 s" 'make-frame-command)
(general-nmap "C-x 5 v" 'make-frame-command)
(general-nmap "C-x 5 g d" 'xref-find-definitions-other-frame)

Run . on all selected lines in visual mode:

(general-vmap "." (general-simulate-key ":normal . RET"))

Run @q on all selected lines in visual mode:

(general-vmap "Q" (general-simulate-key ":normal @q RET"))

Get ]p and [p back, which basically paste stuff while making them indented with current lines. I missed them so much! Also, add the common gp binding which visually select the last pasted content. I have a macro for this:

(defmacro my/indented-paste (paste-func)
  `(lambda (count &optional register yank-handler)
     (interactive "*P<x>")
     (evil-with-single-undo
       (,paste-func count register yank-handler)
       (evil-indent (alist-get ?\[ evil-markers-alist)
                    (alist-get ?\] evil-markers-alist)))))

Their bindings:

(general-nmap "gp" (general-simulate-key "`[v`]"))
(general-nmap "[p" (my/indented-paste evil-paste-before))
(general-nmap "]p" (my/indented-paste evil-paste-after))

evil-collection

evil-collection is a collection of helper functions / settings / etc for things native Evil does bad on.

(use-package evil-collection
  :after evil
  :ensure t
  :custom
  (evil-collection-setup-minibuffer t)
  :config
  (evil-collection-init 'compile)
  (evil-collection-init 'info)
  (evil-collection-init 'custom)
  (evil-collection-init 'dired)
  (evil-collection-init 'minibuffer)
  (evil-collection-init 'helm)
  (evil-collection-init 'flycheck)
  (evil-collection-init 'xref)
  (evil-collection-init 'magit)
  (evil-collection-init 'which-key))

evil-collection assumes evil-want-keybinding is set to nil and evil-want-integration is set to t before loading evil and evil-collection.

(general-setq evil-want-integration t)
(general-setq evil-want-keybinding nil)

evil-easymotion

evil-easymotion is a Evil port of Vim’s, well, easymotion, which basically works in a way that instead of numbering how many jumps needed, by prefixing motions with a leader key, we use visual hint to go to the place we want. I hadn’t tried easymotion during my Vim era though, but I like it now. It also provides integration with evil-snipe.

(use-package evil-easymotion
  :ensure t
  :demand t
  :after (evil evil-snipe)
  :general
  (evil-snipe-parent-transient-map
   "SPC"
   (evilem-create 'evil-snipe-repeat
                  :bind ((evil-snipe-scope 'buffer)
                         (evil-snipe-enable-highlight)
                         (evil-snipe-enable-incremental-highlight))))
  (my/leader-def 'motion
    "j" (evilem-create 'next-line)
    "j" '(:ignore t :which-key t)
    "k" (evilem-create 'previous-line)
    "k" '(:ignore t :which-key t)
    "e" '(evilem-motion-forward-word-end :which-key t)
    "E" '(evilem-motion-forward-WORD-end :which-key t)
    "g e" '(evilem-motion-backward-word-end :which-key t)
    "g E" '(evilem-motion-backward-WORD-end :which-key t)
    "w" '(evilem-motion-forward-word-begin :which-key t)
    "W" '(evilem-motion-forward-WORD-begin :which-key t)
    "b" '(evilem-motion-backward-word-begin :which-key t)
    "B" '(evilem-motion-backward-WORD-begin :which-key t)
    "n" '(evilem-motion-search-next :which-key t)
    "N" '(evilem-motion-search-previous :which-key t)
    "g" '(:ignore t :which-key t)))

evil-snipe

evil-snipe is a Evil port of Vim’s clever-f and vim-sneak. It currently does not support separating the scope for f/F/t/T from for s/S, which is a little bit annoying.

(use-package evil-snipe
  :ensure t
  :demand t
  :after evil
  :general
  (general-vmap evil-snipe-local-mode-map "z" 'evil-snipe-s)
  (general-vmap 'visual evil-snipe-local-mode-map "Z" 'evil-snipe-S)
  :hook (magit-mode . turn-off-evil-snipe-override-mode)
  :custom
  (evil-snipe-scope 'visible)
  (evil-snipe-repeat-scope 'whole-visible)
  (evil-snipe-spillover-scope 'whole-buffer)
  :config
  (evil-snipe-mode +1)
  (evil-snipe-override-mode +1))
[evil-find-char-pinyin](https://github.com/cute-jumper/evil-find-char-pinyin) is a helper plugin that allow `evil-snipe` to search for Chinese characters with their initial pinyins. For example, with this plugin `smt` could find 明天.

This plugin actually works for native Evil's `f/F/t/T`, but I use it mainly for its integration with `evil-snipe`'s motions.

```emacs-lisp
(use-package evil-find-char-pinyin
  :ensure t
  :after (evil evil-snipe)
  :config
  (evil-find-char-pinyin-toggle-snipe-integration t)
  (evil-find-char-pinyin-mode +1))
```

evil-args

evil-args defines a new textobj for function arguments, and some other helpful functions.

(use-package evil-args
  :ensure t
  :general
  (evil-inner-text-objects-map "," 'evil-inner-arg)
  (evil-outer-text-objects-map "," 'evil-outer-arg)
  (general-nmap "]," 'evil-forward-arg)
  (general-nmap "[," 'evil-backward-arg)
  (general-mmap "]," 'evil-forward-arg)
  (general-mmap "[," 'evil-backward-arg)
  (general-nmap "go" 'evil-jump-out-args))

evil-visualstar

evil-visualstar allow using * # on all visual selection.

(use-package evil-visualstar
  :ensure t
  :after evil
  :config
  (global-evil-visualstar-mode))

evil-matchit

evil-matchit is the port of, well, matchit. It also provides two text objects, namely a% and i%.

(use-package evil-matchit
  :after evil
  :ensure t
  :config
  (global-evil-matchit-mode 1))

evil-lion

evil-lion defines an alignment operator.

(use-package evil-lion
  :ensure t
  :general
  (general-nvmap "ga" 'evil-lion-left)
  (general-nvmap "gA" 'evil-lion-right))

evil-replace-with-register

evil-replace-with-register defines a rplace operator.

(use-package evil-replace-with-register
  :ensure t
  :general
  (general-nvmap "_" 'evil-replace-with-register)
  (general-nvmap "_" 'evil-replace-with-register))

evil-numbers

evil-numbers takes c-a back (and can be mapped to different states!).

(use-package evil-numbers
  :ensure t
  :after evil
  :general
  (general-nvmap "C-a" 'evil-numbers/inc-at-pt)
  (general-nvmap "C-S-a" 'evil-numbers/dec-at-pt))

evil-surround

evil-surround defines operators that change/add/delete delimiters around a text object.

(use-package evil-surround
  :ensure t
  :after evil
  :config
  (global-evil-surround-mode 1))

evil-string-inflection

evil-string-inflection provides an operator that toggle a textobj between PascalCase, camalcase, dash-case, snake_case and SYMBOL_CASE. Unfortunately it does not provide a way to disable the default bindings, so I have to unbind it manually.

(use-package evil-string-inflection
  :ensure t
  :after evil
  :general
  (general-unbind 'normal "g~")
  (general-nmap "g~" 'evil-invert-case)
  (general-nmap "g-" 'evil-operator-string-inflection))

evil-commentary

evil-commentary defines operators for commenting.

(use-package evil-commentary
  :ensure t
  :after evil
  :config
  (evil-commentary-mode))

evil-textobj-line

evil-textobj-line defines text objects for a single line.

(use-package evil-textobj-line
  :ensure t
  :after evil)

evil-textobj-indent

evil-indent-plus defines text objects for block of code that has same/higher indentation.

(use-package evil-indent-plus
  :ensure t
  :after evil
  :general
  (evil-inner-text-objects-map "i" 'evil-indent-plus-i-indent)
  (evil-outer-text-objects-map "i" 'evil-indent-plus-a-indent)
  (evil-inner-text-objects-map "I" 'evil-indent-plus-i-indent-up)
  (evil-outer-text-objects-map "I" 'evil-indent-plus-a-indent-up)
  (evil-inner-text-objects-map "J" 'evil-indent-plus-i-indent-up-down)
  (evil-outer-text-objects-map "J" 'evil-indent-plus-a-indent-up-down))

Spell Checking

Emacs comes with its own spell checking mode (ispell.el)…

(use-package ispell
  :if (eq system-type 'gnu/linux)
  :init
  (general-setq ispell-program-name "aspell"))

… and its own on-the-fly spell checker(flyspell, which uses ispell.el as the backend).

(use-package flyspell
  :if (eq system-type 'gnu/linux)
  :hook
  (text-mode . flyspell-mode)
  (prog-mode . flyspell-prog-mode))

UI Enhance

u/GummyKibble has a concise and wise comment on the comparison between Helm and Ivy:

…[T]o me, Helm feels like a replacement for the Emacs UI I’m used to, while Ivy feels like a refinement of it.

For me Helm fits me better because:

  1. I’m new to Emacs anyway, there is no such Emacs UI that I’m used to.
  2. During my Vim era I use Shougo’s wonderful plugins Unite/Denite, which mimics the logic of Helm, so switching to Emacs with Helm mostly does not require switching my mind model for how to find things.
  3. Ivy is new compared with Helm, so it does not have as many add-ons available as Helm.

Helm

Helm is a generic incremental completion and selection narrowing framework for Emacs, as what Denite is for [Neo]vim. I currently does not set Helm to be auto-installed, so just install it with M-x package-install RET helm RET.

References:

(use-package helm-config
  :demand t
  :general
  ([remap find-file]                'helm-find-files)
  ([remap occur]                    'helm-occur)
  ([remap list-buffers]             'helm-buffers-list)
  ([remap dabbrev-expand]           'helm-dabbrev)
  ([remap execute-extended-command] 'helm-M-x)
  ([remap imenu]                    'helm-imenu)
  (general-nmap "gO" 'helm-semantic-or-imenu)
  :init
  (general-setq helm-display-function #'helm-display-buffer-in-own-frame)
  (general-setq helm-command-prefix-key "C-c h")
  (general-setq helm-ff-skip-boring-files t)
  (unless (boundp 'completion-in-region-function)
    (general-def lisp-interaction-mode-map [remap completion-at-point] 'helm-lisp-completion-at-point)
    (general-def emacs-lisp-mode-map       [remap completion-at-point] 'helm-lisp-completion-at-point)))

Turn on helm

(use-package helm-mode
  :config
  (helm-mode 1))

swiper-helm

swiper-helm is a Helm version of swiper. That is, it use Helm as the backend instead of Ivy.

(use-package swiper-helm
  :ensure t
  :after (helm-config swiper)
  :general ("C-s" 'swiper-helm))

helm-gtags

emacs-helm-gtags is a helm interface for GNU GLOBAL.

(use-package helm-gtags
  :disabled
  :hook
  ((c-mode c++-mode asm-mode) . helm-gtags-mode)
  :general
  (general-nmap "C-]" 'helm-gtags-dwim)
  (general-nmap "gd" 'helm-gtags-dwim)
  (general-nmap "gR" 'helm-gtags-select)
  (general-nmap "gr" 'helm-gtags-tags-in-this-function)
  (general-nmap "C-t" 'helm-gtags-previous-history)
  (general-nmap "C-S-t" 'helm-gtags-next-history))

helm and ag/rg

helm-do-grep-ag supports using ag and rg by itself, so technically helm-rg and helm-ag are not necessary. But, helm-rg is used by helm-projectile for some reason (and there seems no way to delegate it to use helm-do-grep-ag, thus

(use-package helm-rg
  :if (executable-find "rg")
  :ensure t
  :init
  (progn
    (general-setq helm-grep-ag-command "rg --color=always --colors 'match:fg:black' --colors 'match:bg:yellow' --smart-case --no-heading --line-number %s %s %s")
    (general-setq helm-grep-ag-pipe-cmd-switches '("--colors 'match:fg:black'" "--colors 'match:bg:yellow'"))))

and also fallback to helm-ag if rg is not available:

(use-package helm-ag
  :if (and (executable-find "ag") (not (executable-find "rg")))
  :ensure t
  :init
  (progn
    (general-setq helm-grep-ag-command "ag --line-numbers -S --hidden --color --color-match '31;43' --nogroup %s %s %s")
    (general-setq helm-grep-ag-pipe-cmd-switches '("--color-match '31;43'"))))

Ivy

ivy is yet another generic incremental completion for Emacs.

I don’t use heavily on ivy anymore, but I still have it because its the dependency of swiper:

(use-package ivy
  :custom
  (ivy-count-format "(%d/%d) " "the style for displaying current candidate count")
  ;; (enable-recursive-minibuffers t "allow minibuffer cmd in minibuffer")
  )

;; (use-package counsel
;;   :ensure t
;;   :requires ivy
;;   )

;; (use-package ivy-rich
;;   :ensure t
;;   :requires ivy
;;   :init
;;   (setcdr (assq t ivy-format-functions-alist) #'ivy-format-function-line)
;;   :config
;;   (ivy-rich-mode 1))

swiper

swiper is an alternative to Emacs’ builtin isearch. I use this over other alternatives because it has better integration by default with Evil’s (or Vim’s) search/substitution system.

(use-package swiper
  :ensure t
  :demand t
  :after ivy
  :general
  (general-imap ivy-minibuffer-map "C-p" 'ivy-previous-line)
  (general-imap ivy-minibuffer-map "C-n" 'ivy-next-line)
  :commands (swiper swiper-backward))

highlight-indent-guides

highlight-indent-guides shows indent guides (with font lock)!

(use-package highlight-indent-guides
  :ensure t
  :hook (prog-mode . highlight-indent-guides-mode)
  :init
  (general-setq highlight-indent-guides-responsive 'top)
  (general-setq highlight-indent-guides-method 'character)
  (general-setq highlight-indent-guides-character ?│))

Undo-tree

undo-tree provides a visualization for the undo history. It is a prereq for Evil.

(use-package undo-tree
  :ensure t
  :demand t
  :init
  (general-setq undo-tree-visualizer-timestamps nil)
  (general-setq undo-tree-visualizer-lazy-drawing t)
  (general-setq undo-tree-visualizer-relative-timestamps nil)
  :general
  (general-mmap undo-tree-visualizer-mode-map
    "t" 'undo-tree-visualizer-toggle-timestamps)
  (my/leader-def 'normal
    "u" 'undo-tree-visualize)
  :config
  (global-undo-tree-mode +1))

ggtags

GNU GLOBAL is a source code tagging system that recognize references and that can also use ctags as a backend. Using universal ctags, it would be like this:

gtags --gtagslabel=new-ctags

ggtags is an Emacs interface to GLOBAL. Different from helm-gtags, it integrates into Emacs’ own ecosystem like xref and eldoc.

(use-package ggtags
  :ensure t
  :hook
  ((c-mode c++-mode asm-mode) . ggtags-mode)
  :general
  (general-nmap "gs" 'ggtags-find-other-symbol)
  :init
  (general-setq ggtags-extra-args (list "--gtagslabel=new-ctags")))

Treemacs

treemacs is a tree layout explorer for files and many things else (tags, for example). It provides integration into many other popular packages in the ecosystem.

(use-package treemacs
  :ensure t
  :defer t
  :general
  (my/leader-def 'normal
    "t r" 'treemacs
    "t b" 'treemacs-bookmark
    "t f" 'treemacs-find-file
    "t t" 'treemacs-find-tag)
  (evil-treemacs-state-map
    "C-j" 'evil-window-down
    "C-k" 'evil-window-up
    "C-h" 'evil-window-left
    "C-l" 'evil-window-right)
  :init
  (general-setq treemacs-persist-file (expand-file-name "emacs/treemacs-persist" (xdg-data-home)))
  (general-setq treemacs-last-error-persist-file (expand-file-name "emacs/treemacs-persist-at-last-error" (xdg-cache-home)))
  :config
  (treemacs-filewatch-mode +1)
  (treemacs-follow-mode 0)
  (pcase (cons (not (null (executable-find "git")))
               (not (null treemacs-python-executable)))
    (`(t . t)
     (treemacs-git-mode 'extended))
    (`(t . _)
     (treemacs-git-mode 'simple))))

Its evil integration:

(use-package treemacs-evil
  :ensure t
  :after (treemacs evil))

Telephone-line

Reference:

  1. Configuration.org
  2. examples.org
  3. telephone-line-segments.org
(use-package telephone-line
  :ensure t
  :init
  (general-setq telephone-line-lhs
                '((evil . (telephone-line-evil-tag-segment))
                  (accent . (telephone-line-vc-segment
                             telephone-line-erc-modified-channels-segment
                             telephone-line-process-segment))
                  (nil . (telephone-line-projectile-segment
                          telephone-line-buffer-segment))))
  (general-setq telephone-line-rhs
                '((nil . (telephone-line-flycheck-segment
                          telephone-line-misc-info-segment))
                  (accent . (telephone-line-major-mode-segment))
                  (evil (telephone-line-airline-position-segment))))
  :custom-face
  (telephone-line-evil-normal      ((t (:inherit telephone-line-evil :foreground "#d5c4a1" :background "#665c54"))))
  (telephone-line-evil-insert      ((t (:inherit telephone-line-evil :foreground "#282828" :background "#83a598"))))
  (telephone-line-evil-replace     ((t (:inherit telephone-line-evil :foreground "#282828" :background "#8ec07c"))))
  (telephone-line-evil-visual      ((t (:inherit telephone-line-evil :foreground "#282828" :background "#fe8019"))))
  (telephone-line-evil-operator    ((t (:inherit telephone-line-evil :foreground "#282828" :background "#fabd2f"))))
  (telephone-line-evil-emacs       ((t (:inherit telephone-line-evil :foreground "#282828" :background "#d3869b"))))
  (telephone-line-evil-motion      ((t (:inherit telephone-line-evil :foreground "#282828" :background "#b8bb26"))))
  (telephone-line-accent-inactive  ((t (:inherit mode-line-inactive  :foreground "#ebdbb2" :background "#282828"))))
  (telephone-line-accent-active    ((t (:inherit mode-line           :foreground "#ebdbb2" :background "#282828" :weight bold))))
  (telephone-line-projectile       ((t (:inherit mode-line           :foreground "#83a598" :weight     bold))))
  (telephone-line-unimportant      ((t (:inherit mode-line           :foreground "#7c6f64"))))
  :config
  (telephone-line-mode +1))

with-editor

with-editor ensure child processes of Emacs know how to call Emacs.

(use-package with-editor
  :ensure t
  :general
  ([remap async-shell-command] 'with-editor-async-shell-command)
  ([remap shell-command] 'with-editor-shell-command)
  :hook
  (shell-mode . with-editor-export-editor)
  (term-exec  . with-editor-export-editor)
  (eshell-mode . with-editor-export-editor))

Origami

origami provides code folding for Emacs, and is also an optional dependencies for Evil’s zo-ish family.

(use-package origami
  :ensure t
  :hook (prog-mode . origami-mode))

Completion

By completion I mean general text/code autocompletion, as Vim’s deoplete.

Company

company seems the most widely-used text completion framework among the Emacs ecosystem.

(use-package company
  :ensure t
  :demand t
  :init
  (general-setq company-auto-complete t)
  (general-setq company-auto-complete-chars '(32 40 41 46))
  (general-setq company-require-match nil)
  (general-setq company-idle-delay 0.6)
  :general
  (general-unbind company-active-map
    "M-n"
    "M-p")
  (company-active-map "C-n" 'company-select-next
                      "C-p" 'company-select-previous
                      "C-h" 'company-quickhelp-manual-begin
                      "ESC" 'company-abort)
  (general-imap
    "C-n" 'company-select-next
    "C-p" 'company-select-previous)
  :config
  (global-company-mode))

Company-quickhelp

company-quickhelp is an add-on for company that make use of popup-el, which will show doc for current selected entry in a popup view.

(use-package company-quickhelp
  :unless (display-graphic-p)
  :ensure t
  :after company
  :init
  (general-setq company-quickhelp-delay nil)
  :config
  (company-quickhelp-mode))

Company-box

company-box is a frontend for company that does not use popup (thus it doesn’t screw up with different font size) and show icons for each candidate.

(use-package company-box
  :ensure t
  :after company
  :hook
  (company-mode . (lambda () (when (display-graphic-p) (company-box-mode)))))

Templates

Templates are always good time savers. Reference: Having Emacs Type for You by Howard Abrams

Yasnippet

yasnippet is a template system for Emacs.

(use-package yasnippet
  :ensure t
  :demand t
  :general
  (general-unbind yas-minor-mode-map
    "C-c & C-n"
    "C-c & C-s"
    "C-c & C-v"
    "C-c &"
    "C-c")
  ;; (my/leader-def
  ;;   :states '(normal visual insert)
  ;;   :keymaps 'yas-minor-mode-map
  ;;   "y n" 'yas-new-snippet
  ;;   "y s" 'yas-insert-snippet
  ;;   "y v" 'yas-visit-snippet-file)
  :config
  (yas-global-mode +1))

I don’t want the default yas-minor-mode-map's C-c & bindings because they conflict with org-marking-goto. Thus I unbind & rebind them into <leader> y. NOTE: I’m using yankpad now instead of directly using yasnippet, so I commented these bindings out.

(general-unbind yas-minor-mode-map
  "C-c & C-n"
  "C-c & C-s"
  "C-c & C-v"
  "C-c &"
  "C-c")
;; (my/leader-def
;;   :states '(normal visual insert)
;;   :keymaps 'yas-minor-mode-map
;;   "y n" 'yas-new-snippet
;;   "y s" 'yas-insert-snippet
;;   "y v" 'yas-visit-snippet-file)

Yankpad

yankpad is a cool package that expand snippets written in org mode and optionally use yasnippet as the backend.

(use-package yankpad
  :ensure t
  :demand t
  :init
  (general-setq yankpad-file (expand-file-name "yankpad.org" user-emacs-directory))
  :general
  (my/leader-def '(normal visual)
    "y" 'yankpad-insert)
  (my/leader-def 'insert
    "y" 'yankpad-expand))

Auto-insert

(defun my/auto-insert-yankpad()
  "replace buffer content with expanded yankpad snippet."
  (let ((str (substring (buffer-substring-no-properties (point-min) (point-max))
                       0 -1)))
    (erase-buffer)
    (yankpad-insert-from-current-category str)))

(use-package autoinsert
  :init
  ;; Don't want to be prompted before insertion:
  (setq auto-insert-query nil)
  (setq auto-insert-directory (locate-user-emacs-file "templates"))
  :config
  (define-auto-insert "\\.h$" ["header.h" my/auto-insert-yankpad])
  (auto-insert-mode 1))

Code

xref

xref is an Emacs built-in cross referencing browsing package.

This file provides a somewhat generic infrastructure for cross referencing commands, in particular “find-definition”.

(use-package xref
  :init
  (general-setq xref-prompt-for-identifier nil)
  :general
  (general-nmap "gr" 'xref-find-references)
  (general-nmap "C-w ]" 'xref-find-definitions-other-window)
  (general-nmap "C-w C-]" 'xref-find-definitions-other-window)
  (general-nmap "C-w g C-]" 'xref-find-definitions-other-window)
  (general-nmap "C-w g ]" 'xref-find-definitions-other-window))

Projectile

projectile is a project interaction library for Emacs.

(use-package projectile
  :defer 10
  :ensure t
  :commands (projectile-mode projectile-command-map)
  :general
  (my/leader-def 'normal
    "p" 'projectile-command-map)
  :init
  (general-setq projectile-cache-file (expand-file-name "emacs/projectile.cache" (xdg-cache-home)))
  (general-setq projectile-known-projects-file (expand-file-name "emacs/projectile-bookmarks.eld" (xdg-data-home)))
  (general-setq projectile-globally-ignored-directories (append '(".ccls-cache") projectile-globally-ignored-directories))
  :config
  (projectile-mode +1))

Replace the wierd default interface with helm by using helm-projectile

(use-package helm-projectile
  :after projectile
  :ensure t
  :config
  (helm-projectile-on))

Its treemacs integration that provides a helper function to add projectile projects to treemacs:

(use-package treemacs-projectile
  :ensure t
  :after treemacs projectile)

flycheck

flycheck is a async syntax checking framework for Emacs, as ALE for Vim.

(use-package flycheck
  :ensure t
  :custom
  (flycheck-keymap-prefix (kbd "C-c *"))
  :config
  (global-flycheck-mode +1))

lsp-mode

lsp-mode is Emacs’ client/library for the Language Server Protocol. It integrates with Emacs’ ecosystem heavily.

(use-package lsp-mode
  :ensure t
  :hook
  ((python-mode rust-mode) . lsp-deferred)
  :commands (lsp lsp-deferred)
  :init
  (general-setq lsp-enable-indentation nil)
  (general-setq lsp-keymap-prefix "C-c L")
  (general-setq lsp-prefer-flymake nil)
  (general-setq lsp-session-file (expand-file-name "emacs/lsp-session-v1" (xdg-cache-home)))
  (general-setq lsp-keep-workspace-alive nil)
  (general-setq lsp-auto-guess-root t))

lsp-ui

lsp-ui is the high-level UI module for lsp-mode.

Based on this PR, there is no need to set up anything because lsp-mode will load lsp-ui itself.

(use-package lsp-ui
  :ensure t
  :commands lsp-ui-mode
  :init
  (general-setq lsp-ui-doc-enable nil)
  (add-hook 'lsp-ui-mode-hook
            (lambda ()
              (general-setq-local evil-lookup-func #'lsp-ui-doc-glance))))

company-lsp

company-lsp, as the name suggests, is the company backend for lsp-mode.

(use-package company-lsp
  :ensure t
  :after (lsp-mode company)
  :config
  (push 'company-lsp company-backends))

lsp-treemacs

lsp-treemacs display lsp-mode linting and symbols in a tree structure, using treemacs as the frontend.

(use-package lsp-treemacs
  :ensure t
  :after (lsp-mode treemacs))

helm-lsp

helm-lsp provides two helper helm commands for lsp-mode that list workspace symbols.

(use-package helm-lsp
  :ensure t
  :after lsp-mode)

lsp-origami

lsp-origami provides support for origami using language server protocol’s textDocument/foldingRange functionality.

(use-package lsp-origami
  :ensure t
  :hook ((c-mode c++-mode asm-mode) . lsp-origami-mode))

Org Mode

From its website

Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.

this is only a facial overall summary of what org-mode is usually used for. It is so powerful that It is one of the reasons I switched from Neovim to Emacs.

Emacs shipped with a relatively old version of org-mode, but many MELPA packages depends on the nightly version, thus I’ll usually get the newest one from its own archive via package-install.

(use-package org
  :pin org
  :ensure t
  :demand t
  :hook
  (org-mode . visual-line-mode)
  (org-mode . variable-pitch-mode)
  :init
  (general-setq org-startup-indented t)
  (general-setq org-enforce-todo-dependencies t)
  (general-setq org-log-done 'note)
  (general-setq org-reverse-note-order t)
  (general-setq org-todo-keywords
                '((sequence "TODO(t!)" "ENGAGE(e!)" "WAIT(w@/@)" "|" "DONE(d@)")
                  ("|" "CANCELED(c@)")))
  (general-setq org-log-into-drawer t)
  (general-setq org-log-refile t)
  (general-setq org-directory "~/documents/tracking")
  (general-setq org-agenda-file-regexp "\\`[^.].*\\.org\\(\\.gpg\\)?\\'")
  (general-setq org-agenda-files (quote ("~/documents/tracking")))
  (general-setq org-default-notes-file "~/documents/tracking/inbox.org.gpg")
  (general-setq org-archive-location "~/documents/archive/%s_archive::* Archived")
  (general-setq org-refile-targets (quote (("maybe.org.gpg" :level . 1)
                                           ("tasks.org.gpg" :level . 1))))
  (general-setq org-capture-templates
                '(("t" "Todo" entry (file+headline "inbox.org.gpg" "Todos")
                   "* TODO %?\n:LOGBOOK:\n- with context %a at %U\n:END:\n")
                  ("b" "Board" entry (file+headline "~/documents/board/board.org" "Uncategorized")
                   "* %?\n:PROPERTIES:\n:URL: %^C\n:END:\n:LOGBOOK:\n- Entered at %U\n:END:\n")
                  ("n" "Note" entry (file+headline "inbox.org.gpg" "Notes")
                   "* %?\n:LOGBOOK:\n- Entered at %U\n:END:\n%x")))
  (general-setq org-agenda-compact-blocks t)
  (general-setq org-agenda-todo-ignore-scheduled 'future)
  (general-setq org-agenda-custom-commands
        '(("n" "Getting things done!"
           ((todo "ENGAGE")
            (agenda "current week"
             ((org-agenda-entry-types '(:deadline :timestamp :sexp))
              (org-agenda-files '("~/documents/tracking"))))
            (todo "TODO")
            (todo "WAIT"))
           ((org-agenda-files '("~/documents/tracking/tasks.org.gpg"))))
          ("i" "Inbox & Maybe"
           ((todo "TODO|WAIT"
                  ((org-agenda-files '("~/documents/tracking/inbox.org.gpg"))))
            (agenda "this two week"
                    ((org-agenda-span 14)))
            (todo "TODO|WAIT"
                  ((org-agenda-files '("~/documents/tracking/maybe.org.gpg")))))
           ((org-agenda-sorting-strategy '(tag-up priority-down))
            (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))
          ("g" . "Todolist")
          ("gt" "Todos"
           ((tags-todo "+CATEGORY=\"Todo\"")
            (agenda "this two week"
                    ((org-agenda-span 14))))
           ((org-agenda-sorting-strategy '(tag-up priority-down))
            (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))
          ("gl" "Learn"
           ((tags-todo "+CATEGORY=\"Learn\"")
            (agenda "this two week"
                    ((org-agenda-span 14))))
           ((org-agenda-sorting-strategy '(tag-up priority-down))
            (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))
          ("ge" "Entertainments"
           ((tags-todo "+CATEGORY=\"Entertain\"")
            (agenda "this two week"
                    ((org-agenda-span 14))))
           ((org-agenda-sorting-strategy '(tag-up priority-down))
            (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))))
  (use-package calendar
    :init
    (general-setq calendar-chinese-all-holidays-flag t)
    (general-setq holiday-hebrew-holidays nil)
    (general-setq holiday-islamic-holidays nil)
    (general-setq holiday-solar-holidays nil)
    (general-setq holiday-bahai-holidays nil))
  (general-setq org-hide-emphasis-markers t)
  (general-setq org-src-fontify-natively t)
  (general-setq org-tags-column 0)
  (general-setq org-pretty-entities nil)
  (general-setq org-format-latex-options (plist-put org-format-latex-options :scale 3.0))
  (defface org-level-9
        '((t (:inherit org-level-8 :height 1.0 :foreground "#83a598")))
        "Face used for level 9 headlines."
        :group 'org-faces)
  (defface org-level-10
        '((t (:inherit org-level-8 :height 1.0 :foreground "#fabd2f")))
        "Face used for level 10 headlines."
        :group 'org-faces)
  (defface org-level-11
        '((t (:inherit org-level-8 :height 1.0 :foreground "#d3869b")))
        "Face used for level 11 headlines."
        :group 'org-faces)
  (defface org-level-12
        '((t (:inherit org-level-8 :height 1.0 :foreground "#fb4933")))
        "Face used for level 12 headlines."
        :group 'org-faces)
  (general-setq org-level-faces (append org-level-faces (list 'org-level-9 'org-level-10 'org-level-11 'org-level-12)))
  (general-setq org-n-level-faces (length org-level-faces))
  (general-setq org-cycle-level-faces nil)
  :general
  (my/leader-def 'normal
    "l"  'org-store-link
    "a"  'org-agenda
    "c"  'org-capture)
  :custom-face
  (org-level-8               ((t (:inherit variable-pitch :weight bold))))
  (org-level-7               ((t (:inherit org-level-8))))
  (org-level-6               ((t (:inherit org-level-8))))
  (org-level-5               ((t (:inherit org-level-8))))
  (org-level-4               ((t (:inherit org-level-8 :height 1.1))))
  (org-level-3               ((t (:inherit org-level-8 :height 1.25))))
  (org-level-2               ((t (:inherit org-level-8 :height 1.5))))
  (org-level-1               ((t (:inherit org-level-8 :height 1.75))))
  (org-document-title        ((t (:inherit org-level-8 :height 2.0 :underline nil))))
  (org-block                 ((t (:inherit fixed-pitch))))
  (org-document-info         ((t (:foreground "dark orange"))))
  (org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
  (org-link                  ((t (:underline t))))
  (org-meta-line             ((t (:inherit (font-lock-comment-face fixed-pitch)))))
  (org-property-value        ((t (:inherit fixed-pitch))))
  (org-table                 ((t (:inherit fixed-pitch))))
  (org-block-begin-line      ((t (:inherit fixed-pitch :weight bold))))
  (org-block-end-line        ((t (:inherit fixed-pitch :weight bold))))
  (org-special-keyword       ((t (:inherit (font-lock-comment-face fixed-pitch)))))
  (org-drawer                ((t (:inherit (font-lock-comment-face fixed-pitch)))))
  (org-tag                   ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
  (org-checkbox              ((t (:inherit fixed-pitch :weight bold))))
  (org-verbatim              ((t (:inherit (shadow fixed-pitch)))))
  (org-code                  ((t (:inherit (shadow fixed-pitch)))))
  (org-indent                ((t (:inherit (org-hide fixed-pitch)))))
  :config
  (hercules-def
   :show-funs #'org-babel-enter
   :hide-funs '(org-babel-exit org-babel-tangle)
   :keymap 'org-babel-map
   :transient t)
  (general-def org-mode-map "C-c C-v" #'org-babel-enter)
  (general-def :prefix-map 'org-babel-map
              "q" #'org-babel-exit)
  )

general settings

Turn on org-indent, aka clean view by default:

(general-setq org-startup-indented t)

Enforce to-do dependencies (i.e. children block their parent)

(general-setq org-enforce-todo-dependencies t)

I found that usually I have something to say when I closing a task, for example a link to the reproduction note. Thus I’d like to have closing note by default.

(general-setq org-log-done 'note)

Put newer note at the top:

(general-setq org-reverse-note-order t)

general keybindings

Global keybindings as recommended in Org Manual:

(my/leader-def 'normal
  "l"  'org-store-link
  "a"  'org-agenda
  "c"  'org-capture)

And of course, org-mode's Evil integration:

(use-package evil-org
  :ensure t
  :after (org evil)
  :hook
  (org-mode . (lambda () (evil-org-mode 1)))
  (org-agenda-mode . (lambda () (evil-org-mode 1)))
  :general
  (org-src-mode-map [remap evil-write] 'org-edit-src-save)
  :commands org-agenda
  :config
  (evil-org-set-key-theme)
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys))

Here are some evil-specific bindings:

(org-src-mode-map [remap evil-write] 'org-edit-src-save)

task management

I generally follow the GTD way as my task management system. reference:

Todo state keywords. The todo state is simple:

(general-setq org-todo-keywords
              '((sequence "TODO(t!)" "ENGAGE(e!)" "WAIT(w@/@)" "|" "DONE(d@)")
                ("|" "CANCELED(c@)")))

Log into a LOGBOOK drawer so that things are folded when we want to read about outcome descriptions

(general-setq org-log-into-drawer t)

When refiling, log down a timestamp:

(general-setq org-log-refile t)

Now finally, org-directory that will be used to look for capture templates:

(general-setq org-directory "~/documents/tracking")

And the list of files/directories I want my agenda to track:

(general-setq org-agenda-file-regexp "\\`[^.].*\\.org\\(\\.gpg\\)?\\'")
(general-setq org-agenda-files (quote ("~/documents/tracking")))

Notice that I also includes all *.org.gpg files, because I’d like to sync my GTD files around, encrypted.

And a default note file for templates that does not specify a target file:

(general-setq org-default-notes-file "~/documents/tracking/inbox.org.gpg")

Archive things into an archive directory:

(general-setq org-archive-location "~/documents/archive/%s_archive::* Archived")

As GTD requires, I want to refile elements to maybe list or next action list:

(general-setq org-refile-targets (quote (("maybe.org.gpg" :level . 1)
                                         ("tasks.org.gpg" :level . 1))))

Caputre templates that helps reduce boilerplate:

(general-setq org-capture-templates
              '(("t" "Todo" entry (file+headline "inbox.org.gpg" "Todos")
                 "* TODO %?\n:LOGBOOK:\n- with context %a at %U\n:END:\n")
                ("b" "Board" entry (file+headline "~/documents/board/board.org" "Uncategorized")
                 "* %?\n:PROPERTIES:\n:URL: %^C\n:END:\n:LOGBOOK:\n- Entered at %U\n:END:\n")
                ("n" "Note" entry (file+headline "inbox.org.gpg" "Notes")
                 "* %?\n:LOGBOOK:\n- Entered at %U\n:END:\n%x")))
(general-setq org-agenda-compact-blocks t)

I schedule things in a sense that I don’t want to think about it until that time. If something is “going to happen” at some time and I need to prepare for it, I directly use angle-braced timestamp. Thus this setting help me fulfill this need:

(general-setq org-agenda-todo-ignore-scheduled 'future)

To use GTD styles in my org-mode workflow, it’s necessary to have those agenda views:

(general-setq org-agenda-custom-commands
      '(("n" "Getting things done!"
         ((todo "ENGAGE")
          (agenda "current week"
           ((org-agenda-entry-types '(:deadline :timestamp :sexp))
            (org-agenda-files '("~/documents/tracking"))))
          (todo "TODO")
          (todo "WAIT"))
         ((org-agenda-files '("~/documents/tracking/tasks.org.gpg"))))
        ("i" "Inbox & Maybe"
         ((todo "TODO|WAIT"
                ((org-agenda-files '("~/documents/tracking/inbox.org.gpg"))))
          (agenda "this two week"
                  ((org-agenda-span 14)))
          (todo "TODO|WAIT"
                ((org-agenda-files '("~/documents/tracking/maybe.org.gpg")))))
         ((org-agenda-sorting-strategy '(tag-up priority-down))
          (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))
        ("g" . "Todolist")
        ("gt" "Todos"
         ((tags-todo "+CATEGORY=\"Todo\"")
          (agenda "this two week"
                  ((org-agenda-span 14))))
         ((org-agenda-sorting-strategy '(tag-up priority-down))
          (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))
        ("gl" "Learn"
         ((tags-todo "+CATEGORY=\"Learn\"")
          (agenda "this two week"
                  ((org-agenda-span 14))))
         ((org-agenda-sorting-strategy '(tag-up priority-down))
          (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))
        ("ge" "Entertainments"
         ((tags-todo "+CATEGORY=\"Entertain\"")
          (agenda "this two week"
                  ((org-agenda-span 14))))
         ((org-agenda-sorting-strategy '(tag-up priority-down))
          (org-agenda-entry-types '(org-agenda-entry-types '(:deadline :timestamp :sexp)))))))

holidays and anniversaries

Given that org-mode show a day/month/year agenda view depending on my choice, why not also show those important days on the view?

Emacs has built-in calendar mode and diary mode, according to Org mode manual, it’s good enough to add this snippet to my GTD org file:

* Holidays
  :PROPERTIES:
  :CATEGORY: Holiday
  :END:
%%(org-calendar-holiday)   ; special function for holiday names

calendar mode needs some configuration to fit my needs:

(use-package calendar
  :init
  (general-setq calendar-chinese-all-holidays-flag t)
  (general-setq holiday-hebrew-holidays nil)
  (general-setq holiday-islamic-holidays nil)
  (general-setq holiday-solar-holidays nil)
  (general-setq holiday-bahai-holidays nil))

babel

References:

I declared a hydra for org-babel to make things easier:

(hercules-def
 :show-funs #'org-babel-enter
 :hide-funs '(org-babel-exit org-babel-tangle)
 :keymap 'org-babel-map
 :transient t)
(general-def org-mode-map "C-c C-v" #'org-babel-enter)
(general-def :prefix-map 'org-babel-map
            "q" #'org-babel-exit)

ob-dot

org-babel integration with dot language, i.e the language used in graphviz:

(use-package ob-dot
  :after org
  :init
  (add-to-list
   'org-src-lang-modes '("dot" . graphviz-dot-mode)))

ob-plantuml

org-babel integration with plantuml.

(use-package ob-plantuml
  :after org
  :init
  (when (eq system-type 'gnu/linux)
    (general-setq org-plantuml-jar-path "/usr/share/java/plantuml/plantuml.jar"))
  (add-to-list
   'org-src-lang-modes '("plantuml" . plantuml)))

ob-ditaa

ditaa basically turns your ascii art into the corresponding fancy diagrams, especially those that cannot be expressed using plantuml.

(use-package ob-ditaa
  :after org
  :init
  (when (eq system-type 'gnu/linux)
    (general-setq org-ditaa-jar-path "/usr/share/java/ditaa/ditaa-0.11.jar")))

Ditaa basically use the built-in artist mode, which behave strangely in Evil insert/normal state. I don’t want to be in Emacs state all the time, thus I have the following configuration to make it more usable:

(use-package artist
  :demand t
  :init
  (defmacro my/artist-move (move-active move-inactive)
    `(lambda (&optional n)
       (interactive "p")
       (if artist-key-is-drawing
           (,move-active n)
         (,move-inactive n))))
  :general
  (general-nmap artist-mode-map
    "RET" 'artist-key-set-point
    "j" (my/artist-move artist-next-line     picture-move-down)
    "k" (my/artist-move artist-previous-line picture-move-up)
    "h" (my/artist-move artist-backward-char picture-backward-column)
    "l" (my/artist-move artist-forward-char  picture-forward-column))
  (my/leader-def 'normal 'artist-mode-map
    "SPC" (general-simulate-key "C-c C-a")))

org-brain

org-brain implements concept mapping for org mode, and is also a tool to build my own personal wiki.

Enable tracking org heading links using globally unique uids. This is a must-have even without org-brain, because org mode won’t fix the broken links when you refile/archive some subtrees to a different file.

(use-package org-id
  :init
  (general-setq org-id-track-globally t)
  (general-setq org-id-link-to-org-use-id t)
  (general-setq org-id-locations-file (expand-file-name "emacs/org-id-locations" (xdg-data-home))))

Now load org-brain:

(use-package org-brain
  :ensure t
  :after (evil org)
  :init
  (general-setq org-brain-path "~/documents")
  (general-setq org-brain-title-max-length 30)
  (evil-set-initial-state 'org-brain-visualize-mode 'emacs)
  :general

  :custom-face
  (org-brain-title ((t (:inherit org-level-8))))
  )

org-brain's keymap conflicts with evil’s normal state, so before manually setting the keymaps it might worth simply using emacs-state by default:

(evil-set-initial-state 'org-brain-visualize-mode 'emacs)

Inherit org-brain-title face from inner-most org-mode heading (so that it won’t be over-sized):

(org-brain-title ((t (:inherit org-level-8))))

org-board

org-board is a helper package that archives web pages locally via wget. It enable my personal wiki to save web content so that I can review / grab things later.

(use-package org-board
  :ensure t
  :after org
  :init
  (general-setq org-board-wget-switches
                '("--page-requisites" "--adjust-extension" "--convert-links"))
  (general-setq org-board-archive-date-format 'hyphenate)
  :ensure t)

org-noter

org-noter is a synchronized document annotator, in Org mode.

(use-package org-noter
  :ensure t
  :demand t
  :general
  (general-nmap org-noter-doc-mode-map
    "i" 'org-noter-insert-note
    "I" 'org-noter-insert-precise-note
    "C-c q" 'org-noter-kill-session
    "gp" 'org-noter-sync-current-page-or-chapter
    "gn" 'org-noter-sync-current-note)
  (general-nmap org-noter-notes-mode-map
    "C-c q" 'org-noter-kill-session
    "gn" 'org-noter-sync-current-note
    "gp" 'org-noter-sync-current-page-or-chapter))

deft

deft is not directly related to org mode, but a general purpose plain-text note browser. It works nicely with org-brain and Org Mode.

(use-package deft
  :ensure t
  :commands (deft)
  :general
  (my/leader-def 'normal
    "d" 'deft)
  :init
  (general-setq deft-auto-save-interval 0) ;; turn off
  (general-setq deft-directory org-brain-path)
  (general-setq deft-recursive t)
  (general-setq deft-extensions '("org")))

toc-org

toc-org will automatically update the content of the first heading with a :TOC: tag in an org file to show an up-to-date TOC whenever the file is saved. Handy!

(use-package toc-org
  :ensure t
  :after org
  :hook (org-mode . toc-org-mode))

ox-hugo

ox-hugo basically add a ox (org-export) backend that exports to Hugo-compatible Markdown (Blackfriday) with YAML or TOML front-matter.

(use-package ox-hugo
  :ensure t
  :after ox)

style and faces

This part of code is basically grabbed from Beautifying Org Mode in Emacs by zzamboni.

Hide =, ~ and other emphasis markers, and fontify src block natively:

(general-setq org-hide-emphasis-markers t)
(general-setq org-src-fontify-natively t)
(general-setq org-tags-column 0)

Display \lambda etc as their unicode symbols (this works pretty well with embeded LaTeX symbol). I turned this feature off because subscript _ is annoying and I don’t need pretty-entries most of the time, plus it’s easy to toggle it just in time using org-toggle-pretty-entries.

(general-setq org-pretty-entities nil)

Latex Preview is too small under hidpi, so

(general-setq org-format-latex-options (plist-put org-format-latex-options :scale 3.0))

Use org-bullets to replace * with some cool unicode symbol. This seems super slow on Windows.

(use-package org-bullets
  :if (memq window-system '(mac ns))
  :ensure t
  :after org
  :hook
  (org-mode . (lambda () (org-bullets-mode 1))))

Use different font-size for headers, use sans-serif for non-code-like parts (powered by Emacs!), while still keeping code-like part using monospace font.

(org-level-8               ((t (:inherit variable-pitch :weight bold))))
(org-level-7               ((t (:inherit org-level-8))))
(org-level-6               ((t (:inherit org-level-8))))
(org-level-5               ((t (:inherit org-level-8))))
(org-level-4               ((t (:inherit org-level-8 :height 1.1))))
(org-level-3               ((t (:inherit org-level-8 :height 1.25))))
(org-level-2               ((t (:inherit org-level-8 :height 1.5))))
(org-level-1               ((t (:inherit org-level-8 :height 1.75))))
(org-document-title        ((t (:inherit org-level-8 :height 2.0 :underline nil))))
(org-block                 ((t (:inherit fixed-pitch))))
(org-document-info         ((t (:foreground "dark orange"))))
(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
(org-link                  ((t (:underline t))))
(org-meta-line             ((t (:inherit (font-lock-comment-face fixed-pitch)))))
(org-property-value        ((t (:inherit fixed-pitch))))
(org-table                 ((t (:inherit fixed-pitch))))
(org-block-begin-line      ((t (:inherit fixed-pitch :weight bold))))
(org-block-end-line        ((t (:inherit fixed-pitch :weight bold))))
(org-special-keyword       ((t (:inherit (font-lock-comment-face fixed-pitch)))))
(org-drawer                ((t (:inherit (font-lock-comment-face fixed-pitch)))))
(org-tag                   ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
(org-checkbox              ((t (:inherit fixed-pitch :weight bold))))
(org-verbatim              ((t (:inherit (shadow fixed-pitch)))))
(org-code                  ((t (:inherit (shadow fixed-pitch)))))
(org-indent                ((t (:inherit (org-hide fixed-pitch)))))

8 levels of headings are sometimes not enough for me, thus I define several more levels:

(defface org-level-9
      '((t (:inherit org-level-8 :height 1.0 :foreground "#83a598")))
      "Face used for level 9 headlines."
      :group 'org-faces)
(defface org-level-10
      '((t (:inherit org-level-8 :height 1.0 :foreground "#fabd2f")))
      "Face used for level 10 headlines."
      :group 'org-faces)
(defface org-level-11
      '((t (:inherit org-level-8 :height 1.0 :foreground "#d3869b")))
      "Face used for level 11 headlines."
      :group 'org-faces)
(defface org-level-12
      '((t (:inherit org-level-8 :height 1.0 :foreground "#fb4933")))
      "Face used for level 12 headlines."
      :group 'org-faces)
(general-setq org-level-faces (append org-level-faces (list 'org-level-9 'org-level-10 'org-level-11 'org-level-12)))
(general-setq org-n-level-faces (length org-level-faces))

The main reason I add more level faces is that I don’t want the large first levels to show up again at level more than 8. Thus it is also reasonable to turn of level cycling:

(general-setq org-cycle-level-faces nil)

Language specific

dot

Emacs does not come with dot language support out of the box. graphviz-dot-mode to the rescue!

(use-package graphviz-dot-mode
  :ensure t)

C and C++

Indent using 1 level:

(defvaralias 'c-continued-statement-offset 'tab-width)
(defvaralias 'c-basic-offset 'tab-width)

Fix indentations. See the help page of c-offsets-alist for details. When in doubt, use C-c C-s to see the fallback order at the cursor position, and C-c C-o to quickly try a different value.

(defun my/c-mode ()
  (c-set-offset 'arglist-intro '+)
  (c-set-offset 'arglist-cont '+)
  (c-set-offset 'arglist-cont-nonempty '+)
  (c-set-offset 'brace-list-intro '+)
  (c-set-offset 'substatement-open 0)
  (c-set-offset 'inextern-lang 0)
  (c-set-offset 'arglist-close 0))
(add-hook 'c-mode-hook 'my/c-mode)

A helper function that convert a header file’s name to a _HEADER_H_ macro:

(defun my/c-header-filename-macro ()
    "convert header filename.h to __FILENAME_H__"
  (let* ((filename  (file-name-nondirectory (buffer-file-name)))
         (basename  (file-name-sans-extension filename))
         (extension (file-name-extension filename)))
    (format "_%s_%s_" (upcase basename) (upcase extension))))

A helper function that get a c/cpp file’s corresponding conventional header file name:

(defun my/c-header-filename-to-this ()
    "convert header filename.{c,cpp} to filename.h"
  (let* ((filename  (file-name-nondirectory (buffer-file-name)))
         (basename  (file-name-sans-extension filename)))
    (my/filename-with-extension basename "h")))

ccls

ccls is a maintained fork of cquery, a language server implementation for C/C++. It provides some extra features beyond the language server protocol, and its author is an Emacs user, so there is a help package available:

(use-package ccls
  :hook ((c-mode c++-mode objc-mode cuda-mode) .
         (lambda () (require 'ccls) (lsp-deferred)))
  :init
  (general-setq-default flycheck-disabled-checkers '(c/c++-clang c/c++-cppcheck c/c++-gcc)))

plantuml

plantuml is a language / tool that draws sequence diagram, class dependency diagram and so on with text instructions.

plantuml-mode:

(use-package plantuml-mode
  :ensure t
  :mode "\\.uml\\'"
  :init
  (when (eq system-type 'gnu/linux)
    (general-setq plantuml-default-exec-mode 'executable)))

rust

(use-package rust-mode
  :ensure t
  :init
  (general-setq lsp-rust-server 'lsp))

beancount

beancount is a plain-text double-entry counting tool. as of this PR and discussion here and here, beancount is no longer compatible with org-mode, because it runs its own major mode.

reference:

(use-package beancount
  :if (eq system-type 'gnu/linux)
  :load-path "/usr/elisp/"
  :mode ("\\.beancount\\'" . beancount-mode)
  :init
  (add-hook 'beancount-mode-hook #'outline-minor-mode)
  (defvaralias 'beancount-transaction-indent 'tab-width))

CMake

cmake is the most (in)famous C/C++ build source generator.

(use-package cmake-mode
  :ensure t
  :mode ("\\'CMakeLists\\.txt\\'" . cmake-mode))

Latex

AUCTeX is an extensible package for writing and formatting TeX files in GNU Emacs.

(use-package tex
  :ensure auctex
  :init
  (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)
  (general-setq TeX-auto-save t)
  (general-setq TeX-parse-self t)
  (general-setq preview-scale-function 3.0))

TextInfo

Info+ provides enhanced features for Info Mode. Stuff viewable in Info Mode including documentation of glibc , of Gnu coreutils, and of course of Emacs.

(use-package info+
  :load-path "~/.emacs.d/elisp/")

Yaml

(use-package yaml-mode
  :ensure t
  :mode "\\.yml\\'")

Tools

Magit

magit is an Emacs interface to git, which provides not only commands to call but also a full GUI-like wrapper around git.

(use-package magit
  :ensure t)

Its Evil integration:

(use-package evil-magit
  :ensure t
  :after (evil magit))

Its treemacs integration that notifies treemacsfilewatch-mode of status changes:

(use-package treemacs-magit
  :ensure t
  :after (treemacs magit))

Pdf Tools

pdf-tools is basically a front-end of poppler for Emacs, turning Emacs into a sane Pdf viewer.

(use-package pdf-tools
  :ensure t
  :config
  (pdf-loader-install)
  (evil-collection-pdf-setup))

Vterm

emacs-libvterm is, guess what, a libvterm binding for Emacs!

(use-package vterm
  :ensure t
  :init
  (general-setq vterm-term-environment-variable "xterm-256color-italic")
  :config
  (evil-collection-vterm-setup))

vterm-toggle provides handy commands to toggle between vterm buffers and previous buffers.

(use-package vterm-toggle
  :ensure t
  :init
  (general-setq vterm-toggle-fullscreen-p nil)
  (add-to-list 'display-buffer-alist
               '((lambda(bufname _) (with-current-buffer bufname (equal major-mode 'vterm-mode)))
                 (display-buffer-reuse-window display-buffer-same-window)))
  :general
  (my/leader-def 'normal "v" 'vterm-toggle))