alexforsale's literate Emacs configuration 2.0
Table of Contents
- The new configuration
- early-init.el
- init.el
- Modules
- Evil external
- configuration
- evil-collection external
- evil-terminal-cursor-changer external
- evil-easymotion external
- evil-surround external
- evil-embrace external
- evil-escape external
- evil-exchange external
- evil-nerd-commenter external
- evil-snipe external
- evil-traces external
- evil-visualstar external
- exato external
- evil-vimish-fold external
- evil-multiedit external
- evil-org-mode external
- evil-goggles external
- Keybindings
- Builtins
- UI
- Base UI builtin
- frame builtin
- window builtin
- comint builtin
- winner builtin
- Fonts
- hs-mode builtin
- mouse builtin
- paren builtin
- repeat-mode builtin
- which-key external
- marginalia external
- avy external
- rainbow-mode external
- rainbow-identifiers external
- rainbow-identifiers external
- doom-themes external
- nerd-icons external
- doom-modeline external
- Shells and Terminals
- Editing
- IDE stuffs
- Org-mode
- org-mode core package builtin
- org-contrib external
- org-entities builtin
- org-faces builtin
- org-archive builtin
- org-capture builtin
- org-refile builtin
- org-fold builtin
- org-id builtin
- org-num builtin
- org-crypt builtin
- org-attach builtin
- org-agenda builtin
- org-clock builtin
- org-timer builtin
- org-eldoc builtin
- org-superstar external
- org-fancy-priority external
- org-modern external
- org-download external
- org-roam external
- org-roam-ui external
- org-protocol-capture-html
- Misc Tools
- Programming languages
- Completion
- Dired
- Evil external
- minimal emacs
The new configuration
My old configuration got bloated after accumulating external packages, and sometimes it's just a test package that I forgot to remove, or some packages I've added that I don't really use.
So this time I want to really focus on the builtins, and only the builtins that I really use.
early-init.el
GC Threshold
A common way of speeding up Emacs startup. The variable gc-cons-threshold
is the number of bytes consing between garbage collection.
Garbage collection can happen automatically once this many bytes have been allocated since the last garbage collection.
Here I set this to a variable most-positive-fixnum
which value is set automatically.
most-positive-fixnum
2305843009213693951
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6)
The gc-cons-percentage
default to 0.1, is the portion of the heap used for allocation.
I set the value differently when Emacs is starting up, by setting this variable into emacs-startup-hook
that runs after Emacs loaded the init files.
(add-hook 'emacs-startup-hook (lambda () (setq gc-cons-threshold (* 16 1024 1024) ; 16mb gc-cons-percentage 0.1)))
Language
Set UTF-8 as the default coding system
(set-language-environment "UTF-8")
Set initial frame
Here I disable the menu-bar, tool-bar and vertical scroll bar.
(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) (add-to-list 'initial-frame-alist '(menu-bar-lines . 0)) (add-to-list 'initial-frame-alist '(tool-bar-lines . 0)) (add-to-list 'default-frame-alist '(tool-bar-lines . 0)) (add-to-list 'initial-frame-alist '(vertical-scroll-bars)) (add-to-list 'default-frame-alist '(vertical-scroll-bars)) (add-to-list 'initial-frame-alist '(fullscreen . maximized)) (add-to-list 'default-frame-alist '(fullscreen . maximized)) (add-to-list 'initial-frame-alist '(font . "Iosevka Nerd Font Mono-8")) (add-to-list 'default-frame-alist '(font . "Iosevka Nerd Font Mono-8")) (use-package emacs :ensure nil :init (load-theme 'modus-vivendi-tinted t))
Define the location of custom-file
The custom-file
is used to save customization settings for future use.
(customize-set-variable 'custom-file (expand-file-name "custom.el" user-emacs-directory)) (when (file-exists-p custom-file) (load custom-file))
Setup package
Now much more simpler since use-package is built into Emacs. It's actually not used since I'm using straight.el
.
(with-eval-after-load 'package (setq package-enable-at-startup nil) ;; Add `melpa` to `package-archives`. (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) ;; gnu-devel (add-to-list 'package-archives '("gnu-devel" . "https://elpa.gnu.org/devel/") t) (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t) (package-initialize))
init.el
Tangle this org-file
This file is already added in my git repositories for initial Emacs loading. But I also include it here to make sure there's no changes made.
(require 'ob-tangle) (when (file-exists-p (expand-file-name "index.org" user-emacs-directory)) (org-babel-tangle-file (expand-file-name "index.org" user-emacs-directory)))
Variables and helper functions
Identity
(setq user-mail-address "alexforsale@yahoo.com" user-full-name "Kristian Alexander P")
merge-list-to-list function
This function is just a wrapper to easily merge lists. I don't think I need it anymore.
;;; From https://emacs.stackexchange.com/questions/38008/adding-many-items-to-a-list/68048#68048 (defun merge-list-to-list (dst src) "Merges content of the 2nd list with the 1st one" (set dst (append (eval dst) src)))
define +config/org-directory variable
(cond ((file-directory-p (expand-file-name "Sync/org" (getenv "HOME"))) (customize-set-variable '+config/org-directory (expand-file-name "Sync/org" (getenv "HOME")))) ((string-match-p "microsoft" (shell-command-to-string "uname -a")) (if (file-directory-p "/mnt/c/Users/SyncthingServiceAcct/Default Folder/org") (customize-set-variable '+config/org-directory "/mnt/c/Users/SyncthingServiceAcct/Default Folder/org"))))
I put my org files inside syncthing default directory (~/Sync
if on Linux) so it will automatically synced to all my devices. But on windows WSL the location will vary depending on how you install syncthing so I won't even bother set it up.
Load each modular configuration file.
evil
All evil-mode
specific configuration and evil
related packages goes here.
(require 'config-evil (expand-file-name "modules/config-evil.el" user-emacs-directory) t)
Keybinding
Most keybindings here depends on evil-mode, so it will be loaded after.
(require 'config-keybinding (expand-file-name "modules/config-keybinding.el" user-emacs-directory) t)
UI
(require 'config-ui (expand-file-name "modules/config-ui.el" user-emacs-directory) t)
Configuration for Emacs's looks and feels.
builtins
(require 'config-builtins (expand-file-name "modules/config-builtins.el" user-emacs-directory) t)
The more I understand the meat inside Emacs the less external packages I'll use (hopefully).
completion
(require 'config-completion (expand-file-name "modules/config-completion.el" user-emacs-directory) t)
For now this is for vertico and friends.
shells
(require 'config-shells (expand-file-name "modules/config-shells.el" user-emacs-directory) t)
Mostly vterm
configurations. Eshell
looks promising, but still hard to configure.
editing
(require 'config-editing (expand-file-name "modules/config-editing.el" user-emacs-directory) t)
Tweaks to make my editing experience nicer.
IDE
(require 'config-ide (expand-file-name "modules/config-ide.el" user-emacs-directory) t)
All of LSP
, git
, flycheck
, and projectile
configuration.
org
(require 'config-org (expand-file-name "modules/config-org.el" user-emacs-directory) t)
Most of my changes are in here, the more configurable org-mode
, the more unusable to me.
tools
(require 'config-tools (expand-file-name "modules/config-tools.el" user-emacs-directory) t)
Small packages that helps Emacs interacts with its surrounding. I have direnv
, exec-path-from-shell
for, you guess it, shells. There's also ox-hugo
for blogging with hugo.
dired
(require 'config-dired (expand-file-name "modules/config-dired.el" user-emacs-directory) t)
I don't use dired
that much, but when I do, I at least know my way around.
treemacs
(require 'config-treemacs (expand-file-name "modules/config-treemacs.el" user-emacs-directory) t)
It's like NERDtree in vim, but in Emacs, I kinda understand why vim need this functionality. C-x C-f
with vertico
and marginalia
already do the same in Emacs. So it's not important to me, but still nice to have.
(require 'config-mail (expand-file-name "modules/config-mail.el" user-emacs-directory) t)
Email in Emacs with the help of notmuch, offlineimap, afew, and davmail for my Exchange mails.
programming modes
(require 'config-progmodes (expand-file-name "modules/config-progmodes.el" user-emacs-directory) t)
Doing stuffs in linux will eventually makes you edit files from various programming languages. This here is just to make me read them comfortly.
local overrides
(require 'config-overrides (expand-file-name "modules/config-overrides.el" user-emacs-directory) t)
Modules
Evil external
configuration
Mostly copied from doom or the official documentation.
(use-package evil :ensure t :hook (after-change-major-mode . (lambda () (setq evil-shift-width tab-width))) :preface (customize-set-variable 'evil-want-keybinding nil) (customize-set-variable 'evil-want-integration t) (customize-set-variable 'evil-undo-system 'undo-redo) (customize-set-variable 'evil-want-C-i-jump nil) ;; fix TAB in terminal org-mode (customize-set-variable 'evil-want-C-u-scroll t) ;; move universal arg to <leader> u (customize-set-variable 'evil-want-C-u-delete t) ;; delete back to indentation in insert state (customize-set-variable 'evil-want-C-g-bindings t) :custom (evil-undo-system #'undo-redo) (evil-search-module 'evil-search) (evil-ex-search-vim-style-regexp t) (evil-ex-interactive-search-highlight 'selected-window) (evil-kbd-macro-suppress-motion-error t) (evil-visual-update-x-selection-p nil) :config (unless noninteractive (setq save-silently t)) (setq evil-normal-state-cursor 'box evil-insert-state-cursor 'bar evil-visual-state-cursor 'hollow) (evil-select-search-module 'evil-search-module 'isearch) (evil-mode 1) (with-eval-after-load 'eldoc (eldoc-add-command 'evil-normal-state 'evil-insert 'evil-change 'evil-delete 'evil-replace)) ;; from doom (defun +evil/window-split-and-follow () "Split current window horizontally, then focus new window. If `evil-split-window-below' is non-nil, the new window isn't focused." (interactive) (let ((evil-split-window-below (not evil-split-window-below))) (call-interactively #'evil-window-split))) (defun +evil/window-vsplit-and-follow () "Split current window vertically, then focus new window. If `evil-vsplit-window-right' is non-nil, the new window isn't focused." (interactive) (let ((evil-vsplit-window-right (not evil-vsplit-window-right))) (call-interactively #'evil-window-vsplit))))
Notes:
evil-search-modules
- I switched this to
isearch
since the 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 :ensure t :init (evil-collection-init) :config (with-eval-after-load 'dired (evil-collection-dired-setup)) (with-eval-after-load 'calendar (evil-collection-calendar-setup)) :custom (evil-collection-setup-minibuffer t) (evil-collection-calendar-want-org-bindings t))
evil-terminal-cursor-changer external
Change cursor shape and color by evil state in terminal.
(use-package evil-terminal-cursor-changer :hook (tty-setup . evil-terminal-cursor-changer-activate) :ensure t :config (setq evil-motion-state-cursor 'box) ; █ (setq evil-visual-state-cursor 'box) ; █ (setq evil-normal-state-cursor 'box) ; █ (setq evil-insert-state-cursor 'bar) ; ⎸ (setq evil-emacs-state-cursor 'hbar) ; _ )
evil-easymotion external
(use-package evil-easymotion :after evil :ensure t :config (evilem-make-motion evilem-motion-search-next #'evil-search-next :bind ((evil-ex-search-highlight-all nil))) (evilem-make-motion evilem-motion-search-previous #'evil-search-previous :bind ((evil-ex-search-highlight-all nil))) (evilem-make-motion evilem-motion-search-word-forward #'evil-search-word-forward :bind ((evil-ex-search-highlight-all nil))) (evilem-make-motion evilem-motion-search-word-backward #'evil-search-word-backward :bind ((evil-ex-search-highlight-all nil))) ;; Rebind scope of w/W/e/E/ge/gE evil-easymotion motions to the visible ;; buffer, rather than just the current line. (put 'visible 'bounds-of-thing-at-point (lambda () (cons (window-start) (window-end)))) (evilem-make-motion evilem-motion-forward-word-begin #'evil-forward-word-begin :scope 'visible) (evilem-make-motion evilem-motion-forward-WORD-begin #'evil-forward-WORD-begin :scope 'visible) (evilem-make-motion evilem-motion-forward-word-end #'evil-forward-word-end :scope 'visible) (evilem-make-motion evilem-motion-forward-WORD-end #'evil-forward-WORD-end :scope 'visible) (evilem-make-motion evilem-motion-backward-word-begin #'evil-backward-word-begin :scope 'visible) (evilem-make-motion evilem-motion-backward-WORD-begin #'evil-backward-WORD-begin :scope 'visible) (evilem-make-motion evilem-motion-backward-word-end #'evil-backward-word-end :scope 'visible) (evilem-make-motion evilem-motion-backward-WORD-end #'evil-backward-WORD-end :scope 'visible) (evilem-default-keybindings "z g"))
This is a useful package for complementing Evil's motion (using zg
).
evilem keybinding | evilem key | Evil | evil key |
---|---|---|---|
evilem-motion-forward-word-begin |
z g w |
evil-forward-word-begin |
w |
evilem-motion-forward-WORD-begin |
z g W |
evil-forward-WORD-begin |
W |
evilem-motion-forward-word-end |
z g e |
evil-forward-word-end |
e |
evilem-motion-forward-WORD-end |
z g E |
evil-forward-WORD-end |
E |
evilem-motion-backward-word-begin |
z g b |
evil-forward-word-begin |
b |
evilem-motion-backward-WORD-begin |
z g B |
evil-forward-WORD-begin |
B |
evilem-motion-backward-word-end |
z g g e |
evil-forward-word-end |
g e |
evilem-motion-backward-WORD-end |
z g g E |
evil-forward-WORD-end |
g E |
These keybindings is also available on visual-mode
. I prefer the simpler evil-snipe, so for now I'm not using this in my configuration
evil-surround external
This package emulates surround.vim by Tim Pope. The functionality is wrapped into a minor mode.
(use-package evil-surround :after evil :ensure t :commands (global-evil-surround-mode evil-surround-edit evil-Surround-edit evil-surround-region) :hook (html-mode . (lambda () (push '(?~ . ("<" . ">")) evil-surround-pairs-alist))) (org-mode . (lambda () (push '(?~ . ("~" . "~")) evil-surround-pairs-alist))) (org-mode . (lambda () (push '(?~ . ("<" . ">")) evil-surround-pairs-alist))) (org-mode . (lambda () (push '(?$ . ("\\(" . "\\)")) evil-surround-pairs-alist))) :init (global-evil-surround-mode 1))
- Usage
- Add surrounding
You can surround in visual-state with
S<textobject>
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 withwhich-key-mode
. - Change surrounding
You can change a surrounding with
cs<old-textobject><new-textobject>
.
- Add surrounding
evil-embrace external
This package provides evil integration of embrace.el. Since evil-surround
provides a similar set of features as embrace.el
, this package aims at adding the goodies of embrace.el
to evil-surround
and making evil-surround
even better.
(use-package evil-embrace :ensure t :commands embrace-add-pair embrace-add-pair-regexp :hook (LaTeX-mode . embrace-LaTeX-mode-hook) :hook (LaTeX-mode . +evil-embrace-latex-mode-hook-h) :hook (org-mode . embrace-org-mode-hook) :hook (ruby-mode . embrace-ruby-mode-hook) :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook) :init (with-eval-after-load 'evil-surround (evil-embrace-enable-evil-surround-integration)) :config ;; from doom (defun +evil--embrace-latex () "LaTeX command support for embrace." (cons (format "\\%s{" (read-string "\\")) "}")) (defun +evil-embrace-latex-mode-hook-h () (dolist (pair '((?\' . ("`" . "\'")) (?\" . ("``" . "\'\'")))) (delete (car pair) evil-embrace-evil-surround-keys) ;; Avoid `embrace-add-pair' because it would overwrite the default ;; rules, which we want for other modes (push (cons (car pair) (make-embrace-pair-struct :key (car pair) :left (cadr pair) :right (cddr pair) :left-regexp (regexp-quote (cadr pair)) :right-regexp (regexp-quote (cddr pair)))) embrace--pairs-list)) (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex)) (defun +evil-embrace-angle-bracket-modes-hook-h () (let ((var (make-local-variable 'evil-embrace-evil-surround-keys))) (set var (delq ?< evil-embrace-evil-surround-keys)) (set var (delq ?> evil-embrace-evil-surround-keys))) (embrace-add-pair-regexp ?< "\\_<[a-z0-9-_]+<" ">" #'+evil--embrace-angle-brackets) (embrace-add-pair ?> "<" ">")))
evil-escape external
Customizable key sequence to escape from insert state and everything else in Emacs. In short, when in insert-mode
, you can easily switch to normal-mode
just by quickly type jk
.
(use-package evil-escape :after evil :ensure t :hook (evil-after-load . evil-escape-mode) :init (setq evil-escape-excluded-states '(normal visual multiedit emacs motion) evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode dired-mode) evil-escape-key-sequence "jk" evil-escape-delay 0.15) (evil-escape-mode 1) (evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape))
evil-exchange external
(use-package evil-exchange :ensure t :init (evil-exchange-install))
Easy text exchange operator for Evil. This is the port of vim-exchange by Tom McDonald.
evil-nerd-commenter external
A Nerd Commenter emulation, help you comment code efficiently. For example, you can press 99,ci
to comment out 99 lines (only works if you set the (evilnc-default-hotkeys t)
).
(use-package evil-nerd-commenter :after evil :ensure t :commands (evilnc-comment-operator evilnc-inner-comment evilnc-outer-commenter) :bind ([remap comment-line] . evilnc-comment-or-uncomment-lines) :init (setq my/evilnc-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "gc") (cons "Comment" my/evilnc-map)) (evil-define-key* nil my/evilnc-map "c" '("comment/uncomment" . evilnc-comment-operator)))
evil-snipe external
Evil-snipe emulates vim-seek and/or vim-sneak in evil-mode.
It provides 2-character motions for quickly (and more accurately) jumping around text, compared to evil's built-in f/F/t/T motions, incrementally highlighting candidate targets as you type.
By default, snipe only binds s
(forward) / S
(backward) to evil-snipe-s
and evil-snipe-S
, respectively. In operator mode, snipe is bound to z
/ Z
and x
/ X
(exclusive).
(use-package evil-snipe :commands evil-snipe-local-mode evil-snipe-override-local-mode :after evil :ensure t :hook (evil-after-load . (lambda () (evil-snipe-override-mode +1))) :hook (evil-after-load . (lambda () (evil-snipe-mode +1))) :hook (magit-mode . turn-off-evil-snipe-override-mode) :init (setq evil-snipe-smart-case t evil-snipe-scope 'line evil-snipe-repeat-scope 'visible evil-snipe-char-fold t) (evil-snipe-mode +1) (evil-snipe-override-mode +1))
evil-traces external
evil-traces
is a port of traces.vim. It enables visual previews for certain evil-ex
commands.
(use-package evil-traces :after evil :ensure t :config (evil-traces-mode))
evil-visualstar external
This is a port of one of the many visual-star plugins for Vim to work with evil-mode.
(use-package evil-visualstar :ensure t :commands (evil-visualstar/begin-search evil-visualstar/begin-search-forward evil-visualstar/begin-search-backward) :init (evil-define-key* 'visual 'global "*" #'evil-visualstar/begin-search-forward "#" #'evil-visualstar/begin-search-backward))
exato external
This package provides the x
text object to manipulate html/xml tag attributes. You can customize the binding.
Try using dax
, vix
and gUix
.
(use-package exato :after evil :ensure t)
evil-vimish-fold external
Integration of vimish-fold with evil
Adds standard vim keybindings of . Fold now can be created with zf
and zd
to create and delete folds (via vimish-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
and zk
.
This provides a near-complete vim folding experience in evil for Emacs.
(use-package evil-vimish-fold :ensure t :demand t :bind (([remap evil-toggle-fold] . vimish-fold-toggle) ([remap evil-close-fold] . vimish-fold-refold) ([remap evil-open-fold] . vimish-fold-unfold) ([remap evil-close-folds] . vimish-fold-refold-all) ([remap evil-open-folds] . vimish-fold-unfold-all)) :commands (evil-vimish-fold/next-fold evil-vimish-fold/previous-fold evil-vimish-fold/delete evil-vimish-fold/delete-all evil-vimish-fold/create evil-vimish-fold/create-line) :init (setq evil-vimish-fold-mode-lighter " ⮒") (setq evil-vimish-fold-target-modes '(prog-mode conf-mode text-mode)) (setq my/vimish-fold-map (make-sparse-keymap)) :config (global-evil-vimish-fold-mode +1))
evil-multiedit external
(use-package evil-multiedit :ensure t :config (defun make-evil-multiedit-case-sensitive (fn &rest args) (let ((case-fold-search (not iedit-case-sensitive))) (apply fn args))) (advice-add #'evil-multiedit-match-and-next :around #'make-evil-multiedit-case-sensitive) (evil-multiedit-default-keybinds) (evil-define-key 'normal 'global (kbd "M-d") #'evil-multiedit-match-symbol-and-next (kbd "M-D") #'evil-multiedit-match-symbol-and-prev) (evil-define-key 'visual 'global "R" #'evil-multiedit-match-all (kbd "M-d") #'evil-multiedit-match-and-next (kbd "M-D") #'evil-multiedit-match-and-prev) (evil-define-key '(visual normal) 'global (kbd "C-M-d") #'evil-multiedit-restore) (with-eval-after-load 'evil-mutliedit (evil-define-key 'multiedit 'global (kbd "M-d") #'evil-multiedit-match-and-next (kbd "M-S-d") #'evil-multiedit-match-and-prev (kbd "RET") #'evil-multiedit-toggle-or-restrict-region) (evil-define-key '(multiedit multiedit-insert) 'global (kbd "C-n") #'evil-multiedit-next (kbd "C-p") #'evil-multiedit-prev)))
Hlissner's implementation of multiple-cursor for evil-mode.
evil-org-mode external
Supplemental evil-mode keybindings to emacs org-mode.
(use-package evil-org :after org :ensure t :hook (org-mode . (lambda () evil-org-mode)) :config (require 'evil-org-agenda) (evil-org-agenda-set-keys))
evil-goggles external
Display visual hint on evil edit operations.
(use-package evil-goggles :ensure t :config (evil-goggles-mode) (evil-goggles-use-diff-faces))
Keybindings
Leader key
This is taken from vim, The leader key in Vim and Neovim is a special key used as a prefix for custom keyboard shortcuts, also known as mappings. It creates a "namespace" for user-defined commands, allowing you to create personalized shortcuts without conflicting with Vim's default commands.
(with-eval-after-load 'evil (evil-set-leader '(normal visual) (kbd "SPC")))
1st level commands
These commands is used often so it will need a faster keybindings (<leader> <key>
).
(with-eval-after-load 'evil (evil-global-set-key 'normal (kbd "<leader>:") '("execute commands" . execute-extended-command)) (evil-global-set-key 'normal (kbd "<leader>;") '("eval expression" . eval-expression)) (evil-global-set-key 'normal (kbd "<leader>u") '("universal arguments" . universal-argument)) (with-eval-after-load 'emms (evil-global-set-key 'normal (kbd "<leader>>") '("emms next" . emms-next)) (evil-global-set-key 'normal (kbd "<leader>>") '("emms previous" . emms-previous))))
C
For now it's reserved only for org-capture
.
(with-eval-after-load 'evil (setq my/c-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>c") (cons "C" my/c-map)) (with-eval-after-load 'org (evil-define-key* nil my/c-map "c" '("org-capture" . org-capture))))
B
All keybindings will go with <leader>b
.
(with-eval-after-load 'evil (setq my/buffer-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>b") (cons "Buffer" my/buffer-map)) (evil-define-key* nil my/buffer-map "." '("set bookmark" . bookmark-set) "[" '("previous buffer" . previous-buffer) "]" '("next buffer" . next-buffer) "c" '("clone indirect buffer" . clone-indirect-buffer) "C" '("clone indirect buffer other window" . clone-indirect-buffer-other-window) "d" '("kill buffer" . kill-buffer) "i" '("ibuffer" . ibuffer) "k" '("kill buffer" . kill-buffer) "l" '("switch to last buffer" . evil-switch-to-windows-last-buffer) "m" '("switch to message buffer". (lambda () (interactive) (switch-to-buffer "*Messages*"))) "n" '("next buffer" . next-buffer) "N" '("new buffer" . evil-buffer-new) "p" '("previous buffer" . previous-buffer) "o" '("other buffer" . (lambda () (interactive) (switch-to-buffer nil))) "r" '("revert buffer" . revert-buffer) "R" '("rename buffer" . rename-buffer) "s" '("save buffer" . save-buffer) "x" '("switch to scratch buffer" . (lambda () (interactive) (switch-to-buffer "*scratch*"))) "z" '("bury buffer" . bury-buffer)))
- repeat-map
These function will be available for the
repeat-mode
.(with-eval-after-load 'evil (defvar my/buffer-map-repeat-map (let ((map (make-sparse-keymap))) (define-key map "n" #'next-buffer) (define-key map "p" #'previous-buffer) (define-key map "[" #'previous-buffer) (define-key map "]" #'next-buffer) map) "Keymap for my/buffer-map repeat") (put 'next-buffer 'repeat-map 'my/buffer-map-repeat-map) (put 'previous-buffer 'repeat-map 'my/buffer-map-repeat-map))
F
Perhaps the most used keybindings, <leader>f
.
(with-eval-after-load 'evil (setq my/file-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>f") (cons "Files" my/file-map)) (evil-define-key* nil my/file-map "f" '("find file" . find-file) "d" '("jump to current directory" . dired-jump) "i" '("open init file" . (lambda () (interactive) (find-file (expand-file-name "init.el" user-emacs-directory)))) "k" '("delete frame" . delete-frame) "r" '("recent file" . recentf) "s" '("save buffer" . save-buffer) "S" '("Save as" . (lambda () (interactive "F") (write-file "./" t))) "t" '("find-file other tab" . find-file-other-tab) "w" '("find file other window" . find-file-other-window)))
G
All git-related commands goes here, <leader>g
.
(with-eval-after-load 'evil (setq my/g-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>g") (cons "G" my/g-map)))
H
This key <leader>h
just simulate the standard Emacs C-h
.
(with-eval-after-load 'evil (evil-define-key* '(normal visual) 'global (kbd "<leader>h") (cons "Help" help-map)))
I
Commands for quick insertion <leader>i
.
(with-eval-after-load 'evil (setq my/insert-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>i") (cons "I" my/insert-map)) (evil-define-key* nil my/insert-map "c" '("insert character" . insert-char)) (evil-define-key* nil my/insert-map "d" '("duplicate-dwim" . duplicate-dwim)))
O
Misc quick commands, also for org-mode <leader>o
.
(with-eval-after-load 'evil (setq my/org-mode-map (make-sparse-keymap)) (setq my/open-map (make-sparse-keymap)) (setq my/open-org (make-sparse-keymap)) (setq my/open-roam-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>o") (cons "O" my/open-map)) (evil-define-key* nil my/open-map (kbd "o") (cons "Org files" my/open-org)) (with-eval-after-load 'org-roam (evil-define-key* nil my/open-map (kbd "r") (cons "Org Roam" my/open-roam-map))) (evil-define-key* nil my/open-map "a" '("Org Agenda" . org-agenda) "c" '("Org Capture" . org-capture) "L" '("Store org link" . org-store-link) "l" '("Avy open link" . link-hint-open-link)) (with-eval-after-load 'notmuch (evil-define-key* nil my/open-map "m" '("mail" . notmuch))) (evil-define-key* nil my/open-org (kbd "n") '("Notes" . (lambda () (interactive) (find-file (expand-file-name "notes.org" +config/org-directory)))) (kbd "r") '("Routines" . (lambda () (interactive) (find-file (expand-file-name "routines.org" +config/org-directory)))) (kbd "p") '("Personal" . (lambda () (interactive) (find-file (expand-file-name "personal.org" +config/org-directory)))) (kbd "P") '("Projects" . (lambda () (interactive) (find-file (expand-file-name "projects.org" +config/org-directory)))) (kbd "R") '("Last refiled" . org-refile-goto-last-stored) (kbd "C") '("Last Capture" . org-capture-goto-last-stored)))
S
Maybe the third most used keys, <leader>s
.
(with-eval-after-load 'evil (setq my/search-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>s") (cons "S" my/search-map)))
T
(with-eval-after-load 'evil (setq my/tab-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>t") (cons "T" my/tab-map)))
W
The second most used keybindings, I will try to use the default vim keys.
(with-eval-after-load 'evil (setq my/window-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>w") (cons "W" my/window-map)) (evil-define-key* nil my/window-map "C-o" '("delete other window" . delete-other-windows) "[" '("left window" . evil-window-left) "]" '("right window" . evil-window-right) "=" '("enlarge window" . enlarge-window) "-" '("shrink window" . shrink-window) "}" '("enlarge horizontal" . enlarge-window-horizontally) "{" '("shrink horizontal" . shrink-window-horizontally) "+" '("increase height" . evil-window-increase-height) "-" '("decrease height" . evil-window-decrease-height) "<" '("decrease width" . evil-window-decrease-width) ">" '("increase width" . evil-window-increase-width) "b" '("bottom right" . evil-window-bottom-right) "c" '("delete" . evil-window-delete) "d" '("delete" . evil-window-delete) "h" '("left window" . evil-window-left) "f" '("find file other window" . ffap-other-window) "j" '("down window" . evil-window-down) "k" '("up window" . evil-window-up) "l" '("window right" . evil-window-right) "n" '("new window" . evil-window-new) "o" '("delete other" . delete-other-windows) "m" '("mru" . evil-window-mru) "q" '("quit window" . evil-quit) "r" '("rotate downward" . evil-window-rotate-downwards) "R" '("rotate upward" . evil-window-rotate-upwards) "s" '("split horizontally and follow" . +evil/window-split-and-follow) "T" '("tear off window" . tear-off-window) "t" '("top left window" . evil-window-top-left) "u" '("undo" . winner-undo) "v" '("split vertical and follow" . +evil/window-vsplit-and-follow) "w" '("other window" . other-window) "W" '("previous window" . evil-window-prev) "x" '("exchange window" . evil-window-exchange) "|" '("set width" . evil-window-set-width) (kbd "<left>") '("left window" . evil-window-left) (kbd "<right>") '("right window" . evil-window-right) (kbd "<down>") '("down window" . evil-window-down) (kbd "<up>") '("up window" . evil-window-up)))
- repeat-map
(with-eval-after-load 'evil (defvar my/window-split-repeat-map (let ((map (make-sparse-keymap))) (define-key map "s" #'+evil/window-split-and-follow) (define-key map "v" #'+evil/window-vsplit-and-follow) map) "Keymap for my/window-exchange repeat") (put '+evil/window-split-and-follow 'repeat-map 'my/window-split-repeat-map) (put '+evil/window-vsplit-and-follow 'repeat-map 'my/window-split-repeat-map)) (with-eval-after-load 'evil (defvar my/window-exchange-repeat-map (let ((map (make-sparse-keymap))) (define-key map "x" #'evil-window-exchange) map) "Keymap for my/window-exchange repeat") (put 'evil-window-exchange 'repeat-map 'my/window-exchange-repeat-map)) (with-eval-after-load 'evil (defvar my/winner-undo-repeat-map (let ((map (make-sparse-keymap))) (define-key map "u" #'winner-undo) map) "Keymap for my/winner-undo repeat") (put 'winner-undo 'repeat-map 'my/winner-undo-repeat-map)) (with-eval-after-load 'evil (defvar my/window-rotate-repeat-map (let ((map (make-sparse-keymap))) (define-key map "r" #'evil-window-rotate-downwards) (define-key map "R" #'evil-window-rotate-downwards) map) "Keymap for my/window-rotate repeat") (put 'evil-window-rotate-upwards 'repeat-map 'my/window-rotate-repeat-map) (put 'evil-window-rotate-downwards 'repeat-map 'my/window-rotate-repeat-map)) (with-eval-after-load 'evil (defvar my/window-size-repeat-map (let ((map (make-sparse-keymap))) (define-key map "=" #'enlarge-window) (define-key map "_" #'shrink-window) (define-key map "{" #'enlarge-window-horizontally) (define-key map "}" #'shrink-window-horizontally) (define-key map "<" #'evil-window-decrease-width) (define-key map ">" #'evil-window-increase-width) (define-key map "+" #'evil-window-increase-height) (define-key map "-" #'evil-window-decrease-height) map) "Keymap for my/buffer-map repeat") (put 'evil-window-increase-height 'repeat-map 'my/window-size-repeat-map) (put 'evil-window-decrease-height 'repeat-map 'my/window-size-repeat-map) (put 'evil-window-decrease-width 'repeat-map 'my/window-size-repeat-map) (put 'evil-window-increase-width 'repeat-map 'my/window-size-repeat-map) (put 'enlarge-window-horizontally 'repeat-map 'my/window-size-repeat-map) (put 'shrink-window-horizontally 'repeat-map 'my/window-size-repeat-map) (put 'enlarge-window 'repeat-map 'my/window-size-repeat-map) (put 'shrink-window 'repeat-map 'my/window-size-repeat-map))
Q
Also used for reloading configuration (like Doomemacs) <leader>q
.
(with-eval-after-load 'evil (setq my/quit-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>q") (cons "Q" my/quit-map)) (evil-define-key* nil my/quit-map "R" '("restart Emacs" . restart-emacs) "r" '("reload configuration" . (lambda () (interactive) (load user-init-file))) "q" '("quit Emacs and save" . save-buffers-kill-terminal)))
Z keys
This is for other evil packages that need its own keybinding, and tbf, there's already a lot of them in the g
and z
keys.
(with-eval-after-load 'evil (setq my/z-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>z") (cons "Z" my/z-map)))
Builtins
base builtin
Usually configuration that defined at the C source code.
(use-package emacs :ensure nil :custom (read-buffer-completion-ignore-case t) (tab-always-indent nil) (visible-bell nil) (use-short-answers t) (use-dialog-box nil) (window-resize-pixelwise nil) (frame-resize-pixelwise t) (ring-bell-function #'ignore) (scroll-preserve-screen-position t) (scroll-conservatively 101) (fast-but-imprecise-scrolling t) (truncate-partial-width-windows nil) (tab-width 4) (fill-column 80) (enable-recursive-minibuffers t) (use-file-dialog nil) (create-lockfiles nil) (delete-by-moving-to-trash t) (inhibit-startup-screen t) :config (when (bound-and-true-p tooltip-mode) (tooltip-mode -1)) (setq completion-ignore-case t read-file-name-completion-ignore-case t read-buffer-completion-ignore-case t load-prefer-newer t auto-window-vscroll nil inhibit-compacting-font-caches t redisplay-skip-fontification-on-input t) (set-default 'indicate-empty-lines nil) (setq-default truncate-lines t) (setq-default x-stretch-cursor nil))
files builtin
Usually configurations related to file manipulations.
(use-package files :ensure nil :config (nconc auto-mode-alist '(("/LICENSE\\'" . text-mode) ("\\.log\\'" . text-mode) ("rc\\'" . conf-mode) ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode))) :hook ((prog-mode text-mode) . auto-save-visited-mode) :custom (auto-save-visited-interval 10) (find-file-suppress-same-file-warnings t) ;;(confirm-kill-emacs #'yes-or-no-p) ; confirm when exiting (confirm-kill-processes nil) ; don't confirm killing processes (revert-without-query (list ".")) (find-file-visit-truename t) ; `find-file' will visit the actual file (make-backup-files nil) (version-control t) (backup-by-copying t) (delete-old-versions t) (kept-new-versions 6) (kept-old-versions 2) (auto-save-include-big-deletions t) (auto-save-list-file-prefix (expand-file-name ".autosave/" user-emacs-directory)) (backup-directory-alist `(("." . ,(expand-file-name ".backup" user-emacs-directory)))) (auto-mode-case-fold nil) (require-final-newline t))
saveplace builtin
When closing a file (like when exiting Emacs or killing a buffer), the point (or cursor) in that buffer will be saved. I find this a must-have feature.
(use-package saveplace :init (save-place-mode 1) :custom (save-place-file (expand-file-name "places" user-emacs-directory)))
autorevert builtin
Here I'm using global-auto-revert-mode
to automatically revert all opened buffer.
(use-package autorevert :hook (focus-in . doom-auto-revert-buffers-h) :hook (after-save . doom-auto-revert-buffers-h) :hook (prog-mode . doom-auto-revert-buffer-h) :custom (auto-revert-interval 60) (auto-revert-use-notify nil) (global-auto-revert-non-file-buffers t) (auto-revert-verbose t) (auto-revert-stop-on-user-input nil) (revert-without-query (list ".")) :config (defun doom-visible-buffer-p (buf) "Return non-nil if BUF is visible." "Return non-nil if BUF is not visible." (not (doom-visible-buffer-p buf))) (defun doom-visible-buffers (&optional buffer-list all-frames) "Return a list of visible buffers (i.e. not buried)." (let ((buffers (delete-dups (cl-loop for frame in (if all-frames (visible-frame-list) (list (selected-frame))) if (window-list frame) nconc (mapcar #'window-buffer it))))) (if buffer-list (cl-delete-if (lambda (b) (memq b buffer-list)) buffers) (delete-dups buffers)))) (defun doom-auto-revert-buffer-h () "Auto revert current buffer, if necessary." (unless (or auto-revert-mode (active-minibuffer-window)) (let ((auto-revert-mode t)) (auto-revert-handler)))) (defun doom-auto-revert-buffers-h () "Auto revert stale buffers in visible windows, if necessary." (dolist (buf (doom-visible-buffers)) (with-current-buffer buf (doom-auto-revert-buffer-h)))))
savehist builtin
Saving the minibuffer history.
(use-package savehist :init (savehist-mode 1) :custom (savehist-file (expand-file-name "history" user-emacs-directory)) (savehist-save-minibuffer-history t) (savehist-autosave-interval nil) (savehist-coding-system 'utf-8) (savehist-additional-variables '(evil-jumps-history command-history kill-ring register-alist mark-ring global-mark-ring search-ring regexp-search-ring)))
recentf builtin
(use-package recentf :bind ("C-c f" . recentf) :commands recentf-open-files :hook (dired-mode . doom--recentf-add-dired-directory-h) :init (recentf-mode 1) :custom (recentf-auto-cleanup t) (recentf-max-saved-items 250) (recentf-max-menu-items 300) (recentf-exclude `("/elpa/" ;; ignore all files in elpa directory "recentf" ;; remove the recentf load file ".*?autoloads.el$" "treemacs-persist" "company-statistics-cache.el" ;; ignore company cache file "/intero/" ;; ignore script files generated by intero "/journal/" ;; ignore daily journal files ".gitignore" ;; ignore `.gitignore' files in projects "/tmp/" ;; ignore temporary files "NEWS" ;; don't include the NEWS file for recentf "bookmarks" "bmk-bmenu" ;; ignore bookmarks file in .emacs.d "loaddefs.el" "^/\\(?:ssh\\|su\\|sudo\\)?:" ;; ignore tramp/ssh files (concat "^" (regexp-quote (or (getenv "XDG_RUNTIME_DIR") "/run"))))) :config (defun doom--recentf-file-truename-fn (file) (if (or (not (file-remote-p file)) (equal "sudo" (file-remote-p file 'method))) (abbreviate-file-name (file-truename (tramp-file-name-localname file))) file)) ;; Resolve symlinks, strip out the /sudo:X@ prefix in local tramp paths, and ;; abbreviate $HOME -> ~ in filepaths (more portable, more readable, & saves ;; space) (add-to-list 'recentf-filename-handlers #'doom--recentf-file-truename-fn) ;; Text properties inflate the size of recentf's files, and there is ;; no purpose in persisting them (Must be first in the list!) (add-to-list 'recentf-filename-handlers #'substring-no-properties) (defun doom--recentf-add-dired-directory-h () "Add dired directories to recentf file list." (recentf-add-file default-directory)) ;; The most sensible time to clean up your recent files list is when you quit ;; Emacs (unless this is a long-running daemon session). (setq recentf-auto-cleanup (if (daemonp) 300)) (add-hook 'kill-emacs-hook #'recentf-cleanup))
Even though I use consult-find-file the actual recentf package is still used (I think).
server builtin
(use-package server :if (display-graphic-p) :defer 1 :config (unless (server-running-p) (server-start)) (require 'org-protocol))
UI
Base UI builtin
(use-package emacs :ensure nil :hook ((prog-mode text-mode conf-mode) . display-line-numbers-mode) :config (setq hscroll-margin 2 hscroll-step 1 ;; Emacs spends too much effort recentering the screen if you scroll the ;; cursor more than N lines past window edges (where N is the settings of ;; `scroll-conservatively'). This is especially slow in larger files ;; during large-scale scrolling commands. If kept over 100, the window is ;; never automatically recentered. The default (0) triggers this too ;; aggressively, so I've set it to 10 to recenter if scrolling too far ;; off-screen. scroll-conservatively 10 scroll-margin 0 scroll-preserve-screen-position t ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll' ;; for tall lines. auto-window-vscroll nil ;; mouse mouse-wheel-scroll-amount '(2 ((shift) . hscroll)) mouse-wheel-scroll-amount-horizontal 2) ;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any ;; feedback after typing is better UX than no feedback at all. (setq echo-keystrokes 0.02) ;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This ;; doesn't look too great with direnv, however... (setq resize-mini-windows 'grow-only) ;; Try to keep the cursor out of the read-only portions of the minibuffer. (setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) ;; Show absolute line numbers for narrowed regions to make it easier to tell the ;; buffer is narrowed, and where you are, exactly. (setq-default display-line-numbers-widen t))
frame builtin
(use-package frame :ensure nil :hook (after-init . window-divider-mode) :config (blink-cursor-mode 1) (setq window-divider-default-places t window-divider-default-bottom-width 1 window-divider-default-right-width 1))
window builtin
(use-package window :ensure nil :config (setq split-width-threshold 160 split-height-threshold nil))
comint builtin
(use-package comint :ensure nil :config (setq comint-prompt-read-only t comint-buffer-maximum-size 2048) (with-eval-after-load 'evil (evil-define-key* '(normal visual) comint-mode-map "q" '(lambda () (interactive) (kill-buffer nil)))))
winner builtin
(use-package winner :ensure nil :init (winner-mode +1) :config (setq winner-boring-buffers '("*Completions*" "*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*" "*esh command on file*")))
Fonts
(set-face-attribute 'default nil :family "Iosevka Nerd Font Mono" :height 80) (set-face-attribute 'variable-pitch nil :family "Iosevka Nerd Font Mono" :height 80) ;;(set-frame-font "Iosevka Nerd Font Mono 8" nil t)
the :height
value is 1/10 pt, so 100 height means 10 pt.
hs-mode builtin
(use-package hideshow :hook (prog-mode . hs-minor-mode))
mouse builtin
(use-package mouse :ensure nil :config (setq mouse-yank-at-point t))
paren builtin
This built-in package is necessary when dealing with Emacs lisp.
(use-package paren :ensure nil :hook (prog-mode . show-paren-local-mode) :config (show-paren-mode 1) :custom (show-paren-style 'parenthesis) (show-paren-delay 0.4) (show-paren-context-when-offscreen 'overlay) (show-paren-when-point-inside-paren nil) (show-paren-when-point-in-periphery nil))
repeat-mode builtin
(use-package repeat :ensure nil :init (repeat-mode +1))
which-key external
Emacs package that displays available keybindings in popup. This is one of the top Emacs packages that I must have in my configuration.
(use-package which-key :ensure t :custom (which-key-lighter "") (which-key-sort-order #'which-key-key-order-alpha) (which-key-sort-uppercase-first nil) (which-key-add-column-padding 1) (which-key-max-display-columns nil) (which-key-min-display-lines 6) (which-key-compute-remaps t) (which-key-side-window-slot -10) (which-key-separator " → ") (which-key-allow-evil-operators nil) (which-key-use-C-h-commands t) (which-key-show-remaining-keys t) (which-key-show-prefix 'bottom) (which-key-show-operator-state-maps nil) :config (which-key-mode) (which-key-setup-side-window-bottom) (which-key-setup-minibuffer) (define-key which-key-mode-map (kbd "C-x <f5>") 'which-key-C-h-dispatch))
marginalia external
(use-package marginalia :ensure t :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle)) :init (marginalia-mode))
avy external
By using gK
or gJ
in normal or visual mode.
(use-package avy :ensure t :config (setq avy-background t) (setq avy-timeout-seconds 0.5) (evil-define-key* '(normal visual) 'global "gJ" 'evil-avy-goto-line-below "gK" 'evil-avy-goto-line-above))
rainbow-mode external
(use-package rainbow-mode :hook (prog-mode . rainbow-mode) :ensure t :config (setq rainbow-html-colors-major-mode-list '(prog-mode conf-mode html-mode css-mode php-mode nxml-mode xml-mode) rainbow-html-colors t))
rainbow-identifiers external
(use-package rainbow-identifiers :ensure t :hook (prog-mode . rainbow-identifiers-mode))
rainbow-identifiers external
(use-package rainbow-delimiters :ensure t :hook (prog-mode . rainbow-delimiters-mode) :config (setq rainbow-delimiters-max-face-count 4))
doom-themes external
(use-package doom-themes :ensure t :custom (doom-themes-enable-bold t) (doom-themes-enable-italic t) :config (load-theme 'doom-nord t) (doom-themes-visual-bell-config) (doom-themes-org-config))
nerd-icons external
(use-package nerd-icons :ensure t)
doom-modeline external
(use-package doom-modeline :ensure t :hook (after-init . doom-modeline-mode) :config (setopt doom-modeline-project-detection 'auto doom-modeline-indent-info t))
Shells and Terminals
eshell builtin
A shell-like command interpreter implemented in Emacs Lisp.
(use-package eshell :ensure nil :custom (eshell-history-size 10000) (eshell-hist-ignore-dups t) (eshell-buffer-maximum-lines 10000) (eshell-scroll-to-bottom-on-input t) (eshell-destroy-buffer-when-process-dies t) (eshell-prompt-regexp "^[^\)]*[\)] "))
sh-mode builtin
Install bash-language-server
with npm install -g bash-language-server
(use-package sh-script :ensure nil :mode ("\\.bats\\'" . sh-mode) :mode ("\\.\\(?:zunit\\|env\\)\\'" . sh-mode) :mode ("/bspwmrc\\'" . sh-mode) :mode ("PKGBUILD" . sh-mode) :config (setq sh-indent-after-continuation 'always) (with-eval-after-load 'rainbow-delimiters (add-hook 'sh-mode-hook #'rainbow-delimiters-mode) (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs '((sh-mode bash-ts-mode) .("bash-language-server" "start")))) (add-hook 'sh-mode-hook #'eglot-ensure) (add-hook 'bash-ts-mode-hook #'eglot-ensure)))
vterm external
(use-package vterm :ensure t :demand t :commands vterm-mode :config (with-eval-after-load 'evil (setq my/vterm-map (make-sparse-keymap)) (evil-define-key* '(normal visual) 'global (kbd "<leader>v") (cons "Vterm" my/vterm-map)) (evil-define-key* nil my/vterm-map "v" '("terminal" . vterm))) (with-eval-after-load 'evil-collection (evil-collection-vterm-setup)) (add-hook 'vterm-mode-hook (lambda () (setq-local global-hl-line-mode nil) (setq-local hscroll-margin 0))) (setq vterm-kill-buffer-on-exit t) (evil-set-initial-state #'vterm-mode 'insert))
multi-vterm external
(use-package multi-vterm :after vterm :ensure t :config (setopt multi-vterm-dedicated-window-height-percent 20) (with-eval-after-load 'evil (evil-define-key* nil my/vterm-map "V" '("toggle terminal" . multi-vterm-dedicated-toggle)) (evil-define-key* 'normal vterm-mode-map (kbd ",c") #'multi-vterm) (evil-define-key* 'normal vterm-mode-map (kbd ",n") #'multi-vterm-next) (evil-define-key* 'normal vterm-mode-map (kbd ",p") #'multi-vterm-prev)))
Editing
select builtin
(use-package select :ensure nil :custom (select-enable-clipboard t))
transient-mark-mode builtin
Highlight region.
(transient-mark-mode 1)
delete-selection-mode builtin
When enabled, typed text replaces the selection if the selection is active. Otherwise, typed text is just inserted at point regardless of any selection.
(delete-selection-mode 1)
subword builtin
(use-package subword :ensure nil :init (global-subword-mode 1))
text-mode builtin
(use-package text-mode :ensure nil :hook ((text-mode . visual-line-mode) (prog-mode . (lambda () (setq-local sentence-end-double-space t)))) :config (setq-default sentence-end-double-space nil) (setq sentence-end-without-period nil) (setq colon-double-space nil) (setq use-hard-newlines t) (setq adaptive-fill-mode t))
whitespace builtin
(use-package whitespace :ensure nil :config (setq whitespace-line-column nil whitespace-style '(face indentation tabs tab-mark spaces space-mark newline trailing lines-tail) whitespace-display-mappings '((tab-mark ?\t [?› ?\t]) (newline-mark ?\n [?¬ ?\n]) (space-mark ?\ [?·] [?.]))))
ispell builtin
(use-package ispell :ensure nil :config (setq ispell-dictionary "english" ispell-personal-dictionary (expand-file-name "dictionary/personal" user-emacs-directory)) (add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:")) (add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC")) (add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_EXAMPLE" . "#\\+END_EXAMPLE")) (when (executable-find "aspell") (setq ispell-program-name (executable-find "aspell") ispell-extra-args '("--sug-mode=ultra" "--run-together")) (unless ispell-aspell-dict-dir (setq ispell-aspell-dict-dir (ispell-get-aspell-config-value "dict-dir"))) (unless ispell-aspell-data-dir (setq ispell-aspell-data-dir (ispell-get-aspell-config-value "data-dir"))) (add-hook 'text-mode-hook (lambda () (setq-local ispell-extra-args (remove "--run-together" ispell-extra-args))))) (when (executable-find "hunspell") (setq ispell-program-name (executable-find "hunspell"))) (when (executable-find "enchant-2") (setq ispell-program-name (executable-find "enchant-2") ispell-cmd-args '("-a"))))
flyspell builtin
(use-package flyspell :ensure nil :config (when (executable-find "aspell") (setq ispell-program-name (executable-find "aspell") ispell-extra-args '("--sug-mode=ultra" "--run-together"))) (when (executable-find "hunspell") (setq ispell-program-name (executable-find "hunspell") ispell-extra-args nil)) (when (executable-find "enchant-2") (setq ispell-program-name (executable-find "enchant-2") ispell-cmd-args '("-a") ispell-extra-args nil)))
IDE stuffs
eldoc builtin
(use-package eldoc :hook ((emacs-lisp-mode lisp-interaction-mode ielm-mode) . eldoc-mode))
eglot builtin
(use-package eglot :hook ((rust-mode nix-mode python-mode toml-mode markdown-mode js-mode js-ts-mode typescript-ts-mode) . eglot-ensure) :init (setopt eglot-sync-connect 1 eglot-autoshutdown t) :config (with-eval-after-load 'cape (advice-add 'eglot-completion-at-point :around #'cape-wrap-super)) (with-eval-after-load 'evil-collection (evil-collection-eglot-setup)) (add-to-list 'eglot-server-programs `(rust-mode . ("rust-analyzer" :initializationOptions ( :procMacro (:enable t) :cargo ( :buildScripts (:enable t) :features "all")))) '(toml-ts-mode . ("taplo" "lsp" "stdio"))) (add-to-list 'eglot-server-programs '(web-mode . ("vscode-html-language-server" "--stdio"))) (add-to-list 'eglot-server-programs '(css-mode . ("vscode-css-language-server" "--stdio"))))
consult-eglot external
(use-package consult-eglot :after '(consult eglot) :ensure t)
consult-eglot external
(use-package consult-eglot-embark :ensure t :after consult-eglot)
flymake builtin
Built-in on-the-fly syntax checking.
(use-package flymake :ensure t :pin gnu :config (with-eval-after-load 'evil-collection (evil-collection-flymake-setup)) (setq flymake-diagnostic-format-alist '((t . (origin code message)))))
copilot external
I still have mixed feelings about AI, but I do want to explore, just don't use it from the start
(use-package copilot :hook (prog-mode . copilot-mode) :load-path "site-lisp/copilot" :bind (:map copilot-mode-map ("<tab>" . 'copilot-accept-completion) ("Tab" . 'copilot-accept-completion) ("C-TAB" . 'copilot-accept-completion-by-word) ("C-<tab>" . 'copilot-accept-completion-by-word)) :config (setopt copilot-idle-delay 2))
projectile external
A project interaction library for Emacs, with little external dependency as possible.
(use-package projectile :ensure t :demand t :commands (projectile-project-root projectile-project-name projectile-project-p projectile-locate-dominating-file projectile-relevant-known-projects) :bind (([remap evil-jump-to-tag] . projectile-find-tag) ([remap find-tag] . projectile-find-tag)) :hook (dired-before-readin . projectile-track-known-projects-find-file-hook) :custom (projectile-cache-file (expand-file-name ".projects" user-emacs-directory)) (projectile-auto-discover nil) (projectile-enable-caching (not noninteractive)) (projectile-globally-ignored-files '("DS_Store" "TAGS")) (projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")) (projectile-kill-buffers-filter 'kill-only-files) (projectile-known-projects-file (expand-file-name ".projectile_projects.eld" user-emacs-directory)) (projectile-ignored-projects '("~/")) (projectile-project-search-path `(,(expand-file-name "Projects" (getenv "HOME")))) (projectile-project-root-files-bottom-up (append '(".projectile" ".project" ".git") (when (executable-find "hg") '(".hg")) (when (executable-find "bzr") '(".bzr")))) (projectile-project-root-files-top-down-recurring '("Makefile")) (compilation-buffer-name-function #'projectile-compilation-buffer-name) (compilation-save-buffers-predicate #'projectile-current-project-buffer-p) (projectile-git-submodule-command nil) (projectile-indexing-method 'hybrid) :config (global-set-key [remap evil-jump-to-tag] #'projectile-find-tag) (global-set-key [remap find-tag] #'projectile-find-tag) (projectile-mode +1) (put 'projectile-git-submodule-command 'initial-value projectile-git-submodule-command) (with-eval-after-load 'evil (evil-define-key* '(normal visual) 'global (kbd "<leader>SPC") '("find project file" . projectile-find-file)) (evil-define-key* '(normal visual) 'global (kbd "<leader>p") (cons "Project" projectile-command-map))))
perspective external
Useful when I'm working on several projectile projects at once. With each perspective having their own separate buffer list.
(use-package perspective :ensure t :demand t :config (setq persp-initial-frame-name "Main" persp-suppress-no-prefix-key-warning t) (if (featurep 'no-littering) (setq persp-state-default-file (expand-file-name ".perspective-state" no-littering-var-directory)) (setq persp-state-default-file (expand-file-name ".perspective-state" user-emacs-directory))) (global-set-key [remap switch-to-buffer] #'persp-switch-to-buffer*) (when (featurep 'consult) (require 'consult) (unless (boundp 'persp-consult-source) (defvar persp-consult-source (list :name "Perspective" :narrow ?s :category 'buffer :state #'consult--buffer-state :history 'buffer-name-history :default t :items #'(lambda () (consult--buffer-query :sort 'visibility :predicate '(lambda (buf) (persp-is-current-buffer buf t)) :as #'buffer-name))))) (consult-customize consult--source-buffer :hidden t :default nil) (add-to-list 'consult-buffer-sources persp-consult-source)) (with-eval-after-load 'evil (evil-define-key* '(normal visual) 'global (kbd "<leader>TAB") (cons "Perspective" perspective-map)) (evil-define-key* nil perspective-map (kbd "TAB") '("swich to last used perspective" . persp-switch-last) "P" '("switch project" . projectile-persp-switch-project))) :init (customize-set-variable 'persp-mode-prefix-key (kbd "C-c TAB")) (unless (equal persp-mode t) (persp-mode 1)) :bind (([remap switch-to-buffer] . persp-switch-to-buffer*) ([remap kill-buffer] . persp-kill-buffer*)) :hook (kill-emacs . persp-state-save))
persp-projectile external
(use-package persp-projectile :ensure t :after perspective :commands projectile-persp-switch-project)
Snippets external
This is provided by yasnippet.
(use-package yasnippet :ensure t :init (yas-global-mode) :config (define-key yas-keymap [tab] 'yas-next-field) (setq yas-snippets-dir (list (expand-file-name "snippets" user-emacs-directory) (expand-file-name "straight/build/yasnippet-snippets/snippets" user-emacs-directory))) (with-eval-after-load 'evil (evil-define-key* nil my/insert-map "s" '("insert snippet" . consult-yasnippet)))) (use-package consult-yasnippet :ensure t) (use-package yasnippet-snippets :ensure t)
yasnippet-capf external
(use-package yasnippet-capf :after cape :ensure t :config (add-to-list 'completion-at-point-functions #'yasnippet-capf))
treesit-auto external
(use-package treesit-auto :ensure t :custom (treesit-auto-install 'prompt) :config (treesit-auto-add-to-auto-mode-alist 'all) (global-treesit-auto-mode))
smartparens external
I've tried to use the built-in electric-pair
for a while, it just doesn't have the default configuration for many of the major-modes that I frequently used, and I don't like spending time configuring for each of them. So smartparens
it is!
(use-package smartparens :ensure t :hook (text-mode markdown-mode prog-mode) :config (require 'smartparens-config))
Org-mode
org-mode core package builtin
(use-package org :demand t :commands org-tempo :hook (org-mode . flyspell-mode) :preface (if (not +config/org-directory) (cond ((file-directory-p (expand-file-name "Dropbox/org" (getenv "HOME"))) (setq org-directory (expand-file-name "Dropbox/org" (getenv "HOME")))) ((file-directory-p (expand-file-name "Sync/org" (getenv "HOME"))) (setq org-directory (expand-file-name "Sync/org" (getenv "HOME")))) ((file-directory-p (expand-file-name "Documents/google-drive/org" (getenv "HOME"))) (setq org-directory (expand-file-name "Documents/google-drive/org" (getenv "HOME"))))) (customize-set-variable 'org-directory +config/org-directory)) :hook ((org-mode . org-indent-mode) (org-mode . +config/org-prettify-symbols) (org-mode . variable-pitch-mode)) :config (with-eval-after-load 'evil (setq my/org-note-map (make-sparse-keymap)) (evil-define-key* '(normal visual) org-mode-map (kbd "<leader>m") (cons "org-mode" my/org-mode-map)) (evil-define-key* nil my/org-mode-map "n" (cons "Notes" my/org-note-map)) (evil-define-key* nil my/org-mode-map (kbd "M-l") 'org-insert-last-stored-link (kbd "C-w") '("Refile" . org-refile)) (evil-define-key nil my/org-note-map "c" '("capture" . org-capture))) (with-eval-after-load 'evil-collection (evil-collection-org-setup)) (global-set-key (kbd "C-c l") #'org-store-link) (global-set-key (kbd "C-c a") #'org-agenda) (global-set-key (kbd "C-c c") #'org-capture) (when (file-directory-p (expand-file-name "braindump/org" org-directory)) (customize-set-variable '+config/org-roam-directory (expand-file-name "braindump/org" org-directory))) (when (file-directory-p (expand-file-name "alexforsale.github.io" org-directory)) (customize-set-variable '+config/blog-directory (expand-file-name "alexforsale.github.io" org-directory))) (modify-syntax-entry ?= "$" org-mode-syntax-table) (modify-syntax-entry ?~ "$" org-mode-syntax-table) (modify-syntax-entry ?_ "$" org-mode-syntax-table) (modify-syntax-entry ?+ "$" org-mode-syntax-table) (modify-syntax-entry ?/ "$" org-mode-syntax-table) (modify-syntax-entry ?* "$" org-mode-syntax-table) (add-to-list 'org-modules 'org-tempo t) (add-to-list 'org-structure-template-alist '("sh" . "src sh")) (add-to-list 'org-structure-template-alist '("co" . "src conf")) (add-to-list 'org-structure-template-alist '("lisp" . "src lisp")) (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) (add-to-list 'org-structure-template-alist '("sc" . "src scheme")) (add-to-list 'org-structure-template-alist '("ts" . "src typescript")) (add-to-list 'org-structure-template-alist '("py" . "src python")) (add-to-list 'org-structure-template-alist '("go" . "src go")) (add-to-list 'org-structure-template-alist '("yaml" . "src yaml")) (add-to-list 'org-structure-template-alist '("js" . "src js")) (add-to-list 'org-structure-template-alist '("json" . "src json")) (add-to-list 'org-structure-template-alist '("n" . "note")) (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (awk . t) (C . t) (css . t) (calc . t) (ditaa . t) ; needs the `ditaa' package (dot . t ) ; `graphviz' (screen . t) (haskell . t) (java . t) (js . t) (latex . t) (lisp . t) (lua . t) (org . t) (perl . t) (plantuml . t) (python .t) (ruby . t) (shell . t) (sed . t) (scheme . t) (sql . t) (sqlite . t))) (setq-default org-use-sub-superscripts '{}) (add-to-list 'org-babel-tangle-lang-exts '("js" . "js")) (defun +config/org-prettify-symbols () (push '("[ ]" . "☐") prettify-symbols-alist) (push '("[X]" . "☑") prettify-symbols-alist) (prettify-symbols-mode)) (require 'org-tempo) :custom (org-highlight-latex-and-related '(native script entities)) (org-replace-disputed-keys t) (org-indirect-buffer-display 'current-window) (org-enforce-todo-dependencies t) (org-fontify-whole-heading-line t) (org-return-follows-link t) (org-mouse-1-follows-link t) (org-image-actual-width nil) (org-adapt-indentation nil) (org-startup-indented t) (org-link-descriptive nil) (org-log-done 'time) (org-log-refile 'time) (org-log-redeadline 'time) (org-log-reschedule 'time) (org-log-into-drawer t) (org-clone-delete-id t) (org-default-notes-file (expand-file-name "notes.org" org-directory)) (org-insert-heading-respect-content nil) (org-pretty-entities t) (org-use-property-inheritance t) (org-priority-highest ?A) (org-priority-lowest ?D) (org-priority-default ?B) (org-todo-keywords '((sequence "TODO(t!)" ; A task that needs doing & is ready to do "NEXT(n!)" ; Tasks that can be delayed "PROG(p!)" ; A task that is in progress "WAIT(w!)" ; Something external is holding up this task "HOLD(h!)" ; This task is paused/on hold because of me "|" "DONE(d!)" ; Task successfully completed "DELEGATED(l!)" ; Task is delegated "NOTES(o!)" ; set as notes "KILL(k!)") ; Task was cancelled, aborted or is no longer applicable )) (org-todo-keyword-faces '(("PROG" . (:foreground "#268bd2" :weight bold)) ("WAIT" . (:foreground "#b58900" :weight bold)) ("HOLD" . (:foreground "#859900" :weight bold)) ("NEXT" . (:foreground "#2aa198" :weight bold)) ("NOTES" . "#6c7c80") ("DELEGATED" . "#d33682") ("KILL" . "#a3be8c"))))
org-contrib external
(use-package org-contrib :ensure t)
org-entities builtin
(use-package org-entities :ensure nil :config (setq org-entities-user '(("flat" "\\flat" nil "" "" "266D" "♭") ("sharp" "\\sharp" nil "" "" "266F" "♯"))))
org-faces builtin
(use-package org-faces :ensure nil :custom (org-fontify-quote-and-verse-blocks t))
org-archive builtin
(use-package org-archive :ensure nil :after org :custom (org-archive-tag "archive") (org-archive-subtree-save-file-p t) (org-archive-mark-done t) (org-archive-reversed-order t) (org-archive-location (concat (expand-file-name "archives.org" org-directory) "::datetree/* Archived Tasks")))
org-capture builtin
(use-package org-capture :after org :ensure nil :demand t :config (org-capture-put :kill-buffer t) (setq org-capture-templates ;; this is the default from `doom'. `(("i" "Inbox - Goes Here first!" entry (file+headline ,(expand-file-name "inbox.org" org-directory) "Inbox") "** %?\n%i\n%a" :prepend t) ;; ("r" "Request" entry (file+headline ,(expand-file-name "inbox.org" org-directory) "Request") ;; (file ,(expand-file-name "request.template" org-directory))) ("l" "Links" entry (file+headline ,(expand-file-name "links.org" org-directory) "Links")))))
org-refile builtin
(use-package org-refile :ensure nil :after org :hook (org-after-refile-insert . save-buffer) :custom (org-refile-targets `((,(expand-file-name "projects.org" org-directory) :maxlevel . 1) (,(expand-file-name "notes.org" org-directory) :maxlevel . 1) (,(expand-file-name "routines.org" org-directory) :maxlevel . 3) (,(expand-file-name "personal.org" org-directory) :maxlevel . 1))) (org-refile-use-outline-path 't) (org-outline-path-complete-in-steps nil))
org-fold builtin
(use-package org-fold :ensure nil :after org org-contrib :custom (org-catch-invisible-edits 'smart))
org-id builtin
(use-package org-id :ensure nil :after org :custom (org-id-locations-file-relative t) (org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))
org-num builtin
(use-package org-num :ensure nil :after org :custom (org-num-face '(:inherit org-special-keyword :underline nil :weight bold)) (org-num-skip-tags '("noexport" "nonum")))
org-crypt builtin
(use-package org-crypt ; built-in :ensure nil :after org :commands org-encrypt-entries org-encrypt-entry org-decrypt-entries org-decrypt-entry ;;:hook (org-reveal-start . org-decrypt-entry) :preface ;; org-crypt falls back to CRYPTKEY property then `epa-file-encrypt-to', which ;; is a better default than the empty string `org-crypt-key' defaults to. (defvar org-crypt-key nil) (with-eval-after-load 'org (add-to-list 'org-tags-exclude-from-inheritance "crypt")) :config (setopt epa-file-encrypt-to "alexforsale@yahoo.com"))
org-attach builtin
(use-package org-attach :ensure nil :after org :commands (org-attach-new org-attach-open org-attach-open-in-emacs org-attach-reveal-in-emacs org-attach-url org-attach-set-directory org-attach-sync) :config (unless org-attach-id-dir (setq-default org-attach-id-dir (expand-file-name ".attach/" org-directory))) (with-eval-after-load 'projectile (add-to-list 'projectile-globally-ignored-directories org-attach-id-dir)) :custom (org-attach-auto-tag nil))
org-agenda builtin
(use-package org-agenda :ensure nil :after org :custom (org-agenda-breadcrumbs-separator " → ") (org-agenda-files (list (concat org-directory "/"))) (org-agenda-file-regexp "\\`[^.].*\\.org\\|[0-9]+$\\'") (org-agenda-include-inactive-timestamps t) (org-agenda-window-setup 'only-window) (org-stuck-projects '("+{project*}-killed-Archives/-DONE-KILL-DELEGATED" ("TODO" "NEXT" "IDEA" "PROG") nil "")) :config (with-eval-after-load 'evil (evil-set-initial-state #'org-agenda-mode 'normal) (evil-define-key 'normal org-agenda-mode-map "q" 'org-agenda-quit)) (setq org-agenda-custom-commands `(("w" "Work Agenda and all TODOs" ((agenda "" ((org-agenda-span 1) (org-agenda-start-on-weekday t) (org-agenda-block-separator nil) (org-agenda-use-time-grid t) (org-agenda-day-face-function (lambda (date) 'org-agenda-date)) (org-agenda-format-date "%A %-e %B %Y") (org-agenda-overriding-header "\nToday\n"))) (tags-todo "TODO=\"TODO\"|\"NEXT\"" ((org-agenda-block-separator nil) (org-agenda-skip-function '(org-agenda-skip-if-todo 'nottodo 'done)) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nIncomplete\n"))) (agenda "" ((org-agenda-span 7) (org-agenda-start-on-weekday 1) (org-agenda-block-separator nil) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nWeekly\n")))) ((org-agenda-tag-filter-preset '("-personal" "-home")))) ("h" "Home Agenda and all personal TODOs" ((agenda "" ((org-agenda-span 1) (org-agenda-start-on-weekday t) (org-agenda-block-separator nil) (org-agenda-use-time-grid t) (org-agenda-day-face-function (lambda (date) 'org-agenda-date)) (org-agenda-format-date "%A %-e %B %Y") (org-agenda-overriding-header "\nToday\n"))) (tags-todo "TODO=\"TODO\"|\"NEXT\"" ((org-agenda-block-separator nil) (org-agenda-skip-function '(org-agenda-skip-if-todo 'nottodo 'done)) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nIncomplete\n"))) (agenda "" ((org-agenda-span 7) (org-agenda-start-on-weekday 1) (org-agenda-block-separator nil) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nWeekly\n")))) ;; ((org-agenda-tag-filter-preset '("+personal"))) ))))
org-clock builtin
(use-package org-clock :ensure nil :after org :commands org-clock-save :hook (kill-emacs . org-clock-save) :custom (org-persist 'history) (org-clock-in-resume t) (org-clock-out-remove-zero-time-clocks t) (org-clock-history-length 20) (org-show-notification-handler "notify-send") (org-agenda-skip-scheduled-if-deadline-is-shown t) :config (org-clock-persistence-insinuate) (with-eval-after-load 'evil (setq my/org-clock-mode-map (make-sparse-keymap)) (evil-define-key* nil my/org-mode-map (kbd "C") (cons "Org Clock" my/org-clock-mode-map)) (evil-define-key* nil my/org-clock-mode-map "d" '("display clock" . org-clock-display) (kbd "C-d") '("display clock" . org-clock-display) "e" '("modify clock estimate" . org-clock-modify-effort-estimate) "i" '("clock in" . org-clock-in) "l" '("clock in last clock" . org-clock-in-last) (kbd "C-x") '("clock in last clock" . org-clock-in-last) "o" '("clock out" . org-clock-out) "g" '("goto clock" . org-clock-goto) (kbd "C-j") '("goto clock" . org-clock-goto) "q" '("cancel clock" . org-clock-cancel) (kbd "q") '("cancel clock" . org-clock-cancel))))
org-timer builtin
(use-package org-timer :ensure nil :config (setq org-timer-format "Timer :: %s"))
org-eldoc builtin
(use-package org-eldoc :ensure nil :after org org-contrib :config (puthash "org" #'ignore org-eldoc-local-functions-cache) ;;(puthash "plantuml" #'ignore org-eldoc-local-functions-cache) (puthash "python" #'python-eldoc-function org-eldoc-local-functions-cache) :custom (org-eldoc-breadcrumb-separator " → "))
org-superstar external
(use-package org-superstar :ensure t :hook (org-mode . org-superstar-mode) :custom (org-superstar-leading-bullet ?\s) (org-superstar-leading-fallback ?\s) (org-hide-leading-stars nil) (org-indent-mode-turns-on-hiding-stars nil) (org-superstar-todo-bullet-alist '(("TODO" . 9744) ("[ ]" . 9744) ("DONE" . 9745) ("[X]" . 9745))) :config (org-superstar-configure-like-org-bullets))
org-fancy-priority external
(use-package org-fancy-priorities ; priority icons :ensure t :defer t :hook (org-mode . org-fancy-priorities-mode) :hook (org-agenda-mode . org-fancy-priorities-mode) :custom (org-fancy-priorities-list '("⚡" "⬆" "⬇" "☕")))
org-modern external
(use-package org-modern :ensure t :demand t :config (set-face-attribute 'org-modern-symbol nil :family "Iosevka Nerd Font") (setq ;; Edit settings org-auto-align-tags nil org-tags-column 0 org-fold-catch-invisible-edits 'show-and-error org-special-ctrl-a/e t org-insert-heading-respect-content t ;; Org styling, hide markup etc. org-hide-emphasis-markers nil ; set to nil for easier editing org-ellipsis "…" ;; Agenda styling org-agenda-tags-column 0 org-agenda-block-separator ?─ org-agenda-time-grid '((daily today require-timed) (800 1000 1200 1400 1600 1800 2000) " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄") org-agenda-current-time-string "◀── now ─────────────────────────────────────────────────") (global-org-modern-mode))
org-download external
(use-package org-download :ensure t :after org :commands org-download-dnd org-download-yank org-download-screenshot org-download-clipboard org-download-dnd-base64 :config (unless org-download-image-dir (setq org-download-image-dir org-attach-id-dir)) (setq org-download-method 'attach org-download-timestamp "_%Y%m%d_%H%M%S" org-download-screenshot-method (cond ((featurep :system 'macos) "screencapture -i %s") ((featurep :system 'linux) (cond ((executable-find "maim") "maim -s %s") ((executable-find "scrot") "scrot -s %s") ((executable-find "gnome-screenshot") "gnome-screenshot -a -f %s")))) org-download-heading-lvl nil org-download-link-format "[[download:%s]]\n" org-download-annotate-function (lambda (_link) "") org-download-link-format-function (lambda (filename) (if (eq org-download-method 'attach) (format "[[attachment:%s]]\n" (org-link-escape (file-relative-name filename (org-attach-dir)))) ;; Handle non-image files a little differently. Images should be ;; inserted as normal with previews. Other files, like pdfs or zips, ;; should be linked to, with an icon indicating the type of file. (format (concat (unless (image-type-from-file-name filename) (concat (+org-attach-icon-for filename) " ")) org-download-link-format) (org-link-escape (funcall org-download-abbreviate-filename-function filename))))) org-download-abbreviate-filename-function (lambda (path) (if (file-in-directory-p path org-download-image-dir) (file-relative-name path org-download-image-dir) path))))
org-roam external
I've tried different Emacs packages for note taking. Although this is not a lightweight solution, but it's still the best one for me. And I publish it to my personal blog.
(use-package org-roam :ensure t :if (not (string-match-p "microsoft" (shell-command-to-string "uname -a"))) :demand t :after org :custom (org-roam-directory +config/org-roam-directory) (org-roam-complete-everywhere t) (org-roam-capture-templates '(("d" "default" plain "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: other\n#+property: header-args :exports both\n#+hugo_tags: \n%?" :if-new (file+head "%<%Y-%m-%d_%H-%M-%S>-${slug}.org" "#+title: ${title}\n") :unnarrowed t) ("p" "programming" plain "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: programming\n#+property: header-args :exports both\n#+hugo_tags: \n%?" :if-new (file+head "%<%Y-%m-%d_%H-%M-%S>-${slug}.org" "#+title: ${title}\n") :unnarrowed t) ("t" "tech" plain "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: tech\n#+property: header-args :exports both\n#+hugo_tags: \n%?" :if-new (file+head "%<%Y-%m-%d_%H-%M-%S>-${slug}.org" "#+title: ${title}\n") :unnarrowed t))) (org-roam-capture-ref-templates '(("r" "ref" plain "#+author: %n\n#+date: %t\n#+description: \n#+hugo_base_dir: ..\n#+hugo_section: posts\n#+hugo_categories: reference\n#+property: header-args :exports both\n#+hugo_tags: \n%?\n* Links\n- %l" :target (file+head "${slug}.org" "#+title: ${title}") :unnarrowed t))) :config (org-roam-setup) (org-roam-db-autosync-mode) (require 'org-roam-protocol) (with-eval-after-load 'evil-collection (evil-collection-org-roam-setup)) (with-eval-after-load 'evil (evil-define-key* nil my/open-roam-map "a" '("add alias" . org-roam-alias-add) "c" '("roam capture" . org-roam-capture) "f" '("find roam node" . org-roam-node-find) "i" '("insert roam node at point" . org-roam-node-insert) "l" '("toggle roam buffer" . org-roam-buffer-toggle) "r" '("remove alias" . org-roam-alias-remove))))
To be fair, this package is very powerful, but for me, at least, not all its feature is needed. I'll try to start simple, and make additional configuration as needed.
- org-roam-protocol
This is from the orgroam-manual.
[Desktop Entry] Name=Org-Protocol Exec=emacsclient %u Icon=emacs-icon Type=Application Terminal=false MimeType=x-scheme-handler/org-protocol
org-roam-ui external
(use-package org-roam-ui :ensure t :after org-roam ;; or :after org ;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have ;; a hookable mode anymore, you're advised to pick something yourself ;; if you don't care about startup time, use ;; :hook (after-init . org-roam-ui-mode) :config (setq org-roam-ui-sync-theme t org-roam-ui-follow t org-roam-ui-update-on-save t org-roam-ui-open-on-start t))
org-protocol-capture-html
Requires pandoc
(use-package org-protocol-capture-html :load-path "site-lisp/org-protocol-capture-html" :after org :config (add-to-list 'org-capture-templates `("w" "Web site" entry (file+headline ,(expand-file-name "links.org" org-directory) "Links") "** %a :website:\n\n:PROPERTIES:\n:CREATED: %U\n:END:\n %?\n\n%:initial")))
Misc Tools
make-mode builtin
(use-package make-mode :ensure nil :config (add-hook 'makefile-mode-hook 'indent-tabs-mode))
editorconfig external
(use-package editorconfig :ensure t)
executable builtin
(use-package executable :ensure nil :hook (after-save . executable-make-buffer-file-executable-if-script-p))
magit external
(use-package magit :ensure t :demand t :config (with-eval-after-load 'evil-collection (evil-collection-magit-repos-setup) (evil-collection-magit-section-setup) (evil-collection-magit-setup)) (with-eval-after-load 'evil (evil-set-initial-state #'git-commit-mode 'insert) (evil-define-key* nil my/g-map "g" '("git status" . magit-status))) :custom (magit-revision-show-gravatars '("^Author: " . "^Commit: ")) (magit-diff-refine-hunk 'all) (magit-log-arguments '("-n100" "--graph" "--decorate")))
elfeed external
An Emacs web feeds client.
(use-package elfeed :ensure t :demand t :config (with-eval-after-load 'evil-collection (evil-collection-elfeed-setup)) (with-eval-after-load 'evil (evil-define-key* nil my/open-map "e" '("open elfeed" . elfeed))))
- extensions
- elfeed-org
Configure the Elfeed RSS reader with an Orgmode file.
(use-package elfeed-org :after elfeed :ensure t :init (elfeed-org) :config (setq rmh-elfeed-org-files (list (expand-file-name "elfeed.org" org-directory))))
- elfeed-goodies
(use-package elfeed-goodies :after elfeed :ensure t :init (elfeed-goodies/setup))
- elfeed-org
ox-hugo external
Blogging tools
(use-package ox-hugo :ensure t :if (executable-find "hugo") :after ox)
- org-capture template for blog
(defun my/create-blog-capture-file () "Create a subdirectory and `org-mode' file under `+config/blog-directory'." (interactive) (let* ((name (read-string "slug: ")) (content-dir (expand-file-name "content-org/" +config/blog-directory))) (unless (file-directory-p (expand-file-name name content-dir)) (make-directory (expand-file-name name content-dir))) (expand-file-name (concat name ".org") (expand-file-name name content-dir)))) (add-to-list 'org-capture-templates '("h" "Hugo Post" plain (file my/create-blog-capture-file) "#+options: ':nil -:nil ^:nil num:nil toc:nil #+author: %n #+title: %^{Title} #+description: #+date: %t #+hugo_categories: %^{Categories|misc|desktop|emacs|learning} #+hugo_tags: %^{Tags} #+hugo_auto_set_lastmod: t #+hugo_section: posts #+hugo_base_dir: ../../ #+language: en #+startup: inlineimages * %?" :jump-to-captured t))
pass external
(use-package password-store :ensure t :demand t :config (setq password-store-password-length 12) (setq auth-sources '(password-store "~/.authinfo.gpg" "~/.netrc"))) (use-package password-store-otp :ensure t :defer t :after password-store) (use-package pass :ensure t :defer t :config (with-eval-after-load 'evil-collection (evil-collection-pass-setup))) (use-package auth-source-pass :ensure nil :init (auth-source-pass-enable))
Elpaca complains about different of pass version installed. But it installed just fine.
pinentry external
(use-package pinentry :ensure t :defer t :config (pinentry-start))
fzf external
(use-package fzf :if (executable-find "fzf") :ensure t :config (with-eval-after-load 'evil (setq my/fzf-map (make-sparse-keymap)) (evil-define-key* nil my/file-map (kbd "z") (cons "fzf" my/fzf-map)) (evil-define-key* nil my/fzf-map "b" '("fzf find in buffer" . fzf-find-in-buffer) "f" '("fzf" . fzf-find-file) "d" '("fzf dir" . fzf-directory) "r" '("fzf recentf" . fzf-recentf)) (evil-define-key* nil my/search-map "z" '("fzf-grep" . fzf-grep)) (evil-define-key* nil my/buffer-map "z" '("fzf switch buffer" . fzf-switch-buffer)) (with-eval-after-load 'projectile (evil-define-key* nil projectile-command-map "z" '("fzf projectile" . fzf-projectile)))) (setq fzf/args "-x --color dark --print-query --margin=1,0 --no-hscroll" fzf/executable "fzf" fzf/git-grep-args "-i --line-number %s" ;; command used for `fzf-grep-*` functions ;; example usage for ripgrep: ;; fzf/grep-command "rg --no-heading -nH" fzf/grep-command "rg --no-heading -nH" ;; If nil, the fzf buffer will appear at the top of the window fzf/position-bottom t fzf/window-height 15))
rg external
Packaged as ripgrep
in archlinux. The :if
keyword won't load this if the executable not found.
(use-package rg :ensure t :if (executable-find "rg") :defer t :config (with-eval-after-load 'evil-collection (evil-collection-rg-setup)))
mastodon external
(use-package mpv :ensure t) (use-package emojify :ensure t :hook (after-init . global-emojify-mode)) (use-package mastodon :ensure t :config (mastodon-discover) (with-eval-after-load 'evil (setq my/mastodon-map (make-sparse-keymap)) (evil-define-key* nil my/c-map "m" (cons "Mastodon" my/mastodon-map)) (evil-define-key* nil my/mastodon-map "m" '("Mastodon" . mastodon) "t" '("new toot" . mastodon-toot) "N" '("Get notification" . mastodon-notifications-get) "O" '("My Profile" . mastodon-profile-my-profile)) (evil-define-key '(normal visual) mastodon-mode-map "n" '("next item" . mastodon-tl-goto-next-item) "p" '("previous item" . mastodon-tl-goto-prev-item) (kbd "<tab>") '("next tab item" . mastodon-tl-next-tab-item) (kbd "S-<tab>") '("previous tab item" . mastodon-tl-next-tab-item) (kbd "M-n") '("next tab item" . mastodon-tl-next-tab-item) (kbd "M-p") '("previous tab item" . mastodon-tl-previous-tab-item) "F" '("Federated timeline" . mastodon-tl-get-federated-timeline) "H" '("Home timeline" . mastodon-tl-get-home-timeline) "L" '("Local timeline" . mastodon-tl-get-local-timeline) "N" '("Notification" . mastodon-notifications-get) "@" '("Mention notification" . mastodon-notifications-get-mentions) "u" '("Update current timeline" . mastodon-tl-update) "T" '("Open thread for toot at point" . mastodon-tl-thread) "#" '("Prompt for tag and open its timeline" . mastodon-tl-get-tag-timeline) "A" '("Open author profile of toot at point" . mastodon-profile-get-toot-author) "P" '("Open profile of user attached to toot at point" . mastodon-profile-show-user) "O" '("View own profile" . mastodon-profile-my-profile) "U" '("update your profile bio note" . mastodon-profile-update-user-profile-note) ";" '("view instance description for toot at point" . mastodon-views-view-instance-description) ":" '("current user settings" . mastodon-user-settings) (kbd "C-:") '("notifications policy options" . mastodon-notifications-policy) "," '("List the favouriters of toot at point" . mastodon-toot-list-favouriters) "." '("List the boosters of toot at point" . mastodon-toot-list-boosters) "/" '("Switch to a live mastodon buffer" . mastodon-switch-to-buffer) "\\" '("prompt for an instance domain and view its local timeline" . mastodon-tl-get-remote-local-timeline) "Z" '("Report the author of the toot at point to your instance moderators" . mastodon-tl-report-to-mods) "s" '("Prompt for a search QUERY" . mastodon-search-query) "t" '("Update instance with new toot." . mastodon-toot) "b" '("Boost/unboost toot" . mastodon-toot-toggle-boost) "f" '("Favourite/unfavourite toot" . mastodon-toot-toggle-favourite) "k" '("Bookmark or unbookmark toot" . mastodon-toot-toggle-bookmark) "r" '("reply to toot" . mastodon-toot-reply) "v" '("vote on a poll at point" . mastodon-tl-poll-vote) "C" '("Copy URL of toot at point" . mastodon-toot-copy-toot-url) (kbd "C-<return>") '("play video/gif at point" . mastodon-tl-mpv-play-video-at-point) "e" '("Edit user toot at point" . mastodon-toot-edit-toot-at-point) "i" '("Pin or unpin user’s toot at point" . mastodon-toot-pin-toot-toggle) "i" '("Delete user’s toot at point synchronously" . mastodon-toot-delete-toot) "!" '("Toggle the folding status of the toot at point" . mastodon-tl-fold-post-toggle) "W" '("Follow user" . mastodon-tl-follow-user) (kbd "S-C-w") '("Unollow user" . mastodon-tl-unfollow-user) "M" '("Mute user" . mastodon-tl-mute-user) (kbd "S-C-m") '("unmute user" . mastodon-tl-unmute-user) "B" '("Block user" . mastodon-tl-block-user) (kbd "S-C-b") '("unblock user" . mastodon-tl-unblock-user) "q" '("Kill buffer" . kill-current-buffer) "Q" '("Kill window" . mastodon-kill-window) )) (setopt mastodon-instance-url "https://mastodon.social" mastodon-active-user "alexforsale"))
There's lot of keybindings
emms external
Autoload these functions.
;;;###autoload (defun +emms/notification () (interactive) (let ((artist (string-trim (shell-command-to-string "playerctl -p mpd metadata xesam:artist"))) (album (string-trim (shell-command-to-string "playerctl -p mpd metadata xesam:album"))) (cover (string-trim (shell-command-to-string "playerctl -p mpd metadata mpris:artUrl"))) (title (string-trim (shell-command-to-string "playerctl -p mpd metadata --format '{{title}}'")))) (call-process "notify-send" nil nil nil "-a" "emms" "-t" "2600" "-r" "27073" (format "%s" title) (format "<span color='#fdf6e3'>%s </span>\n<span color='#268bd2'>%s</span>" artist album) "-i" cover)))
(use-package emms :ensure t :config :if (executable-find "mpd") (require 'emms-setup) (require 'emms-player-mpd) (emms-all) (setopt emms-player-mpd-music-directory (expand-file-name "Music" (getenv "HOME"))) (setopt emms-player-list '(emms-player-mpd)) (setopt emms-info-functions 'emms-info-mpd) (add-hook 'emms-player-started-hook #'+emms/notification nil) (emms-default-players) (emms-cache-set-from-mpd-all) (emms-player-mpd-connect) ;(emms-mpris-enable) (with-eval-after-load 'evil (evil-collection-emms-setup)) ;; keybindings (with-eval-after-load 'evil (setopt my/emms-map (make-sparse-keymap)) (evil-define-key nil my/open-map (kbd "E") (cons "EMMS" my/emms-map)) (evil-define-key* nil my/emms-map "e" '("emms" . emms)) ) ;; from `doom' (defun +emms/mpd-start-music-daemon () (interactive) (start-process "mpd" nil "mpd") (+emms/mpc-update-database) (emms-player-mpd-connect) (emms-cache-set-from-mpd-all) (message "MPD Started!")) (defun +emms/mpd-kill-music-daemon () (interactive) (emms-stop) (call-process "mpd" nil nil nil "--kill") (message "MPD Killed!")) (defun +emms/mpc-update-database () (interactive) (call-process "mpc" nil nil nil "update") (message "MPD Database Updated!")) (defun +emms/mpd-restart-music-daemon () (interactive) (+emms/mpd-kill-music-daemon) (+emms/mpd-start-music-daemon) (message "MPD Restarted!")))
notmuch
(use-package notmuch :if (executable-find "notmuch") :ensure t :defer t :commands (notmuch) :hook (message-setup . mml-secure-sign-pgpmime) :config (global-set-key (kbd "<XF86Mail>") 'notmuch) (setq notmuch-fcc-dirs nil notmuch-search-result-format '(("date" . "%12s ") ("count" . "%-7s ") ("authors" . "%-30s ") ("subject" . "%-72s ") ("tags" . "(%s)")) notmuch-tag-formats '(("unread" (propertize tag 'face 'notmuch-tag-unread)) ("flagged" (propertize tag 'face 'notmuch-tag-flagged) (notmuch-tag-format-image-data tag (notmuch-tag-star-icon)))) notmuch-tagging-keys '(("a" notmuch-archive-tags "Archive") ("u" notmuch-show-mark-read-tags "Mark read") ("f" ("+flagged") "Flag") ("s" ("+spam" "-inbox") "Mark as spam") ("d" ("+deleted" "-inbox") "Delete")) notmuch-saved-searches '((:name "flagged" :query "tag:flagged" :key "f") (:name "archive" :query "tag:archive" :key "i") (:name "inbox" :query "tag:inbox" :key "i") (:name "sent" :query "tag:sent" :key "s") (:name "drafts" :query "tag:draft" :key "d") (:name "all mail" :query "*" :key "a") (:name "unread" :query "tag:unread" :key "u") (:name "zum" :query "tag:zum" :key "z") (:name "mkn" :query "tag:mkn" :key "c") (:name "gmail" :query "tag:gmail" :key "g") (:name "hotmail" :query "tag:hotmail" :key "h") (:name "yahoo" :query "tag:yahoo" :key "h") (:name "ymail" :query "tag:ymail" :key "m") (:name "Today" :query "date:today AND NOT tag:spam AND NOT tag:bulk" :key "T" :search-type 'tree :sort-order 'newest-first) (:name "This Week" :query "date:weeks AND NOT tag:spam AND NOT tag:bulk" :key "W" :search-type 'tree :sort-order 'newest-first) (:name "This Month" :query "date:months AND NOT tag:spam AND NOT tag:bulk" :key "M" :search-type 'tree :sort-order 'newest-first) (:name "flagged" :query "tag:flagged AND NOT tag:spam AND NOT tag:bulk" :key "f" :search-type 'tree :sort-order 'newest-first) (:name "spam" :query "tag:spam")) notmuch-archive-tags '("-inbox" "-unread" "+archive")) (setq-default notmuch-search-oldest-first nil) (if (executable-find "gpg2") (setq notmuch-crypto-gpg-program "gpg2") (setq notmuch-crypto-gpg-program "gpg")) (setq notmuch-crypto-process-mime t mml-secure-openpgp-sign-with-sender t) (define-key notmuch-show-mode-map "S" (lambda () "Mark message as spam" (interactive) (notmuch-show-tag (list +spam -new))))) (use-package notmuch-indicator :if (executable-find "notmuch") :ensure t :config (setq notmuch-indicator-args '((:terms "tag:unread and tag:inbox" :label "U" :label-face success))) (notmuch-indicator-mode)) (use-package consult-notmuch :after consult :ensure t :if (executable-find "notmuch") :config (add-to-list 'consult-buffer-sources 'consult-notmuch-buffer-source)) (use-package ol-notmuch :ensure t :if (executable-find "notmuch")) (use-package notmuch-maildir :ensure t :if (executable-find "notmuch") :config (notmuch-maildir-inject-section)) (use-package message :ensure nil :if (executable-find "notmuch") :custom (message-directory (expand-file-name ".mail" (getenv "HOME"))) (message-sendmail-envelope-from 'header)) (use-package sendmail :ensure nil :if (executable-find "notmuch") :custom (mail-specify-envelope-from t) (mail-envelope-from 'header) (send-mail-function 'sendmail-send-it) (sendmail-program (executable-find "msmtp")))
Programming languages
javascript builtin
(use-package js :ensure nil :mode ("\\.[mc]?js\\'" . js-mode) :mode ("\\.es6\\'" . js-mode) :mode ("\\.pac\\'" . js-mode) :config (with-eval-after-load 'eglot (add-hook 'js-mode-hook #'eglot-ensure) (add-hook 'js-ts-mode-hook #'eglot-ensure)) (with-eval-after-load 'smartparens (sp-local-pair 'js-mode "<" nil :actions :rem) (sp-local-pair 'js-ts-mode "<" nil :actions :rem)) (setopt js-chain-indent t))
- nodejs-repl external
(use-package nodejs-repl :ensure t)
- typescript-mode external
(use-package typescript-mode :ensure t :interpreter "ts-node" :config (with-eval-after-load 'eglot (add-hook 'js-ts-mode-hook #'eglot-ensure) (add-hook 'typescript-ts-mode-hook #'eglot-ensure)))
- rjsx external
(use-package rjsx-mode :ensure t)
kdl-mode external
(use-package kdl-mode :ensure t)
Markdown external
(use-package markdown-mode :ensure t :defer t :mode ("README\\.md\\'" . gfm-mode) :init (with-eval-after-load 'eglot (add-hook 'markdown-mode-hook 'eglot-ensure)) (when (executable-find "markdown") (setq markdown-command "markdown")) (when (executable-find "multimarkdown") (setq markdown-command "multimarkdown")) :config (with-eval-after-load 'evil-collection (evil-collection-markdown-mode-setup)) (with-eval-after-load 'lsp-mode (add-hook 'markdown-mode-hook #'lsp-deferred)) (setq markdown-fontify-code-blocks-natively t)) (use-package poly-markdown :ensure t :init :mode (("README\\.md\\'" . gfm-mode) ("\\.md$" . markdown-mode) ("\\.markdown$" . markdown-mode))) (use-package edit-indirect :ensure t :after markdown-mode)
Python builtin
(use-package python :ensure nil :mode ("[./]flake8\\'" . conf-mode) :mode ("/Pipfile\\'" . conf-mode) :config (with-eval-after-load 'evil-collection (evil-collection-python-setup)) (with-eval-after-load 'eglot (add-hook 'python-mode-hook #'eglot-ensure) (add-hook 'python-ts-mode-hook #'eglot-ensure) (add-to-list 'eglot-server-programs `(python-mode . ,(eglot-alternatives '(("pyright-langserver" "--stdio") "jedi-language-server" "pylsp"))))) (with-eval-after-load 'lsp-mode (add-hook 'python-mode-hook #'lsp-deferred) (add-hook 'python-mode-hook #'lsp-ui-mode) (add-hook 'python-mode-hook #'lsp-ui-doc-mode) (add-hook 'python-mode-local-vars-hook '(lambda () (lsp-deferred)))) (if (executable-find "ipython") (setq python-interpreter (executable-find "ipython")) (setq python-interpreter (executable-find "python3"))) (when (featurep 'projectile) (add-to-list 'projectile-globally-ignored-directories "^\\.venv$")) (let ((directories `("/usr/bin/" "/usr/local/bin/" "/opt/bin" ,(expand-file-name ".local/bin/" (getenv "HOME"))))) (dolist (directory directories) (when (file-directory-p directory) (add-to-list 'python-shell-exec-path directory)))) (setq python-indent-guess-indent-offset nil python-shell-completion-native-enable nil python-shell-exec-path (list "/usr/bin/" "/usr/local/bin" (expand-file-name ".local/bin/" (getenv "HOME"))) python-indent-guess-indent-offset-verbose nil) (when (featurep 'lsp-mode) (setq lsp-pylsp-plugins-rope-completion-enabled t ; needs python-rope package lsp-pylsp-plugins-mccabe-enabled t ; needs python-mccabe package lsp-ruff-lsp-python-path (executable-find "python3")) (when (executable-find "black") (setq lsp-pylsp-plugins-black-enabled t)) (when (executable-find "autopep8") (setq lsp-pylsp-plugins-autopep8-enabled t)) (when (executable-find "flake8") (setq lsp-pylsp-plugins-flake8-enabled t)) (when (executable-find "pycodestyle") (setq lsp-pylsp-plugins-pycodestyle-enabled t)) (when (executable-find "pydocstyle") (setq lsp-pylsp-plugins-pydocstyle-enabled t)) (when (executable-find "pylint") (setq lsp-pylsp-plugins-pylint-enabled t)) (when (executable-find "pyflakes") (setq lsp-pylsp-plugins-pyflakes-enabled t)) (when (executable-find "yapf") (setq lsp-pylsp-plugins-yapf-enabled t))) (when (featurep 'flycheck) (setq flycheck-python-mypy-executable (executable-find "mypy") flycheck-python-flake8-executable (executable-find "flake8") flycheck-python-pylint-executable (executable-find "pylint") flycheck-python-pyright-executable (executable-find "pyright"))) (with-eval-after-load 'tree-sitter (add-hook 'python-mode-local-vars-hook #'tree-sitter! 'append)) (defun +python-executable-find (exe) "Resolve the path to the EXE executable. Tries to be aware of your active conda/pipenv/virtualenv environment, before falling back on searching your PATH." (if (file-name-absolute-p exe) (and (file-executable-p exe) exe) (let ((exe-root (format "bin/%s" exe))) (cond ((when python-shell-virtualenv-root (let ((bin (expand-file-name exe-root python-shell-virtualenv-root))) (if (file-exists-p bin) bin)))) ((when (require 'conda nil t) (let ((bin (expand-file-name (concat conda-env-current-name "/" exe-root) (conda-env-default-location)))) (if (file-executable-p bin) bin)))) ((when-let (bin (projectile-locate-dominating-file default-directory "bin/python")) (setq-local doom-modeline-python-executable (expand-file-name "bin/python" bin)))) ((executable-find exe)))))) (defun +python/open-repl () "Open the Python REPL." (interactive) (require 'python) (unless python-shell-interpreter (user-error "`python-shell-interpreter' isn't set")) (pop-to-buffer (process-buffer (let ((dedicated (bound-and-true-p python-shell-dedicated))) (if-let* ((pipenv (+python-executable-find "pipenv")) (pipenv-project (pipenv-project-p))) (let ((default-directory pipenv-project) (python-shell-interpreter-args (format "run %s %s" python-shell-interpreter python-shell-interpreter-args)) (python-shell-interpreter pipenv)) (run-python nil dedicated t)) (run-python nil dedicated t)))))) (defun +python/open-ipython-repl () "Open an IPython REPL." (interactive) (require 'python) (let ((python-shell-interpreter (or (+python-executable-find (car +python-ipython-command)) "ipython")) (python-shell-interpreter-args (string-join (cdr +python-ipython-command) " "))) (+python/open-repl))) (defun +python/open-jupyter-repl () "Open a Jupyter console." (interactive) (require 'python) (add-to-list 'python-shell-completion-native-disabled-interpreters "jupyter") (let ((python-shell-interpreter (or (+python-executable-find (car +python-jupyter-command)) "jupyter")) (python-shell-interpreter-args (string-join (cdr +python-jupyter-command) " "))) (+python/open-repl))) (defun +python/optimize-imports () "organize imports" (interactive) (pyimport-remove-unused) (py-isort-buffer)) (defun +python-use-correct-flycheck-executables-h () "Use the correct Python executables for Flycheck." (let ((executable python-shell-interpreter)) (save-excursion (goto-char (point-min)) (save-match-data (when (or (looking-at "#!/usr/bin/env \\(python[^ \n]+\\)") (looking-at "#!\\([^ \n]+/python[^ \n]+\\)")) (setq executable (substring-no-properties (match-string 1)))))) ;; Try to compile using the appropriate version of Python for ;; the file. (setq-local flycheck-python-pycompile-executable executable) ;; We might be running inside a virtualenv, in which case the ;; modules won't be available. But calling the executables ;; directly will work. (setq-local flycheck-python-pylint-executable "pylint") (setq-local flycheck-python-flake8-executable "flake8"))) (defun +python-pyenv-mode-set-auto-h () "Set pyenv-mode version from buffer-local variable." (when (eq major-mode 'python-mode) (when (not (local-variable-p '+pyenv--version)) (make-local-variable '+pyenv--version) (setq +pyenv--version (+python-pyenv-read-version-from-file))) (if +pyenv--version (pyenv-mode-set +pyenv--version) (pyenv-mode-unset)))) (defun +python-pyenv-read-version-from-file () "Read pyenv version from .python-version file." (when-let (root-path (projectile-locate-dominating-file default-directory ".python-version")) (let* ((file-path (expand-file-name ".python-version" root-path)) (version (with-temp-buffer (insert-file-contents-literally file-path) (string-trim (buffer-string))))) (if (member version (pyenv-mode-versions)) version ;; return. (message "pyenv: version `%s' is not installed (set by `%s')." version file-path))))) (with-eval-after-load 'evil (setq my/python-map (make-sparse-keymap)) (evil-define-key* '(normal visual) python-mode-map (kbd "<leader>m") (cons "python-mode" my/python-map)) (evil-define-key* '(normal visual) python-ts-mode-map (kbd "<leader>m") (cons "python-mode" my/python-map)) (setq my/python-send-map (make-sparse-keymap)) (setq my/python-repl-map (make-sparse-keymap)) (evil-define-key* nil my/python-map (kbd "s") (cons "send to repl" my/python-send-map)) (evil-define-key* nil my/python-map "c" '("check" . python-check) "i" '("fix import" . python-fix-imports) "r" '("open python repl" . +python/open-repl)) (evil-define-key* nil my/python-send-map "b" '("send buffer" . python-shell-send-buffer) "B" '("send block" . python-shell-send-block) "d" '("send defun" . python-shell-send-defun) "r" '("send region" . python-shell-send-region) "s" '("send statement" . python-shell-send-statement))))
Rust external
For rust there are two options: rust-mode, or rustic, I chose the latter since it's basically rust-mode
with additional features.
(use-package rust-mode :ensure t :init (add-hook 'rustic-mode-hook (lambda () flymake-mode -1)) (setq rust-mode-treesitter-derive t)) (use-package rustic :ensure t :after rust-mode :init (with-eval-after-load 'rustic-flycheck (remove-hook 'rustic-mode-hook #'flycheck-mode) (remove-hook 'rustic-mode-hook #'flymake-mode) (remove-hook 'rustic-mode-hook #'flymake-mode-off) (remove-hook 'flycheck-mode-hook #'rustic-flycheck-setup)) (add-hook 'rustic-mode-hook (lambda () flymake-mode -1)) (with-eval-after-load 'eglot (setq rustic-lsp-client 'eglot rustic-lsp-setup-p nil) (add-hook 'eglot--managed-mode-hook (lambda () (flymake-mode +1))) (add-to-list 'eglot-server-programs '((rust-ts-mode rust-mode rustic-mode) ("rust-analyzer" :initializationOptions (:check (:command "clippy")))))) (with-eval-after-load 'org-src (add-to-list 'org-src-lang-modes '("rust" . rustic))) :config (setq rustic-format-on-save nil) (when (executable-find "rustup") (setq rustic-analyzer-command `(,(executable-find "rustup") "run" "stable" "rust-analyzer"))) (setq rustic-indent-method-chain t) (setq rustic-babel-format-src-block nil rustic-format-trigger nil) (with-eval-after-load 'evil (setq my/rustic-map (make-sparse-keymap)) (setq my/cargo-map (make-sparse-keymap)) (evil-define-key* '(normal visual) rustic-mode-map (kbd "<leader>m") (cons "rustic-mode" my/rustic-map)) (evil-define-key* nil my/rustic-map (kbd "c") (cons "Cargo" my/cargo-map)) (evil-define-key* nil my/cargo-map "a" '("cargo add" . rustic-cargo-add) "B" '("cargo build" . rustic-cargo-build) "b" '("cargo bench" . rustic-cargo-bench) "c" '("cargo check" . rustic-cargo-check) "C" '("cargo clippy" . rustic-cargo-clippy) "F" '("cargo fix" . rustic-rustfix) "f" '("cargo format workspace" . rustic-cargo-fmt) "i" '("cargo initialize" . rustic-cargo-init) "o" '("cargo outdated" . rustic-cargo-outdated) "t" '("cargo test" . rustic-cargo-test) "r" '("cargo run" . rustic-cargo-run)) (with-eval-after-load 'consult (evil-define-key* nil my/rustic-map "m" '("consult flymake" . consult-flymake))) (with-eval-after-load 'eglot (evil-define-key* nil my/rustic-map "a" '("eglot code action" . eglot-code-actions) "I" '("eglot organize Import" . eglot-code-action-organize-imports) "r" '("eglot rename" . eglot-rename)))) :custom (rustic-analyzer-command '("rustup" "run" "stable" "rust-analyzer")) (rustic-cargo-use-last-stored-arguments t))
toml-mode external
(use-package toml-mode :ensure t :demand t :mode ("\\.toml\\'" . toml-mode) :config (with-eval-after-load 'eglot (add-hook 'toml-mode-hook #'eglot-ensure) (add-hook 'toml-ts-mode-hook #'eglot-ensure) (add-to-list 'eglot-server-programs '(toml-ts-mode . ("taplo" "lsp" "stdio")))) (with-eval-after-load 'lsp-mode (add-hook 'toml-mode-hook #'lsp-deferred) (setq lsp-toml-command (executable-find "taplo")) (add-to-list 'lsp-language-id-configuration '("\\.toml$" . "toml"))))
Install taplo-cli from archlinux package or using cargo with cargo install taplo-cli --features lsp
.
web-mode
- emmet external
(use-package emmet-mode :ensure t :hook (sgml-mode css-mode html-mode web-mode haml-mode nxml-mode rjsx-mode reason-mode) :config (setopt emmet-move-cursor-between-quotes t emmet-self-closing-tag-style " /") (when (require 'yasnippet nil t) (add-hook 'emmet-mode-hook #'yas-minor-mode-on)) (with-eval-after-load 'evil (evil-define-key 'visual emmet-mode-keymap (kbd "<tab>") 'emmet-wrap-with-markup) (evil-define-key '(normal insert emacs) emmet-mode-keymap (kbd "<tab>") 'emmet-expand-line)))
- web-mode external
(use-package web-mode :ensure t :mode "\\.[px]?html?\\'" :mode "\\.\\(?:tpl\\|blade\\)\\(?:\\.php\\)?\\'" :mode "\\.erb\\'" :mode "\\.[lh]?eex\\'" :mode "\\.jsp\\'" :mode "\\.as[cp]x\\'" :mode "\\.ejs\\'" :mode "\\.hbs\\'" :mode "\\.mustache\\'" :mode "\\.svelte\\'" :mode "\\.twig\\'" :mode "\\.jinja2?\\'" :mode "\\.eco\\'" :mode "wp-content/themes/.+/.+\\.php\\'" :mode "templates/.+\\.php\\'" :init (add-to-list 'auto-mode-alist '("\\.vue\\'" . web-mode) 'append) :mode "\\.vue\\'" :hook (web-mode . eglot-ensure) :config (setopt web-mode-enable-html-entities-fontification t web-mode-auto-close-style 1) (with-eval-after-load 'smartparens (defun +web-is-auto-close-style-3 (_id action _context) (and (eq action 'insert) (eq web-mode-auto-close-style 3))) (sp-local-pair 'web-mode "<" ">" :unless '(:add +web-is-auto-close-style-3)) (setopt web-mode-enable-auto-quoting nil web-mode-enable-auto-pairing t) (dolist (alist web-mode-engines-auto-pairs) (setcdr alist (cl-loop for pair in (cdr alist) unless (string-match-p "^[a-z-]" (cdr pair)) collect (cons (car pair) (string-trim-right (cdr pair) "\\(?:>\\|]\\|}\\)+\\'"))))) (cl-callf2 delq nil web-mode-engines-auto-pairs)) (add-to-list 'web-mode-engines-alist '("elixir" . "\\.eex\\'")) (add-to-list 'web-mode-engines-alist '("phoenix" . "\\.[lh]eex\\'")) (setf (alist-get "javascript" web-mode-comment-formats nil nil #'equal) "//"))
- css-mode builtin
(use-package css-mode :ensure nil :hook (css-mode . rainbow-mode) :hook (css-mode . eglot-ensure))
- scss-mode external
(use-package scss-mode :ensure t :mode "\\.scss\\'" :hook (scss-mode . rainbow-mode) :hook (scss-mode . eglot-ensure) :init (with-eval-after-load 'flymake (require 'flymake-proc nil t)))
yaml-mode external
(use-package yaml-mode :ensure t :config (with-eval-after-load 'eglot (add-hook 'yaml-mode-hook #'eglot-ensure) (add-hook 'yaml-ts-mode-hook #'eglot-ensure) (setq-default eglot-workspace-configuration '(:yaml (:format (:enable t) :validate t :hover t :completion t :schemas (https://raw.githubusercontent.com/my-user/my-project/project.schema.yml ["project.yml"] https://json.schemastore.org/yamllint.json ["/*.yml"]) :schemaStore (:enable t))))) (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)) (add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-mode)) (setq tab-width 2) ;; (when (treesit-available-p) ;; (add-to-list 'major-mode-remap-alist '(yaml-mode . yaml-ts-mode))) :hook ((yaml-mode . (lambda () (define-key yaml-mode-map "\C-m" 'newline-and-indent))) (yaml-mode . (lambda () (run-hooks 'prog-mode-hook)))))
Needs the package yaml-language-server
, can be obtain from the archlinux repository.
yuck-mode
(use-package yuck-mode :ensure t)
Completion
Vertico external
(use-package vertico :ensure t :hook (rfn-eshadow-update-overlay . vertico-directory-tidy) :custom ;; (vertico-scroll-margin 0) ;; Different scroll margin (vertico-count 15) ;; Show more candidates (vertico-resize nil) ;; Grow and shrink the Vertico minibuffer (vertico-cycle t) ;; Enable cycling for `vertico-next/previous' :config (advice-add #'tmm-add-prompt :after #'minibuffer-hide-completions) (keymap-set vertico-map "?" #'minibuffer-completion-help) :init (vertico-mode)) ;; Emacs minibuffer configurations. (use-package emacs :custom ;; Enable context menu. `vertico-multiform-mode' adds a menu in the minibuffer ;; to switch display modes. (context-menu-mode t) ;; Support opening new minibuffers from inside existing minibuffers. (enable-recursive-minibuffers t) ;; Hide commands in M-x which do not work in the current mode. Vertico ;; commands are hidden in normal buffers. This setting is useful beyond ;; Vertico. (read-extended-command-predicate #'command-completion-default-include-p) ;; Do not allow the cursor in the minibuffer prompt (minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)))
- Extensions
- vertico-directory
(use-package vertico-directory :after vertico :ensure nil ;; More convenient directory navigation commands :bind (:map vertico-map ("RET" . vertico-directory-enter) ("DEL" . vertico-directory-delete-char) ("M-DEL" . vertico-directory-delete-word)) ;; Tidy shadowed file names :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
- vertico-quick
(use-package vertico-quick :after vertico :ensure nil :bind (:map vertico-map ("M-q" . vertico-quick-insert) ("C-q" . vertico-quick-exit)))
- vertico-multiform
(use-package vertico-multiform :ensure nil :init (vertico-multiform-mode) :config (setq vertico-multiform-commands `((describe-symbol (vertico-sort-function . vertico-sort-alpha)) (consult-outline buffer ,(lambda (_) (text-scale-set -1))) (org-ctrl-c-ctrl-c flat))) (defun sort-directories-first (files) (setq files (vertico-sort-history-length-alpha files)) (nconc (seq-filter (lambda (x) (string-suffix-p "/" x)) files) (seq-remove (lambda (x) (string-suffix-p "/" x)) files))) (setq vertico-multiform-categories '((symbol (vertico-sort-function . vertico-sort-alpha)) (file (vertico-sort-function . sort-directories-first)))))
- vertico-directory
orderless external
(use-package orderless :ensure t :init ;; Configure a custom style dispatcher (see the Consult wiki) ;; (setq orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch) ;; orderless-component-separator #'orderless-escapable-split-on-space) (setq completion-styles '(orderless basic substring partial-completion) completion-category-defaults nil completion-category-overrides '((file (styles orderless partial-completion))) orderless-component-separator #'orderless-escapable-split-on-space) (set-face-attribute 'completions-first-difference nil :inherit nil))
partial-completion
and flex
are built-ins completion-styles.
consult external
- consult main package
(use-package consult :ensure t :demand t :hook (completion-list-mode . consult-preview-at-point-mode) :init ;; Optionally configure the register formatting. This improves the register ;; preview for `consult-register', `consult-register-load', ;; `consult-register-store' and the Emacs built-ins. (setq register-preview-delay 0.5 register-preview-function #'consult-register-format) ;; Optionally tweak the register preview window. ;; This adds thin lines, sorting and hides the mode line of the window. (advice-add #'register-preview :override #'consult-register-window) ;; Use Consult to select xref locations with preview (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) ;; Configure other variables and modes in the :config section, ;; after lazily loading the package. :config ;; Optionally configure preview. The default value ;; is 'any, such that any key triggers the preview. ;; (setq consult-preview-key 'any) ;; (setq consult-preview-key "M-.") ;; (setq consult-preview-key '("S-<down>" "S-<up>")) ;; For some commands and buffer sources it is useful to configure the ;; :preview-key on a per-command basis using the `consult-customize' macro. (consult-customize consult-theme :preview-key '(:debounce 0.2 any) consult-ripgrep consult-git-grep consult-grep consult-bookmark consult-recent-file consult-xref consult--source-bookmark consult--source-file-register consult--source-recent-file consult--source-project-recent-file ;; :preview-key "M-." :preview-key '(:debounce 0.4 any)) ;; Optionally configure the narrowing key. ;; Both < and C-+ work reasonably well. (setq consult-narrow-key "<") ;; "C-+" ;; Optionally make narrowing help available in the minibuffer. ;; You may want to use `embark-prefix-help-command' or which-key instead. ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help) ;; By default `consult-project-function' uses `project-root' from project.el. ;; Optionally configure a different project root function. ;;;; 1. project.el (the default) ;;(setq consult-project-function #'consult--default-project--function) ;;;; 2. vc.el (vc-root-dir) ;; (setq consult-project-function (lambda (_) (vc-root-dir))) ;;;; 3. locate-dominating-file ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git"))) ;;;; 4. projectile.el (projectile-project-root) (with-eval-after-load 'projectile (autoload 'projectile-project-root "projectile") (setq consult-project-function (lambda (_) (projectile-project-root)))) ;;;; 5. No project support ;; (setq consult-project-function nil)) (setq consult-line-numbers-widen t consult-async-min-input 2 consult-async-refresh-delay 0.15 consult-async-input-throttle 0.2 consult-async-input-debounce 0.1) (with-eval-after-load 'evil (evil-define-key* '(normal visual) 'global (kbd "<leader>.") '("Store register" . consult-register-store) (kbd "<leader> C-.") '("Load register" . consult-register-load)) (evil-define-key* nil my/file-map "r" '("recent files" . consult-recent-file)) (evil-define-key* nil my/buffer-map "b" '("buffers" . consult-buffer) "B" '("project buffers" . consult-project-buffer)) (evil-define-key* nil my/open-map "O" '("Open agenda files" . consult-org-agenda)) (with-eval-after-load 'org (evil-define-key* nil my/org-mode-map "O" '("search heading" . consult-org-heading))) (evil-define-key* nil my/search-map "a" '("grep search" . consult-grep) "b" '("search bookmarks" . consult-bookmark) "B" '("search global mark" . consult-global-mark) "d" '("search directory" . consult-dir) "f" '("consult find" . consult-find) "F" '("focus line" . consult-focus-line) "h" '("isearch history" . consult-isearch-history) "m" '("search mark" . consult-mark) "i" '("imenu" . consult-imenu) "o" '("search outline" . consult-outline) "s" '("search line" . consult-line) "S" '("search multi line" . consult-line-multi) "\"" '("search register" . consult-register)) (evil-define-key* '(normal visual) 'global (kbd "M-y") 'consult-yank-pop) (with-eval-after-load 'consult-notmuch (evil-define-key* nil my/search-map "M" '("search mail" . consult-notmuch))) (cond ((executable-find "fd") (evil-define-key* nil my/search-map "A" '("fd search" . consult-fd))) (t (evil-define-key* nil my/search-map "A" '("find search" . consult-find)))) (when (executable-find "rg") (evil-define-key* nil my/search-map "r" '("search ripgrep" . consult-ripgrep)))) (keymap-set isearch-mode-map "M-e" #'consult-isearch-history) (keymap-set isearch-mode-map "M-l" #'consult-line) (keymap-set isearch-mode-map "M-L" #'consult-line-multi) (keymap-set minibuffer-local-map "M-s" #'consult-history) (keymap-set minibuffer-local-map "M-r" #'consult-history) (global-set-key [remap Info-search] 'consult-info) (global-set-key [remap yank-pop] 'consult-yank-pop) (global-set-key [remap bookmark-jump] 'consult-bookmark) (global-set-key [remap evil-show-marks] 'consult-mark) (global-set-key [remap evil-show-registers] 'consult-register) (global-set-key [remap goto-line] 'consult-goto-line) (global-set-key [remap imenu] 'consult-imenu) (global-set-key [remap locate] 'consult-locate) (global-set-key [remap load-theme] 'consult-theme) (global-set-key [remap man] 'consult-man) (global-set-key [remap recentf-open-files] 'consult-recent-file) (global-set-key [remap switch-to-buffer] 'consult-buffer) (global-set-key [remap switch-to-buffer-other-frame] 'consult-buffer-other-frame) (global-set-key [remap switch-to-buffer-other-window] 'consult-buffer-other-window))
Apart from the
consult-projectile-function
, this is pretty much the default. - consult-dir
(use-package consult-dir :ensure t :bind (:map vertico-map ("C-x C-d" . consult-dir) ("C-x C-j" . consult-dir-jump-file)))
embark external
(use-package embark :ensure t :defer t :bind (("C-." . embark-act) ;; pick some comfortable binding ("C-;" . embark-dwim) ;; good alternative: M-. ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' :init ;; Optionally replace the key help with a completing-read interface (setq prefix-help-command #'embark-prefix-help-command) ;; Show the Embark target at point via Eldoc. You may adjust the ;; Eldoc strategy, if you want to see the documentation from ;; multiple providers. Beware that using this can be a little ;; jarring since the message shown in the minibuffer can be more ;; than one line, causing the modeline to move up and down: ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) :config (with-eval-after-load 'evil-collection (evil-collection-embark-setup)) (setq which-key-use-C-h-commands nil prefix-help-command #'embark-prefix-help-command) (with-eval-after-load 'evil (evil-define-key* '(normal visual insert) 'global (kbd "C-.") 'embark-act)) ;; Hide the mode line of the Embark live/completions buffers (add-to-list 'display-buffer-alist '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" nil (window-parameters (mode-line-format . none)))))
This is a package that I should learn more, but the configuration is overwhealming.
embark-consult external
(use-package embark-consult :ensure t :hook (embark-collect-mode . consult-preview-at-point-mode))
consult integration for embark.
corfu external
(use-package corfu :ensure t :custom (corfu-cycle t) (corfu-auto t) (corfu-auto-delay 0.18) (corfu-auto-prefix 2) (corfu-quit-no-match 'separator) (corfu-preselect 'prompt) (corfu-count 16) (corfu-max-width 120) (corfu-on-exact-match nil) (corfu-quit-no-match corfu-quit-at-boundary) (completion-cycle-threshold 3) (text-mode-ispell-word-completion nil) :hook (lsp-completion-mode . my/lsp-mode-setup-completion) :init (defun my/orderless-dispatch-flex-first (_pattern index _total) (and (eq index 0) 'orderless-flex)) (defun my/lsp-mode-setup-completion () (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) '(orderless)) ;; Optionally configure the first word as flex filtered. (add-hook 'orderless-style-dispatchers #'my/orderless-dispatch-flex-first nil 'local) ;; Optionally configure the cape-capf-buster. (setq-local completion-at-point-functions (list (cape-capf-buster #'lsp-completion-at-point)))) :config (add-to-list 'completion-category-overrides `(lsp-capf (styles ,@completion-styles))) ;;(add-to-list 'corfu-auto-commands #'lispy-colon) (add-hook 'evil-insert-state-exit-hook #'corfu-quit) (with-eval-after-load 'exwm (advice-add #'corfu--make-frame :around (defun +corfu--make-frame-a (oldfun &rest args) (cl-letf (((symbol-function #'frame-parent) (lambda (frame) (or (frame-parameter frame 'parent-frame) exwm-workspace--current)))) (apply oldfun args)) (when exwm--connection (set-frame-parameter corfu--frame 'parent-frame nil)))) (advice-add #'corfu--popup-redirect-focus :override (defun +corfu--popup-redirect-focus-a () (redirect-frame-focus corfu--frame (or (frame-parent corfu--frame) exwm-workspace--current))))) (defun corfu-enable-always-in-minibuffer () "Enable Corfu in the minibuffer if Vertico/Mct are not active." (unless (or (bound-and-true-p mct--active) (bound-and-true-p vertico--input) (eq (current-local-map) read-passwd-map)) ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup corfu-popupinfo-delay nil) (corfu-mode 1))) (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) :init (global-corfu-mode))
This is the basic configuration, with the addition of enabling corfu in the minibuffer.
corfu-history external
(use-package corfu-history :ensure nil :hook ((corfu-mode . corfu-history-mode)) :config (with-eval-after-load 'savehist (add-to-list 'savehist-additional-variables 'corfu-history)))
corfu-popupinfo external
(use-package corfu-popupinfo :ensure nil :hook ((corfu-mode . corfu-popupinfo-mode)) :config (setq corfu-popupinfo-delay '(0.5 . 1.0)) (setq corfu-popupinfo-hide nil))
cape external
(use-package cape :ensure t ;; Bind dedicated completion commands ;; Alternative prefix keys: C-c p, M-p, M-+, ... :bind (("C-c p p" . completion-at-point) ;; capf ("C-c p t" . complete-tag) ;; etags ("C-c p d" . cape-dabbrev)) ;; or dabbrev-completion :hook (prog-mode . +corfu-add-cape-file-h) ((org-mode markdown-mode) . +corfu-add-cape-elisp-block-h) :init ;; Add to the global default value of `completion-at-point-functions' which is ;; used by `completion-at-point'. The order of the functions matters, the ;; first function returning a result wins. Note that the list of buffer-local ;; completion functions takes precedence over the global list. (add-hook 'completion-at-point-functions #'cape-dabbrev) (add-hook 'completion-at-point-functions #'cape-file) (add-hook 'completion-at-point-functions #'cape-elisp-block) :config (setq cape-dabbrev-check-other-buffers t) (defun +corfu-add-cape-file-h () (add-hook 'completion-at-point-functions #'cape-file -10 t)) (defun +corfu-add-cape-elisp-block-h () (add-hook 'completion-at-point-functions #'cape-elisp-block 0 t)) (with-eval-after-load 'lsp-mode (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible) (advice-add #'lsp-completion-at-point :around #'cape-wrap-nonexclusive)) (advice-add #'comint-completion-at-point :around #'cape-wrap-nonexclusive) (with-eval-after-load 'eglot (advice-add #'eglot-completion-at-point :around #'cape-wrap-nonexclusive)) (advice-add #'pcomplete-completions-at-point :around #'cape-wrap-nonexclusive) ;; From the `cape' readme. Without this, Eshell autocompletion is broken on ;; Emacs28. (when (< emacs-major-version 29) (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)))
kind-icon external
(use-package kind-icon :if (display-graphic-p) :ensure t :after corfu :config (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
Dired
dired
(use-package dired :ensure nil :commands (dired dired-jump) :init (setq dired-dwim-target t ; guess a default target directory dired-hide-details-hide-symlink-targets nil ; don't hide symbolic link targets dired-auto-revert-buffer #'dired-buffer-stale-p ; revert stale only dired-recursive-copies 'always ; always copy recursively dired-recursive-deletes 'top ; ask only for top-level dired-create-destination-dirs 'ask dired-listing-switches "-AGFhl --group-directories-first --time-style=long-iso" dired-clean-confirm-killing-deleted-buffers nil) :config (setq dired-mouse-drag-files t) (with-eval-after-load 'evil-collection (evil-collection-dired-setup)))
The arguments for dired-listing-switches
are:
-A
- Show hidden files (“dotfiles”), such as
.bashrc
, but omit the implied.=
and..
targets. The latter two refer to the present and parent directory, respectively. -G
- Do not show the group name in the long listing. Only show the owner of the file.
-F
- Differentiate regular from special files by appending a character to them. The
*
is for executables, the/
is for directories, the|
is for a named pipe, the=
is for a socket, the@
and the>
are for stuff I have never seen. -h
- Make file sizes easier to read.
-l
- Produce a long, detailed listing. Dired requires this.
--group-directories-first
- Just as it said.
--time-style=long-iso
- Uses the international standard for time representation in the file listing. So we have something like 2023-12-30 06:38 to show the last modified time.
image-dired
(use-package image-dired :ensure nil :config (setq image-dired-thumb-size 150 image-dired-thumbnail-storage 'standard image-dired-external-viewer "xdg-open"))
dired-x
(use-package dired-x :ensure nil :hook (dired-mode . dired-omit-mode) :config (setq dired-omit-files (concat dired-omit-files "\\|^\\.DS_Store\\'" "\\|^\\.project\\(?:ile\\)?\\'" "\\|^\\.\\(?:svn\\|git\\)\\'" "\\|^\\.ccls-cache\\'" "\\|\\(?:\\.js\\)?\\.meta\\'" "\\|\\.\\(?:elc\\|o\\|pyo\\|swp\\|class\\)\\'")) ;; Disable the prompt about whether I want to kill the Dired buffer for a ;; deleted directory. Of course I do! (setq dired-clean-confirm-killing-deleted-buffers nil) (let ((cmd "xdg-open")) (setq dired-guess-shell-alist-user `(("\\.\\(?:docx\\|pdf\\|djvu\\|eps\\)\\'" ,cmd) ("\\.\\(?:jpe?g\\|png\\|gif\\|xpm\\)\\'" ,cmd) ("\\.\\(?:xcf\\)\\'" ,cmd) ("\\.csv\\'" ,cmd) ("\\.tex\\'" ,cmd) ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\)\\(?:\\.part\\)?\\'" ,cmd) ("\\.\\(?:mp3\\|flac\\)\\'" ,cmd) ("\\.html?\\'" ,cmd) ("\\.md\\'" ,cmd)))))
The dired-guess-shell-alist-user
expect us to have xdg-open
(which is always available in my system anyway).
fd-dired external
Needs an external package fd
, or fd-find
(use-package fd-dired :if (executable-find "fd") :ensure t :defer t :init (global-set-key [remap find-dired] #'fd-dired))
dired-git-info external
(use-package dired-git-info :ensure t :defer t ;; :hook ;; (dired-after-readin . dired-git-info-auto-enable) :config ;; (setq +dired--git-info-p (bound-and-true-p dired-git-info-mode)) ;; (when +dired--git-info-p ;; (dired-git-info-mode -1)) (setq dgi-auto-hide-details-p nil))
dired-aux
(use-package dired-aux :ensure nil :config (setq dired-create-destination-dirs 'ask dired-vc-rename-file t dired-isearch-filenames 'dwim dired-create-destination-dirs-on-trailing-dirsep t))
dired-rsync external
(use-package dired-rsync :ensure t :defer t)
diredfl external
(use-package diredfl :ensure t :defer t :hook (dired-mode . diredfl-global-mode))
all-the-icons-dired external
(use-package all-the-icons-dired :ensure t :after all-the-icons :hook (dired-mode . all-the-icons-dired-mode))
wdired builtin
(use-package wdired :ensure nil :commands (wdired-change-to-wdired-mode) :config (with-eval-after-load 'evil-collection (evil-collection-wdired-setup)))
minimal emacs
minimal-init.el
This file is intended to be used on a remote system, so configuration should only exists on this one file only. No external packages, the sole purpose is to edit files.
(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) (add-to-list 'initial-frame-alist '(menu-bar-lines . 0)) (add-to-list 'initial-frame-alist '(tool-bar-lines . 0)) (add-to-list 'default-frame-alist '(tool-bar-lines . 0)) (add-to-list 'initial-frame-alist '(vertical-scroll-bars)) (add-to-list 'default-frame-alist '(vertical-scroll-bars)) (add-to-list 'initial-frame-alist '(fullscreen . maximized)) (add-to-list 'default-frame-alist '(fullscreen . maximized)) (use-package emacs :ensure nil :init (load-theme 'modus-vivendi-tinted t)) (setq user-mail-address "alexforsale@yahoo.com" user-full-name "Kristian Alexander P") (use-package emacs :ensure nil :custom (read-buffer-completion-ignore-case t) (tab-always-indent nil) (visible-bell nil) (use-short-answers t) (use-dialog-box nil) (window-resize-pixelwise nil) (frame-resize-pixelwise t) (ring-bell-function #'ignore) (scroll-preserve-screen-position t) (scroll-conservatively 101) (fast-but-imprecise-scrolling t) (truncate-partial-width-windows nil) (tab-width 4) (fill-column 80) (enable-recursive-minibuffers t) (use-file-dialog nil) (create-lockfiles nil) (delete-by-moving-to-trash t) (inhibit-startup-screen t) :config (when (bound-and-true-p tooltip-mode) (tooltip-mode -1)) (setq completion-ignore-case t read-file-name-completion-ignore-case t read-buffer-completion-ignore-case t load-prefer-newer t auto-window-vscroll nil inhibit-compacting-font-caches t redisplay-skip-fontification-on-input t) (set-default 'indicate-empty-lines nil) (setq-default truncate-lines t) (setq-default x-stretch-cursor nil)) (use-package files :ensure nil :config (nconc auto-mode-alist '(("/LICENSE\\'" . text-mode) ("\\.log\\'" . text-mode) ("rc\\'" . conf-mode) ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode))) :hook ((prog-mode text-mode) . auto-save-visited-mode) :custom (auto-save-visited-interval 10) (find-file-suppress-same-file-warnings t) ;;(confirm-kill-emacs #'yes-or-no-p) ; confirm when exiting (confirm-kill-processes nil) ; don't confirm killing processes (revert-without-query (list ".")) (find-file-visit-truename t) ; `find-file' will visit the actual file (make-backup-files nil) (version-control t) (backup-by-copying t) (delete-old-versions t) (kept-new-versions 6) (kept-old-versions 2) (auto-save-include-big-deletions t) (auto-save-list-file-prefix (expand-file-name ".autosave/" user-emacs-directory)) (backup-directory-alist `(("." . ,(expand-file-name ".backup" user-emacs-directory)))) (auto-mode-case-fold nil) (require-final-newline t)) (use-package saveplace :init (save-place-mode 1) :custom (save-place-file (expand-file-name "places" user-emacs-directory))) (use-package autorevert :hook (focus-in . doom-auto-revert-buffers-h) :hook (after-save . doom-auto-revert-buffers-h) :hook (prog-mode . doom-auto-revert-buffer-h) :custom (auto-revert-interval 60) (auto-revert-use-notify nil) (global-auto-revert-non-file-buffers t) (auto-revert-verbose t) (auto-revert-stop-on-user-input nil) (revert-without-query (list ".")) :config (defun doom-visible-buffer-p (buf) "Return non-nil if BUF is visible." "Return non-nil if BUF is not visible." (not (doom-visible-buffer-p buf))) (defun doom-visible-buffers (&optional buffer-list all-frames) "Return a list of visible buffers (i.e. not buried)." (let ((buffers (delete-dups (cl-loop for frame in (if all-frames (visible-frame-list) (list (selected-frame))) if (window-list frame) nconc (mapcar #'window-buffer it))))) (if buffer-list (cl-delete-if (lambda (b) (memq b buffer-list)) buffers) (delete-dups buffers)))) (defun doom-auto-revert-buffer-h () "Auto revert current buffer, if necessary." (unless (or auto-revert-mode (active-minibuffer-window)) (let ((auto-revert-mode t)) (auto-revert-handler)))) (defun doom-auto-revert-buffers-h () "Auto revert stale buffers in visible windows, if necessary." (dolist (buf (doom-visible-buffers)) (with-current-buffer buf (doom-auto-revert-buffer-h))))) (use-package savehist :init (savehist-mode 1) :custom (savehist-file (expand-file-name "history" user-emacs-directory)) (savehist-save-minibuffer-history t) (savehist-autosave-interval nil) (savehist-coding-system 'utf-8) (savehist-additional-variables '(evil-jumps-history command-history kill-ring register-alist mark-ring global-mark-ring search-ring regexp-search-ring))) (use-package recentf :bind ("C-c f" . recentf) :commands recentf-open-files :hook (dired-mode . doom--recentf-add-dired-directory-h) :init (recentf-mode 1) :custom (recentf-auto-cleanup t) (recentf-max-saved-items 250) (recentf-max-menu-items 300) (recentf-exclude `("/elpa/" ;; ignore all files in elpa directory "recentf" ;; remove the recentf load file ".*?autoloads.el$" "treemacs-persist" "company-statistics-cache.el" ;; ignore company cache file "/intero/" ;; ignore script files generated by intero "/journal/" ;; ignore daily journal files ".gitignore" ;; ignore `.gitignore' files in projects "/tmp/" ;; ignore temporary files "NEWS" ;; don't include the NEWS file for recentf "bookmarks" "bmk-bmenu" ;; ignore bookmarks file in .emacs.d "loaddefs.el" "^/\\(?:ssh\\|su\\|sudo\\)?:" ;; ignore tramp/ssh files (concat "^" (regexp-quote (or (getenv "XDG_RUNTIME_DIR") "/run"))))) :config (defun doom--recentf-file-truename-fn (file) (if (or (not (file-remote-p file)) (equal "sudo" (file-remote-p file 'method))) (abbreviate-file-name (file-truename (tramp-file-name-localname file))) file)) ;; Resolve symlinks, strip out the /sudo:X@ prefix in local tramp paths, and ;; abbreviate $HOME -> ~ in filepaths (more portable, more readable, & saves ;; space) (add-to-list 'recentf-filename-handlers #'doom--recentf-file-truename-fn) ;; Text properties inflate the size of recentf's files, and there is ;; no purpose in persisting them (Must be first in the list!) (add-to-list 'recentf-filename-handlers #'substring-no-properties) (defun doom--recentf-add-dired-directory-h () "Add dired directories to recentf file list." (recentf-add-file default-directory)) ;; The most sensible time to clean up your recent files list is when you quit ;; Emacs (unless this is a long-running daemon session). (setq recentf-auto-cleanup (if (daemonp) 300)) (add-hook 'kill-emacs-hook #'recentf-cleanup)) (use-package emacs :ensure nil :hook ((prog-mode text-mode conf-mode) . display-line-numbers-mode) :config (setq hscroll-margin 2 hscroll-step 1 ;; Emacs spends too much effort recentering the screen if you scroll the ;; cursor more than N lines past window edges (where N is the settings of ;; `scroll-conservatively'). This is especially slow in larger files ;; during large-scale scrolling commands. If kept over 100, the window is ;; never automatically recentered. The default (0) triggers this too ;; aggressively, so I've set it to 10 to recenter if scrolling too far ;; off-screen. scroll-conservatively 10 scroll-margin 0 scroll-preserve-screen-position t ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll' ;; for tall lines. auto-window-vscroll nil ;; mouse mouse-wheel-scroll-amount '(2 ((shift) . hscroll)) mouse-wheel-scroll-amount-horizontal 2) ;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any ;; feedback after typing is better UX than no feedback at all. (setq echo-keystrokes 0.02) ;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This ;; doesn't look too great with direnv, however... (setq resize-mini-windows 'grow-only) ;; Try to keep the cursor out of the read-only portions of the minibuffer. (setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) ;; Show absolute line numbers for narrowed regions to make it easier to tell the ;; buffer is narrowed, and where you are, exactly. (setq-default display-line-numbers-widen t)) (use-package frame :ensure nil :hook (after-init . window-divider-mode) :config (blink-cursor-mode 1) (setq window-divider-default-places t window-divider-default-bottom-width 1 window-divider-default-right-width 1)) (use-package window :ensure nil :config (setq split-width-threshold 160 split-height-threshold nil)) (use-package comint :ensure nil :config (setq comint-prompt-read-only t comint-buffer-maximum-size 2048) (with-eval-after-load 'evil (evil-define-key* '(normal visual) comint-mode-map "q" '(lambda () (interactive) (kill-buffer nil))))) (use-package winner :ensure nil :init (winner-mode +1) :config (setq winner-boring-buffers '("*Completions*" "*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*" "*esh command on file*"))) (use-package font-core :ensure nil :init (global-font-lock-mode t)) (use-package hideshow :hook (prog-mode . hs-minor-mode)) (use-package mouse :ensure nil :config (setq mouse-yank-at-point t)) (use-package paren :ensure nil :hook (prog-mode . show-paren-local-mode) :config (show-paren-mode 1) :custom (show-paren-style 'parenthesis) (show-paren-delay 0.4) (show-paren-context-when-offscreen 'overlay) (show-paren-when-point-inside-paren nil) (show-paren-when-point-in-periphery nil)) (use-package repeat :ensure nil :init (repeat-mode +1)) (use-package eshell :ensure nil :custom (eshell-history-size 10000) (eshell-hist-ignore-dups t) (eshell-buffer-maximum-lines 10000) (eshell-scroll-to-bottom-on-input t) (eshell-destroy-buffer-when-process-dies t) (eshell-prompt-regexp "^[^\)]*[\)] ")) (use-package sh-script :ensure nil :mode ("\\.bats\\'" . sh-mode) :mode ("\\.\\(?:zunit\\|env\\)\\'" . sh-mode) :mode ("/bspwmrc\\'" . sh-mode) :mode ("PKGBUILD" . sh-mode) :config (setq sh-indent-after-continuation 'always) (with-eval-after-load 'rainbow-delimiters (add-hook 'sh-mode-hook #'rainbow-delimiters-mode) (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs '((sh-mode bash-ts-mode) .("bash-language-server" "start")))) (add-hook 'sh-mode-hook #'eglot-ensure) (add-hook 'bash-ts-mode-hook #'eglot-ensure))) (use-package select :ensure nil :custom (select-enable-clipboard t)) (transient-mark-mode 1) (delete-selection-mode 1) (use-package subword :ensure nil :init (global-subword-mode 1)) (use-package text-mode :ensure nil :hook ((text-mode . visual-line-mode) (prog-mode . (lambda () (setq-local sentence-end-double-space t)))) :config (setq-default sentence-end-double-space nil) (setq sentence-end-without-period nil) (setq colon-double-space nil) (setq use-hard-newlines t) (setq adaptive-fill-mode t)) (use-package whitespace :ensure nil :config (setq whitespace-line-column nil whitespace-style '(face indentation tabs tab-mark spaces space-mark newline trailing lines-tail) whitespace-display-mappings '((tab-mark ?\t [?› ?\t]) (newline-mark ?\n [?¬ ?\n]) (space-mark ?\ [?·] [?.])))) (use-package eldoc :hook ((emacs-lisp-mode lisp-interaction-mode ielm-mode) . eldoc-mode)) (use-package eglot :hook ((rust-mode nix-mode python-mode toml-mode markdown-mode js-mode js-ts-mode typescript-ts-mode) . eglot-ensure) :init (setopt eglot-sync-connect 1 eglot-autoshutdown t) :config (with-eval-after-load 'cape (advice-add 'eglot-completion-at-point :around #'cape-wrap-super)) (with-eval-after-load 'evil-collection (evil-collection-eglot-setup)) (add-to-list 'eglot-server-programs `(rust-mode . ("rust-analyzer" :initializationOptions ( :procMacro (:enable t) :cargo ( :buildScripts (:enable t) :features "all")))) '(toml-ts-mode . ("taplo" "lsp" "stdio"))) (add-to-list 'eglot-server-programs '(web-mode . ("vscode-html-language-server" "--stdio"))) (add-to-list 'eglot-server-programs '(css-mode . ("vscode-css-language-server" "--stdio")))) (use-package executable :ensure nil :hook (after-save . executable-make-buffer-file-executable-if-script-p)) (use-package dired :ensure nil :commands (dired dired-jump) :init (setq dired-dwim-target t ; guess a default target directory dired-hide-details-hide-symlink-targets nil ; don't hide symbolic link targets dired-auto-revert-buffer #'dired-buffer-stale-p ; revert stale only dired-recursive-copies 'always ; always copy recursively dired-recursive-deletes 'top ; ask only for top-level dired-create-destination-dirs 'ask dired-listing-switches "-AGFhl --group-directories-first --time-style=long-iso" dired-clean-confirm-killing-deleted-buffers nil) :config (setq dired-mouse-drag-files t) (with-eval-after-load 'evil-collection (evil-collection-dired-setup))) (use-package image-dired :ensure nil :config (setq image-dired-thumb-size 150 image-dired-thumbnail-storage 'standard image-dired-external-viewer "xdg-open")) (use-package dired-x :ensure nil :hook (dired-mode . dired-omit-mode) :config (setq dired-omit-files (concat dired-omit-files "\\|^\\.DS_Store\\'" "\\|^\\.project\\(?:ile\\)?\\'" "\\|^\\.\\(?:svn\\|git\\)\\'" "\\|^\\.ccls-cache\\'" "\\|\\(?:\\.js\\)?\\.meta\\'" "\\|\\.\\(?:elc\\|o\\|pyo\\|swp\\|class\\)\\'")) ;; Disable the prompt about whether I want to kill the Dired buffer for a ;; deleted directory. Of course I do! (setq dired-clean-confirm-killing-deleted-buffers nil) (let ((cmd "xdg-open")) (setq dired-guess-shell-alist-user `(("\\.\\(?:docx\\|pdf\\|djvu\\|eps\\)\\'" ,cmd) ("\\.\\(?:jpe?g\\|png\\|gif\\|xpm\\)\\'" ,cmd) ("\\.\\(?:xcf\\)\\'" ,cmd) ("\\.csv\\'" ,cmd) ("\\.tex\\'" ,cmd) ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\)\\(?:\\.part\\)?\\'" ,cmd) ("\\.\\(?:mp3\\|flac\\)\\'" ,cmd) ("\\.html?\\'" ,cmd) ("\\.md\\'" ,cmd))))) (use-package dired-aux :ensure nil :config (setq dired-create-destination-dirs 'ask dired-vc-rename-file t dired-isearch-filenames 'dwim dired-create-destination-dirs-on-trailing-dirsep t))