alexforsale's literate Emacs configuration 2.0

Table of Contents

The new configuration

My old configuration got bloated after accumulating external packages, and sometimes it's just a test package that I forgot to remove, or some packages I've added that I don't really use.

So this time I want to really focus on the builtins, and only the builtins that I really use.

early-init.el

GC Threshold

A common way of speeding up Emacs startup. The variable gc-cons-threshold is the number of bytes consing between garbage collection. Garbage collection can happen automatically once this many bytes have been allocated since the last garbage collection. Here I set this to a variable most-positive-fixnum which value is set automatically.

most-positive-fixnum
2305843009213693951
(setopt gc-cons-threshold most-positive-fixnum
        gc-cons-percentage 0.6)

The gc-cons-percentage default to 0.1, is the portion of the heap used for allocation.

I set the value differently when Emacs is starting up, by setting this variable into emacs-startup-hook that runs after Emacs loaded the init files.

(add-hook 'emacs-startup-hook
        (lambda ()
          (setopt gc-cons-threshold (* 16 1024 1024) ; 16mb
                gc-cons-percentage 0.1)))

Language

Set UTF-8 as the default coding system

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

Set initial frame

Here I disable the menu-bar, tool-bar and vertical scroll bar.

(add-to-list 'default-frame-alist '(menu-bar-lines . 0))
(add-to-list 'initial-frame-alist '(menu-bar-lines . 0))

(add-to-list 'initial-frame-alist '(tool-bar-lines . 0))
(add-to-list 'default-frame-alist '(tool-bar-lines . 0))

(add-to-list 'initial-frame-alist '(vertical-scroll-bars))
(add-to-list 'default-frame-alist '(vertical-scroll-bars))

(add-to-list 'initial-frame-alist '(fullscreen . maximized))
(add-to-list 'default-frame-alist '(fullscreen . maximized))

Define the location of custom-file

The custom-file is used to save customization settings for future use.

(customize-set-variable 'custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file))

Setup package

Now much more simpler since use-package is built into Emacs. It's actually not used since I'm using straight.el.

(with-eval-after-load 'package
  (setopt package-enable-at-startup nil)
  ;; Add `melpa` to `package-archives`.
  (add-to-list 'package-archives
                         '("melpa" . "https://melpa.org/packages/") t)
  ;; gnu-devel
  (add-to-list 'package-archives '("gnu-devel" . "https://elpa.gnu.org/devel/") t)
  (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t)
  (unless package-archive-contents
      (package-refresh-contents)))

By setting the variable use-package-compute-statistics to t, we'll be able to have reports to see which packages are taking too much time to set up.

(customize-set-variable 'use-package-compute-statistics t)
(require 'use-package)

init.el

Tangle this org-file

This file is already added in my git repositories for initial Emacs loading. But I also include it here to make sure there's no changes made.

(require 'ob-tangle)

(when (file-exists-p (expand-file-name "index.org" user-emacs-directory))
  (org-babel-tangle-file (expand-file-name "index.org" user-emacs-directory)))

Variables and helper functions

Identity

(setopt user-mail-address "alexforsale@yahoo.com"
              user-full-name "Kristian Alexander P")

merge-list-to-list function   archive

This function is just a wrapper to easily merge lists. I don't think I need it anymore.

;;; From https://emacs.stackexchange.com/questions/38008/adding-many-items-to-a-list/68048#68048
(defun merge-list-to-list (dst src)
  "Merges content of the 2nd list with the 1st one"
  (set dst
       (append (eval dst) src)))

define +config/org-directory variable

(cond ((file-directory-p (expand-file-name "Sync/org" (getenv "HOME")))
         (customize-set-variable '+config/org-directory (expand-file-name "Sync/org" (getenv "HOME"))))
        ((string-match-p "microsoft" (shell-command-to-string "uname -a"))
         (if (file-directory-p "/mnt/c/Users/SyncthingServiceAcct/Default Folder/org")
                 (customize-set-variable '+config/org-directory "/mnt/c/Users/SyncthingServiceAcct/Default Folder/org"))))

I put my org files inside syncthing default directory (~/Sync if on Linux) so it will automatically synced to all my devices. But on windows WSL the location will vary depending on how you install syncthing so I won't even bother set it up.

Load each modular configuration file.

evil

All evil-mode specific configuration and evil related packages goes here.

(require 'config-evil (expand-file-name "modules/config-evil.el" user-emacs-directory) t)

Keybinding

Most keybindings here depends on evil-mode, so it will be loaded after.

(require 'config-keybinding (expand-file-name "modules/config-keybinding.el" user-emacs-directory) t)

UI

(require 'config-ui (expand-file-name "modules/config-ui.el" user-emacs-directory) t)

Configuration for Emacs's looks and feels.

builtins

(require 'config-builtins (expand-file-name "modules/config-builtins.el" user-emacs-directory) t)

The more I understand the meat inside Emacs the less external packages I'll use (hopefully).

completion

(require 'config-completion (expand-file-name "modules/config-completion.el" user-emacs-directory) t)

For now this is for vertico and friends.

shells

(require 'config-shells (expand-file-name "modules/config-shells.el" user-emacs-directory) t)

Mostly vterm configurations. Eshell looks promising, but still hard to configure.

editing

(require 'config-editing (expand-file-name "modules/config-editing.el" user-emacs-directory) t)

Tweaks to make my editing experience nicer.

IDE

(require 'config-ide (expand-file-name "modules/config-ide.el" user-emacs-directory) t)

All of LSP, git, flycheck, and projectile configuration.

org

(require 'config-org (expand-file-name "modules/config-org.el" user-emacs-directory) t)

Most of my changes are in here, the more configurable org-mode, the more unusable to me.

tools

(require 'config-tools (expand-file-name "modules/config-tools.el" user-emacs-directory) t)

Small packages that helps Emacs interacts with its surrounding. I have direnv, exec-path-from-shell for, you guess it, shells. There's also ox-hugo for blogging with hugo.

dired

(require 'config-dired (expand-file-name "modules/config-dired.el" user-emacs-directory) t)

I don't use dired that much, but when I do, I at least know my way around.

treemacs

(require 'config-treemacs (expand-file-name "modules/config-treemacs.el" user-emacs-directory) t)

It's like NERDtree in vim, but in Emacs, I kinda understand why vim need this functionality. C-x C-f with vertico and marginalia already do the same in Emacs. So it's not important to me, but still nice to have.

mail

(require 'config-mail (expand-file-name "modules/config-mail.el" user-emacs-directory) t)

Email in Emacs with the help of notmuch, offlineimap, afew, and davmail for my Exchange mails.

programming modes

(require 'config-progmodes (expand-file-name "modules/config-progmodes.el" user-emacs-directory) t)

Doing stuffs in linux will eventually makes you edit files from various programming languages. This here is just to make me read them comfortly.

local overrides

(require 'config-overrides (expand-file-name "modules/config-overrides.el" user-emacs-directory) t)

Modules

Evil   external

configuration

Mostly copied from doom or the official documentation.

(use-package evil
  :ensure t
  :hook
  (after-change-major-mode . (lambda ()
                               (setopt evil-shift-width tab-width)))
  :preface
  (customize-set-variable 'evil-want-keybinding nil)
  (customize-set-variable 'evil-want-integration t)
  (customize-set-variable 'evil-undo-system 'undo-redo)
  (customize-set-variable 'evil-want-C-i-jump nil) ;; fix TAB in terminal org-mode
  (customize-set-variable 'evil-want-C-u-scroll t) ;; move universal arg to <leader> u
  (customize-set-variable 'evil-want-C-u-delete t) ;; delete back to indentation in insert state
  (customize-set-variable 'evil-want-C-g-bindings t)
  :custom
  (evil-undo-system #'undo-redo)
  (evil-search-module 'evil-search)
  (evil-ex-search-vim-style-regexp t)
  (evil-ex-interactive-search-highlight 'selected-window)
  (evil-kbd-macro-suppress-motion-error t)
  (evil-visual-update-x-selection-p nil)
  :config
  (unless noninteractive
    (setopt save-silently t))
  (setopt evil-normal-state-cursor 'box
        evil-insert-state-cursor 'bar
        evil-visual-state-cursor 'hollow)
  (evil-select-search-module 'evil-search-module 'isearch)
  (evil-mode 1)
  (with-eval-after-load 'eldoc
    (eldoc-add-command 'evil-normal-state
                       'evil-insert
                       'evil-change
                       'evil-delete
                       'evil-replace))
  ;; from doom
  (defun +evil/window-split-and-follow ()
    "Split current window horizontally, then focus new window.
If `evil-split-window-below' is non-nil, the new window isn't focused."
    (interactive)
    (let ((evil-split-window-below (not evil-split-window-below)))
      (call-interactively #'evil-window-split)))
  (defun +evil/window-vsplit-and-follow ()
    "Split current window vertically, then focus new window.
If `evil-vsplit-window-right' is non-nil, the new window isn't focused."
    (interactive)
    (let ((evil-vsplit-window-right (not evil-vsplit-window-right)))
      (call-interactively #'evil-window-vsplit))))

Notes:

evil-search-modules
I switched this to isearch since the default evil-search didn't search through collapsed headings in org-mode.
evil-want-keybindings
I set this to nil so it won't load evil-keybindings.el.
evil-want-integration
Loads evil-integration.el.
evil-undo-system
Use undo-redo which is natively available in Emacs 28.
evil-want-C-i-jump
Set to nil so C-i will insert a tab character, needed when I'm using Emacs inside a terminal.
evil-want-C-u-scroll
Set C-u to scrolls up (like Vim), I'm not using it for prefix arguments since it's already mapped to SPC u in normal mode.
evil-want-C-u-delete
The same for insert mode, set C-u to deletes back to indentation in insert state, prefix argument is set to C-c SPC u in insert mode.
evil-want-C-g-bindings
Set a consistent bindings for C-g, though it also depends on the packages.
evil-ex-search-vim-style-regexp
Enables Vim-style backlash codes in search patterns.
evil-ex-interactive-search-highlight
Set the interactive highlighting only to selected-window.
evil-visual-update-x-selection-p
Not updating the X PRIMARY selection with the current visual region, not sure how it works under wayland though.

I've also steal two functions from Doomemacs,

+evil/window-vsplit-and-follow
which is bound to SPC w v.
+evil/window-split-and-follow
which is bound to SPC w s.

Why is this the first package in my list? My right hand fingers always land on h, j, k, l. You can't teach old dog new tricks. I've used the vanilla keybindings, it's great but it's faster with /evil-mode.

  • Useful keybindings

    Not a full list of keybindings, just ones that I should remember to use.

    keybindings function description
    normal-state ] b evil-next-buffer Go to the COUNTth next buffer in the buffer list.
    normal-state [ b evil-previous-buffer Go to the COUNTth previous buffer in the buffer list.
    normal-state ] SPC evil-collection-unimpaired-insert-newline-below Insert COUNT blank line(s) below current line.
    normal-state [ SPC evil-collection-unimpaired-insert-newline-above Insert COUNT blank line(s) above current line.

evil-collection   external

This is a collection of Evil bindings for the parts of Emacs that Evil does not cover properly by default, such as help-mode, M-x calendar, Eshell and more.

(use-package evil-collection
  :after evil
  :ensure t
  :init
  (evil-collection-init)
  :config
  (with-eval-after-load 'dired
    (evil-collection-dired-setup))
  (with-eval-after-load 'calendar
    (evil-collection-calendar-setup))
  :custom
  (evil-collection-setup-minibuffer t)
  (evil-collection-calendar-want-org-bindings t))

evil-terminal-cursor-changer   external

Change cursor shape and color by evil state in terminal.

(use-package evil-terminal-cursor-changer
  :hook (tty-setup . evil-terminal-cursor-changer-activate)
  :ensure t
  :config
  (setopt evil-motion-state-cursor 'box)  ;   (setopt evil-visual-state-cursor 'box)  ;   (setopt evil-normal-state-cursor 'box)  ;   (setopt evil-insert-state-cursor 'bar)  ;   (setopt evil-emacs-state-cursor  'hbar) ; _
  )

evil-surround   external

This package emulates surround.vim by Tim Pope. The functionality is wrapped into a minor mode.

(use-package evil-surround
  :after evil
  :ensure t
  :commands (global-evil-surround-mode
             evil-surround-edit
             evil-Surround-edit
             evil-surround-region)
  :hook
  (html-mode . (lambda () (push '(?~ . ("<" . ">")) evil-surround-pairs-alist)))
  (org-mode . (lambda () (push '(?~ . ("~" . "~")) evil-surround-pairs-alist)))
  (org-mode . (lambda () (push '(?~ . ("<" . ">")) evil-surround-pairs-alist)))
  (org-mode . (lambda () (push '(?$ . ("\\(" . "\\)")) evil-surround-pairs-alist)))
  :init
  (global-evil-surround-mode 1))
  • Usage
    • Add surrounding

      You can surround in visual-state with S<textobject> or gS<textobject>. Or in normal-state with ys<textobject> or yS<textobject>. I'm still using the Shift I in visual mode, and the ys in normal mode conflicts with which-key-mode.

    • Change surrounding

      You can change a surrounding with cs<old-textobject><new-textobject>.

evil-embrace   external

This package provides evil integration of embrace.el. Since evil-surround provides a similar set of features as embrace.el, this package aims at adding the goodies of embrace.el to evil-surround and making evil-surround even better.

(use-package evil-embrace
  :ensure t
  :commands embrace-add-pair embrace-add-pair-regexp
  :hook (LaTeX-mode . embrace-LaTeX-mode-hook)
  :hook (LaTeX-mode . +evil-embrace-latex-mode-hook-h)
  :hook (org-mode . embrace-org-mode-hook)
  :hook (ruby-mode . embrace-ruby-mode-hook)
  :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook)
  :init
  (with-eval-after-load 'evil-surround
    (evil-embrace-enable-evil-surround-integration))
  :config
  ;; from doom
  (defun +evil--embrace-latex ()
    "LaTeX command support for embrace."
    (cons (format "\\%s{" (read-string "\\")) "}"))

  (defun +evil-embrace-latex-mode-hook-h ()
    (dolist (pair '((?\' . ("`" . "\'"))
                    (?\" . ("``" . "\'\'"))))
      (delete (car pair) evil-embrace-evil-surround-keys)
      ;; Avoid `embrace-add-pair' because it would overwrite the default
      ;; rules, which we want for other modes
      (push (cons (car pair) (make-embrace-pair-struct
                              :key (car pair)
                              :left (cadr pair)
                              :right (cddr pair)
                              :left-regexp (regexp-quote (cadr pair))
                              :right-regexp (regexp-quote (cddr pair))))
            embrace--pairs-list))
    (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex))
  (defun +evil-embrace-angle-bracket-modes-hook-h ()
    (let ((var (make-local-variable 'evil-embrace-evil-surround-keys)))
      (set var (delq ?< evil-embrace-evil-surround-keys))
      (set var (delq ?> evil-embrace-evil-surround-keys)))
    (embrace-add-pair-regexp ?< "\\_<[a-z0-9-_]+<" ">" #'+evil--embrace-angle-brackets)
    (embrace-add-pair ?> "<" ">")))

evil-escape   external

Customizable key sequence to escape from insert state and everything else in Emacs. In short, when in insert-mode, you can easily switch to normal-mode just by quickly type jk.

(use-package evil-escape
  :after evil
  :ensure t
  :hook (evil-after-load . evil-escape-mode)
  :init
  (setopt evil-escape-excluded-states '(normal visual multiedit emacs motion)
        evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode dired-mode)
        evil-escape-key-sequence "jk"
        evil-escape-delay 0.15)
  (evil-escape-mode 1)
  (evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape))

evil-exchange   external

(use-package evil-exchange
  :ensure t
  :init
  (evil-exchange-install))

Easy text exchange operator for Evil. This is the port of vim-exchange by Tom McDonald.

  • Default Bindings
    • gx (evil-exchange)

      On the first use, define (and highlight) the first {motion} to exchange. On the second use, define the second {motion} and perform the exchange.

    • gX (evil-exchange-cancel)

      Clear any {motion} pending for exchange.

evil-nerd-commenter   external

A Nerd Commenter emulation, help you comment code efficiently. For example, you can press 99,ci to comment out 99 lines (only works if you set the (evilnc-default-hotkeys t)).

(use-package evil-nerd-commenter
  :after evil
  :ensure t
  :commands (evilnc-comment-operator
             evilnc-inner-comment
             evilnc-outer-commenter)
  :bind ([remap comment-line] . evilnc-comment-or-uncomment-lines)
  :init
  (setopt my/evilnc-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "gc") (cons "Comment" my/evilnc-map))
  (evil-define-key* nil my/evilnc-map
    "c" '("comment/uncomment" . evilnc-comment-operator)))

evil-traces   external

evil-traces is a port of traces.vim. It enables visual previews for certain evil-ex commands.

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

evil-visualstar   external

This is a port of one of the many visual-star plugins for Vim to work with evil-mode.

(use-package evil-visualstar
  :ensure t
  :commands (evil-visualstar/begin-search
             evil-visualstar/begin-search-forward
             evil-visualstar/begin-search-backward)
  :init
  (evil-define-key* 'visual 'global
    "*" #'evil-visualstar/begin-search-forward
    "#" #'evil-visualstar/begin-search-backward))

evil-vimish-fold   external

Integration of vimish-fold with evil

Adds standard vim keybindings of zf and zd to create and delete folds (via vimish-fold) respectively. Fold now can be created with <leader>zf in a region, after that, it hooks into evil so the usual vim keybindings for fold toggling (za), opening (zo), closing (zc) etc all work as expected with vimish-fold.

Finally, also supports navigation between folds using zj and zk.

This provides a near-complete vim folding experience in evil for Emacs.

(use-package evil-vimish-fold
  :ensure t
  :demand t
  :bind
  (([remap evil-toggle-fold] . vimish-fold-toggle)
   ([remap evil-close-fold] . vimish-fold-refold)
   ([remap evil-open-fold] . vimish-fold-unfold)
   ([remap evil-close-folds] . vimish-fold-refold-all)
   ([remap evil-open-folds] . vimish-fold-unfold-all))
  :commands (evil-vimish-fold/next-fold evil-vimish-fold/previous-fold
                                        evil-vimish-fold/delete evil-vimish-fold/delete-all
                                        evil-vimish-fold/create evil-vimish-fold/create-line)
  :init
  (setopt evil-vimish-fold-mode-lighter " ⮒")
  (setopt evil-vimish-fold-target-modes '(prog-mode conf-mode text-mode))
  (setopt my/vimish-fold-map (make-sparse-keymap))
  :config
  (global-evil-vimish-fold-mode +1))

evil-multiedit   external

(use-package evil-multiedit
  :ensure t
  :config
  (defun make-evil-multiedit-case-sensitive (fn &rest args)
    (let ((case-fold-search (not iedit-case-sensitive)))
      (apply fn args)))
  (advice-add #'evil-multiedit-match-and-next :around #'make-evil-multiedit-case-sensitive)
  (evil-multiedit-default-keybinds)
  (evil-define-key 'normal 'global
    (kbd "M-d")   #'evil-multiedit-match-symbol-and-next
    (kbd "M-D")   #'evil-multiedit-match-symbol-and-prev)
  (evil-define-key 'visual 'global
    "R"           #'evil-multiedit-match-all
    (kbd "M-d")   #'evil-multiedit-match-and-next
    (kbd "M-D")   #'evil-multiedit-match-and-prev)
  (evil-define-key '(visual normal) 'global
    (kbd "C-M-d") #'evil-multiedit-restore)

  (with-eval-after-load 'evil-mutliedit
    (evil-define-key 'multiedit 'global
      (kbd "M-d")   #'evil-multiedit-match-and-next
      (kbd "M-S-d") #'evil-multiedit-match-and-prev
      (kbd "RET")   #'evil-multiedit-toggle-or-restrict-region)
    (evil-define-key '(multiedit multiedit-insert) 'global
      (kbd "C-n")   #'evil-multiedit-next
      (kbd "C-p")   #'evil-multiedit-prev)))

Hlissner's implementation of multiple-cursor for evil-mode.

evil-org-mode   external

Supplemental evil-mode keybindings to emacs org-mode.

(use-package evil-org
  :after org
  :ensure t
  :hook (org-mode . (lambda () evil-org-mode))
  :config
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys))

evil-goggles   external

Display visual hint on evil edit operations.

(use-package evil-goggles
  :ensure t
  :config
  (evil-goggles-mode)
  (evil-goggles-use-diff-faces))

avy   external

By using gK or gJ in normal or visual mode.

(use-package avy
  :ensure t
  :config
  (setopt avy-background t)
  (setopt avy-timeout-seconds 0.5)
  (evil-define-key* '(normal visual) 'global
      "gJ" 'evil-avy-goto-line-below
      "gK" 'evil-avy-goto-line-above))

Keybindings

Leader key

This is taken from vim, The leader key in Vim and Neovim is a special key used as a prefix for custom keyboard shortcuts, also known as mappings. It creates a "namespace" for user-defined commands, allowing you to create personalized shortcuts without conflicting with Vim's default commands.

(with-eval-after-load 'evil
  (evil-set-leader '(normal visual) (kbd "SPC")))

1st level commands

These commands is used often so it will need a faster keybindings (<leader> <key>).

(with-eval-after-load 'evil
  (evil-global-set-key 'normal (kbd "<leader>:") '("execute commands" . execute-extended-command))
  (evil-global-set-key 'normal (kbd "<leader>;") '("eval expression" . eval-expression))
  (evil-global-set-key 'normal (kbd "<leader>u") '("universal arguments" . universal-argument)))

A

(with-eval-after-load 'evil
  (setopt my/a-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>a") (cons "Act" my/a-map)))

C

For now it's reserved only for org-capture.

(with-eval-after-load 'evil
  (setopt my/c-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>c") (cons "C" my/c-map))
  (with-eval-after-load 'org
    (evil-define-key* nil my/c-map "c" '("org-capture" . org-capture))))

B

All keybindings will go with <leader>b.

(with-eval-after-load 'evil
  (setopt my/buffer-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>b") (cons "Buffer" my/buffer-map))
  (evil-define-key* nil my/buffer-map
    "." '("set bookmark" . bookmark-set)
    "[" '("previous buffer" . previous-buffer)
    "]" '("next buffer" . next-buffer)
    "c" '("clone indirect buffer" . clone-indirect-buffer)
    "C" '("clone indirect buffer other window" . clone-indirect-buffer-other-window)
    "d" '("kill buffer" . kill-buffer)
    "i" '("ibuffer" . ibuffer)
    "k" '("kill buffer" . kill-buffer)
    "l" '("switch to last buffer" . evil-switch-to-windows-last-buffer)
    "m" '("switch to message buffer". (lambda () (interactive) (switch-to-buffer "*Messages*")))
    "n" '("next buffer" . next-buffer)
    "N" '("new buffer" . evil-buffer-new)
    "p" '("previous buffer" . previous-buffer)
    "o" '("other buffer" . (lambda () (interactive) (switch-to-buffer nil)))
    "r" '("revert buffer" . revert-buffer)
    "R" '("rename buffer" . rename-buffer)
    "s" '("save buffer" . save-buffer)
    "x" '("switch to scratch buffer" . (lambda () (interactive) (switch-to-buffer "*scratch*")))
    "z" '("bury buffer" . bury-buffer)))
  • repeat-map

    These function will be available for the repeat-mode.

    (with-eval-after-load 'evil
      (defvar my/buffer-map-repeat-map
        (let ((map (make-sparse-keymap)))
          (define-key map "n" #'next-buffer)
          (define-key map "p" #'previous-buffer)
          (define-key map "[" #'previous-buffer)
          (define-key map "]" #'next-buffer)
          map)
        "Keymap for my/buffer-map repeat")
      (put 'next-buffer 'repeat-map 'my/buffer-map-repeat-map)
      (put 'previous-buffer 'repeat-map 'my/buffer-map-repeat-map))
    

F

Perhaps the most used keybindings, <leader>f.

(with-eval-after-load 'evil
  (setopt my/file-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>f") (cons "Files" my/file-map))
  (evil-define-key* nil my/file-map
    "f" '("find file" . find-file)
    "d" '("jump to current directory" . dired-jump)
    "i" '("open init file" . (lambda () (interactive)
                               (find-file
                                (expand-file-name "init.el" user-emacs-directory))))
    "k" '("delete frame" . delete-frame)
    "r" '("recent file" . recentf)
    "s" '("save buffer" . save-buffer)
    "S" '("Save as" . (lambda () (interactive "F") (write-file "./" t)))
    "t" '("find-file other tab" . find-file-other-tab)
    "w" '("find file other window" . find-file-other-window)))

G

All git-related commands goes here, <leader>g.

(with-eval-after-load 'evil
  (setopt my/g-map (make-sparse-keymap))
    (evil-define-key* '(normal visual) 'global (kbd "<leader>g") (cons "G" my/g-map)))

H

This key <leader>h just simulate the standard Emacs C-h.

(with-eval-after-load 'evil
  (evil-define-key* '(normal visual) 'global (kbd "<leader>h") (cons "Help" help-map)))

I

Commands for quick insertion <leader>i.

(with-eval-after-load 'evil
  (setopt my/insert-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>i") (cons "I" my/insert-map))
  (evil-define-key* nil my/insert-map "c" '("insert character" . insert-char))
  (evil-define-key* nil my/insert-map "d" '("duplicate-dwim" . duplicate-dwim)))
  • repeat-map
    (with-eval-after-load 'evil
      (defvar my/insert-map-repeat-map
        (let ((map (make-sparse-keymap)))
          (define-key map "d" #'duplicate-dwim)
          map)
        "Keymap for my/insert-map repeat")
      (put 'duplicate-dwim 'repeat-map 'my/insert-map-repeat-map))
    

O

Misc quick commands, also for org-mode <leader>o.

(with-eval-after-load 'evil
  (setopt my/org-mode-map (make-sparse-keymap))
  (setopt my/open-map (make-sparse-keymap))
  (setopt my/open-org (make-sparse-keymap))
  (setopt my/open-roam-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>o") (cons "O" my/open-map))
  (evil-define-key* nil my/open-map (kbd "o") (cons "Org files" my/open-org))
  (with-eval-after-load 'org-roam
    (evil-define-key* nil my/open-map (kbd "r") (cons "Org Roam" my/open-roam-map)))

  (evil-define-key* nil my/open-map
    "a" '("Org Agenda" . org-agenda)
    "c" '("Org Capture" . org-capture)
    "L" '("Store org link" . org-store-link)
    "l" '("Avy open link" . link-hint-open-link))

  (with-eval-after-load 'notmuch
    (evil-define-key* nil my/open-map
      "m" '("mail" . notmuch)))

  (evil-define-key* nil my/open-org
    (kbd "n") '("Notes" . (lambda () (interactive) (find-file (expand-file-name "notes.org" +config/org-directory))))
    (kbd "r") '("Routines" . (lambda () (interactive) (find-file (expand-file-name "routines.org" +config/org-directory))))
    (kbd "p") '("Personal" . (lambda () (interactive) (find-file (expand-file-name "personal.org" +config/org-directory))))
    (kbd "P") '("Projects" . (lambda () (interactive) (find-file (expand-file-name "projects.org" +config/org-directory))))
    (kbd "R") '("Last refiled" . org-refile-goto-last-stored)
    (kbd "C") '("Last Capture" . org-capture-goto-last-stored)))

S

Maybe the third most used keys, <leader>s.

(with-eval-after-load 'evil
  (setopt my/search-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>s") (cons "S" my/search-map)))

T

(with-eval-after-load 'evil
  (setopt my/tab-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>t") (cons "T" my/tab-map)))

W

The second most used keybindings, I will try to use the default vim keys.

(with-eval-after-load 'evil
  (setopt my/window-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>w") (cons "W" my/window-map))
  (evil-define-key* nil my/window-map
    "C-o" '("delete other window" . delete-other-windows)
    "[" '("left window" . evil-window-left)
    "]" '("right window" . evil-window-right)
    "=" '("enlarge window" . enlarge-window)
    "-" '("shrink window" . shrink-window)
    "}" '("enlarge horizontal" . enlarge-window-horizontally)
    "{" '("shrink horizontal" . shrink-window-horizontally)
    "+" '("increase height" . evil-window-increase-height)
    "-" '("decrease height" . evil-window-decrease-height)
    "<" '("decrease width" . evil-window-decrease-width)
    ">" '("increase width" . evil-window-increase-width)
    "b" '("bottom right" . evil-window-bottom-right)
    "c" '("delete" . evil-window-delete)
    "d" '("delete" . evil-window-delete)
    "h" '("left window" . evil-window-left)
    "f" '("find file other window" . ffap-other-window)
    "j" '("down window" . evil-window-down)
    "k" '("up window" . evil-window-up)
    "l" '("window right" . evil-window-right)
    "n" '("new window" . evil-window-new)
    "o" '("delete other" . delete-other-windows)
    "m" '("mru" . evil-window-mru)
    "q" '("quit window" . evil-quit)
    "r" '("rotate downward" . evil-window-rotate-downwards)
    "R" '("rotate upward" . evil-window-rotate-upwards)
    "s" '("split horizontally and follow" . +evil/window-split-and-follow)
    "T" '("tear off window" . tear-off-window)
    "t" '("top left window" . evil-window-top-left)
    "u" '("undo" . winner-undo)
    "v" '("split vertical and follow" . +evil/window-vsplit-and-follow)
    "w" '("other window" . other-window)
    "W" '("previous window" . evil-window-prev)
    "x" '("exchange window" . evil-window-exchange)
    "|" '("set width" . evil-window-set-width)
    (kbd "<left>") '("left window" . evil-window-left)
    (kbd "<right>") '("right window" . evil-window-right)
    (kbd "<down>") '("down window" . evil-window-down)
    (kbd "<up>") '("up window" . evil-window-up)))
  • repeat-map
    (with-eval-after-load 'evil
      (defvar my/window-split-repeat-map
        (let ((map (make-sparse-keymap)))
          (define-key map "s" #'+evil/window-split-and-follow)
          (define-key map "v" #'+evil/window-vsplit-and-follow)
          map)
        "Keymap for my/window-exchange repeat")
      (put '+evil/window-split-and-follow 'repeat-map 'my/window-split-repeat-map)
      (put '+evil/window-vsplit-and-follow 'repeat-map 'my/window-split-repeat-map))
    
    (with-eval-after-load 'evil
      (defvar my/window-exchange-repeat-map
        (let ((map (make-sparse-keymap)))
          (define-key map "x" #'evil-window-exchange)
          map)
        "Keymap for my/window-exchange repeat")
      (put 'evil-window-exchange 'repeat-map 'my/window-exchange-repeat-map))
    
    (with-eval-after-load 'evil
      (defvar my/winner-undo-repeat-map
        (let ((map (make-sparse-keymap)))
          (define-key map "u" #'winner-undo)
          map)
        "Keymap for my/winner-undo repeat")
      (put 'winner-undo 'repeat-map 'my/winner-undo-repeat-map))
    
    (with-eval-after-load 'evil
      (defvar my/window-rotate-repeat-map
        (let ((map (make-sparse-keymap)))
          (define-key map "r" #'evil-window-rotate-downwards)
          (define-key map "R" #'evil-window-rotate-downwards)
          map)
        "Keymap for my/window-rotate repeat")
      (put 'evil-window-rotate-upwards 'repeat-map 'my/window-rotate-repeat-map)
      (put 'evil-window-rotate-downwards 'repeat-map 'my/window-rotate-repeat-map))
    
    (with-eval-after-load 'evil
      (defvar my/window-size-repeat-map
        (let ((map (make-sparse-keymap)))
          (define-key map "=" #'enlarge-window)
          (define-key map "_" #'shrink-window)
          (define-key map "{" #'enlarge-window-horizontally)
          (define-key map "}" #'shrink-window-horizontally)
          (define-key map "<" #'evil-window-decrease-width)
          (define-key map ">" #'evil-window-increase-width)
          (define-key map "+" #'evil-window-increase-height)
          (define-key map "-" #'evil-window-decrease-height)
          map)
        "Keymap for my/buffer-map repeat")
      (put 'evil-window-increase-height 'repeat-map 'my/window-size-repeat-map)
      (put 'evil-window-decrease-height 'repeat-map 'my/window-size-repeat-map)
      (put 'evil-window-decrease-width 'repeat-map 'my/window-size-repeat-map)
      (put 'evil-window-increase-width 'repeat-map 'my/window-size-repeat-map)
      (put 'enlarge-window-horizontally 'repeat-map 'my/window-size-repeat-map)
      (put 'shrink-window-horizontally 'repeat-map 'my/window-size-repeat-map)
      (put 'enlarge-window 'repeat-map 'my/window-size-repeat-map)
      (put 'shrink-window 'repeat-map 'my/window-size-repeat-map))
    

Q

Also used for reloading configuration (like Doomemacs) <leader>q.

(with-eval-after-load 'evil
  (setopt my/quit-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>q") (cons "Q" my/quit-map))
  (evil-define-key* nil my/quit-map
    "R" '("restart Emacs" . restart-emacs)
    "r" '("reload configuration" . (lambda () (interactive) (load user-init-file)))
    "q" '("quit Emacs and save" . save-buffers-kill-terminal)))

Z keys

This is for other evil packages that need its own keybinding, and tbf, there's already a lot of them in the g and z keys.

(with-eval-after-load 'evil
  (setopt my/z-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>z") (cons "Z" my/z-map)))

Builtins

base   builtin

(use-package emacs
  :ensure nil
  :config
  (delete-selection-mode 1)
  (transient-mark-mode 1)
  (setopt use-short-answers t
        delete-by-moving-to-trash t
        create-lockfiles nil
        load-prefer-newer t
        read-buffer-completion-ignore-case t
        read-file-name-completion-ignore-case t))

repeat-mode   builtin

(use-package repeat
  :ensure nil
  :init
  (repeat-mode +1))

files   builtin

Usually configurations related to file manipulations.

(use-package files
  :ensure nil
  :config
  (nconc
   auto-mode-alist
   '(("/LICENSE\\'" . text-mode)
     ("\\.log\\'" . text-mode)
     ("rc\\'" . conf-mode)
     ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode)))
  :hook
  ((prog-mode text-mode) . auto-save-visited-mode)
  :custom
  (auto-save-visited-interval 10)
  (find-file-suppress-same-file-warnings t)
  ;;(confirm-kill-emacs #'yes-or-no-p) ; confirm when exiting
  (confirm-kill-processes nil) ; don't confirm killing processes
  (revert-without-query (list "."))
  (find-file-visit-truename t) ; `find-file' will visit the actual file
  (make-backup-files nil)
  (version-control t)
  (backup-by-copying t)
  (delete-old-versions t)
  (kept-new-versions 6)
  (kept-old-versions 2)
  (auto-save-include-big-deletions t)
  (auto-save-list-file-prefix (expand-file-name ".autosave/" user-emacs-directory))
  (backup-directory-alist `(("." . ,(expand-file-name ".backup" user-emacs-directory))))
  (auto-mode-case-fold nil)
  (require-final-newline t))

savehist   builtin

Saving the minibuffer history.

(use-package savehist
  :init
  (savehist-mode 1)
  :custom
  (savehist-file (expand-file-name "history" user-emacs-directory))
  (savehist-save-minibuffer-history t)
  (savehist-autosave-interval nil)
  (savehist-coding-system 'utf-8)
  (savehist-additional-variables
   '(evil-jumps-history
     command-history
     kill-ring
     register-alist
     mark-ring
     global-mark-ring
     search-ring
     regexp-search-ring)))

comint   builtin

(use-package comint
  :ensure nil
  :config
  (setopt comint-prompt-read-only t
        comint-buffer-maximum-size 2048)
  (evil-define-key* '(normal visual) comint-mode-map "q" '(lambda () (interactive) (kill-buffer nil))))

UI

base UI   builtin

(use-package emacs
  :ensure nil
  :config
  (when (bound-and-true-p tooltip-mode)
    (tooltip-mode -1))
  (setopt hscroll-margin 2
        hscroll-step 1
        scroll-conservatively 10
        scroll-margin 0
        scroll-preserve-screen-position t
        auto-window-vscroll nil
        window-resize-pixelwise t
        frame-resize-pixelwise t
        use-dialog-box nil
        use-file-dialog nil
        echo-keystrokes 0.02
        resize-mini-windows 'grow-only
        inhibit-startup-screen t
        inhibit-compacting-font-caches t
        minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)))

font-core   builtin

(use-package font-core
  :ensure nil
  :init
  (global-font-lock-mode t))
  • Fonts
    (set-face-attribute 'default nil :family "Iosevka Nerd Font Mono" :height 80)
    (set-face-attribute 'variable-pitch nil :family "Iosevka Nerd Font Mono" :height 80)
    
    ;;(set-frame-font "Iosevka Nerd Font Mono 8" nil t)
    

    the :height value is 1/10 pt, so 100 height means 10 pt.

which-key   builtin

Emacs package that displays available keybindings in popup. This is one of the top Emacs packages that I must have in my configuration. Now built in into Emacs.

(use-package which-key
  :ensure nil
  :custom
  (which-key-lighter "")
  (which-key-sort-order #'which-key-key-order-alpha)
  (which-key-sort-uppercase-first nil)
  (which-key-add-column-padding 1)
  (which-key-max-display-columns nil)
  (which-key-min-display-lines 6)
  (which-key-compute-remaps t)
  (which-key-side-window-slot -10)
  (which-key-separator " → ")
  (which-key-allow-evil-operators nil)
  (which-key-use-C-h-commands t)
  (which-key-show-remaining-keys t)
  (which-key-show-prefix 'bottom)
  (which-key-show-operator-state-maps nil)
  :config
  (which-key-mode)
  (which-key-setup-side-window-bottom)
  (which-key-setup-minibuffer)
  (define-key which-key-mode-map (kbd "C-x <f5>") 'which-key-C-h-dispatch))

winner   builtin

(use-package winner
  :ensure nil
  :init
  (winner-mode +1)
  :config
  (setopt winner-boring-buffers '("*Completions*" "*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*"
                                "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*"
                                "*esh command on file*")))

marginalia   external

(use-package marginalia
  :ensure t
  :bind (:map minibuffer-local-map
              ("M-A" . marginalia-cycle))
  :init
  (marginalia-mode))

nerd-icons-completion   external

(use-package nerd-icons-completion
  :after marginalia
  :ensure t
  :hook (marginalia-mode . nerd-icons-completion-marginalia-setup)
  :config
  (nerd-icons-completion-mode))

rainbow-mode   external

(use-package rainbow-mode
  :hook (prog-mode . rainbow-mode)
  :ensure t
  :config
  (setopt rainbow-html-colors-major-mode-list
        '(prog-mode conf-mode html-mode css-mode php-mode nxml-mode xml-mode)
        rainbow-html-colors t))

rainbow-identifiers   external

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

rainbow-delimiters   external

(use-package rainbow-delimiters
  :ensure t
  :hook (prog-mode . rainbow-delimiters-mode)
  :config
  (setopt rainbow-delimiters-max-face-count 4))

indent-bars   external

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

nerd-icons   external

(use-package nerd-icons
  :ensure t)

doom-themes   external

(use-package doom-themes
  :ensure t
  :custom
  (doom-themes-enable-bold t)
  (doom-themes-enable-italic t)
  :config
  (load-theme 'doom-moonlight t)
  (doom-themes-visual-bell-config)
  (doom-themes-org-config))

doom-modeline   external

(use-package doom-modeline
  :ensure t
  :hook (after-init . doom-modeline-mode)
  :config
  (setopt doom-modeline-project-detection 'auto
        doom-modeline-indent-info t))

window   builtin

(use-package window
  :ensure nil
  :config
  (setopt split-width-threshold 160
      split-height-threshold nil))

Shells and Terminals

vterm   external

(use-package vterm
  :ensure t
  :demand t
  :commands vterm-mode
  :config
  (setopt my/vterm-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>v") (cons "Vterm" my/vterm-map))
  (evil-define-key* nil my/vterm-map "V" '("terminal" . vterm))
  (evil-collection-vterm-setup)
  (add-hook 'vterm-mode-hook
          (lambda ()
            (setq-local global-hl-line-mode nil)
            (setq-local hscroll-margin 0)))
  (setopt vterm-kill-buffer-on-exit t)
  (evil-set-initial-state #'vterm-mode 'insert))

multi-vterm   external

(use-package multi-vterm
  :after vterm
  :ensure t
  :config
  (setopt multi-vterm-dedicated-window-height-percent 20)
  (evil-define-key* nil my/vterm-map "v" '("toggle terminal" . multi-vterm-dedicated-toggle))
  (global-set-key (kbd "C-\\") #'multi-vterm-dedicated-toggle)
  (evil-define-key 'insert vterm-mode-map (kbd "C-\\") #'multi-vterm-dedicated-toggle)
  (evil-define-key* 'normal vterm-mode-map (kbd ",c") #'multi-vterm)
  (evil-define-key* 'normal vterm-mode-map (kbd ",n") #'multi-vterm-next)
  (evil-define-key* 'normal vterm-mode-map (kbd ",p") #'multi-vterm-prev))

Editing

saveplace   builtin

When closing a file (like when exiting Emacs or killing a buffer), the point (or cursor) in that buffer will be saved. I find this a must-have feature.

(use-package saveplace
  :init
  (save-place-mode 1)
  :custom
  (save-place-file (expand-file-name "places" user-emacs-directory)))

recentf   builtin

(use-package recentf
  :bind ("C-c f" . recentf)
  :commands recentf-open-files
  :init
  (recentf-mode 1)
  :custom
  (recentf-auto-cleanup t)
  (recentf-max-saved-items 250)
  (recentf-max-menu-items 300)
  (recentf-exclude
   `("/elpa/" ;; ignore all files in elpa directory
     "recentf" ;; remove the recentf load file
     ".*?autoloads.el$"
     "treemacs-persist"
     "company-statistics-cache.el" ;; ignore company cache file
     "/intero/" ;; ignore script files generated by intero
     "/journal/" ;; ignore daily journal files
     ".gitignore" ;; ignore `.gitignore' files in projects
     "/tmp/" ;; ignore temporary files
     "NEWS" ;; don't include the NEWS file for recentf
     "bookmarks"  "bmk-bmenu" ;; ignore bookmarks file in .emacs.d
     "loaddefs.el"
     "^/\\(?:ssh\\|su\\|sudo\\)?:" ;; ignore tramp/ssh files
     (concat "^" (regexp-quote (or (getenv "XDG_RUNTIME_DIR")
                                   "/run"))))))

Even though I use consult-find-file the actual recentf package is still used (I think).

autorevert   builtin

Here I'm using global-auto-revert-mode to automatically revert all opened buffer.

(use-package autorevert
  :hook (focus-in . doom-auto-revert-buffers-h)
  :hook (after-save . doom-auto-revert-buffers-h)
  :hook (prog-mode . doom-auto-revert-buffer-h)
  :custom
  (auto-revert-interval 60)
  (auto-revert-use-notify nil)
  (global-auto-revert-non-file-buffers t)
  (auto-revert-verbose t)
  (auto-revert-stop-on-user-input nil)
  (revert-without-query (list "."))
  :config
  (defun doom-visible-buffer-p (buf)
    "Return non-nil if BUF is visible."
    "Return non-nil if BUF is not visible."
    (not (doom-visible-buffer-p buf)))
  (defun doom-visible-buffers (&optional buffer-list all-frames)
"Return a list of visible buffers (i.e. not buried)."
(let ((buffers
       (delete-dups
        (cl-loop for frame in (if all-frames (visible-frame-list) (list (selected-frame)))
                 if (window-list frame)
                 nconc (mapcar #'window-buffer it)))))
  (if buffer-list
      (cl-delete-if (lambda (b) (memq b buffer-list))
                    buffers)
    (delete-dups buffers))))
  (defun doom-auto-revert-buffer-h ()
    "Auto revert current buffer, if necessary."
    (unless (or auto-revert-mode (active-minibuffer-window))
      (let ((auto-revert-mode t))
        (auto-revert-handler))))
  (defun doom-auto-revert-buffers-h ()
    "Auto revert stale buffers in visible windows, if necessary."
    (dolist (buf (doom-visible-buffers))
      (with-current-buffer buf
        (doom-auto-revert-buffer-h)))))

mouse   builtin

(use-package mouse
  :ensure nil
  :config
  (setopt mouse-yank-at-point t))

subword   builtin

(use-package subword
  :ensure nil
  :init
  (global-subword-mode 1))

text-mode   builtin

(use-package text-mode
  :ensure nil
  :hook (((text-mode prog-mode) . visual-line-mode)
         (prog-mode . (lambda () (setq-local sentence-end-double-space t))))
  :config
  (setq-default sentence-end-double-space nil)
  (setopt sentence-end-without-period nil)
  (setopt colon-double-space nil)
  (setopt adaptive-fill-mode t))

select   builtin

(use-package select
  :ensure nil
  :custom
  (select-enable-clipboard t))

ws-butler   external

(use-package ws-butler
  :ensure t
  :hook ((prog-mode text-mode) . ws-butler-mode))

IDE stuffs

treesit-auto   external

(add-to-list 'treesit-language-source-alist '(html "https://github.com/tree-sitter/tree-sitter-html"))
(add-to-list 'treesit-language-source-alist '(json "https://github.com/tree-sitter/tree-sitter-json"))

(use-package treesit-auto
  :ensure t
  :custom
  (treesit-auto-install 'prompt)
  :config
  (add-hook 'yaml-ts-mode-hook '(lambda () (setq-local tab-width 2)))
  (add-hook 'json-ts-mode-hook '(lambda () (setq-local tab-width 2)))
  (add-hook 'python-ts-mode-hook '(lambda () (setq-local tab-width 4)))
  (add-hook 'c-ts-mode-hook '(lambda () (setq-local tab-width 4)))
  ;; mjs
  (add-to-list 'auto-mode-alist '("\\.mjs\\'" . js-ts-mode))
  (treesit-auto-add-to-auto-mode-alist 'all)
  (global-treesit-auto-mode))

lsp-mode   external

(use-package lsp-mode
  :ensure t
  :hook ((nix-ts-mode lua-ts-mode python-ts-mode html-ts-mode
                    yaml-ts-mode toml-ts-mode json-ts-mode
                    bash-ts-mode js-ts-mode rust-ts-mode
                    css-ts-mode typescript-ts-mode c-ts-mode) . lsp-deferred)
  :hook (lsp-completion-mode . my/lsp-mode-setup-completion)
  :commands (lsp lsp-deferred)
  :init
  (setopt lsp-keymap-prefix "C-c l")
  (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
        '(orderless))
    (add-hook 'orderless-style-dispatchers #'my/orderless-dispatch-flex-first nil 'local)
    (setq-local completion-at-point-functions (list (cape-capf-buster #'lsp-completion-at-point))))
  :config
  (evil-define-key 'normal lsp-mode-map
    (kbd "gD") 'lsp-goto-implementation
    (kbd "gA") 'lsp-execute-code-action
    (kbd "gr") 'lsp-find-references
    (kbd "gR") 'lsp-rename
    (kbd "K")  'lsp-describe-thing-at-point
    (kbd "<leader>ma") 'lsp-avy-lens
    (kbd "<leader>aA") 'lsp-execute-code-action)
  :custom
  (lsp-completion-provider :none))

(use-package lsp-ui
  :ensure t
  :commands lsp-ui-mode)

yasnippets   external

This is provided by yasnippet.

(use-package yasnippet
  :ensure t
  :config
  (yas-global-mode)
  (define-key yas-keymap [tab] 'yas-next-field)
  (setopt yas-snippets-dir
        (list (expand-file-name "snippets" user-emacs-directory)
              (expand-file-name "straight/build/yasnippet-snippets/snippets" user-emacs-directory)))
  (evil-define-key* nil my/insert-map "s" '("insert snippet" . consult-yasnippet)))

(use-package consult-yasnippet :ensure t)

(use-package yasnippet-snippets :ensure t)

yasnippet-capf   external

(use-package yasnippet-capf
  :after cape
  :ensure t
  :config
  (add-to-list 'completion-at-point-functions #'yasnippet-capf))

projectile   external

A project interaction library for Emacs, with little external dependency as possible.

(use-package projectile
  :ensure t
  :demand t
  :commands (projectile-project-root
           projectile-project-name
           projectile-project-p
           projectile-locate-dominating-file
           projectile-relevant-known-projects)
  :bind (([remap evil-jump-to-tag] . projectile-find-tag)
       ([remap find-tag] . projectile-find-tag))
  :hook (dired-before-readin . projectile-track-known-projects-find-file-hook)
  :custom
  (projectile-cache-file (expand-file-name ".projects" user-emacs-directory))
  (projectile-auto-discover nil)
  (projectile-enable-caching (not noninteractive))
  (projectile-globally-ignored-files '("DS_Store" "TAGS"))
  (projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o"))
  (projectile-kill-buffers-filter 'kill-only-files)
  (projectile-known-projects-file (expand-file-name ".projectile_projects.eld" user-emacs-directory))
  (projectile-ignored-projects '("~/"))
  (projectile-project-search-path `(,(expand-file-name "Projects" (getenv "HOME"))))
  (projectile-project-root-files-bottom-up
   (append '(".projectile" ".project" ".git")
         (when (executable-find "hg")
           '(".hg"))
         (when (executable-find "bzr")
           '(".bzr"))))
  (projectile-project-root-files-top-down-recurring '("Makefile"))
  (compilation-buffer-name-function #'projectile-compilation-buffer-name)
  (compilation-save-buffers-predicate #'projectile-current-project-buffer-p)
  (projectile-git-submodule-command nil)
  (projectile-indexing-method 'hybrid)
  :config
  (global-set-key [remap evil-jump-to-tag] #'projectile-find-tag)
  (global-set-key [remap find-tag]         #'projectile-find-tag)
  (projectile-mode +1)
  (put 'projectile-git-submodule-command 'initial-value projectile-git-submodule-command)
    (evil-define-key* '(normal visual) 'global (kbd "<leader>SPC") '("find project file" . projectile-find-file))
    (evil-define-key* '(normal visual) 'global (kbd "<leader>p") (cons "Project" projectile-command-map)))

perspective   external

Useful when I'm working on several projectile projects at once. With each perspective having their own separate buffer list.

(use-package perspective
  :ensure t
  :demand t
  :config
  (persp-mode)
  (setopt persp-initial-frame-name "Main"
        persp-suppress-no-prefix-key-warning t)
  (if (featurep 'no-littering)
      (setopt persp-state-default-file (expand-file-name ".perspective-state" no-littering-var-directory))
    (setopt persp-state-default-file (expand-file-name ".perspective-state" user-emacs-directory)))
  (global-set-key [remap switch-to-buffer] #'persp-switch-to-buffer*)
  (when (featurep 'consult)
    (require 'consult)
    (unless (boundp 'persp-consult-source)
      (defvar persp-consult-source
      (list :name     "Perspective"
            :narrow   ?s
            :category 'buffer
            :state    #'consult--buffer-state
            :history  'buffer-name-history
            :default  t
            :items
            #'(lambda () (consult--buffer-query
                          :sort 'visibility
                          :predicate '(lambda (buf) (persp-is-current-buffer buf t))
                          :as #'buffer-name)))))
    (consult-customize consult--source-buffer :hidden t :default nil)
    (add-to-list 'consult-buffer-sources persp-consult-source))
  (evil-define-key* '(normal visual) 'global
    (kbd "<leader>TAB") (cons "Perspective" perspective-map))
  (evil-define-key* nil perspective-map
    (kbd "TAB") '("swich to last used perspective" . persp-switch-last)
    "P" '("switch project" . projectile-persp-switch-project))
  :init
  (customize-set-variable 'persp-mode-prefix-key (kbd "C-c TAB"))
  :bind (([remap switch-to-buffer] . persp-switch-to-buffer*)
       ([remap kill-buffer] . persp-kill-buffer*))
  :hook (kill-emacs . persp-state-save))

persp-projectile   external

(use-package persp-projectile
  :ensure t
  :after perspective
  :commands projectile-persp-switch-project)

smartparens   external

I've tried to use the built-in electric-pair for a while, it just doesn't have the default configuration for many of the major-modes that I frequently used, and I don't like spending time configuring for each of them. So smartparens it is!

(use-package smartparens
  :ensure t
  :hook (text-mode markdown-mode prog-mode)
  :config
  (require 'smartparens-config))

consult-lsp   external

(use-package consult-lsp
  :ensure t
  :after consult lsp
  :config
  (define-key lsp-mode-map [remap xref-find-apropos] #'consult-lsp-symbols))

dap-mode   external

(use-package dap-mode
  :ensure t)

Org-mode

variables

  • +config/org-directory

    This is for checking the correct directory for org-mode files between my various machines.

    (if (not +config/org-directory)
          (cond
           ((file-directory-p
             (expand-file-name "Dropbox/org" (getenv "HOME")))
            (setopt org-directory (expand-file-name "Dropbox/org" (getenv "HOME"))))
           ((file-directory-p
             (expand-file-name "Sync/org" (getenv "HOME")))
            (setopt org-directory (expand-file-name "Sync/org" (getenv "HOME"))))
           ((file-directory-p
             (expand-file-name "Documents/google-drive/org" (getenv "HOME")))
            (setopt org-directory (expand-file-name "Documents/google-drive/org" (getenv "HOME")))))
        (customize-set-variable 'org-directory +config/org-directory))
    

org-mode core package   builtin

(defun +confing/org-mode-keybindings ()
  (with-eval-after-load 'evil
      (setopt my/org-note-map (make-sparse-keymap))
      (evil-define-key* '(normal visual) org-mode-map (kbd "<leader>m") (cons "org-mode" my/org-mode-map))
      (evil-define-key* nil my/org-mode-map "n" (cons "Notes" my/org-note-map))
      (evil-define-key* nil my/org-mode-map
        (kbd "M-l") 'org-insert-last-stored-link
        (kbd "C-w") '("Refile" . org-refile))
      (evil-define-key nil my/org-note-map
        "c" '("capture" . org-capture)))
  (global-set-key (kbd "C-c l") #'org-store-link)
  (global-set-key (kbd "C-c a") #'org-agenda)
  (global-set-key (kbd "C-c c") #'org-capture))

(use-package org
  :demand t
  :commands org-tempo
  :hook (org-mode . flyspell-mode)
  :hook ((org-mode . org-indent-mode)
         (org-mode . +config/org-prettify-symbols)
         (org-mode . variable-pitch-mode))
  :config
  (with-eval-after-load 'evil
    (setopt my/org-note-map (make-sparse-keymap))
    (evil-define-key* '(normal visual) org-mode-map (kbd "<leader>m") (cons "org-mode" my/org-mode-map))
    (evil-define-key* nil my/org-mode-map "n" (cons "Notes" my/org-note-map))
    (evil-define-key* nil my/org-mode-map
      (kbd "M-l") 'org-insert-last-stored-link
      (kbd "C-w") '("Refile" . org-refile))
    (evil-define-key nil my/org-note-map
      "c" '("capture" . org-capture)))
  (with-eval-after-load 'evil-collection
    (evil-collection-org-setup))
  (global-set-key (kbd "C-c l") #'org-store-link)
  (global-set-key (kbd "C-c a") #'org-agenda)
  (global-set-key (kbd "C-c c") #'org-capture)
  (cond ((file-directory-p (expand-file-name "braindump/org" org-directory))
               (customize-set-variable '+config/org-roam-directory
                                                               (expand-file-name "braindump/org" org-directory)))
              ((file-directory-p (expand-file-name "Projects/personal/braindump/org" (getenv "HOME")))
               (customize-set-variable '+config/org-roam-directory
                                                               (expand-file-name "Projects/personal/braindump/org" (getenv "HOME")))))
  (cond ((file-directory-p (expand-file-name "alexforsale.github.io" org-directory))
               (customize-set-variable '+config/blog-directory
                                                               (expand-file-name "alexforsale.github.io" org-directory)))
              ((file-directory-p (expand-file-name "Projects/personal/alexforsale.github.io" (getenv "HOME")))
               (customize-set-variable '+config/blog-directory
                                                               (expand-file-name "Projects/personal/alexforsale.github.io" (getenv "HOME")))))
  (modify-syntax-entry ?= "$" org-mode-syntax-table)
  (modify-syntax-entry ?~ "$" org-mode-syntax-table)
  (modify-syntax-entry ?_ "$" org-mode-syntax-table)
  (modify-syntax-entry ?+ "$" org-mode-syntax-table)
  (modify-syntax-entry ?/ "$" org-mode-syntax-table)
  (modify-syntax-entry ?* "$" org-mode-syntax-table)
  (add-to-list 'org-modules 'org-tempo t)
  (add-to-list 'org-structure-template-alist '("sh" . "src sh"))
  (add-to-list 'org-structure-template-alist '("co" . "src conf"))
  (add-to-list 'org-structure-template-alist '("lisp" . "src lisp"))
  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
  (add-to-list 'org-structure-template-alist '("sc" . "src scheme"))
  (add-to-list 'org-structure-template-alist '("ts" . "src typescript"))
  (add-to-list 'org-structure-template-alist '("py" . "src python"))
  (add-to-list 'org-structure-template-alist '("go" . "src go"))
  (add-to-list 'org-structure-template-alist '("yaml" . "src yaml"))
  (add-to-list 'org-structure-template-alist '("js" . "src js"))
  (add-to-list 'org-structure-template-alist '("json" . "src json"))
  (add-to-list 'org-structure-template-alist '("n" . "note"))
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (awk . t)
     (C . t)
     (css . t)
     (calc . t)
     (ditaa . t) ; needs the `ditaa' package
     (dot . t ) ; `graphviz'
     (screen . t)
     (haskell . t)
     (java . t)
     (js . t)
     (latex . t)
     (lisp . t)
     (lua . t)
     (org . t)
     (perl . t)
     (plantuml . t)
     (python .t)
     (ruby . t)
     (shell . t)
     (sed . t)
     (scheme . t)
     (sql . t)
     (sqlite . t)))
  (setq-default org-use-sub-superscripts '{})
  (add-to-list 'org-babel-tangle-lang-exts '("js" . "js"))
  (defun +config/org-prettify-symbols ()
    (push '("[ ]" . "☐") prettify-symbols-alist)
    (push '("[X]" . "☑") prettify-symbols-alist)
    (prettify-symbols-mode))
  (require 'org-tempo)
  :custom
  (org-highlight-latex-and-related '(native script entities))
  (org-replace-disputed-keys t)
  (org-indirect-buffer-display 'current-window)
  (org-enforce-todo-dependencies t)
  (org-fontify-whole-heading-line t)
  (org-return-follows-link t)
  (org-mouse-1-follows-link t)
  (org-image-actual-width nil)
  (org-adapt-indentation nil)
  (org-startup-indented t)
  (org-link-descriptive nil)
  (org-log-done 'time)
  (org-log-refile 'time)
  (org-log-redeadline 'time)
  (org-log-reschedule 'time)
  (org-log-into-drawer t)
  (org-clone-delete-id t)
  (org-default-notes-file (expand-file-name "notes.org" org-directory))
  (org-insert-heading-respect-content nil)
  (org-pretty-entities t)
  (org-use-property-inheritance t)
  (org-priority-highest ?A)
  (org-priority-lowest ?D)
  (org-priority-default ?B)
  (org-todo-keywords
   '((sequence
      "TODO(t!)"  ; A task that needs doing & is ready to do
      "NEXT(n!)"  ; Tasks that can be delayed
      "PROG(p!)"  ; A task that is in progress
      "WAIT(w!)"  ; Something external is holding up this task
      "HOLD(h!)"  ; This task is paused/on hold because of me
      "|"
      "DONE(d!)"  ; Task successfully completed
      "DELEGATED(l!)" ; Task is delegated
      "NOTES(o!)" ; set as notes
      "KILL(k!)") ; Task was cancelled, aborted or is no longer applicable
     ))
  (org-todo-keyword-faces
   '(("PROG" . (:foreground "#268bd2" :weight bold))
     ("WAIT" . (:foreground "#b58900" :weight bold))
     ("HOLD" . (:foreground "#859900" :weight bold))
     ("NEXT" . (:foreground "#2aa198" :weight bold))
     ("NOTES" . "#6c7c80")
     ("DELEGATED" . "#d33682")
     ("KILL" . "#a3be8c"))))

org-contrib   external

(use-package org-contrib
  :ensure t)

org-entities   builtin

(use-package org-entities
  :ensure nil
  :config
  (setopt org-entities-user
        '(("flat"  "\\flat" nil "" "" "266D" "♭")
          ("sharp" "\\sharp" nil "" "" "266F" "♯"))))

org-faces   builtin

(use-package org-faces
  :ensure nil
  :custom
  (org-fontify-quote-and-verse-blocks t))

org-archive   builtin

(use-package org-archive
  :ensure nil
  :after org
  :custom
  (org-archive-tag "archive")
  (org-archive-subtree-save-file-p t)
  (org-archive-mark-done t)
  (org-archive-reversed-order t)
  (org-archive-location (concat (expand-file-name "archives.org" org-directory) "::datetree/* Archived Tasks")))

org-capture   builtin

(use-package org-capture
  :after org
  :ensure nil
  :demand t
  :config
  (org-capture-put :kill-buffer t)
  (setq org-capture-templates ;; this is the default from `doom'.
        `(("i" "Inbox - Goes Here first!" entry
           (file+headline ,(expand-file-name "inbox.org" org-directory) "Inbox")
           "** %?\n%i\n%a" :prepend t)
          ;; ("r" "Request" entry (file+headline ,(expand-file-name "inbox.org" org-directory) "Request")
          ;;  (file ,(expand-file-name "request.template" org-directory)))
          ("l" "Links" entry
           (file+headline ,(expand-file-name "links.org" org-directory) "Links")))))

org-refile   builtin

(use-package org-refile
  :ensure nil
  :after org
  :hook (org-after-refile-insert . save-buffer)
  :custom
  (org-refile-targets
   `((,(expand-file-name "projects.org" org-directory) :maxlevel . 1)
     (,(expand-file-name "notes.org" org-directory) :maxlevel . 1)
     (,(expand-file-name "routines.org" org-directory) :maxlevel . 3)
     (,(expand-file-name "personal.org" org-directory) :maxlevel . 1)))
  (org-refile-use-outline-path 't)
  (org-outline-path-complete-in-steps nil))

org-fold   builtin

(use-package org-fold
  :ensure nil
  :after org org-contrib
  :custom
  (org-catch-invisible-edits 'smart))

org-id   builtin

(use-package org-id
  :ensure nil
  :after org
  :custom
  (org-id-locations-file-relative t)
  (org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))

org-num   builtin

(use-package org-num
  :ensure nil
  :after org
  :custom
  (org-num-face '(:inherit org-special-keyword :underline nil :weight bold))
  (org-num-skip-tags '("noexport" "nonum")))

org-crypt   builtin

(use-package org-crypt ; built-in
  :ensure nil
  :after org
  :commands org-encrypt-entries org-encrypt-entry org-decrypt-entries org-decrypt-entry
  ;;:hook (org-reveal-start . org-decrypt-entry)
  :preface
  ;; org-crypt falls back to CRYPTKEY property then `epa-file-encrypt-to', which
  ;; is a better default than the empty string `org-crypt-key' defaults to.
  (defvar org-crypt-key nil)
  (with-eval-after-load 'org
    (add-to-list 'org-tags-exclude-from-inheritance "crypt"))
  :config
  (setopt epa-file-encrypt-to "alexforsale@yahoo.com"))

org-attach   builtin

(use-package org-attach
  :ensure nil
  :after org
  :commands (org-attach-new
             org-attach-open
             org-attach-open-in-emacs
             org-attach-reveal-in-emacs
             org-attach-url
             org-attach-set-directory
             org-attach-sync)
  :config
  (unless org-attach-id-dir
    (setq-default org-attach-id-dir (expand-file-name ".attach/" org-directory)))
  (with-eval-after-load 'projectile
    (add-to-list 'projectile-globally-ignored-directories org-attach-id-dir))
  :custom
  (org-attach-auto-tag nil))

org-agenda   builtin

(use-package org-agenda
  :ensure nil
  :after org
  :custom
  (org-agenda-breadcrumbs-separator " → ")
  (org-agenda-files (list (concat org-directory "/")))
  (org-agenda-file-regexp "\\`[^.].*\\.org\\|[0-9]+$\\'")
  (org-agenda-include-inactive-timestamps t)
  (org-agenda-window-setup 'only-window)
  (org-stuck-projects '("+{project*}-killed-Archives/-DONE-KILL-DELEGATED"
                        ("TODO" "NEXT" "IDEA" "PROG")
                        nil ""))
  :config
  (with-eval-after-load 'evil
    (evil-set-initial-state #'org-agenda-mode 'normal)
    (evil-define-key 'normal org-agenda-mode-map "q" 'org-agenda-quit))
  (setopt org-agenda-custom-commands
        `(("w" "Work Agenda and all TODOs"
           ((agenda ""
                    ((org-agenda-span 1)
                     (org-agenda-start-on-weekday t)
                     (org-agenda-block-separator nil)
                     (org-agenda-use-time-grid t)
                     (org-agenda-day-face-function (lambda (date) 'org-agenda-date))
                     (org-agenda-format-date "%A %-e %B %Y")
                     (org-agenda-overriding-header "\nToday\n")))
            (tags-todo "TODO=\"TODO\"|\"NEXT\""
                       ((org-agenda-block-separator nil)
                        (org-agenda-skip-function '(org-agenda-skip-if-todo 'nottodo 'done))
                        (org-agenda-use-time-grid nil)
                        (org-agenda-overriding-header "\nIncomplete\n")))
            (agenda ""
                    ((org-agenda-span 7)
                     (org-agenda-start-on-weekday 1)
                     (org-agenda-block-separator nil)
                     (org-agenda-use-time-grid nil)
                     (org-agenda-overriding-header "\nWeekly\n"))))
           ((org-agenda-tag-filter-preset '("-personal" "-home"))))
          ("h" "Home Agenda and all personal TODOs"
           ((agenda ""
                    ((org-agenda-span 1)
                     (org-agenda-start-on-weekday t)
                     (org-agenda-block-separator nil)
                     (org-agenda-use-time-grid t)
                     (org-agenda-day-face-function (lambda (date) 'org-agenda-date))
                     (org-agenda-format-date "%A %-e %B %Y")
                     (org-agenda-overriding-header "\nToday\n")))
            (tags-todo "TODO=\"TODO\"|\"NEXT\""
                       ((org-agenda-block-separator nil)
                        (org-agenda-skip-function '(org-agenda-skip-if-todo 'nottodo 'done))
                        (org-agenda-use-time-grid nil)
                        (org-agenda-overriding-header "\nIncomplete\n")))
            (agenda ""
                    ((org-agenda-span 7)
                     (org-agenda-start-on-weekday 1)
                     (org-agenda-block-separator nil)
                     (org-agenda-use-time-grid nil)
                     (org-agenda-overriding-header "\nWeekly\n"))))
           ;; ((org-agenda-tag-filter-preset '("+personal")))
           ))))

org-clock   builtin

(use-package org-clock
  :ensure nil
  :after org
  :commands org-clock-save
  :hook (kill-emacs . org-clock-save)
  :custom
  (org-persist 'history)
  (org-clock-in-resume t)
  (org-clock-out-remove-zero-time-clocks t)
  (org-clock-history-length 20)
  (org-show-notification-handler "notify-send")
  (org-agenda-skip-scheduled-if-deadline-is-shown t)
  :config
  (org-clock-persistence-insinuate)
  (with-eval-after-load 'evil
    (setopt my/org-clock-mode-map (make-sparse-keymap))
    (evil-define-key* nil my/org-mode-map (kbd "C") (cons "Org Clock" my/org-clock-mode-map))
    (evil-define-key* nil my/org-clock-mode-map
      "d" '("display clock" . org-clock-display)
      (kbd "C-d") '("display clock" . org-clock-display)
      "e" '("modify clock estimate" . org-clock-modify-effort-estimate)
      "i" '("clock in" . org-clock-in)
      "l" '("clock in last clock" . org-clock-in-last)
      (kbd "C-x") '("clock in last clock" . org-clock-in-last)
      "o" '("clock out" . org-clock-out)
      "g" '("goto clock" . org-clock-goto)
      (kbd "C-j") '("goto clock" . org-clock-goto)
      "q" '("cancel clock" . org-clock-cancel)
      (kbd "q") '("cancel clock" . org-clock-cancel))))

org-timer   builtin

(use-package org-timer
  :ensure nil
  :config
  (setopt org-timer-format "Timer :: %s"))

org-eldoc   builtin

(use-package org-eldoc
  :ensure nil
  :after org org-contrib
  :config
  (puthash "org" #'ignore org-eldoc-local-functions-cache)
  ;;(puthash "plantuml" #'ignore org-eldoc-local-functions-cache)
  (puthash "python" #'python-eldoc-function org-eldoc-local-functions-cache)
  :custom
  (org-eldoc-breadcrumb-separator " → "))

org-superstar   external

(use-package org-superstar
  :ensure t
  :hook (org-mode . org-superstar-mode)
  :custom
  (org-superstar-leading-bullet ?\s)
  (org-superstar-leading-fallback ?\s)
  (org-hide-leading-stars nil)
  (org-indent-mode-turns-on-hiding-stars nil)
  (org-superstar-todo-bullet-alist
   '(("TODO" . 9744)
     ("[ ]"  . 9744)
     ("DONE" . 9745)
     ("[X]"  . 9745)))
  :config
  (org-superstar-configure-like-org-bullets))

org-fancy-priorities   external

(use-package org-fancy-priorities ; priority icons
  :ensure t
  :defer t
  :hook (org-mode . org-fancy-priorities-mode)
  :hook (org-agenda-mode . org-fancy-priorities-mode)
  :custom
  (org-fancy-priorities-list '("⚡" "⬆" "⬇" "☕")))

org-modern   external

(use-package org-modern
  :ensure t
  :demand t
  :config
  (set-face-attribute 'org-modern-symbol nil :family "Iosevka Nerd Font")
  (setq
   ;; Edit settings
   org-auto-align-tags nil
   org-tags-column 0
   org-fold-catch-invisible-edits 'show-and-error
   org-special-ctrl-a/e t
   org-insert-heading-respect-content t
   ;; Org styling, hide markup etc.
   org-hide-emphasis-markers nil ; set to nil for easier editing
   org-ellipsis "…"
   ;; Agenda styling
   org-agenda-tags-column 0
   org-agenda-block-separator ?─
   org-agenda-time-grid
   '((daily today require-timed)
     (800 1000 1200 1400 1600 1800 2000)
     " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")
   org-agenda-current-time-string
   "◀── now ─────────────────────────────────────────────────")
  (global-org-modern-mode))

org-download   external

(use-package org-download
  :ensure t
  :after org
  :commands
  org-download-dnd
  org-download-yank
  org-download-screenshot
  org-download-clipboard
  org-download-dnd-base64
  :config
  (unless org-download-image-dir
    (setopt org-download-image-dir org-attach-id-dir))
  (setopt org-download-method 'attach
        org-download-timestamp "_%Y%m%d_%H%M%S"
        org-download-screenshot-method
        (cond ((featurep :system 'macos) "screencapture -i %s")
              ((featurep :system 'linux)
               (cond ((executable-find "maim")  "maim -s %s")
                     ((executable-find "scrot") "scrot -s %s")
                     ((executable-find "gnome-screenshot") "gnome-screenshot -a -f %s"))))
        org-download-heading-lvl nil
        org-download-link-format "[[download:%s]]\n"
        org-download-annotate-function (lambda (_link) "")
        org-download-link-format-function
        (lambda (filename)
          (if (eq org-download-method 'attach)
              (format "[[attachment:%s]]\n"
                      (org-link-escape
                       (file-relative-name filename (org-attach-dir))))
            ;; Handle non-image files a little differently. Images should be
            ;; inserted as normal with previews. Other files, like pdfs or zips,
            ;; should be linked to, with an icon indicating the type of file.
            (format (concat (unless (image-type-from-file-name filename)
                              (concat (+org-attach-icon-for filename)
                                      " "))
                            org-download-link-format)
                    (org-link-escape
                     (funcall org-download-abbreviate-filename-function filename)))))
        org-download-abbreviate-filename-function
        (lambda (path)
          (if (file-in-directory-p path org-download-image-dir)
              (file-relative-name path org-download-image-dir)
            path))))

org-roam   external

I've tried different Emacs packages for note taking. Although this is not a lightweight solution, but it's still the best one for me. And I publish it to my personal blog.

(use-package org-roam
  :ensure t
  :if (not (equal 'windows-nt system-type))
  :demand t
  :after org
  :custom
  (org-roam-directory +config/org-roam-directory)
  (org-roam-complete-everywhere t)
  (org-roam-capture-templates
   '(("d" "default" plain
      "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: other\n#+property: header-args :exports both\n#+hugo_tags: \n%?"
      :if-new (file+head "%<%Y-%m-%d_%H-%M-%S>-${slug}.org" "#+title: ${title}\n")
      :unnarrowed t)
     ("p" "programming" plain
      "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: programming\n#+property: header-args :exports both\n#+hugo_tags: \n%?"
      :if-new (file+head "%<%Y-%m-%d_%H-%M-%S>-${slug}.org" "#+title: ${title}\n")
      :unnarrowed t)
     ("t" "tech" plain
      "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: tech\n#+property: header-args :exports both\n#+hugo_tags: \n%?"
      :if-new (file+head "%<%Y-%m-%d_%H-%M-%S>-${slug}.org" "#+title: ${title}\n")
      :unnarrowed t)))
  (org-roam-capture-ref-templates
   '(("r" "ref" plain "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: reference\n#+property: header-args :exports both\n#+hugo_tags: \n%?\n* Links\n- %l" :target (file+head "${slug}.org" "#+title: ${title}")
      :unnarrowed t)))
  :config
  (org-roam-setup)
  (org-roam-db-autosync-mode)
  (require 'org-roam-protocol)
  (with-eval-after-load 'evil-collection
    (evil-collection-org-roam-setup))
  (with-eval-after-load 'evil
    (evil-define-key* nil my/open-roam-map
      "a" '("add alias" . org-roam-alias-add)
      "c" '("roam capture" . org-roam-capture)
      "f" '("find roam node" . org-roam-node-find)
      "i" '("insert roam node at point" . org-roam-node-insert)
      "l" '("toggle roam buffer" . org-roam-buffer-toggle)
      "r" '("remove alias" . org-roam-alias-remove))))

To be fair, this package is very powerful, but for me, at least, not all its feature is needed. I'll try to start simple, and make additional configuration as needed.

  • org-roam-protocol

    This is from the orgroam-manual.

    [Desktop Entry]
    Name=Org-Protocol
    Exec=emacsclient %u
    Icon=emacs-icon
    Type=Application
    Terminal=false
    MimeType=x-scheme-handler/org-protocol
    

org-roam-ui   external

(use-package org-roam-ui
  :ensure t
  :after org-roam ;; or :after org
  ;;         normally we'd recommend hooking orui after org-roam, but since org-roam does not have
  ;;         a hookable mode anymore, you're advised to pick something yourself
  ;;         if you don't care about startup time, use
  ;;  :hook (after-init . org-roam-ui-mode)
  :config
  (setopt org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t))

org-protocol-capture-html   external

Requires pandoc

(use-package org-protocol-capture-html
  :load-path "site-lisp/org-protocol-capture-html"
  :after org
  :config
  (add-to-list 'org-capture-templates
               `("w" "Web site" entry
                 (file+headline ,(expand-file-name "links.org" org-directory) "Links")
                 "** %a :website:\n\n:PROPERTIES:\n:CREATED: %U\n:END:\n %?\n\n%:initial")))

pdf-tools   external

(use-package pdf-tools
  :ensure t
  :config
  (pdf-tools-install)
  (setq-default pdf-view-display-size 'fit-width)
  (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)
  :custom
  (pdf-annot-activate-created-annotations t "automatically annotate highlights"))

Tools

server   builtin

(use-package server
  :ensure nil
  :config
  (unless (server-running-p)
    (server-start))
  (require 'org-protocol))

magit   external

emacs-magit.png

Figure 1: Emacs magit

(use-package magit
  :ensure t
  :demand t
  :config
  (evil-collection-magit-repos-setup)
  (evil-collection-magit-section-setup)
  (evil-collection-magit-setup)
  (evil-set-initial-state #'git-commit-mode 'insert)
  (evil-define-key* nil my/g-map
    "g" '("git status" . magit-status))
  :custom
  (magit-revision-show-gravatars '("^Author:     " . "^Commit:     "))
  (magit-diff-refine-hunk 'all)
  (magit-log-arguments '("-n100" "--graph" "--decorate")))

pass   external

(use-package password-store
  :ensure t
  :demand t
  :config
  (setopt password-store-password-length 12)
  (setopt auth-sources '(password-store "~/.authinfo.gpg" "~/.netrc")))

(use-package password-store-otp
  :ensure t
  :defer t
  :after password-store)

(use-package pass
  :ensure t
  :defer t
  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-pass-setup)))

(use-package auth-source-pass
  :ensure nil
  :init
  (auth-source-pass-enable))

Elpaca complains about different of pass version installed. But it installed just fine.

pinentry   external

(use-package pinentry
  :ensure t
  :defer t
  :config
  (pinentry-start))

fzf   external

(use-package fzf
  :if (executable-find "fzf")
  :ensure t
  :config
  (setopt my/fzf-map (make-sparse-keymap))
  (evil-define-key* nil my/file-map (kbd "z") (cons "fzf" my/fzf-map))
  (evil-define-key* nil my/fzf-map
    "b" '("fzf find in buffer" . fzf-find-in-buffer)
    "f" '("fzf" . fzf-find-file)
    "d" '("fzf dir" . fzf-directory)
    "r" '("fzf recentf" . fzf-recentf))
  (evil-define-key* nil my/search-map "z" '("fzf-grep" . fzf-grep))
  (evil-define-key* nil my/buffer-map "z" '("fzf switch buffer" . fzf-switch-buffer))
  (evil-define-key* nil projectile-command-map "z" '("fzf projectile" . fzf-projectile))
  (setopt fzf/args "-x --color dark --print-query --margin=1,0 --no-hscroll"
        fzf/executable "fzf"
        fzf/git-grep-args "-i --line-number %s"
        ;; command used for `fzf-grep-*` functions
        ;; example usage for ripgrep:
        ;; fzf/grep-command "rg --no-heading -nH"
        fzf/grep-command "rg --no-heading -nH"
        ;; If nil, the fzf buffer will appear at the top of the window
        fzf/position-bottom t
        fzf/window-height 15))

rg   external

Packaged as ripgrep in archlinux. The :if keyword won't load this if the executable not found.

(use-package rg
  :ensure t
  :if (executable-find "rg")
  :defer t
  :config
  (evil-collection-rg-setup))

ox-hugo   external

Blogging tools

(use-package ox-hugo
  :ensure t
  :if (executable-find "hugo")
  :after ox)
  • org-capture template for blog
    (defun my/create-blog-capture-file ()
      "Create a subdirectory and `org-mode' file under `+config/blog-directory'."
      (interactive)
      (let* ((name (read-string "slug: "))
                   (content-dir (expand-file-name "content-org/" +config/blog-directory)))
          (unless (file-directory-p (expand-file-name name content-dir))
            (make-directory (expand-file-name name content-dir)))
          (expand-file-name (concat name ".org") (expand-file-name name content-dir))))
    
    (add-to-list 'org-capture-templates
                           '("h" "Hugo Post" plain
                             (file my/create-blog-capture-file)
                             "#+options: ':nil -:nil ^:nil num:nil toc:nil
    #+author: %n
    #+title: %^{Title}
    #+description:
    #+date: %t
    #+hugo_categories: %^{Categories|misc|desktop|emacs|learning}
    #+hugo_tags: %^{Tags}
    #+hugo_auto_set_lastmod: t
    #+hugo_section: posts
    #+hugo_base_dir: ../../
    #+language: en
    #+startup: inlineimages
    
    * %?" :jump-to-captured t))
    

Mail

notmuch   external

(use-package notmuch
  :if (executable-find "notmuch")
  :ensure t
  :defer t
  :commands (notmuch)
  :hook
  (message-setup . mml-secure-sign-pgpmime)
  :config
  (global-set-key (kbd "<XF86Mail>") 'notmuch)
  (setq notmuch-fcc-dirs nil
      notmuch-search-result-format
      '(("date" . "%12s ")
        ("count" . "%-7s ")
        ("authors" . "%-30s ")
        ("subject" . "%-72s ")
        ("tags" . "(%s)"))
      notmuch-tag-formats
      '(("unread"
         (propertize tag 'face 'notmuch-tag-unread))
        ("flagged"
         (propertize tag 'face 'notmuch-tag-flagged)
         (notmuch-tag-format-image-data tag
                                        (notmuch-tag-star-icon))))
      notmuch-tagging-keys
      '(("a" notmuch-archive-tags "Archive")
        ("u" notmuch-show-mark-read-tags "Mark read")
        ("f" ("+flagged") "Flag")
        ("s" ("+spam" "-inbox") "Mark as spam")
        ("d" ("+deleted" "-inbox") "Delete"))
      notmuch-saved-searches
      '((:name "flagged" :query "tag:flagged" :key "f")
        (:name "archive" :query "tag:archive" :key "i")
        (:name "inbox" :query "tag:inbox" :key "i")
        (:name "sent" :query "tag:sent" :key "s")
        (:name "drafts"  :query "tag:draft" :key "d")
        (:name "all mail" :query "*" :key "a")
        (:name "unread" :query "tag:unread" :key "u")
        (:name "Today"
               :query "date:today AND NOT tag:spam AND NOT tag:bulk"
               :key "T"
               :search-type 'tree
               :sort-order 'newest-first)
        (:name "This Week"
               :query "date:weeks AND NOT tag:spam AND NOT tag:bulk"
               :key "W"
               :search-type 'tree
               :sort-order 'newest-first)
        (:name "This Month"
               :query "date:months AND NOT tag:spam AND NOT tag:bulk"
               :key "M"
               :search-type 'tree
               :sort-order 'newest-first)
        (:name "flagged"
               :query "tag:flagged AND NOT tag:spam AND NOT tag:bulk"
               :key "f"
               :search-type 'tree
               :sort-order 'newest-first)
        (:name "spam" :query "tag:spam"))
      notmuch-archive-tags '("-inbox" "-unread" "+archive"))
  (setq-default notmuch-search-oldest-first nil)
  (if (executable-find "gpg2")
      (setopt notmuch-crypto-gpg-program "gpg2")
    (setopt notmuch-crypto-gpg-program "gpg"))
  (setopt notmuch-crypto-process-mime t
        mml-secure-openpgp-sign-with-sender t)
  (define-key notmuch-show-mode-map "S"
            (lambda ()
              "Mark message as spam"
              (interactive)
              (notmuch-show-tag (list +spam -new)))))

(use-package notmuch-indicator
  :if (executable-find "notmuch")
  :ensure t
  :config
  (setopt notmuch-indicator-args
          '((:terms "tag:unread and tag:inbox" :label "U" :label-face success)))
  (notmuch-indicator-mode))

(use-package consult-notmuch
  :after consult
  :ensure t
  :if (executable-find "notmuch")
  :config
  (add-to-list 'consult-buffer-sources 'consult-notmuch-buffer-source))

(use-package ol-notmuch
  :ensure t
  :if (executable-find "notmuch"))

(use-package notmuch-maildir
  :ensure t
  :if (executable-find "notmuch")
  :config
  (notmuch-maildir-inject-section))

(use-package message
  :ensure nil
  :if (executable-find "notmuch")
  :custom
  (message-directory (expand-file-name ".mail" (getenv "HOME")))
  (message-sendmail-envelope-from 'header))

(use-package sendmail
  :ensure nil
  :if (executable-find "notmuch")
  :custom
  (mail-specify-envelope-from t)
  (mail-envelope-from 'header)
  (send-mail-function 'sendmail-send-it)
  (sendmail-program (executable-find "msmtp")))

Programming languages

display-line-numbers   builtin

(use-package display-line-numbers
  :ensure nil
  :hook (prog-mode . display-line-numbers-mode)
  :config
  (setopt display-line-numbers-type 'relative))

lisp   builtin

(use-package eldoc
  :ensure nil
  :hook ((emacs-lisp-mode
        lisp-interaction-mode
        ielm-mode) . eldoc-mode))

make-mode   builtin

(use-package make-mode
  :ensure nil
  :config
  (add-hook 'makefile-mode-hook 'indent-tabs-mode))

executable   builtin

(use-package executable
  :ensure nil
  :hook
  (after-save . executable-make-buffer-file-executable-if-script-p))

nix-ts-mode   external

(use-package nix-ts-mode
  :ensure t
  :mode "\\.nix\\'"
  :config
  (add-hook 'nix-ts-mode-hook (lambda () (setq-local tab-width 2))))

yuck-mode   external

(use-package yuck-mode
  :ensure t)

astro-ts-mode   external

(use-package astro-ts-mode
  :ensure t
  :mode "\\.astro\\'"
  :hook (astro-ts-mode . lsp-deferred)
  :config
  (setq treesit-language-source-alist
      '((astro "https://github.com/virchau13/tree-sitter-astro"))))

Completion

Vertico   external

(use-package vertico
  :ensure t
  :hook
  (rfn-eshadow-update-overlay . vertico-directory-tidy)
  :custom
  ;; (vertico-scroll-margin 0) ;; Different scroll margin
  (vertico-count 15) ;; Show more candidates
  (vertico-resize nil) ;; Grow and shrink the Vertico minibuffer
  (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
  :config
  (advice-add #'tmm-add-prompt :after #'minibuffer-hide-completions)
    (keymap-set vertico-map "?" #'minibuffer-completion-help)
  :init
  (vertico-mode))

;; Emacs minibuffer configurations.
(use-package emacs
  :custom
  ;; Enable context menu. `vertico-multiform-mode' adds a menu in the minibuffer
  ;; to switch display modes.
  (context-menu-mode t)
  ;; Support opening new minibuffers from inside existing minibuffers.
  (enable-recursive-minibuffers t)
  ;; Hide commands in M-x which do not work in the current mode.  Vertico
  ;; commands are hidden in normal buffers. This setting is useful beyond
  ;; Vertico.
  (read-extended-command-predicate #'command-completion-default-include-p)
  ;; Do not allow the cursor in the minibuffer prompt
  (minibuffer-prompt-properties
   '(read-only t cursor-intangible t face minibuffer-prompt)))
  • Extensions
    • vertico-directory
      (use-package vertico-directory
        :after vertico
        :ensure nil
        ;; More convenient directory navigation commands
        :bind (:map vertico-map
                    ("RET" . vertico-directory-enter)
                    ("DEL" . vertico-directory-delete-char)
                    ("M-DEL" . vertico-directory-delete-word))
        ;; Tidy shadowed file names
        :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
      
    • vertico-quick
      (use-package vertico-quick
        :after vertico
        :ensure nil
        :bind (:map vertico-map
                    ("M-q" . vertico-quick-insert)
                    ("C-q" . vertico-quick-exit)))
      
    • vertico-multiform
      (use-package vertico-multiform
        :ensure nil
        :init
        (vertico-multiform-mode)
        :config
        (setopt vertico-multiform-commands
              `((describe-symbol (vertico-sort-function . vertico-sort-alpha))
                (consult-outline buffer ,(lambda (_) (text-scale-set -1)))
                (org-ctrl-c-ctrl-c flat)))
      
        (defun sort-directories-first (files)
          (setopt files (vertico-sort-history-length-alpha files))
          (nconc (seq-filter (lambda (x) (string-suffix-p "/" x)) files)
                 (seq-remove (lambda (x) (string-suffix-p "/" x)) files)))
      
        (setopt vertico-multiform-categories
              '((symbol (vertico-sort-function . vertico-sort-alpha))
                (file (vertico-sort-function . sort-directories-first)))))
      

orderless   external

(use-package orderless
  :ensure t
  :init
  ;; Configure a custom style dispatcher (see the Consult wiki)
  ;; (setopt orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch)
  ;;       orderless-component-separator #'orderless-escapable-split-on-space)
  (setopt completion-styles '(orderless basic substring partial-completion)
        completion-category-defaults nil
        completion-category-overrides '((file (styles orderless partial-completion)))
        orderless-component-separator #'orderless-escapable-split-on-space)
  (set-face-attribute 'completions-first-difference nil :inherit nil))

partial-completion and flex are built-ins completion-styles.

corfu   external

(use-package corfu
  :ensure t
  :custom
  (corfu-cycle t)
  (corfu-auto t)
  (corfu-auto-delay 0.18)
  (corfu-auto-prefix 2)
  (corfu-quit-no-match 'separator)
  (corfu-preselect 'prompt)
  (corfu-count 16)
  (corfu-max-width 120)
  (corfu-on-exact-match nil)
  (corfu-quit-no-match corfu-quit-at-boundary)
  (completion-cycle-threshold 3)
  (text-mode-ispell-word-completion nil)
  :hook (lsp-completion-mode . my/lsp-mode-setup-completion)
  :init
  (defun my/orderless-dispatch-flex-first (_pattern index _total)
    (and (eq index 0) 'orderless-flex))
  (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
          '(orderless))
    ;; Optionally configure the first word as flex filtered.
    (add-hook 'orderless-style-dispatchers #'my/orderless-dispatch-flex-first nil 'local)
    ;; Optionally configure the cape-capf-buster.
    (setq-local completion-at-point-functions (list (cape-capf-buster #'lsp-completion-at-point))))
  :config
  (add-to-list 'completion-category-overrides `(lsp-capf (styles ,@completion-styles)))
  ;;(add-to-list 'corfu-auto-commands #'lispy-colon)
  (add-hook 'evil-insert-state-exit-hook #'corfu-quit)
  (with-eval-after-load 'exwm
    (advice-add #'corfu--make-frame :around
                (defun +corfu--make-frame-a (oldfun &rest args)
                  (cl-letf (((symbol-function #'frame-parent)
                             (lambda (frame)
                               (or (frame-parameter frame 'parent-frame)
                                   exwm-workspace--current))))
                    (apply oldfun args))
                  (when exwm--connection
                    (set-frame-parameter corfu--frame 'parent-frame nil))))

    (advice-add #'corfu--popup-redirect-focus :override
                (defun +corfu--popup-redirect-focus-a ()
                  (redirect-frame-focus corfu--frame
                                        (or (frame-parent corfu--frame)
                                            exwm-workspace--current)))))
  (defun corfu-enable-always-in-minibuffer ()
    "Enable Corfu in the minibuffer if Vertico/Mct are not active."
    (unless (or (bound-and-true-p mct--active)
                (bound-and-true-p vertico--input)
                (eq (current-local-map) read-passwd-map))
      ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion
      (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
                  corfu-popupinfo-delay nil)
      (corfu-mode 1)))
  (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)
  :init
  (global-corfu-mode))

This is the basic configuration, with the addition of enabling corfu in the minibuffer.

  • corfu-history   external
    (use-package corfu-history
      :ensure nil
      :hook ((corfu-mode . corfu-history-mode))
      :config
      (with-eval-after-load 'savehist
        (add-to-list 'savehist-additional-variables 'corfu-history)))
    
  • corfu-popupinfo   external
    (use-package corfu-popupinfo
      :ensure nil
      :hook ((corfu-mode . corfu-popupinfo-mode))
      :config
      (setopt corfu-popupinfo-delay '(0.5 . 1.0))
      (setopt corfu-popupinfo-hide nil))
    

consult   external

  • consult main package
    (use-package consult
      :ensure t
      :demand t
      :hook (completion-list-mode . consult-preview-at-point-mode)
      :init
      ;; Optionally configure the register formatting. This improves the register
      ;; preview for `consult-register', `consult-register-load',
      ;; `consult-register-store' and the Emacs built-ins.
      (setopt register-preview-delay 0.5
            register-preview-function #'consult-register-format)
      ;; Optionally tweak the register preview window.
      ;; This adds thin lines, sorting and hides the mode line of the window.
      (advice-add #'register-preview :override #'consult-register-window)
      ;; Use Consult to select xref locations with preview
      (setopt xref-show-xrefs-function #'consult-xref
            xref-show-definitions-function #'consult-xref)
      ;; Configure other variables and modes in the :config section,
      ;; after lazily loading the package.
      :config
      ;; Optionally configure preview. The default value
      ;; is 'any, such that any key triggers the preview.
      ;; (setopt consult-preview-key 'any)
      ;; (setopt consult-preview-key "M-.")
      ;; (setopt consult-preview-key '("S-<down>" "S-<up>"))
      ;; For some commands and buffer sources it is useful to configure the
      ;; :preview-key on a per-command basis using the `consult-customize' macro.
      (consult-customize
       consult-theme :preview-key '(:debounce 0.2 any)
       consult-ripgrep consult-git-grep consult-grep
       consult-bookmark consult-recent-file consult-xref
       consult--source-bookmark consult--source-file-register
       consult--source-recent-file consult--source-project-recent-file
       ;; :preview-key "M-."
       :preview-key '(:debounce 0.4 any))
      ;; Optionally configure the narrowing key.
      ;; Both < and C-+ work reasonably well.
      (setopt consult-narrow-key "<") ;; "C-+"
      ;; Optionally make narrowing help available in the minibuffer.
      ;; You may want to use `embark-prefix-help-command' or which-key instead.
      ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)
      ;; By default `consult-project-function' uses `project-root' from project.el.
      ;; Optionally configure a different project root function.
    ;;;; 1. project.el (the default)
      ;;(setopt consult-project-function #'consult--default-project--function)
    ;;;; 2. vc.el (vc-root-dir)
      ;; (setopt consult-project-function (lambda (_) (vc-root-dir)))
    ;;;; 3. locate-dominating-file
      ;; (setopt consult-project-function (lambda (_) (locate-dominating-file "." ".git")))
    ;;;; 4. projectile.el (projectile-project-root)
      (with-eval-after-load 'projectile
        (autoload 'projectile-project-root "projectile")
        (setopt consult-project-function (lambda (_) (projectile-project-root))))
    ;;;; 5. No project support
      ;; (setopt consult-project-function nil))
      (setopt consult-line-numbers-widen t
            consult-async-min-input 2
            consult-async-refresh-delay  0.15
            consult-async-input-throttle 0.2
            consult-async-input-debounce 0.1)
      (with-eval-after-load 'evil
        (evil-define-key* '(normal visual) 'global
          (kbd "<leader>.") '("Store register" . consult-register-store)
          (kbd "<leader> C-.") '("Load register" . consult-register-load))
        (evil-define-key* nil my/file-map
          "r" '("recent files" . consult-recent-file))
        (evil-define-key* nil my/buffer-map
          "b" '("buffers" . consult-buffer)
          "B" '("project buffers" . consult-project-buffer))
        (evil-define-key* nil my/open-map
          "O" '("Open agenda files" . consult-org-agenda))
        (with-eval-after-load 'org
          (evil-define-key* nil my/org-mode-map
                  "O" '("search heading" . consult-org-heading)))
        (evil-define-key* nil my/search-map
          "a" '("grep search" . consult-grep)
          "b" '("search bookmarks" . consult-bookmark)
          "B" '("search global mark" . consult-global-mark)
          "d" '("search directory" . consult-dir)
          "f" '("consult find" . consult-find)
          "F" '("focus line" . consult-focus-line)
          "h" '("isearch history" . consult-isearch-history)
          "m" '("search mark" . consult-mark)
          "i" '("imenu" . consult-imenu)
          "o" '("search outline" . consult-outline)
          "s" '("search line" . consult-line)
          "S" '("search multi line" . consult-line-multi)
          "\"" '("search register" . consult-register))
        (evil-define-key* '(normal visual) 'global (kbd "M-y") 'consult-yank-pop)
        (with-eval-after-load 'consult-notmuch
          (evil-define-key* nil my/search-map
            "M" '("search mail" . consult-notmuch)))
        (cond ((executable-find "fd")
               (evil-define-key* nil my/search-map "A" '("fd search" . consult-fd)))
              (t (evil-define-key* nil my/search-map "A" '("find search" . consult-find))))
        (when (executable-find "rg")
          (evil-define-key* nil my/search-map "r" '("search ripgrep" . consult-ripgrep))))
    
      (keymap-set isearch-mode-map "M-e" #'consult-isearch-history)
      (keymap-set isearch-mode-map "M-l" #'consult-line)
      (keymap-set isearch-mode-map "M-L" #'consult-line-multi)
    
      (keymap-set minibuffer-local-map "M-s" #'consult-history)
      (keymap-set minibuffer-local-map "M-r" #'consult-history)
    
      (global-set-key [remap Info-search] 'consult-info)
      (global-set-key [remap yank-pop] 'consult-yank-pop)
      (global-set-key [remap bookmark-jump] 'consult-bookmark)
      (global-set-key [remap evil-show-marks] 'consult-mark)
      (global-set-key [remap evil-show-registers] 'consult-register)
      (global-set-key [remap goto-line] 'consult-goto-line)
      (global-set-key [remap imenu] 'consult-imenu)
      (global-set-key [remap locate] 'consult-locate)
      (global-set-key [remap load-theme] 'consult-theme)
      (global-set-key [remap man] 'consult-man)
      (global-set-key [remap recentf-open-files] 'consult-recent-file)
      (global-set-key [remap switch-to-buffer] 'consult-buffer)
      (global-set-key [remap switch-to-buffer-other-frame] 'consult-buffer-other-frame)
      (global-set-key [remap switch-to-buffer-other-window] 'consult-buffer-other-window))
    

    Apart from the consult-projectile-function, this is pretty much the default.

  • consult-dir
    (use-package consult-dir
      :ensure t
      :bind (:map vertico-map
                  ("C-x C-d" . consult-dir)
                  ("C-x C-j" . consult-dir-jump-file)))
    

embark   external

(use-package embark
  :ensure t
  :defer t
  :bind
  (("C-." . embark-act)         ;; pick some comfortable binding
   ("C-;" . embark-dwim)        ;; good alternative: M-.
   ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
  :init
  ;; Optionally replace the key help with a completing-read interface
  (setopt prefix-help-command #'embark-prefix-help-command)
  ;; Show the Embark target at point via Eldoc. You may adjust the
  ;; Eldoc strategy, if you want to see the documentation from
  ;; multiple providers. Beware that using this can be a little
  ;; jarring since the message shown in the minibuffer can be more
  ;; than one line, causing the modeline to move up and down:

  ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)
  ;; (setopt eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)

  :config
  (global-set-key (kbd "C-.") #'embark-act)
  (global-set-key (kbd "C-;") #'embark-dwim)
  (global-set-key (kbd "C-h b") #'embark-bindings)
  (evil-collection-embark-setup)
  (evil-define-key nil my/a-map
    (kbd ";") '("Embark DWIM" . embark-dwim)
    (kbd "a") '("Embark Act" . embark-act))
  (setopt which-key-use-C-h-commands nil
        prefix-help-command #'embark-prefix-help-command)
  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

This is a package that I should learn more, but the configuration is overwhealming.

embark-consult   external

(use-package embark-consult
  :ensure t
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

consult integration for embark.

cape   external

(use-package cape
  :ensure t
  ;; Bind dedicated completion commands
;; Alternative prefix keys: C-c p, M-p, M-+, ...
:bind (("C-c p p" . completion-at-point) ;; capf
         ("C-c p t" . complete-tag)        ;; etags
         ("C-c p d" . cape-dabbrev))       ;; or dabbrev-completion
  :hook
  (prog-mode . +corfu-add-cape-file-h)
  ((org-mode markdown-mode) . +corfu-add-cape-elisp-block-h)
  :init
  ;; Add to the global default value of `completion-at-point-functions' which is
  ;; used by `completion-at-point'.  The order of the functions matters, the
  ;; first function returning a result wins.  Note that the list of buffer-local
  ;; completion functions takes precedence over the global list.
  (add-hook 'completion-at-point-functions #'cape-dabbrev)
  (add-hook 'completion-at-point-functions #'cape-file)
  (add-hook 'completion-at-point-functions #'cape-elisp-block)
  :config
  (setopt cape-dabbrev-check-other-buffers t)
  (defun +corfu-add-cape-file-h ()
    (add-hook 'completion-at-point-functions #'cape-file -10 t))
  (defun +corfu-add-cape-elisp-block-h ()
    (add-hook 'completion-at-point-functions #'cape-elisp-block 0 t))
  (with-eval-after-load 'lsp-mode
    (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible)
    (advice-add #'lsp-completion-at-point :around #'cape-wrap-nonexclusive))
  (advice-add #'comint-completion-at-point :around #'cape-wrap-nonexclusive)
  (advice-add #'pcomplete-completions-at-point :around #'cape-wrap-nonexclusive)
   ;; From the `cape' readme. Without this, Eshell autocompletion is broken on
  ;; Emacs28.
  (when (< emacs-major-version 29)
    (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent)
    (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)))

kind-icon   external

(use-package kind-icon
  :if (display-graphic-p)
  :ensure t
  :after corfu
  :config
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

Dired

all-the-icons-dired   external

(use-package all-the-icons-dired
  :ensure t
  :hook (dired-mode . all-the-icons-dired-mode))

minimal Emacs

minimal-init.el

This file is intended to be used on a remote system, so configuration should only exists on this one file only. No external packages, the sole purpose is to edit files.

(add-to-list 'default-frame-alist '(menu-bar-lines . 0))
(add-to-list 'initial-frame-alist '(menu-bar-lines . 0))

(add-to-list 'initial-frame-alist '(tool-bar-lines . 0))
(add-to-list 'default-frame-alist '(tool-bar-lines . 0))

(add-to-list 'initial-frame-alist '(vertical-scroll-bars))
(add-to-list 'default-frame-alist '(vertical-scroll-bars))

(add-to-list 'initial-frame-alist '(fullscreen . maximized))
(add-to-list 'default-frame-alist '(fullscreen . maximized))

(setopt user-mail-address "alexforsale@yahoo.com"
              user-full-name "Kristian Alexander P")

(use-package repeat
  :ensure nil
  :init
  (repeat-mode +1))

(use-package files
  :ensure nil
  :config
  (nconc
   auto-mode-alist
   '(("/LICENSE\\'" . text-mode)
     ("\\.log\\'" . text-mode)
     ("rc\\'" . conf-mode)
     ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode)))
  :hook
  ((prog-mode text-mode) . auto-save-visited-mode)
  :custom
  (auto-save-visited-interval 10)
  (find-file-suppress-same-file-warnings t)
  ;;(confirm-kill-emacs #'yes-or-no-p) ; confirm when exiting
  (confirm-kill-processes nil) ; don't confirm killing processes
  (revert-without-query (list "."))
  (find-file-visit-truename t) ; `find-file' will visit the actual file
  (make-backup-files nil)
  (version-control t)
  (backup-by-copying t)
  (delete-old-versions t)
  (kept-new-versions 6)
  (kept-old-versions 2)
  (auto-save-include-big-deletions t)
  (auto-save-list-file-prefix (expand-file-name ".autosave/" user-emacs-directory))
  (backup-directory-alist `(("." . ,(expand-file-name ".backup" user-emacs-directory))))
  (auto-mode-case-fold nil)
  (require-final-newline t))

(use-package savehist
  :init
  (savehist-mode 1)
  :custom
  (savehist-file (expand-file-name "history" user-emacs-directory))
  (savehist-save-minibuffer-history t)
  (savehist-autosave-interval nil)
  (savehist-coding-system 'utf-8)
  (savehist-additional-variables
   '(evil-jumps-history
     command-history
     kill-ring
     register-alist
     mark-ring
     global-mark-ring
     search-ring
     regexp-search-ring)))

(use-package comint
  :ensure nil
  :config
  (setopt comint-prompt-read-only t
        comint-buffer-maximum-size 2048)
  (evil-define-key* '(normal visual) comint-mode-map "q" '(lambda () (interactive) (kill-buffer nil))))

(use-package font-core
  :ensure nil
  :init
  (global-font-lock-mode t))

(use-package winner
  :ensure nil
  :init
  (winner-mode +1)
  :config
  (setopt winner-boring-buffers '("*Completions*" "*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*"
                                "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*"
                                "*esh command on file*")))

(use-package window
  :ensure nil
  :config
  (setopt split-width-threshold 160
      split-height-threshold nil))

(use-package saveplace
  :init
  (save-place-mode 1)
  :custom
  (save-place-file (expand-file-name "places" user-emacs-directory)))

(use-package recentf
  :bind ("C-c f" . recentf)
  :commands recentf-open-files
  :init
  (recentf-mode 1)
  :custom
  (recentf-auto-cleanup t)
  (recentf-max-saved-items 250)
  (recentf-max-menu-items 300)
  (recentf-exclude
   `("/elpa/" ;; ignore all files in elpa directory
     "recentf" ;; remove the recentf load file
     ".*?autoloads.el$"
     "treemacs-persist"
     "company-statistics-cache.el" ;; ignore company cache file
     "/intero/" ;; ignore script files generated by intero
     "/journal/" ;; ignore daily journal files
     ".gitignore" ;; ignore `.gitignore' files in projects
     "/tmp/" ;; ignore temporary files
     "NEWS" ;; don't include the NEWS file for recentf
     "bookmarks"  "bmk-bmenu" ;; ignore bookmarks file in .emacs.d
     "loaddefs.el"
     "^/\\(?:ssh\\|su\\|sudo\\)?:" ;; ignore tramp/ssh files
     (concat "^" (regexp-quote (or (getenv "XDG_RUNTIME_DIR")
                                   "/run"))))))

(use-package autorevert
  :hook (focus-in . doom-auto-revert-buffers-h)
  :hook (after-save . doom-auto-revert-buffers-h)
  :hook (prog-mode . doom-auto-revert-buffer-h)
  :custom
  (auto-revert-interval 60)
  (auto-revert-use-notify nil)
  (global-auto-revert-non-file-buffers t)
  (auto-revert-verbose t)
  (auto-revert-stop-on-user-input nil)
  (revert-without-query (list "."))
  :config
  (defun doom-visible-buffer-p (buf)
    "Return non-nil if BUF is visible."
    "Return non-nil if BUF is not visible."
    (not (doom-visible-buffer-p buf)))
  (defun doom-visible-buffers (&optional buffer-list all-frames)
"Return a list of visible buffers (i.e. not buried)."
(let ((buffers
       (delete-dups
        (cl-loop for frame in (if all-frames (visible-frame-list) (list (selected-frame)))
                 if (window-list frame)
                 nconc (mapcar #'window-buffer it)))))
  (if buffer-list
      (cl-delete-if (lambda (b) (memq b buffer-list))
                    buffers)
    (delete-dups buffers))))
  (defun doom-auto-revert-buffer-h ()
    "Auto revert current buffer, if necessary."
    (unless (or auto-revert-mode (active-minibuffer-window))
      (let ((auto-revert-mode t))
        (auto-revert-handler))))
  (defun doom-auto-revert-buffers-h ()
    "Auto revert stale buffers in visible windows, if necessary."
    (dolist (buf (doom-visible-buffers))
      (with-current-buffer buf
        (doom-auto-revert-buffer-h)))))

(use-package mouse
  :ensure nil
  :config
  (setopt mouse-yank-at-point t))

(use-package subword
  :ensure nil
  :init
  (global-subword-mode 1))

(use-package text-mode
  :ensure nil
  :hook (((text-mode prog-mode) . visual-line-mode)
         (prog-mode . (lambda () (setq-local sentence-end-double-space t))))
  :config
  (setq-default sentence-end-double-space nil)
  (setopt sentence-end-without-period nil)
  (setopt colon-double-space nil)
  (setopt adaptive-fill-mode t))

(use-package select
  :ensure nil
  :custom
  (select-enable-clipboard t))

(use-package executable
  :ensure nil
  :hook
  (after-save . executable-make-buffer-file-executable-if-script-p))

Date: 2025-09-06 Sat 00:00

Author: Kristian Alexander P

Created: 2026-01-02 Fri 17:01