21 KiB
Emacs Config
- Early Init
- Straight.el
- Benchmarking
- Misc Stuff
- Visuals
- Text
- Org Mode
- VEMCO
- Helpful
- Dirvish/Dired
- Terminals/Shells
- Undo Tree
- Which Key
- Text Editor
- IDE Features
- Magit
- Restclient
- Finish up
- COMMENT Local variables
Early Init
Garbage Collection
The default Garbage Collector is triggered at 800 KB, way too conservative, let's bump to 512 MB. Garbage collection is a big contributor to startup times. This fends it off, then is reset later by enabling `gcmh-mode'. Not resetting it will cause stuttering/freezes.
;; -*- lexical-binding: t -*-
(defvar default-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)
(setq gc-cons-threshold (expt 2 32))
(add-hook 'emacs-startup-hook
(lambda ()
"Restore defalut values after init."
(setq file-name-handler-alist default-file-name-handler-alist)
(if (boundp 'after-focus-change-function)
(add-function :after after-focus-change-function
(lambda ()
(unless (frame-focus-state)
(garbage-collect))))
(add-hook 'focus-out-hook 'garbage-collect))))
(setq native-comp-async-report-warnings-errors nil)
(setq native-comp-deferred-compilation t)
Disabling these classic visual options during early-init seems to net a 0.04 ms boost in init time
(setq max-specpdl-size 1200)
(setq max-lisp-eval-depth 800)
(scroll-bar-mode -1)
(tool-bar-mode -1)
(menu-bar-mode -1)
(tooltip-mode -1)
Straight.el Prep
Disable package.el, since we will be using straight.el. According to the straight.el documentation;
While it is technically possible to use both package.el and straight.el at the same time, there is no real reason to, and it might result in oddities like packages getting loaded more than once.
Either way, if you need to quickly install a package for testing, you can just run
(straight-use-package)
interactively.
(setq package-enable-at-startup nil)
Enhancements
Prioritize old byte-compiled source files over newer sources. It saves us a little IO time to skip all the mtime checks on each lookup.
(setq load-prefer-newer nil)
(setq safe-local-variable-values
'((eval add-hook 'after-save-hook
'(lambda nil
(org-babel-tangle))
nil t)))
UTF-8 Support
(set-language-environment "UTF-8")
(setq default-input-method nil)
;;; early-init.el ends here
Straight.el
For now, use straight.el until [[https://github.com/progfolio/elpaca ][elpaca]] is ready for production use.
Then bootstrap
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 6))
(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))
Benchmarking
This is commented out since it adds ever so slightly to init time, but keep it around in case we need to benchmark slow init times later.
;; (straight-use-package 'benchmark-init)
;; (require 'benchmark-init)
;; (add-hook 'after-init-hook 'benchmark-init/deactivate)
Misc Stuff
(setq default-directory "/home/joe/")
(setq vc-follow-symlinks t) ; Visit real file when editing a symlink without prompting.
(global-auto-revert-mode t) ; Revert buffer's file when the file changes on disk
;; (setq confirm-kill-emacs 'y-or-n-p)
Don't let emacs add customised settings to init.el, which in our case is worse since we have a literate file.
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file t)
This avoids those annoying #backup# files that get added and eventually slow down loading the file again.
(setq auto-save-default nil)
(setq create-lockfiles nil)
(setq use-dialog-box nil)
(fset 'yes-or-no-p 'y-or-n-p)
(setq large-file-warning-threshold 100000000)
(setq backup-directory-alist `((".*" . ,(expand-file-name "backups" user-emacs-directory))))
(setq backup-by-copying t
delete-old-versions t
kept-new-versions 6
kept-old-versions 2
version-control t)
(global-set-key (kbd "C-x k") #'kill-this-buffer)
(global-set-key (kbd "C-x M-k") #'kill-buffer)
Visuals
Dashboard
Use Dashboard.el. First load `all-the-icons` for nicer rendering
(straight-use-package 'all-the-icons)
(straight-use-package 'dashboard)
(require 'dashboard)
(dashboard-setup-startup-hook)
(setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
(setq dashboard-items '((recents . 8)
(bookmarks . 8)))
(setq dashboard-startup-banner 'logo)
(setq dashboard-center-content t)
(setq dashboard-set-file-icons t)
(setq dashboard-set-navigator t)
(setq dashboard-set-heading-icons t)
(add-hook 'dashboard-mode-hook (lambda () (setq-local line-spacing 12)))
Olivetti
(straight-use-package 'olivetti)
(setq olivetti-minimum-body-width 100)
Themes
Simple functions to remember the last chosen theme.
(straight-use-package 'doom-themes)
(setq custom-safe-themes t)
(defun joe/save-current-theme ()
(let ((filename (expand-file-name "data" user-emacs-directory)))
(with-temp-buffer
(print `((last-used-theme . ,custom-enabled-themes)) (current-buffer))
(if (file-writable-p filename)
(write-file filename)
(message (format "Cannot save themes because %s is not writable" filename))))))
(add-hook 'kill-emacs-hook #'joe/save-current-theme)
(let ((filename (expand-file-name "data" user-emacs-directory)))
(if (file-readable-p filename)
(let ((theme-data (with-temp-buffer
(insert-file-contents filename)
(buffer-string))))
(mapc 'load-theme (cdr (car (read theme-data)))))))
Other
Setup other stuff
(add-hook 'text-mode-hook (lambda () (setq fill-column 100) (turn-on-auto-fill)))
(setq-default display-line-numbers 'relative)
(make-variable-buffer-local 'global-hl-line-mode)
(dolist (mode '( dashboard-mode-hook org-mode-hook term-mode-hook eww-mode-hook vterm-mode-hook
eshell-mode-hook dired-mode-hook shell-mode-hook magit-mode-hook))
(add-hook mode (lambda () (display-line-numbers-mode 0))))
(set-window-margins nil 0)
(setq-default right-fringe-width 10)
(setq scroll-margin 0
scroll-conservatively 100000
scroll-preserve-screen-position 1)
(global-hl-line-mode +1)
(column-number-mode +1)
;; (scroll-bar-mode -1)
;; (tool-bar-mode -1)
;; (menu-bar-mode -1)
;; (tooltip-mode -1)
(modify-all-frames-parameters
'((right-divider-width . 5)
(internal-border-width . 10)))
(setq inhibit-startup-screen t)
(straight-use-package 'ligature)
(global-ligature-mode)
(straight-use-package 'highlight-quoted)
(add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)
(straight-use-package 'nano-modeline)
(nano-modeline-mode)
Text
(set-face-attribute 'default nil :font "Fira Code Nerd Font" :height 105)
(setq-default c-basic-offset 4) ;; This is annoying
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
(setq-default line-spacing 5)
(setq indent-line-function 'insert-tab)
(set-default 'truncate-lines nil)
(set-default 'truncate-partial-width-windows nil)
(add-hook 'before-save-hook 'whitespace-cleanup)
Org Mode
(straight-use-package 'org-bullets)
(eval-after-load 'org (lambda ()
(setq org-todo-keywords '((sequence "TODO" "IN-PROGRESS" "|" "DONE" "BACKLOG")))
(setq org-agenda-files '("~/todo.org"))
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(setq org-edit-src-content-indentation 0)))
(add-hook 'org-mode-hook (defun joe/org-hook ()
(org-bullets-mode)
(org-indent-mode)))
VEMCO
Vertico Embark Marginalia Consult Orderless
(require 'all-the-icons)
(straight-use-package 'all-the-icons-completion)
(straight-use-package '(vertico :files (:defaults "extensions/*") :includes (vertico-directory)))
;; :bind (:map vertico-map
;; ("\M-G" . vertico-multiform-mode)
;; ("\M-e" . embark-act)))
(vertico-mode)
(require 'savehist)
(savehist-mode)
(straight-use-package 'vertico-directory)
;; :bind (:map vertico-map
;; ("RET" . vertico-directory-enter)
;; ("DEL" . vertico-directory-delete-char)
;; ("M-DEL" . vertico-directory-delete-word))
;; :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
(straight-use-package 'embark)
(straight-use-package 'embark-consult)
(straight-use-package 'marginalia)
(setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
(marginalia-mode)
(require 'all-the-icons-completion)
(all-the-icons-completion-mode)
(all-the-icons-completion-marginalia-setup)
(add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup)
(straight-use-package 'consult)
(global-set-key (kbd "C-x b") #'consult-buffer)
(straight-use-package 'orderless)
(setq completion-styles '(orderless basic)
completion-category-overrides '((file (styles basic partial-completion))))
Helpful
(straight-use-package 'helpful)
(global-set-key (kbd "C-h f") #'helpful-callable)
(global-set-key (kbd "C-h v") #'helpful-variable)
(global-set-key (kbd "C-h k") #'helpful-key)
(global-set-key (kbd "C-c C-d") #'helpful-at-point)
(global-set-key (kbd "C-h F") #'helpful-function)
(global-set-key (kbd "C-h C") #'helpful-command)
Dirvish/Dired
(straight-use-package 'dirvish)
(dirvish-override-dired-mode)
(setq delete-by-moving-to-trash t)
(setq dired-dwim-target t)
(dirvish-define-preview exa (file)
"Use `exa' to generate directory preview."
:require ("exa") ; tell Dirvish to check if we have the executable
(when (file-directory-p file) ; we only interest in directories here
`(shell . ("exa" "-H" "--icons" "--color=always" "--no-user" "-al" "--group-directories-first" ,file))))
(add-to-list 'dirvish-preview-dispatchers 'exa)
(setq dired-listing-switches "-l --almost-all --human-readable --time-style=long-iso --group-directories-first --no-group")
(setq dirvish-preview-dispatchers (cl-substitute 'pdf-preface 'pdf dirvish-preview-dispatchers))
(defun joe/dirvish-find-directory (dir)
(interactive "FDirvish Directory:")
(dirvish-dwim dir))
(define-key global-map (kbd "C-x d") #'dirvish)
(define-key global-map (kbd "C-x C-d") #'joe/dirvish-find-directory)
Terminals/Shells
(straight-use-package 'vterm)
(setq vterm-shell "/bin/fish")
(setq vterm-timer-delay 0.01)
(setq vterm-buffer-name-string "VTerm - %s")
(setq vterm-max-scrollback 100000)
(straight-use-package 'multi-vterm)
(define-key global-map (kbd "C-c t v") #'vterm-other-window)
(define-key global-map (kbd "C-c t t") #'multi-vterm)
(define-key global-map (kbd "C-c t p") #'multi-vterm-prev)
(define-key global-map (kbd "C-c t n") #'multi-vterm-next)
Undo Tree
(straight-use-package 'undo-tree)
(require 'undo-tree)
(global-undo-tree-mode)
(setq undo-tree-visualizer-diff t)
(setq undo-tree-history-directory-alist `(("." . ,(expand-file-name "undo" user-emacs-directory))))
Which Key
(straight-use-package 'which-key)
(setq which-key-idle-delay 0.3)
(which-key-mode)
Text Editor
Emacs is an great operating system, if only it had a good text editor…
Hydra
(require 'view)
(straight-use-package 'hydra)
(require 'hydra)
(defhydra hydra-zoom (global-map "<f2>")
"Window Manipulation"
("g" text-scale-increase "in")
("l" text-scale-decrease "out")
("d" View-scroll-half-page-forward "half page down")
("u" View-scroll-half-page-backward "half page up")
("e" View-scroll-line-forward "line down")
("y" View-scroll-line-backward "line up")
)
Avy
(straight-use-package 'avy)
(setq avy-case-fold-search nil) ;; case sensitive makes selection easier
(define-key global-map (kbd "C-;") 'avy-goto-char-2) ;; I use this most frequently
(define-key global-map (kbd "C-'") 'avy-goto-line) ;; Consistent with ivy-avy
(define-key global-map (kbd "M-g c") 'avy-goto-char)
(define-key global-map (kbd "M-g e") 'avy-goto-word-0) ;; lots of candidates
(define-key global-map (kbd "M-g g") 'avy-goto-line) ;; digits behave like goto-line
(define-key global-map (kbd "M-g w") 'avy-goto-word-1) ;; first character of the word
(define-key global-map (kbd "M-g (") 'avy-goto-open-paren)
(define-key global-map (kbd "M-g )") 'avy-goto-close-paren)
(define-key global-map (kbd "M-g P") 'avy-pop-mark)
Multiple Cursors
(straight-use-package 'multiple-cursors)
Meow
(straight-use-package 'meow)
(require 'meow)
(defun meow-setup ()
(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
(meow-motion-overwrite-define-key
'("j" . meow-next)
'("k" . meow-prev)
'("<escape>" . ignore))
(meow-leader-define-key
;; SPC j/k will run the original command in MOTION state.
'("j" . "H-j")
'("k" . "H-k")
;; Use SPC (0-9) for digit arguments.
'("1" . meow-digit-argument)
'("2" . meow-digit-argument)
'("3" . meow-digit-argument)
'("4" . meow-digit-argument)
'("5" . meow-digit-argument)
'("6" . meow-digit-argument)
'("7" . meow-digit-argument)
'("8" . meow-digit-argument)
'("9" . meow-digit-argument)
'("0" . meow-digit-argument)
'("/" . meow-keypad-describe-key)
'("?" . meow-cheatsheet))
(meow-normal-define-key
'("0" . meow-expand-0)
'("9" . meow-expand-9)
'("8" . meow-expand-8)
'("7" . meow-expand-7)
'("6" . meow-expand-6)
'("5" . meow-expand-5)
'("4" . meow-expand-4)
'("3" . meow-expand-3)
'("2" . meow-expand-2)
'("1" . meow-expand-1)
'("-" . negative-argument)
'(";" . meow-reverse)
'("," . meow-inner-of-thing)
'("." . meow-bounds-of-thing)
'("[" . meow-beginning-of-thing)
'("]" . meow-end-of-thing)
'("a" . meow-append)
'("A" . meow-open-below)
'("b" . meow-back-word)
'("B" . meow-back-symbol)
'("c" . meow-change)
'("d" . meow-delete)
'("D" . meow-backward-delete)
'("e" . meow-next-word)
'("E" . meow-next-symbol)
'("f" . meow-find)
'("g" . meow-cancel-selection)
'("G" . meow-grab)
'("h" . meow-left)
'("H" . meow-left-expand)
'("i" . meow-insert)
'("I" . meow-open-above)
'("j" . meow-next)
'("J" . meow-next-expand)
'("k" . meow-prev)
'("K" . meow-prev-expand)
'("l" . meow-right)
'("L" . meow-right-expand)
'("m" . meow-join)
'("n" . meow-search)
'("o" . meow-block)
'("O" . meow-to-block)
'("p" . meow-yank)
'("q" . meow-quit)
'("Q" . meow-goto-line)
'("r" . meow-replace)
'("R" . meow-swap-grab)
'("s" . meow-kill)
'("t" . meow-till)
'("u" . meow-undo)
'("U" . meow-undo-in-selection)
'("v" . meow-visit)
'("w" . meow-mark-word)
'("W" . meow-mark-symbol)
'("x" . meow-line)
'("X" . meow-goto-line)
'("y" . meow-save)
'("Y" . meow-sync-grab)
'("z" . meow-pop-selection)
'("'" . repeat)
'("<escape>" . ignore)))
(meow-setup)
(meow-global-mode t)
Boon
(straight-use-package 'boon)
(require 'boon-qwerty)
(boon-mode)
Kakoune.el
(straight-use-package 'kakoune)
(require 'kakoune)
(defun ryo-enter () "Enter normal mode" (interactive) (ryo-modal-mode 1))
(defun my/kakoune-setup ()
"Call kakoune-setup-keybinds and then add some personal config."
(kakoune-setup-keybinds)
(setq ryo-modal-cursor-type 'box)
(add-hook 'prog-mode-hook #'ryo-enter)
(define-key ryo-modal-mode-map (kbd "SPC h") 'help-command)
;; Access all C-x bindings easily
(define-key ryo-modal-mode-map (kbd "z") ctl-x-map)
(ryo-modal-keys
("," save-buffer)
("P" counsel-yank-pop)
("m" mc/mark-next-like-this)
("M" mc/skip-to-next-like-this)
("n" mc/mark-previous-like-this)
("N" mc/skip-to-previous-like-this)
("M-m" mc/edit-lines)
("*" mc/mark-all-like-this)
("v" er/expand-region)
("C-v" set-rectangular-region-anchor)
("M-s" mc/split-region)
(";" (("q" delete-window)
("v" split-window-horizontally)
("s" split-window-vertically)))
("C-h" windmove-left)
("C-j" windmove-down)
("C-k" windmove-up)
("C-l" windmove-right)
("C-u" scroll-down-command :first '(deactivate-mark))
("C-d" scroll-up-command :first '(deactivate-mark))))
(add-hook 'after-init-hook 'my/kakoune-setup)
IDE Features
C
;; (straight-use-package 'yasnippet)
(straight-use-package 'markdown-mode)
(straight-use-package 'lsp-mode)
;; (straight-use-package 'eglot)
;; (setq completion-in-region-function (kind-icon-enhance-completion completion-in-region-function))
(setq completion-in-region-function #'consult-completion-in-region)
(global-set-key [remap dabbrev-expand] 'hippie-expand)
;; (straight-use-package 'cape)
;; (add-to-list 'completion-at-point-functions #'cape-symbol)
;; (add-to-list 'completion-at-point-functions #'cape-file)
;; (add-to-list 'completion-at-point-functions #'cape-dabbrev)
;; (straight-use-package 'kind-icon)
;; (require 'kind-icon)
(straight-use-package 'company)
(require 'company)
(setq company-idle-delay 0
company-tooltip-idle-delay 10
company-require-match nil
company-frontends '(company-preview-frontend company-echo-metadata-frontend)
;; company-frontends '(company-text-icons-margin company-echo-metadata-frontend)
company-backends '((company-capf company-files)))
(setq company-transformers '(company-sort-by-occurrence))
;; (setq company-transformers '())
(add-to-list 'completion-at-point-functions #'elisp-completion-at-point)
(with-eval-after-load 'company
(define-key company-active-map (kbd "C-f") #'company-complete-selection)
(define-key company-active-map [tab] #'company-complete-common-or-cycle)
(define-key company-active-map (kbd "<backtab>") (lambda () (interactive) (company-complete-common-or-cycle -1))))
(global-company-mode)
;; (delete 'elisp-completion-at-point completion-at-point-functions)
;; (delete 'cape-symbol completion-at-point-functions)
;; (straight-use-package 'corfu)
;; (global-corfu-mode)
;; (setq
;; corfu-cycle t ;; Enable cycling for `corfu-next/previous'
;; corfu-auto t ;; Enable auto completion
;; corfu-separator ?\s ;; Orderless field separator
;; corfu-quit-at-boundary nil ;; Never quit at completion boundary
;; corfu-quit-no-match nil ;; Never quit, even if there is no match
;; corfu-preview-current t ;; Disable current candidate preview
;; corfu-preselect-first t ;; Disable candidate preselection
;; corfu-on-exact-match nil ;; Configure handling of exact matches
;; corfu-echo-documentation t ;; Disable documentation in the echo area
;; corfu-scroll-margin 5) ;; Use scroll margin
;; (setq corfu-preview-current t)
;; (straight-use-package
;; (setq-local corfu-auto t
;; corfu-auto-delay 0.1
;; corfu-auto-prefix 1
;; completion-styles '(orderless basic))
;; (global-set-key (kbd "<tab>")
;; (lambda ()
;; (interactive)
;; (let ((company-tooltip-idle-delay 0.0))
;; (company-complete)
;; (and company-candidates
;; (company-call-frontends 'post-command)))))
;; Enable fancy-dabbrev previews everywhere:
;; (global-fancy-dabbrev-mode)
;; (global-set-key [tab] #'fancy-dabbrev-expand-or-indent)
;; (global-set-key (kbd "<backtab>") 'fancy-dabbrev-backward)
;; (require 'yasnippet)
;; (yas-global-mode 1)
Magit
The best git porcelain/client I've ever used
(straight-use-package 'magit)
Restclient
(straight-use-package 'restclient)
Finish up
COMMENT Local variables
;; Local Variables: ;; eval: (add-hook 'after-save-hook '(lambda () (org-babel-tangle)) nil t) ;; End: