alexforsale's literate Emacs configuration
Table of Contents
This repository is my personal Emacs configuration. In case you don't know what Emacs is, I won't be the one explaining. To me Emacs is crucial and should exists in all of my machines, including the proprietary ones. In fact, this repository is the first one that I clone into my new machines (with my dotfiles being the second one).
The Goal
I used to blindly copying and pasting every snippets of Emacs configuration I found over the internet. The result usually is me having to mix and match them into one configuration, which is fine until it broke, and I start from scratch again and the cycle repeats.
Keep it simple
If the default configuration for a certain package is enough, don't add more. a certain package is enough, don't add more. So this configuration is constantly changing, sometimes something gets added, only to be removed after a while when I don't use it anymore (or when I don't know how to use it).
Reproducible
This configuration is for me, not for anyone else
It's good if anyone who stumble into this repository find it useful, but the priority is to make it useful for me now. I'm trying to stop adding configuration that someday would be useful.
Editor first, IDE second
I'm not a programmer, I use Emacs mainly for editing files, tasks management, note-taking. My main focus is org-mode, since this mode is what I use most.
Documented
- List every external packages needed for this configuration.
- Explain why a package is configured.
- If possible, also compare with other package with similar functionality.
Mnemonic keybindings
Try setting a sensible keybindings, especially for external packages. Don't mess with the default keybindings if possible.
Use org-mode
This way I don't have to separate each configurations into a separate modules. But having all the configuration in one file is also made debugging quite hard, so I'll still separate them into separate files.
Try to minimize the use of external packages
More dependencies means more bloats, avoid it when possible.
early-init.el
GC Threshold
A common way of speeding up Emacs startup. The variable gc-cons-threshold
is the number of bytes consing between garbage collection.
Garbage collection can happen atuomatically once this many bytes have been allocated since the last garbage collection.
Here I set this to a variable most-positive-fixnum
which value is set automatically.
most-positive-fixnum
2305843009213693951
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6)
The gc-cons-percentage
default to 0.1, is the portion of the heap used for allocation.
I set the value differently when Emacs is starting up, by setting this variable into emacs-startup-hook
that runs after Emacs loaded the init files.
(add-hook 'emacs-startup-hook (lambda () (setq gc-cons-threshold (* 16 1024 1024) ; 16mb gc-cons-percentage 0.1)))
Language
Set UTF-8 as the default coding system
(set-language-environment "UTF-8")
Set initial frame
Here I disable the menu-bar, tool-bar and vertical scroll bar.
(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) (add-to-list 'initial-frame-alist '(menu-bar-lines . 0)) (add-to-list 'initial-frame-alist '(tool-bar-lines . 0)) (add-to-list 'default-frame-alist '(tool-bar-lines . 0)) (add-to-list 'initial-frame-alist '(vertical-scroll-bars)) (add-to-list 'default-frame-alist '(vertical-scroll-bars)) (add-to-list 'initial-frame-alist '(fullscreen . maximized)) (add-to-list 'default-frame-alist '(fullscreen . maximized))
Define the location of custom-file
The custom-file
is used to save customization settings for future use.
(customize-set-variable 'custom-file (expand-file-name "custom.el" user-emacs-directory)) (when (file-exists-p custom-file) (load custom-file))
Setup package
Now much more simpler since [[https://github.com/jwiegley/use-package][use-package]]
is built into Emacs. It's actually not used since I'm using straight.el
.
(with-eval-after-load 'package ;; Add `melpa` to `package-archives`. (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) ;; gnu-devel (add-to-list 'package-archives '("gnu-devel" . "https://elpa.gnu.org/devel/")) (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/")))
init.el
straight.el
(customize-set-variable 'straight-use-package-by-default t) (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" (or (bound-and-true-p straight-base-dir) user-emacs-directory))) (bootstrap-version 7)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) (straight-register-package '(org :depth 1)) (straight-use-package 'org) (straight-use-package 'use-package)
I use straight-register-package
for org-mode
since full cloning the repository would take too much time.
Tangle this org-file
This file is already added in my git repositories for initial Emacs loading. But I also include it here to make sure there's no changes made.
(require 'ob-tangle) (when (file-exists-p (expand-file-name "index.org" user-emacs-directory)) (org-babel-tangle-file (expand-file-name "index.org" user-emacs-directory)))
Variables and helper functions
- Identity
(setq user-mail-address "alexforsale@yahoo.com" user-full-name "Kristian Alexander P")
- merge-list-to-list function
This function is just a wrapper to easily merge lists. I don't think I need it anymore.
;;; From https://emacs.stackexchange.com/questions/38008/adding-many-items-to-a-list/68048#68048 (defun merge-list-to-list (dst src) "Merges content of the 2nd list with the 1st one" (set dst (append (eval dst) src)))
- define +config/org-directory variable
(cond ((file-directory-p (expand-file-name "Sync/org" (getenv "HOME"))) (customize-set-variable '+config/org-directory (expand-file-name "Sync/org" (getenv "HOME")))) ((string-match-p "microsoft" (shell-command-to-string "uname -a")) (customize-set-variable '+config/org-directory (expand-file-name "org" (getenv "HOME")))))
I put my org files inside syncthing default directory (
~/Sync
if on Linux) so it will automatically synced to all my devices. But on windows WSL the location will vary depending on how you install syncthing so I won't even bother set it up.
Load each modular configuration file.
- evil
All
evil-mode
specific configuration andevil
related packages goes here.(require 'config-evil (expand-file-name "modules/config-evil.el" user-emacs-directory) t)
- general
(require 'config-general (expand-file-name "modules/config-general.el" user-emacs-directory) t)
All
general
keybindings are in this file. - UI
(require 'config-ui (expand-file-name "modules/config-ui.el" user-emacs-directory) t)
Configuration for Emacs's looks and feels.
- builtins
(require 'config-builtins (expand-file-name "modules/config-builtins.el" user-emacs-directory) t)
The more I understand the meat inside Emacs the less external packages I'll use (hopefully).
- completion
(require 'config-completion (expand-file-name "modules/config-completion.el" user-emacs-directory) t)
For now this is for vertico and friends.
- shells
(require 'config-shells (expand-file-name "modules/config-shells.el" user-emacs-directory) t)
Mostly
vterm
configurations.Eshell
looks promising, but still hard to configure. - editing
(require 'config-editing (expand-file-name "modules/config-editing.el" user-emacs-directory) t)
Tweaks to make my editing experience nicer.
- IDE
(require 'config-ide (expand-file-name "modules/config-ide.el" user-emacs-directory) t)
All of
LSP
,git
,flycheck
, andprojectile
configuration. - org
(require 'config-org (expand-file-name "modules/config-org.el" user-emacs-directory) t)
Most of my changes are in here, the more configurable
org-mode
, the more unusable to me. - tools
(require 'config-tools (expand-file-name "modules/config-tools.el" user-emacs-directory) t)
Small packages that helps Emacs interacts with its surrounding. I have
direnv
,exec-path-from-shell
for, you guess it, shells. There's alsoox-hugo
for blogging with hugo. - dired
(require 'config-dired (expand-file-name "modules/config-dired.el" user-emacs-directory) t)
I don't use
dired
that much, but when I do, I at least know my way around. - treemacs
(require 'config-treemacs (expand-file-name "modules/config-treemacs.el" user-emacs-directory) t)
It's like NERDtree in vim, but in Emacs, I kinda understand why vim need this functionality.
C-x C-f
withvertico
andmarginalia
already do the same in Emacs. So it's not important to me, but still nice to have. - mail
(require 'config-mail (expand-file-name "modules/config-mail.el" user-emacs-directory) t)
Email in Emacs with the help of notmuch, offlineimap, afew, and davmail for my Exchange mails.
- programming modes
(require 'config-progmodes (expand-file-name "modules/config-progmodes.el" user-emacs-directory) t)
Doing stuffs in linux will eventually makes you edit files from various programming languages. This here is just to make me read them comfortly.
- local overrides
(require 'config-overrides (expand-file-name "modules/config-overrides.el" user-emacs-directory) t)
Modules
Evil
- configuration
Mostly copied from doom or the official documentation.
(use-package evil :demand t :hook (after-change-major-mode . (lambda () (setq evil-shift-width tab-width))) :preface (customize-set-variable 'evil-want-keybinding nil) (customize-set-variable 'evil-want-integration t) (customize-set-variable 'evil-undo-system 'undo-redo) (customize-set-variable 'evil-want-C-i-jump nil) ;; fix TAB in terminal org-mode (customize-set-variable 'evil-want-C-u-scroll t) ;; move universal arg to <leader> u (customize-set-variable 'evil-want-C-u-delete t) ;; delete back to indentation in insert state (customize-set-variable 'evil-want-C-g-bindings t) :custom (evil-undo-system #'undo-redo) (evil-search-module 'evil-search) (evil-ex-search-vim-style-regexp t) (evil-ex-interactive-search-highlight 'selected-window) (evil-kbd-macro-suppress-motion-error t) (evil-visual-update-x-selection-p nil) :config (unless noninteractive (setq save-silently t)) (setq evil-normal-state-cursor 'box evil-insert-state-cursor 'bar evil-visual-state-cursor 'hollow) (evil-select-search-module 'evil-search-module 'isearch) (evil-mode 1) (with-eval-after-load 'eldoc (eldoc-add-command 'evil-normal-state 'evil-insert 'evil-change 'evil-delete 'evil-replace)) ;; from doom (defun +evil/window-split-and-follow () "Split current window horizontally, then focus new window. If `evil-split-window-below' is non-nil, the new window isn't focused." (interactive) (let ((evil-split-window-below (not evil-split-window-below))) (call-interactively #'evil-window-split))) (defun +evil/window-vsplit-and-follow () "Split current window vertically, then focus new window. If `evil-vsplit-window-right' is non-nil, the new window isn't focused." (interactive) (let ((evil-vsplit-window-right (not evil-vsplit-window-right))) (call-interactively #'evil-window-vsplit))))
Notes:
evil-search-modules
- I switched this to
isearch
since the defaultevil-search
didn't search through collapsed headings inorg-mode
. evil-want-keybindings
- I set this to nil so it won't load
evil-keybindings.el
. evil-want-integration
- Loads
evil-integration.el
. evil-undo-system
- Use
undo-redo
which is natively available in Emacs 28. evil-want-C-i-jump
- Set to nil so
C-i
will insert a tab character, needed when I'm using Emacs inside a terminal. evil-want-C-u-scroll
- Set
C-u
to scrolls up (like Vim), I'm not using it for prefix arguments since it's already mapped toSPC u
in normal mode. evil-want-C-u-delete
- The same for insert mode, set
C-u
to deletes back to indentation in insert state, prefix argument is set toC-c SPC u
in insert mode. evil-want-C-g-bindings
- Set a consistent bindings for
C-g
, though it also depends on the packages. evil-ex-search-vim-style-regexp
- Enables Vim-style backlash codes in search patterns.
evil-ex-interactive-search-highlight
- Set the interactive highlighting only to
selected-window
. evil-visual-update-x-selection-p
- Not updating the
X PRIMARY
selection with the current visual region, not sure how it works under wayland though.
I've also steal two functions from Doomemacs,
+evil/window-vsplit-and-follow
- which is bound to
SPC w v
. +evil/window-split-and-follow
- which is bound to
SPC w s
.
Why is this the first package in my list? My right hand fingers always land on
h
,j
,k
,l
. You can't teach old dog new tricks. I've used the vanilla keybindings, it's great but it's faster with /evil-mode.- Useful keybindings
Not a full list of keybindings, just ones that I should remember to use.
keybindings function description normal-state ] b
evil-next-buffer
Go to the COUNTth next buffer in the buffer list. normal-state [ b
evil-previous-buffer
Go to the COUNTth previous buffer in the buffer list. normal-state ] SPC
evil-collection-unimpaired-insert-newline-below
Insert COUNT blank line(s) below current line. normal-state [ SPC
evil-collection-unimpaired-insert-newline-above
Insert COUNT blank line(s) above current line.
- evil-collection
This is a collection of Evil bindings for the parts of Emacs that Evil does not cover properly by default, such as help-mode, M-x calendar, Eshell and more.
(use-package evil-collection :after evil :init (evil-collection-init) :custom (evil-collection-setup-minibuffer t) (evil-collection-calendar-want-org-bindings t))
- evil-terminal-cursor-changer
Change cursor shape and color by evil state in terminal.
(use-package evil-terminal-cursor-changer :unless (display-graphic-p) :init (evil-terminal-cursor-changer-activate) :config (setq evil-motion-state-cursor 'box) ; █ (setq evil-visual-state-cursor 'box) ; █ (setq evil-normal-state-cursor 'box) ; █ (setq evil-insert-state-cursor 'bar) ; ⎸ (setq evil-emacs-state-cursor 'hbar) ; _ )
- evil-easymotion
(use-package evil-easymotion :after evil :demand t :commands evilem-create evilem-default-keybindings :config (evilem-make-motion evilem-motion-search-next #'evil-search-next :bind ((evil-ex-search-highlight-all nil))) (evilem-make-motion evilem-motion-search-previous #'evil-search-previous :bind ((evil-ex-search-highlight-all nil))) (evilem-make-motion evilem-motion-search-word-forward #'evil-search-word-forward :bind ((evil-ex-search-highlight-all nil))) (evilem-make-motion evilem-motion-search-word-backward #'evil-search-word-backward :bind ((evil-ex-search-highlight-all nil))) ;; Rebind scope of w/W/e/E/ge/gE evil-easymotion motions to the visible ;; buffer, rather than just the current line. (put 'visible 'bounds-of-thing-at-point (lambda () (cons (window-start) (window-end)))) (evilem-make-motion evilem-motion-forward-word-begin #'evil-forward-word-begin :scope 'visible) (evilem-make-motion evilem-motion-forward-WORD-begin #'evil-forward-WORD-begin :scope 'visible) (evilem-make-motion evilem-motion-forward-word-end #'evil-forward-word-end :scope 'visible) (evilem-make-motion evilem-motion-forward-WORD-end #'evil-forward-WORD-end :scope 'visible) (evilem-make-motion evilem-motion-backward-word-begin #'evil-backward-word-begin :scope 'visible) (evilem-make-motion evilem-motion-backward-WORD-begin #'evil-backward-WORD-begin :scope 'visible) (evilem-make-motion evilem-motion-backward-word-end #'evil-backward-word-end :scope 'visible) (evilem-make-motion evilem-motion-backward-WORD-end #'evil-backward-WORD-end :scope 'visible) (evilem-default-keybindings "z g") )
This is a useful package for complementing Evil's motion. With
general.el
, I set the keymap toSPC g e
.evilem keybinding evilem key Evil evil key evilem-motion-forward-word-begin
SPC g e w
evil-forward-word-begin
w
evilem-motion-forward-WORD-begin
SPC g e W
evil-forward-WORD-begin
W
evilem-motion-forward-word-end
SPC g e e
evil-forward-word-end
e
evilem-motion-forward-WORD-end
SPC g e E
evil-forward-WORD-end
E
evilem-motion-backward-word-begin
SPC g e b
evil-forward-word-begin
b
evilem-motion-backward-WORD-begin
SPC g e B
evil-forward-WORD-begin
B
evilem-motion-backward-word-end
SPC g e g e
evil-forward-word-end
g e
evilem-motion-backward-WORD-end
SPC g e g E
evil-forward-WORD-end
g E
These keybindings is also available on
visual-mode
. - evil-surround
This package emulates surround.vim by Tim Pope. The functionality is wrapped into a minor mode.
(use-package evil-surround :after evil :demand t :commands (global-evil-surround-mode evil-surround-edit evil-Surround-edit evil-surround-region) :hook (org-mode . (lambda () (push '(?~ . ("~" . "~")) evil-surround-pairs-alist))) (org-mode . (lambda () (push '(?$ . ("\\(" . "\\)")) evil-surround-pairs-alist))) :init (global-evil-surround-mode 1))
- Usage
- Add surrounding
You can surround in visual-state with
S<textobject>
orgS<textobject>
. Or in normal-state withys<textobject>
oryS<textobject>
. I'm still using theShift I
in visual mode, and theys
in normal mode conflicts with=which-key-mode
. - Change surrounding
You can change a surrounding with
cs<old-textobject><new-textobject>
.
- Add surrounding
- Usage
- evil-embrace
This package provides evil integration of embrace.el. Since
evil-surround
provides a similar set of features asembrace.el
, this package aims at adding the goodies ofembrace.el
toevil-surround
and makingevil-surround
even better.(use-package evil-embrace :commands embrace-add-pair embrace-add-pair-regexp :hook (LaTeX-mode . embrace-LaTeX-mode-hook) :hook (LaTeX-mode . +evil-embrace-latex-mode-hook-h) :hook (org-mode . embrace-org-mode-hook) :hook (ruby-mode . embrace-ruby-mode-hook) :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook) :init (with-eval-after-load 'evil-surround (evil-embrace-enable-evil-surround-integration)) :config ;; from doom (defun +evil--embrace-latex () "LaTeX command support for embrace." (cons (format "\\%s{" (read-string "\\")) "}")) (defun +evil-embrace-latex-mode-hook-h () (dolist (pair '((?\' . ("`" . "\'")) (?\" . ("``" . "\'\'")))) (delete (car pair) evil-embrace-evil-surround-keys) ;; Avoid `embrace-add-pair' because it would overwrite the default ;; rules, which we want for other modes (push (cons (car pair) (make-embrace-pair-struct :key (car pair) :left (cadr pair) :right (cddr pair) :left-regexp (regexp-quote (cadr pair)) :right-regexp (regexp-quote (cddr pair)))) embrace--pairs-list)) (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex)) (defun +evil-embrace-angle-bracket-modes-hook-h () (let ((var (make-local-variable 'evil-embrace-evil-surround-keys))) (set var (delq ?< evil-embrace-evil-surround-keys)) (set var (delq ?> evil-embrace-evil-surround-keys))) (embrace-add-pair-regexp ?< "\\_<[a-z0-9-_]+<" ">" #'+evil--embrace-angle-brackets) (embrace-add-pair ?> "<" ">")))
- evil-escape
Customizable key sequence to escape from insert state and everything else in Emacs.
(use-package evil-escape :commands evil-escape :after evil :hook (evil-after-load . evil-escape-mode) :init (setq evil-escape-excluded-states '(normal visual multiedit emacs motion) evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode dired-mode) evil-escape-key-sequence "jk" evil-escape-delay 0.15) (evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape))
- evil-exchange
(use-package evil-exchange :init (evil-exchange-install))
Easy text exchange operator for Evil. This is the port of vim-exchange by Tom McDonald.
- Default Bindings
- evil-quick-diff
This is a replacement for the linediff.vim plugin. It's not a faithful port, as it uses ediff for diffing, but the spirit is the same. The default operator key for diffing is
god
. WhilegoD
is used for canceling.(use-package evil-quick-diff :straight (:host github :repo "rgrinberg/evil-quick-diff") :after evil :commands (evil-quick-diff evil-quick-diff-cancel) :init ;; change default key bindings (if you want) HERE ;; (setq evil-quick-diff-key (kbd "zx")) (evil-quick-diff-install))
- evil-nerd-commenter
A Nerd Commenter emulation, help you comment code efficiently. For example, you can press
99,ci
to comment out 99 lines.(use-package evil-nerd-commenter :after evil :commands (evilnc-comment-operator evilnc-inner-comment evilnc-outer-commenter) :bind ([remap comment-line] . evilnc-comment-or-uncomment-lines))
- evil-snipe
Evil-snipe emulates vim-seek and/or vim-sneak in evil-mode.
It provides 2-character motions for quickly (and more accurately) jumping around text, compared to evil's built-in f/F/t/T motions, incrementally highlighting candidate targets as you type.
By default, snipe only binds
s
(forward) /S
(backward) toevil-snipe-s
andevil-snipe-S
, respectively. In operator mode, snipe is bound toz
/Z
andx
/X
(exclusive).(use-package evil-snipe :commands evil-snipe-local-mode evil-snipe-override-local-mode :after evil :hook (evil-after-load . (lambda () (evil-snipe-override-mode +1))) :hook (evil-after-load . (lambda () (evil-snipe-mode +1))) :hook (magit-mode . turn-off-evil-snipe-override-mode) :init (setq evil-snipe-smart-case t evil-snipe-scope 'line evil-snipe-repeat-scope 'visible evil-snipe-char-fold t))
- evil-textobj-anyblock
Evil-textobj-anyblock is port of the vim plugin vim-textobj-anyblock. It gives a text object that will select the closest of
()
,{}
,[]
,<>
,''
,""
,` `
, or“”
by default. This can be convenient for operating on the closest block without having to type its symbol.In addition to the features provided by vim-textobj-anyblock, anyblock will seek forward in the case that there is not a surrounding match. Also, repeatedly using a text object in visual mode has an expand-region-like functionality by expanding the visual selection to the next block. This is not a primary feature of anyblock but may be nice in some simple cases and is given by evil for free.
(use-package evil-textobj-anyblock :straight (:host github :repo "willghatch/evil-textobj-anyblock") :after evil :defer t :config (setq evil-textobj-anyblock-blocks '(("(" . ")") ("{" . "}") ("\\[" . "\\]") ("<" . ">"))))
- evil-traces
evil-traces
is a port of traces.vim. It enables visual previews for certainevil-ex
commands.(use-package evil-traces :after evil :config (evil-traces-mode))
- evil-visualstar
This is a port of one of the many visual-star plugins for Vim to work with evil-mode.
(use-package evil-visualstar :commands (evil-visualstar/begin-search evil-visualstar/begin-search-forward evil-visualstar/begin-search-backward) :init (evil-define-key* 'visual 'global "*" #'evil-visualstar/begin-search-forward "#" #'evil-visualstar/begin-search-backward))
- exato
This package provides the
x
text object to manipulate html/xml tag attributes. You can customize the binding.Try using
dax
,vix
andgUix
.(use-package exato :after evil :commands evil-outer-xml-attr evil-inner-xml-attr)
- evil-vimish-fold
Integration of vimish-fold with evil
Adds standard vim keybindings of
zf
andzd
to create and delete folds (viavimish-fold
) respectively. Also hooks into evil so the usual vim keybindings for fold toggling (za
), opening (zo
), closing (zc
) etc all work as expected with vimish-fold.Finally, also supports navigation between folds using
zj
andzk
.This provides a near-complete vim folding experience in evil for Emacs.
(use-package evil-vimish-fold :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) :config (vimish-fold-global-mode +1) (with-eval-after-load 'general (general-define-key [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)) (evil-define-key* 'motion 'global "zj" #'vimish-fold-next-fold "zk" #'vimish-fold-previous-fold "zf" #'evil-vimish-fold/create "zF" #'evil-vimish-fold/create-line "zd" #'evil-vimish-fold/delete "zE" #'evil-vimish-fold/delete-all))
- evil-mc
Multiple cursors implementation for
evil-mode
. I'm using the default prefixg .
.(use-package evil-mc :commands (evil-mc-make-cursor-here evil-mc-make-all-cursors evil-mc-undo-all-cursors evil-mc-pause-cursors evil-mc-resume-cursors evil-mc-make-and-goto-first-cursor evil-mc-make-and-goto-last-cursor evil-mc-make-cursor-in-visual-selection-beg evil-mc-make-cursor-in-visual-selection-end evil-mc-make-cursor-move-next-line evil-mc-make-cursor-move-prev-line evil-mc-make-cursor-at-pos evil-mc-has-cursors-p evil-mc-make-and-goto-next-cursor evil-mc-skip-and-goto-next-cursor evil-mc-make-and-goto-prev-cursor evil-mc-skip-and-goto-prev-cursor evil-mc-make-and-goto-next-match evil-mc-skip-and-goto-next-match evil-mc-skip-and-goto-next-match evil-mc-make-and-goto-prev-match evil-mc-skip-and-goto-prev-match) :init (global-evil-mc-mode 1) :config ;; (evil-define-key 'visual evil-mc-key-map ;; "A" #'evil-mc-make-cursor-in-visual-selection-end ;; "I" #'evil-mc-make-cursor-in-visual-selection-beg) (with-eval-after-load 'general (general-define-key :states '(normal visual) :keymaps 'global "gz" '(:ignore t :wk "evil-mc") "gzm" 'evil-mc-make-all-cursors "gzu" 'evil-mc-undo-all-cursors ;; "gzz" '+evil/mc-toggle-cursors ;; "gzc" '+evil/mc-make-cursor-here "gzn" 'evil-mc-make-and-goto-next-cursor "gzp" 'evil-mc-make-and-goto-prev-cursor "gzN" 'evil-mc-make-and-goto-last-cursor "gzP" 'evil-mc-make-and-goto-first-cursor) (general-define-key :states '(normal visual) :keymaps 'evil-mc-key-map "C-n" 'evil-mc-make-and-goto-next-cursor "C-N" 'evil-mc-make-and-goto-next-cursor "C-p" 'evil-mc-make-and-goto-prev-cursor "C-P" 'evil-mc-make-and-goto-first-cursor)))
- evil-multiedit
(use-package evil-multiedit :config (defun make-evil-multiedit-case-sensitive (fn &rest args) (let ((case-fold-search (not iedit-case-sensitive))) (apply fn args))) (advice-add #'evil-multiedit-match-and-next :around #'make-evil-multiedit-case-sensitive) (evil-multiedit-default-keybinds) (evil-define-key 'normal 'global (kbd "M-d") #'evil-multiedit-match-symbol-and-next (kbd "M-D") #'evil-multiedit-match-symbol-and-prev) (evil-define-key 'visual 'global "R" #'evil-multiedit-match-all (kbd "M-d") #'evil-multiedit-match-and-next (kbd "M-D") #'evil-multiedit-match-and-prev) (evil-define-key '(visual normal) 'global (kbd "C-M-d") #'evil-multiedit-restore) (with-eval-after-load 'evil-mutliedit (evil-define-key 'multiedit 'global (kbd "M-d") #'evil-multiedit-match-and-next (kbd "M-S-d") #'evil-multiedit-match-and-prev (kbd "RET") #'evil-multiedit-toggle-or-restrict-region) (evil-define-key '(multiedit multiedit-insert) 'global (kbd "C-n") #'evil-multiedit-next (kbd "C-p") #'evil-multiedit-prev)))
Hlissner's implementation of multiple-cursor for evil-mode.
- evil-org-mode
Supplemental evil-mode keybindings to emacs org-mode.
(use-package evil-org :straight (:type git :repo "Somelauw/evil-org-mode") :after org :hook (org-mode . (lambda () evil-org-mode)) :config (require 'evil-org-agenda) (evil-org-agenda-set-keys))
- evil-goggles
Display visual hint on evil edit operations.
(use-package evil-goggles :config (evil-goggles-mode) (evil-goggles-use-diff-faces))
general
Keybindings in Emacs is always been my struggle, my brain isn't big enough to memorize all the default keybindings (though over the years my fingers started to memorize some of them). At first I started using hydra, it's a wonderful package for customized keybindings. But configuring them feels like a chore, considering I'm still working my way on finding my own comfort in keybindings.
- configuration
(use-package general :init (general-evil-setup t) (general-auto-unbind-keys) :config (general-override-mode) (general-create-definer +config/leader-key :keymaps 'override :states '(insert emacs normal hybrid motion visual operator) :prefix "SPC" :non-normal-prefix "C-c SPC") (general-create-definer +config/local-leader :keymaps 'override :states '(emacs normal hybrid motion visual operator) :prefix "SPC m" :non-normal-prefix "C-c SPC m" "" '(:ignore t :which-key (lambda (arg) `(,(cadr (split-string (car arg) " ")) . ,(replace-regexp-in-string "-mode$" "" (symbol-name major-mode)))))) ;; useful macro (defmacro +config/leader-menu! (name infix-key &rest body) "Create a definer NAME `+config/leader-NAME' wrapping `+config/leader-key'. Create prefix map: `+config/leader-NAME-map'. Prefix bindings in BODY with INFIX-KEY." (declare (indent 2)) `(progn (general-create-definer ,(intern (concat "+config/leader-" name)) :wrapping +config/leader-key :prefix-map (quote ,(intern (concat "+config/leader-" name "-map"))) :infix ,infix-key :wk-full-keys nil "" '(:ignore t :which-key ,name)) (,(intern (concat "+config/leader-" name)) ,@body))))
What I'm doing before is setting the general keybindings inside their respective use-package declarations. So in org-roam, I set a general keybinding. The problem with this is, if the package is deferred (with the keyword
:defer t
), the keybindings won't show. I prefer to have the keybindings inside the use-package declaration, this way if I decided not to use the package(s), their custom keybindings will also be removed. The other way for this is to use the:demand t
keyword, with the down side of having a longer initialization time. - Packages keybindings
- First level menu
;; First level menu ;; (+config/leader-menu! "activities" "C-a") (+config/leader-menu! "buffer" "b") (+config/leader-menu! "files" "f") (+config/leader-menu! "go" "g") (+config/leader-menu! "insert" "i") (+config/leader-menu! "mail" "M-m") (+config/leader-menu! "mark" "m") (+config/leader-menu! "notes" "n") (+config/leader-menu! "open" "o") (+config/leader-menu! "quit" "q") (+config/leader-menu! "register" "gr") (+config/leader-menu! "tree" "t") (+config/leader-menu! "tab" "t TAB") (+config/leader-menu! "vterm" "v") (+config/leader-menu! "window" "w") (+config/leader-menu! "search" "/")
This is the immediate menu after pressing
SPC
in normal and visual mode. and alsoC-c SPC
in insert mode. Mostly inspired byDoomemacs
. - First level keybinding
;; keybindings (+config/leader-key ";" 'pp-eval-expression ":" 'execute-extended-command "^" '(subword-capitalize :wk "Capitalize subword") "u" 'universal-argument)
These are the commands I used often.
- buffer
;; buffer (+config/leader-buffer "C-b" 'consult-buffer "[" '(previous-buffer :wk "previous buffer") "]" '(next-buffer :wk "next buffer") "TAB" '((lambda () (interactive) (switch-to-buffer nil)) :wk "other-buffer") "b" '(switch-to-buffer :wk "switch to buffer") "s" '(basic-save-buffer :wk "save buffer") "c" '(clone-indirect-buffer :wk "clone buffer") "C" '(clone-indirect-buffer-other-window :wk "clone buffer other window") "d" '(kill-current-buffer :wk "kill current buffer") "i" 'ibuffer "k" '(kill-buffer :wk "kill buffer") "l" '(evil-switch-to-windows-last-buffer :wk "Switch to last open buffer") "m" '((lambda () (interactive) (switch-to-buffer "*Messages*")) :wk "switch to messages buffer") "n" '(next-buffer :wk "next buffer") "N" '(evil-buffer-new :wk "New unnamed buffer") "p" '(previous-buffer :wk "previous buffer") "o" '((lambda () (interactive) (switch-to-buffer nil)) :wk "other-buffer") "r" '(revert-buffer-quick :wk "revert buffer") "R" '(rename-buffer :wk "rename buffer") "x" '((lambda () (interactive) (switch-to-buffer "*scratch*")) :wk "switch to scratch buffer") "z" '(bury-buffer :wk "bury buffer"))
Buffer-related keybindings. Some I don't use that much, like
ibuffer
, it's like dired for buffers.There are some keybindings that are bound to functions, like switching to Messages or scratch buffer.
- consult
Consult have too many commands it warrants a special heading in this keybindings.
- global
This is for temporary, ideally, it should go into separate categories.
(with-eval-after-load 'consult (general-define-key [remap Info-search] 'consult-info [remap yank-pop] 'consult-yank-pop [remap bookmark-jump] 'consult-bookmark [remap evil-show-marks] 'consult-mark [remap evil-show-registers] 'consult-register [remap goto-line] 'consult-goto-line [remap imenu] 'consult-imenu [remap Info-search] 'consult-info [remap locate] 'consult-locate [remap load-theme] 'consult-theme [remap man] 'consult-man [remap recentf-open-files] 'consult-recent-file [remap switch-to-buffer] 'consult-buffer [remap switch-to-buffer-other-frame] 'consult-buffer-other-frame [remap switch-to-buffer-other-window] 'consult-buffer-other-window) (+config/leader-key "M-g" '(:ignore t :wk "consult") "M-g M-:" 'consult-mode-command "M-g e" 'consult-compile-error "M-g G" 'consult-git-grep "M-g h" 'consult-history "M-g k" 'consult-kmacro "M-g m" 'consult-man "M-g i" 'consult-info "M-g :" 'consult-complex-command "M-g b" 'consult-bookmark))
- search
I'm trying to learn a more complex searching in Emacs, for faster navigation.
(with-eval-after-load 'consult (+config/leader-search "c" 'consult-locate "d" 'consult-find "4" '(:ignore t :wk "other window") "4 b" 'consult-buffer-other-window "5" '(:ignore t :wk "other frame") "5 b" 'consult-buffer-other-frame "B" 'consult-project-buffer "g" 'consult-goto-line "G" 'consult-grep "m" '(:ignore t :wk "imenu") "m" 'consult-imenu "M" 'consult-imenu-multi "M-g" 'consult-goto-line "o" 'consult-outline "l" 'consult-line "L" 'consult-line-multi "k" 'consult-keep-lines "s" '(:ignore t :wk "isearch") "e" 'consult-isearch-history "u" 'consult-focus-lines) (general-define-key :keymaps 'minibuffer-local-map "M-s" 'consult-history "M-r" 'consult-history))
If I happened to have the
fd
binary, also add this keybindings.(when (executable-find "fd") (+config/leader-search "f" 'consult-fd))
Also if I have
ripgrep
:(when (executable-find "rg") (+config/leader-search "R" 'consult-ripgrep))
In
org-mode
add this keybindings for navigating through headings.(+config/leader-search :keymaps 'org-mode-map "O" 'consult-org-heading)
- bookmark
(with-eval-after-load 'consult (+config/leader-search "b" '(:ignore t :wk "bookmark") "bb" 'consult-bookmark "bk" 'consult-global-mark "bm" 'consult-mark))
Also for the goal of faster text navigation. I think it's a feature I should use more.
- register
(with-eval-after-load 'consult (+config/leader-search "r" '(:ignore t :wk "register") "r #" '(consult-register :wk "consult-register") ;; require `consult' package ))
Still wrapping my head around the concept of registers. But I do hope to use this more.
- isearch
(with-eval-after-load 'consult (general-define-key :keymaps 'isearch-mode-map "M-e" 'consult-isearch-history "M-s e" 'consult-isearch-history "M-s l" 'consult-line "M-s L" 'consult-line-multi))
It's just normal Isearch wrapped in consult.
- global
- Editing
- smartparens
(with-eval-after-load 'smartparens (general-nvmap :keymaps 'smartparens-mode-map "<" 'sp-backward-barf-sexp "C-<" 'sp-backward-slurp-sexp ">" 'sp-forward-slurp-sexp "C->" 'sp-forward-barf-sexp) (general-define-key :keymaps 'smartparens-mode-map "M-DEL" 'sp-unwrap-sexp))
Smartparens is a wonderful package, once you know how to use it.
- evil-easymotion
(with-eval-after-load 'general (+config/leader-go "e" '(:keymap evilem-map :package evil-easymotion :wk "evilem")))
- smartparens
- dired
(general-nvmap 'dired-mode-map "g$" 'dired-hide-subdir "g?" 'dired-summary "gG" 'dired-do-chgrp "gj" 'dired-next-dirline "gk" 'dired-prev-dirline "go" 'dired-view-file "gO" 'dired-find-file-other-window "gy" 'dired-show-file-type)
Is this even needed?
- files
;; files (+config/leader-files "D" 'dired "d" 'dired-jump "f" '(find-file :wk "find file") "F" '(find-file-other-frame :wk "find file other frame") "k" 'delete-frame "r" 'recentf "S" '(write-file :wk "save file") "s" '(save-buffer :wk "save buffer") "w" '(find-file-other-window :wk "find file other window"))
Most of this is built-in, but some packages offer a much more better experience, like
consult-recent-file
:(with-eval-after-load 'consult (+config/leader-files "r" 'consult-recent-file))
Which have a preview-mode when navigating through files.
- help
;; help (+config/leader-key "h" (general-simulate-key "C-h" :state '(normal visual) :name general-SPC-h-simulates-C-h :docstring "Simulates C-h in normal and visual mode." :which-key "Help"))
This command is a wrapper for the actual
C-h
. - insert
;; insert (+config/leader-insert "u" '(insert-char :wk "insert character"))
for inserting special characters or symbols.
- mark
;; mark (+config/leader-search "bB" '(bookmark-jump-other-window :wk "jump to bookmark other window") "b C-c b" '(bookmark-jump-other-frame :wk "jump to bookmark other frame") "bc" '(consult-bookmark :wk "consult bookmark") ;; require `consult' package "bl" '(bookmark-bmenu-list :wk "list bookmarks") "bL" '(bookmark-load :wk "load bookmark") "bd" '(bookmark-delete :wk "delete bookmark") "bD" '(bookmark-delete-all :wk "delete all bookmarks") "bs" '(bookmark-save :wk "save bookmark") "br" '(bookmark-rename :wk "rename bookmark"))
Continuation from the bookmark section in consult earlier.
- open
;; open (+config/leader-open "i" '((lambda () (interactive) (if (not chemacs-profile) (find-file user-init-file) (find-file (expand-file-name "init.el" user-emacs-directory)))) :wk "open Emacs configuration file"))
Right now just for opening Emacs init file. And I'm tangling it from an org-file so it needs another addition.
(+config/leader-open "I" '((lambda () (interactive) (find-file (expand-file-name "index.org" user-emacs-directory))) :wk "open Emacs configuration file"))
- org-mode
This is the global keybidings.
(general-define-key :keymaps 'override "C-c l" '(org-store-link :wk "Store link") "C-c a" '(org-agenda :wk "Org Agenda") "C-c c" '(org-capture :wk "Org Capture") "C-c C-s" 'org-schedule)
For notes-related keybindings.
(+config/leader-notes "a" 'org-agenda "c" 'org-capture "C" 'org-capture-goto-last-stored "r" 'org-refile-goto-last-stored)
The keybindings under local leader (
SPC m
), honestly this is too much and I needs to reorganize this.(+config/local-leader :keymaps 'org-mode-map "C-a" 'org-attach "C-b" 'org-backward-heading-same-level "C-c" 'org-ctrl-c-ctrl-c "C-d" 'org-deadline "C-e" 'org-export-dispatch "C-f" 'org-forward-heading-same-level "C-j" 'org-goto "C-k" 'org-kill-note-or-show-branches "C-l" 'org-insert-link "C-n" 'org-next-visible-heading "C-o" 'org-open-at-point "C-p" 'org-previous-visible-heading "C-q" 'org-set-tags-command "C-r" 'org-fold-reveal "C-s" 'org-schedule "C-t" 'org-todo "C-w" 'org-refile "C-y" 'org-evaluate-time-range "C-z" 'org-add-note "M-b" 'org-previous-block "M-f" 'org-next-block "M-l" 'org-insert-last-stored-link "M-w" 'org-refile-copy "C-^" 'org-up-element "C-_" 'org-down-element "C-<" 'org-promote-subtree "C->" 'org-demote-subtree "C-," 'org-insert-structure-template "C-*" 'org-list-make-subtree "{" 'org-table-toggle-formula-debugger "}" 'org-table-toggle-coordinate-overlays "`" 'org-table-edit-field "\\" 'org-match-sparse-tree "^" 'org-sort "[" 'org-agenda-file-to-front "]" 'org-remove-file "@" 'org-mark-subtree "?" 'org-table-field-info "=" 'org-table-eval-formula ">" 'org-goto-calendar "+" 'org-table-sum "<" 'org-date-from-calendar ";" 'org-toggle-comment ":" 'org-toggle-fixed-width "/" 'org-sparse-tree "," 'org-priority "." 'org-timestamp "*" 'org-ctrl-c-star "-" 'org-ctrl-c-minus "#" 'org-update-statistics-cookies "$" 'org-archive-subtree "%" 'org-mark-ring-push "'" 'org-edit-special "TAB" '(org-clock-in :wk "clock-in") "!" '(org-reload :wk "reload org") "[" '(org-mark-ring-goto :wk "Jump to previous position in the mark ring") "b" '(:ignore t :wk "buffer") "bs" '(org-save-all-org-buffers :wk "save all org buffer") "f" '(:ignore t :wk "files") "fs" '(org-save-all-org-buffers :wk "save all org buffer") "s" '(org-schedule :wk "org schedule") "i" '(:ignore t :wk "insert") "id" '(org-insert-drawer :wk "insert drawer") "i@" '(org-cite-insert :wk "insert citation") "if" '(org-footnote-action :wk "footnote") "i C-f" '(org-emphasize :wk "insert emphasize") "il" '(org-insert-link :wk "insert link") "iD" '(org-deadline :wk "insert deadline") "is" '(org-schedule :wk "insert schedule") "ip" '(org-set-property :wk "insert property") "it" '(org-insert-time-stamp :wk "insert time-stamp at point"))
- quit
For quitting Emacs.
(+config/leader-quit "q" '(save-buffers-kill-terminal :wk "quit and save") "R" '(restart-emacs :wk "restart Emacs"))
- register
Not quite register since I moved most of it. Also
SPC g r
is just too many keystrokes.(+config/leader-register "M-w" '(copy-rectangle-as-kill :wk "copy region-rectangle and save") "c" '(clear-rectangle :wk "blank out region-rectangle") "d" '(delete-rectangle :wk "delete region-rectangle") "k" '(kill-rectangle :wk "cut rectangle into killed-rectangle") "l" '(bookmark-bmenu-list :wk "display existing bookmarks") "m" '(bookmark-set :wk "set bookmark") "M" '(bookmark-set-no-overwrite :wk "set bookmark no overwrite") "N" '(rectangle-number-lines :wk "insert number in front of region-rectangle") "o" '(open-rectangle :wk "blank out region-rectangle") "r" '(copy-rectangle-to-register :wk "copy rectangle-region to register") "t" '(string-rectangle :wk "replace rectangle with string") "w" '(window-configuration-to-register :wk "store window configuration to register") "y" '(yank-rectangle :wk "yank last killed rectangle with upper left corner at point"))
- window
For window navigation.
(+config/leader-window "C-o" '(delete-other-windows :wk "delete other windows") "[" '(evil-window-left :wk "left window") "]" '(evil-window-right :wk "right window") "+" '(enlarge-window :wk "enlarge window") "-" '(shrink-window :wk "shrink window") "}" '(enlarge-window-horizontally :wk "enlarge window horizontally") "{" '(shrink-window-horizontally :wk "shrink window horizontally") "+" 'evil-window-increase-height "-" 'evil-window-decrease-height ":" 'evil-ex "<" 'evil-window-decrease-width "=" 'balance-windows ">" 'evil-window-increase-height "_" 'evil-window-set-height "b" 'evil-window-bottom-right "c" 'evil-window-delete "d" '(delete-window :wk "delete window") "h" 'evil-window-left "f" '(ffap-other-window :wk "ffap other window") "j" 'evil-window-down "k" 'evil-window-up "l" 'evil-window-right "n" 'evil-window-new "p" 'evil-window-mru "q" 'evil-quit "r" 'evil-window-rotate-downwards "R" 'evil-window-rotate-upwards "s" '+evil/window-split-and-follow "T" '(tear-off-window :wk "tear off window") "t" 'evil-window-top-left "u" 'winner-undo "v" '+evil/window-vsplit-and-follow "w" '(other-window :wk "other window") "W" 'evil-window-prev "x" 'evil-window-exchange "|" 'evil-window-set-width "<left>" 'evil-window-left "<right>" 'evil-window-right "<down>" 'evil-window-down "<up>" 'evil-win-up)
- search
(+config/leader-search "r" '(:ignore t :wk "register") "rf" '(frameset-to-register :wk "store frameset to register") "rg" '(insert-register :wk "insert register") "ri" '(insert-register :wk "insert register") "rj" '(jump-to-register :wk "jump to register") "rn" '(number-to-register :wk "store a number in a register") "rs" '(copy-to-register :wk "copy region to register") "rx" '(copy-to-register :wk "copy region to register") "r+" '(increment-register :wk "augment content of register") "r C-@" '(point-to-register :wk "store current point to register") "r C-SPC" '(point-to-register :wk "store current point to register") "r SPC" '(point-to-register :wk "store current point to register") )
- First level menu
UI
I suppose here is the most external packages used.
- Doom themes and modelines
- doom-themes external
A megapack of themes for GNU Emacs. I'm not the kind of guy who enjoys ricing, I look every once in a while for a nice colorscheme and then I just stick it everywhere else. Been a solarized fans most of the time, now I'm trying nord.
(use-package doom-themes :demand t :config ;; Global settings (defaults) (setq doom-themes-enable-bold t ; if nil, bold is universally disabled doom-themes-enable-italic t) ; if nil, italics is universally disabled (load-theme 'doom-nord t) ;; Enable flashing mode-line on errors (doom-themes-visual-bell-config) ;; Enable custom neotree theme (all-the-icons must be installed!) ;; (doom-themes-neotree-config) ;; or for treemacs users (setq doom-themes-treemacs-theme "doom-atom") ; use "doom-colors" for less minimal icon theme (doom-themes-treemacs-config) ;; Corrects (and improves) org-mode's native fontification. (doom-themes-org-config))
- doom-modeline external
A fancy and fast mode-line inspired by minimalism design. I'm using their themes, why not the modeline as well? I used mostly the default settings apart from project detection and indent info for showing the current indentation style (useful when editing yaml files).
(use-package doom-modeline :init (doom-modeline-mode 1) :config (with-eval-after-load 'projectile (setq doom-modeline-project-detection 'projectile)) (setq doom-modeline-indent-info t))
- doom-themes external
- ace-window external
(use-package posframe) (use-package ace-window :init (when (display-graphic-p) (ace-window-posframe-mode t)) :config (setq aw-keys '(?q ?w ?e ?r ?t ?a ?s ?d ?f ?z ?x ?c ?v) aw-background nil aw-scope 'frame aw-frame-size 2) (custom-set-faces '(aw-leading-char-face ((t (:inherit ace-jump-face-foreground :height 4.0))))) (with-eval-after-load 'general (+config/leader-window "S" 'ace-swap-window)) :bind ([remap other-window] . ace-window))
Setting
aw-scope
toframe
so it doesn't select other frames. - all-the-icons external
A utility package to collect various Icon Fonts and propertize them within Emacs.
(use-package all-the-icons :if (display-graphic-p))
This needs a manual intervention from the user to install the fonts (by running the
all-the-icons-install-fonts
command). - page-break-lines
(use-package page-break-lines)
- dashboard external
An extensible emacs dashboard.
My sole reason for using dashboard is to avoid seeing an empty scratch buffer each time I opened a new Emacs frame.
(use-package dashboard :demand t :config (setq initial-buffer-choice (lambda () (get-buffer-create dashboard-buffer-name)) dashboard-startup-banner 'logo dashboard-center-content t dashboard-vertically-center-content t dashboard-navigation-cycle t dashboard-icon-type 'all-the-icons dashboard-heading-icons '((recents . "history") (bookmarks . "bookmark") (agenda . "calendar") (projects . "rocket") (registers . "database")) dashboard-items '((recents . 3) (bookmarks . 2) (projects . 5) (agenda . 8) (registers . 2))) (with-eval-after-load 'nerd-icons (setq dashboard-icon-type 'nerd-icons)) (with-eval-after-load 'projectile (setq dashboard-projects-backend 'projectile)) (with-eval-after-load 'persp-projectile (setq dashboard-projects-switch-function 'projectile-persp-switch-project)) (dashboard-setup-startup-hook))
- which-key external
Emacs package that displays available keybindings in popup. This is one of the top Emacs packages that I must have in my configuration.
(use-package which-key :demand t :custom (which-key-lighter "") (which-key-sort-order #'which-key-key-order-alpha) (which-key-sort-uppercase-first nil) (which-key-add-column-padding 1) (which-key-max-display-columns nil) (which-key-min-display-lines 6) (which-key-compute-remaps t) (which-key-side-window-slot -10) (which-key-separator " → ") (which-key-allow-evil-operators t) (which-key-use-C-h-commands t) (which-key-show-remaining-keys t) (which-key-show-prefix 'bottom) (which-key-show-operator-state-maps t) :config (which-key-mode) (which-key-setup-side-window-bottom) (which-key-setup-minibuffer) (define-key which-key-mode-map (kbd "C-x <f5>") 'which-key-C-h-dispatch))
- Fonts
(set-face-attribute 'default nil :family "Iosevka Nerd Font Mono" :height 100) (set-face-attribute 'variable-pitch nil :family "Iosevka Nerd Font Mono" :height 100)
the
:height
value is 1/10 pt. - rainbow-mode, rainbow-identifiers, and rainbow-delimiters external
Making Emacs more colourful.
(use-package rainbow-mode :hook (prog-mode . rainbow-mode) :config (setq rainbow-html-colors-major-mode-list '(prog-mode conf-mode html-mode css-mode php-mode nxml-mode xml-mode) rainbow-html-colors t)) (use-package rainbow-identifiers :hook (prog-mode . rainbow-identifiers-mode)) (use-package rainbow-delimiters :demand t :hook (prog-mode . rainbow-delimiters-mode) :config (setq rainbow-delimiters-max-face-count 4))
- helpful external
Since Emacs is a self-documenting editor. It's certainly nice to have a more contextual help buffers.
Figure 2: Helpful will also show the source code when available
Figure 3: more prettier docstring
Figure 4: helpful will also display any keybindings related
(use-package helpful :bind ("C-h f" . helpful-function) ([remap describe-symbol] . helpful-symbol) ([remap describe-variable] . helpful-variable) ([remap describe-command] . helpful-command) ([remap describe-key] . helpful-key) :custom (helpful-max-buffers 2) :config (with-eval-after-load 'apropos (dolist (fun-bt '(apropos-function apropos-macro apropos-command)) (button-type-put fun-bt 'action (lambda (button) (helpful-callable (button-get button 'apropos-symbol))))) (dolist (var-bt '(apropos-variable apropos-user-option)) (button-type-put var-bt 'action (lambda (button) (helpful-variable (button-get button 'apropos-symbol)))))) (with-eval-after-load 'general (general-define-key "C-h F" 'helpful-function :keymaps 'prog-mode-map "C-c C-d" 'helpful-at-point) ;; (general-nvmap ;; :keymaps 'prog-mode-map ;; "K" '(helpful-at-point :wk "helpful at point") ;; "gr" '(helpful-update :wk "update"))) ))
- hl-line builtin
Enable line highlighting in all buffers.
(use-package hl-line :config (global-hl-line-mode 1))
- highlight-quoted
(use-package highlight-quoted :hook ((lisp-mode lisp-interaction-mode) . highlight-quoted-mode))
- hs-mode
(use-package hideshow :hook (prog-mode . hs-minor-mode))
- paren builtin
This built-in package is necessary when dealing with Emacs lisp.
(use-package paren :config (show-paren-mode 1) :custom (show-paren-style 'mixed) (show-paren-delay 0.1) (show-paren-highlight-openparen t) (show-paren-when-point-inside-paren t) (show-paren-when-point-in-periphery t))
- mouse
(use-package mouse :straight nil :config (setq mouse-yank-at-point t))
- Base UI
(use-package emacs :straight nil :hook ((prog-mode text-mode conf-mode) . display-line-numbers-mode) :config (setq hscroll-margin 2 hscroll-step 1 ;; Emacs spends too much effort recentering the screen if you scroll the ;; cursor more than N lines past window edges (where N is the settings of ;; `scroll-conservatively'). This is especially slow in larger files ;; during large-scale scrolling commands. If kept over 100, the window is ;; never automatically recentered. The default (0) triggers this too ;; aggressively, so I've set it to 10 to recenter if scrolling too far ;; off-screen. scroll-conservatively 10 scroll-margin 0 scroll-preserve-screen-position t ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll' ;; for tall lines. auto-window-vscroll nil ;; mouse mouse-wheel-scroll-amount '(2 ((shift) . hscroll)) mouse-wheel-scroll-amount-horizontal 2) ;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any ;; feedback after typing is better UX than no feedback at all. (setq echo-keystrokes 0.02) ;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This ;; doesn't look too great with direnv, however... (setq resize-mini-windows 'grow-only) ;; Try to keep the cursor out of the read-only portions of the minibuffer. (setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) ;; Show absolute line numbers for narrowed regions to make it easier to tell the ;; buffer is narrowed, and where you are, exactly. (setq-default display-line-numbers-widen t) )
- frame
(use-package frame :straight nil :hook (after-init . window-divider-mode) :config (blink-cursor-mode -1) (setq window-divider-default-places t window-divider-default-bottom-width 1 window-divider-default-right-width 1))
- window
(use-package window :straight nil :config (setq split-width-threshold 160 split-height-threshold nil))
- comint
(use-package comint :straight nil :config (setq comint-prompt-read-only t comint-buffer-maximum-size 2048))
- winner
(use-package winner :straight nil :init (winner-mode +1) :config (setq winner-boring-buffers '("*Completions*" "*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*" "*esh command on file*")))
- visual-regexp
(use-package visual-regexp :config (with-eval-after-load 'general (+config/leader-search "/" '(:ignore t :wk "regex") "/m" 'vr/mc-mark "/r" 'vr/replace "/q" 'vr/query-replace)))
- symbol-overlay
Highlight symbols with keymap-enabled overlays.
(use-package symbol-overlay :hook (prog-mode . (lambda () symbol-overlay-mode +1)) :config (with-eval-after-load 'general (+config/leader-search :keymaps 'symbol-overlay-map "s" '(:ignore t :wk "symbol-overlay") "ss" 'symbol-overlay-put "sr" 'symbol-overlay-remove-all)))
- solaire-mode
If only certain buffers could be so grossly incandescent.
(use-package solaire-mode :init (solaire-global-mode +1))
Builtins
- base
Usually configuration that defined at the C source code.
(use-package emacs :straight nil :custom (read-buffer-completion-ignore-case t) (tab-always-indent nil) (visible-bell nil) (use-short-answers t) (use-dialog-box nil) (window-resize-pixelwise nil) (frame-resize-pixelwise t) (ring-bell-function #'ignore) (scroll-preserve-screen-position t) (scroll-conservatively 101) (fast-but-imprecise-scrolling t) (truncate-partial-width-windows nil) (tab-width 4) (fill-column 80) (enable-recursive-minibuffers t) (use-file-dialog nil) (create-lockfiles nil) (delete-by-moving-to-trash t) (inhibit-startup-screen t) :config (when (bound-and-true-p tooltip-mode) (tooltip-mode -1)) (setq completion-ignore-case t read-file-name-completion-ignore-case t read-buffer-completion-ignore-case t load-prefer-newer t auto-window-vscroll nil inhibit-compacting-font-caches t redisplay-skip-fontification-on-input t) (set-default 'indicate-empty-lines nil) (setq-default truncate-lines t) (setq-default x-stretch-cursor nil))
- tooltip
(use-package tooltip :straight nil :config (when (bound-and-true-p tooltip-mode) (tooltip-mode -1)))
- compile
(use-package compile :straight nil :hook (compilation-filter . ansi-color-compilation-filter) :config (setq compilation-always-kill t ; kill compilation process before starting another compilation-ask-about-save nil ; save all buffers on `compile' compilation-scroll-output 'first-error) ;; Automatically truncate compilation buffers so they don't accumulate too ;; much data and bog down the rest of Emacs. (autoload 'comint-truncate-buffer "comint" nil t) (add-hook 'compilation-filter-hook #'comint-truncate-buffer))
- files
Usually configurations related to file manipulations.
(use-package files :straight nil :config (nconc auto-mode-alist '(("/LICENSE\\'" . text-mode) ("\\.log\\'" . text-mode) ("rc\\'" . conf-mode) ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode))) :hook ((prog-mode text-mode) . auto-save-visited-mode) :custom (auto-save-visited-interval 10) (find-file-suppress-same-file-warnings t) (confirm-kill-emacs #'yes-or-no-p) ; confirm when exiting (confirm-kill-processes nil) ; don't confirm killing processes (revert-without-query (list ".")) (find-file-visit-truename t) ; `find-file' will visit the actual file (make-backup-files nil) (version-control t) (backup-by-copying t) (delete-old-versions t) (kept-new-versions 6) (kept-old-versions 2) (auto-save-include-big-deletions t) (auto-save-list-file-prefix (expand-file-name ".autosave/" user-emacs-directory)) (backup-directory-alist `(("." . ,(expand-file-name ".backup" user-emacs-directory)))) (auto-mode-case-fold nil) (require-final-newline t))
- saveplace
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
Here I'm using
global-auto-revert-mode
to automatically revert all opened buffer.(use-package autorevert :hook (focus-in . doom-auto-revert-buffers-h) :hook (after-save . doom-auto-revert-buffers-h) :custom (auto-revert-interval 60) (auto-revert-use-notify nil) (global-auto-revert-non-file-buffers t) (auto-revert-verbose t) (auto-revert-stop-on-user-input nil) (revert-without-query (list ".")) :config (defun doom-visible-buffer-p (buf) "Return non-nil if BUF is visible." "Return non-nil if BUF is not visible." (not (doom-visible-buffer-p buf))) (defun doom-visible-buffers (&optional buffer-list all-frames) "Return a list of visible buffers (i.e. not buried)." (let ((buffers (delete-dups (cl-loop for frame in (if all-frames (visible-frame-list) (list (selected-frame))) if (window-list frame) nconc (mapcar #'window-buffer it))))) (if buffer-list (cl-delete-if (lambda (b) (memq b buffer-list)) buffers) (delete-dups buffers)))) (defun doom-auto-revert-buffer-h () "Auto revert current buffer, if necessary." (unless (or auto-revert-mode (active-minibuffer-window)) (let ((auto-revert-mode t)) (auto-revert-handler)))) (defun doom-auto-revert-buffers-h () "Auto revert stale buffers in visible windows, if necessary." (dolist (buf (doom-visible-buffers)) (with-current-buffer buf (doom-auto-revert-buffer-h)))))
- savehist
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
(use-package recentf :bind ("C-c f" . recentf) :commands recentf-open-files :hook (dired-mode . doom--recentf-add-dired-directory-h) :custom (recentf-auto-cleanup t) (recentf-max-saved-items 250) (recentf-max-menu-items 300) (recentf-exclude `("/elpa/" ;; ignore all files in elpa directory "recentf" ;; remove the recentf load file ".*?autoloads.el$" "treemacs-persist" "company-statistics-cache.el" ;; ignore company cache file "/intero/" ;; ignore script files generated by intero "/journal/" ;; ignore daily journal files ".gitignore" ;; ignore `.gitignore' files in projects "/tmp/" ;; ignore temporary files "NEWS" ;; don't include the NEWS file for recentf "bookmarks" "bmk-bmenu" ;; ignore bookmarks file in .emacs.d "loaddefs.el" "^/\\(?:ssh\\|su\\|sudo\\)?:" ;; ignore tramp/ssh files (concat "^" (regexp-quote (or (getenv "XDG_RUNTIME_DIR") "/run"))))) :config (defun doom--recentf-file-truename-fn (file) (if (or (not (file-remote-p file)) (equal "sudo" (file-remote-p file 'method))) (abbreviate-file-name (file-truename (tramp-file-name-localname file))) file)) ;; Resolve symlinks, strip out the /sudo:X@ prefix in local tramp paths, and ;; abbreviate $HOME -> ~ in filepaths (more portable, more readable, & saves ;; space) (add-to-list 'recentf-filename-handlers #'doom--recentf-file-truename-fn) ;; Text properties inflate the size of recentf's files, and there is ;; no purpose in persisting them (Must be first in the list!) (add-to-list 'recentf-filename-handlers #'substring-no-properties) (defun doom--recentf-add-dired-directory-h () "Add dired directories to recentf file list." (recentf-add-file default-directory)) ;; The most sensible time to clean up your recent files list is when you quit ;; Emacs (unless this is a long-running daemon session). (setq recentf-auto-cleanup (if (daemonp) 300)) (add-hook 'kill-emacs-hook #'recentf-cleanup) )
Even though I use consult-find-file the actual recentf package is still used (I think).
- server
(use-package server :if (display-graphic-p) :defer 1 :config (unless (server-running-p) (server-start)) (require 'org-protocol))
- prog-mode
(use-package prog-mode :straight nil :hook ((prog-mode . prettify-symbols-mode) (prog-mode . visual-line-mode) ;; (prog-mode . (lambda () (electric-pair-mode 1))) ) :config (setq prettify-symbols-alist '(("|>" . "▷ ") ("<|" . "◁ ") ("->>" . "↠ ") ("->" . "→ ") ("<-" . "← ") ("=>" . "⇒ ") ("<=" . "≤ ") (">=" . "≥ "))))
Basically, in
prog-mode
, some combination of characters will look different. - bookmark
(use-package bookmark :custom (bookmark-save-flag 1) (bookmark-default-file (expand-file-name ".bookmark" user-emacs-directory)))
The configuration for bookmark.
- tramp
(use-package tramp :straight nil :config (setq tramp-remote-path (append '("~/.guix-profile/bin" "~/.guix-profile/sbin" "~/.config/guix/current/bin" "~/.config/guix/current/sbin" "/run/current-system/profile/bin" "/run/current-system/profile/sbin") tramp-remote-path)) (connection-local-set-profile-variables 'guix-system '((tramp-remote-path . (tramp-own-remote-path)))) :custom (tramp-backup-directory-alist backup-directory-alist) (tramp-auto-save-directory (expand-file-name ".tramp-autosave/" user-emacs-directory)))
I'm still figuring out how to do a proper TRAMP connection into a guix-system machine.
- epg-config
(use-package epg-config :straight nil :custom (epg-pinentry-mode 'loopback))
This is for Gnupg.
- tabify
(use-package tabify :straight nil :custom (tabify-regex "^\t* [ \t]+"))
- simple
(use-package simple :straight nil :custom (blink-matching-paren nil) (save-interprogram-paste-before-kill t) (shift-select-mode nil) (kill-do-not-save-duplicates t) (shift-select-mode nil) (set-mark-command-repeat-pop t) (indent-tabs-mode nil) (column-number-mode t) (idle-update-delay 1.0) :config (with-eval-after-load 'evil (evil-set-initial-state #'message-mode 'insert)))
The important part is the Evil integration, which is should be handled already by evil-collection.
- vc-hooks
By default visiting / opening symlinked file will ask if we want to visit the actual file.
(use-package vc-hooks :straight nil :custom (vc-follow-symlinks t))
- dabbrev
This is part of corfu configuration.
;; Use Dabbrev with Corfu! (use-package dabbrev ;; Swap M-/ and C-M-/ :bind (("M-/" . dabbrev-completion) ("C-M-/" . dabbrev-expand)) :config (add-to-list 'dabbrev-ignored-buffer-regexps '("\\` " "\\(TAGS\\|tags\\|ETAGS\\|etags\\|GTAGS\\|GRTAGS\\|GPATH\\)\\(<[0-9]+>\\)?")) ;; Since 29.1, use `dabbrev-ignored-buffer-regexps' on older. (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode) (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode) (add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode))
- repeat-mode
(use-package repeat :straight nil :init (repeat-mode +1))
- so-long
(use-package so-long :straight nil :init (global-so-long-mode) :config ;; Emacs 29 introduced faster long-line detection, so they can afford a much ;; larger `so-long-threshold' and its default `so-long-predicate'. (if (fboundp 'buffer-line-statistics) (unless (featurep 'native-compile) (setq so-long-threshold 5000)) ;; reduce false positives w/ larger threshold (setq so-long-threshold 400)) (defun doom-buffer-has-long-lines-p () (unless (bound-and-true-p visual-line-mode) (let ((so-long-skip-leading-comments ;; HACK Fix #2183: `so-long-detected-long-line-p' calls ;; `comment-forward' which tries to use comment syntax, which ;; throws an error if comment state isn't initialized, leading ;; to a wrong-type-argument: stringp error. ;; DEPRECATED Fixed in Emacs 28. (bound-and-true-p comment-use-syntax))) (so-long-detected-long-line-p)))) (setq so-long-predicate #'doom-buffer-has-long-lines-p))
- ediff
(use-package ediff :straight nil :config (setq ediff-diff-options "-w" ; turn off whitespace checking ediff-split-window-function #'split-window-horizontally ediff-window-setup-function #'ediff-setup-windows-plain))
Completion
- Vertico frameworks
I call this framework since usually these packages are what people use together, though they work fine separately.
- vertico
- Basic configuration
(use-package vertico :straight (vertico :files (:defaults "extensions/*.el") :includes (vertico-buffer vertico-directory vertico-flat vertico-grid vertico-indexed vertico-mouse vertico-multiform vertico-quick vertico-reverse vertico-repeat vertico-suspend vertico-unobstrusive )) :hook (rfn-eshadow-update-overlay . vertico-directory-tidy) (minibuffer-setup . vertico-repeat-save) :init (vertico-mode) ;; Different scroll margin (setq vertico-scroll-margin 0) ;; Show more candidates (setq vertico-count 17) ;; Grow and shrink the Vertico minibuffer (setq vertico-resize nil) ;; Optionally enable cycling for `vertico-next' and `vertico-previous'. (setq vertico-cycle t) :config (advice-add #'tmm-add-prompt :after #'minibuffer-hide-completions) (with-eval-after-load 'general (general-define-key :keymaps 'vertico-map "?" 'minibuffer-completion-help "M-RET" 'minibuffer-force-complete-and-exit "M-TAB" 'minibuffer-complete "<tab>" 'vertico-insert "<escape>" 'minibuffer-keyboard-quit "C-M-n" 'vertico-next-group "C-M-p" 'vertico-previous-group "C-j" 'vertico-next "C-k" 'vertico-previous "C-M-j" 'vertico-next-group "C-M-k" 'vertico-previous-group "DEL" 'vertico-directory-delete-char) (general-define-key :keymaps 'minibuffer-local-map "M-h" 'backward-kill-word))) ;; A few more useful configurations... (use-package emacs :custom ;; Support opening new minibuffers from inside existing minibuffers. (enable-recursive-minibuffers t) ;; Emacs 28 and newer: Hide commands in M-x which do not work in the current ;; mode. Vertico commands are hidden in normal buffers. This setting is ;; useful beyond Vertico. (read-extended-command-predicate #'command-completion-default-include-p) :init ;; Add prompt indicator to `completing-read-multiple'. ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma. (defun crm-indicator (args) (cons (format "[CRM%s] %s" (replace-regexp-in-string "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" crm-separator) (car args)) (cdr args))) (advice-add #'completing-read-multiple :filter-args #'crm-indicator) ;; Do not allow the cursor in the minibuffer prompt (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))
- Extensions
- vertico-directory
(use-package vertico-directory :after vertico :straight nil ;; More convenient directory navigation commands :bind (:map vertico-map ("RET" . vertico-directory-enter) ("DEL" . vertico-directory-delete-char) ("M-DEL" . vertico-directory-delete-word)) ;; Tidy shadowed file names :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
- vertico-quick
(use-package vertico-quick :after vertico :straight nil :bind (:map vertico-map ("M-q" . vertico-quick-insert) ("C-q" . vertico-quick-exit)))
- vertico-multiform
(use-package vertico-multiform :straight nil :init (vertico-multiform-mode) :config (setq vertico-multiform-commands `((describe-symbol (vertico-sort-function . vertico-sort-alpha)) (consult-outline buffer ,(lambda (_) (text-scale-set -1))) (org-ctrl-c-ctrl-c flat))) (defun sort-directories-first (files) (setq files (vertico-sort-history-length-alpha files)) (nconc (seq-filter (lambda (x) (string-suffix-p "/" x)) files) (seq-remove (lambda (x) (string-suffix-p "/" x)) files))) (setq vertico-multiform-categories '((symbol (vertico-sort-function . vertico-sort-alpha)) (file (vertico-sort-function . sort-directories-first)))) )
- vertico-directory
- Basic configuration
- marginalia
;; Enable rich annotations using the Marginalia package (use-package marginalia :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle)) :init (marginalia-mode))
- corfu
- Basic Configuration
(use-package corfu :custom (corfu-cycle t) (corfu-auto t) (corfu-auto-delay 0.18) (corfu-auto-prefix 2) (corfu-quit-no-match 'separator) (corfu-preselect 'prompt) (corfu-count 16) (corfu-max-width 120) (corfu-on-exact-match nil) (corfu-quit-no-match corfu-quit-at-boundary) (completion-cycle-threshold 3) (text-mode-ispell-word-completion nil) :config (add-to-list 'completion-category-overrides `(lsp-capf (styles ,@completion-styles))) (add-to-list 'corfu-auto-commands #'lispy-colon) (add-hook 'evil-insert-state-exit-hook #'corfu-quit) (with-eval-after-load 'exwm (advice-add #'corfu--make-frame :around (defun +corfu--make-frame-a (oldfun &rest args) (cl-letf (((symbol-function #'frame-parent) (lambda (frame) (or (frame-parameter frame 'parent-frame) exwm-workspace--current)))) (apply oldfun args)) (when exwm--connection (set-frame-parameter corfu--frame 'parent-frame nil)))) (advice-add #'corfu--popup-redirect-focus :override (defun +corfu--popup-redirect-focus-a () (redirect-frame-focus corfu--frame (or (frame-parent corfu--frame) exwm-workspace--current))))) (defun corfu-enable-always-in-minibuffer () "Enable Corfu in the minibuffer if Vertico/Mct are not active." (unless (or (bound-and-true-p mct--active) (bound-and-true-p vertico--input) (eq (current-local-map) read-passwd-map)) ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup corfu-popupinfo-delay nil) (corfu-mode 1))) (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) :init (global-corfu-mode))
This is the basic configuration, with the addition of enabling corfu in the minibuffer.
- corfu-history
(use-package corfu-history :straight nil :hook ((corfu-mode . corfu-history-mode)) :config (with-eval-after-load 'savehist (add-to-list 'savehist-additional-variables 'corfu-history)))
- corfu-popupinfo
(use-package corfu-popupinfo :straight nil :hook ((corfu-mode . corfu-popupinfo-mode)) :config (setq corfu-popupinfo-delay '(0.5 . 1.0)))
- Basic Configuration
- consult
- Basic Configuration
(use-package consult :hook (completion-list-mode . consult-preview-at-point-mode) :init ;; Optionally configure the register formatting. This improves the register ;; preview for `consult-register', `consult-register-load', ;; `consult-register-store' and the Emacs built-ins. (setq register-preview-delay 0.5 register-preview-function #'consult-register-format) ;; Optionally tweak the register preview window. ;; This adds thin lines, sorting and hides the mode line of the window. (advice-add #'register-preview :override #'consult-register-window) ;; Use Consult to select xref locations with preview (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) ;; Configure other variables and modes in the :config section, ;; after lazily loading the package. :config ;; Optionally configure preview. The default value ;; is 'any, such that any key triggers the preview. ;; (setq consult-preview-key 'any) ;; (setq consult-preview-key "M-.") ;; (setq consult-preview-key '("S-<down>" "S-<up>")) ;; For some commands and buffer sources it is useful to configure the ;; :preview-key on a per-command basis using the `consult-customize' macro. (consult-customize consult-theme :preview-key '(:debounce 0.2 any) consult-ripgrep consult-git-grep consult-grep consult-bookmark consult-recent-file consult-xref consult--source-bookmark consult--source-file-register consult--source-recent-file consult--source-project-recent-file ;; :preview-key "M-." :preview-key '(:debounce 0.4 any)) ;; Optionally configure the narrowing key. ;; Both < and C-+ work reasonably well. (setq consult-narrow-key "<") ;; "C-+" ;; Optionally make narrowing help available in the minibuffer. ;; You may want to use `embark-prefix-help-command' or which-key instead. ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help) ;; By default `consult-project-function' uses `project-root' from project.el. ;; Optionally configure a different project root function. ;;;; 1. project.el (the default) ;; (setq consult-project-function #'consult--default-project--function) ;;;; 2. vc.el (vc-root-dir) ;; (setq consult-project-function (lambda (_) (vc-root-dir))) ;;;; 3. locate-dominating-file ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git"))) ;;;; 4. projectile.el (projectile-project-root) (with-eval-after-load 'projectile (autoload 'projectile-project-root "projectile") (setq consult-project-function (lambda (_) (projectile-project-root)))) ;;;; 5. No project support ;; (setq consult-project-function nil)) (setq consult-line-numbers-widen t consult-async-min-input 2 consult-async-refresh-delay 0.15 consult-async-input-throttle 0.2 consult-async-input-debounce 0.1 ) )
Apart from the
consult-projectile-function
, this is pretty much the default. - consult-dir
(use-package consult-dir :config (with-eval-after-load 'general (general-define-key :keymaps 'vertico-map "C-x C-d" 'consult-dir "C-x C-j" 'consult-dir-jump-file)))
- Basic Configuration
- orderless
- Basic Configuration
(use-package orderless :init ;; Configure a custom style dispatcher (see the Consult wiki) ;; (setq orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch) ;; orderless-component-separator #'orderless-escapable-split-on-space) (setq completion-styles '(orderless basic substring partial-completion) completion-category-defaults nil completion-category-overrides '((file (styles orderless partial-completion))) orderless-component-separator #'orderless-escapable-split-on-space) (set-face-attribute 'completions-first-difference nil :inherit nil))
partial-completion
andflex
are built-ins completion-styles.
- Basic Configuration
- embark
(use-package embark :defer t :bind (("C-." . embark-act) ;; pick some comfortable binding ("C-;" . embark-dwim) ;; good alternative: M-. ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' :init ;; Optionally replace the key help with a completing-read interface (setq prefix-help-command #'embark-prefix-help-command) ;; Show the Embark target at point via Eldoc. You may adjust the ;; Eldoc strategy, if you want to see the documentation from ;; multiple providers. Beware that using this can be a little ;; jarring since the message shown in the minibuffer can be more ;; than one line, causing the modeline to move up and down: ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) :config (setq which-key-use-C-h-commands nil prefix-help-command #'embark-prefix-help-command) (with-eval-after-load 'general (general-define-key :keymaps 'override "C-." 'embark-act "C-;" 'embark-dwim "C-h B" 'embark-bindings) (general-define-key :keymaps 'minibuffer-local-map "C-;" 'embark-act "C-c C-;" 'embark-export "C-c C-l" 'embark-collect) (+config/leader-key "." 'embark-act)) ;; Hide the mode line of the Embark live/completions buffers (add-to-list 'display-buffer-alist '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" nil (window-parameters (mode-line-format . none)))))
This is a package that I should learn more, but the configuration is overwhealming.
- embark-consult
(use-package embark-consult :hook (embark-collect-mode . consult-preview-at-point-mode))
consult integration for embark.
- affe
Asynchronous Fuzzy Finder for Emacs.
(use-package affe :config ;; Manual preview key for `affe-grep' (consult-customize affe-grep :preview-key "M-.") (defun affe-orderless-regexp-compiler (input _type _ignorecase) (setq input (cdr (orderless-compile input))) (cons input (apply-partially #'orderless--highlight input t))) (setq affe-regexp-compiler #'affe-orderless-regexp-compiler) (with-eval-after-load 'general (+config/leader-search "a" 'affe-grep "A" 'affe-find)))
- cape
(use-package cape :defer t ;; Bind dedicated completion commands ;; Alternative prefix keys: C-c p, M-p, M-+, ... :bind (("C-c p p" . completion-at-point) ;; capf ("C-c p t" . complete-tag) ;; etags ("C-c p d" . cape-dabbrev) ;; or dabbrev-completion ("C-c p h" . cape-history) ("C-c p f" . cape-file) ("C-c p k" . cape-keyword) ("C-c p s" . cape-elisp-symbol) ("C-c p e" . cape-elisp-block) ("C-c p a" . cape-abbrev) ("C-c p l" . cape-line) ("C-c p w" . cape-dict) ("C-c p :" . cape-emoji) ("C-c p \\" . cape-tex) ("C-c p _" . cape-tex) ("C-c p ^" . cape-tex) ("C-c p &" . cape-sgml) ("C-c p r" . cape-rfc1345)) :hook (prog-mode . +corfu-add-cape-file-h) ((org-mode markdown-mode) . +corfu-add-cape-elisp-block-h) :init ;; Add to the global default value of `completion-at-point-functions' which is ;; used by `completion-at-point'. The order of the functions matters, the ;; first function returning a result wins. Note that the list of buffer-local ;; completion functions takes precedence over the global list. (add-hook 'completion-at-point-functions #'cape-dabbrev) (add-hook 'completion-at-point-functions #'cape-file) (add-hook 'completion-at-point-functions #'cape-elisp-block) (add-hook 'completion-at-point-functions #'cape-history) (add-hook 'completion-at-point-functions #'cape-keyword) ;;(add-hook 'completion-at-point-functions #'cape-tex) ;;(add-hook 'completion-at-point-functions #'cape-sgml) ;;(add-hook 'completion-at-point-functions #'cape-rfc1345) ;;(add-hook 'completion-at-point-functions #'cape-abbrev) (add-hook 'completion-at-point-functions #'cape-dict) ;;(add-hook 'completion-at-point-functions #'cape-elisp-symbol) ;; (add-hook 'completion-at-point-functions #'cape-line) :config (setq cape-dabbrev-check-other-buffers t) (defun +corfu-add-cape-file-h () (add-hook 'completion-at-point-functions #'cape-file -10 t)) (defun +corfu-add-cape-elisp-block-h () (add-hook 'completion-at-point-functions #'cape-elisp-block 0 t)) (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible) (advice-add #'lsp-completion-at-point :around #'cape-wrap-nonexclusive) (advice-add #'comint-completion-at-point :around #'cape-wrap-nonexclusive) (advice-add #'eglot-completion-at-point :around #'cape-wrap-nonexclusive) (advice-add #'pcomplete-completions-at-point :around #'cape-wrap-nonexclusive) ;; From the `cape' readme. Without this, Eshell autocompletion is broken on ;; Emacs28. (when (< emacs-major-version 29) (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)))
- jinx
Enchanted Spell Checker. Here I use hunspell as a backend dictionary
(use-package jinx :if (executable-find "enchant-2") :hook (emacs-startup . global-jinx-mode) :config (with-eval-after-load 'general (general-define-key :keymaps 'override [remap ispell-word] 'jinx-correct "C-M-$" 'jinx-languages)))
- tempel
Simple templates for Emacs.
(use-package tempel :init (defun tempel-setup-capf () ;; Add the Tempel Capf to `completion-at-point-functions'. ;; `tempel-expand' only triggers on exact matches. Alternatively use ;; `tempel-complete' if you want to see all matches, but then you ;; should also configure `tempel-trigger-prefix', such that Tempel ;; does not trigger too often when you don't expect it. NOTE: We add ;; `tempel-expand' *before* the main programming mode Capf, such ;; that it will be tried first. (setq-local completion-at-point-functions (cons #'tempel-expand completion-at-point-functions))) (global-tempel-abbrev-mode) :hook ((conf-mode prog-mode text-mode) . tempel-setup-capf) :config (with-eval-after-load 'general (general-define-key :keymaps 'general "M-+" 'tempel-complete "M-*" 'tempel-insert))) (use-package tempel-collection :after tempel :load-path "site-lisp/tempel-collection")
- vertico
Shells
- eshell
A shell-like command interpreter implemented in Emacs Lisp.
(use-package eshell :straight nil :custom (eshell-history-size 10000) (eshell-hist-ignore-dups t) (eshell-buffer-maximum-lines 10000) (eshell-scroll-to-bottom-on-input t) (eshell-destroy-buffer-when-process-dies t) (eshell-prompt-regexp "^[^\)]*[\)] "))
- eshell-syntax-highlighting
(use-package eshell-syntax-highlighting :defer t :after eshell :config (eshell-syntax-highlighting-global-mode +1))
- sh-script
(use-package sh-script :mode ("\\.bats\\'" . sh-mode) :mode ("\\.\\(?:zunit\\|env\\)\\'" . sh-mode) :mode ("/bspwmrc\\'" . sh-mode) :hook (sh-mode-local-vars . lsp-deferred) :hook (sh-mode-local-vars . tree-sitter-mode) :config (with-eval-after-load 'lsp (add-hook 'sh-mode-hook #'lsp-deferred) (add-hook 'shell-mode-hook #'lsp-deferred) (add-hook 'sh-mode-local-vars-hook #'lsp-deferred)) (with-eval-after-load 'rainbow-delimiters (add-hook 'sh-mode #'rainbow-delimiters-mode)) ;; recognize function names with dashes in them (setq sh-indent-after-continuation 'always) (add-to-list 'sh-imenu-generic-expression '(sh (nil "^\\s-*function\\s-+\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*\\(?:()\\)?" 1) (nil "^\\s-*\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*()" 1))) (with-eval-after-load 'smartparens (sp-local-pair 'sh-mode "`" "`" :unless '(sp-point-before-word-p sp-point-before-same-p))))
- vterm
(use-package vterm :defer t :commands vterm-mode :config (add-hook 'vterm-mode-hook (lambda () (setq-local global-hl-line-mode nil) (setq-local hscroll-margin 0))) (setq vterm-kill-buffer-on-exit t) (evil-set-initial-state #'vterm-mode 'insert) (with-eval-after-load 'general (+config/leader-vterm "v" 'vterm)) )
- multi-vterm
(use-package multi-vterm :defer t :after vterm :config (with-eval-after-load 'general (general-iemap :keymaps 'vterm-mode-map "C-SPC" '(:ignore t :wk "multi vterm") "C-SPC a" '(multi-vterm :wk "new vterm buffer") "C-SPC n" '(multi-vterm-next :wk "next vterm buffer") "C-SPC p" '(multi-vterm-prev :wk "previous vterm buffer"))) )
- vterm-toggle
(use-package vterm-toggle :defer t :after vterm :commands vterm-toggle :bind (:map vterm-mode-map ("C-<return>" . vterm-toggle-insert-cd)) :config (setq vterm-toggle-fullscreen-p nil) (add-to-list 'display-buffer-alist '((lambda (buffer-or-name _) (let ((buffer (get-buffer buffer-or-name))) (with-current-buffer buffer (or (equal major-mode 'vterm-mode) (string-prefix-p vterm-buffer-name (buffer-name buffer)))))) (display-buffer-reuse-window display-buffer-at-bottom) ;;(display-buffer-reuse-window display-buffer-in-direction) ;;display-buffer-in-direction/direction/dedicated is added in emacs27 ;;(direction . bottom) (dedicated . t) ;dedicated is supported in emacs27 (reusable-frames . visible) (window-height . 0.4))))
- bash-completion
(use-package bash-completion :defer t :config (bash-completion-setup) :hook (shell-dynamic-complete-function bash-completion-dynamic-complete))
- emacs-corfu-terminal
(straight-use-package '(corfu-terminal :type git :repo "https://codeberg.org/akib/emacs-corfu-terminal.git")) (use-package corfu-terminal :unless (display-graphic-p) :init (corfu-terminal-mode +1))
Editing
- select
(use-package select :straight nil :custom (select-enable-clipboard t))
- transient-mark-mode
Highlight region.
(transient-mark-mode 1)
- delete-selection-mode
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
(use-package subword :straight nil :init (global-subword-mode 1))
- text-mode
(use-package text-mode :straight nil :hook (text-mode . visual-line-mode) :config (setq-default sentence-end-double-space nil))
- whitespace
(use-package whitespace :straight nil :config (setq whitespace-line-column nil whitespace-style '(face indentation tabs tab-mark spaces space-mark newline newline-mark trailing lines-tail) whitespace-display-mappings '((tab-mark ?\t [?› ?\t]) (newline-mark ?\n [?¬ ?\n]) (space-mark ?\ [?·] [?.]))))
- ws-butler
(use-package ws-butler :hook ((prog-mode text-mode) . ws-butler-mode))
- better-jumper
(use-package better-jumper :init (global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward) (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) (global-set-key [remap xref-pop-marker-stack] #'better-jumper-jump-backward) (global-set-key [remap xref-go-back] #'better-jumper-jump-backward) (global-set-key [remap xref-go-forward] #'better-jumper-jump-forward) (better-jumper-mode +1))
- smartparens
I still have a mixed feeling for this package, but it still easier to configure than the builtin electric-pair-mode. Also, should this be in the Lisp section?
;; smartparens (use-package smartparens :demand t :commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string :init (smartparens-global-mode) :config ;; smartparens recognizes `slime-mrepl-mode', but not `sly-mrepl-mode', so... (add-to-list 'sp-lisp-modes 'sly-mrepl-mode) (require 'smartparens-config) (add-hook 'eval-expression-minibuffer-setup-hook #'smartparens-mode) ;; Overlays are too distracting and not terribly helpful. show-parens does ;; this for us already (and is faster), so... (setq sp-highlight-pair-overlay nil sp-highlight-wrap-overlay nil sp-highlight-wrap-tag-overlay nil) (show-smartparens-global-mode 1) (smartparens-global-mode 1) ;; Fix usage of ' in Lisp modes ;; THANKS: https://github.com/Fuco1/smartparens/issues/286#issuecomment-32324743 ;; (eval) is used as a hack to quiet Flycheck errors about (sp-with-modes) (eval '(sp-with-modes sp-lisp-modes ;; disable ', it's the quote character! (sp-local-pair "'" nil :actions nil) ;; also only use the pseudo-quote inside strings where it serve as ;; hyperlink. (sp-local-pair "`" "'" :when '(sp-in-string-p sp-in-comment-p)) (sp-local-pair "`" nil :skip-match (lambda (ms mb me) (cond ((equal ms "'") (or (sp--org-skip-markup ms mb me) (not (sp-point-in-string-or-comment)))) (t (not (sp-point-in-string-or-comment)))))))) (sp-with-modes '(html-mode sgml-mode nxml-mode web-mode) (sp-local-pair "<" ">")) (defun sp--markdown-skip-asterisk (ms mb me) (save-excursion (goto-char mb) (save-match-data (looking-at "^\\* ")))) (sp-with-modes 'markdown-mode (sp-local-pair "*" "*" :unless '(sp-point-after-word-p sp-point-at-bol-p) :skip-match 'sp--markdown-skip-asterisk) (sp-local-pair "**" "**") (sp-local-pair "_" "_" :unless '(sp-point-after-word-p))) ;;; org-mode (defun sp--org-skip-asterisk (ms mb me) (or (and (= (line-beginning-position) mb) (eq 32 (char-after (1+ mb)))) (and (= (1+ (line-beginning-position)) me) (eq 32 (char-after me))))) (defun sp--org-inside-LaTeX (id action context) (org-inside-LaTeX-fragment-p)) (sp-with-modes 'org-mode (sp-local-pair "*" "*" :unless '(sp-point-after-word-p sp--org-inside-LaTeX sp-point-at-bol-p) :skip-match 'sp--org-skip-asterisk) (sp-local-pair "/" "/" :unless '(sp-point-after-word-p sp--org-inside-LaTeX)) (sp-local-pair "~" "~" :unless '(sp-point-after-word-p sp--org-inside-LaTeX)) (sp-local-pair "=" "=" :unless '(sp-point-after-word-p sp--org-inside-LaTeX)) (sp-local-pair "\\[" "\\]")) ;; haskell (add-to-list 'sp-no-reindent-after-kill-modes 'haskell-mode) ;; You're likely writing lisp in the minibuffer, therefore, disable these ;; quote pairs, which lisps doesn't use for strings: (sp-local-pair '(minibuffer-mode minibuffer-inactive-mode) "'" nil :actions nil) (sp-local-pair '(minibuffer-mode minibuffer-inactive-mode) "`" nil :actions nil) )
The keybinding I got from the example configuration messes up my evil-mode. I have them commented for now.
- apheleia
(use-package apheleia :defer t :init (apheleia-global-mode +1))
- expand-region
Emacs extension to increase selected region by semantic units.
(use-package expand-region :config (with-eval-after-load 'general (general-define-key :keymaps 'global "C-=" 'er/expand-region)))
- ispell
(use-package ispell :ensure nil :config (setq ispell-dictionary "english" ispell-personal-dictionary (expand-file-name "dictionary/personal" user-emacs-directory)) (add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:")) (add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC")) (add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_EXAMPLE" . "#\\+END_EXAMPLE")) (when (executable-find "aspell") (setq ispell-program-name (executable-find "aspell") ispell-extra-args '("--sug-mode=ultra" "--run-together")) (unless ispell-aspell-dict-dir (setq ispell-aspell-dict-dir (ispell-get-aspell-config-value "dict-dir"))) (unless ispell-aspell-data-dir (setq ispell-aspell-data-dir (ispell-get-aspell-config-value "data-dir"))) (add-hook 'text-mode-hook (lambda () (setq-local ispell-extra-args (remove "--run-together" ispell-extra-args))))) (when (executable-find "hunspell") (setq ispell-program-name (executable-find "hunspell"))) (when (executable-find "enchant-2") (setq ispell-program-name (executable-find "enchant-2") ispell-cmd-args '("-a"))))
IDE stuffs
- eldoc
(use-package eldoc :hook ((emacs-lisp-mode lisp-interaction-mode ielm-mode) . eldoc-mode))
- magit external
(use-package magit :demand t :config (evil-set-initial-state #'git-commit-mode 'insert) (with-eval-after-load 'general (+config/leader-go "g" 'magit-status)) :custom (magit-revision-show-gravatars '("^Author: " . "^Commit: ")) (magit-diff-refine-hunk 'all) (magit-log-arguments '("-n100" "--graph" "--decorate")))
- projectile external
A project interaction library for Emacs, with little external dependency as possible.
(use-package projectile :demand t :commands (projectile-project-root projectile-project-name projectile-project-p projectile-locate-dominating-file projectile-relevant-known-projects) :bind (([remap evil-jump-to-tag] . projectile-find-tag) ([remap find-tag] . projectile-find-tag)) :hook (dired-before-readin . projectile-track-known-projects-find-file-hook) :custom (projectile-cache-file (expand-file-name ".projects" user-emacs-directory)) (projectile-auto-discover nil) (projectile-enable-caching (not noninteractive)) (projectile-globally-ignored-files '("DS_Store" "TAGS")) (projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")) (projectile-kill-buffers-filter 'kill-only-files) (projectile-known-projects-file (expand-file-name ".projectile_projects.eld" user-emacs-directory)) (projectile-ignored-projects '("~/")) (projectile-project-search-path `(,(expand-file-name "Projects" (getenv "HOME")))) (projectile-project-root-files-bottom-up (append '(".projectile" ".project" ".git") (when (executable-find "hg") '(".hg")) (when (executable-find "bzr") '(".bzr")))) (projectile-project-root-files-top-down-recurring '("Makefile")) (compilation-buffer-name-function #'projectile-compilation-buffer-name) (compilation-save-buffers-predicate #'projectile-current-project-buffer-p) (projectile-git-submodule-command nil) (projectile-indexing-method 'hybrid) :config (global-set-key [remap evil-jump-to-tag] #'projectile-find-tag) (global-set-key [remap find-tag] #'projectile-find-tag) (projectile-mode +1) (put 'projectile-git-submodule-command 'initial-value projectile-git-submodule-command) (with-eval-after-load 'general (+config/leader-key "SPC" 'projectile-find-file "p" '(:keymap projectile-command-map :package projectile :wk "projectile"))) )
- diff-hl external
(use-package diff-hl :hook (find-file . diff-hl-mode) :hook (vc-dir-mode . diff-hl-dir-mode) :hook (dired-mode . diff-hl-dired-mode) :hook (diff-hl-mode . diff-hl-flydiff-mode) :hook (diff-hl-mode . diff-hl-show-hunk-mouse-mode) :hook (magit-pre-refresh-hook . diff-hl-magit-pre-refresh) :hook (magit-post-refresh-hook . diff-hl-magit-post-refresh) :init (global-diff-hl-mode) :custom (vc-git-diff-switches '("--histogram") diff-hl-flydiff-delay 0.5 diff-hl-show-staged-changes nil) :config (when (featurep 'flycheck) (setq flycheck-indication-mode 'right-fringe)) (with-eval-after-load 'general (+config/leader-go "*" 'diff-hl-show-hunk "=" 'diff-hl-diff-goto-hunk "[" 'diff-hl-previous-hunk "]" 'diff-hl-next-hunk "{" 'diff-hl-show-hunk-previous "}" 'diff-hl-show-hunk-next)))
- perspective external
Useful when I'm working on several projectile projects at once. With each perspective having their own separate buffer list.
(use-package perspective :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 'general (general-def :keymaps 'perspective-map "TAB" '(persp-switch-last :wk "switch to last perspective") "P" 'projectile-persp-switch-project) (+config/leader-key "TAB" (general-simulate-key "C-c TAB" :state '(normal visual) :name general-SPC-h-simulates-C-c-TAB :docstring "Simulates C-c TAB in normal and visual mode." :which-key "Perspective")) (+config/leader-key "C-x" '(persp-switch-to-scratch-buffer :wk "switch to scratch buffer"))) :init (customize-set-variable 'persp-mode-prefix-key (kbd "C-c TAB")) (unless (equal persp-mode t) (persp-mode 1)) :bind (([remap switch-to-buffer] . persp-switch-to-buffer*) ([remap kill-buffer] . persp-kill-buffer*)) :hook (kill-emacs . persp-state-save) )
- persp-projectile external
(use-package persp-projectile :after perspective :commands projectile-persp-switch-project :config (with-eval-after-load 'general (general-def :keymaps 'perspective-map "P" 'projectile-persp-switch-project)) )
- git-link external
(use-package git-link :demand t :commands (git-link git-link-commit git-link-homepage) :config (with-eval-after-load 'general (+config/leader-go "G" '(:ignore t :wk "git") "Gl" 'git-link "Gh" 'git-link-homepage "Gc" 'git-link-commit)) )
- git-messenger external
- git-timemachine external
(use-package git-timemachine :after magit :config (with-eval-after-load 'general (+config/leader-go "Gt" 'git-timemachine-toggle)) )
- org-project-capture external
(use-package org-project-capture :bind (("C-c n p" . org-project-capture-project-todo-completing-read)) :config (progn (setq org-project-capture-backend (make-instance 'org-project-capture-projectile-backend)) ; Replace with your backend of choice (setq org-project-capture-projects-file (expand-file-name "projects.org" org-directory)) (org-project-capture-single-file)))
- lsp external
- Basic configuration
- LSP
(use-package lsp-mode :init (setq lsp-keymap-prefix "C-c C-l") (defun my/orderless-dispatch-flex-first (_pattern index _total) (and (eq index 0) 'orderless-flex)) (defun my/lsp-mode-setup-completion () (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) '(orderless))) (add-hook 'orderless-style-dispatchers #'my/orderless-dispatch-flex-first nil 'local) (when (featurep 'cape) (setq-local completion-at-point-functions (list (cape-capf-buster #'lsp-completion-at-point)))) :hook (lsp-mode . lsp-enable-which-key-integration) (lsp-completion-mode . my/lsp-mode-setup-completion) ;; ((c-mode c++-mode xml-mode python-mode ;; yaml-mode toml-mode python-mode-local-vars ;; lua-mode jinja2-mode markdown-mode ;; json-mode html-mode ;; web-mode html-mode-local-vars-hook ;; web-mode-local-vars-hook nxml-mode-local-vars-hook ;; scss-mode-local-vars css-mode-local-vars ;; less-css-mode-local-vars css-mode ;; typescript-mode javascript-mode ;; js2-mode typescript-tsx-mode) . lsp-deferred) :config (setq lsp-toml-command (if (file-exists-p (expand-file-name ".cargo/bin/taplo" "~")) (expand-file-name ".cargo/bin/taplo" "~") "taplo") lsp-rust-rls-server-command "rls" ;;lsp-eldoc-render-all t lsp-enable-snippet nil lsp-enable-indentation nil lsp-prefer-flymake nil lsp-keep-workspace-alive nil ;; lsp-modeline-code-actions-segments '(count icon name) ) (when (featurep 'exwm) (advice-add #'corfu--make-frame :around (defun +corfu--make-frame-a (oldfun &rest args) (cl-letf (((symbol-function #'frame-parent) (lambda (frame) (or (frame-parameter frame 'parent-frame) exwm-workspace--current)))) (apply oldfun args)) (when exwm--connection (set-frame-parameter corfu--frame 'parent-frame nil)))) (advice-add #'corfu--popup-redirect-focus :override (defun +corfu--popup-redirect-focus-a () (redirect-frame-focus corfu--frame (or (frame-parent corfu--frame) exwm-workspace--current))))) (with-eval-after-load 'general (+config/leader-key :keymaps 'lsp-mode-map "l" (general-simulate-key "C-c C-l" :name general-SPC-l-simulates-C-c-C-l :docstring "Simulates C-c C-l" :which-key "LSP"))) (general-nvmap :keymaps 'lsp-mode-map "K" 'lsp-describe-thing-at-point) :custom (lsp-completion-provider :none) )
- LSP
- lsp-ui
(use-package lsp-ui :hook (lsp-mode . lsp-ui-mode) :demand t :init (setq lsp-ui-sideline-enable t lsp-ui-sideline-update-mode 'line lsp-ui-sideline-show-code-actions t lsp-ui-sideline-show-hover t lsp-ui-doc-enable t lsp-ui-doc-include-signature t lsp-ui-doc-show-with-cursor t lsp-eldoc-enable-hover nil ; Disable eldoc displays in minibuffer lsp-ui-doc-position 'at-point lsp-ui-imenu-enable t lsp-ui-sideline-ignore-duplicate t lsp-ui-peek-enable t) :config (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions) (define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references) (add-to-list 'lsp-language-id-configuration '(jinja2-mode . "jinja2") t) (add-to-list 'lsp-language-id-configuration '("\\.js2$" . "jinja2") t) (with-eval-after-load 'general (general-define-key :keymaps '(lsp-ui-mode-map) [remap xref-find-definitions] 'lsp-ui-peek-find-definitions [remap xref-find-references] 'lsp-ui-peek-find-references "M-." 'lsp-ui-peek-find-definitions "M-?" 'lsp-ui-peek-find-references)) )
- consult-lsp
(use-package consult-lsp :after consult lsp :bind (:map lsp-mode-map ([remap xref-find-apropos] . consult-lsp-symbols)))
- Basic configuration
- dap-mode
(use-package dap-mode :config (dap-auto-configure-mode) (dap-ui-mode 1) (dap-tooltip-mode 1) (dap-ui-controls-mode 1))
- Tree-sitter
(use-package tree-sitter-langs) (use-package tree-sitter-indent) (use-package tree-sitter :defer t :config (require 'tree-sitter-langs) (setq tree-sitter-debug-jump-buttons t ;; and this highlights the entire sub tree in your code tree-sitter-debug-highlight-jump-region t)) (use-package evil-textobj-tree-sitter :defer t :init (with-eval-after-load 'tree-sitter (require 'evil-textobj-tree-sitter)) :config (defvar +tree-sitter-inner-text-objects-map (make-sparse-keymap)) (defvar +tree-sitter-outer-text-objects-map (make-sparse-keymap)) (defvar +tree-sitter-goto-previous-map (make-sparse-keymap)) (defvar +tree-sitter-goto-next-map (make-sparse-keymap)) (defvar +tree-sitter-hl-enabled-modes '(not web-mode typescript-tsx-mode) "A list of major modes which should be highlighted by tree-sitter. If this list begins with `not', then it negates the list. If it is t, it is enabled in all modes. If nil, it is disabled in all modes") (defun tree-sitter! () "Dispatch to turn on tree sitter. Used as a hook function which turns on `tree-sitter-mode' and selectively turn on `tree-sitter-hl-mode'. according to `+tree-sitter-hl-enabled-modes'" (turn-on-tree-sitter-mode) ;; conditionally enable `tree-sitter-hl-mode' (let ((mode (bound-and-true-p tree-sitter-hl-mode))) (when-let (mode (if (pcase +tree-sitter-hl-enabled-modes (`(not . ,modes) (not (memq major-mode modes))) ((and `(,_ . ,_) modes) (memq major-mode modes)) (bool bool)) (unless mode +1) (if mode -1))) (tree-sitter-hl-mode mode)))) ;;;###autodef (fset 'set-tree-sitter-lang! #'ignore) (defun set-tree-sitter-lang! (mode lang) "Associate LANG with major MODE." (after! tree-sitter-langs (add-to-list 'tree-sitter-major-mode-language-alist (cons mode lang)))) (defun +tree-sitter-get-textobj (group &optional query) "A wrapper around `evil-textobj-tree-sitter-get-textobj' to prevent eager expansion." (eval `(evil-textobj-tree-sitter-get-textobj ,group ,query))) ;;;###autoload (defun +tree-sitter-goto-textobj (group &optional previous end query) "Thin wrapper that returns the symbol of a named function, used in keybindings." (let ((sym (intern (format "+goto%s%s-%s" (if previous "-previous" "") (if end "-end" "") group)))) (fset sym (lambda () (interactive) (evil-textobj-tree-sitter-goto-textobj group previous end query))) sym)) (evil-define-key '(visual operator) 'tree-sitter-mode "i" +tree-sitter-inner-text-objects-map "a" +tree-sitter-outer-text-objects-map) (evil-define-key 'normal 'tree-sitter-mode "[g" +tree-sitter-goto-previous-map "]g" +tree-sitter-goto-next-map) (with-eval-after-load 'general (general-define-key :keymaps '+tree-sitter-inner-text-objects-map "A" (+tree-sitter-get-textobj '("parameter.inner" "call.inner")) "f" (+tree-sitter-get-textobj "function.inner") "F" (+tree-sitter-get-textobj "call.inner") "C" (+tree-sitter-get-textobj "class.inner") "v" (+tree-sitter-get-textobj "conditional.inner") "l" (+tree-sitter-get-textobj "loop.inner") :keymaps '+tree-sitter-outer-text-objects-map "A" (+tree-sitter-get-textobj '("parameter.outer" "call.outer")) "f" (+tree-sitter-get-textobj "function.outer") "F" (+tree-sitter-get-textobj "call.outer") "C" (+tree-sitter-get-textobj "class.outer") "c" (+tree-sitter-get-textobj "comment.outer") "v" (+tree-sitter-get-textobj "conditional.outer") "l" (+tree-sitter-get-textobj "loop.outer") :keymaps '+tree-sitter-goto-previous-map "a" (+tree-sitter-goto-textobj "parameter.outer" t) "f" (+tree-sitter-goto-textobj "function.outer" t) "F" (+tree-sitter-goto-textobj "call.outer" t) "C" (+tree-sitter-goto-textobj "class.outer" t) "c" (+tree-sitter-goto-textobj "comment.outer" t) "v" (+tree-sitter-goto-textobj "conditional.outer" t) "l" (+tree-sitter-goto-textobj "loop.outer" t) :keymaps '+tree-sitter-goto-next-map "a" (+tree-sitter-goto-textobj "parameter.outer") "f" (+tree-sitter-goto-textobj "function.outer") "F" (+tree-sitter-goto-textobj "call.outer") "C" (+tree-sitter-goto-textobj "class.outer") "c" (+tree-sitter-goto-textobj "comment.outer") "v" (+tree-sitter-goto-textobj "conditional.outer") "l" (+tree-sitter-goto-textobj "loop.outer"))))
- gptel external
(use-package gptel :after password-store :config (setq gptel-backend (gptel-make-gemini "Gemini" :key (password-store-get "google.com/gemini_api_key") :stream t) gptel-default-mode #'org-mode))
An LLM client for Emacs, I'm trying it with google's gemini. This package is already good on its own. But the other reason is I'm also trying out consult-web, which integrates search results from various sources.
- magit-gptcommit
Magit commit with the help of gpt.
(use-package magit-gptcommit :demand t :hook (gptel-mode . visual-line-mode) :after gptel magit password-store :config (setq magit-gptcommit-llm-provider `(make-llm-gemini :key ,(password-store-get "google.com/gemini_api_key"))) (magit-gptcommit-mode 1) (magit-gptcommit-status-buffer-setup) (with-eval-after-load 'general (+config/leader-go "c" '(:ignore t :wk "chat") "cm" 'gptel-menu "cc" 'gptel "cs" 'gptel-send "cA" 'gptel-abort)) :bind (:map git-commit-mode-map ("C-c C-g" . magit-gptcommit-commit-accept)))
- flycheck
(use-package flycheck :init (global-flycheck-mode +1) :commands flycheck-list-errors flycheck-buffer :config (setq flycheck-emacs-lisp-load-path 'inherit) ;; Rerunning checks on every newline is a mote excessive. (delq 'new-line flycheck-check-syntax-automatically) ;; And don't recheck on idle as often (setq flycheck-idle-change-delay 1.0) ;; For the above functionality, check syntax in a buffer that you switched to ;; only briefly. This allows "refreshing" the syntax check state for several ;; buffers quickly after e.g. changing a config file. (setq flycheck-buffer-switch-check-intermediate-buffers t) ;; Display errors a little quicker (default is 0.9s) (setq flycheck-display-errors-delay 0.25) (with-eval-after-load 'general (+config/leader-key "!" (general-simulate-key "C-c !" :state '(normal visual) :name general-SPC-!-simulates-C-c-! :docstring "Simulates C-c ! in normal and visual mode." :which-key "flycheck")) (general-nvmap :keymaps 'flycheck-error-list-mode-map "C-n" 'flycheck-error-list-next-error "C-p" 'flycheck-error-list-previous-error "j" 'flycheck-error-list-next-error "k" 'flycheck-error-list-previous-error "RET" 'flycheck-error-list-goto-error "[return]" 'flycheck-error-list-goto-error)))
- flycheck-popup-tip
(use-package flycheck-popup-tip :commands flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup :hook (flycheck-mode . flycheck-popup-tip-mode))
- flycheck-posframe
(use-package flycheck-posframe :after flycheck :config (add-hook 'flycheck-mode-hook #'flycheck-posframe-mode) (setq flycheck-posframe-warning-prefix "⚠ " flycheck-posframe-info-prefix "ⓘ " flycheck-posframe-error-prefix "⮾ "))
- consult-flycheck
(use-package consult-flycheck :after (consult flycheck) :config (with-eval-after-load 'general (+config/leader-search "f" 'consult-flycheck)))
- flycheck-popup-tip
Emacs org-mode
- org-mode core package
(use-package org :demand t :commands org-tempo :preface (if (not +config/org-directory) (cond ((file-directory-p (expand-file-name "Dropbox/org" (getenv "HOME"))) (setq org-directory (expand-file-name "Dropbox/org" (getenv "HOME")))) ((file-directory-p (expand-file-name "Sync/org" (getenv "HOME"))) (setq org-directory (expand-file-name "Sync/org" (getenv "HOME")))) ((file-directory-p (expand-file-name "Documents/google-drive/org" (getenv "HOME"))) (setq org-directory (expand-file-name "Documents/google-drive/org" (getenv "HOME"))))) (customize-set-variable 'org-directory +config/org-directory)) :hook ((org-mode . org-indent-mode) (org-mode . +config/org-prettify-symbols) (org-mode . (lambda () (electric-indent-local-mode -1))) (org-mode . variable-pitch-mode)) :config (global-set-key (kbd "C-c l") #'org-store-link) (global-set-key (kbd "C-c a") #'org-agenda) (global-set-key (kbd "C-c c") #'org-capture) (when(file-directory-p (expand-file-name "braindump/org" org-directory)) (customize-set-variable '+config/org-roam-directory (expand-file-name "braindump/org" org-directory))) (when (file-directory-p (expand-file-name "alexforsale.github.io" org-directory)) (customize-set-variable '+config/blog-directory (expand-file-name "alexforsale.github.io" org-directory))) (modify-syntax-entry ?= "$" org-mode-syntax-table) (modify-syntax-entry ?~ "$" org-mode-syntax-table) (modify-syntax-entry ?_ "$" org-mode-syntax-table) (modify-syntax-entry ?+ "$" org-mode-syntax-table) (modify-syntax-entry ?/ "$" org-mode-syntax-table) (modify-syntax-entry ?* "$" org-mode-syntax-table) (add-to-list 'org-modules 'org-tempo t) (add-to-list 'org-structure-template-alist '("sh" . "src sh")) (add-to-list 'org-structure-template-alist '("co" . "src conf")) (add-to-list 'org-structure-template-alist '("lisp" . "src lisp")) (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) (add-to-list 'org-structure-template-alist '("sc" . "src scheme")) (add-to-list 'org-structure-template-alist '("ts" . "src typescript")) (add-to-list 'org-structure-template-alist '("py" . "src python")) (add-to-list 'org-structure-template-alist '("go" . "src go")) (add-to-list 'org-structure-template-alist '("yaml" . "src yaml")) (add-to-list 'org-structure-template-alist '("js" . "src js")) (add-to-list 'org-structure-template-alist '("json" . "src json")) (add-to-list 'org-structure-template-alist '("n" . "note")) (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (awk . t) (C . t) (css . t) (calc . t) (ditaa . t) ; needs the `ditaa' package (dot . t ) ; `graphviz' (screen . t) (haskell . t) (java . t) (js . t) (latex . t) (lisp . t) (lua . t) (org . t) (perl . t) (plantuml . t) (python .t) (ruby . t) (shell . t) (sed . t) (scheme . t) (sql . t) (sqlite . t))) (setq-default org-use-sub-superscripts '{}) (add-to-list 'org-babel-tangle-lang-exts '("js" . "js")) (defun +config/org-prettify-symbols () (push '("[ ]" . "☐") prettify-symbols-alist) (push '("[X]" . "☑") prettify-symbols-alist) (prettify-symbols-mode)) (require 'org-tempo) :custom (org-highlight-latex-and-related '(native script entities)) (org-replace-disputed-keys t) (org-indirect-buffer-display 'current-window) (org-enforce-todo-dependencies t) (org-fontify-whole-heading-line t) (org-return-follows-link t) (org-mouse-1-follows-link t) (org-image-actual-width nil) (org-adapt-indentation nil) (org-startup-indented t) (org-link-descriptive nil) (org-log-done 'time) (org-log-refile 'time) (org-log-redeadline 'time) (org-log-reschedule 'time) (org-log-into-drawer t) (org-clone-delete-id t) (org-default-notes-file (expand-file-name "notes.org" org-directory)) (org-insert-heading-respect-content nil) (org-pretty-entities t) (org-use-property-inheritance t) (org-priority-highest ?A) (org-priority-lowest ?D) (org-priority-default ?B) (org-todo-keywords '((sequence "TODO(t!)" ; A task that needs doing & is ready to do "NEXT(n!)" ; Tasks that can be delayed "PROG(p!)" ; A task that is in progress "WAIT(w!)" ; Something external is holding up this task "HOLD(h!)" ; This task is paused/on hold because of me "|" "DONE(d!)" ; Task successfully completed "DELEGATED(l!)" ; Task is delegated "KILL(k!)") ; Task was cancelled, aborted or is no longer applicable )) (org-todo-keyword-faces '(("PROG" . (:foreground "#5e81ac" :weight bold)) ("WAIT" . (:foreground "#ebcb8b" :weight bold)) ("HOLD" . (:foreground "#d08770" :weight bold)) ("NEXT" . (:foreground "#81a1c1" :weight bold)) ("DELEGATED" . "#8fbcbb") ("KILL" . "#a3be8c"))))
- org-entities
(use-package org-entities :straight nil :config (setq org-entities-user '(("flat" "\\flat" nil "" "" "266D" "♭") ("sharp" "\\sharp" nil "" "" "266F" "♯"))))
- ob-diagrams
(use-package ob-diagrams :after org :config (add-to-list 'org-babel-load-languages '(diagrams . t)))
- org-faces
(use-package org-faces :straight nil :custom (org-fontify-quote-and-verse-blocks t))
- org-archive
(use-package org-archive :straight nil :after org :custom (org-archive-tag "archive") (org-archive-subtree-save-file-p t) (org-archive-mark-done t) (org-archive-reversed-order t) (org-archive-location (concat (expand-file-name "archives.org" org-directory) "::datetree/* Archived Tasks")))
- org-capture
(use-package org-capture :after org :straight nil :demand t :config (org-capture-put :kill-buffer t) (setq org-capture-templates ;; this is the default from `doom'. `(("i" "Inbox - Goes Here first!" entry (file+headline ,(expand-file-name "inbox.org" org-directory) "Inbox") "** %?\n%i\n%a" :prepend t) ("r" "Request" entry (file+headline ,(expand-file-name "inbox.org" org-directory) "Request") (file ,(expand-file-name "request.template" org-directory))) ("l" "Links" entry (file+headline ,(expand-file-name "links.org" org-directory) "Links")))))
- org-refile
(use-package org-refile :straight nil :after org :hook (org-after-refile-insert . save-buffer) :custom (org-refile-targets `((,(expand-file-name "projects.org" org-directory) :maxlevel . 1) (,(expand-file-name "routines.org" org-directory) :maxlevel . 1) (,(expand-file-name "personal.org" org-directory) :maxlevel . 1))) (org-refile-use-outline-path 'file) (org-outline-path-complete-in-steps nil))
- org-fold
(use-package org-fold :straight nil :after org org-contrib :custom (org-catch-invisible-edits 'smart))
- org-id
(use-package org-id :straight nil :after org :custom (org-id-locations-file-relative t) (org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))
- org-num
(use-package org-num :straight nil :after org :custom (org-num-face '(:inherit org-special-keyword :underline nil :weight bold)) (org-num-skip-tags '("noexport" "nonum")))
- org-crypt
(use-package org-crypt ; built-in :straight nil :after org :commands org-encrypt-entries org-encrypt-entry org-decrypt-entries org-decrypt-entry ;;:hook (org-reveal-start . org-decrypt-entry) :preface ;; org-crypt falls back to CRYPTKEY property then `epa-file-encrypt-to', which ;; is a better default than the empty string `org-crypt-key' defaults to. (defvar org-crypt-key nil) (with-eval-after-load 'org (add-to-list 'org-tags-exclude-from-inheritance "crypt")))
- org-attach
(use-package org-attach :straight nil :after org :commands (org-attach-new org-attach-open org-attach-open-in-emacs org-attach-reveal-in-emacs org-attach-url org-attach-set-directory org-attach-sync) :config (unless org-attach-id-dir (setq-default org-attach-id-dir (expand-file-name ".attach/" org-directory))) (with-eval-after-load 'projectile (add-to-list 'projectile-globally-ignored-directories org-attach-id-dir)) :custom (org-attach-auto-tag nil))
- org-agenda
(use-package org-agenda :straight nil :after org :custom (org-agenda-files (list (concat org-directory "/"))) (org-agenda-file-regexp "\\`[^.].*\\.org\\|[0-9]+$\\'") (org-agenda-include-inactive-timestamps t) (org-agenda-window-setup 'only-window) (org-stuck-projects '("+{project*}-killed-Archives/-DONE-KILL-DELEGATED" ("TODO" "NEXT" "IDEA" "PROG") nil "")) :config (with-eval-after-load 'evil (evil-set-initial-state #'org-agenda-mode 'normal)) (setq org-agenda-custom-commands `(("w" "Work Agenda and all TODOs" ((agenda "" ((org-agenda-span 1) (org-agenda-start-on-weekday t) (org-agenda-block-separator nil) (org-agenda-use-time-grid t) (org-agenda-day-face-function (lambda (date) 'org-agenda-date)) (org-agenda-format-date "%A %-e %B %Y") (org-agenda-overriding-header "\nToday\n"))) (tags-todo "TODO=\"TODO\"|\"NEXT\"" ((org-agenda-block-separator nil) (org-agenda-skip-function '(org-agenda-skip-if-todo 'nottodo 'done)) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nIncomplete\n"))) (agenda "" ((org-agenda-span 7) (org-agenda-start-on-weekday 1) (org-agenda-block-separator nil) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nWeekly\n")))) ((org-agenda-tag-filter-preset '("-personal" "-home")))) ("h" "Home Agenda and all personal TODOs" ((agenda "" ((org-agenda-span 1) (org-agenda-start-on-weekday t) (org-agenda-block-separator nil) (org-agenda-use-time-grid t) (org-agenda-day-face-function (lambda (date) 'org-agenda-date)) (org-agenda-format-date "%A %-e %B %Y") (org-agenda-overriding-header "\nToday\n"))) (tags-todo "TODO=\"TODO\"|\"NEXT\"" ((org-agenda-block-separator nil) (org-agenda-skip-function '(org-agenda-skip-if-todo 'nottodo 'done)) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nIncomplete\n"))) (agenda "" ((org-agenda-span 7) (org-agenda-start-on-weekday 1) (org-agenda-block-separator nil) (org-agenda-use-time-grid nil) (org-agenda-overriding-header "\nWeekly\n")))) ((org-agenda-tag-filter-preset '("+personal")))))))
- org-clock
(use-package org-clock :straight nil :after org :commands org-clock-save :hook (kill-emacs . org-clock-save) :custom (org-persist 'history) (org-clock-in-resume t) (org-clock-out-remove-zero-time-clocks t) (org-clock-history-length 20) (org-show-notification-handler "notify-send") (org-agenda-skip-scheduled-if-deadline-is-shown t) :config (org-clock-persistence-insinuate))
- org-timer
(use-package org-timer :straight nil :config (setq org-timer-format "Timer :: %s"))
- org-eldoc
(use-package org-eldoc :straight nil :after org org-contrib :config (puthash "org" #'ignore org-eldoc-local-functions-cache) (puthash "plantuml" #'ignore org-eldoc-local-functions-cache) (puthash "python" #'python-eldoc-function org-eldoc-local-functions-cache) :custom (org-eldoc-breadcrumb-separator " → "))
- org-superstar external
(use-package org-superstar :hook (org-mode . org-superstar-mode) :custom (org-superstar-leading-bullet ?\s) (org-superstar-leading-fallback ?\s) (org-hide-leading-stars nil) (org-indent-mode-turns-on-hiding-stars nil) (org-superstar-todo-bullet-alist '(("TODO" . 9744) ("[ ]" . 9744) ("DONE" . 9745) ("[X]" . 9745))) :config (org-superstar-configure-like-org-bullets))
- org-fancy-priority external
(use-package org-fancy-priorities ; priority icons :defer t :hook (org-mode . org-fancy-priorities-mode) :hook (org-agenda-mode . org-fancy-priorities-mode) :custom (org-fancy-priorities-list '("⚡" "⬆" "⬇" "☕")))
- org-modern external
(use-package org-modern :demand t :config (set-face-attribute 'org-modern-symbol nil :family "Iosevka Nerd Font") (setq ;; Edit settings org-auto-align-tags nil org-tags-column 0 org-fold-catch-invisible-edits 'show-and-error org-special-ctrl-a/e t org-insert-heading-respect-content t ;; Org styling, hide markup etc. org-hide-emphasis-markers nil ; set to nil for easier editing org-ellipsis "…" ;; Agenda styling org-agenda-tags-column 0 org-agenda-block-separator ?─ org-agenda-time-grid '((daily today require-timed) (800 1000 1200 1400 1600 1800 2000) " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄") org-agenda-current-time-string "◀── now ─────────────────────────────────────────────────") (global-org-modern-mode))
- org-download
(use-package org-download :after org :commands org-download-dnd org-download-yank org-download-screenshot org-download-clipboard org-download-dnd-base64 :config (unless org-download-image-dir (setq org-download-image-dir org-attach-id-dir)) (setq org-download-method 'attach org-download-timestamp "_%Y%m%d_%H%M%S" org-download-screenshot-method (cond ((featurep :system 'macos) "screencapture -i %s") ((featurep :system 'linux) (cond ((executable-find "maim") "maim -s %s") ((executable-find "scrot") "scrot -s %s") ((executable-find "gnome-screenshot") "gnome-screenshot -a -f %s")))) org-download-heading-lvl nil org-download-link-format "[[download:%s]]\n" org-download-annotate-function (lambda (_link) "") org-download-link-format-function (lambda (filename) (if (eq org-download-method 'attach) (format "[[attachment:%s]]\n" (org-link-escape (file-relative-name filename (org-attach-dir)))) ;; Handle non-image files a little differently. Images should be ;; inserted as normal with previews. Other files, like pdfs or zips, ;; should be linked to, with an icon indicating the type of file. (format (concat (unless (image-type-from-file-name filename) (concat (+org-attach-icon-for filename) " ")) org-download-link-format) (org-link-escape (funcall org-download-abbreviate-filename-function filename))))) org-download-abbreviate-filename-function (lambda (path) (if (file-in-directory-p path org-download-image-dir) (file-relative-name path org-download-image-dir) path))))
- org-noter
(use-package org-noter :defer t :preface ;; Allow the user to preempt this and set the document search path ;; If not set then use `org-directory' (defvar org-noter-notes-search-path nil) :config (unless org-noter-notes-search-path (setq org-noter-notes-search-path (list org-directory))) (setq org-noter-auto-save-last-location t org-noter-separate-notes-from-heading t))
- org-appear
(use-package org-appear ; better markup edit :hook (org-mode . org-appear-mode))
- org-cliplink
(use-package org-cliplink :after org :config (with-eval-after-load 'general (+config/leader-insert :keymaps 'org-mode-map "l" 'org-cliplink)))
Misc Tools
- make-mode
(use-package make-mode :straight nil :config (add-hook 'makefile-mode-hook 'indent-tabs-mode))
- executable
(use-package executable :straight nil :hook (after-save . executable-make-buffer-file-executable-if-script-p))
- pinentry
(use-package pinentry :defer t :config (pinentry-start))
- pass
(use-package password-store :defer t :config (setq password-store-password-length 12)) (use-package password-store-otp :defer t :after password-store) (use-package pass :defer t) (use-package auth-source-pass :straight nil :init (auth-source-pass-enable))
Elpaca complains about different of pass version installed. But it installed just fine.
- rg
Packaged as
ripgrep
in archlinux. The:if
keyword won't load this if the executable not found.(use-package rg :if (executable-find "rg") :defer t)
- direnv
- ox-hugo
Blogging tools
(use-package ox-hugo :after ox)
- exec-path-from-shell
(use-package exec-path-from-shell :config (when (or (memq window-system '(mac ns x)) (and (daemonp) (memq window-system '(mac ns x)))) (exec-path-from-shell-initialize)))
- elfeed
An Emacs web feeds client.
(use-package elfeed :config (with-eval-after-load 'general (+config/leader-open "e" 'elfeed)))
- extensions
- elfeed-org
Configure the Elfeed RSS reader with an Orgmode file.
(use-package elfeed-org :after elfeed :init (elfeed-org) :config (setq rmh-elfeed-org-files (list (expand-file-name "elfeed.org" org-directory))))
- elfeed-goodies
(use-package elfeed-goodies :after elfeed :init (elfeed-goodies/setup))
- elfeed-org
- extensions
Dired
- dired
(use-package dired :straight nil :commands (dired dired-jump) :init (setq dired-dwim-target t ; guess a default target directory dired-hide-details-hide-symlink-targets nil ; don't hide symbolic link targets dired-auto-revert-buffer #'dired-buffer-stale-p ; revert stale only dired-recursive-copies 'always ; always copy recursively dired-recursive-deletes 'top ; ask only for top-level dired-create-destination-dirs 'ask dired-listing-switches "-AGFhl --group-directories-first --time-style=long-iso" dired-clean-confirm-killing-deleted-buffers nil) :config (setq dired-mouse-drag-files t))
The arguments for
dired-listing-switches
are:-A
- Show hidden files (“dotfiles”), such as
.bashrc
, but omit the implied.=
and..
targets. The latter two refer to the present and parent directory, respectively. -G
- Do not show the group name in the long listing. Only show the owner of the file.
-F
- Differentiate regular from special files by appending a character to them. The
*
is for executables, the/
is for directories, the|
is for a named pipe, the=
is for a socket, the@
and the>
are for stuff I have never seen. -h
- Make file sizes easier to read.
-l
- Produce a long, detailed listing. Dired requires this.
--group-directories-first
- Just as it said.
--time-style=long-iso
- Uses the international standard for time representation in the file listing. So we have something like 2023-12-30 06:38 to show the last modified time.
- image-dired
(use-package image-dired :straight nil :config (setq image-dired-thumb-size 150 image-dired-thumbnail-storage 'standard image-dired-external-viewer "xdg-open"))
- dired-x
(use-package dired-x :straight nil :hook (dired-mode . dired-omit-mode) :config (setq dired-omit-files (concat dired-omit-files "\\|^\\.DS_Store\\'" "\\|^\\.project\\(?:ile\\)?\\'" "\\|^\\.\\(?:svn\\|git\\)\\'" "\\|^\\.ccls-cache\\'" "\\|\\(?:\\.js\\)?\\.meta\\'" "\\|\\.\\(?:elc\\|o\\|pyo\\|swp\\|class\\)\\'")) ;; Disable the prompt about whether I want to kill the Dired buffer for a ;; deleted directory. Of course I do! (setq dired-clean-confirm-killing-deleted-buffers nil) (let ((cmd "xdg-open")) (setq dired-guess-shell-alist-user `(("\\.\\(?:docx\\|pdf\\|djvu\\|eps\\)\\'" ,cmd) ("\\.\\(?:jpe?g\\|png\\|gif\\|xpm\\)\\'" ,cmd) ("\\.\\(?:xcf\\)\\'" ,cmd) ("\\.csv\\'" ,cmd) ("\\.tex\\'" ,cmd) ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\)\\(?:\\.part\\)?\\'" ,cmd) ("\\.\\(?:mp3\\|flac\\)\\'" ,cmd) ("\\.html?\\'" ,cmd) ("\\.md\\'" ,cmd)))))
The
dired-guess-shell-alist-user
expect us to havexdg-open
(which is always available in my system anyway). - fd-dired external
Needs an external package
fd
, orfd-find
(use-package fd-dired :if (executable-find "fd") :defer t :init (global-set-key [remap find-dired] #'fd-dired))
- dired-git-info
(use-package dired-git-info :defer t ;; :hook ;; (dired-after-readin . dired-git-info-auto-enable) :config ;; (setq +dired--git-info-p (bound-and-true-p dired-git-info-mode)) ;; (when +dired--git-info-p ;; (dired-git-info-mode -1)) (setq dgi-auto-hide-details-p nil))
- dired-aux
(use-package dired-aux :straight nil :config (setq dired-create-destination-dirs 'ask dired-vc-rename-file t dired-isearch-filenames 'dwim dired-create-destination-dirs-on-trailing-dirsep t))
- dired-rsync
(use-package dired-rsync :defer t)
- diredfl
(use-package diredfl :defer t :hook (dired-mode . diredfl-global-mode))
- all-the-icons-dired
(use-package all-the-icons-dired :after all-the-icons :hook (dired-mode . all-the-icons-dired-mode))
- wdired
(use-package wdired :straight nil :commands (wdired-change-to-wdired-mode))
Treemacs
- treemacs core package
(use-package treemacs :hook ((treemacs-mode . (lambda () (hs-minor-mode -1)))) :config (setq treemacs-hide-gitignored-files-mode t treemacs-no-png-images t treemacs-silent-refresh t treemacs-sorting 'mod-time-desc treemacs-python-executable (executable-find "python3") treemacs-collapse-dirs (if treemacs-python-executable 3 0) treemacs-deferred-git-apply-delay 0.5 treemacs-directory-name-transformer #'identity treemacs-display-in-side-window t treemacs-eldoc-display 'simple treemacs-file-event-delay 2000 treemacs-file-extension-regex treemacs-last-period-regex-value treemacs-file-follow-delay 0.2 treemacs-file-name-transformer #'identity treemacs-follow-after-init t treemacs-expand-after-init t treemacs-find-workspace-method 'find-for-file-or-pick-first treemacs-git-command-pipe "" treemacs-goto-tag-strategy 'refetch-index treemacs-header-scroll-indicators '(nil . "^^^^^^") treemacs-hide-dot-git-directory t treemacs-indentation 2 treemacs-indentation-string " " treemacs-is-never-other-window nil treemacs-max-git-entries 5000 treemacs-missing-project-action 'ask treemacs-move-forward-on-expand nil treemacs-no-delete-other-windows t treemacs-project-follow-cleanup nil treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory) treemacs-position 'left treemacs-read-string-input 'from-child-frame treemacs-recenter-distance 0.1 treemacs-recenter-after-file-follow nil treemacs-recenter-after-tag-follow nil treemacs-recenter-after-project-jump 'always treemacs-recenter-after-project-expand 'on-distance treemacs-litter-directories '("/node_modules" "/.venv" "/.cask") treemacs-project-follow-into-home nil treemacs-show-cursor nil treemacs-show-hidden-files t treemacs-silent-filewatch t treemacs-select-when-already-in-treemacs 'move-back treemacs-space-between-root-nodes t treemacs-tag-follow-cleanup t treemacs-tag-follow-delay 1.5 treemacs-text-scale nil treemacs-user-mode-line-format nil treemacs-user-header-line-format nil treemacs-wide-toggle-width 70 treemacs-width 35 treemacs-width-increment 1 treemacs-width-is-initially-locked t treemacs-workspace-switch-cleanup nil) (treemacs-peek-mode 1) (treemacs-filewatch-mode t) (treemacs-follow-mode t) (treemacs-fringe-indicator-mode 'always) (when treemacs-python-executable (treemacs-git-commit-diff-mode t)) (pcase (cons (not (null (executable-find "git"))) (not (null treemacs-python-executable))) (`(t . t) (treemacs-git-mode 'deferred)) (`(t . _) (treemacs-git-mode 'simple))) :bind (:map global-map ("M-0" . treemacs-select-window) ("C-x t 1" . treemacs-delete-other-windows) ("C-x t t" . treemacs) ("C-x t d" . treemacs-select-directory) ("C-x t B" . treemacs-bookmark) ("C-x t C-t" . treemacs-find-file) ("C-x t M-t" . treemacs-find-tag)) :config (with-eval-after-load 'general (+config/leader-tree "t" 'treemacs "p" 'treemacs-add-and-display-current-project-exclusively) (general-define-key :keymaps 'treemacs-mode-map "[mouse-1]" 'treemacs-single-click-expand-action) (general-nvmap :keymaps 'treemacs-mode-map "gr" '(treemacs-refresh :wk "refresh tree") "zm" '(:ignore t :wk "move") "zmf" '(treemacs-move-file :wk "move file") "zmd" '(treemacs-move-project-down :wk "move project down") "zmu" '(treemacs-move-project-up :wk "move project up") "zr" '(:ignore t :wk "rename") "zrf" '(treemacs-rename-file :wk "rename file") "zrp" '(treemacs-rename-project :wk "rename project") "zrw" '(treemacs-rename-workspace :wk "rename workspace") "zc" '(:ignore t :wk "create/clean") "zcC" '(treemacs-cleanup-litter :wk "cleanup litter") "zcd" '(treemacs-create-dir :wk "create directory") "zcf" '(treemacs-create-file :wk "create file") "zd" '(treemacs-delete-file :wk "delete file"))) )
- treemacs-projectile
(use-package treemacs-projectile :after treemacs projectile)
- treemacs-icons-dired
(use-package treemacs-icons-dired :after treemacs :hook (dired-mode . treemacs-icons-dired-enable-once))
- treemacs-magit
(use-package treemacs-magit :after treemacs magit)
- treemacs-evil
(use-package treemacs-evil :after treemacs evil)
- lsp-treemacs
- treemacs-perspective
(use-package treemacs-perspective :after treemacs perspective :config (treemacs-set-scope-type 'Perspectives))
- treemacs-tab-bar
(use-package treemacs-tab-bar :after treemacs tab-bar)
- treemacs-nerd-icons
(use-package treemacs-nerd-icons :after treemacs nerd-icons :config (treemacs-load-theme "nerd-icons"))
- notmuch
(use-package notmuch :if (executable-find "notmuch") :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 "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")) (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)))) (with-eval-after-load 'general (+config/leader-menu! "mail" "M-m" "m" '(notmuch :wk "Notmuch Mail")) (+config/leader-mail "m" '(notmuch :wk "open notmuch mail") "M" '(compose-mail :wk "new mail")) (general-nmap 'notmuch-common-keymap "gr" 'notmuch-refresh-this-buffer "gR" 'notmuch-poll-and-refresh-this-buffer)) )
- notmuch-indicator
(use-package notmuch-indicator :if (executable-find "notmuch") :config (setq notmuch-indicator-args '((:terms "tag:unread and tag:inbox" :label "U" :label-face success))) (notmuch-indicator-mode))
- consult-notmuch
(use-package consult-notmuch :after consult :if (executable-find "notmuch") :config (add-to-list 'consult-buffer-sources 'consult-notmuch-buffer-source))
- ol-notmuch
(use-package ol-notmuch :if (executable-find "notmuch"))
- notmuch-maildir
(use-package notmuch-maildir :if (executable-find "notmuch") :config (notmuch-maildir-inject-section))
- message
(use-package message :straight nil :custom (message-directory (expand-file-name ".mail" (getenv "HOME"))) (message-sendmail-envelope-from 'header))
- sendmail
(use-package sendmail :straight nil :custom (mail-specify-envelope-from t) (mail-envelope-from 'header) (send-mail-function 'sendmail-send-it) (sendmail-program (executable-find "msmtp")))
Programming languages
- Lisp
- Emacs-lisp
- elisp-mode
(when (locate-library "aggressive-indent") (add-hook 'lisp-mode-hook #'aggressive-indent-mode) (add-hook 'clojure-mode-hook #'aggressive-indent-mode) (add-hook 'scheme-mode-hook #'aggressive-indent-mode)) ;;;; emacs-lisp (use-package elisp-mode :diminish emacs-lisp-mode :diminish elisp-mode :diminish outline-minor-mode :diminish lisp-data-mode :mode ("\\.Cask\\'" . emacs-lisp-mode) :straight nil :hook (emacs-lisp-mode . (lambda () (outline-minor-mode) (rainbow-delimiters-mode) (highlight-quoted-mode))))
- ielm
(use-package ielm :straight nil ;;:hook (ielm-mode . (turn-on-smartparens-mode)) :config (setq ielm-font-lock-keywords (append '(("\\(^\\*\\*\\*[^*]+\\*\\*\\*\\)\\(.*$\\)" (1 font-lock-comment-face) (2 font-lock-constant-face))) (when (require 'highlight-numbers nil t) (highlight-numbers--get-regexp-for-mode 'emacs-lisp-mode)) (cl-loop for (matcher . match-highlights) in (append lisp-el-font-lock-keywords-2 lisp-cl-font-lock-keywords-2) collect `((lambda (limit) (when ,(if (symbolp matcher) `(,matcher limit) `(re-search-forward ,matcher limit t)) ;; Only highlight matches after the prompt (> (match-beginning 0) (car comint-last-prompt)) ;; Make sure we're not in a comment or string (let ((state (syntax-ppss))) (not (or (nth 3 state) (nth 4 state)))))) ,@match-highlights)))))
- ielm
- macrostep external
(use-package macrostep :defer t :bind (:map emacs-lisp-mode-map ("C-c e" . macrostep-expand)))
- elisp-mode
- Common lisp
;;;; common-lisp (defvar inferior-lisp-program "sbcl") (when (executable-find "ros") (setq inferior-lisp-program "ros -Q run")) (add-hook 'lisp-mode-hook #'rainbow-delimiters-mode)
- sly external
(use-package sly :commands sly-autoloads :hook ((lisp-mode-local-vars . rainbow-delimiters-mode) (lisp-mode . sly-editing-mode)) :config (require 'sly-autoloads))
- sly-quicklisp
(use-package sly-quicklisp :after sly :load-path "site-lisp/sly-quicklisp")
- sly-quicklisp
(use-package sly-macrostep :after sly :load-path "site-lisp/sly-macrostep")
- sly external
- clojure
- clojure-mode
(use-package clojure-mode :hook (clojure-mode . rainbow-delimiters-mode) :config (with-eval-after-load 'lsp-mode (add-hook '(clojure-mode-local-vars-hook #'lsp-deferred)) (add-hook '(clojurec-mode-local-vars-hook #'lsp-deferred)) (add-hook '(clojurescript-mode-local-vars-hook #'lsp-deferred)) (setq-local lsp-enable-indentation nil)) (with-eval-after-load 'lsp-clojure (dolist (m '(clojure-mode clojurec-mode clojurescript-mode clojurex-mode)) (add-to-list 'lsp-language-id-configuration (cons m "clojure")))) (with-eval-after-load 'tree-sitter-langs (add-to-list 'tree-sitter-major-mode-language-alist '(clojurec-mode . clojure)) (add-to-list 'tree-sitter-major-mode-language-alist '(clojurescript-mode . clojure))))
- cider external
;;;; clojure (use-package cider :hook (clojure-mode-local-vars . cider-mode) :init ;; HACK Fix radian-software/radian#446: CIDER tries to calculate the frame's ;; background too early; sometimes before the initial frame has been ;; initialized, causing errors. (defvar cider-docview-code-background-color nil) (defvar cider-stacktrace-frames-background-color nil) :config (add-hook 'cider-mode-hook #'eldoc-mode) (setq nrepl-hide-special-buffers t nrepl-log-messages nil cider-font-lock-dynamically '(macro core function var deprecated) cider-overlays-use-font-lock t cider-print-options '(("length" 100)) cider-prompt-for-symbol nil cider-repl-history-display-duplicates nil cider-repl-history-display-style 'one-line cider-repl-history-file (concat doom-cache-dir "cider-repl-history") cider-repl-history-highlight-current-entry t cider-repl-history-quit-action 'delete-and-restore cider-repl-history-highlight-inserted-item t cider-repl-history-size 1000 cider-repl-result-prefix ";; => " cider-repl-use-clojure-font-lock t cider-repl-use-pretty-printing t cider-repl-wrap-history nil cider-stacktrace-default-filters '(tooling dup) ;; Don't focus the CIDER REPL when it starts. Since it can take so long ;; to start up, you either wait for a minute doing nothing or be ;; prepared for your cursor to suddenly change buffers without warning. ;; See https://github.com/clojure-emacs/cider/issues/1872 cider-repl-pop-to-buffer-on-connect 'display-only) (with-eval-after-load 'lsp-mode (setq cider-eldoc-display-for-symbol-at-point nil cider-font-lock-dynamically nil) (defun +clojure--cider-disable-completion () "Use lsp completion instead of cider." (remove-hook 'completion-at-point-functions #'cider-complete-at-point t)) (add-hook 'cider-mode-hook #'+clojure--cider-disable-completion)))
- clj-refactor external
(use-package clj-refactor :defer t :hook (clojure-mode . +config/clojure-mode-hook) :config (defun +config/clojure-mode-hook () (clj-refactor-mode 1) ;; This choice of keybinding leaves cider-macroexpand-1 unbound (cljr-add-keybindings-with-prefix "C-c r")))
- clojure-mode
- Scheme
(use-package scheme :straight nil :interpreter ("scsh" . scheme-mode) :hook (scheme-mode . rainbow-delimiters-mode) :config (defvar calculate-lisp-indent-last-sexp) (defun +scheme-indent-function-a (indent-point state) "Advice to replace `scheme-indent-function'. This function is the same as `scheme-indent-function' except it properly indents property lists and names starting with 'default'." (let ((normal-indent (current-column))) (goto-char (1+ (elt state 1))) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) (if (and (elt state 2) ;; NOTE looking-at -> looking-at-p (not (looking-at-p "\\sw\\|\\s_"))) (progn ;; NOTE (if (not ...) (progn ...)) -> (unless ... ...) (unless (> (save-excursion (forward-line 1) (point)) calculate-lisp-indent-last-sexp) (goto-char calculate-lisp-indent-last-sexp) (beginning-of-line) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)) (backward-prefix-chars) (current-column)) ;; NOTE let -> let* & moved `method' def into let bindings (let* ((function (buffer-substring (point) (progn (forward-sexp 1) (point)))) (method (or (get (intern-soft function) 'scheme-indent-function) (get (intern-soft function) 'scheme-indent-hook)))) (cond ((or (eq method 'defun) (and (null method) (> (length function) 3) ;; NOTE string-match -> string-match-p ;; NOTE The original regexp is "\\`def" but it will mess ;; up indentation with such names as 'default-...'. (string-match-p "\\`def" function))) (lisp-indent-defform state indent-point)) ;; NOTE Added this clause to handle alignment of keyword symbols ((and (null method) (> (length function) 1) ;; NOTE string-match -> string-match-p (string-match-p "\\`:" function)) (let ((lisp-body-indent 1)) (lisp-indent-defform state indent-point))) ((integerp method) (lisp-indent-specform method state indent-point normal-indent)) (method (funcall method state indent-point normal-indent))))))) (advice-add #'scheme-indent-function :override #'+scheme-indent-function-a))
- geiser external
;;;; scheme (use-package geiser :defer t :diminish geiser-autodoc-mode ;; :hook (geiser-repl-mode . turn-on-smartparens-strict-mode) :init (setq geiser-autodoc-identifier-format "%s → %s" geiser-repl-per-project-p t geiser-repl-history-filename (concat user-emacs-directory "geiser-history")) (defun +scheme/open-repl () "Open the Scheme REPL." (interactive) (call-interactively #'geiser-repl-switch) (current-buffer)) :config (with-eval-after-load 'general (+config/local-leader :keymaps '(scheme-mode-map geiser-mode-map) "'" 'geiser-repl-switch "\"" 'geiser-connect "[" 'geiser-squarify "\\" 'geiser-insert-lambda "s" 'geiser-set-scheme "R" 'geiser-reload "h" '(:ignore t :wk "help") "h<" 'geiser-xref-callers "h>" 'geiser-xref-callees "ha" 'geiser-autodoc-mode "hs" 'geiser-autodoc-show "hm" 'geiser-doc-lookup-manual "h." 'geiser-doc-symbol-at-point "r" '(:ignore t :wk "repl") "rf" 'geiser-load-file "rr" 'geiser-restart-repl) (+config/local-leader :keymaps 'scheme-mode-map "e" '(:ignore t :wk "eval") "eb" 'geiser-eval-buffer "B" 'geiser-eval-buffer-and-go "e" 'geiser-eval-last-sexp "d" 'geiser-eval-definition "D" 'geiser-eval-definition-and-go "r" 'geiser-eval-region "R" 'geiser-eval-region-and-go "r" '(ignore t :wk "repl") "rb" 'geiser-load-current-buffer) (+config/local-leader :keymaps 'geiser-repl-mode-map "c" 'geiser-repl-clear-buffer "q" 'geiser-repl-exit)))
- geiser-guile external
(use-package geiser-guile :defer t :config (let ((nonguix-path (expand-file-name "Projects/guix/nonguix" (getenv "HOME"))) (personal-path (expand-file-name "Projects/guix/devel/src" (getenv "HOME")))) (when (file-directory-p nonguix-path) (add-to-list 'geiser-guile-load-path nonguix-path)) (when (file-directory-p personal-path) (add-to-list 'geiser-guile-load-path personal-path))) (when (file-directory-p (expand-file-name "~/.config/guix/current/share/guile/site/3.0")) (add-to-list 'geiser-guile-load-path (expand-file-name "~/.config/guix/current/share/guile/site/3.0"))))
- macrostep-geiser external
(use-package macrostep-geiser :defer t :after geiser-mode geiser-repl :hook ((geiser-mode geiser-repl-mode) . macrostep-geiser-setup) :bind (:map geiser-mode-map ("C-c e" . macrostep-expand)) :config (with-eval-after-load 'general (+config/local-leader :keymaps '(scheme-mode-map geiser-repl-mode-map) "m" 'macrostep-expand "M" 'macrostep-geiser-expand-all)))
- flycheck-guile
(use-package flycheck-guile :after geiser)
- geiser external
- Emacs-lisp
- C and C++
- cc-mode builtin
(use-package cc-mode :straight nil :mode ("\\.mm\\'" . objc-mode) :mode ("\\.h\\'" . +cc-c-c++-objc-mode) :hook (c-mode-common . rainbow-delimiters-mode) ;; :hook (c++-mode-local-vars . c++-ts-mode) ;; :hook (c-mode-common . c-ts-base-mode) ;; :hook (cmake-mode-local-vars . cmake-ts-mode) :config (setq c-basic-offset tab-width c-backspace-function #'delete-backward-char) (with-eval-after-load 'ffap (add-to-list 'ffap-alist '(c-mode . ffap-c-mode))) (with-eval-after-load 'tree-sitter (add-hook 'c-mode-hook #'tree-sitter! 'append)) (with-eval-after-load 'lsp-mode (add-hook 'c-mode-hook #'lsp-deferred)))
- demangle-mode
(use-package demangle-mode :hook llvm-mode)
- cc-mode builtin
- XML
- YAML
- yaml-mode external
(use-package yaml-mode :config (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)))))
- yaml-mode external
- TOML
- toml-mode external
(use-package toml-mode :mode ("\\.toml\\'" . toml-mode) :config ;; (when (treesit-available-p) ;; (add-to-list 'major-mode-remap-alist '(toml-mode . toml-ts-mode))) (with-eval-after-load 'lsp-mode (setq lsp-toml-command (executable-find "taplo"))) (add-to-list 'lsp-language-id-configuration '("\\.toml$" . "toml")) (add-hook 'toml-mode-hook #'lsp-deferred))
- toml-mode external
- Lua
- Jinja2
- jinja2-mode external
(use-package jinja2-mode :defer t :config ;; The default behavior is to reindent the whole buffer on save. This is ;; disruptive and imposing. There are indentation commands available; the user ;; can decide when they want their code reindented. (setq jinja2-enable-indent-on-save nil) (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)) (add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-mode)))
- jinja2-mode external
- Json
- Web
- web-mode external
(defun +config/django-web-mode () "Set web-engine to django if `manage.py' detected in `projectile-project-root'." (if (projectile-project-p) (if (file-exists-p (concat (projectile-project-root) "manage.py")) (web-mode-set-engine "django")))) (defun +config/web-mode-fix-js-comment () "Fix comment handling in `web-mode' for JavaScript (from `doom')." (when (member web-mode-content-type '("javascript" "jsx")) ;; For some reason the default is to insert HTML comments even ;; in JavaScript. (setq-local comment-start "//") (setq-local comment-end "") ;; Needed since otherwise the default value generated by ;; `comment-normalize-vars' will key off the syntax and think ;; that a single "/" starts a comment, which completely borks ;; auto-fill. (setq-local comment-start-skip "// *"))) (use-package web-mode :defer 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 ;; If the user has installed `vue-mode' then, by appending this to ;; `auto-mode-alist' rather than prepending it, its autoload will have ;; priority over this one. (add-to-list 'auto-mode-alist '("\\.vue\\'" . web-mode) 'append) :mode "\\.vue\\'" :config (add-to-list 'web-mode-engine-file-regexps '("django" . "\\.\\(djhtml\\|tmpl\\|dtl\\|liquid\\|j2\\|njk\\|.html\\)\\'")) (setq web-mode-engines-alist '(("django" . "\\.jinja\\'") ("django" . "\\.djhtml\\'") ("django" . "\\.html\\'") ("erb" . "\\.erb\\'") ("erb" . "\\.rhtml\\'") ("erb" . "\\.ejs\\'") ("php" . "\\.phtml\\'") ("php" . "\\.php\\'") ("php" . "\\.psp\\'") ("php" . "\\.ctp\\'") ("jsp" . "\\.jsp\\'") ("jsp" . "\\.gsp\\'") ("asp" . "\\.asp\\'") ("aspx" . "\\.aspx\\'") ("aspx" . "\\.ascx\\'") ("closure" . "\\.soy\\'") ("lsp" . "\\.lsp\\'") ("mako" . "\\.mako\\'") ("blade" . "\\.blade\\.") ("svelte" . "\\.svelte\\'"))) (setq web-mode-markup-indent-offset 2 web-mode-enable-auto-closing t web-mode-auto-close-style 1 web-mode-css-indent-offset 2 web-mode-code-indent-offset 2 web-mode-enable-auto-pairing nil web-mode-enable-block-face t web-mode-enable-comment-interpolation t web-mode-enable-heredoc-fontification t web-mode-enable-css-colorization t web-mode-enable-part-face t web-mode-enable-current-element-highlight t web-mode-enable-current-column-highlight t web-mode-style-padding 1 web-mode-script-padding 1 web-mode-block-padding 0 web-mode-comment-style 1 web-mode-enable-curly-brace-indentation t web-mode-enable-auto-quoting nil) ;; web-mode-extra-auto-pairs ;; '(("erb" . (("beg" "end"))) ;; ("php" . (("beg" "end"))))) (with-eval-after-load 'smartparens (sp-local-pair 'web-mode "<" nil :actions :rem) (sp-local-pair 'web-mode "{%" " %}") (sp-local-pair 'web-mode "{{" " }}") (sp-local-pair 'web-mode "<!--" " -->") ) (with-eval-after-load 'general (general-nvmap :keymaps 'web-mode-map "gn" 'web-mode-navigate "zo" 'web-mode-fold-or-unfold "zw" 'web-mode-whitespaces-show) (general-vmap :keymaps 'web-mode-map "gs" 'web-mode-surround)) (defun +config/web-mode-hook () "Hooks for Web mode." (local-set-key (kbd "RET") 'newline-and-indent)) ;; Use // instead of /* as the default comment delimited in JS (with-eval-after-load 'web-mode (setf (alist-get "javascript" web-mode-comment-formats nil nil #'equal) "//")) :hook ((html-mode-local-vars-hook mhtml-mode-local-vars-hook) . tree-sitter) :hook (web-mode . +config/django-web-mode) :hook (web-mode . +config/web-mode-fix-js-comment))
- emmet-mode external
(use-package emmet-mode :defer t :hook (css-mode web-mode html-mode haml-mode nxml-mode rjsx-mode reason-mode) :config (when (require 'yasnippet nil t) (add-hook 'emmet-mode-hook #'yas-minor-mode-on)) (setq emmet-move-cursor-between-quotes t) (with-eval-after-load 'general (general-vmap :keymaps 'emmet-mode-keymap "TAB" 'emmet-wrap-with-markup) (general-def :keymaps 'emmet-mode-keymap "M-E" 'emmet-expand-line)) )
- skewer-mode external
(use-package skewer-mode :defer t :hook ((web-mode . skewer-html-mode) (js2-mode . skewer-mode)))
- lsp-tailwindcss external
(use-package lsp-tailwindcss :init (setq lsp-tailwindcss-add-on-mode t))
- sass-mode external
(use-package sass-mode :defer t :hook (scss-mode . rainbow-mode))
- css-eldoc external
(use-package css-eldoc :defer t :commands turn-on-css-eldoc ;;add a hook if you want always to see the selector options in the minibuffer :config (add-hook 'css-mode-hook 'turn-on-css-eldoc) (add-hook 'scss-mode-hook 'turn-on-css-eldoc))
- com-css-sort external
(use-package com-css-sort :defer t :commands (com-css-sort com-css-sort-attributes-block com-css-sort-attributes-document) :config (setq com-css-sort-sort-type 'alphabetic-sort))
- css-mode
(use-package css-mode :straight nil :hook ((css-mode stylus-mode) . rainbow-mode) :hook (css-mode-local-vars . tree-sitter) :config (with-eval-after-load 'skewer-mode (add-hook 'css-mode-hook 'skewer-css-mode)))
- javascript
(defvar +javascript-npm-conf (make-hash-table :test 'equal)) ;;;###autoload (defun +javascript-npm-conf (&optional project-root refresh-p) "Retrieves an alist of this project's 'package.json'. If REFRESH-P is non-nil ignore the cache." (let ((project-root (or project-root (doom-project-root)))) (or (and (not refresh-p) (gethash project-root +javascript-npm-conf)) (let ((package-file (expand-file-name "package.json" project-root))) (when-let (json (and (file-exists-p package-file) (require 'json) (json-read-file package-file))) (puthash project-root json +javascript-npm-conf)))))) ;;;###autoload (defun +javascript-npm-dep-p (packages &optional project-root refresh-p) (when-let (data (and (bound-and-true-p +javascript-npm-mode) (+javascript-npm-conf project-root refresh-p))) (let ((deps (append (cdr (assq 'dependencies data)) (cdr (assq 'devDependencies data))))) (cond ((listp packages) (funcall (if (eq (car packages) 'and) #'cl-every #'cl-some) (lambda (pkg) (assq pkg deps)) (if (listp packages) packages (list packages)))) ((symbolp packages) (assq packages deps)) (t (error "Expected a package symbol or list, got %s" packages)))))) ;;;###autoload (defun +javascript-add-npm-path-h () "Add node_modules/.bin to `exec-path'." (when-let ((search-directory (or (doom-project-root) default-directory)) (node-modules-parent (locate-dominating-file search-directory "node_modules/")) (node-modules-dir (expand-file-name "node_modules/.bin/" node-modules-parent))) (make-local-variable 'exec-path) (add-to-list 'exec-path node-modules-dir) (doom-log ":lang:javascript: add %s to $PATH" (expand-file-name "node_modules/" node-modules-parent)))) ;; ;; Commands ;;;###autoload (defun +javascript/open-repl () "Open a Javascript REPL. Meaning either `skewer-repl', if any of the skewer-*-mode's are enabled, or `nodejs-repl' otherwise." (interactive) (call-interactively (if (and (featurep 'skewer-mode) (or (bound-and-true-p skewer-mode) (bound-and-true-p skewer-css-mode) (bound-and-true-p skewer-html-mode))) #'skewer-repl #'nodejs-repl)) (current-buffer)) ;;;###autoload (defun +javascript/skewer-this-buffer () "Toggle a globalized skewer-mode, attaching an external browser (once), initiating an internal httpd server (once) and enabling the appropriate skewer-mode for the current buffer. Run this for any buffer you want to skewer." (interactive) (when (bound-and-true-p impatient-mode) (error "Skewer-mode isn't compatible with impatient mode")) (require 'skewer-mode) (unless (process-status "httpd") (run-skewer)) (pcase major-mode ((or 'css-mode 'scss-mode 'less-css-mode) (unless (bound-and-true-p skewer-css-mode) (skewer-css-mode +1))) ((or 'web-mode 'html-mode) (unless (bound-and-true-p skewer-html-mode) (skewer-html-mode +1))) ('js2-mode (unless (bound-and-true-p skewer-mode) (skewer-mode +1))) (_ (error "Invalid mode %s" major-mode)))) ;;;###autoload (defun +javascript/skewer-cleanup () "Disable skewer-mode globally and disable the httpd server." (interactive) (when (process-status "httpd") (httpd-stop)) (dolist (buf (buffer-list)) (with-current-buffer buf (if (bound-and-true-p skewer-mode) (skewer-mode -1)) (if (bound-and-true-p skewer-css-mode) (skewer-css-mode -1)) (if (bound-and-true-p skewer-html-mode) (skewer-html-mode -1))))) ;; ;; Hooks ;;;###autoload (defun +javascript-cleanup-tide-processes-h () "Clean up dangling tsserver processes if there are no more buffers with `tide-mode' active that belong to that server's project." (when tide-mode (unless (cl-loop with project-name = (tide-project-name) for buf in (delq (current-buffer) (buffer-list)) if (and (buffer-local-value 'tide-mode buf) (with-current-buffer buf (string= (tide-project-name) project-name))) return buf) (kill-process (tide-current-server))))) ;; ;; Advice ;;;###autoload (defun +javascript-tide-project-root-a () "Resolve to `doom-project-root' if `tide-project-root' fails." (or tide-project-root (or (locate-dominating-file default-directory "tsconfig.json") (locate-dominating-file default-directory "jsconfig.json")))) (with-eval-after-load 'projectile (add-to-list 'projectile-globally-ignored-directories "^node_modules$") (add-to-list 'projectile-globally-ignored-directories "^flow-typed$"))
- js2-mode external
(use-package js2-mode :defer t :interpreter ("node" . js2-mode) :hook (js2-mode . rainbow-delimiters-mode) :hook (js2-mode . js2-imenu-extras-mode) :hook (js2-mode . (lambda () (push '("function" . ?ƒ) prettify-symbols-alist))) :config (with-eval-after-load 'skewer-mode (add-hook 'js2-mode 'skewer-mode)) (setq-default js2-basic-indent 2 js2-basic-offset 2 js2-auto-indent-p t js2-cleanup-whitespace t js2-enter-indents-newline t js2-indent-on-enter-key t js2-global-externs (list "window" "module" "require" "buster" "sinon" "assert" "refute" "setTimeout" "clearTimeout" "setInterval" "clearInterval" "location" "__dirname" "console" "JSON" "jQuery" "$")))
- js2-refactor external
(use-package js2-refactor :defer t :hook (js2-mode . js2-refactor-mode) :init (js2r-add-keybindings-with-prefix "C-c C-r") :config (setq js2-skip-preprocessor-directives t) (with-eval-after-load 'general (general-nvmap :keymap 'js2-refactor-mode-map "zr" (general-simulate-key "C-c C-r" :name general-z-r-simulates-C-c-C-r :docstring "Simulates C-c C-r" :which-key "JS2 Refactor"))) )
- rjsx-mode external
(use-package rjsx-mode :defer t :mode "\\.[mc]?js\\'" :mode "\\.es6\\'" :mode "\\.pac\\'" :hook (rjsx-mode . rainbow-delimiters-mode) :hook (rjsx-mode . rainbow-mode) :bind (([remap comment-dwim ] . rjsx-comment-dwim)) :init (with-eval-after-load 'compilation (add-to-list 'compilation-error-regexp-alist 'node) (add-to-list 'compilation-error-regexp-alist-alist '(node "^[[:blank:]]*at \\(.*(\\|\\)\\(.+?\\):\\([[:digit:]]+\\):\\([[:digit:]]+\\)" 2 3 4))) :config (with-eval-after-load 'compilation (add-to-list 'compilation-error-regexp-alist 'node) (add-to-list 'compilation-error-regexp-alist-alist '(node "^[[:blank:]]*at \\(.*(\\|\\)\\(.+?\\):\\([[:digit:]]+\\):\\([[:digit:]]+\\)" 2 3 4))) (setq js-chain-indent t ;; These have become standard in the JS community js2-basic-offset 2 ;; Don't mishighlight shebang lines js2-skip-preprocessor-directives t ;; let flycheck handle this js2-mode-show-parse-errors nil js2-mode-show-strict-warnings nil ;; Flycheck provides these features, so disable them: conflicting with ;; the eslint settings. js2-strict-missing-semi-warning nil ;; maximum fontification js2-highlight-level 3 js2-idle-timer-delay 0.15) (with-eval-after-load 'general (general-nvmap :keymaps 'rjsx-mode-map "'" 'rjsx-jump-tag "gj" 'rjsx-jump-closing-tag "gk" 'rjsx-jump-opening-tag "gr" 'rjsx-rename-tag-at-point "z>" 'rjsx-electric-gt "z<" 'rjsx-electric-lt)) )
- xref-js2 external
(use-package xref-js2 :defer t :if (executable-find "ag") ; silver-searcher :config (add-hook 'js2-mode-hook (lambda () (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t))))
- typescript-mode external
(use-package typescript-mode :defer t :hook (typescript-mode . rainbow-delimiters-mode) :hook (typescript-tsx-mode . rainbow-delimiters-mode) :hook (typescript-mode . (lambda () (setq comment-line-break-function #'js2-line-break typescript-indent-level (or (and (bound-and-true-p tide-mode) (plist-get (tide-tsfmt-options) :indentSize)) typescript-indent-level) emmet-expand-jsx-className? t))) :init (add-to-list 'auto-mode-alist (cons "\\.tsx\\'" #'typescript-mode)) (with-eval-after-load 'flycheck (flycheck-add-mode 'javascript-eslint 'web-mode) (flycheck-add-mode 'javascript-eslint 'typescript-mode) (flycheck-add-mode 'javascript-eslint 'typescript-tsx-mode) (flycheck-add-mode 'typescript-tslint 'typescript-tsx-mode)) (defun +javascript-disable-tide-checkers-h () (add-to-list 'flycheck-disabled-checkers 'javascript-jshint) (add-to-list 'flycheck-disabled-checkers 'tsx-tide) (add-to-list 'flycheck-disabled-checkers 'jsx-tide)) (add-hook 'typescript-tsx-mode-hook #'+javascript-disable-tide-checkers-h) :config (when (fboundp 'web-mode) (define-derived-mode typescript-tsx-mode web-mode "TypeScript-TSX")) (autoload 'js2-line-break "js2-mode" nil t))
- tide external
(use-package tide :defer t :hook (tide-mode . tide-hl-identifier-mode) :config (setq tide-completion-detailed t tide-always-show-documentation t tide-server-max-response-length 524288 tide-completion-setup-company-backend nil) (add-hook 'tide-mode-hook (lambda () (add-hook 'kill-buffer-hook #'+javascript-cleanup-tide-processes-h nil 'local))))
- npm-mode external
(use-package npm-mode :defer t)
- nodejs-repl external
(use-package nodejs-repl :defer t)
- web-mode external
- Latex
- auctex
(use-package auctex :straight nil :mode (("\\.tex\\'" . latex-mode) ("\\.latex\\'" . latex-mode)) :commands (latex-mode LaTeX-mode plain-tex-mode) :init (progn (add-hook 'LaTeX-mode-hook #'LaTeX-preview-setup) (add-hook 'LaTeX-mode-hook #'flyspell-mode) (add-hook 'LaTeX-mode-hook #'turn-on-reftex) (setq-default TeX-engine 'xetex) (setq TeX-auto-save nil TeX-parse-self nil TeX-save-query nil TeX-PDF-mode t) (setq-default TeX-master nil)))
- preview
(use-package preview :straight nil :after auctex :commands LaTeX-preview-setup :init (progn (setq-default preview-scale 1.4 preview-scale-function '(lambda () (* (/ 10.0 (preview-document-pt)) preview-scale)))))
- reftex
(use-package reftex :straight nil :commands turn-on-reftex :init (setq reftex-plug-into-AUCTeX t))
- bibtex
(use-package bibtex :straight nil :config (setq bibtex-autokey-year-length 4 bibtex-autokey-name-year-separator "-" bibtex-autokey-year-title-separator "-" bibtex-autokey-titleword-separator "-" bibtex-autokey-titlewords 2 bibtex-autokey-titlewords-stretch 1 bibtex-autokey-titleword-length 5))
- LaTex
;; Auto-fill for LaTeX (defun lem-latex-auto-fill () "Turn on auto-fill for LaTeX mode." (turn-on-auto-fill) (set-fill-column 80) (setq default-justification 'left)) (add-hook 'LaTeX-mode-hook #'lem-latex-auto-fill) ;; Compilation command (add-hook 'LaTeX-mode-hook (lambda () (setq compile-command "latexmk -pdflatex=xelatex -f -pdf %f"))) ;; Prevent ispell from verifying some LaTeX commands ;; http://stat.genopole.cnrs.fr/dw/~jchiquet/fr/latex/emacslatex (defvar lem-ispell-tex-skip-alists '("cite" "nocite" "includegraphics" "author" "affil" "ref" "eqref" "pageref" "label")) (setq ispell-tex-skip-alists (list (append (car ispell-tex-skip-alists) (mapcar #'(lambda (cmd) (list (concat "\\\\" cmd) 'ispell-tex-arg-end)) lem-ispell-tex-skip-alists)) (cadr ispell-tex-skip-alists))) ;; Indentation with align-current in LaTeX environments (defvar lem-LaTeX-align-environments '("tabular" "tabular*")) (add-hook 'LaTeX-mode-hook (lambda () (require 'align) (setq LaTeX-indent-environment-list ;; For each item in the list... (mapcar (lambda (item) ;; The car is an environment (let ((env (car item))) ;; If this environment is in our list... (if (member env lem-LaTeX-align-environments) ;; ...then replace this item with a correct one (list env 'align-current) ;; else leave it alone item))) LaTeX-indent-environment-list)))) ;; Use dvipdfmx to convert DVI files to PDF in AUCTeX (eval-after-load 'tex '(add-to-list 'TeX-command-list '("DVI to PDF" "dvipdfmx %d" TeX-run-command t t) t)) ;; SyncTeX (http://www.emacswiki.org/emacs/AUCTeX#toc19) (defun synctex/un-urlify (fname-or-url) "A trivial function that replaces a prefix of file:/// with just /." (if (string= (substring fname-or-url 0 8) "file:///") (substring fname-or-url 7) fname-or-url))
- auctex
- Markdown
- Basic Configuration
(use-package markdown-mode :mode ("README\\.md\\'" . gfm-mode) :init (when (executable-find "markdown") (setq markdown-command "markdown")) (when (executable-find "multimarkdown") (setq markdown-command "multimarkdown")) :config (with-eval-after-load 'lsp-mode (add-hook 'markdown-mode-hook #'lsp-deferred))) ;; (use-package markdown-ts-mode ;; :init ;; (add-to-list 'major-mode-remap-alist '(markdown-mode . markdown-ts-mode))) (use-package poly-markdown :init :mode (("README\\.md\\'" . gfm-mode) ("\\.md$" . markdown-mode) ("\\.markdown$" . markdown-mode))) (use-package edit-indirect :after markdown-mode)
- Basic Configuration
- PlantUML
(use-package plantuml-mode :commands plantuml-download-jar :init (setq plantuml-jar-path (concat user-emacs-directory "data/plantuml.jar") org-plantuml-jar-path plantuml-jar-path) :config (setq plantuml-default-exec-mode (cond ((file-exists-p plantuml-jar-path) 'jar) ((executable-find "plantuml") 'executable) (plantuml-default-exec-mode)))) (use-package flycheck-plantuml :after plantuml-mode :config (flycheck-plantuml-mode) (when (eq plantuml-default-exec-mode 'executable) ;; Surprisingly, this works, even though flycheck-plantuml specifies -Djava.awt... (setq-default flycheck-plantuml-executable plantuml-executable-path))) (defun +plantuml-org-babel-execute:plantuml-a (body params) "Execute a block of plantuml code with org-babel. This function is called by `org-babel-execute-src-block'." (require 'plantuml-mode) ;; REVIEW Refactor me (let* ((body (replace-regexp-in-string "^[[:blank:]\n]*\\(@start\\)" "\\\\\\1" body)) (fullbody (org-babel-plantuml-make-body body params)) (out-file (or (cdr (assq :file params)) (org-babel-temp-file "plantuml-" ".png"))) (in-file (org-babel-temp-file "plantuml-"))) (if (eq plantuml-default-exec-mode 'server) (if (bound-and-true-p org-export-current-backend) (user-error "Exporting plantuml diagrams in server mode is not supported (see `plantuml-default-exec-mode')") (save-current-buffer (save-match-data (with-current-buffer (url-retrieve-synchronously (plantuml-server-encode-url body) nil t) (goto-char (point-min)) ;; skip the HTTP headers (while (not (looking-at "\n")) (forward-line)) (delete-region (point-min) (+ 1 (point))) (write-file out-file))))) (let* ((cmd (concat (cond ((eq plantuml-default-exec-mode 'executable) (unless (executable-find plantuml-executable-path) (error "Could not find plantuml at %s" (executable-find plantuml-executable-path))) (concat (shell-quote-argument (executable-find plantuml-executable-path)) " --headless")) ((not (file-exists-p plantuml-jar-path)) (error "Could not find plantuml.jar at %s" org-plantuml-jar-path)) ((concat "java " (cdr (assoc :java params)) " -jar " (shell-quote-argument (expand-file-name plantuml-jar-path))))) " " (pcase (file-name-extension out-file) ("png" "-tpng") ("svg" "-tsvg") ("eps" "-teps") ("pdf" "-tpdf") ("tex" "-tlatex") ("vdx" "-tvdx") ("xmi" "-txmi") ("scxml" "-tscxml") ("html" "-thtml") ("txt" "-ttxt") ("utxt" "-utxt")) " " " -p " (cdr (assoc :cmdline params)) " < " (org-babel-process-file-name in-file) " > " (org-babel-process-file-name out-file)))) (with-temp-file in-file (insert fullbody)) (message "%s" cmd) (org-babel-eval cmd ""))) (unless (cdr (assq :file params)) out-file))) (with-eval-after-load 'ob-plantuml (advice-add #'org-babel-execute:plantuml :override #'+plantuml-org-babel-execute:plantuml-a) (add-to-list 'org-babel-default-header-args:plantuml '(:cmdline . "-charset utf-8")))
- Python
- Builtin python mode builtin
(use-package python :straight nil :mode ("[./]flake8\\'" . conf-mode) :mode ("/Pipfile\\'" . conf-mode) :hook ((python-mode . lsp-deferred) (python-mode . lsp-ui-mode) (python-mode . lsp-ui-doc-mode) (python-mode . +python-use-correct-flycheck-executables-h) ;; (python-mode . guess-style-guess-tab-mode) (python-mode-local-vars . (lambda () (lsp-deferred)))) :config (if (executable-find "ipython") (setq python-interpreter (executable-find "ipython")) (setq python-interpreter (executable-find "python3"))) (when (featurep 'projectile) (add-to-list 'projectile-globally-ignored-directories "^\\.venv$")) (let ((directories `("/usr/bin/" "/usr/local/bin/" "/opt/bin" ,(expand-file-name ".local/bin/" (getenv "HOME"))))) (dolist (directory directories) (when (file-directory-p directory) (add-to-list 'python-shell-exec-path directory)))) (setq python-indent-guess-indent-offset nil python-shell-completion-native-enable nil python-shell-exec-path (list "/usr/bin/" "/usr/local/bin" (expand-file-name ".local/bin/" (getenv "HOME"))) python-indent-guess-indent-offset-verbose nil) (when (featurep 'lsp-mode) (setq lsp-pylsp-plugins-rope-completion-enabled t ; needs python-rope package lsp-pylsp-plugins-mccabe-enabled t ; needs python-mccabe package lsp-ruff-lsp-python-path (executable-find "python3") ) (when (executable-find "black") (setq lsp-pylsp-plugins-black-enabled t)) (when (executable-find "autopep8") (setq lsp-pylsp-plugins-autopep8-enabled t)) (when (executable-find "flake8") (setq lsp-pylsp-plugins-flake8-enabled t)) (when (executable-find "pycodestyle") (setq lsp-pylsp-plugins-pycodestyle-enabled t)) (when (executable-find "pydocstyle") (setq lsp-pylsp-plugins-pydocstyle-enabled t)) (when (executable-find "pylint") (setq lsp-pylsp-plugins-pylint-enabled t)) (when (executable-find "pyflakes") (setq lsp-pylsp-plugins-pyflakes-enabled t)) (when (executable-find "yapf") (setq lsp-pylsp-plugins-yapf-enabled t))) (when (featurep 'flycheck) (setq flycheck-python-mypy-executable (executable-find "mypy") flycheck-python-flake8-executable (executable-find "flake8") flycheck-python-pylint-executable (executable-find "pylint") flycheck-python-pyright-executable (executable-find "pyright"))) (with-eval-after-load 'tree-sitter (add-hook 'python-mode-local-vars-hook #'tree-sitter! 'append)) (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "d" '(:ignore t :wk "doc"))) ) (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)))))
- pydoc external
(use-package pydoc :config (with-eval-after-load 'general (general-def :keymaps 'python-mode-map "C-c C-d" 'pydoc-at-point-no-jedi)) )
- ob-ipython
(use-package ob-ipython :defer t :init (setq ob-ipython-resources-dir ".ob-ipython-resrc") (with-eval-after-load 'org-src (add-to-list 'org-src-lang-modes '("ipython" . python))) (with-eval-after-load 'ob-async (add-to-list 'ob-async-no-async-languages-alist "ipython")))
- pip-requirements external
(use-package pip-requirements :defer t :config (add-hook 'pip-requirements-mode-hook #'pip-requirements-auto-complete-setup))
- inferior-python-mode builtin
(use-package python :straight nil ;;:hook (inferior-python-mode . hide-mode-line-mode) :config (+config/local-leader :keymaps 'python-mode-map "r" '(run-python :wk "run python")))
- lsp-pyright external
(use-package lsp-pyright :after lsp-mode :hook (python-mode . (lambda () (require 'lsp-pyright) (lsp-deferred) (lsp-ui-mode) (lsp-ui-doc-mode))) :config (setq lsp-pyright-python-executable-cmd (if (executable-find "python3.12") (executable-find "python3") "python")))
- python-docstring external
(use-package python-docstring :diminish :hook (python-mode . python-docstring-mode) :config (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "dq" 'python-docstring-fill)) )
- pipenv external
(use-package pipenv :diminish :after python :commands pipenv-project-p :hook (python-mode . pipenv-mode) :init (setq pipenv-executable (executable-find "pipenv")) :config (setq pipenv-with-projectile nil) (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "p" '(:keymap pipenv-command-map :package pipenv :wk "pipenv"))) )
- pyvenv external
;; (use-package pyvenv ;; :after python ;; :demand t ;; :init ;; (pyvenv-mode +1) ;; :config ;; ;; Set correct Python interpreter ;; (setq pyvenv-post-activate-hooks ;; (list (lambda () ;; (setq python-shell-interpreter (concat pyvenv-virtual-env "bin/python3") ;; lsp-pyright-venv-path pyvenv-virtual-env)))) ;; (setq pyvenv-post-deactivate-hooks ;; (list (lambda () ;; (setq python-shell-interpreter "python3" ;; lsp-pyright-venv-path nil))))) (use-package pyvenv :after python :config (add-hook 'python-mode-local-vars-hook #'pyvenv-track-virtualenv) (add-to-list 'global-mode-string '(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name " ")) 'append))
- pyimport
(use-package pyimport :defer t :config (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "i" '(:ignore t :wk "import") "ii" 'pyimport-insert-missing "iR" 'pyimport-remove-unused "io" '+python/optimize-imports)))
- py-isort
(use-package py-isort :defer t :config (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "i" '(:ignore t :wk "import") "is" 'py-isort-buffer "ir" 'py-isort-region)))
- python-pytest
(use-package python-pytest :commands python-pytest-dispatch :config (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "t" '(:ignore t :wk "test") "ta" 'python-pytest "tf" 'python-pytest-file-dwim "tF" 'python-pytest-file "tt" 'python-pytest-function-dwim "tT" 'python-pytest-function "tr" 'python-pytest-repeat "tp" 'python-pytest-dispatch)))
- poetry
(use-package poetry :after python :init (setq poetry-tracking-strategy 'switch-buffer) ;; (add-hook 'python-mode-hook #'poetry-tracking-mode) :config (with-eval-after-load 'general (+config/local-leader :keymaps 'python-mode-map "p" 'poetry)) )
- pyenv
(use-package pyenv :after python :config (when (executable-find "pyenv") (global-pyenv-mode) (add-to-list 'exec-path (expand-file-name "shims" (or (getenv "PYENV_ROOT") "~/.pyenv")))) (add-hook 'python-mode-local-vars-hook #'+python-pyenv-mode-set-auto-h))
- flycheck-pycheckers external
(use-package flycheck-pycheckers :after flycheck :config (with-eval-after-load 'flycheck (add-hook 'flycheck-mode-hook #'flycheck-pycheckers-setup)))
- ein external
(use-package ein)
- Builtin python mode builtin
- Rust
- rust-mode
;;;###autoload (defun +rust-cargo-project-p () "Return t if this is a cargo project." (locate-dominating-file buffer-file-name "Cargo.toml")) ;;; Custom Cargo commands (autoload 'rustic-run-cargo-command "rustic-cargo") ;;;###autoload (defun +rust/cargo-audit () "Run 'cargo audit' for the current project." (interactive) (rustic-run-cargo-command "cargo audit")) (with-eval-after-load 'projectile (add-to-list 'projectile-project-root-files "Cargo.toml"))
- rustic external
(use-package rustic :mode ("\\.rs$" . rustic-mode) :bind (:map rustic-mode-map ("M-j" . lsp-ui-menu) ("M-?" . lsp-find-references) ("C-c C-c l" . flycheck-list-errors) ("C-c C-c a" . lsp-execute-code-action) ("C-c C-c r" . lsp-rename) ("C-c C-c q" . lsp-workspace-restart) ("C-c C-c Q" . lsp-workspace-shutdown) ("C-c C-c s" . lsp-rust-analyzer-status)) :init (with-eval-after-load 'rustic-flycheck (remove-hook 'rustic-mode-hook #'flycheck-mode) (remove-hook 'rustic-mode-hook #'flymake-mode-off) (remove-hook 'flycheck-mode-hook #'rustic-flycheck-setup)) (with-eval-after-load 'org-src (defalias 'org-babel-execute:rust #'org-babel-execute:rustic) (add-to-list 'org-src-lang-modes '("rust" . rustic))) :config (add-hook 'rustic-cargo-run-mode-hook (lambda () (comint-mode) (read-only-mode 0))) (add-hook 'rustic-mode-hook #'rainbow-delimiters-mode) (setq rustic-indent-method-chain t) (add-hook 'rustic-mode-local-vars-hook #'rustic-setup-lsp 'append) (add-hook 'rustic-mode-local-vars-hook #'flycheck-mode) (with-eval-after-load 'tree-sitter (add-hook 'rustic-mode-local-vars-hook #'tree-sitter! 'append)) (when (executable-find "rustup") (setq rustic-analyzer-command `(,(executable-find "rustup") "run" "stable" "rust-analyzer"))) (setq rustic-indent-method-chain t) (setq rustic-babel-format-src-block nil rustic-format-trigger nil) (defun +rust/cargo-audit () "Run 'cargo audit' for the current project." (interactive) (rustic-run-cargo-command "cargo audit")) (with-eval-after-load 'general (+config/local-leader :keymaps 'rustic-mode-map "b" '(:ignore t :wk "build") "ba" '+rust/cargo-audit "bb" 'rustic-cargo-build "bB" 'rustic-cargo-bench "bc" 'rustic-cargo-check "bC" 'rustic-cargo-clippy "bd" 'rustic-cargo-build-doc "bD" 'rustic-cargo-doc "bf" 'rustic-cargo-fmt "bn" 'rustic-cargo-new "bo" 'rustic-cargo-outdated "br" 'rustic-cargo-run "t" '(:ignore t :wk "test") "ta" 'rustic-cargo-test "t" 'rustic-cargo-current-test)))
- flycheck-rust external
(use-package flycheck-rust :after flycheck :config (with-eval-after-load 'rustic (add-hook 'flycheck-mode-hook #'flycheck-rust-setup)))
- rust-mode
- Ansible
- ansible external
(use-package ansible :defer t :if (executable-find "ansible") :commands ansible-auto-decrypt-encrypt :hook (ansible . 'ansible-auto-decrypt-encrypt) :init (put 'ansible-vault-password-file 'safe-local-variable #'stringp) :config (setq ansible-section-face 'font-lock-variable-name-face ansible-task-label-face 'font-lock-doc-face) (with-eval-after-load 'general (+config/local-leader :keymaps 'yaml-mode-map "a" '(:ignore t :wk ansible) "ad" 'ansible-decrypt-buffer "ae" 'ansible-encrypt-buffer)) )
- ansible-doc external
(use-package ansible-doc :after ansible :hook (yaml-mode . ansible-doc-mode) :config (when (featurep 'evil) (evil-set-initial-state '(ansible-doc-module-mode) 'emacs)))
- ansible external
- Perl
- Haskell
- haskell-mode external
(use-package haskell-mode :defer t :init (setq flymake-allowed-file-name-masks nil) :custom (haskell-process-load-or-reload-prompt t) (haskell-process-auto-import-loaded-modules t) (haskell-process-log t) (haskell-tags-on-save t) :config (with-eval-after-load 'lsp-mode (add-hook 'haskell-mode-hook #'lsp-deferred)))
- lsp-haskell external
(use-package lsp-haskell :defer t :after lsp)
- haskell-mode external
- Ron-mode
(use-package ron-mode)
- yuck-mode
(use-package yuck-mode)
- java
Lsp-java requires jdt-language-server, this package should automatically installs it using the
lsp-install-server
commands(use-package lsp-java :after lsp-mode :hook (java-mode . lsp))
- fish-mode
(use-package fish-mode :config (setq fish-enable-auto-indent t))