My vanilla Emacs configurations

This config is no longer in use

Since March 2020, I have migrated to Doom Emacs, and won’t be using this config anymore. This emacs configuration will be archived at this Github repo.

See my Doom Emacs config here.

1 Prerequisite

1.1 Reproducible information

Current emacs version:

GNU Emacs 27.1 (build 1, x86_64-pc-linux-gnu, GTK+ Version 2.24.32)
 of 2020-09-11

I build my emacs from source, with the following configuration options:

--with-modules --with-json --with-mailutils --with-rsvg

1.2 Installing

The whole thing is driven by the following .emacs:

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
        (url-retrieve-synchronously  ""
                                     'silent 'inhibit-cookies)
      (goto-char (point-max))
  (load bootstrap-file nil 'nomessage))

;; Install use-package
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
(use-package diminish)
(use-package bind-key :straight use-package)

(straight-use-package 'org-plus-contrib)

(setq config-directory "~/Documents/Emacs/emacs-config/")
 (expand-file-name "" config-directory))

Then I clone this github repo into “~/Documents/Emacs/emacs-config/”. Everything is installed using straight.el and use-package.

2 Initiation

Things may breaks mid-setup (this is a personal config after all), so I want the basic things to be put in place first.

2.1 Setup and bootstrap

First and foremost: enable lexical-binding

;; -*- lexical-binding: t; -*-

The first part of the config is to ensure things run smoothly during startup time.

;; The GC can easily double startup time,
;; so we suppress it at startup by turning up `gc-cons-threshold`
;; and perhaps `gc-cons-percentage` temporarily:

(setq gc-cons-threshold most-positive-fixnum ; 2^61 bytes
      gc-cons-percentage 0.6)

(add-hook 'emacs-startup-hook
          (lambda ()
            (setq gc-cons-threshold 16777216 ; 16mb
                  gc-cons-percentage 0.1)))

;; Auto-revert mode
(global-auto-revert-mode 1)
(setq auto-revert-interval 0.5)

;; Backup stored in /tmp
(setq backup-directory-alist
      `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
      `((".*" , temporary-file-directory t)))

;; Delete old backup
(message "Deleting old backup files...")
(let ((week (* 60 60 24 7))
      (current (float-time (current-time))))
  (dolist (file (directory-files temporary-file-directory t))
    (when (and (backup-file-name-p file)
               (> (- current (float-time (nth 5 (file-attributes file))))
      (message "%s" file)
      (delete-file file))))

;; Information settings
(setq user-full-name "Hiếu Phẩy"
      user-mail-address "")

;; Set emacs as a client
(use-package server
  (unless (server-running-p) (server-start)))

2.2 Better Defaults

Emacs is made in 1976, so it retains some weird defaults. I modified them to my opinionated preferences here:

;; Ensure starting from home directory
(cd "~/")

;; Everything utf-8
(set-language-environment "UTF-8")
(prefer-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-buffer-file-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(setq default-input-method 'vietnamese-telex)
(setq search-default-mode 'char-fold-to-regexp)

;; Set some annoying command disabled
;; the function `unbind-key` require `use-package`
(unbind-key "<insert>") 		;overwrite-mode
(unbind-key "C-x C-z"   )		;suspend-frame
(unbind-key "C-x m")			;compose-mail
(unbind-key "C-x C-l")                  ;downcase region
(unbind-key "C-x <right>")              ;next-buffer
(unbind-key "C-x <left>")               ;previous-buffer
(unbind-key "C-v")                      ;scroll-up-command
;; Rebind some commands to more sane hotkeys
(bind-key "M-p" 'other-window)

;; And keep quited please
(setq visible-bell 1)

;; Delete marked region when input
(delete-selection-mode 1)

;; Pressing TAB indents first then complete
(setq tab-always-indent 'complete)

;; Indent always use spaces instead of tabs
(setq indent-tabs-mode nil)

;; Global mark ring
(setq global-mark-ring-max 50000)

;; Auto save abbreviation
(setq save-abbrevs 'silently)

;; "Yes or no"? Too much writing
(defalias 'yes-or-no-p 'y-or-n-p)

;; Make comint promts read-only
(setq comint-prompt-read-only t)

;; Set kill ring size
(setq global-mark-ring-max 50000)

;; Bound undo to C-z
(global-set-key (kbd "C-z") 'undo)

;; Scrolling
(setq scroll-step 1) ; keyboard scroll one line at a time
(setq scroll-preserve-screen-position t)
(setq scroll-conservatively 101)

2.3 Informative variable

These variable is for handily tangle blocks on different OSes.

(set 'linuxp (when (eq system-type 'gnu/linux) "yes"))
(set 'windowp (when (eq system-type 'windows-nt) "yes"))

3 Appearence

3.1 Interface

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

;; Global truncate line, except in text-based modes
(set-default 'truncate-lines t)

;; Initialize Emacs full screen
;; (add-to-list 'initial-frame-alist '(fullscreen . maximized))
;; (global-set-key (kbd "<f11>") 'toggle-frame-maximized)

;; No startup messages on *scratch* buffer
(setq initial-scratch-message "")

;; Cursor type
(setq-default cursor-type 'bar
              cursor-in-non-selected-windows nil)

;; Global font-lock mode
(setq global-font-lock-mode t)

;; Enable line number and column number
(setq column-number-mode t)

;; Display line number
;; (add-hook 'text-mode-hook (lambda () (setq display-line-numbers 'relative)))
(add-hook 'prog-mode-hook (lambda () (setq display-line-numbers 'relative)))
(add-hook 'conf-mode-hook (lambda () (setq display-line-numbers 'relative)))
(setq-default display-line-numbers-width 2)
(setq-default display-line-numbers-widen t)

;; Disable tool bar, menu bar, and scroll bar
(tool-bar-mode -1)
(scroll-bar-mode -1)
(menu-bar-mode 1)
(add-hook 'after-init-hook (lambda () (window-divider-mode -1)))

3.1.1 Beacon-mode

Show a trailing flash of light whenever my cursor jumps over a certain distance. Great to keep track of the cursor’s position.

(use-package beacon
  (setq beacon-push-mark 35)
  (setq beacon-color "#d65d0e")
  (beacon-mode t)

3.1.2 Smooth-scrolling

Better scrolling in Emacs.

;; Smooth scrolling
(use-package smooth-scrolling :config (smooth-scrolling-mode t))

3.1.3 Visual fill colunmn

visual-fill-column-mode (link) complements the built in visual-fill-mode by allowing lines to wraps at a certain column.

(use-package visual-fill-column
  (dolist (hook '(visual-line-mode-hook
                  ;; prog-mode-hook
    (add-hook hook #'visual-fill-column-mode))
  (setq visual-fill-column-width 80)
  :hook ((visual-fill-column-mode-hook . visual-line-mode))
   ;; visual-fill-column-center-text nil
   ;; visual-fill-column-fringes-outside-margins nil
   split-window-preferred-function 'visual-fill-column-split-window-sensibly)
  (defun toggle-frame-fullscreen-and-visual-fill-adjust ()
    (run-with-timer 0.1 nil 'visual-fill-column--adjust-window))
  ("<f11>" . toggle-frame-fullscreen-and-visual-fill-adjust))

3.2 Aesthetics

3.2.1 Faces

My favorites fonts:

;; Default font
(when (member "Iosevka" (font-family-list))
  (set-frame-font "Iosevka 11" nil t))
(when (member "Source Han Sans" (font-family-list))
  (set-fontset-font t 'han (font-spec :name "Source Han Sans")))

(set-face-attribute 'variable-pitch nil
                    :font "Iosevka Aile")
(set-face-attribute 'fixed-pitch nil
                    :font "Iosevka")

(use-package gruvbox-theme
  (load-theme 'gruvbox-dark-medium t)
  (set-face-attribute 'secondary-selection nil
                      :weight 'bold :background "#1d2021"))

(use-package rainbow-delimiters
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

3.2.2 Mode-line

The ultimate pimp for Emacs. This mode-line makes Emacs look a lot more modern.

(use-package spaceline-config
  :straight (spaceline :host github :repo "TheBB/spaceline" :branch "master")
  (setq spaceline-workspace-numbers-unicode t)
  (spaceline-helm-mode 1))

4 Hydra and self-defined commands

4.1 My commands

Handy self-defined commands that I use frequently.

;; Rename file and buffer
;; source:
(defun hieu/rename-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive "sNew name: ")
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not filename)
        (message "Buffer '%s' is not visiting a file!" name)
      (if (get-buffer new-name)
          (message "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil))))))

;; Eval and replace lisp expression
(defun hieu/fc-eval-and-replace ()
  "Replace the preceding sexp with its value."
  (prin1 (eval (read (current-kill 0)))

(bind-key "C-c e" 'hieu/fc-eval-and-replace)

;; Move line/region up/down
(defun hieu--move-text-internal (arg)
   ((and mark-active transient-mark-mode)
    (if (> (point) (mark))
    (let ((column (current-column))
          (text (delete-and-extract-region (point) (mark))))
      (forward-line arg)
      (move-to-column column t)
      (set-mark (point))
      (insert text)
      (setq deactivate-mark nil)))
    (when (or (> arg 0) (not (bobp)))
      (when (or (< arg 0) (not (eobp)))
        (transpose-lines arg))
      (forward-line -1)))))

(defun hieu/move-text-down (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines down."
  (interactive "*p")
  (hieu--move-text-internal arg))

(defun hieu/move-text-up (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines up."
  (interactive "*p")
  (hieu--move-text-internal (- arg)))

(bind-key "M-<up>" 'hieu/move-text-up)
(bind-key "M-<down>" 'hieu/move-text-down)

;; Open the gnome terminal
(defun hieu/open-gnome-terminal ()
  "Open an instance of gnome-terminal on Ubuntu machine"
  (shell-command "gnome-terminal"))

(bind-key "<f10>" 'hieu/open-gnome-terminal)

;; Insert current date (and time)
(defun hieu/insert-current-date () (interactive)
       (insert (shell-command-to-string "bash -c 'echo -n $(date +%Y-%m-%d)'")))

(defun hieu/insert-current-time () (interactive)
       (insert (shell-command-to-string "bash -c 'echo -n $(date +%H:%M)'")))

(bind-key "C-x M-d" 'hieu/insert-current-date)
(bind-key "C-x M-t" 'hieu/insert-current-time)

;; Replace Org keywords to lowercase, in consistent with Org-mode 9.2
(defun hieu/lower-case-org-keywords ()
  "Lower case Org keywords and block identifiers.

  Example: \"#+TITLE\" -> \"#+title\"
           \"#+BEGIN_EXAMPLE\" -> \"#+begin_example\"

    (goto-char (point-min))
    (let ((case-fold-search nil)
          (count 0))
      ;; Match examples: "#+FOO bar", "#+FOO:", "=#+FOO=", "~#+FOO~",
      ;;                 "‘#+FOO’", "“#+FOO”", ",#+FOO bar",
      ;;                 "#+FOO_bar<eol>", "#+FOO<eol>".
      (while (re-search-forward "\\(?1:#\\+[A-Z_]+\\(?:_[[:alpha:]]+\\)*\\)\\(?:[ :=~’”]\\|$\\)" nil :noerror)
        (setq count (1+ count))
        (replace-match (downcase (match-string-no-properties 1)) :fixedcase nil nil 1))
      (message "Lower-cased %d matches" count))))

4.2 My hydras

The hydra package create a reasonable UI for complicated keybinds.

(use-package hydra)

(defhydra hydra-straight-helper (:hint nil)
    _c_heck all       |_f_etch all     |_m_erge all      |_n_ormalize all   |p_u_sh all
    _C_heck package   |_F_etch package |_M_erge package  |_N_ormlize package|p_U_sh package
    _r_ebuild all     |_p_ull all      |_v_ersions freeze|_w_atcher start   |_g_et recipe
    _R_ebuild package |_P_ull package  |_V_ersions thaw  |_W_atcher quit    |prun_e_ build"
  ("c" straight-check-all)
  ("C" straight-check-package)
  ("r" straight-rebuild-all)
  ("R" straight-rebuild-package)
  ("f" straight-fetch-all)
  ("F" straight-fetch-package)
  ("p" straight-pull-all)
  ("P" straight-pull-package)
  ("m" straight-merge-all)
  ("M" straight-merge-package)
  ("n" straight-normalize-all)
  ("N" straight-normalize-package)
  ("u" straight-push-all)
  ("U" straight-push-package)
  ("v" straight-freeze-versions)
  ("V" straight-thaw-versions)
  ("w" straight-watcher-start)
  ("W" straight-watcher-quit)
  ("g" straight-get-recipe)
  ("e" straight-prune-build)
  ("q" nil))

5 Editing

Various packages that helps with editing:

5.1 Incremental completion with helm

Helm can be opened in a separate frame. In Gnome, I can press S-<direction> to move this window around.

(use-package helm-config
  :straight helm
  (helm-mode 1)
   ;; Open helm in a seperate frame
   helm-display-function                 'helm-display-buffer-in-own-frame
   helm-display-buffer-reuse-frame       t
   helm-use-undecorated-frame-option     t
   helm-display-buffer-width             80     ;; move to end or beginning of source when reaching top or bottom of source	.
   helm-move-to-line-cycle-in-source     t
   ;; Inherit input method
   helm-inherit-input-method             nil
   ;; Others
   helm-M-x-fuzzy-match                  t
   helm-ff-skip-boring-files             t
   helm-ff-file-name-history-use-recentf t)

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

  :bind (("C-c h" . helm-command-prefix)
         ("C-x b" . helm-mini)
         ("M-x" . helm-M-x)
         ("C-x C-f" . helm-find-files)
         ("M-y" . helm-show-kill-ring)
         :map helm-map
         ("<tab>" . helm-execute-persistent-action) ; rebind tab to run persistent action
         ("C-i" . helm-execute-persistent-action)   ; make TAB work in terminal
         ("M-x" . helm-select-action)              ; list actions using C-z
         :map helm-command-map
         ("o" . helm-occur)))

5.1.1 Helm-company

Integrates helm-mode with company-mode.

;; Use "C-:" to switch to Helm interface during company-ing
(use-package helm-company
  :after company
  :bind (:map company-mode-map
         (("C-:" . helm-company))
         :map company-active-map
         (("C-:" . helm-company))))

5.1.2 Swiper-helm

helm interface to swiper. swiper is a replacement for the built-in search command.

(use-package swiper-helm
  (setq swiper-helm-display-function 'helm-display-buffer-in-own-frame)
  :bind ("C-s" . swiper-helm))

5.2 Company

Company is THE completion mechanism for Emacs

(use-package company
  ;; Activate globally
  (add-hook 'after-init-hook 'global-company-mode)
  ;; Press <F1> to show the documentation buffer and press C-<F1> to jump to it
  (defun my/company-show-doc-buffer ()
    "Temporarily show the documentation buffer for the selection."
    (let* ((selected (nth company-selection company-candidates))
           (doc-buffer (or (company-call-backend 'doc-buffer selected)
                           (error "No documentation available"))))
      (with-current-buffer doc-buffer
        (goto-char (point-min)))
      (display-buffer doc-buffer t)))

  ;; Some useful configs
  (setq company-selection-wrap-around t
        company-tooltip-align-annotations t
        company-tooltip-limit 10
        company-idle-delay 0.5)
  ;; Add yasnippet support for all company backends
  (defvar company-mode/enable-yas t "Enable yasnippet for all backends.")
  (defun company-mode/backend-with-yas (backend)
    (if (or (not company-mode/enable-yas) (and (listp backend)    (member 'company-yasnippet backend)))
      (append (if (consp backend) backend (list backend))
              '(:with company-yasnippet))))
  (setq company-backends (mapcar #'company-mode/backend-with-yas company-backends))
  (:map company-active-map
   ("C-<f1>" . my/company-show-doc-buffer)
   ("C-n" . company-select-next)
   ("C-p" . company-select-previous)

5.3 Yasnippets

Yasnippet is THE templating system for Emacs. Each snippet is defined in a text file. My custom snippet files are stored here.

;; Enable Yasnippets
(use-package yasnippet
  ;; It will test whether it can expand, if yes, change cursor color}
  (defun yasnippet-can-fire-p (&optional field)
    (setq yas--condition-cache-timestamp (current-time))
    (let (templates-and-pos)
      (unless (and yas-expand-only-for-last-commands
                   (not (member last-command yas-expand-only-for-last-commands)))
        (setq templates-and-pos (if field
                                      (narrow-to-region (yas--field-start field)
                                                        (yas--field-end field))

      (set-cursor-color (if (and templates-and-pos (first templates-and-pos))
                            "#d65d0e" (face-attribute 'default :foreground)))))
  (add-hook 'post-command-hook 'yasnippet-can-fire-p)
  (yas-global-mode 1)
  (setq yas-fallback-behavior 'call-other-command)

  (setq yas-snippet-dirs-custom (format "%s/%s" config-directory "Snippets/"))
  (add-to-list' yas-snippet-dirs 'yas-snippet-dirs-custom)

  :bind*  (("<C-tab>" . yas-insert-snippet)
           :map yas-minor-mode-map
           ("`" . yas-expand-from-trigger-key)))

5.4 Smartparens

Working with, not just parenthesises and braces, but also every abstract pair of text objects.

(use-package smartparens-config
  :straight smartparens
  :hook (((text-mode prog-mode comint-mode) . smartparens-mode)
	 (smartparens-mode . show-smartparens-mode))
  ;; Define a hydra
  (defhydra hydra-smartparens (:idle 1 :hint nil)
  S-exps (quit with _q_)

  ^Nav^            ^Barf/Slurp^          ^Depth^
  _f_: forward     _s_:  slurp forward   _R_:      splice
  _b_: backward    _S_:  barf forward    _r_:      raise
  _a_: begin       _d_:  slurp backward  _<up>_:   raise backward
  _e_: end         _D_:  barf backward   _<down>_: raise forward
  _m_: mark

  ^Kill^           ^Misc^                       ^Wrap^
  _w_: copy        _j_: join                    _(_: wrap with ( )
  _k_: kill        _s_: split                   _{_: wrap with { }
  ^^               _t_: transpose               _'_: wrap with ' '
  ^^               _c_: convolute               _\"_: wrap with \" \"
  ^^               _i_: indent defun"
    ("q" nil)
    ;; Wrapping
    ("(" (lambda (a) (interactive "P") (sp-wrap-with-pair "(")))
    ("{" (lambda (a) (interactive "P") (sp-wrap-with-pair "{")))
    ("'" (lambda (a) (interactive "P") (sp-wrap-with-pair "'")))
    ("\"" (lambda (a) (interactive "P") (sp-wrap-with-pair "\"")))
    ;; Navigation
    ("f" sp-beginning-of-next-sexp)
    ("b" sp-beginning-of-previous-sexp)
    ("a" sp-beginning-of-sexp)
    ("e" sp-end-of-sexp)
    ("m" sp-mark-sexp)
    ;; Kill/copy
    ("w" sp-copy-sexp :exit t)
    ("k" sp-kill-sexp :exit t)
    ;; Misc
    ("t" sp-transpose-sexp)
    ("j" sp-join-sexp)
    ("c" sp-convolute-sexp)
    ("i" sp-indent-defun)
    ;; Depth changing
    ("R" sp-splice-sexp)
    ("r" sp-splice-sexp-killing-around)
    ("<up>" sp-splice-sexp-killing-backward)
    ("<down>" sp-splice-sexp-killing-forward)
    ;; Barfing/slurping
    ("s" sp-forward-slurp-sexp)
    ("S" sp-forward-barf-sexp)
    ("D" sp-backward-barf-sexp)
    ("d" sp-backward-slurp-sexp))
  :bind (("M-<backspace>" . sp-unwrap-sexp)
     ("C-c s" . hydra-smartparens/body)))

(use-package smartparens-org
  :straight smartparens
  :after org)

5.5 Multiple-cursor

Operate on multiple text positions at once.

(use-package multiple-cursors
  ;; In case commands behavior is messy with multiple-cursors,
  ;; check your ~/.emacs.d/.mc-lists.el
  (defun mc/check-command-behavior ()
    "Open ~/.emacs.d/.mc-lists.el.
  So you can fix the list for run-once and run-for-all multiple-cursors commands."
    (find-file "~/.emacs.d/.mc-lists.el"))
  (defhydra hydra-multiple-cursors (:columns 3 :idle 1.0)
    "Multiple cursors"
    ("l" mc/edit-lines "Edit lines in region" :exit t)
    ("b" mc/edit-beginnings-of-lines "Edit beginnings of lines in region" :exit t)
    ("e" mc/edit-ends-of-lines "Edit ends of lines in region" :exit t)
    ("a" mc/mark-all-like-this "Mark all like this" :exit t)
    ("S" mc/mark-all-symbols-like-this "Mark all symbols likes this" :exit t)
    ("w" mc/mark-all-words-like-this "Mark all words like this" :exit t)
    ("r" mc/mark-all-in-region "Mark all in region" :exit t)
    ("R" mc/mark-all-in-region-regexp "Mark all in region (regexp)" :exit t)
    ("i" (lambda (n)
           (interactive "nInsert initial number: ")
           (mc/insert-numbers n))
     "Insert numbers")
    ("s" mc/sort-regions "Sort regions")
    ("v" mc/reverse-regions "Reverse order")
    ("d" mc/mark-all-dwim "Mark all dwim")
    ("n" mc/mark-next-like-this "Mark next like this")
    ("N" mc/skip-to-next-like-this "Skip to next like this")
    ("M-n" mc/unmark-next-like-this "Unmark next like this")
    ("p" mc/mark-previous-like-this "Mark previous like this")
    ("P" mc/skip-to-previous-like-this "Skip to previous like this")
    ("M-p" mc/unmark-previous-like-this "Unmark previous like this")
    ("q" nil "Quit" :exit t))
  ("C-c m" . hydra-multiple-cursors/body))

5.6 Electric-operator

Electric operator is an Emacs minor-mode that automatically insert spacing around operators. It’s handy for keeping code clean without much effort.

(use-package electric-operator
  (setq electric-operator-R-named-argument-style 'spaced)
  (add-hook 'ess-mode-hook #'electric-operator-mode)
  (add-hook 'python-mode-hook #'electric-operator-mode)

  (electric-operator-add-rules-for-mode 'ess-r-mode
                                        (cons ":=" " := ")))

5.7 Auto-highlight-symbol

Auto highlight the same symbol in the buffer.

(use-package auto-highlight-symbol
  :init (add-hook 'prog-mode-hook 'auto-highlight-symbol-mode)
  (setq ahs-idle-interval 1.0
        ahs-default-range 'ahs-range-whole-buffer
        ahs-inhibit-face-list '(font-lock-comment-delimiter-face
  (unbind-key "M--" auto-highlight-symbol-mode-map))

5.8 Expand-region

Gradually expanding the selected region.

(use-package expand-region :bind ("M-." . er/expand-region))

5.9 Eyebrowse

Eyebrowse falicitates workspaces in Emacs.

(use-package eyebrowse
  (setq eyebrowse-new-workspace t)
  (eyebrowse-mode 1)
  ;; define hydra
  (defhydra hydra-eyebrowse (:hint nil :color red)
  Window Manager
  _0_ to _9_, _s_: Switch     _<left>_: Previous      _<right>_: Next
  _c_: Create             _C_: Close              _r_: Rename"
    ("q" nil :color blue)
    ("0" eyebrowse-switch-to-window-config-0)
    ("1" eyebrowse-switch-to-window-config-1)
    ("2" eyebrowse-switch-to-window-config-2)
    ("3" eyebrowse-switch-to-window-config-3)
    ("4" eyebrowse-switch-to-window-config-4)
    ("5" eyebrowse-switch-to-window-config-5)
    ("6" eyebrowse-switch-to-window-config-6)
    ("7" eyebrowse-switch-to-window-config-7)
    ("8" eyebrowse-switch-to-window-config-8)
    ("9" eyebrowse-switch-to-window-config-9)
    ("r" eyebrowse-rename-window-config :exit t)
    ("c" eyebrowse-create-window-config :exit t)
    ("s" eyebrowse-switch-to-window-config :exit t)
    ("C" eyebrowse-close-window-config :exit t)
    ("<left>" eyebrowse-prev-window-config)
    ("<right>" eyebrowse-next-window-config)
  :bind* ("C-c C-w" . hydra-eyebrowse/body))

6 Notetaking with org-mode

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

6.1 Setting up

;; org has quite some spurious commands
(unbind-key "C-c C-z" org-mode-map)	;org-add-note

;; org-indent-mode looks better
(add-hook 'org-mode-hook 'org-indent-mode)

;; Enable shift selection
(setq org-support-shift-select t)

;; Fontification
(set-face-attribute 'org-document-title nil :height 150)
(set-face-attribute 'org-level-1 nil :weight 'bold)
(set-face-attribute 'org-level-2 nil :weight 'bold)
(set-face-attribute 'org-block nil :background
                     (face-attribute 'default :background) 2))
;; Highlight temporal notes in texts with ~...~
(add-to-list 'org-emphasis-alist
             '("~" (:foreground "#d65d0e" :background "#1d2021")

;; Highlight latex stuffs
(setq org-highlight-latex-and-related '(latex entities))

;; Variable pitch
(add-hook 'org-mode-hook
          '(lambda ()
             (variable-pitch-mode 1)))

(mapc (lambda (face)
        (set-face-attribute face nil :inherit 'fixed-pitch))
      (list 'org-code

(setq org-startup-with-latex-preview t
      ;; Make latex preview with "C-c C-x C-l" slightly bigger
      (plist-put org-format-latex-options :scale 1.8)
      ;; Cache the preview images elsewhere
      org-preview-latex-image-directory "~/.cache/ltximg/")

;; Auto expand preview latex images when cursor is on it
(use-package org-fragtog
  :config (add-hook 'org-mode-hook 'org-fragtog-mode))

;; org-open-file use Evince if possible
(add-to-list 'org-file-apps '("\\.pdf\\'" . "evince %s"))

org-tempo: quickly insert templates with <trigger TAB. It used to be defaults before Org 9.2

(use-package org-tempo :straight org)

6.2 Org-exports

Convert .org to every other filetypes.

(use-package ox-latex
  :straight org
  ;; Highlight code blocks in org-latex-export-to-pdf
  ;; Minted options can be found in:
  (setq org-latex-listings 'minted
        org-latex-packages-alist '(("" "minted"))
        org-latex-minted-options '(
                                   ;; ("breaklines" "true")
                                   ;; ("breakanywhere" "true")
                                   ;; ("mathescape")
                                   ;; ("linenos" "true")
                                   ;; ("firstnumber" "last")
                                   ;; ("frame" "lines")
                                   ("fontsize" "\\footnotesize")
                                   ("bgcolor" "yellow!5")
                                   ;; ("framesep" "2mm")
        '("latexmk -pdflatex='%latex -shell-escape -bibtex -interaction=nonstopmode' -pdf -output-directory=%o -f %f")

  ;; Default packages
  (setq org-latex-default-packages-alist
        '(("AUTO" "inputenc" t
          ("T1" "fontenc" t
          ("" "fontspec" t
          ("" "graphicx" t)
          ("" "grffile" t)
          ;; Array, tabularx, booktabs are for tables
          ("" "array" nil)
          ("" "tabularx" nil)
          ("" "booktabs" nil)
          ("" "multirow" nil)
          ("" "siunitx" nil)
          ("" "wrapfig" nil)
          ("" "rotating" nil)
          ("normalem" "ulem" t)
          ("" "amsmath" t)
          ("" "textcomp" t)
          ("" "amssymb" t)
          ("" "capt-of" nil)
          ("dvipsnames" "xcolor" nil)
          ("colorlinks=true, linkcolor=Blue, citecolor=BrickRed, urlcolor=PineGreen" "hyperref" nil)
          ("" "indentfirst" nil))

  ;; Writing latex in org-mode
  (add-hook 'org-mode-hook 'org-cdlatex-mode)

  ;; Add KOMA-scripts classes to org export
  (add-to-list 'org-latex-classes
               '("koma-article" "\\documentclass{scrartcl}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

  (add-to-list 'org-latex-classes
               '("koma-report" "\\documentclass{scrreprt}"
                 ("\\part{%s}" . "\\part*{%s}")
                 ("\\chapter{%s}" . "\\chapter*{%s}")
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")))

  (add-to-list 'org-latex-classes
               '("koma-book" "\\documentclass[11pt]{scrbook}"
                 ("\\part{%s}" . "\\part*{%s}")
                 ("\\chapter{%s}" . "\\chapter*{%s}")
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")))

I also use ox-hugo to write this blog.

(use-package ox-hugo
  :after ox)

;; This may breaks things
(use-package org-ref-ox-hugo
  :ensure org-ref
  :straight (:host github :repo "jethrokuan/org-ref-ox-hugo"))

(add-to-list 'org-ref-formatted-citation-formats
               ("article" . "${author} (${year}), *${title}*, ${journal}, *${volume}(${number})*, ${pages}. ${doi}")
               ("inproceedings" . "${author} (${year}), *${title}*, In ${editor}, ${booktitle} (pp. ${pages}). ${address}: ${publisher}.")
               ("book" . "${author} (${year}), *${title}*, ${address}: ${publisher}.")
               ("phdthesis" . "${author} (${year}), *${title}* (Doctoral dissertation). ${school}, ${address}.")
               ("inbook" . "${author} (${year}), *${title}*, In ${editor} (Eds.), ${booktitle} (pp. ${pages}). ${address}: ${publisher}.")
               ("incollection" . "${author} (${year}), *${title}*, In ${editor} (Eds.), ${booktitle} (pp. ${pages}). ${address}: ${publisher}.")
               ("proceedings" . "${editor} (Eds.), _${booktitle}_ (${year}). ${address}: ${publisher}.")
               ("unpublished" . "${author} (${year}), *${title}*. Unpublished manuscript.")
               ("misc" . "${author} (${year}). *${title}*. Retrieved from [${howpublished}](${howpublished}). ${note}.")
               (nil . "${author} (${year}), *${title}*.")))

6.3 Org-agenda


(use-package org-agenda
  :straight org
  (setq org-agenda-files '("~/Dropbox/Notes/Agenda")
        org-default-notes-file "~/Dropbox/Notes/Agenda/"
        "%60ITEM(Task) %10Effort(Estimate){:} %CLOCKSUM")
  (defun hieu/open-agenda()
    (org-agenda nil "n")
  :bind ("<f1>" . org-agenda)
  ;; :hook (after-init . hieu/open-agenda)

(use-package org-super-agenda
  :after org-agenda
  (setq org-super-agenda-groups
        '((:auto-outline-path t :time-grid t)))

(use-package org-capture
  :straight org
  ("C-c c" . org-capture))

6.4 Org-ref

Org-ref makes inserting citations and managing bibliographies much easier.

(use-package org-ref
   org-ref-default-bibliography	     '("~/Dropbox/Notes/Research/papers.bib")
   org-ref-pdf-directory             "~/Dropbox/Notes/Papers/"
   bibtex-dialect                    'biblatex
   bibtex-completion-notes-extension ""
   bibtex-completion-notes-path      "~/Dropbox/Notes/Roam/"
   bibtex-completion-bibliography    "~/Dropbox/Notes/Research/papers.bib"
   bibtex-completion-library-path    "~/Dropbox/Notes/Papers/"
   ;; Optimize for 80 character frame display
   '((t . "${title:46} ${author:20} ${year:4} ${=type=:3}${=has-pdf=:1}${=has-note=:1}"))
   "#+title: ${author-or-editor} (${year}): ${title}
  #+roam_key: cite:${=key=}
  #+roam_tags: bibliography"
   bibtex-completion-pdf-symbol ""
   bibtex-completion-notes-symbol ""
   ;; Open pdf in external tool instead of in Emacs
   (lambda (fpath)
     (call-process "evince" nil 0 nil fpath)))
  :bind ("C-c ]" . helm-bibtex))

6.5 Org-journal

A journal inside Emacs.

(use-package org-journal
  ("C-c n j" . org-journal-new-entry)
  (setq org-journal-date-format "%A, %Y-%m-%d"
        org-journal-date-prefix "* Daily Journal "
        org-journal-file-format ""
        org-journal-dir "~/Dropbox/Notes/Roam/"
        org-journal-file-header "#+title: %Y-%m-%d %a\n#+roam_tags: journal\n"
        org-journal-enable-agenda-integration t))

6.6 Org-roam

Org-roam falicitates a system for networked note-taking that I am experimenting with.

(use-package org-roam
  :after org
  :straight (:host github :repo "jethrokuan/org-roam" :branch "master")
  (after-init . org-roam-mode)
  (setq org-roam-directory "~/Dropbox/Notes/Roam/"
        org-roam-db-location "~/.emacs.d/org-roam.db")
  ;; Exclude roam files from helm
  (add-to-list 'helm-boring-buffer-regexp-list "^[0-9]\\{14\\}.+\\.org$")
  :bind (:map org-roam-mode-map
         (("C-c n l" . org-roam)
          ("C-c n f" . org-roam-find-file)
          ("C-c n g" . org-roam-graph)
          ("C-c n b" . org-roam-switch-to-buffer)
          ("C-c n r" . org-roam-find-ref)
          ("C-c n d" . org-roam-find-directory))
         :map org-mode-map
         (("C-c n i" . org-roam-insert))))

(use-package org-roam-protocol :straight org-roam)

(use-package org-roam-graph
  :straight org-roam
  (setq org-roam-graph-executable	    (executable-find "dot")
        org-roam-graph-extra-config        '(("overlap" . "false")
                                             ("concentrate" . "true")
                                             ("bgcolor" . "lightblue"))
        '(("color" . "gray")
          ("style" . "dashed")
          ("sep" . "20"))
        org-roam-graph-shorten-titles      'wrap
        org-roam-graph-max-title-length    50
        org-roam-graph-exclude-matcher     '("journal")))

(use-package org-roam-capture
  :straight org-roam
  (setq org-roam-capture-templates
        '(("d" "default" plain (function org-roam-capture--get-point)
           :file-name "%<%Y%m%d%H%M%S>-${slug}"
           :head "#+title: ${title}\n#+roam_alias:\n#+roam_tags:\n"
           :unnarrowed t))
        '(("r" "ref" plain (function org-roam-capture--get-point)
           "#+roam_key: ${ref}\n%?"
           :file-name "%<%Y%m%d%H%M%S>_web_${slug}"
           :head "#+title: ${title}]\n#+roam_tags: website\n"
           :unnarrowed t))
        '(("d" "daily" plain (function org-roam-capture--get-point)
           :immediate-finish t
           :file-name "journal_%<%Y-%m-%d>"
           :head "#+title: %<%Y-%m-%d %a>\n#+roam_tags: journal\n"))

(use-package company-org-roam
  :straight (:host github :repo "jethrokuan/company-org-roam")
  (push 'company-org-roam company-backends))

7 Utilities

7.1 Projectile

Working with multiple projects.

(use-package projectile
  (setq projectile-keymap-prefix (kbd "C-c C-p"))
  (setq projectile-completion-system 'helm)
  (setq projectile-mode-line '(:eval (format " 𝐏[%s]" (projectile-project-name)))))

;; Helm-projectile
(use-package helm-projectile

7.2 Magit

Magit is an interface to the version control system Git, implemented as an Emacs package. Magit aspires to be a complete Git porcelain. See here

(use-package magit
  ;; Set magit-status to F9
  ("<f9>" . magit-status)
  ;; Currently magit cause some error when auto revert mode is on
  (setq magit-auto-revert-mode nil))

(use-package git-gutter

(use-package git-gutter-fringe
  :after git-gutter)

Ripgrep is the best search tool out there.

(use-package rg :config (rg-enable-default-bindings))

7.4 Which-key

which-key is a minor mode for Emacs that suggests commands whenever you inserted an incomplete chains of key-binds.

(use-package which-key
  :diminish which-key-mode
  (which-key-mode 1))

7.5 Restart-emacs

Make restarting easier, handy whenever I’m grokking Emacs.

(use-package restart-emacs)

8 Languages Modes

8.1 R: ess-mode

(use-package ess-site
  :straight ess
  ;; Execute screen options after initialize process
  (add-hook 'ess-post-run-hook 'ess-execute-screen-options)

  ;; Disable IDO so helm is used instead
  (setq ess-use-ido nil)

  ;; We don’t want R evaluation to hang the editor, hence
  (setq ess-eval-visibly 'nowait)

  ;; Unbind ess-insert-assign (defaut value is "_")
  (setq ess-smart-S-assign-key nil))

(use-package ess-r-mode
  :straight ess
  ;; Hot key C-S-m for pipe operator in ESS
  (defun pipe_R_operator ()
    "R - %>% operator or 'then' pipe operator"
    (just-one-space 1)
    (insert "%>%")
    (just-one-space 1))

  ;; ESS syntax highlight
  (setq ess-R-font-lock-keywords
        '((ess-R-fl-keyword:keywords . t)
          (ess-R-fl-keyword:constants . t)
          (ess-R-fl-keyword:modifiers . t)
          (ess-R-fl-keyword:fun-defs . t)
          (ess-R-fl-keyword:assign-ops . t)
          (ess-fl-keyword:fun-calls . t)
          (ess-fl-keyword:numbers . t)
          (ess-fl-keyword:operators . t)
          (ess-fl-keyword:delimiters . t)
          (ess-fl-keyword:= . t)
          (ess-R-fl-keyword:F&T . t)
          (ess-R-fl-keyword:%op% . t)))

  (setq inferior-ess-r-font-lock-keywords
        '((ess-S-fl-keyword:prompt . t)
          (ess-R-fl-keyword:messages . t)
          (ess-R-fl-keyword:modifiers . nil)
          (ess-R-fl-keyword:fun-defs . t)
          (ess-R-fl-keyword:keywords . nil)
          (ess-R-fl-keyword:assign-ops . t)
          (ess-R-fl-keyword:constants . t)
          (ess-fl-keyword:matrix-labels . t)
          (ess-fl-keyword:fun-calls . nil)
          (ess-fl-keyword:numbers . nil)
          (ess-fl-keyword:operators . nil)
          (ess-fl-keyword:delimiters . nil)
          (ess-fl-keyword:= . t)
          (ess-R-fl-keyword:F&T . nil)))

  (:map ess-r-mode-map
   ("M--" . ess-insert-assign)
   ("C-S-m" . pipe_R_operator)
   ("M--" . ess-insert-assign)
   ("C-S-m" . pipe_R_operator))

8.2 Python: elpy

(use-package python
  :mode ("\\.py\\'" . python-mode)
  (setq python-shell-interpreter "python3"))

(use-package elpy
  :after python
  ;; Truncate long line in inferior mode
  (add-hook 'inferior-python-mode-hook (lambda () (setq truncate-lines t)))
  ;; Enable company
  (add-hook 'python-mode-hook 'company-mode)
  (add-hook 'inferior-python-mode-hook 'company-mode)
  ;; Enable highlight indentation
  (add-hook 'highlight-indentation-mode-hook
  ;; Enable elpy
  ;; Do not enable elpy flymake for now
  (remove-hook 'elpy-modules 'elpy-module-flymake)
  (remove-hook 'elpy-modules 'elpy-module-highlight-indentation)

  ;; The old `elpy-use-ipython' is obseleted, see:
  ;; (setq python-shell-interpreter "ipython3"
  ;; python-shell-interpreter-args "-i --simple-prompt")

  (setq elpy-rpc-python-command "python3")

  ;; Completion backend
  (setq elpy-rpc-backend "rope")

  ;; Function: send block to elpy: bound to C-c C-c
  (defun forward-block (&optional n)
    (interactive "p")
    (let ((n (if (null n) 1 n)))
      (search-forward-regexp "\n[\t\n ]*\n+" nil "NOERROR" n)))

  (defun elpy-shell-send-current-block ()
    "Send current block to Python shell."
    (display-buffer (process-buffer (elpy-shell-get-or-create-process))

  ;; Font-lock
  (add-hook 'python-mode-hook
                '(("\\<\\([_A-Za-z0-9]*\\)(" 1
                   font-lock-function-name-face) ; highlight function names

  :bind (:map python-mode-map
         ("C-c <RET>" . elpy-shell-send-region-or-buffer)
         ("C-c C-c" . elpy-send-current-block)))

(use-package pipenv
  :hook (python-mode . pipenv-mode))

8.3 Julia: julia-mode and julia-snail

(use-package julia-mode
  :magic ("%JL" . julia-mode)
  (setq inferior-julia-program-name 'julia)
  (define-key julia-mode-map (kbd "TAB") 'julia-latexsub-or-indent))

(use-package julia-snail
  :after julia
  :ensure vterm
  :hook (julia-mode . julia-snail-mode))

8.4 Jupyter Notebook: emacs-jupiter

(use-package jupyter)

(use-package jupyter-org-extensions
  :straight jupyter
  :bind (:map jupyter-org-interaction-mode-map
         ("C-c h" . nil)
         ("C-c j" . jupyter-org-hydra/body)))

(use-package ob-jupyter :straight jupyter)

(setq org-babel-default-header-args:jupyter-julia
      '((:async . "yes")
        (:session . "jl")
        (:kernel . "julia")))

8.5 LaTeX: AUCTeX and CDLaTeX

8.5.1 AUCTeX

AUCTeX is the TeX extension for Emacs. Be careful with configurations because it overrides the built-in tex package.

(use-package auctex
  :mode ("\\.tex\\'" . TeX-latex-mode)
  ;; General configs
  (setq TeX-master		 nil
        TeX-auto-save		 t
        TeX-parse-self		 t
        TeX-PDF-mode		 t
        TeX-electric-escape	 t)
  ;; Turn on RefTeX in AUCTeX
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
  ;; Reftex default bibfile
  (setq reftex-default-bibliography "~/Dropbox/Notes/Research/papers.bib")
  ;; Activate nice interface between RefTeX and AUCTeX
  (setq reftex-plug-into-AUCTeX t)

;; Completion
(use-package company-auctex
  :after tex

8.5.2 CDLaTex

CDLaTeX is a minor mode that support fast insertion of environment templates and math stuffs in LaTeX. For more information see here

(use-package cdlatex
  :after (tex)
  (add-hook 'LaTeX-mode-hook 'turn-on-cdlatex))

8.6 Markdown: markdown-mode

(use-package markdown-mode
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :bind (:map markdown-mode-map
         ("C-c i" . markdown-insert-code-chunk)))

8.7 Emacs Lisp

Customisation to emacs-lisp itself, this is mainly syntax highlighting

(use-package highlight-defined
  (add-hook 'emacs-lisp-mode-hook 'highlight-defined-mode))

(use-package highlight-quoted
  (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)
  (set-face-attribute 'highlight-quoted-symbol nil
                      :inherit 'font-lock-string-face))

(use-package helpful
  (("C-h f" . helpful-callable)
   ("C-h v" . helpful-variable)
   ("C-h k" . helpful-key)))

8.8 css: css-mode

(use-package css-mode
  :mode (("\\.css?\\'" . css-mode)))

8.9 pdf: pdf-tools

(use-package pdf-tools
  :magic ("%PDF". pdf-view-mode)
  (pdf-tools-install :no-query))

8.10 Org-mode Babel

Finally, the Babel functionality of Org-mode makes literate programming a lot easier. This config itself is powered by org-babel. Since it requires the major-mode for other languages, I put this at the bottom of configuration.

(setq org-confirm-babel-evaluate nil)

 '((emacs-lisp . t)
   (julia . t)
   (R . t)
   (python . t)
   (jupyter . t)))