Skip to content

Latest commit

 

History

History
1715 lines (1471 loc) · 47.1 KB

init.org

File metadata and controls

1715 lines (1471 loc) · 47.1 KB

Emacs Configuration

Emacs saves you time when you work, and takes it back when you play with it.

init.el

Use M-x org-babel-tangle or C-c C-v t to manually extract lisp code.

Pre init

Load an optional pre init file.

(load (locate-user-emacs-file "local.pre.init.el") 'noerror)

Personal Information

Name and mail addresses

(setq user-full-name "Peng Mei Yu"
      user-mail-address "pmy@xqzp.net")

Identities

(setq my-irc-nick "pmy")

Bootstrap

Startup optimizations

;; Set garbage collection threshold to a big value.
(setq gc-cons-threshold-original gc-cons-threshold)
(setq gc-cons-threshold (* 100 gc-cons-threshold-original))

;; Set garbage collection threshold to a smaller value after initialization.
(add-hook 'after-init-hook
          (lambda ()
            (setq gc-cons-threshold (* 10 gc-cons-threshold-original))
            (makunbound 'gc-cons-threshold-original)
            (message "Init done")))

Enforce minimum Emacs version

(let ((min-version "27.1"))
  (when (version< emacs-version min-version)
    (error "Gnu Emacs %s or newer is required" min-version)))
(setq load-prefer-newer t)

Emacs server

Detect existing emacs server and switch to it if possible

(require 'server)

(defun my-server-shunt ()
  "Shunts to emacsclient"
  (let ((args (append '("emacsclient" "-c" "-n")
                      (cdr command-line-args))))
    (shell-command (substring (format "%S" args) 1 -1))
    (kill-emacs)))

;; Keep only one Emacs server instance
(if (server-running-p)
    (if (daemonp)
        (error "Another running Emacs server detected, abort")
      (my-server-shunt))
  (server-start))

Package manager

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/"))

Load packages managed by Guix.

(unless (require 'guix-emacs nil t)
  (let ((default-directory "~/.guix-profile/share/emacs/site-lisp/"))
    (when (file-accessible-directory-p default-directory)
      (normal-top-level-add-to-load-path '("."))
      (normal-top-level-add-subdirs-to-load-path))))

Bootstrap use-package

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

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

(setq use-package-verbose t)

;; Disable lazy loading in daemon mode
(if (daemonp)
    (setq use-package-always-demand t))

custom.el

Variables configured via the interactive ‘customize’ interface

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file 'noerror)

Core

Emacs built-in features

Require common lisp

(require 'cl-lib)

XDG specification

Define XDG directories

(require 'xdg)

(defvar user-emacs-config-dir
  (expand-file-name "emacs" (xdg-config-home)))

(defvar user-emacs-data-dir
  (expand-file-name "emacs" (xdg-data-home)))

(defvar user-emacs-cache-dir
  (expand-file-name "emacs" (xdg-cache-home)))

Environment

Determine operating system type

(defconst *os-is-gnu* (eq system-type 'gnu/linux))
(defconst *os-is-mac* (eq system-type 'darwin))
(defconst *os-is-windows* (eq system-type 'windows-nt))

Language

(set-language-environment "UTF-8")

Locale

`set-locale-environment` changes the default coding system, therefore call it before setting coding system.

(if *os-is-gnu*
    (set-locale-environment "en_US.UTF-8"))
(if *os-is-mac*
    (set-locale-environment "en_US.UTF-8"))
(if *os-is-windows*
    (set-locale-environment "ENU"))

Encoding

(set-default-coding-systems 'utf-8-unix)

Use hexadecimal instead of octal for quoted-insert (C-q).

(setq read-quoted-char-radix 16)

Basic interface

Inhibits the startup screen

(setq inhibit-startup-screen t)

*scratch* buffer’s default content

(setq initial-scratch-message nil)

Hide all kinds of bars

(menu-bar-mode -1)
(if (fboundp 'tool-bar-mode)
    (tool-bar-mode -1))
(if (fboundp 'scroll-bar-mode)
    (scroll-bar-mode -1))

mode line

(line-number-mode t)
(column-number-mode t)

(size-indication-mode t)

ring

(setq ring-bell-function 'ignore)

buffer name

(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)
(setq uniquify-separator "/")
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-ignore-buffers-re "^\\*")

frame name

;; show either a file or a buffer name
(setq frame-title-format
      '("" invocation-name " - "
        (:eval (if (buffer-file-name)
                   (abbreviate-file-name (buffer-file-name))
                 "%b"))))

Key bindings

Let ‘yes-or-no-p’ use shorter answers “y” or “n”.

(setq use-short-answers t)

Disable dialog boxes.

(setq use-dialog-box nil)

Bind C-x k to kill-this-buffer

(global-set-key (kbd "C-x k") 'kill-this-buffer)

Expand text

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

Upcase or downcase text

(global-set-key (kbd "M-u") 'upcase-dwim)
(global-set-key (kbd "M-l") 'downcase-dwim)

shell

(global-set-key (kbd "C-c s") 'eshell)

Completion

(add-to-list 'completion-styles 'flex 'append)

Editing

Fill column

(setq-default fill-column 80)

Final new line

(setq require-final-newline t)

Delete the selection with a key press

(delete-selection-mode t)

Smart tab key behavior, indent or complete

(setq tab-always-indent 'complete)

Indentation

;; don't use tabs to indent
(setq-default indent-tabs-mode nil)

(setq-default tab-width 8)

Revert buffers automatically when underlying files are changed externally

(global-auto-revert-mode t)

Automatically save buffers to file when losing focus

(defun my-save-buffers ()
  "Save all file-visiting buffers."
  (save-some-buffers t nil))

(add-hook 'focus-out-hook 'my-save-buffers)

Automatically make a shell script executable on save

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

Highlight

(blink-cursor-mode -1)

;; highlight the current line
(global-hl-line-mode 1)

;; highlight matching parentheses when the point is on them
(show-paren-mode t)

(setq blink-matching-paren nil)

whitespace-mode

(require 'whitespace)
(setq whitespace-style '(face empty trailing lines-tail indentation
                              missing-newline-at-eof))
(setq whitespace-line-column 80)

(defun my-whitespace-mode-setup ()
  (whitespace-mode 1)
  (add-hook 'before-save-hook 'whitespace-cleanup nil t))

Basic major modes

text-mode

(add-hook 'text-mode-hook 'auto-fill-mode)
(add-hook 'text-mode-hook 'my-whitespace-mode-setup)

prog-mode

(add-hook 'prog-mode-hook 'abbrev-mode)
(add-hook 'prog-mode-hook 'my-whitespace-mode-setup)

(defun my-prog-mode-setup ()
  (which-function-mode 1)

  (setq-local comment-auto-fill-only-comments t)
  (auto-fill-mode 1)

  ;; highlight a bunch of well known comment annotations
  (font-lock-add-keywords
   nil
   '(("\\<\\(\\(FIX\\(ME\\)?\\|TODO\\|OPTIMIZE\\|HACK\\|REFACTOR\\):\\)"
      1 font-lock-warning-face t))))

(add-hook 'prog-mode-hook 'my-prog-mode-setup)

Search

(add-to-list 'completion-ignored-extensions ".jar")

Spell Checking

flyspell

(let ((enable-flyspell nil))
  (cond
   ((executable-find "aspell")
    (setq ispell-program-name "aspell")
    (setq enable-flyspell t))
   ((executable-find "hunspell")
    (setq ispell-program-name "hunspell")
    (setq ispell-dictionary "en_US")
    (setq enable-flyspell t))
   (t
    (message "Neither aspell nor hunspell found")))

  (when enable-flyspell
    (require 'flyspell)
    (add-hook 'text-mode-hook 'flyspell-mode)
    (add-hook 'prog-mode-hook 'flyspell-prog-mode)))

Tramp

(require 'tramp)
(setq tramp-default-method "ssh")

Dired

(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'top)

(require 'dired-x)
(setq dired-clean-confirm-killing-deleted-buffers nil)

Bookmark

(require 'bookmark)
(setq bookmark-save-flag 1)

Calendar

(require 'calendar)
(require 'holidays)
(require 'cal-china)

(calendar-set-date-style 'iso)
(setq calendar-mark-holidays-flag t)
(setq calendar-chinese-all-holidays-flag t)
(setq calendar-holidays
      (append holiday-general-holidays
              holiday-local-holidays
              holiday-other-holidays
              holiday-oriental-holidays))

(setq calendar-chinese-celestial-stem
      ["" "" "" "" "" "" "" "" "" ""])
(setq calendar-chinese-terrestrial-branch
      ["" "" "" "" "" "" "" "" "" "" "" ""])

Internet

Don’t send anything in HTTP header field

(setq url-privacy-level 'paranoid)

Proxy

SOCKS 5 proxy

(setq url-gateway-method 'socks)
(setq socks-server '("Default server" "localhost" 1080 5))

HTTP proxy

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

Browser

eww

(global-set-key (kbd "C-c w") 'eww)
(global-set-key (kbd "C-c b") 'eww-list-bookmarks)

Email

message mode

;; Turn on PGP
(add-hook 'message-mode-hook 'epa-mail-mode)
(setq mml-secure-openpgp-encrypt-to-self t)

;; Message signature
(setq message-signature-directory
      (expand-file-name "signature" (xdg-config-home)))
(setq message-signature-file "personal")

;; Don't keep message buffer after sending a message.
(setq message-kill-buffer-on-exit t)

SMTP

(setq message-send-mail-function 'message-smtpmail-send-it)

(setq smtpmail-smtp-server "smtp.gmail.com"
      smtpmail-stream-type 'ssl  ;; StartTLS is evil.
      smtpmail-smtp-service 465)

sendmail

(setq message-send-mail-function 'message-send-mail-with-sendmail)

;; Use the "From:" address in mail header as envelope-from address.
(setq mail-specify-envelope-from t
      mail-envelope-from 'header)
(setq message-sendmail-envelope-from 'header)

msmtp

(setq sendmail-program "msmtp")

Security

GPG

Query passphrase through the minibuffer, instead of the pinentry program.

(unless (display-graphic-p)
  (setq epg-pinentry-mode 'loopback))

auth-source

(setq auth-sources
      (list (expand-file-name "auth/netrc.gpg" (xdg-data-home))))

Get secret from auth-source

(cl-defun my-get-secret (&rest spec &key domain port user &allow-other-keys)
  (let ((record (nth 0 (auth-source-search :max 1
                                           :host domain
                                           :port port
                                           :user user
                                           :require '(:secret)))))
    (if record
        (let ((secret (plist-get record :secret)))
          (if (functionp secret)
              (funcall secret)
            secret))
      nil)))

Session

Desktop

Disable prompt for vc-follow-symlinks during initialization.

;; When desktop-save-mode restores buffers, the VC prompt is useless and
;; annoying.
(setq vc-follow-symlinks nil)

;; Restore default values after initialization.
(add-hook 'after-init-hook
          (lambda ()
            (setq vc-follow-symlinks 'ask)))
(setq desktop-auto-save-timeout 600)
(setq desktop-save t)
(setq desktop-dirname user-emacs-directory)
(desktop-save-mode t)

Recent Files

(require 'recentf)
(setq recentf-auto-cleanup 'never)
(setq recentf-exclude
      (mapcar 'expand-file-name
              (list "/gnu" "/nix" "/run" "/tmp" "/ssh:" "~/.cache"
                    package-user-dir)))
(recentf-mode 1)

Minibuffer History

(savehist-mode 1)

Auto Save

(setq auto-save-list-file-prefix
      (expand-file-name "auto-save-list/" user-emacs-cache-dir))

Backup

(let ((backup-dir (expand-file-name "backup" user-emacs-cache-dir)))
  (setq-default backup-directory-alist `((".*" . ,backup-dir))))

Theme

Fonts

(use-package cnfonts
  :ensure t
  :config
  (cnfonts-enable))

Theme

(use-package color-theme-sanityinc-tomorrow
  :ensure t
  :config
  (load-theme 'sanityinc-tomorrow-night 'no-confirm))

Transparency

alpha ‘(<active> . <inactive>)

(set-frame-parameter (selected-frame) 'alpha '(95 . 90))
(add-to-list 'default-frame-alist '(alpha . (98 . 90)))

Mode line

(use-package diminish
  :ensure t
  :config
  (diminish 'abbrev-mode)
  (diminish 'auto-fill-function)
  (diminish 'auto-revert-mode)
  (diminish 'eldoc-mode)
  (diminish 'whitespace-mode))

Cursor

Highlight the cursor whenever the window scrolls

(use-package beacon
  :ensure t
  :diminish beacon-mode
  :config
  (beacon-mode t))

Utilities

Helm

(use-package helm
  :ensure t
  :defer 3
  :diminish helm-mode
  :bind-keymap ("C-c h" . helm-command-map)
  :bind (("C-c f" . helm-recentf)
         ("C-h a" . helm-apropos)
         ("C-x b" . helm-mini)
         ("C-x C-b" . helm-buffers-list)
         ("C-x C-d" . helm-browse-project)
         ("C-x C-f" . helm-find-files)
         ("M-x" . helm-M-x)
         ("M-y" . helm-show-kill-ring)
         ("M-s o" . helm-occur)
         :map helm-command-map
         ("M-g g" . helm-do-grep-rg))
  :init
  (defalias 'helm-do-grep-rg 'helm-do-grep-ag)
  :config
  (helm-mode 1)

  (setq helm-move-to-line-cycle-in-source t)

  ;; fuzzy matching
  (setq helm-mode-fuzzy-match t)
  (setq helm-completion-in-region-fuzzy-match t)
  (setq helm-M-x-fuzzy-match t
        helm-buffers-fuzzy-matching t
        helm-recentf-fuzzy-match t)

  (add-to-list 'helm-mini-default-sources 'helm-source-bookmarks 'append)

  (setq helm-ff-file-name-history-use-recentf t)
  (setq helm-ff-skip-boring-files t)

  ;; ripgrep
  (setq helm-grep-ag-command
        (concat "rg --color=always --colors 'match:fg:black'"
                " --colors 'match:bg:yellow' --smart-case"
                " --no-heading --line-number %s %s %s"))
  (setq helm-grep-ag-pipe-cmd-switches
        '("--colors 'match:fg:black'" "--colors 'match:bg:yellow'")))

helm-rg

(use-package helm-rg
  :ensure t
  :after helm
  :bind (:map helm-command-map
              ("g" . helm-rg)))

File explorer

(use-package dired-sidebar
  :ensure t
  :commands (dired-sidebar-toggle-sidebar))

Crux

(use-package crux
  :ensure t
  :bind (("C-a" . crux-move-beginning-of-line)
         ("C-c d" . crux-duplicate-current-line-or-region)
         ("C-c D" . crux-delete-file-and-buffer)
         ("C-c e" . crux-eval-and-replace)
         ("C-c I" . crux-find-user-init-file)
         ("C-c o o" . crux-open-with)
         ("C-c o r" . crux-sudo-edit)
         ("C-c r n" . crux-rename-file-and-buffer)
         ("C-c TAB" . crux-indent-defun)
         ("C-x K" . crux-kill-other-buffers)
         ("C-^" . crux-top-join-line)
         ("C-<BACKSPACE>" . crux-kill-line-backwards)
         ("C-S-<BACKSPACE>" . crux-kill-whole-line)))

Key map

which-key

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

discover-my-major

(use-package discover-my-major
  :ensure t
  :commands (discover-my-major discover-my-mode)
  :bind ("C-h m" . discover-my-major))

Key Statistics

keyfreq

(use-package keyfreq
  :ensure t
  :defer 10
  :config
  (setq keyfreq-file (locate-user-emacs-file "keyfreq"))
  (setq keyfreq-file.lock (concat keyfreq-file ".lock"))
  (keyfreq-mode 1)
  (keyfreq-autosave-mode 1))

undo-tree

(use-package undo-tree
  :ensure t
  :diminish undo-tree-mode
  :bind ("C-x u" . undo-tree-visualize)
  :config
  (setq undo-tree-history-directory-alist
        `(("." . ,(expand-file-name "undo-tree/" user-emacs-cache-dir))))
  (global-undo-tree-mode t))

Move cursor

Avy

Jump to visible text using a char-based decision tree

(use-package avy
  :ensure t
  :bind (("C-c j" . avy-goto-char-timer)
         ("M-g g" . avy-goto-line))
  :config
  (setq avy-background t))

ace-window

select a window

(use-package ace-window
  :ensure t
  :bind ("C-x o" . ace-window)
  :config
  (setq aw-scope 'frame))

Multiple cursors

(use-package multiple-cursors
  :ensure t
  :bind (("C-|" . mc/edit-lines)
         ("C->" . mc/mark-next-like-this)
         ("C-<" . mc/mark-previous-like-this)
         ("C-S-<mouse-1>" . mc/add-cursor-on-click)))

Search

anzu-mode enhances isearch & query-replace by showing total matches and current match position in the mode-line

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

Alert

(use-package alert
  :ensure t
  :config
  (setq alert-default-style 'libnotify))

Version control

Git

Magit

(use-package magit
  :ensure t
  :mode ("/\\(\
\\(\\(COMMIT\\|NOTES\\|PULLREQ\\|TAG\\)_EDIT\\|MERGE_\\|\\)MSG\
\\|\\(BRANCH\\|EDIT\\)_DESCRIPTION\\)\\'" . git-commit-mode)
  :bind (("C-x g" . magit-status)
         ("C-x M-g" . magit-dispatch)
         ("C-c M-g" . magit-file-dispatch)))

Git modes

(use-package git-modes
  :ensure t
  :defer 10)

diff-hl

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

Completion

(use-package company
  :ensure t
  :defer 10
  :diminish company-mode
  :config
  (setq company-minimum-prefix-length 2)
  (global-company-mode 1))

Input Method

Rime input method. This package should be installed by a system package manager.

(use-package rime
  :ensure nil
  :pin manual
  :requires rime
  :config
  (setq default-input-method "rime")
  (setq rime-show-candidate 'posframe)
  (setq rime-posframe-style 'vertical)
  (setq rime-disable-predicates
        '(active-minibuffer-window
          rime-predicate-ace-window-p
          rime-predicate-prog-in-code-p))
  (add-to-list 'rime-translate-keybindings "C-`")

  (defun my-telega-msg-p ()
    "Check if the current point is on a telega message."
    (string= "telega-msg-button"
             (symbol-name (get-text-property (point) 'category))))
  (add-to-list 'rime-disable-predicates 'my-telega-msg-p))

Programming

Flycheck

(use-package flycheck
  :ensure t
  :diminish flycheck-mode
  :hook (prog-mode . flycheck-mode)
  :config
  (setq flycheck-display-errors-function
        'flycheck-display-error-messages-unless-error-list))

Language server protocol

Eglot

(use-package eglot
  :ensure t
  :commands (eglot eglot-ensure)
  :bind (:map eglot-mode-map
              ("C-c l a" . eglot-code-actions)
              ("C-c l f" . eglot-format)
              ("C-c l r" . eglot-rename)
              ("C-c l t" . eglot-find-typeDefinition)
              ("C-c l i" . eglot-find-implementation)))

Parenthesis

smart parens

(use-package smartparens
  :ensure t
  :defer 10
  :diminish smartparens-mode
  :hook (prog-mode . smartparens-strict-mode)
  :config
  (require 'smartparens-config)
  (show-smartparens-global-mode 1))

colorful parens

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

Yasnippet

(use-package yasnippet
  :ensure t
  :defer 20
  :diminish yas-minor-mode
  :config
  (let ((dir "~/Projects/guix/etc/snippets"))
    (when (file-directory-p dir)
      (add-to-list 'yas-snippet-dirs dir)))

  (yas-global-mode 1))
(use-package yasnippet-snippets
  :ensure t
  :after yasnippet)

Lisp

Indentation

(use-package aggressive-indent
  :ensure t
  :diminish aggressive-indent-mode
  :hook ((emacs-lisp-mode scheme-mode) . aggressive-indent-mode))

Scheme

geiser

(use-package geiser
  :ensure t
  :hook (scheme-mode . geiser-mode--maybe-activate)
  :config
  (setq geiser-active-implementations '(guile))
  (setq geiser-mode-start-repl-p t)
  (setq geiser-repl-history-filename
        (expand-file-name "geiser_history" user-emacs-directory))

  (use-package geiser-guile
    :ensure t))

Guix

(use-package guix
  :ensure t
  :defer 20
  :hook (scheme-mode . guix-devel-mode))

C

(setq c-default-style "linux")
(setq-default c-basic-offset 4)

clang-format

(use-package clang-format
  :ensure t
  :commands (clang-format-buffer))

(defun clang-format-buffer-smart ()
  "Reformat buffer if .clang-format exists in the project root."
  (when (f-exists? (expand-file-name ".clang-format" (project-root (project-current))))
    (clang-format-buffer)))

(defun my-c-mode-setup ()
  (add-hook 'before-save-hook 'clang-format-buffer-smart nil t))

(add-hook 'c-mode-hook 'my-c-mode-setup)
(add-hook 'c++-mode-hook 'my-c-mode-setup)

C#

csharp-mode

(use-package csharp-mode
  :ensure nil
  :mode ("\\.cs\\'" . csharp-mode)
  :config
  (defun my-csharp-mode-setup ()
    (c-set-style "c#"))

  (add-hook 'csharp-mode-hook 'my-csharp-mode-setup))

fish shell

(use-package fish-mode
  :ensure t
  :mode ("\\.fish\\'" . fish-mode)
  :interpreter ("fish"))

Go

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

(use-package go-eldoc
  :ensure t
  :after (go-mode)
  :hook (go-mode . go-eldoc-setup))
(defun my-go-mode-setup ()
  (add-hook 'before-save-hook 'gofmt-before-save nil t))

(add-hook 'go-mode-hook 'my-go-mode-setup)

Java

java-mode

(defun my-java-mode-setup ()
  (setq fill-column 120))

(add-hook 'java-mode-hook 'my-java-mode-setup)
(add-hook 'java-mode-hook 'subword-mode)

Kotlin

kotlin-mode

(use-package kotlin-mode
  :ensure nil
  :mode ("\\.kts?\\'" . kotlin-mode))

Nix

nix-mode

(use-package nix-mode
  :ensure t
  :mode ("\\.nix\\'" . nix-mode)
  :config
  (setq nix-nixfmt-bin "nixpkgs-fmt"))

Powershell

powershell-mode

(use-package powershell
  :ensure nil
  :mode ("\\.ps[dm]?1\\'" . powershell-mode))

Python

Prefer Python 3

(setq python-shell-interpreter "python3")

python-mode

(defun my-python-mode-setup ()
  (add-hook 'post-self-insert-hook
            'electric-layout-post-self-insert-function
            nil t))

(add-hook 'python-mode-hook 'my-python-mode-setup)

Rust

rust-mode

(use-package rust-mode
  :ensure nil
  :mode ("\\.rs\\'" . rust-mode)
  :config
  (setq rust-format-on-save t))

SQL

Syntax-based indentation for sql-mode.

(use-package sql-indent
  :ensure t
  :hook (sql-mode . sqlind-minor-mode))

Web

HTML

htmlize – Converting buffer text and decorations to HTML.

(use-package htmlize
  :ensure t
  :defer 20)

rainbow mode

(use-package rainbow-mode
  :ensure t
  :hook ((html-mode css-mode) . rainbow-mode))

Vue

vue-mode

(use-package vue-mode
  :ensure nil
  :mode ("\\.vue\\'" . vue-mode))

File formats

Org Mode

org

(use-package org
  :ensure org-contrib
  :defer 10
  :mode ("\\.org\\'" . org-mode)
  :bind (("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         ("C-c o l" . org-store-link)
         ("C-c C-," . org-insert-structure-template))
  :config
  (setq org-directory "~/Sync/Org")
  (setq org-agenda-files (list org-directory))
  (setq org-default-notes-file
        (expand-file-name "Organizer.org" org-directory))
  (setq my-org-inbox-file (expand-file-name "Inbox.org" org-directory))

  (setq org-catch-invisible-edits 'show)
  (setq org-id-track-globally nil)      ; Do not store org IDs on disk.
  (setq org-use-sub-superscripts '{})

  ;;; org-agenda
  (setq org-agenda-default-appointment-duration 60)
  (setq org-agenda-compact-blocks t)
  (setq org-agenda-span 'month)
  (setq org-agenda-start-on-weekday nil)

  ;;; org-todo
  (setq org-log-done 'time)
  (setq org-log-into-drawer t)
  (setq org-todo-keywords
        '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!/!)" "CANCELLED(c@/!)")))
  (setq org-todo-repeat-to-state "NEXT")
  (setq org-todo-keyword-faces '(("NEXT" :inherit warning)))

  ;;; org-tag
  (setq org-fast-tag-selection-single-key 'expert)
  (setq org-tags-column -80)            ; Align right edge to 80th column.

  ;;; org-capture
  (setq org-capture-templates
        `(("i" "inbox"
           entry (file my-org-inbox-file)
           "* %?\n")
          ("j" "journal"
           entry (file+olp org-default-notes-file "Journal")
           "* %u\n%?\n")
          ("t" "todo"
           entry (file+olp org-default-notes-file "Agenda")
           "* TODO %?\n  :PROPERTIES:\n  :Captured_at: %U\n  :END:\n")))

  (add-to-list 'org-structure-template-alist
               '("semacs" . "src emacs-lisp") t)
  (add-to-list 'org-structure-template-alist
               '("sscheme" . "src scheme") t)
  (add-to-list 'org-structure-template-alist
               '("sshell" . "src sh") t)
  (add-to-list 'org-structure-template-alist
               '("ssql" . "src sql") t)
  (add-to-list 'org-structure-template-alist
               '("shttp" . "src http") t)

  ;;; org-clock
  ;; Persist the running clock and all clock history
  (org-clock-persistence-insinuate)
  (setq org-clock-persist t)
  (setq org-clock-in-resume t)
  ;; Save clock data and notes in drawer
  (setq org-clock-into-drawer t)
  ;; Remove the clock line when result time is zero
  (setq org-clock-out-remove-zero-time-clocks t)

  ;;; org-babel
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((dot . t)
     (latex . t)
     (ledger . t)
     (python . t)
     (scheme . t)
     (shell . t)
     (sql . t)))

  ;; Prefer Python 3
  (setq org-babel-python-command "python3")

  ;; Disable emacs-lisp-checker for org-src-mode
  (add-hook 'org-src-mode-hook
            (lambda ()
              (setq-local flycheck-disabled-checkers
                          '(emacs-lisp-checkdoc))))

  ;;; org-export
  (setq org-export-exclude-tags '("noexport" "private"))
  (setq org-export-with-section-numbers nil)
  (setq org-export-with-sub-superscripts '{})
  (setq org-export-with-toc nil)

  ;;; org-html
  (setq org-html-doctype "html5")
  (setq org-html-html5-fancy-p t)
  (setq org-html-validation-link nil)

  ;;; org-latex
  (add-to-list 'org-latex-packages-alist '("" "color"))
  (add-to-list 'org-latex-packages-alist '("" "listings"))
  (setq org-latex-listings t
        org-latex-listings-options '(("basicstyle" "\\small")
                                     ("frame" "single")))

  ;;; org-icalendar
  (setq org-icalendar-alarm-time 60)    ; 60 minutes before the event.
  (setq org-icalendar-combined-agenda-file
        (expand-file-name "agenda.ics" org-directory))
  (setq org-icalendar-exclude-tags
        (append org-export-exclude-tags '("archive" "journal")))
  ;; Include tasks that are not in DONE state.
  (setq org-icalendar-include-todo t)
  ;; Include scheduled and deadline events.
  (setq org-icalendar-use-scheduled
        '(event-if-todo event-if-not-todo todo-start))
  (setq org-icalendar-use-deadline
        '(event-if-todo event-if-not-todo todo-due))
  ;; Whether to make events from plain time stamps.
  (setq org-icalendar-with-timestamps 'active))

org-alert

Notifications for org agenda items

(use-package org-alert
  :ensure t
  :defer 20
  :config
  (setq org-alert-interval 600)
  (org-alert-enable))

org-roam

(use-package org-roam
  :ensure nil
  :after org
  :defer 10
  :bind (("C-c r l" . org-roam-buffer-toggle)
         ("C-c r f" . org-roam-node-file)
         ("C-c r g" . org-roam-graph)
         ("C-c r c" . org-roam-capture)
         ("C-c r i" . org-roam-node-insert))
  :init
  (setq org-roam-v2-ack t)
  :config
  (setq org-roam-directory org-directory)
  (setq org-roam-capture-templates
        `(("i" "inbox" entry nil
           :if-new (file+olp ,my-org-inbox-file ())
           :unnarrowed t)
          ("j" "journal" entry nil
           :if-new (file+olp ,org-default-notes-file ("Journal"))
           :unnarrowed t)
          ("t" "todo" entry nil
           :if-new (file+olp ,org-default-notes-file ("Agenda"))
           :unnarrowed t)))
  (org-roam-setup))

ob-http

(use-package ob-http
  :ensure t
  :after (ob)
  :mode ("\\.http\\'" . org-mode)
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((http . t))))

ox-hugo

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

Add UUID to all org headlines

(defun my-org-add-uuid-to-headlines-in-buffer ()
  "Add ID property to all headlines in the current buffer."
  (interactive)
  (org-map-entries 'org-id-get-create))

CSV

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

Ledger

(use-package ledger-mode
  :ensure nil
  :mode ("\\.ledger\\'" . ledger-mode)
  :config
  (use-package flycheck-ledger
    :ensure t))

Markdown

(use-package markdown-mode
  :ensure t
  :mode ("\\.md\\'" . markdown-mode))

po-mode

po-mode is provided by “gettext”

(use-package po-mode
  :ensure nil
  :mode ("\\.pot?\\'" . po-mode)
  :config
  ;; Do not wrap lines when editing msgstr.
  (add-hook 'po-subedit-mode-hook
            (lambda ()
              (setq fill-column 1000)))

  ;; Enable input method.
  (add-hook 'po-subedit-mode-hook
            (lambda ()
              (toggle-input-method))))

Wrap po file

;; https://www.emacswiki.org/emacs/PoMode
(defun po-wrap ()
  "Filter current po-mode buffer through `msgcat' tool to wrap all lines."
  (interactive)
  (if (eq major-mode 'po-mode)
      (let ((tmp-file (make-temp-file "po-wrap."))
            (tmp-buf (generate-new-buffer "*temp*")))
        (unwind-protect
            (progn
              (write-region (point-min) (point-max) tmp-file nil 1)
              (if (zerop
                   (call-process
                    "msgcat" nil tmp-buf t (shell-quote-argument tmp-file)))
                  (let ((saved (point))
                        (inhibit-read-only t))
                    (delete-region (point-min) (point-max))
                    (insert-buffer tmp-buf)
                    (goto-char (min saved (point-max))))
                (with-current-buffer tmp-buf
                  (error (buffer-string)))))
          (kill-buffer tmp-buf)
          (delete-file tmp-file)))))

TeX

AUCTeX

(use-package auctex
  :ensure t
  :mode ("\\.tex\\'" . LaTeX-mode)
  :config
  (setq TeX-auto-save t
        TeX-parse-self t
        TeX-PDF-mode t)
  (setq-default TeX-master nil)

  (use-package company-auctex
    :ensure t
    :after (company auctex)
    :config (company-auctex-init)))

RefTeX

(setq reftex-plug-into-AUCTeX t)
(add-hook 'LaTeX-mode-hook 'turn-on-reftex)

YAML

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

Internet

BBDB

(use-package bbdb
  :ensure t
  :config
  (bbdb-initialize 'gnus 'message 'mu4e))

Gnus

(use-package gnus
  :commands (gnus)
  :config
  ;; Email servers
  (setq my--gnus-local '(nnmaildir "local"
                                   (directory "~/Mail")
                                   (get-new-mail nil)))
  ;; Usenet servers
  (setq my--gnus-gmane
        '(nntp "gmane"
               (nntp-address "news.gmane.org")
               (nntp-port-number 563)
               (nntp-open-connection-function nntp-open-tls-stream))
        my--gnus-aioe
        '(nntp "aioe"
               (nntp-address "nntp.aioe.org")
               (nntp-port-number 563)
               (nntp-open-connection-function nntp-open-tls-stream)))

  (setq gnus-select-method my--gnus-local)
  (setq gnus-secondary-select-methods
        (list my--gnus-gmane my--gnus-aioe)))

mu4e

(setq mail-user-agent 'mu4e-user-agent)

(use-package mu4e
  :ensure nil
  :bind (("C-c m" . mu4e))
  :config

  (setq mu4e-confirm-quit nil)

  ;; Don't save message to the "sent" folder if IMAP takes care of this.
  ;; (setq mu4e-sent-messages-behavior 'delete)

  ;; Fetch email.
  (setq mu4e-get-mail-command "fdm fetch -v")

  ;; Default context.
  (setq mu4e-drafts-folder "/drafts")
  (setq mu4e-refile-folder "/archive/inbox")
  (setq mu4e-sent-folder   "/sent")
  (setq mu4e-trash-folder  "/trash")

  (setq mu4e-maildir-shortcuts
        '((:maildir "/archive/inbox" :key ?a)
          (:maildir "/drafts" :key ?d)
          (:maildir "/inbox" :key ?i)
          (:maildir "/sent" :key ?s)
          (:maildir "/spam" :key ?j)
          (:maildir "/trash" :key ?t)))

  (add-to-list 'mu4e-headers-actions
               '("git apply mbox" . mu4e-action-git-apply-mbox) t)

  (add-to-list 'mu4e-view-actions
               '("git apply mbox" . mu4e-action-git-apply-mbox) t))

IRC

ERC

(use-package erc
  :commands (erc my-erc-start-or-switch)
  :config
  (setq erc-nick my-irc-nick)
  (setq erc-autojoin-channels-alist
        '((".*\\.freenode.net" "#emacs")))
  (erc-autojoin-mode t)

  ;; spell checking
  (erc-spelling-mode 1)

  ;; logging
  (setq erc-log-channels-directory
        (expand-file-name "erc" (xdg-data-home)))

  (setq erc-save-buffer-on-part t)

  ;; fallback to auth-source
  (setq erc-prompt-for-password nil)

  ;; Kill buffers for channels after /part
  (setq erc-kill-buffer-on-part t)
  ;; Kill buffers for private queries after quitting the server
  (setq erc-kill-queries-on-quit t)
  ;; Kill buffers for server messages after quitting the server
  (setq erc-kill-server-buffer-on-quit t)

  ;; open query buffers in the current window
  (setq erc-query-display 'buffer)

  (setq erc-auto-reconnect nil)

  (erc-track-mode t)
  (setq erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT" "MODE"
                                  "324" "329" "332" "333" "353" "477"))

  (defun my-erc-start-or-switch ()
    "Connect to ERC, or switch to last active buffer."
    (interactive)
    (if (get-buffer "irc.libera.chat:6697")
        (erc-track-switch-buffer 1)
      (when (y-or-n-p "Start ERC? ")
        (erc-tls :server "irc.libera.chat" :port 6697
                 :nick my-irc-nick)))))

Telegram

(use-package telega
  :ensure nil
  :commands (telega))

RSS feed

elfeed

(use-package elfeed
  :ensure t
  :bind (("C-c n" . my-elfeed-open)
         :map elfeed-search-mode-map
         ("q" . my-elfeed-quit))
  :config
  (setq elfeed-db-directory
        (expand-file-name "elfeed" (xdg-data-home)))

  (setq elfeed-search-title-max-width 120)

  (use-package elfeed-org
    :ensure t
    :after elfeed
    :config
    (elfeed-org))

  (defun my-elfeed-open ()
    (interactive)
    (elfeed-db-load)
    (elfeed))

  (defun my-elfeed-quit ()
    (interactive)
    (elfeed-search-quit-window)
    (elfeed-db-unload)))

Lookup Wikipedia

(require 'browse-url)

(defun my-lookup-wikipedia ()
  "Look up the word under cursor in Wikipedia.
If there is a text selection, use that."
  (interactive)
  (let (word)
    (setq word
          (if (use-region-p)
              (buffer-substring-no-properties (region-beginning) (region-end))
            (current-word)))
    (setq word (replace-regexp-in-string " " "_" word))
    (browse-url (format "https://en.wikipedia.org/wiki/%s" word))))

Lookup Wiktionary

(autoload 'ispell-get-word "ispell")

(defun my-lookup-wiktionary (word)
  "Look up the word under cursor in Wiktionary."
  (interactive (list (save-excursion (car (ispell-get-word nil)))))
  (browse-url (format "https://en.wiktionary.org/wiki/%s" word)))

(global-set-key (kbd "M-#") 'my-lookup-wiktionary)

Media

EPUB

(use-package nov
  :ensure nil
  :mode ("\\.epub\\'" . nov-mode))

PDF

(use-package pdf-tools
  :ensure nil
  :mode ("\\.pdf\\'" . pdf-view-mode)
  :config
  (require 'pdf-occur)
  (pdf-tools-install))

Developer Tools

debbugs

(use-package debbugs
  :ensure nil
  :commands (debbugs-gnu
             debbugs-org
             debbugs-gnu-bugs
             debbugs-org-bugs
             debbugs-gnu-search
             debbugs-org-search)
  :config
  (setq debbugs-gnu-default-packages '("guix")))

Chinese compatibility hack

Fix org-export

by zwz.github.io

(defun clear-single-linebreak-in-cjk-string (string)
  "Clear single line-break between CJK characters that is usually soft
line-breaks"
  (let* ((cjk-char "[\u3000-\u303F]\\|[\u4E00-\u9FFF]\\|[\uFF01-\uFF5E]")
         (regexp (concat "\\(" cjk-char "\\)\n\\(" cjk-char "\\)"))
         (start (string-match regexp string)))
    (while start
      (setq string (replace-match "\\1\\2" nil nil string)
            start (string-match regexp string start))))
  string)

(defun ox-html-clear-single-linebreak-for-cjk (string backend info)
  (when (org-export-derived-backend-p backend 'html)
    (clear-single-linebreak-in-cjk-string string)))

(eval-after-load "ox"
  '(add-to-list 'org-export-filter-final-output-functions
                'ox-html-clear-single-linebreak-for-cjk))

Devices

Office

(when (string-match-p "office" (system-name))
  (with-eval-after-load 'org
    (setq org-directory "~/Office")
    (setq org-agenda-files (list org-directory))
    (setq org-default-notes-file
          (expand-file-name "Office.org" org-directory))
    (setq my-org-inbox-file (expand-file-name "Office.org" org-directory)))

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

  (use-package spacemacs-theme
    :ensure t
    :defer t
    :init (load-theme 'spacemacs-dark 'no-confirm)))

Post init

Load an optional post init file.

(load (locate-user-emacs-file "local.post.init.el") 'noerror)