alexforsale's literate Emacs configuration
Table of Contents
This repository is my personal Emacs configuration. In case you don't know what Emacs is, I won't be the one explaining. To me Emacs is crucial and should exists in all of my machines, including the proprietary ones. In fact, this repository is the first one that I clone into my new machines (with my dotfiles being the second one).
The Goal
I used to blindly copying and pasting every snippets of Emacs configuration I found over the internet. The result usually is me having to mix and match them into one configuration, which is fine until it broke, and I start from scratch again and the cycle repeats.
Keep it simple
If the default configuration for a certain package is enough, don't add more. a certain package is enough, don't add more. So this configuration is constantly changing, sometimes something gets added, only to be removed after a while when I don't use it anymore (or when I don't know how to use it). At the moment there's lot of package installed, but hopefully I will remove the one that I'm not using.
Reproducible
This configuration is for me, not for anyone else
It's good if anyone who stumble into this repository find it useful, but the priority is to make it useful for me now. I'm trying to stop adding configuration that someday would be useful.
Editor first, IDE second
I'm not a programmer, I use Emacs mainly for editing files, tasks management, note-taking. My main focus is org-mode, since this mode is what I use most.
Documented
- List every external packages needed for this configuration.
- Explain why a package is configured.
- If possible, also compare with other package with similar functionality.
Mnemonic keybindings
Try setting a sensible keybindings, especially for external packages. Don't mess with the default keybindings if possible.
Use org-mode
With a literate configuration I don't have to separate each configurations into a separate modules. But having all the configuration in one file is also made debugging quite hard, so I'll still separate them into separate files.
Try to minimize the use of external packages
More dependencies means more bloats, avoid it when possible. Also, if a built-in package offers the same functionality, try it first.
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 atuomatically 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))
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 ;; Add `melpa` to `package-archives`. (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) ;; gnu-devel (add-to-list 'package-archives '("gnu-devel" . "https://elpa.gnu.org/devel/")) (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/")))
init.el
straight.el
(customize-set-variable 'straight-use-package-by-default t) (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" (or (bound-and-true-p straight-base-dir) user-emacs-directory))) (bootstrap-version 7)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) (straight-register-package '(org :depth 1)) (straight-use-package 'org) (straight-use-package 'use-package)
I use straight-register-package
for org-mode
since full cloning the repository would take too much time.
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")) (customize-set-variable '+config/org-directory (expand-file-name "org" (getenv "HOME")))))
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 andevil
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
, andprojectile
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 alsoox-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
withvertico
andmarginalia
already do the same in Emacs. So it's not important to me, but still nice to have. - 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 :demand 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 defaultevil-search
didn't search through collapsed headings inorg-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 toSPC 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 toC-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 :init (evil-collection-init) :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 :unless (display-graphic-p) :init (evil-terminal-cursor-changer-activate) :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 :demand t :commands evilem-create evilem-default-keybindings :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 :demand t :commands (global-evil-surround-mode evil-surround-edit evil-Surround-edit evil-surround-region) :hook (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>
orgS<textobject>
. Or in normal-state withys<textobject>
oryS<textobject>
. I'm still using theShift I
in visual mode, and theys
in normal mode conflicts with=which-key-mode
. - Change surrounding
You can change a surrounding with
cs<old-textobject><new-textobject>
.
- Add surrounding
- Usage
- evil-embrace external
This package provides evil integration of embrace.el. Since
evil-surround
provides a similar set of features asembrace.el
, this package aims at adding the goodies ofembrace.el
toevil-surround
and makingevil-surround
even better.(use-package evil-embrace :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.
(use-package evil-escape :commands evil-escape :after evil :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-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape))
- evil-exchange external
(use-package evil-exchange :init (evil-exchange-install))
Easy text exchange operator for Evil. This is the port of vim-exchange by Tom McDonald.
- evil-quick-diff external
This is a replacement for the linediff.vim plugin. It's not a faithful port, as it uses ediff for diffing, but the spirit is the same. The default operator key for diffing is
god
. WhilegoD
is used for canceling.(use-package evil-quick-diff :straight (:host github :repo "rgrinberg/evil-quick-diff") :after evil :commands (evil-quick-diff evil-quick-diff-cancel) :init ;; change default key bindings (if you want) HERE ;; (setq evil-quick-diff-key (kbd "zx")) (evil-quick-diff-install))
- 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 :commands (evilnc-comment-operator evilnc-inner-comment evilnc-outer-commenter) :bind ([remap comment-line] . evilnc-comment-or-uncomment-lines) :config (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-or-uncomment-lines)) (evil-define-key* nil my/evilnc-map "o" '("copy and paste commented" . evilnc-copy-and-comment-lines)))
- 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) toevil-snipe-s
andevil-snipe-S
, respectively. In operator mode, snipe is bound toz
/Z
andx
/X
(exclusive).(use-package evil-snipe :commands evil-snipe-local-mode evil-snipe-override-local-mode :after evil :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-textobj-anyblock external
Evil-textobj-anyblock is port of the vim plugin vim-textobj-anyblock. It gives a text object that will select the closest of
()
,{}
,[]
,<>
,''
,""
,` `
, or“”
by default. This can be convenient for operating on the closest block without having to type its symbol.In addition to the features provided by vim-textobj-anyblock, anyblock will seek forward in the case that there is not a surrounding match. Also, repeatedly using a text object in visual mode has an expand-region-like functionality by expanding the visual selection to the next block. This is not a primary feature of anyblock but may be nice in some simple cases and is given by evil for free.
(use-package evil-textobj-anyblock :straight (:host github :repo "willghatch/evil-textobj-anyblock") :after evil :defer t :config (setq evil-textobj-anyblock-blocks '(("(" . ")") ("{" . "}") ("\\[" . "\\]") ("<" . ">"))))
- evil-traces external
evil-traces
is a port of traces.vim. It enables visual previews for certainevil-ex
commands.(use-package evil-traces :after evil :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 :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
andgUix
.(use-package exato :after evil :commands evil-outer-xml-attr evil-inner-xml-attr)
- evil-vimish-fold external
Integration of vimish-fold with evil
Adds standard vim keybindings of. Fold now can be created withzf
andzd
to create and delete folds (viavimish-fold
) respectively<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
andzk
.This provides a near-complete vim folding experience in evil for Emacs.
(use-package evil-vimish-fold :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-define-key '(normal visual) 'global (kbd "zF") (cons "vimish-fold" my/vimish-fold-map)) ;; (evil-define-key nil my/vimish-fold-map ;; "j" '("next fold" . vimish-fold-next-fold) ;; "k" '("prev fold" . vimish-fold-previous-fold) ;; "d" '("delete" . evil-vimish-fold/delete) ;; "E" '("delete all" . evil-vimish-fold/delete-all)) )
- evil-mc external
Multiple cursors implementation for
evil-mode
. I'm using the default prefixg .
. Considering to disable this in favor for evil-multiedit.(use-package evil-mc :commands (evil-mc-make-cursor-here evil-mc-make-all-cursors evil-mc-undo-all-cursors evil-mc-pause-cursors evil-mc-resume-cursors evil-mc-make-and-goto-first-cursor evil-mc-make-and-goto-last-cursor evil-mc-make-cursor-in-visual-selection-beg evil-mc-make-cursor-in-visual-selection-end evil-mc-make-cursor-move-next-line evil-mc-make-cursor-move-prev-line evil-mc-make-cursor-at-pos evil-mc-has-cursors-p evil-mc-make-and-goto-next-cursor evil-mc-skip-and-goto-next-cursor evil-mc-make-and-goto-prev-cursor evil-mc-skip-and-goto-prev-cursor evil-mc-make-and-goto-next-match evil-mc-skip-and-goto-next-match evil-mc-skip-and-goto-next-match evil-mc-make-and-goto-prev-match evil-mc-skip-and-goto-prev-match) :init (global-evil-mc-mode 1))
- evil-multiedit external
(use-package evil-multiedit :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 :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 :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.
(evil-set-leader '(normal visual) (kbd "SPC")) (evil-set-leader '(insert emacs) (kbd "C-c SPC"))
- 1st level commands
These commands is used often so it will need a faster keybindings (
<leader> <key>
).(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))
- Chat
(setq my/c-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>c") (cons "Chat" my/c-map)) (setq my/gptel-map (make-sparse-keymap)) (evil-define-key* nil my/c-map "g" (cons "Gptel" my/gptel-map)) (evil-define-key nil my/gptel-map "a" '("Add/remove region" . gptel-add)) (evil-define-key nil my/gptel-map "g" '("gptel" . gptel)) (evil-define-key nil my/gptel-map "G" '("gptel-menu" . gptel-menu)) (evil-define-key nil my/gptel-map "s" '("send" . gtpel-send))
- Buffers
All keybindings will go with
<leader>b
.(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))
- Filetree
This is for treemacs
<leader>e
, would be good for dired also.(setq my/tree-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>e") (cons "Tree" my/tree-map))
- Files
Perhaps the most used keybindings,
<leader>f
.(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))) "w" '("find file other window" . find-file-other-window))
- Git
All git-related commands goes here,
<leader>g
.(setq my/git-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>g") (cons "Git" my/git-map))
- Help
This key
<leader>h
just simulate the standard EmacsC-h
.(evil-define-key* '(normal visual) 'global (kbd "<leader>h") (cons "Help" help-map))
- Insert
Commands for quick insertion
<leader>i
.(setq my/insert-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>i") (cons "Insert" my/insert-map)) (evil-define-key* nil my/insert-map "c" '("insert character" . insert-char))
- minor-mode
- org-mode
(setq my/org-mode-map (make-sparse-keymap)) (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) "C" '("goto last capture" . org-capture-goto-last-stored) "r" '("last stored refile" . org-refile-goto-last-stored))
- rustic
(setq my/rustic-map (make-sparse-keymap))
- python
(setq my/python-map (make-sparse-keymap)) (with-eval-after-load 'python (evil-define-key* '(normal visual) python-mode-map (kbd "<leader>m") (cons "python-mode" my/python-map)))
- haskell
(setq my/haskell-map (make-sparse-keymap)) (with-eval-after-load 'haskell-mode (evil-define-key* '(normal visual) haskell-mode-map (kbd "<leader>m") (cons "haskell-mode" my/haskell-map)))
- org-mode
- Open/org
Misc quick commands, also for org-mode
<leader>o
.(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 "Open" my/open-map)) (evil-define-key* nil my/open-map (kbd "o") (cons "Org files" my/open-org)) (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)) (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)))))
- Search
Maybe the third most used keys,
<leader>s
.(setq my/search-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>s") (cons "Search" my/search-map))
- Terminal
Sets to
<leader>v
since I mostly only use vterm.(setq my/vterm-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>v") (cons "Vterm" my/vterm-map))
- Window
The second most used keybindings, I will try to use the default vim keys.
(setq my/window-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>w") (cons "Window" 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))
- Quit
Also used for reloading configuration (like Doomemacs)
<leader>q
.(setq my/quit-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>q") (cons "Quit" 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
andz
keys.(setq my/z-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>z") (cons "Z" my/z-map))
- Non-leader keys
These keybindings is mostly package-specifics.
UI
I suppose here is the most external packages used.
- Doom themes and modelines
- doom-themes external
A megapack of themes for GNU Emacs. I'm not the kind of guy who enjoys ricing, I look every once in a while for a nice colorscheme and then I just stick it everywhere else. Been a solarized fans most of the time, now I'm trying nord.
(use-package doom-themes :demand t :config ;; Global settings (defaults) (setq doom-themes-enable-bold t ; if nil, bold is universally disabled doom-themes-enable-italic t) ; if nil, italics is universally disabled (load-theme 'doom-nord t) ;; Enable flashing mode-line on errors (doom-themes-visual-bell-config) ;; Enable custom neotree theme (all-the-icons must be installed!) ;; (doom-themes-neotree-config) ;; or for treemacs users (setq doom-themes-treemacs-theme "doom-atom") ; use "doom-colors" for less minimal icon theme (doom-themes-treemacs-config) ;; Corrects (and improves) org-mode's native fontification. (doom-themes-org-config))
- doom-modeline external
A fancy and fast mode-line inspired by minimalism design. I'm using their themes, why not the modeline as well? I used mostly the default settings apart from project detection and indent info for showing the current indentation style (useful when editing yaml files).
(use-package doom-modeline :init (doom-modeline-mode 1) :config (with-eval-after-load 'projectile (setq doom-modeline-project-detection 'projectile)) (setq doom-modeline-indent-info t))
- doom-themes external
- ace-window external
(use-package posframe) (use-package ace-window :init (when (display-graphic-p) (ace-window-posframe-mode t)) :config (setq aw-keys '(?q ?w ?e ?r ?t ?a ?s ?d ?f ?z ?x ?c ?v) aw-background nil aw-scope 'frame aw-frame-size 2) (custom-set-faces '(aw-leading-char-face ((t (:inherit ace-jump-face-foreground :height 4.0))))) (evil-define-key* nil my/window-map "S" '("swap window" . ace-swap-window)) :bind ([remap other-window] . ace-window))
Setting
aw-scope
toframe
so it doesn't select other frames. - all-the-icons external
A utility package to collect various Icon Fonts and propertize them within Emacs.
(use-package all-the-icons :if (display-graphic-p))
This needs a manual intervention from the user to install the fonts (by running the
all-the-icons-install-fonts
command). - page-break-lines
(use-package page-break-lines)
- dashboard external
An extensible emacs dashboard.
My sole reason for using dashboard is to avoid seeing an empty scratch buffer each time I opened a new Emacs frame.
(use-package dashboard :demand t :config (setq initial-buffer-choice (lambda () (get-buffer-create dashboard-buffer-name)) dashboard-startup-banner 'logo dashboard-center-content t dashboard-vertically-center-content t dashboard-navigation-cycle t dashboard-set-heading-icons t dashboard-set-navigator t dashboard-set-file-icons t dashboard-icon-type 'all-the-icons dashboard-heading-icons '((bookmarks . "nf-oct-bookmark") (history . "nf-oct-history") (agenda . "nf-oct-calendar") (projects . "nf-oct-rocket") (registers . "nf-oct-database")) dashboard-items '((recents . 3) (bookmarks . 2) (projects . 5) (agenda . 8) (registers . 2))) (with-eval-after-load 'nerd-icons (setq dashboard-icon-type 'nerd-icons)) (with-eval-after-load 'projectile (setq dashboard-projects-backend 'projectile)) (with-eval-after-load 'perspective (setq dashboard-projects-switch-function 'projectile-persp-switch-project)) (dashboard-setup-startup-hook))
- 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 :demand 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 t) (which-key-use-C-h-commands t) (which-key-show-remaining-keys t) (which-key-show-prefix 'bottom) (which-key-show-operator-state-maps t) :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))
- 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)
the
:height
value is 1/10 pt, so 100 height means 10 pt. - rainbow-mode, rainbow-identifiers, and rainbow-delimiters external
Making Emacs more colourful.
(use-package rainbow-mode :hook (prog-mode . rainbow-mode) :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)) (use-package rainbow-identifiers :hook (prog-mode . rainbow-identifiers-mode)) (use-package rainbow-delimiters :demand t :hook (prog-mode . rainbow-delimiters-mode) :config (setq rainbow-delimiters-max-face-count 4))
- helpful external
Since Emacs is a self-documenting editor. It's certainly nice to have a more contextual help buffers.
Figure 2: Helpful will also show the source code when available
Figure 3: more prettier docstring
Figure 4: helpful will also display any keybindings related
(use-package helpful :bind ("C-h f" . helpful-function) ([remap describe-symbol] . helpful-symbol) ([remap describe-variable] . helpful-variable) ([remap describe-command] . helpful-command) ([remap describe-key] . helpful-key) :custom (helpful-max-buffers 2) :config (with-eval-after-load 'apropos (dolist (fun-bt '(apropos-function apropos-macro apropos-command)) (button-type-put fun-bt 'action (lambda (button) (helpful-callable (button-get button 'apropos-symbol))))) (dolist (var-bt '(apropos-variable apropos-user-option)) (button-type-put var-bt 'action (lambda (button) (helpful-variable (button-get button 'apropos-symbol)))))) (keymap-global-set "C-h F" '("helpful Function" . helpful-function)) (define-key prog-mode-map (kbd "C-c C-d") '("help at point" . helpful-at-point)))
- hl-line builtin
Enable line highlighting in all buffers.
(use-package hl-line :config (global-hl-line-mode 1))
- highlight-quoted external
(use-package highlight-quoted :hook ((lisp-mode lisp-interaction-mode) . highlight-quoted-mode))
- hs-mode builtin
(use-package hideshow :hook (prog-mode . hs-minor-mode))
- paren builtin
This built-in package is necessary when dealing with Emacs lisp.
(use-package paren :config (show-paren-mode 1) :custom (show-paren-style 'mixed) (show-paren-delay 0.1) (show-paren-highlight-openparen t) (show-paren-when-point-inside-paren t) (show-paren-when-point-in-periphery t))
- mouse builtin
(use-package mouse :straight nil :config (setq mouse-yank-at-point t))
- Base UI builtin
(use-package emacs :straight 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 :straight 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 :straight nil :config (setq split-width-threshold 160 split-height-threshold nil))
- comint builtin
(use-package comint :straight nil :config (setq comint-prompt-read-only t comint-buffer-maximum-size 2048) (evil-define-key* '(normal visual) comint-mode-map "q" '(lambda () (interactive) (kill-buffer nil))))
- winner builtin
(use-package winner :straight 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*")))
- solaire-mode external
If only certain buffers could be so grossly incandescent.
(use-package solaire-mode :init (solaire-global-mode +1))
- beacon-mode
(use-package beacon :init (beacon-mode 1))
- hl-todo
Highlight TODO keywords.
(use-package hl-todo :init (add-hook 'prog-mode-hook (lambda () (hl-todo-mode 1))) :config (setq hl-todo-keyword-faces '(("TODO" . "#FF0000") ("FIXME" . "#FF0000") ("DEBUG" . "#A020F0") ("GOTCHA" . "#FF4500") ("STUB" . "#1E90FF"))) (with-eval-after-load 'magit (add-hook 'magit-log-wash-summary-hook #'hl-todo-search-and-highlight t) (add-hook 'magit-revision-wash-message-hook #'hl-todo-search-and-highlight t)))
Builtins
- base builtin
Usually configuration that defined at the C source code.
(use-package emacs :straight 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))
- tooltip builtin
(use-package tooltip :straight nil :config (when (bound-and-true-p tooltip-mode) (tooltip-mode -1)))
- compile builtin
(use-package compile :straight nil :hook (compilation-filter . ansi-color-compilation-filter) :config (setq compilation-always-kill t ; kill compilation process before starting another compilation-ask-about-save nil ; save all buffers on `compile' compilation-scroll-output 'first-error) ;; Automatically truncate compilation buffers so they don't accumulate too ;; much data and bog down the rest of Emacs. (autoload 'comint-truncate-buffer "comint" nil t) (add-hook 'compilation-filter-hook #'comint-truncate-buffer))
- files builtin
Usually configurations related to file manipulations.
(use-package files :straight 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) :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) :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))
- prog-mode builtin
(use-package prog-mode :straight nil :hook ((prog-mode . prettify-symbols-mode) (prog-mode . visual-line-mode) ;; (prog-mode . (lambda () (electric-pair-mode 1))) ) ;; :config ;; (setq prettify-symbols-alist ;; '(("|>" . "▷ ") ;; ("<|" . "◁ ") ;; ("->>" . "↠ ") ;; ("->" . "→ ") ;; ("<-" . "← ") ;; ("=>" . "⇒ ") ;; ("<=" . "≤ ") ;; (">=" . "≥ "))) )
Basically, in
prog-mode
, some combination of characters will look different. - bookmark builtin
(use-package bookmark :custom (bookmark-save-flag 1) (bookmark-default-file (expand-file-name ".bookmark" user-emacs-directory)))
The configuration for bookmark.
- tramp builtin
(use-package tramp :straight nil :config (setq tramp-remote-path (append '("~/.guix-profile/bin" "~/.guix-profile/sbin" "~/.config/guix/current/bin" "~/.config/guix/current/sbin" "/run/current-system/profile/bin" "/run/current-system/profile/sbin") tramp-remote-path)) (connection-local-set-profile-variables 'guix-system '((tramp-remote-path . (tramp-own-remote-path)))) :custom (tramp-backup-directory-alist backup-directory-alist) (tramp-auto-save-directory (expand-file-name ".tramp-autosave/" user-emacs-directory)))
I'm still figuring out how to do a proper TRAMP connection into a guix-system machine.
- epg-config builtin
(use-package epg-config :straight nil :custom (epg-pinentry-mode 'loopback) (epg-gpg-program "gpg2"))
This is for Gnupg.
- tabify builtin
(use-package tabify :straight nil :custom (tabify-regex "^\t* [ \t]+"))
- simple builtin
(use-package simple :straight nil :custom (blink-matching-paren nil) (save-interprogram-paste-before-kill t) (shift-select-mode nil) (kill-do-not-save-duplicates t) (shift-select-mode nil) (set-mark-command-repeat-pop t) (indent-tabs-mode nil) (column-number-mode t) (idle-update-delay 1.0) :config (with-eval-after-load 'evil (evil-set-initial-state #'message-mode 'insert)))
The important part is the Evil integration, which is should be handled already by evil-collection.
- vc-hooks builtin
By default visiting / opening symlinked file will ask if we want to visit the actual file.
(use-package vc-hooks :straight nil :custom (vc-follow-symlinks t))
- dabbrev builtin
This is part of corfu configuration.
;; Use Dabbrev with Corfu! (use-package dabbrev ;; Swap M-/ and C-M-/ :bind (("M-/" . dabbrev-completion) ("C-M-/" . dabbrev-expand)) :config (add-to-list 'dabbrev-ignored-buffer-regexps '("\\` " "\\(TAGS\\|tags\\|ETAGS\\|etags\\|GTAGS\\|GRTAGS\\|GPATH\\)\\(<[0-9]+>\\)?")) ;; Since 29.1, use `dabbrev-ignored-buffer-regexps' on older. (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode) (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode) (add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode))
- repeat-mode builtin
(use-package repeat :straight nil :init (repeat-mode +1))
- so-long builtin
(use-package so-long :straight nil :init (global-so-long-mode) :config ;; Emacs 29 introduced faster long-line detection, so they can afford a much ;; larger `so-long-threshold' and its default `so-long-predicate'. (if (fboundp 'buffer-line-statistics) (unless (featurep 'native-compile) (setq so-long-threshold 5000)) ;; reduce false positives w/ larger threshold (setq so-long-threshold 400)) (defun doom-buffer-has-long-lines-p () (unless (bound-and-true-p visual-line-mode) (let ((so-long-skip-leading-comments ;; HACK Fix #2183: `so-long-detected-long-line-p' calls ;; `comment-forward' which tries to use comment syntax, which ;; throws an error if comment state isn't initialized, leading ;; to a wrong-type-argument: stringp error. ;; DEPRECATED Fixed in Emacs 28. (bound-and-true-p comment-use-syntax))) (so-long-detected-long-line-p)))) (setq so-long-predicate #'doom-buffer-has-long-lines-p))
- ediff builtin
(use-package ediff :straight nil :config (setq ediff-diff-options "-w" ; turn off whitespace checking ediff-split-window-function #'split-window-horizontally ediff-window-setup-function #'ediff-setup-windows-plain))
Completion
- Vertico frameworks external
I call this framework since usually these packages are what people use together, though they work fine separately.
- vertico
- Basic configuration
(use-package vertico :straight (vertico :files (:defaults "extensions/*.el") :includes (vertico-buffer vertico-directory vertico-flat vertico-grid vertico-indexed vertico-mouse vertico-multiform vertico-quick vertico-reverse vertico-repeat vertico-suspend vertico-unobstrusive )) :hook (rfn-eshadow-update-overlay . vertico-directory-tidy) (minibuffer-setup . vertico-repeat-save) :init (vertico-mode) ;; Different scroll margin (setq vertico-scroll-margin 0) ;; Show more candidates (setq vertico-count 17) ;; Grow and shrink the Vertico minibuffer (setq vertico-resize nil) ;; Optionally enable cycling for `vertico-next' and `vertico-previous'. (setq vertico-cycle t) :config (advice-add #'tmm-add-prompt :after #'minibuffer-hide-completions) (keymap-set vertico-map "?" #'minibuffer-completion-help) (keymap-set vertico-map "M-RET" #'minibuffer-force-complete-and-exit) (keymap-set vertico-map "TAB" #'vertico-insert) (keymap-set vertico-map "C-j" #'vertico-next) (keymap-set vertico-map "C-k" #'vertico-previous) (keymap-set vertico-map "DEL" #'vertico-directory-delete-char)) ;; A few more useful configurations... (use-package emacs :custom ;; Support opening new minibuffers from inside existing minibuffers. (enable-recursive-minibuffers t) ;; Emacs 28 and newer: 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) :init ;; Add prompt indicator to `completing-read-multiple'. ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma. (defun crm-indicator (args) (cons (format "[CRM%s] %s" (replace-regexp-in-string "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" crm-separator) (car args)) (cdr args))) (advice-add #'completing-read-multiple :filter-args #'crm-indicator) ;; Do not allow the cursor in the minibuffer prompt (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))
- Extensions
- vertico-directory
(use-package vertico-directory :after vertico :straight 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 :straight nil :bind (:map vertico-map ("M-q" . vertico-quick-insert) ("C-q" . vertico-quick-exit)))
- vertico-multiform
(use-package vertico-multiform :straight 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)))) )
- vertico-directory
- Basic configuration
- marginalia
;; Enable rich annotations using the Marginalia package (use-package marginalia :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle)) :init (marginalia-mode))
- corfu
- Basic Configuration
(use-package corfu :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
(use-package corfu-history :straight nil :hook ((corfu-mode . corfu-history-mode)) :config (with-eval-after-load 'savehist (add-to-list 'savehist-additional-variables 'corfu-history)))
- corfu-popupinfo
(use-package corfu-popupinfo :straight nil :hook ((corfu-mode . corfu-popupinfo-mode)) :config (setq corfu-popupinfo-delay '(0.5 . 1.0)))
- Basic Configuration
- consult
- Basic Configuration
(use-package consult :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) (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/buffer-map "b" '("buffers" . consult-buffer) "B" '("project buffers" .consult-project-buffer) ) (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) "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) (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 :bind (:map vertico-map ("C-x C-d" . consult-dir) ("C-x C-j" . consult-dir-jump-file)))
- Basic Configuration
- orderless
- Basic Configuration
(use-package orderless :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
andflex
are built-ins completion-styles.
- Basic Configuration
- embark
(use-package embark :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 (setq which-key-use-C-h-commands nil prefix-help-command #'embark-prefix-help-command) (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
(use-package embark-consult :hook (embark-collect-mode . consult-preview-at-point-mode))
consult integration for embark.
- cape
(use-package cape :defer 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 ("C-c p h" . cape-history) ("C-c p f" . cape-file) ("C-c p k" . cape-keyword) ("C-c p s" . cape-elisp-symbol) ("C-c p e" . cape-elisp-block) ("C-c p a" . cape-abbrev) ("C-c p l" . cape-line) ("C-c p w" . cape-dict) ("C-c p :" . cape-emoji) ("C-c p \\" . cape-tex) ("C-c p _" . cape-tex) ("C-c p ^" . cape-tex) ("C-c p &" . cape-sgml) ("C-c p r" . cape-rfc1345)) :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) ;; (add-hook 'completion-at-point-functions #'cape-history) ;; (add-hook 'completion-at-point-functions #'cape-keyword) ;;(add-hook 'completion-at-point-functions #'cape-tex) ;;(add-hook 'completion-at-point-functions #'cape-sgml) ;;(add-hook 'completion-at-point-functions #'cape-rfc1345) ;;(add-hook 'completion-at-point-functions #'cape-abbrev) ;; (add-hook 'completion-at-point-functions #'cape-dict) ;;(add-hook 'completion-at-point-functions #'cape-elisp-symbol) ;; (add-hook 'completion-at-point-functions #'cape-line) :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)) (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible) (advice-add #'lsp-completion-at-point :around #'cape-wrap-nonexclusive) (advice-add #'comint-completion-at-point :around #'cape-wrap-nonexclusive) (advice-add #'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)))
- jinx
Enchanted Spell Checker. Here I use hunspell as a backend dictionary
(use-package jinx :if (executable-find "enchant-2") :hook (emacs-startup . global-jinx-mode) :config (global-set-key [remap ispell-word] 'jinx-correct) (evil-global-set-key 'normal (kbd "C-M-$") 'jinx-language))
- Snippets
This is provided by yasnippet.
(use-package yasnippet :init (yas-global-mode) :config (setq yas-snippets-dir (list (expand-file-name "snippets" user-emacs-directory) (expand-file-name "straight/build/yasnippet-snippets/snippets" user-emacs-directory))) (evil-define-key* nil my/insert-map "s" '("insert snippet" . consult-yasnippet))) (use-package consult-yasnippet) (use-package yasnippet-snippets)
- vertico
Shells
- eshell builtin
A shell-like command interpreter implemented in Emacs Lisp.
(use-package eshell :straight 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 "^[^\)]*[\)] "))
- vterm external
(use-package vterm :demand t :commands vterm-mode :config (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 :config (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))
- bash-completion external
(use-package bash-completion :defer t :config (bash-completion-setup) :hook (shell-dynamic-complete-function bash-completion-dynamic-complete))
- emacs-corfu-terminal
(straight-use-package '(corfu-terminal :type git :repo "https://codeberg.org/akib/emacs-corfu-terminal.git")) (use-package corfu-terminal :unless (display-graphic-p) :init (corfu-terminal-mode +1))
Editing
- select builtin
(use-package select :straight 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 :straight nil :init (global-subword-mode 1))
- text-mode builtin
(use-package text-mode :straight nil :hook (text-mode . visual-line-mode) :config (setq-default sentence-end-double-space nil))
- whitespace builtin
(use-package whitespace :straight nil :config (setq whitespace-line-column nil whitespace-style '(face indentation tabs tab-mark spaces space-mark newline newline-mark trailing lines-tail) whitespace-display-mappings '((tab-mark ?\t [?› ?\t]) (newline-mark ?\n [?¬ ?\n]) (space-mark ?\ [?·] [?.]))))
- ws-butler external
(use-package ws-butler :hook ((prog-mode text-mode) . ws-butler-mode))
- better-jumper external
(use-package better-jumper :init (global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward) (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) (global-set-key [remap xref-pop-marker-stack] #'better-jumper-jump-backward) (global-set-key [remap xref-go-back] #'better-jumper-jump-backward) (global-set-key [remap xref-go-forward] #'better-jumper-jump-forward) (better-jumper-mode +1))
- smartparens external
I still have a mixed feeling for this package, but it still easier to configure than the builtin electric-pair-mode. Also, should this be in the Lisp section?
;; smartparens (use-package smartparens :demand t :commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string :init (smartparens-global-mode) :config ;; smartparens recognizes `slime-mrepl-mode', but not `sly-mrepl-mode', so... (add-to-list 'sp-lisp-modes 'sly-mrepl-mode) (require 'smartparens-config) (add-hook 'eval-expression-minibuffer-setup-hook #'smartparens-mode) ;; Overlays are too distracting and not terribly helpful. show-parens does ;; this for us already (and is faster), so... (setq sp-highlight-pair-overlay nil sp-highlight-wrap-overlay nil sp-highlight-wrap-tag-overlay nil) (show-smartparens-global-mode 1) (smartparens-global-mode 1) ;; Fix usage of ' in Lisp modes ;; THANKS: https://github.com/Fuco1/smartparens/issues/286#issuecomment-32324743 ;; (eval) is used as a hack to quiet Flycheck errors about (sp-with-modes) (eval '(sp-with-modes sp-lisp-modes ;; disable ', it's the quote character! (sp-local-pair "'" nil :actions nil) ;; also only use the pseudo-quote inside strings where it serve as ;; hyperlink. (sp-local-pair "`" "'" :when '(sp-in-string-p sp-in-comment-p)) (sp-local-pair "`" nil :skip-match (lambda (ms mb me) (cond ((equal ms "'") (or (sp--org-skip-markup ms mb me) (not (sp-point-in-string-or-comment)))) (t (not (sp-point-in-string-or-comment)))))))) (sp-with-modes '(html-mode sgml-mode nxml-mode web-mode) (sp-local-pair "<" ">")) (defun sp--markdown-skip-asterisk (ms mb me) (save-excursion (goto-char mb) (save-match-data (looking-at "^\\* ")))) (sp-with-modes 'markdown-mode (sp-local-pair "*" "*" :unless '(sp-point-after-word-p sp-point-at-bol-p) :skip-match 'sp--markdown-skip-asterisk) (sp-local-pair "**" "**") (sp-local-pair "_" "_" :unless '(sp-point-after-word-p))) ;;; org-mode (defun sp--org-skip-asterisk (ms mb me) (or (and (= (line-beginning-position) mb) (eq 32 (char-after (1+ mb)))) (and (= (1+ (line-beginning-position)) me) (eq 32 (char-after me))))) (defun sp--org-inside-LaTeX (id action context) (org-inside-LaTeX-fragment-p)) (sp-with-modes 'org-mode (sp-local-pair "*" "*" :unless '(sp-point-after-word-p sp--org-inside-LaTeX sp-point-at-bol-p) :skip-match 'sp--org-skip-asterisk) (sp-local-pair "/" "/" :unless '(sp-point-after-word-p sp--org-inside-LaTeX)) (sp-local-pair "~" "~" :unless '(sp-point-after-word-p sp--org-inside-LaTeX)) (sp-local-pair "=" "=" :unless '(sp-point-after-word-p sp--org-inside-LaTeX)) (sp-local-pair "\\[" "\\]")) ;; haskell (add-to-list 'sp-no-reindent-after-kill-modes 'haskell-mode) ;; You're likely writing lisp in the minibuffer, therefore, disable these ;; quote pairs, which lisps doesn't use for strings: (sp-local-pair '(minibuffer-mode minibuffer-inactive-mode) "'" nil :actions nil) (sp-local-pair '(minibuffer-mode minibuffer-inactive-mode) "`" nil :actions nil) (evil-define-key* '(normal visual) smartparens-mode-map (kbd "M-\(") '(sp-wrap-round) "<" 'sp-backward-barf-sexp (kbd "C-<") 'sp-backward-slurp-sexp ">" 'sp-forward-barf-sexp (kbd "C->") 'sp-forward-slurp-sexp) )
The keybinding I got from the example configuration messes up my evil-mode. I have them commented for now.
- apheleia external
(use-package apheleia :defer t :init (apheleia-global-mode +1))
- expand-region external
Emacs extension to increase selected region by semantic units.
(use-package expand-region :config (with-eval-after-load 'general (general-define-key :keymaps 'global "C-=" 'er/expand-region)))
- 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"))))
IDE stuffs
- eldoc builtin
(use-package eldoc :hook ((emacs-lisp-mode lisp-interaction-mode ielm-mode) . eldoc-mode))
- magit external
(use-package magit :demand t :config (evil-set-initial-state #'git-commit-mode 'insert) (evil-define-key* nil my/git-map "g" '("git status" . magit-status)) :custom (magit-revision-show-gravatars '("^Author: " . "^Commit: ")) (magit-diff-refine-hunk 'all) (magit-log-arguments '("-n100" "--graph" "--decorate")))
- projectile external
A project interaction library for Emacs, with little external dependency as possible.
(use-package projectile :demand t :commands (projectile-project-root projectile-project-name projectile-project-p projectile-locate-dominating-file projectile-relevant-known-projects) :bind (([remap evil-jump-to-tag] . projectile-find-tag) ([remap find-tag] . projectile-find-tag)) :hook (dired-before-readin . projectile-track-known-projects-find-file-hook) :custom (projectile-cache-file (expand-file-name ".projects" user-emacs-directory)) (projectile-auto-discover nil) (projectile-enable-caching (not noninteractive)) (projectile-globally-ignored-files '("DS_Store" "TAGS")) (projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")) (projectile-kill-buffers-filter 'kill-only-files) (projectile-known-projects-file (expand-file-name ".projectile_projects.eld" user-emacs-directory)) (projectile-ignored-projects '("~/")) (projectile-project-search-path `(,(expand-file-name "Projects" (getenv "HOME")))) (projectile-project-root-files-bottom-up (append '(".projectile" ".project" ".git") (when (executable-find "hg") '(".hg")) (when (executable-find "bzr") '(".bzr")))) (projectile-project-root-files-top-down-recurring '("Makefile")) (compilation-buffer-name-function #'projectile-compilation-buffer-name) (compilation-save-buffers-predicate #'projectile-current-project-buffer-p) (projectile-git-submodule-command nil) (projectile-indexing-method 'hybrid) :config (global-set-key [remap evil-jump-to-tag] #'projectile-find-tag) (global-set-key [remap find-tag] #'projectile-find-tag) (projectile-mode +1) (put 'projectile-git-submodule-command 'initial-value projectile-git-submodule-command) (evil-define-key* '(normal visual) 'global (kbd "<leader>SPC") '("find project file" . projectile-find-file)) (evil-define-key* '(normal visual) 'global (kbd "<leader>p") (cons "Project" projectile-command-map)))
- diff-hl external
(use-package diff-hl :hook (find-file . diff-hl-mode) :hook (vc-dir-mode . diff-hl-dir-mode) :hook (dired-mode . diff-hl-dired-mode) :hook (diff-hl-mode . diff-hl-flydiff-mode) :hook (diff-hl-mode . diff-hl-show-hunk-mouse-mode) :hook (magit-pre-refresh-hook . diff-hl-magit-pre-refresh) :hook (magit-post-refresh-hook . diff-hl-magit-post-refresh) :init (global-diff-hl-mode) :custom (vc-git-diff-switches '("--histogram") diff-hl-flydiff-delay 0.5 diff-hl-show-staged-changes nil) :config (when (featurep 'flycheck) (setq flycheck-indication-mode 'right-fringe)) (evil-define-key* '(normal visual) 'global "z*" '("show hunk" . diff-hl-show-hunk) "z[" '("prev hunk" . diff-hl-previous-hunk) "z]" '("next hunk" . diff-hl-next-hunk) "z{" '("go and show prev hunk" . diff-hl-show-hunk-previous) "z}" '("go and show next hunk" . diff-hl-show-hunk-next)))
- 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 :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)) (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 :after perspective :commands projectile-persp-switch-project)
- lsp external
- Basic configuration
- LSP
(use-package lsp-mode :after which-key :init (setq lsp-keymap-prefix "C-c C-l") (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))) (add-hook 'orderless-style-dispatchers #'my/orderless-dispatch-flex-first nil 'local) (when (featurep 'cape) (setq-local completion-at-point-functions (list (cape-capf-buster #'lsp-completion-at-point)))) :hook (lsp-mode . lsp-enable-which-key-integration) (lsp-mode . evil-normalize-keymaps) (lsp-completion-mode . my/lsp-mode-setup-completion) :config (with-eval-after-load 'treemacs (add-hook 'lsp-mode-hook #'lsp-treemacs-sync-mode)) (evil-define-key 'normal lsp-mode-map (kbd "<leader>l") (cons "LSP" lsp-command-map)) (evil-define-key 'normal lsp-mode-map "K" '("describe at point" . lsp-describe-thing-at-point)) (setq lsp-toml-command (if (file-exists-p (expand-file-name ".cargo/bin/taplo" "~")) (expand-file-name ".cargo/bin/taplo" "~") "taplo") lsp-rust-rls-server-command "rls" ;;lsp-eldoc-render-all t lsp-enable-snippet nil lsp-enable-indentation nil lsp-prefer-flymake nil lsp-keep-workspace-alive nil) (when (featurep '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))))) :custom (lsp-completion-provider :none)) ;; (evil-define-key* '(normal visual) lsp-mode-map (kbd "<leader>l") (cons "LSP" lsp-command-map)) ;; (evil-define-key* '(normal visual) lsp-mode-map "K" '("describe at point" . lsp-describe-thing-at-point)
- LSP
- lsp-ui
(use-package lsp-ui :hook (lsp-mode . lsp-ui-mode) :demand t :init (setq lsp-ui-sideline-enable t lsp-ui-sideline-update-mode 'line lsp-ui-sideline-show-code-actions t lsp-ui-sideline-show-hover t lsp-ui-doc-enable t lsp-ui-doc-include-signature t lsp-ui-doc-show-with-cursor t lsp-eldoc-enable-hover nil ; Disable eldoc displays in minibuffer lsp-ui-doc-position 'at-point lsp-ui-imenu-enable t lsp-ui-sideline-ignore-duplicate t lsp-ui-peek-enable t) :config (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions) (define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references) (add-to-list 'lsp-language-id-configuration '(jinja2-mode . "jinja2") t) (add-to-list 'lsp-language-id-configuration '("\\.js2$" . "jinja2") t) (define-key lsp-ui-mode-map (kbd "M-.") 'lsp-ui-peek-find-definitions) (define-key lsp-ui-mode-map (kbd "M-?") 'lsp-ui-peek-find-references))
- consult-lsp external
(use-package consult-lsp :after consult lsp :bind (:map lsp-mode-map ([remap xref-find-apropos] . consult-lsp-symbols)))
- Basic configuration
- gptel external
(use-package gptel :after password-store :demand t :config (setq gptel-backend (gptel-make-gemini "Gemini" :key (password-store-get "google.com/gemini_api_key") :stream t) gptel-model 'gemini-2.5-pro gptel-default-mode #'org-mode))
An LLM client for Emacs, I'm trying it with google's gemini. This package is already good on its own. But the other reason is I'm also trying out consult-web, which integrates search results from various sources.
- flycheck external
(use-package flycheck :init (global-flycheck-mode +1) :commands flycheck-list-errors flycheck-buffer :config (setq flycheck-emacs-lisp-load-path 'inherit) ;; Rerunning checks on every newline is a mote excessive. (delq 'new-line flycheck-check-syntax-automatically) ;; And don't recheck on idle as often (setq flycheck-idle-change-delay 1.0) ;; For the above functionality, check syntax in a buffer that you switched to ;; only briefly. This allows "refreshing" the syntax check state for several ;; buffers quickly after e.g. changing a config file. (setq flycheck-buffer-switch-check-intermediate-buffers t) ;; Display errors a little quicker (default is 0.9s) (setq flycheck-display-errors-delay 0.25) (evil-define-key* '(normal visual) 'global (kbd "<leader>!") (cons "Flycheck" flycheck-command-map)) (evil-define-key* '(normal visual) flycheck-error-list-mode-map "C-n" 'flycheck-error-list-next-error "C-p" 'flycheck-error-list-previous-error "j" 'flycheck-error-list-next-error "k" 'flycheck-error-list-previous-error "RET" 'flycheck-error-list-goto-error "[return]" 'flycheck-error-list-goto-error))
- flycheck-color-mode-line external
(use-package flycheck-color-mode-line :hook (flycheck-mode . flycheck-color-mode-line-mode))
- flycheck-pos-tip external
(use-package flycheck-pos-tip :init (flycheck-pos-tip-mode))
- flycheck-popup-tip external
(use-package flycheck-popup-tip :commands flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup :hook (flycheck-mode . flycheck-popup-tip-mode))
- flycheck-posframe external
(use-package flycheck-posframe :after flycheck :config (add-hook 'flycheck-mode-hook #'flycheck-posframe-mode) (setq flycheck-posframe-warning-prefix "⚠ " flycheck-posframe-info-prefix "ⓘ " flycheck-posframe-error-prefix "⮾ "))
- consult-flycheck external
(use-package consult-flycheck :after (consult flycheck) :config (evil-define-key* nil my/search-map "F" '("Flycheck" . consult-flycheck)))
- flycheck-color-mode-line external
Emacs org-mode
- org-mode core package builtin
(use-package org :demand t :commands org-tempo :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 . (lambda () (electric-indent-local-mode -1))) (org-mode . variable-pitch-mode)) :config (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-entities builtin
(use-package org-entities :straight nil :config (setq org-entities-user '(("flat" "\\flat" nil "" "" "266D" "♭") ("sharp" "\\sharp" nil "" "" "266F" "♯"))))
- org-faces builtin
(use-package org-faces :straight nil :custom (org-fontify-quote-and-verse-blocks t))
- org-archive builtin
(use-package org-archive :straight 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 :straight 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 :straight 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 :straight nil :after org org-contrib :custom (org-catch-invisible-edits 'smart))
- org-id builtin
(use-package org-id :straight 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 :straight 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 :straight 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")))
- org-attach builtin
(use-package org-attach :straight 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 :straight nil :after org :custom (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 (evil-define-key 'normal org-agenda-mode-map "q" 'org-agenda-quit) (with-eval-after-load 'evil (evil-set-initial-state #'org-agenda-mode 'normal)) (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 :straight 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))
- org-timer builtin
(use-package org-timer :straight nil :config (setq org-timer-format "Timer :: %s"))
- org-eldoc builtin
(use-package org-eldoc :straight 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 :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 :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 :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 :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-noter external onexport disabled archive
(use-package org-noter :defer t :preface ;; Allow the user to preempt this and set the document search path ;; If not set then use `org-directory' (defvar org-noter-notes-search-path nil) :config (unless org-noter-notes-search-path (setq org-noter-notes-search-path (list org-directory))) (setq org-noter-auto-save-last-location t org-noter-separate-notes-from-heading t))
- org-roam
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 :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: post\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) (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-protocol
- org-roam-ui
(use-package org-roam-ui :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 :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
(use-package make-mode :straight nil :config (add-hook 'makefile-mode-hook 'indent-tabs-mode))
- executable
(use-package executable :straight nil :hook (after-save . executable-make-buffer-file-executable-if-script-p))
- pinentry
(use-package pinentry :defer t :config (pinentry-start))
- pass
(use-package password-store :demand t :config (setq password-store-password-length 12) (setq auth-sources '(password-store "~/.authinfo.gpg" "~/.netrc"))) (use-package password-store-otp :defer t :after password-store) (use-package pass :defer t) (use-package auth-source-pass :straight nil :init (auth-source-pass-enable))
Elpaca complains about different of pass version installed. But it installed just fine.
- rg
Packaged as
ripgrep
in archlinux. The:if
keyword won't load this if the executable not found.(use-package rg :if (executable-find "rg") :defer t)
- ox-hugo
Blogging tools
(use-package ox-hugo :if (executable-find "hugo") :after ox)
- exec-path-from-shell
(use-package exec-path-from-shell :disabled :config (when (or (memq window-system '(mac ns x)) (and (daemonp) (memq window-system '(mac ns x)))) (exec-path-from-shell-initialize)))
- elfeed
An Emacs web feeds client.
(use-package elfeed :config (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 :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 :init (elfeed-goodies/setup))
- elfeed-org
- extensions
- Khalel
Interacting through Emacs with locally-stored calendars via the console application
khal
and syncing with remote CalDAV calendars usingvdirsyncer
.(use-package khalel :if (executable-find "khal") :after org :config (khalel-add-capture-template) (setq khalel-import-org-file (expand-file-name "calendar.org" org-directory)))
khalel-import-org-file
should be on the list oforg-agenda-files
.- Vdirsyncer
First you'll need to setup a new project in Google API Manager, this is needed for the vdirsyncer configuration, see their configuration manual for further information. From this you'll get the Client ID and Client Secret.
The configuration file should be placed in
~/.config/vdirsyncer/config
# An example configuration for vdirsyncer. # # Move it to ~/.vdirsyncer/config or ~/.config/vdirsyncer/config and edit it. # Run `vdirsyncer --help` for CLI usage. # # Optional parameters are commented out. # This file doesn't document all available parameters, see # http://vdirsyncer.pimutils.org/ for the rest of them. [general] # A folder where vdirsyncer can store some metadata about each pair. status_path = "~/.local/share/vdirsyncer/status/" # CARDDAV [pair alexarians_contacts] # A `[pair <name>]` block defines two storages `a` and `b` that should be # synchronized. The definition of these storages follows in `[storage <name>]` # blocks. This is similar to accounts in OfflineIMAP. a = "alexarians_contacts_local" b = "alexarians_contacts_remote" # Synchronize all collections that can be found. # You need to run `vdirsyncer discover` if new calendars/addressbooks are added # on the server. collections = ["from a", "from b"] # Synchronize the "display name" property into a local file (~/.contacts/displayname). metadata = ["displayname", "description"] # To resolve a conflict the following values are possible: # `null` - abort when collisions occur (default) # `"a wins"` - assume a's items to be more up-to-date # `"b wins"` - assume b's items to be more up-to-date #conflict_resolution = null [storage alexarians_contacts_local] # A storage references actual data on a remote server or on the local disk. # Similar to repositories in OfflineIMAP. type = "filesystem" path = "~/.contacts/" fileext = ".vcf" [storage alexarians_contacts_remote] type = "google_contacts" token_file = "~/.local/share/vdirsyncer/google_contacts_token" client_id = "<Client ID from Google developer console>" client_secret = "<Client secret from Google developer console>" #username = # The password can also be fetched from the system password storage, netrc or a # custom command. See http://vdirsyncer.pimutils.org/en/stable/keyring.html #password = # CALDAV [pair alexarians_calendar] a = "alexarians_calendar_local" b = "alexarians_calendar_remote" collections = ["from a", "from b"] # Calendars also have a color property metadata = ["displayname", "color"] [storage alexarians_calendar_local] type = "filesystem" path = "~/.calendars/" fileext = ".ics" [storage alexarians_calendar_remote] type = "google_calendar" token_file = "~/.local/share/vdirsyncer/google_calendar_token" client_id = "<Client ID from Google developer console>" client_secret = "<Client secret from Google developer console>" #url = "https://owncloud.example.com/remote.php/caldav/" #username = #password =
Vdirsyncer is packaged in archlinux's extra repository, and it comes with a user systemd unit file, after running
vdirsyncer discover
, enable the unit file withsystemctl --user enable vdirsyncer.timer
. - Khal
Next is the
khal
configuration. This should be placed in~/.config/khal/config
:[calendars] [[personal]] path = /home/alexforsale/.calendars/* type = discover color = auto [locale] timeformat = %H:%M dateformat = %Y-%m-%d longdateformat = %Y-%m-%d %a datetimeformat = %Y-%m-%d %H:%M longdatetimeformat = %Y-%m-%d %H:%M [default] default_calendar = *
- Vdirsyncer
- khardel
Emacs package integrating khard, a console cardav client.
(use-package khardel :if (executable-find "khard") :config (evil-define-key* nil my/insert-map "e" '("insert email" . khardel-insert-email)))
- khard configuration
This is taken from the example configuration. Should be placed in
~/.config/khard/khard.conf
# example configuration file for khard version > 0.14.0 # place it under ~/.config/khard/khard.conf # This file is parsed by the configobj library. The syntax is described at # https://configobj.readthedocs.io/en/latest/configobj.html#the-config-file-format [addressbooks] [[family]] path = ~/.contacts/family/ [[friends]] path = ~/.contacts/friends/ [[work]] path = ~/.work/**/client-*-contacts/ type = discover [general] debug = no default_action = list # These are either strings or comma seperated lists editor = vim, -i, NONE merge_editor = vimdiff [contact table] # display names by first or last name: first_name / last_name / formatted_name display = first_name # group by address book: yes / no group_by_addressbook = no # reverse table ordering: yes / no reverse = no # append nicknames to name column: yes / no show_nicknames = no # show uid table column: yes / no show_uids = yes # show kind table column: yes / no show_kinds = no # sort by first or last name: first_name / last_name / formatted_name sort = last_name # localize dates: yes / no localize_dates = yes # set a comma separated list of preferred phone number types in descending priority # or nothing for non-filtered alphabetical order preferred_phone_number_type = pref, cell, home # set a comma separated list of preferred email address types in descending priority # or nothing for non-filtered alphabetical order preferred_email_address_type = pref, work, home [vcard] # extend contacts with your own private objects # these objects are stored with a leading "X-" before the object name in the vcard files # every object label may only contain letters, digits and the - character # example: # private_objects = Jabber, Skype, Twitter # default: , (the empty list) private_objects = Jabber, Skype, Twitter # preferred vcard version: 3.0 / 4.0 preferred_version = 3.0 # Look into source vcf files to speed up search queries: yes / no search_in_source_files = no # skip unparsable vcard files: yes / no skip_unparsable = no
All the paths should reflect the configuration on
vdirsyncer
. See the online documentation for more information.
- khard configuration
Dired
- dired
(use-package dired :straight 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))
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 :straight 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 :straight 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 havexdg-open
(which is always available in my system anyway). - fd-dired external
Needs an external package
fd
, orfd-find
(use-package fd-dired :if (executable-find "fd") :defer t :init (global-set-key [remap find-dired] #'fd-dired))
- dired-git-info
(use-package dired-git-info :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 :straight 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
(use-package dired-rsync :defer t)
- diredfl
(use-package diredfl :defer t :hook (dired-mode . diredfl-global-mode))
- all-the-icons-dired
(use-package all-the-icons-dired :after all-the-icons :hook (dired-mode . all-the-icons-dired-mode))
- wdired
(use-package wdired :straight nil :commands (wdired-change-to-wdired-mode))
Treemacs
- treemacs core package external
(use-package treemacs :hook ((treemacs-mode . (lambda () (hs-minor-mode -1)))) :config (setq treemacs-hide-gitignored-files-mode t treemacs-no-png-images t treemacs-silent-refresh t treemacs-sorting 'mod-time-desc treemacs-python-executable (executable-find "python3") treemacs-collapse-dirs (if treemacs-python-executable 3 0) treemacs-deferred-git-apply-delay 0.5 treemacs-directory-name-transformer #'identity treemacs-display-in-side-window t treemacs-eldoc-display 'simple treemacs-file-event-delay 2000 treemacs-file-extension-regex treemacs-last-period-regex-value treemacs-file-follow-delay 0.2 treemacs-file-name-transformer #'identity treemacs-follow-after-init t treemacs-expand-after-init t treemacs-find-workspace-method 'find-for-file-or-pick-first treemacs-git-command-pipe "" treemacs-goto-tag-strategy 'refetch-index treemacs-header-scroll-indicators '(nil . "^^^^^^") treemacs-hide-dot-git-directory t treemacs-indentation 2 treemacs-indentation-string " " treemacs-is-never-other-window nil treemacs-max-git-entries 5000 treemacs-missing-project-action 'ask treemacs-move-forward-on-expand nil treemacs-no-delete-other-windows t treemacs-project-follow-cleanup nil treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory) treemacs-position 'left treemacs-read-string-input 'from-child-frame treemacs-recenter-distance 0.1 treemacs-recenter-after-file-follow nil treemacs-recenter-after-tag-follow nil treemacs-recenter-after-project-jump 'always treemacs-recenter-after-project-expand 'on-distance treemacs-litter-directories '("/node_modules" "/.venv" "/.cask") treemacs-project-follow-into-home nil treemacs-show-cursor nil treemacs-show-hidden-files t treemacs-silent-filewatch t treemacs-select-when-already-in-treemacs 'move-back treemacs-space-between-root-nodes t treemacs-tag-follow-cleanup t treemacs-tag-follow-delay 1.5 treemacs-text-scale nil treemacs-user-mode-line-format nil treemacs-user-header-line-format nil treemacs-wide-toggle-width 70 treemacs-width 35 treemacs-width-increment 1 treemacs-width-is-initially-locked t treemacs-workspace-switch-cleanup nil) (treemacs-peek-mode 1) (treemacs-filewatch-mode t) (treemacs-follow-mode t) (treemacs-fringe-indicator-mode 'always) (when treemacs-python-executable (treemacs-git-commit-diff-mode t)) (pcase (cons (not (null (executable-find "git"))) (not (null treemacs-python-executable))) (`(t . t) (treemacs-git-mode 'deferred)) (`(t . _) (treemacs-git-mode 'simple))) :bind (:map global-map ("M-0" . treemacs-select-window) ("C-x t 1" . treemacs-delete-other-windows) ("C-x t t" . treemacs) ("C-x t d" . treemacs-select-directory) ("C-x t B" . treemacs-bookmark) ("C-x t C-t" . treemacs-find-file) ("C-x t M-t" . treemacs-find-tag)) :config (evil-define-key* nil my/tree-map "e" '("treemacs" . treemacs)) (evil-define-key nil treemacs-mode-map "[mouse-1]" 'treemacs-single-click-expand-action))
- treemacs-projectile external
(use-package treemacs-projectile :after treemacs projectile)
- treemacs-icons-dired external
(use-package treemacs-icons-dired :after treemacs :hook (dired-mode . treemacs-icons-dired-enable-once))
- treemacs-magit external
(use-package treemacs-magit :after treemacs magit)
- treemacs-evil external
(use-package treemacs-evil :after treemacs evil)
- lsp-treemacs external
- treemacs-perspective external
(use-package treemacs-perspective :after treemacs perspective :config (treemacs-set-scope-type 'Perspectives))
- treemacs-tab-bar external
(use-package treemacs-tab-bar :after treemacs tab-bar)
Programming languages
- Python
- Builtin python mode builtin
(use-package python :straight nil :mode ("[./]flake8\\'" . conf-mode) :mode ("/Pipfile\\'" . conf-mode) :hook ((python-mode . lsp-deferred) (python-mode . lsp-ui-mode) (python-mode . lsp-ui-doc-mode) (python-mode . +python-use-correct-flycheck-executables-h) ;; (python-mode . guess-style-guess-tab-mode) (python-mode-local-vars . (lambda () (lsp-deferred)))) :config (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)))) (setq my/python-send-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))))
- inferior-python-mode builtin
(use-package python :straight nil ;;:hook (inferior-python-mode . hide-mode-line-mode) :config (setq my/python-map (make-sparse-keymap)) (evil-define-key* '(normal visual) python-mode-map (kbd "<leader>m") (cons "Python" my/python-map)) (evil-define-key* nil my/python-map "r" '("run python" . run-python)))
- lsp-pyright external
(use-package lsp-pyright :after lsp-mode :hook (python-mode . (lambda () (require 'lsp-pyright) (lsp-deferred) (lsp-ui-mode) (lsp-ui-doc-mode))) :config (setq lsp-pyright-python-executable-cmd (if (executable-find "python3.12") (executable-find "python3") "python")))
- python-docstring external disabled archive
(use-package python-docstring :diminish :hook (python-mode . python-docstring-mode) :config (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "dq" 'python-docstring-fill)) )
- flycheck-pycheckers external
(use-package flycheck-pycheckers :after flycheck :config (with-eval-after-load 'flycheck (add-hook 'flycheck-mode-hook #'flycheck-pycheckers-setup)))
- Builtin python mode builtin
- Rust
- rustic external
(use-package rustic :mode ("\\.rs$" . rustic-mode) :bind (:map rustic-mode-map ("M-j" . lsp-ui-menu) ("M-?" . lsp-find-references) ("C-c C-c l" . flycheck-list-errors) ("C-c C-c q" . lsp-workspace-restart) ("C-c C-c Q" . lsp-workspace-shutdown) ("C-c C-c s" . lsp-rust-analyzer-status)) :init (with-eval-after-load 'rustic-flycheck (remove-hook 'rustic-mode-hook #'flycheck-mode) (remove-hook 'rustic-mode-hook #'flymake-mode-off) (remove-hook 'flycheck-mode-hook #'rustic-flycheck-setup)) (with-eval-after-load 'org-src (defalias 'org-babel-execute:rust #'org-babel-execute:rustic) (add-to-list 'org-src-lang-modes '("rust" . rustic))) :config (add-hook 'rustic-cargo-run-mode-hook (lambda () (comint-mode) (read-only-mode 0))) (add-hook 'rustic-mode-hook #'rainbow-delimiters-mode) (setq rustic-indent-method-chain t) (add-hook 'rustic-mode-local-vars-hook #'rustic-setup-lsp 'append) (add-hook 'rustic-mode-local-vars-hook #'flycheck-mode) (with-eval-after-load 'tree-sitter (add-hook 'rustic-mode-local-vars-hook #'tree-sitter! 'append)) (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) (evil-define-key* '(normal visual) rustic-mode-map (kbd "<leader>m") (cons "rustic-mode" my/rustic-map)) (evil-define-key* nil my/rustic-map "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)))
- flycheck-rust external
(use-package flycheck-rust :after flycheck :config (with-eval-after-load 'rustic (add-hook 'flycheck-mode-hook #'flycheck-rust-setup)))
- rustic external
- Perl
- Haskell
(use-package haskell-mode :defer t :init (setq flymake-allowed-file-name-masks nil) :custom (haskell-process-load-or-reload-prompt t) (haskell-process-auto-import-loaded-modules t) (haskell-process-log t) (haskell-tags-on-save t) :config (with-eval-after-load 'lsp-mode (add-hook 'haskell-mode-hook #'lsp-deferred))) (use-package lsp-haskell :defer t :hook ((haskell-mode haskell-literate-mode) . lsp-deferred) :after lsp) (evil-define-key* nil my/haskell-map "c" '("compile" . haskell-compile) "d" '("describe" . haskell-describe) "h" '("search hoogle" . haskell-hoogle) "l" '("load and run" . haskell-load-and-run) "r" '("run haskell" . run-haskell))
- java external
Lsp-java requires jdt-language-server, this package should automatically installs it using the
lsp-install-server
commands(use-package lsp-java :after lsp-mode :hook (java-mode . lsp))