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
(setq 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 ()
          (setq 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))

(add-to-list 'initial-frame-alist
                       '(font . "Iosevka Nerd Font Mono-8"))
(add-to-list 'default-frame-alist
                       '(font . "Iosevka Nerd Font Mono-8"))

(use-package emacs
  :ensure nil
  :init
  (load-theme 'modus-vivendi-tinted t))

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
  (setq 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)
  (package-initialize))

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

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

merge-list-to-list function

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 ()
                               (setq 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
    (setq save-silently t))
  (setq 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
  (setq evil-motion-state-cursor 'box)  ;   (setq evil-visual-state-cursor 'box)  ;   (setq evil-normal-state-cursor 'box)  ;   (setq evil-insert-state-cursor 'bar)  ;   (setq evil-emacs-state-cursor  'hbar) ; _
  )

evil-easymotion   external

(use-package evil-easymotion
  :after evil
  :ensure t
  :config
  (evilem-make-motion evilem-motion-search-next #'evil-search-next
                      :bind ((evil-ex-search-highlight-all nil)))
  (evilem-make-motion evilem-motion-search-previous #'evil-search-previous
                      :bind ((evil-ex-search-highlight-all nil)))
  (evilem-make-motion evilem-motion-search-word-forward #'evil-search-word-forward
                      :bind ((evil-ex-search-highlight-all nil)))
  (evilem-make-motion evilem-motion-search-word-backward #'evil-search-word-backward
                      :bind ((evil-ex-search-highlight-all nil)))
  ;; Rebind scope of w/W/e/E/ge/gE evil-easymotion motions to the visible
  ;; buffer, rather than just the current line.
  (put 'visible 'bounds-of-thing-at-point (lambda () (cons (window-start) (window-end))))
  (evilem-make-motion evilem-motion-forward-word-begin #'evil-forward-word-begin :scope 'visible)
  (evilem-make-motion evilem-motion-forward-WORD-begin #'evil-forward-WORD-begin :scope 'visible)
  (evilem-make-motion evilem-motion-forward-word-end #'evil-forward-word-end :scope 'visible)
  (evilem-make-motion evilem-motion-forward-WORD-end #'evil-forward-WORD-end :scope 'visible)
  (evilem-make-motion evilem-motion-backward-word-begin #'evil-backward-word-begin :scope 'visible)
  (evilem-make-motion evilem-motion-backward-WORD-begin #'evil-backward-WORD-begin :scope 'visible)
  (evilem-make-motion evilem-motion-backward-word-end #'evil-backward-word-end :scope 'visible)
  (evilem-make-motion evilem-motion-backward-WORD-end #'evil-backward-WORD-end :scope 'visible)
  (evilem-default-keybindings "z g"))

This is a useful package for complementing Evil's motion (using zg).

evilem keybinding evilem key Evil evil key
evilem-motion-forward-word-begin z g w evil-forward-word-begin w
evilem-motion-forward-WORD-begin z g W evil-forward-WORD-begin W
evilem-motion-forward-word-end z g e evil-forward-word-end e
evilem-motion-forward-WORD-end z g E evil-forward-WORD-end E
evilem-motion-backward-word-begin z g b evil-forward-word-begin b
evilem-motion-backward-WORD-begin z g B evil-forward-WORD-begin B
evilem-motion-backward-word-end z g g e evil-forward-word-end g e
evilem-motion-backward-WORD-end z g g E evil-forward-WORD-end g E

These keybindings is also available on visual-mode. I prefer the simpler evil-snipe, so for now I'm not using this in my configuration

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
  (setq 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
  (setq 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-snipe   external

Evil-snipe emulates vim-seek and/or vim-sneak in evil-mode.

It provides 2-character motions for quickly (and more accurately) jumping around text, compared to evil's built-in f/F/t/T motions, incrementally highlighting candidate targets as you type.

By default, snipe only binds s (forward) / S (backward) to evil-snipe-s and evil-snipe-S, respectively. In operator mode, snipe is bound to z / Z and x / X (exclusive).

(use-package evil-snipe
  :commands evil-snipe-local-mode evil-snipe-override-local-mode
  :after evil
  :ensure t
  :hook (evil-after-load . (lambda () (evil-snipe-override-mode +1)))
  :hook (evil-after-load . (lambda () (evil-snipe-mode +1)))
  :hook (magit-mode . turn-off-evil-snipe-override-mode)
  :init
  (setq evil-snipe-smart-case t
        evil-snipe-scope 'line
        evil-snipe-repeat-scope 'visible
        evil-snipe-char-fold t)
  (evil-snipe-mode +1)
  (evil-snipe-override-mode +1))

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))

exato   external

This package provides the x text object to manipulate html/xml tag attributes. You can customize the binding.

Try using dax, vix and gUix.

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

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
  (setq evil-vimish-fold-mode-lighter " ⮒")
  (setq evil-vimish-fold-target-modes '(prog-mode conf-mode text-mode))
  (setq 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))

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))
  (with-eval-after-load 'emms
      (evil-global-set-key 'normal (kbd "<leader>>") '("emms next" . emms-next))
      (evil-global-set-key 'normal (kbd "<leader>>") '("emms previous" . emms-previous))))

C

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

(with-eval-after-load 'evil
  (setq 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
  (setq 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
  (setq 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
  (setq 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
  (setq 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
  (setq my/org-mode-map (make-sparse-keymap))
  (setq my/open-map (make-sparse-keymap))
  (setq my/open-org (make-sparse-keymap))
  (setq 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
  (setq 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
  (setq 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
  (setq 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
  (setq 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
  (setq my/z-map (make-sparse-keymap))
  (evil-define-key* '(normal visual) 'global (kbd "<leader>z") (cons "Z" my/z-map)))

Builtins

base   builtin

Usually configuration that defined at the C source code.

(use-package emacs
  :ensure nil
  :custom
  (read-buffer-completion-ignore-case t)
  (tab-always-indent nil)
  (visible-bell nil)
  (use-short-answers t)
  (use-dialog-box nil)
  (window-resize-pixelwise nil)
  (frame-resize-pixelwise t)
  (ring-bell-function #'ignore)
  (scroll-preserve-screen-position t)
  (scroll-conservatively 101)
  (fast-but-imprecise-scrolling t)
  (truncate-partial-width-windows nil)
  (tab-width 4)
  (fill-column 80)
  (enable-recursive-minibuffers t)
  (use-file-dialog nil)
  (create-lockfiles nil)
  (delete-by-moving-to-trash t)
  (inhibit-startup-screen t)
  :config
  (when (bound-and-true-p tooltip-mode)
(tooltip-mode -1))
  (setq completion-ignore-case t
        read-file-name-completion-ignore-case t
        read-buffer-completion-ignore-case t
        load-prefer-newer t
        auto-window-vscroll nil
        inhibit-compacting-font-caches t
        redisplay-skip-fontification-on-input t)
  (set-default 'indicate-empty-lines nil)
  (setq-default truncate-lines t)
  (setq-default x-stretch-cursor nil))

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))

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)))

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)))))

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)))

recentf   builtin

(use-package recentf
  :bind ("C-c f" . recentf)
  :commands recentf-open-files
  :hook (dired-mode . doom--recentf-add-dired-directory-h)
  :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")))))
  :config
  (defun doom--recentf-file-truename-fn (file)
    (if (or (not (file-remote-p file))
            (equal "sudo" (file-remote-p file 'method)))
        (abbreviate-file-name (file-truename (tramp-file-name-localname file)))
      file))
  ;; Resolve symlinks, strip out the /sudo:X@ prefix in local tramp paths, and
  ;; abbreviate $HOME -> ~ in filepaths (more portable, more readable, & saves
  ;; space)
  (add-to-list 'recentf-filename-handlers #'doom--recentf-file-truename-fn)
  ;; Text properties inflate the size of recentf's files, and there is
  ;; no purpose in persisting them (Must be first in the list!)
  (add-to-list 'recentf-filename-handlers #'substring-no-properties)
  (defun doom--recentf-add-dired-directory-h ()
    "Add dired directories to recentf file list."
    (recentf-add-file default-directory))
  ;; The most sensible time to clean up your recent files list is when you quit
  ;; Emacs (unless this is a long-running daemon session).
  (setq recentf-auto-cleanup (if (daemonp) 300))
  (add-hook 'kill-emacs-hook #'recentf-cleanup))

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

server   builtin

(use-package server
  :if (display-graphic-p)
  :defer 1
  :config
  (unless (server-running-p)
    (server-start))
  (require 'org-protocol))

UI

Base UI   builtin

(use-package emacs
  :ensure nil
  :hook ((prog-mode text-mode conf-mode) . display-line-numbers-mode)
  :config
  (setq hscroll-margin 2
        hscroll-step 1
        ;; Emacs spends too much effort recentering the screen if you scroll the
        ;; cursor more than N lines past window edges (where N is the settings of
        ;; `scroll-conservatively'). This is especially slow in larger files
        ;; during large-scale scrolling commands. If kept over 100, the window is
        ;; never automatically recentered. The default (0) triggers this too
        ;; aggressively, so I've set it to 10 to recenter if scrolling too far
        ;; off-screen.
        scroll-conservatively 10
        scroll-margin 0
        scroll-preserve-screen-position t
        ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll'
        ;; for tall lines.
        auto-window-vscroll nil
        ;; mouse
        mouse-wheel-scroll-amount '(2 ((shift) . hscroll))
        mouse-wheel-scroll-amount-horizontal 2)
  ;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any
  ;; feedback after typing is better UX than no feedback at all.
  (setq echo-keystrokes 0.02)
  ;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This
  ;; doesn't look too great with direnv, however...
  (setq resize-mini-windows 'grow-only)
  ;; Try to keep the cursor out of the read-only portions of the minibuffer.
  (setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
  ;; Show absolute line numbers for narrowed regions to make it easier to tell the
  ;; buffer is narrowed, and where you are, exactly.
  (setq-default display-line-numbers-widen t))

frame   builtin

(use-package frame
  :ensure nil
  :hook (after-init . window-divider-mode)
  :config
  (blink-cursor-mode 1)
  (setq window-divider-default-places t
        window-divider-default-bottom-width 1
        window-divider-default-right-width 1))

window   builtin

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

comint   builtin

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

winner   builtin

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

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.

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

hs-mode   builtin

(use-package hideshow
  :hook (prog-mode . hs-minor-mode))

mouse   builtin

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

paren   builtin

This built-in package is necessary when dealing with Emacs lisp.

(use-package paren
  :ensure nil
  :hook (prog-mode . show-paren-local-mode)
  :config
  (show-paren-mode 1)
  :custom
  (show-paren-style 'parenthesis)
  (show-paren-delay 0.4)
  (show-paren-context-when-offscreen 'overlay)
  (show-paren-when-point-inside-paren nil)
  (show-paren-when-point-in-periphery nil))

repeat-mode   builtin

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

which-key   external

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

(use-package which-key
  :ensure t
  :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))

marginalia   external

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

avy   external

By using gK or gJ in normal or visual mode.

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

rainbow-mode   external

(use-package rainbow-mode
  :hook (prog-mode . rainbow-mode)
  :ensure t
  :config
  (setq 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-identifiers   external

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

doom-themes   external

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

nerd-icons   external

(use-package nerd-icons
  :ensure t)

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))

Shells and Terminals

eshell   builtin

emacs-eshell.png

Figure 1: eshell in action

A shell-like command interpreter implemented in Emacs Lisp.

(use-package eshell
  :ensure nil
  :custom
  (eshell-history-size 10000)
  (eshell-hist-ignore-dups t)
  (eshell-buffer-maximum-lines 10000)
  (eshell-scroll-to-bottom-on-input t)
  (eshell-destroy-buffer-when-process-dies t)
  (eshell-prompt-regexp "^[^\)]*[\)] "))

sh-mode   builtin

Install bash-language-server with npm install -g bash-language-server

(use-package sh-script
  :ensure nil
  :mode ("\\.bats\\'" . sh-mode)
  :mode ("\\.\\(?:zunit\\|env\\)\\'" . sh-mode)
  :mode ("/bspwmrc\\'" . sh-mode)
  :mode ("PKGBUILD" . sh-mode)
  :config
  (setq sh-indent-after-continuation 'always)
  (with-eval-after-load 'rainbow-delimiters
    (add-hook 'sh-mode-hook #'rainbow-delimiters-mode)
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '((sh-mode bash-ts-mode) .("bash-language-server" "start"))))
  (add-hook 'sh-mode-hook #'eglot-ensure)
  (add-hook 'bash-ts-mode-hook #'eglot-ensure)))

vterm   external

(use-package vterm
  :ensure t
  :demand t
  :commands vterm-mode
  :config
  (with-eval-after-load 'evil
      (setq 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)))
  (with-eval-after-load 'evil-collection
    (evil-collection-vterm-setup))
  (add-hook 'vterm-mode-hook
            (lambda ()
              (setq-local global-hl-line-mode nil)
              (setq-local hscroll-margin 0)))
  (setq 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)
  (with-eval-after-load 'evil
    (evil-define-key* nil my/vterm-map "V" '("toggle terminal" . 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

select   builtin

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

transient-mark-mode   builtin

Highlight region.

(transient-mark-mode 1)

delete-selection-mode   builtin

When enabled, typed text replaces the selection if the selection is active. Otherwise, typed text is just inserted at point regardless of any selection.

(delete-selection-mode 1)

subword   builtin

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

text-mode   builtin

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

whitespace   builtin

(use-package whitespace
  :ensure nil
  :config
  (setq whitespace-line-column nil
        whitespace-style
        '(face indentation tabs tab-mark spaces space-mark newline
               trailing lines-tail)
        whitespace-display-mappings
        '((tab-mark ?\t [?› ?\t])
          (newline-mark ?\n [?¬ ?\n])
          (space-mark ?\  [?·] [?.]))))

ispell   builtin

(use-package ispell
  :ensure nil
  :config
  (setq ispell-dictionary "english"
        ispell-personal-dictionary (expand-file-name "dictionary/personal" user-emacs-directory))
  (add-to-list 'ispell-skip-region-alist
               '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:"))
  (add-to-list 'ispell-skip-region-alist
               '("#\\+BEGIN_SRC" . "#\\+END_SRC"))
  (add-to-list 'ispell-skip-region-alist
               '("#\\+BEGIN_EXAMPLE" . "#\\+END_EXAMPLE"))
  (when (executable-find "aspell")
    (setq ispell-program-name (executable-find "aspell")
          ispell-extra-args '("--sug-mode=ultra"
                              "--run-together"))
    (unless ispell-aspell-dict-dir
      (setq ispell-aspell-dict-dir
            (ispell-get-aspell-config-value "dict-dir")))
    (unless ispell-aspell-data-dir
      (setq ispell-aspell-data-dir
            (ispell-get-aspell-config-value "data-dir")))
    (add-hook 'text-mode-hook
              (lambda ()
                (setq-local ispell-extra-args
                            (remove "--run-together" ispell-extra-args)))))
  (when (executable-find "hunspell")
    (setq ispell-program-name (executable-find "hunspell")))
  (when (executable-find "enchant-2")
    (setq ispell-program-name (executable-find "enchant-2")
          ispell-cmd-args '("-a"))))

flyspell   builtin

(use-package flyspell
  :ensure nil
  :config
  (when (executable-find "aspell")
    (setq ispell-program-name (executable-find "aspell")
          ispell-extra-args '("--sug-mode=ultra"
                              "--run-together")))
  (when (executable-find "hunspell")
    (setq ispell-program-name (executable-find "hunspell")
          ispell-extra-args nil))
  (when (executable-find "enchant-2")
    (setq ispell-program-name (executable-find "enchant-2")
          ispell-cmd-args '("-a")
          ispell-extra-args nil)))

IDE stuffs

eldoc   builtin

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

eglot   builtin

(use-package eglot
  :hook ((rust-mode nix-mode python-mode
                                      toml-mode markdown-mode
                                      js-mode js-ts-mode typescript-ts-mode) . eglot-ensure)
  :init
  (setopt eglot-sync-connect 1
                eglot-autoshutdown t)
  :config
  (with-eval-after-load 'cape
      (advice-add 'eglot-completion-at-point :around #'cape-wrap-super))
  (with-eval-after-load 'evil-collection
    (evil-collection-eglot-setup))
  (add-to-list 'eglot-server-programs
               `(rust-mode . ("rust-analyzer" :initializationOptions
                              ( :procMacro (:enable t)
                                :cargo ( :buildScripts (:enable t)
                                         :features "all"))))
               '(toml-ts-mode . ("taplo" "lsp" "stdio")))
  (add-to-list 'eglot-server-programs
                         '(web-mode . ("vscode-html-language-server" "--stdio")))
  (add-to-list 'eglot-server-programs
                         '(css-mode . ("vscode-css-language-server" "--stdio"))))

consult-eglot   external

(use-package consult-eglot
  :after '(consult eglot)
  :ensure t)

consult-eglot   external

(use-package consult-eglot-embark
  :ensure t
  :after consult-eglot)

flymake   builtin

Built-in on-the-fly syntax checking.

(use-package flymake
  :ensure t
  :pin gnu
  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-flymake-setup))
  (setq flymake-diagnostic-format-alist
        '((t . (origin code message)))))

copilot   external

I still have mixed feelings about AI, but I do want to explore, just don't use it from the start

(use-package copilot
  :hook (prog-mode . copilot-mode)
  :load-path "site-lisp/copilot"
  :bind (:map copilot-mode-map
            ("<tab>" . 'copilot-accept-completion)
            ("Tab" . 'copilot-accept-completion)
            ("C-TAB" . 'copilot-accept-completion-by-word)
            ("C-<tab>" . 'copilot-accept-completion-by-word))
  :config
  (setopt copilot-idle-delay 2))

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)
  (with-eval-after-load 'evil
    (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
  (setq persp-initial-frame-name "Main"
        persp-suppress-no-prefix-key-warning t)
  (if (featurep 'no-littering)
      (setq persp-state-default-file (expand-file-name ".perspective-state" no-littering-var-directory))
    (setq 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))
  (with-eval-after-load 'evil
    (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"))
  (unless (equal persp-mode t)
    (persp-mode 1))
  :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)

Snippets   external

This is provided by yasnippet.

(use-package yasnippet
  :ensure t
  :init
  (yas-global-mode)
  :config
  (define-key yas-keymap [tab] 'yas-next-field)
  (setq yas-snippets-dir
        (list (expand-file-name "snippets" user-emacs-directory)
              (expand-file-name "straight/build/yasnippet-snippets/snippets" user-emacs-directory)))
  (with-eval-after-load 'evil
    (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))

treesit-auto   external

(use-package treesit-auto
  :ensure t
  :custom
  (treesit-auto-install 'prompt)
  :config
  (treesit-auto-add-to-auto-mode-alist 'all)
  (global-treesit-auto-mode))

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))

Org-mode

org-mode core package   builtin

(use-package org
  :demand t
  :commands org-tempo
  :hook (org-mode . flyspell-mode)
  :preface
  (if (not +config/org-directory)
      (cond
       ((file-directory-p
         (expand-file-name "Dropbox/org" (getenv "HOME")))
        (setq org-directory (expand-file-name "Dropbox/org" (getenv "HOME"))))
       ((file-directory-p
         (expand-file-name "Sync/org" (getenv "HOME")))
        (setq org-directory (expand-file-name "Sync/org" (getenv "HOME"))))
       ((file-directory-p
         (expand-file-name "Documents/google-drive/org" (getenv "HOME")))
        (setq org-directory (expand-file-name "Documents/google-drive/org" (getenv "HOME")))))
    (customize-set-variable 'org-directory +config/org-directory))
  :hook ((org-mode . org-indent-mode)
         (org-mode . +config/org-prettify-symbols)
         (org-mode . variable-pitch-mode))
  :config
  (with-eval-after-load 'evil
    (setq 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)
  (when (file-directory-p (expand-file-name "braindump/org" org-directory))
    (customize-set-variable '+config/org-roam-directory
                            (expand-file-name "braindump/org" org-directory)))
  (when (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)))
  (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
  (setq 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))
  (setq 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
    (setq 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
  (setq 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-priority   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
    (setq org-download-image-dir org-attach-id-dir))
  (setq 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 (string-match-p "microsoft" (shell-command-to-string "uname -a")))
  :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
  (setq 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

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")))

Misc Tools

make-mode   builtin

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

editorconfig   external

(use-package editorconfig
  :ensure t)

executable   builtin

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

magit   external

emacs-magit.png

Figure 2: Emacs magit

(use-package magit
  :ensure t
  :demand t
  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-magit-repos-setup)
    (evil-collection-magit-section-setup)
    (evil-collection-magit-setup))
  (with-eval-after-load 'evil
    (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")))

elfeed   external

An Emacs web feeds client.

(use-package elfeed
  :ensure t
  :demand t
  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-elfeed-setup))
  (with-eval-after-load 'evil
    (evil-define-key* nil my/open-map "e" '("open elfeed" . elfeed))))
  • extensions
    • elfeed-org

      Configure the Elfeed RSS reader with an Orgmode file.

      (use-package elfeed-org
        :after elfeed
        :ensure t
        :init
        (elfeed-org)
        :config
        (setq rmh-elfeed-org-files (list (expand-file-name "elfeed.org" org-directory))))
      
    • elfeed-goodies
      (use-package elfeed-goodies
        :after elfeed
        :ensure t
        :init
        (elfeed-goodies/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))
    

pass   external

(use-package password-store
  :ensure t
  :demand t
  :config
  (setq password-store-password-length 12)
  (setq 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
  (with-eval-after-load 'evil
    (setq 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))
    (with-eval-after-load 'projectile
      (evil-define-key* nil projectile-command-map "z" '("fzf projectile" . fzf-projectile))))
  (setq 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
  (with-eval-after-load 'evil-collection
    (evil-collection-rg-setup)))

mastodon   external

(use-package mpv
  :ensure t)

(use-package emojify
  :ensure t
  :hook (after-init . global-emojify-mode))

(use-package mastodon
  :ensure t
  :config
  (mastodon-discover)
  (with-eval-after-load 'evil
      (setq my/mastodon-map (make-sparse-keymap))
      (evil-define-key* nil my/c-map
        "m" (cons "Mastodon" my/mastodon-map))
      (evil-define-key* nil my/mastodon-map
        "m" '("Mastodon" . mastodon)
        "t" '("new toot" . mastodon-toot)
        "N" '("Get notification" . mastodon-notifications-get)
        "O" '("My Profile" . mastodon-profile-my-profile))
      (evil-define-key '(normal visual) mastodon-mode-map
        "n" '("next item" . mastodon-tl-goto-next-item)
        "p" '("previous item" . mastodon-tl-goto-prev-item)
        (kbd "<tab>") '("next tab item" . mastodon-tl-next-tab-item)
        (kbd "S-<tab>") '("previous tab item" . mastodon-tl-next-tab-item)
        (kbd "M-n") '("next tab item" . mastodon-tl-next-tab-item)
        (kbd "M-p") '("previous tab item" . mastodon-tl-previous-tab-item)
        "F" '("Federated timeline" . mastodon-tl-get-federated-timeline)
        "H" '("Home timeline" . mastodon-tl-get-home-timeline)
        "L" '("Local timeline" . mastodon-tl-get-local-timeline)
        "N" '("Notification" . mastodon-notifications-get)
        "@" '("Mention notification" . mastodon-notifications-get-mentions)
        "u" '("Update current timeline" . mastodon-tl-update)
        "T" '("Open thread for toot at point" . mastodon-tl-thread)
        "#" '("Prompt for tag and open its timeline" . mastodon-tl-get-tag-timeline)
        "A" '("Open author profile of toot at point" . mastodon-profile-get-toot-author)
        "P" '("Open profile of user attached to toot at point" . mastodon-profile-show-user)
        "O" '("View own profile" . mastodon-profile-my-profile)
        "U" '("update your profile bio note" . mastodon-profile-update-user-profile-note)
        ";" '("view instance description for toot at point" . mastodon-views-view-instance-description)
        ":" '("current user settings" . mastodon-user-settings)
        (kbd "C-:") '("notifications policy options" . mastodon-notifications-policy)
        "," '("List the favouriters of toot at point" . mastodon-toot-list-favouriters)
        "." '("List the boosters of toot at point" . mastodon-toot-list-boosters)
        "/" '("Switch to a live mastodon buffer" . mastodon-switch-to-buffer)
        "\\" '("prompt for an instance domain and view its local timeline" . mastodon-tl-get-remote-local-timeline)
        "Z" '("Report the author of the toot at point to your instance moderators" . mastodon-tl-report-to-mods)
        "s" '("Prompt for a search QUERY" . mastodon-search-query)
        "t" '("Update instance with new toot." . mastodon-toot)
        "b" '("Boost/unboost toot" . mastodon-toot-toggle-boost)
        "f" '("Favourite/unfavourite toot" . mastodon-toot-toggle-favourite)
        "k" '("Bookmark or unbookmark toot" . mastodon-toot-toggle-bookmark)
        "r" '("reply to toot" . mastodon-toot-reply)
        "v" '("vote on a poll at point" . mastodon-tl-poll-vote)
        "C" '("Copy URL of toot at point" . mastodon-toot-copy-toot-url)
        (kbd "C-<return>") '("play video/gif at point" . mastodon-tl-mpv-play-video-at-point)
        "e" '("Edit user toot at point" . mastodon-toot-edit-toot-at-point)
        "i" '("Pin or unpin user’s toot at point" . mastodon-toot-pin-toot-toggle)
        "i" '("Delete user’s toot at point synchronously" . mastodon-toot-delete-toot)
        "!" '("Toggle the folding status of the toot at point" . mastodon-tl-fold-post-toggle)
        "W" '("Follow user" . mastodon-tl-follow-user)
        (kbd "S-C-w") '("Unollow user" . mastodon-tl-unfollow-user)
        "M" '("Mute user" . mastodon-tl-mute-user)
        (kbd "S-C-m") '("unmute user" . mastodon-tl-unmute-user)
        "B" '("Block user" . mastodon-tl-block-user)
        (kbd "S-C-b") '("unblock user" . mastodon-tl-unblock-user)
        "q" '("Kill buffer" . kill-current-buffer)
        "Q" '("Kill window" . mastodon-kill-window)
        ))
  (setopt mastodon-instance-url "https://mastodon.social"
                mastodon-active-user "alexforsale"))

There's lot of keybindings

emms   external

Autoload these functions.

;;;###autoload
(defun +emms/notification ()
(interactive)
(let ((artist (string-trim (shell-command-to-string "playerctl -p mpd metadata xesam:artist")))
      (album (string-trim (shell-command-to-string "playerctl -p mpd metadata xesam:album")))
      (cover (string-trim (shell-command-to-string "playerctl -p mpd metadata mpris:artUrl")))
      (title (string-trim (shell-command-to-string "playerctl -p mpd metadata --format '{{title}}'"))))
(call-process "notify-send" nil nil nil
                        "-a" "emms" "-t" "2600"
                        "-r" "27073"
                        (format "%s" title)
                        (format "<span color='#fdf6e3'>%s </span>\n<span color='#268bd2'>%s</span>" artist album)
                        "-i" cover)))
(use-package emms
  :ensure t
  :config
  :if (executable-find "mpd")
  (require 'emms-setup)
  (require 'emms-player-mpd)
  (emms-all)
  (setopt emms-player-mpd-music-directory (expand-file-name "Music" (getenv "HOME")))
  (setopt emms-player-list '(emms-player-mpd))
  (setopt emms-info-functions 'emms-info-mpd)
  (add-hook 'emms-player-started-hook #'+emms/notification nil)
  (emms-default-players)
  (emms-cache-set-from-mpd-all)
  (emms-player-mpd-connect)
  ;(emms-mpris-enable)
  (with-eval-after-load 'evil
      (evil-collection-emms-setup))

  ;; keybindings
  (with-eval-after-load 'evil
      (setopt my/emms-map (make-sparse-keymap))
      (evil-define-key nil my/open-map
        (kbd "E") (cons "EMMS" my/emms-map))
      (evil-define-key* nil my/emms-map
        "e" '("emms" . emms))
      )

  ;; from `doom'
  (defun +emms/mpd-start-music-daemon ()
      (interactive)
      (start-process "mpd" nil "mpd")
      (+emms/mpc-update-database)
      (emms-player-mpd-connect)
      (emms-cache-set-from-mpd-all)
      (message "MPD Started!"))

  (defun +emms/mpd-kill-music-daemon ()
      (interactive)
      (emms-stop)
      (call-process "mpd" nil nil nil "--kill")
      (message "MPD Killed!"))

  (defun +emms/mpc-update-database ()
      (interactive)
      (call-process "mpc" nil nil nil "update")
      (message "MPD Database Updated!"))

  (defun +emms/mpd-restart-music-daemon ()
      (interactive)
      (+emms/mpd-kill-music-daemon)
      (+emms/mpd-start-music-daemon)
      (message "MPD Restarted!")))

Mail

notmuch

(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 "zum" :query "tag:zum" :key "z")
          (:name "mkn" :query "tag:mkn" :key "c")
          (:name "gmail" :query "tag:gmail" :key "g")
          (:name "hotmail" :query "tag:hotmail" :key "h")
          (:name "yahoo" :query "tag:yahoo" :key "h")
          (:name "ymail" :query "tag:ymail" :key "m")
          (: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")
      (setq notmuch-crypto-gpg-program "gpg2")
    (setq notmuch-crypto-gpg-program "gpg"))
  (setq 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
  (setq 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

javascript   builtin

(use-package js
  :ensure nil
  :mode ("\\.[mc]?js\\'" . js-mode)
  :mode ("\\.es6\\'" . js-mode)
  :mode ("\\.pac\\'" . js-mode)
  :config
  (with-eval-after-load 'eglot
      (add-hook 'js-mode-hook #'eglot-ensure)
      (add-hook 'js-ts-mode-hook #'eglot-ensure))
  (with-eval-after-load 'smartparens
      (sp-local-pair 'js-mode "<" nil :actions :rem)
      (sp-local-pair 'js-ts-mode "<" nil :actions :rem))
  (setopt js-chain-indent t))
  • nodejs-repl   external
    (use-package nodejs-repl
      :ensure t)
    
  • typescript-mode   external
    (use-package typescript-mode
      :ensure t
      :interpreter "ts-node"
      :config
      (with-eval-after-load 'eglot
          (add-hook 'js-ts-mode-hook #'eglot-ensure)
          (add-hook 'typescript-ts-mode-hook #'eglot-ensure)))
    
  • rjsx   external
    (use-package rjsx-mode
      :ensure t)
    

kdl-mode   external

(use-package kdl-mode
  :ensure t)

Markdown   external

(use-package markdown-mode
  :ensure t
  :defer t
  :mode
  ("README\\.md\\'" . gfm-mode)
  :init
  (with-eval-after-load 'eglot
      (add-hook 'markdown-mode-hook 'eglot-ensure))
  (when (executable-find "markdown")
    (setq markdown-command "markdown"))
  (when (executable-find "multimarkdown")
    (setq markdown-command "multimarkdown"))
  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-markdown-mode-setup))
  (with-eval-after-load 'lsp-mode
    (add-hook 'markdown-mode-hook #'lsp-deferred))
  (setq markdown-fontify-code-blocks-natively t))

(use-package poly-markdown
  :ensure t
  :init
  :mode
  (("README\\.md\\'" . gfm-mode)
   ("\\.md$" . markdown-mode)
   ("\\.markdown$" . markdown-mode)))

(use-package edit-indirect
  :ensure t
  :after markdown-mode)

Python   builtin

(use-package python
  :ensure nil
  :mode ("[./]flake8\\'" . conf-mode)
  :mode ("/Pipfile\\'" . conf-mode)
  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-python-setup))
  (with-eval-after-load 'eglot
    (add-hook 'python-mode-hook #'eglot-ensure)
    (add-hook 'python-ts-mode-hook #'eglot-ensure)
    (add-to-list 'eglot-server-programs
                 `(python-mode
                   . ,(eglot-alternatives '(("pyright-langserver" "--stdio")
                                            "jedi-language-server"
                                            "pylsp")))))
  (with-eval-after-load 'lsp-mode
    (add-hook 'python-mode-hook #'lsp-deferred)
    (add-hook 'python-mode-hook #'lsp-ui-mode)
    (add-hook 'python-mode-hook #'lsp-ui-doc-mode)
    (add-hook 'python-mode-local-vars-hook '(lambda () (lsp-deferred))))
  (if (executable-find "ipython")
      (setq python-interpreter (executable-find "ipython"))
    (setq python-interpreter (executable-find "python3")))
  (when (featurep 'projectile)
    (add-to-list 'projectile-globally-ignored-directories "^\\.venv$"))
  (let ((directories `("/usr/bin/" "/usr/local/bin/" "/opt/bin" ,(expand-file-name ".local/bin/" (getenv "HOME")))))
    (dolist (directory directories) (when (file-directory-p directory) (add-to-list 'python-shell-exec-path directory))))
  (setq python-indent-guess-indent-offset nil
        python-shell-completion-native-enable nil
        python-shell-exec-path (list "/usr/bin/" "/usr/local/bin" (expand-file-name ".local/bin/" (getenv "HOME")))
        python-indent-guess-indent-offset-verbose nil)
  (when (featurep 'lsp-mode)
    (setq lsp-pylsp-plugins-rope-completion-enabled t ; needs python-rope package
          lsp-pylsp-plugins-mccabe-enabled t ; needs python-mccabe package
          lsp-ruff-lsp-python-path (executable-find "python3"))
    (when (executable-find "black")
      (setq lsp-pylsp-plugins-black-enabled t))
    (when (executable-find "autopep8")
      (setq lsp-pylsp-plugins-autopep8-enabled t))
    (when (executable-find "flake8")
      (setq lsp-pylsp-plugins-flake8-enabled t))
    (when (executable-find "pycodestyle")
      (setq lsp-pylsp-plugins-pycodestyle-enabled t))
    (when (executable-find "pydocstyle")
      (setq lsp-pylsp-plugins-pydocstyle-enabled t))
    (when (executable-find "pylint")
      (setq lsp-pylsp-plugins-pylint-enabled t))
    (when (executable-find "pyflakes")
      (setq lsp-pylsp-plugins-pyflakes-enabled t))
    (when (executable-find "yapf")
      (setq lsp-pylsp-plugins-yapf-enabled t)))
  (when (featurep 'flycheck)
    (setq flycheck-python-mypy-executable (executable-find "mypy")
          flycheck-python-flake8-executable (executable-find "flake8")
          flycheck-python-pylint-executable (executable-find "pylint")
          flycheck-python-pyright-executable (executable-find "pyright")))
  (with-eval-after-load 'tree-sitter
    (add-hook 'python-mode-local-vars-hook #'tree-sitter! 'append))

  (defun +python-executable-find (exe)
    "Resolve the path to the EXE executable.
  Tries to be aware of your active conda/pipenv/virtualenv environment, before
  falling back on searching your PATH."
    (if (file-name-absolute-p exe)
        (and (file-executable-p exe)
             exe)
      (let ((exe-root (format "bin/%s" exe)))
        (cond ((when python-shell-virtualenv-root
                 (let ((bin (expand-file-name exe-root python-shell-virtualenv-root)))
                   (if (file-exists-p bin) bin))))
              ((when (require 'conda nil t)
                 (let ((bin (expand-file-name (concat conda-env-current-name "/" exe-root)
                                              (conda-env-default-location))))
                   (if (file-executable-p bin) bin))))
              ((when-let (bin (projectile-locate-dominating-file default-directory "bin/python"))
                 (setq-local doom-modeline-python-executable (expand-file-name "bin/python" bin))))
              ((executable-find exe))))))

  (defun +python/open-repl ()
    "Open the Python REPL."
    (interactive)
    (require 'python)
    (unless python-shell-interpreter
      (user-error "`python-shell-interpreter' isn't set"))
    (pop-to-buffer
     (process-buffer
      (let ((dedicated (bound-and-true-p python-shell-dedicated)))
        (if-let* ((pipenv (+python-executable-find "pipenv"))
                  (pipenv-project (pipenv-project-p)))
            (let ((default-directory pipenv-project)
                  (python-shell-interpreter-args
                   (format "run %s %s"
                           python-shell-interpreter
                           python-shell-interpreter-args))
                  (python-shell-interpreter pipenv))
              (run-python nil dedicated t))
          (run-python nil dedicated t))))))

  (defun +python/open-ipython-repl ()
    "Open an IPython REPL."
    (interactive)
    (require 'python)
    (let ((python-shell-interpreter
           (or (+python-executable-find (car +python-ipython-command))
               "ipython"))
          (python-shell-interpreter-args
           (string-join (cdr +python-ipython-command) " ")))
      (+python/open-repl)))

  (defun +python/open-jupyter-repl ()
    "Open a Jupyter console."
    (interactive)
    (require 'python)
    (add-to-list 'python-shell-completion-native-disabled-interpreters "jupyter")
    (let ((python-shell-interpreter
           (or (+python-executable-find (car +python-jupyter-command))
               "jupyter"))
          (python-shell-interpreter-args
           (string-join (cdr +python-jupyter-command) " ")))
      (+python/open-repl)))

  (defun +python/optimize-imports ()
    "organize imports"
    (interactive)
    (pyimport-remove-unused)
    (py-isort-buffer))

  (defun +python-use-correct-flycheck-executables-h ()
    "Use the correct Python executables for Flycheck."
    (let ((executable python-shell-interpreter))
      (save-excursion
        (goto-char (point-min))
        (save-match-data
          (when (or (looking-at "#!/usr/bin/env \\(python[^ \n]+\\)")
                    (looking-at "#!\\([^ \n]+/python[^ \n]+\\)"))
            (setq executable (substring-no-properties (match-string 1))))))
      ;; Try to compile using the appropriate version of Python for
      ;; the file.
      (setq-local flycheck-python-pycompile-executable executable)
      ;; We might be running inside a virtualenv, in which case the
      ;; modules won't be available. But calling the executables
      ;; directly will work.
      (setq-local flycheck-python-pylint-executable "pylint")
      (setq-local flycheck-python-flake8-executable "flake8")))

  (defun +python-pyenv-mode-set-auto-h ()
    "Set pyenv-mode version from buffer-local variable."
    (when (eq major-mode 'python-mode)
      (when (not (local-variable-p '+pyenv--version))
        (make-local-variable '+pyenv--version)
        (setq +pyenv--version (+python-pyenv-read-version-from-file)))
      (if +pyenv--version
          (pyenv-mode-set +pyenv--version)
        (pyenv-mode-unset))))

  (defun +python-pyenv-read-version-from-file ()
    "Read pyenv version from .python-version file."
    (when-let (root-path (projectile-locate-dominating-file default-directory ".python-version"))
      (let* ((file-path (expand-file-name ".python-version" root-path))
             (version
              (with-temp-buffer
                (insert-file-contents-literally file-path)
                (string-trim (buffer-string)))))
        (if (member version (pyenv-mode-versions))
            version  ;; return.
          (message "pyenv: version `%s' is not installed (set by `%s')."
                   version file-path)))))

  (with-eval-after-load 'evil
      (setq my/python-map (make-sparse-keymap))
      (evil-define-key* '(normal visual) python-mode-map (kbd "<leader>m") (cons "python-mode" my/python-map))
      (evil-define-key* '(normal visual) python-ts-mode-map (kbd "<leader>m") (cons "python-mode" my/python-map))
    (setq my/python-send-map (make-sparse-keymap))
    (setq my/python-repl-map (make-sparse-keymap))
    (evil-define-key* nil my/python-map (kbd "s") (cons "send to repl" my/python-send-map))
    (evil-define-key* nil my/python-map
      "c" '("check" . python-check)
      "i" '("fix import" . python-fix-imports)
      "r" '("open python repl" . +python/open-repl))
    (evil-define-key* nil my/python-send-map
      "b" '("send buffer" . python-shell-send-buffer)
      "B" '("send block" . python-shell-send-block)
      "d" '("send defun" . python-shell-send-defun)
      "r" '("send region" . python-shell-send-region)
      "s" '("send statement" . python-shell-send-statement))))

Rust   external

For rust there are two options: rust-mode, or rustic, I chose the latter since it's basically rust-mode with additional features.

(use-package rust-mode
  :ensure t
  :init
  (add-hook 'rustic-mode-hook (lambda () flymake-mode -1))
  (setq rust-mode-treesitter-derive t))

(use-package rustic
  :ensure t
  :after rust-mode
  :init
  (with-eval-after-load 'rustic-flycheck
    (remove-hook 'rustic-mode-hook #'flycheck-mode)
    (remove-hook 'rustic-mode-hook #'flymake-mode)
    (remove-hook 'rustic-mode-hook #'flymake-mode-off)
    (remove-hook 'flycheck-mode-hook #'rustic-flycheck-setup))
  (add-hook 'rustic-mode-hook (lambda () flymake-mode -1))
  (with-eval-after-load 'eglot
    (setq rustic-lsp-client 'eglot
          rustic-lsp-setup-p nil)
    (add-hook 'eglot--managed-mode-hook (lambda () (flymake-mode +1)))
  (add-to-list 'eglot-server-programs
               '((rust-ts-mode rust-mode rustic-mode)
                 ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))))
  (with-eval-after-load 'org-src
    (add-to-list 'org-src-lang-modes '("rust" . rustic)))
  :config
  (setq rustic-format-on-save nil)
  (when (executable-find "rustup")
    (setq rustic-analyzer-command
          `(,(executable-find "rustup")
            "run"
            "stable"
            "rust-analyzer")))
  (setq rustic-indent-method-chain t)
  (setq rustic-babel-format-src-block nil
        rustic-format-trigger nil)
  (with-eval-after-load 'evil
    (setq my/rustic-map (make-sparse-keymap))
    (setq my/cargo-map (make-sparse-keymap))
    (evil-define-key* '(normal visual) rustic-mode-map (kbd "<leader>m") (cons "rustic-mode" my/rustic-map))
    (evil-define-key* nil my/rustic-map (kbd "c") (cons "Cargo" my/cargo-map))
    (evil-define-key* nil my/cargo-map
      "a" '("cargo add" . rustic-cargo-add)
      "B" '("cargo build" . rustic-cargo-build)
      "b" '("cargo bench" . rustic-cargo-bench)
      "c" '("cargo check" . rustic-cargo-check)
      "C" '("cargo clippy" . rustic-cargo-clippy)
      "F" '("cargo fix" . rustic-rustfix)
      "f" '("cargo format workspace" . rustic-cargo-fmt)
      "i" '("cargo initialize" . rustic-cargo-init)
      "o" '("cargo outdated" . rustic-cargo-outdated)
      "t" '("cargo test" . rustic-cargo-test)
      "r" '("cargo run" . rustic-cargo-run))
    (with-eval-after-load 'consult
      (evil-define-key* nil my/rustic-map
        "m" '("consult flymake" . consult-flymake)))
    (with-eval-after-load 'eglot
      (evil-define-key* nil my/rustic-map
        "a" '("eglot code action" . eglot-code-actions)
        "I" '("eglot organize Import" . eglot-code-action-organize-imports)
        "r" '("eglot rename" . eglot-rename))))
  :custom
  (rustic-analyzer-command '("rustup" "run" "stable" "rust-analyzer"))
  (rustic-cargo-use-last-stored-arguments t))

toml-mode   external

(use-package toml-mode
  :ensure t
  :demand t
  :mode
  ("\\.toml\\'" . toml-mode)
  :config
  (with-eval-after-load 'eglot
    (add-hook 'toml-mode-hook #'eglot-ensure)
    (add-hook 'toml-ts-mode-hook #'eglot-ensure)
    (add-to-list 'eglot-server-programs
                 '(toml-ts-mode . ("taplo" "lsp" "stdio"))))
  (with-eval-after-load 'lsp-mode
    (add-hook 'toml-mode-hook #'lsp-deferred)
    (setq lsp-toml-command (executable-find "taplo"))
    (add-to-list 'lsp-language-id-configuration '("\\.toml$" . "toml"))))

Install taplo-cli from archlinux package or using cargo with cargo install taplo-cli --features lsp.

web-mode

  • emmet   external
    (use-package emmet-mode
      :ensure t
      :hook (sgml-mode css-mode html-mode web-mode haml-mode nxml-mode rjsx-mode reason-mode)
      :config
      (setopt emmet-move-cursor-between-quotes t
                    emmet-self-closing-tag-style " /")
      (when (require 'yasnippet nil t)
          (add-hook 'emmet-mode-hook #'yas-minor-mode-on))
      (with-eval-after-load 'evil
          (evil-define-key 'visual emmet-mode-keymap
            (kbd "<tab>") 'emmet-wrap-with-markup)
          (evil-define-key '(normal insert emacs) emmet-mode-keymap
            (kbd "<tab>") 'emmet-expand-line)))
    
  • web-mode   external
    (use-package web-mode
      :ensure t
      :mode "\\.[px]?html?\\'"
      :mode "\\.\\(?:tpl\\|blade\\)\\(?:\\.php\\)?\\'"
      :mode "\\.erb\\'"
      :mode "\\.[lh]?eex\\'"
      :mode "\\.jsp\\'"
      :mode "\\.as[cp]x\\'"
      :mode "\\.ejs\\'"
      :mode "\\.hbs\\'"
      :mode "\\.mustache\\'"
      :mode "\\.svelte\\'"
      :mode "\\.twig\\'"
      :mode "\\.jinja2?\\'"
      :mode "\\.eco\\'"
      :mode "wp-content/themes/.+/.+\\.php\\'"
      :mode "templates/.+\\.php\\'"
      :init
      (add-to-list 'auto-mode-alist '("\\.vue\\'" . web-mode) 'append)
      :mode "\\.vue\\'"
      :hook (web-mode . eglot-ensure)
      :config
      (setopt web-mode-enable-html-entities-fontification t
                    web-mode-auto-close-style 1)
      (with-eval-after-load 'smartparens
          (defun +web-is-auto-close-style-3 (_id action _context)
            (and (eq action 'insert)
                     (eq web-mode-auto-close-style 3)))
          (sp-local-pair 'web-mode "<" ">" :unless '(:add +web-is-auto-close-style-3))
          (setopt web-mode-enable-auto-quoting nil
                          web-mode-enable-auto-pairing t)
          (dolist (alist web-mode-engines-auto-pairs)
            (setcdr alist
                            (cl-loop for pair in (cdr alist)
                                             unless (string-match-p "^[a-z-]" (cdr pair))
                                             collect (cons (car pair)
                                                                           (string-trim-right (cdr pair)
                                                                                                                  "\\(?:>\\|]\\|}\\)+\\'")))))
          (cl-callf2 delq nil web-mode-engines-auto-pairs))
      (add-to-list 'web-mode-engines-alist '("elixir" . "\\.eex\\'"))
      (add-to-list 'web-mode-engines-alist '("phoenix" . "\\.[lh]eex\\'"))
      (setf (alist-get "javascript" web-mode-comment-formats nil nil #'equal)
                  "//"))
    
  • css-mode   builtin
    (use-package css-mode
      :ensure nil
      :hook (css-mode . rainbow-mode)
      :hook (css-mode . eglot-ensure))
    
  • scss-mode   external
    (use-package scss-mode
      :ensure t
      :mode "\\.scss\\'"
      :hook (scss-mode . rainbow-mode)
      :hook (scss-mode . eglot-ensure)
      :init
      (with-eval-after-load 'flymake
          (require 'flymake-proc nil t)))
    

yaml-mode   external

(use-package yaml-mode
  :ensure t
  :config
  (with-eval-after-load 'eglot
    (add-hook 'yaml-mode-hook #'eglot-ensure)
    (add-hook 'yaml-ts-mode-hook #'eglot-ensure)
    (setq-default eglot-workspace-configuration
                  '(:yaml (:format (:enable t)
                                   :validate t
                                   :hover t
                                   :completion t
                                   :schemas (https://raw.githubusercontent.com/my-user/my-project/project.schema.yml ["project.yml"]
                                                                                                                     https://json.schemastore.org/yamllint.json ["/*.yml"])
                                   :schemaStore (:enable t)))))
  (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))
  (add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-mode))
  (setq tab-width 2)
  ;; (when (treesit-available-p)
  ;;   (add-to-list 'major-mode-remap-alist '(yaml-mode . yaml-ts-mode)))
  :hook
  ((yaml-mode . (lambda ()
                  (define-key yaml-mode-map "\C-m" 'newline-and-indent)))
   (yaml-mode . (lambda ()
                  (run-hooks 'prog-mode-hook)))))

Needs the package yaml-language-server, can be obtain from the archlinux repository.

yuck-mode

(use-package yuck-mode
  :ensure t)

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
        (setq 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)
          (setq 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)))
      
        (setq 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)
  ;; (setq orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch)
  ;;       orderless-component-separator #'orderless-escapable-split-on-space)
  (setq 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.

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.
      (setq 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
      (setq 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.
      ;; (setq consult-preview-key 'any)
      ;; (setq consult-preview-key "M-.")
      ;; (setq 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.
      (setq 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)
      ;;(setq consult-project-function #'consult--default-project--function)
    ;;;; 2. vc.el (vc-root-dir)
      ;; (setq consult-project-function (lambda (_) (vc-root-dir)))
    ;;;; 3. locate-dominating-file
      ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git")))
    ;;;; 4. projectile.el (projectile-project-root)
      (with-eval-after-load 'projectile
        (autoload 'projectile-project-root "projectile")
        (setq consult-project-function (lambda (_) (projectile-project-root))))
    ;;;; 5. No project support
      ;; (setq consult-project-function nil))
      (setq 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
  (setq 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)
  ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)

  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-embark-setup))
  (setq which-key-use-C-h-commands nil
        prefix-help-command #'embark-prefix-help-command)
  (with-eval-after-load 'evil
    (evil-define-key* '(normal visual insert) 'global (kbd "C-.") 'embark-act))

  ;; 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.

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
  (setq corfu-popupinfo-delay '(0.5 . 1.0))
  (setq corfu-popupinfo-hide nil))

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
  (setq 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)
  (with-eval-after-load 'eglot
    (advice-add #'eglot-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

dired

(use-package dired
  :ensure nil
  :commands (dired dired-jump)
  :init
  (setq dired-dwim-target t ; guess a default target directory
        dired-hide-details-hide-symlink-targets nil ; don't hide symbolic link targets
        dired-auto-revert-buffer #'dired-buffer-stale-p ; revert stale only
        dired-recursive-copies 'always ; always copy recursively
        dired-recursive-deletes 'top ; ask only for top-level
        dired-create-destination-dirs 'ask
        dired-listing-switches "-AGFhl --group-directories-first --time-style=long-iso"
        dired-clean-confirm-killing-deleted-buffers nil)
  :config
  (setq dired-mouse-drag-files t)
  (with-eval-after-load 'evil-collection
    (evil-collection-dired-setup)))

The arguments for dired-listing-switches are:

-A
Show hidden files (“dotfiles”), such as .bashrc, but omit the implied .= and .. targets. The latter two refer to the present and parent directory, respectively.
-G
Do not show the group name in the long listing. Only show the owner of the file.
-F
Differentiate regular from special files by appending a character to them. The * is for executables, the / is for directories, the | is for a named pipe, the = is for a socket, the @ and the > are for stuff I have never seen.
-h
Make file sizes easier to read.
-l
Produce a long, detailed listing. Dired requires this.
--group-directories-first
Just as it said.
--time-style=long-iso
Uses the international standard for time representation in the file listing. So we have something like 2023-12-30 06:38 to show the last modified time.

image-dired

(use-package image-dired
  :ensure nil
  :config
  (setq image-dired-thumb-size 150
        image-dired-thumbnail-storage 'standard
        image-dired-external-viewer "xdg-open"))

dired-x

(use-package dired-x
  :ensure nil
  :hook (dired-mode . dired-omit-mode)
  :config
  (setq dired-omit-files
        (concat dired-omit-files
                "\\|^\\.DS_Store\\'"
                "\\|^\\.project\\(?:ile\\)?\\'"
                "\\|^\\.\\(?:svn\\|git\\)\\'"
                "\\|^\\.ccls-cache\\'"
                "\\|\\(?:\\.js\\)?\\.meta\\'"
                "\\|\\.\\(?:elc\\|o\\|pyo\\|swp\\|class\\)\\'"))
  ;; Disable the prompt about whether I want to kill the Dired buffer for a
  ;; deleted directory. Of course I do!
  (setq dired-clean-confirm-killing-deleted-buffers nil)
  (let ((cmd "xdg-open"))
    (setq dired-guess-shell-alist-user
          `(("\\.\\(?:docx\\|pdf\\|djvu\\|eps\\)\\'" ,cmd)
            ("\\.\\(?:jpe?g\\|png\\|gif\\|xpm\\)\\'" ,cmd)
            ("\\.\\(?:xcf\\)\\'" ,cmd)
            ("\\.csv\\'" ,cmd)
            ("\\.tex\\'" ,cmd)
            ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\)\\(?:\\.part\\)?\\'" ,cmd)
            ("\\.\\(?:mp3\\|flac\\)\\'" ,cmd)
            ("\\.html?\\'" ,cmd)
            ("\\.md\\'" ,cmd)))))

The dired-guess-shell-alist-user expect us to have xdg-open (which is always available in my system anyway).

fd-dired   external

Needs an external package fd, or fd-find

(use-package fd-dired
  :if (executable-find "fd")
  :ensure t
  :defer t
  :init
  (global-set-key [remap find-dired] #'fd-dired))

dired-git-info   external

(use-package dired-git-info
  :ensure t
  :defer t
  ;; :hook
  ;; (dired-after-readin . dired-git-info-auto-enable)
  :config
  ;; (setq +dired--git-info-p (bound-and-true-p dired-git-info-mode))
  ;; (when +dired--git-info-p
  ;;   (dired-git-info-mode -1))
  (setq dgi-auto-hide-details-p nil))

dired-aux

(use-package dired-aux
  :ensure nil
  :config
  (setq dired-create-destination-dirs 'ask
        dired-vc-rename-file t
        dired-isearch-filenames 'dwim
        dired-create-destination-dirs-on-trailing-dirsep t))

dired-rsync   external

(use-package dired-rsync
  :ensure t
  :defer t)

diredfl   external

(use-package diredfl
  :ensure t
  :defer t
  :hook (dired-mode . diredfl-global-mode))

all-the-icons-dired   external

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

wdired   builtin

(use-package wdired
  :ensure nil
  :commands (wdired-change-to-wdired-mode)
  :config
  (with-eval-after-load 'evil-collection
    (evil-collection-wdired-setup)))

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))

(use-package emacs
  :ensure nil
  :init
  (load-theme 'modus-vivendi-tinted t))

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

(use-package emacs
  :ensure nil
  :custom
  (read-buffer-completion-ignore-case t)
  (tab-always-indent nil)
  (visible-bell nil)
  (use-short-answers t)
  (use-dialog-box nil)
  (window-resize-pixelwise nil)
  (frame-resize-pixelwise t)
  (ring-bell-function #'ignore)
  (scroll-preserve-screen-position t)
  (scroll-conservatively 101)
  (fast-but-imprecise-scrolling t)
  (truncate-partial-width-windows nil)
  (tab-width 4)
  (fill-column 80)
  (enable-recursive-minibuffers t)
  (use-file-dialog nil)
  (create-lockfiles nil)
  (delete-by-moving-to-trash t)
  (inhibit-startup-screen t)
  :config
  (when (bound-and-true-p tooltip-mode)
(tooltip-mode -1))
  (setq completion-ignore-case t
        read-file-name-completion-ignore-case t
        read-buffer-completion-ignore-case t
        load-prefer-newer t
        auto-window-vscroll nil
        inhibit-compacting-font-caches t
        redisplay-skip-fontification-on-input t)
  (set-default 'indicate-empty-lines nil)
  (setq-default truncate-lines t)
  (setq-default x-stretch-cursor nil))

(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 saveplace
  :init
  (save-place-mode 1)
  :custom
  (save-place-file (expand-file-name "places" user-emacs-directory)))

(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 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 recentf
  :bind ("C-c f" . recentf)
  :commands recentf-open-files
  :hook (dired-mode . doom--recentf-add-dired-directory-h)
  :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")))))
  :config
  (defun doom--recentf-file-truename-fn (file)
    (if (or (not (file-remote-p file))
            (equal "sudo" (file-remote-p file 'method)))
        (abbreviate-file-name (file-truename (tramp-file-name-localname file)))
      file))
  ;; Resolve symlinks, strip out the /sudo:X@ prefix in local tramp paths, and
  ;; abbreviate $HOME -> ~ in filepaths (more portable, more readable, & saves
  ;; space)
  (add-to-list 'recentf-filename-handlers #'doom--recentf-file-truename-fn)
  ;; Text properties inflate the size of recentf's files, and there is
  ;; no purpose in persisting them (Must be first in the list!)
  (add-to-list 'recentf-filename-handlers #'substring-no-properties)
  (defun doom--recentf-add-dired-directory-h ()
    "Add dired directories to recentf file list."
    (recentf-add-file default-directory))
  ;; The most sensible time to clean up your recent files list is when you quit
  ;; Emacs (unless this is a long-running daemon session).
  (setq recentf-auto-cleanup (if (daemonp) 300))
  (add-hook 'kill-emacs-hook #'recentf-cleanup))

(use-package emacs
  :ensure nil
  :hook ((prog-mode text-mode conf-mode) . display-line-numbers-mode)
  :config
  (setq hscroll-margin 2
        hscroll-step 1
        ;; Emacs spends too much effort recentering the screen if you scroll the
        ;; cursor more than N lines past window edges (where N is the settings of
        ;; `scroll-conservatively'). This is especially slow in larger files
        ;; during large-scale scrolling commands. If kept over 100, the window is
        ;; never automatically recentered. The default (0) triggers this too
        ;; aggressively, so I've set it to 10 to recenter if scrolling too far
        ;; off-screen.
        scroll-conservatively 10
        scroll-margin 0
        scroll-preserve-screen-position t
        ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll'
        ;; for tall lines.
        auto-window-vscroll nil
        ;; mouse
        mouse-wheel-scroll-amount '(2 ((shift) . hscroll))
        mouse-wheel-scroll-amount-horizontal 2)
  ;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any
  ;; feedback after typing is better UX than no feedback at all.
  (setq echo-keystrokes 0.02)
  ;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This
  ;; doesn't look too great with direnv, however...
  (setq resize-mini-windows 'grow-only)
  ;; Try to keep the cursor out of the read-only portions of the minibuffer.
  (setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
  ;; Show absolute line numbers for narrowed regions to make it easier to tell the
  ;; buffer is narrowed, and where you are, exactly.
  (setq-default display-line-numbers-widen t))

(use-package frame
  :ensure nil
  :hook (after-init . window-divider-mode)
  :config
  (blink-cursor-mode 1)
  (setq window-divider-default-places t
        window-divider-default-bottom-width 1
        window-divider-default-right-width 1))

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

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

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

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

(use-package hideshow
  :hook (prog-mode . hs-minor-mode))

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

(use-package paren
  :ensure nil
  :hook (prog-mode . show-paren-local-mode)
  :config
  (show-paren-mode 1)
  :custom
  (show-paren-style 'parenthesis)
  (show-paren-delay 0.4)
  (show-paren-context-when-offscreen 'overlay)
  (show-paren-when-point-inside-paren nil)
  (show-paren-when-point-in-periphery nil))

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

(use-package eshell
  :ensure nil
  :custom
  (eshell-history-size 10000)
  (eshell-hist-ignore-dups t)
  (eshell-buffer-maximum-lines 10000)
  (eshell-scroll-to-bottom-on-input t)
  (eshell-destroy-buffer-when-process-dies t)
  (eshell-prompt-regexp "^[^\)]*[\)] "))

(use-package sh-script
  :ensure nil
  :mode ("\\.bats\\'" . sh-mode)
  :mode ("\\.\\(?:zunit\\|env\\)\\'" . sh-mode)
  :mode ("/bspwmrc\\'" . sh-mode)
  :mode ("PKGBUILD" . sh-mode)
  :config
  (setq sh-indent-after-continuation 'always)
  (with-eval-after-load 'rainbow-delimiters
    (add-hook 'sh-mode-hook #'rainbow-delimiters-mode)
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '((sh-mode bash-ts-mode) .("bash-language-server" "start"))))
  (add-hook 'sh-mode-hook #'eglot-ensure)
  (add-hook 'bash-ts-mode-hook #'eglot-ensure)))

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

(transient-mark-mode 1)

(delete-selection-mode 1)

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

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

(use-package whitespace
  :ensure nil
  :config
  (setq whitespace-line-column nil
        whitespace-style
        '(face indentation tabs tab-mark spaces space-mark newline
               trailing lines-tail)
        whitespace-display-mappings
        '((tab-mark ?\t [?› ?\t])
          (newline-mark ?\n [?¬ ?\n])
          (space-mark ?\  [?·] [?.]))))

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

(use-package eglot
  :hook ((rust-mode nix-mode python-mode
                                      toml-mode markdown-mode
                                      js-mode js-ts-mode typescript-ts-mode) . eglot-ensure)
  :init
  (setopt eglot-sync-connect 1
                eglot-autoshutdown t)
  :config
  (with-eval-after-load 'cape
      (advice-add 'eglot-completion-at-point :around #'cape-wrap-super))
  (with-eval-after-load 'evil-collection
    (evil-collection-eglot-setup))
  (add-to-list 'eglot-server-programs
               `(rust-mode . ("rust-analyzer" :initializationOptions
                              ( :procMacro (:enable t)
                                :cargo ( :buildScripts (:enable t)
                                         :features "all"))))
               '(toml-ts-mode . ("taplo" "lsp" "stdio")))
  (add-to-list 'eglot-server-programs
                         '(web-mode . ("vscode-html-language-server" "--stdio")))
  (add-to-list 'eglot-server-programs
                         '(css-mode . ("vscode-css-language-server" "--stdio"))))

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

(use-package dired
  :ensure nil
  :commands (dired dired-jump)
  :init
  (setq dired-dwim-target t ; guess a default target directory
        dired-hide-details-hide-symlink-targets nil ; don't hide symbolic link targets
        dired-auto-revert-buffer #'dired-buffer-stale-p ; revert stale only
        dired-recursive-copies 'always ; always copy recursively
        dired-recursive-deletes 'top ; ask only for top-level
        dired-create-destination-dirs 'ask
        dired-listing-switches "-AGFhl --group-directories-first --time-style=long-iso"
        dired-clean-confirm-killing-deleted-buffers nil)
  :config
  (setq dired-mouse-drag-files t)
  (with-eval-after-load 'evil-collection
    (evil-collection-dired-setup)))

(use-package image-dired
  :ensure nil
  :config
  (setq image-dired-thumb-size 150
        image-dired-thumbnail-storage 'standard
        image-dired-external-viewer "xdg-open"))

(use-package dired-x
  :ensure nil
  :hook (dired-mode . dired-omit-mode)
  :config
  (setq dired-omit-files
        (concat dired-omit-files
                "\\|^\\.DS_Store\\'"
                "\\|^\\.project\\(?:ile\\)?\\'"
                "\\|^\\.\\(?:svn\\|git\\)\\'"
                "\\|^\\.ccls-cache\\'"
                "\\|\\(?:\\.js\\)?\\.meta\\'"
                "\\|\\.\\(?:elc\\|o\\|pyo\\|swp\\|class\\)\\'"))
  ;; Disable the prompt about whether I want to kill the Dired buffer for a
  ;; deleted directory. Of course I do!
  (setq dired-clean-confirm-killing-deleted-buffers nil)
  (let ((cmd "xdg-open"))
    (setq dired-guess-shell-alist-user
          `(("\\.\\(?:docx\\|pdf\\|djvu\\|eps\\)\\'" ,cmd)
            ("\\.\\(?:jpe?g\\|png\\|gif\\|xpm\\)\\'" ,cmd)
            ("\\.\\(?:xcf\\)\\'" ,cmd)
            ("\\.csv\\'" ,cmd)
            ("\\.tex\\'" ,cmd)
            ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\)\\(?:\\.part\\)?\\'" ,cmd)
            ("\\.\\(?:mp3\\|flac\\)\\'" ,cmd)
            ("\\.html?\\'" ,cmd)
            ("\\.md\\'" ,cmd)))))

(use-package dired-aux
  :ensure nil
  :config
  (setq dired-create-destination-dirs 'ask
        dired-vc-rename-file t
        dired-isearch-filenames 'dwim
        dired-create-destination-dirs-on-trailing-dirsep t))

Date: 2025-09-06 Sat 00:00

Author: Kristian Alexander P

Created: 2025-09-28 Sun 15:39