Table of Contents
Getting Started
This is my personal Emacs configuration. It's deployed on my personal site via the Emacs Org publish system that generates Org files to HTML. In other words, it's a static site generator that takes Org file input and outputs a static website.
Try pulling from the main file in the blog repo. In Emacs, navigate to
this file and call M-x org-babel-tangle
, which bounds to keychord
C-c C-v C-t
as a shortcut. This will create or override the
init.el
file that Emacs reads to tweak its config.
Setup
Emacs documents all of its variables. When in doubt, call
M-x describe-variable
or M-x describe-function
.
Keychords may also be used: C-h v [variable symbol]
, C-h f [function symbol]
respectively.
Package manager
(require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
Default Emacs UI
(menu-bar-mode -1) ; Disable for more headspace (tool-bar-mode -1) ; Remove toolbar (scroll-bar-mode -1) ; Remove scroll bar (save-place-mode 1) ; Remember cursor position (load-theme 'modus-vivendi nil)
General
(setq-default inhibit-startup-message t ; Don't show default emacs startup screen window-combination-resize t ; Resize windows proportionally display-time-default-load-average nil ; Don't show system load time in modeline indent-tabs-mode nil ; Stop using tabs to indent tab-width 4 ; Change default tab width ;; My machine has enough memory to increase the default values gc-cons-threshold 50000000 large-file-warning-threshold 100000000 ;; Increase default max recursion depth (when coding in Elisp) max-specpdl-size 19500 max-lisp-eval-depth 24000 bidi-paragraph-direction 'left-to-right ; Just work with left to right langs bidi-inhibit-bpa t ;; Directories delete-by-moving-to-trash t auto-revert-check-vc-info t use-short-answers t) ;; Toggle features not available in a fresh Emacs install (put 'downcase-region 'disabled nil) ; Allow use of C-x C-l (downcase region) (put 'upcase-region 'disabled nil) ; Allow use of C-x C-u (capitalize region) (put 'narrow-to-region 'disabled nil) ; Allows to narrow region ;; Useful Register setup when appending/prepending (setq register-separator ?+) (set-register register-separator "\n\n") ;; (set-register ?z '(file . "")) ;; Useful functionality (add-hook 'before-save-hook 'delete-trailing-whitespace) (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p) (global-set-key (kbd "C-x k") 'kill-this-buffer) (global-so-long-mode 1) ;; Change Emacs backup files location (setq backup-directory-alist '(("." . "~/.config/emacs"))) ;; This change prevents the crashing of the React server after any file change (setq create-lockfiles nil) ;; inherit PATH from shell, depends on `exec-path-from-shell' package (exec-path-from-shell-copy-envs '("LANG" "LC_TYPE" "SSH_AUTH_SOCK" "SSH_AGENT_PID")) (exec-path-from-shell-initialize) (setq project-vc-extra-root-markers '("tsconfig.json" "build.clj")) (setq project-vc-ignores '("node_modules"))
Custom prefix keys
Some functions get called often but are not binded to a keychord by default. Custom prefix keys allows defining a key and bind it to a function.
;; Keys may be binded globally like below (global-set-key (kbd "<C-M-return>") 'delete-other-windows) ;; Define C-z key map (define-prefix-command 'my-super-z-map) ;; Bind C-z to custom prefix command (global-set-key (kbd "C-z") 'my-super-z-map) ;; Set of mapped keys in `C-z' (define-key my-super-z-map (kbd "o") 'browse-url-at-point) (define-key my-super-z-map (kbd "a") 'add-file-local-variable-prop-line)
Org
(with-eval-after-load 'org (visual-line-mode 1) ; wrap lines (setq org-src-fontify-natively t ; highlight syntax in code source blocks org-catch-invisible-edits t)) ;; Org Babel Setup (org-babel-do-load-languages 'org-babel-load-languages '((shell . t) (latex . t) (python . t) (ditaa . t)))
Org Tree Slide
Create slide-shows with Org mode.
(with-eval-after-load 'org-tree-slide-mode
(org-image-actual-width nil))
Extras
Nice to have packages and functionality. Most of these improve coding experience.
Electricity
;; Electric Layout Mode (add-hook 'css-mode 'electric-layout-mode) ; insert newline after the insertion of '{' (electric-indent-mode +1) ; toggle on the fly re-indentation ;; Electric Pairs (add-hook 'mhtml-mode-hook 'electric-pair-local-mode) (add-hook 'emacs-lisp-mode-hook 'electric-pair-local-mode) (add-hook 'clojure-mode-hook 'electric-pair-local-mode) (add-hook 'lisp-interaction-mode-hook 'electric-pair-local-mode) (add-hook 'web-mode-hook 'electric-pair-local-mode) (add-hook 'ielm-mode-hook 'electric-pair-local-mode) (add-hook 'js-mode-hook 'electric-pair-local-mode) (add-hook 'typescript-mode-hook 'electric-pair-local-mode) (add-hook 'org-mode-hook 'electric-pair-local-mode) (add-hook 'scheme-mode-hook 'electric-pair-local-mode) (add-hook 'python-mode-hook 'electric-pair-local-mode) (add-hook 'css-mode-hook 'electric-pair-local-mode) ;; Add extra pairs for js-mode (defvar js-mode-electric-pairs '((?` . ?`)) "Electric pairs for js-mode.") (defun js-mode-add-electric-pairs () (setq-local electric-pair-pairs (append electric-pair-pairs js-mode-electric-pairs)) (setq-local electric-pair-text-pairs electric-pair-pairs)) (add-hook 'js-mode-hook 'js-mode-add-electric-pairs) (add-hook 'mhtml-mode-hook 'js-mode-add-electric-pairs) ; needs it for `script` tags ;; Subword Mode (add-hook 'js-mode-hook #'subword-mode) (add-hook 'js-jsx-mode-hook #'subword-mode) (add-hook 'typescript-mode-hook #'subword-mode) (add-hook 'python-mode-hook #'subword-mode) (add-hook 'c-mode-hook #'subword-mode) (add-hook 'clojure-mode-hook #'subword-mode) ;; Enable Dash font-locking (eval-after-load 'dash '(dash-enable-font-lock))
Prettify Symbols
(global-prettify-symbols-mode t) (defun my-add-pretty-lambda () "Make some word or string show as pretty Unicode symbols" (push '("lambda" . 955) prettify-symbols-alist) ; λ (push '("->" . 8594) prettify-symbols-alist) ; → (push '("=>" . 8658) prettify-symbols-alist) ; ⇒ (push '("map" . 8614) prettify-symbols-alist) ; ↦ ) (add-hook 'tex-mode-hook 'my-add-pretty-lambda) (add-hook 'emacs-lisp-mode-hook (lambda () "Beautify Emacs Symbols" (push '("<=" . "≤") prettify-symbols-alist))) (add-hook 'scheme-mode-hook (lambda () "Beautify Emacs Symbols" (push '("<=" . "≤") prettify-symbols-alist))) (add-hook 'clojure-mode-hook (lambda () "Beautify Clojure Symbols" (push '("map" . 8614) prettify-symbols-alist) (push '("->" . 8594) prettify-symbols-alist)))
Rainbow Delimiters
(custom-set-faces '(rainbow-delimiters-depth-1-face ((t (:foreground "blue violet")))) '(rainbow-delimiters-depth-2-face ((t (:foreground "red")))) '(rainbow-delimiters-depth-3-face ((t (:foreground "cyan3")))) '(rainbow-delimiters-depth-4-face ((t (:foreground "blue")))) '(rainbow-delimiters-depth-5-face ((t (:foreground "gold")))) '(rainbow-delimiters-depth-6-face ((t (:foreground "lavender")))) '(rainbow-delimiters-depth-7-face ((t (:foreground "ivory")))) '(rainbow-delimiters-depth-8-face ((t (:foreground "magenta")))) '(rainbow-delimiters-depth-9-face ((t (:foreground "red"))))) (add-hook 'clojure-mode-hook #'rainbow-delimiters-mode) (add-hook 'emacs-lisp-mode-hook #'rainbow-delimiters-mode) (add-hook 'ielm-mode-hook #'rainbow-delimiters-mode) (add-hook 'lisp-interaction-mode-hook #'rainbow-delimiters-mode) (add-hook 'lisp-mode-hook #'rainbow-delimiters-mode)
Packages
Vertico / Orderless / CtrlF / Marginalia / Consult
Better buffer search and improved minibuffer experience
(vertico-mode) (marginalia-mode) (setq completion-styles '(orderless basic) completion-category-overrides '((file (styles basic partial-completion)))) (advice-add #'vertico--setup :after (lambda (&rest _) (setq-local completion-auto-help nil completion-show-inline-help nil))) ;; Consult (global-set-key (kbd "C-x b") 'consult-buffer) (global-set-key (kbd "C-x 4 b") 'consult-buffer-other-window) (global-set-key (kbd "M-s G") 'consult-git-grep) (global-set-key (kbd "M-g g") 'consult-goto-line) (global-set-key (kbd "M-g M-g") 'consult-goto-line) (global-set-key (kbd "M-g f") 'consult-flymake) (global-set-key (kbd "C-x p b") 'consult-project-buffer) (global-set-key (kbd "M-s l") 'consult-line) ;; Use Consult to select xref locations with preview (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) ;; Enable vertico-multiform (vertico-multiform-mode) ;; Configure the display per completion category. ;; Use the grid display for files and a buffer ;; for the consult-grep commands. (setq vertico-multiform-categories '( ;;(file grid) (consult-grep buffer)))
Corfu & Cape
Auto-completion in Emacs.
;; TAB cycle if there are only few candidates (setq completion-cycle-threshold 3) (setq tab-always-indent 'complete) (setq corfu-auto t) (global-corfu-mode) ;; Cape ;; Add `completion-at-point-functions', used by `completion-at-point'. (add-to-list 'completion-at-point-functions #'cape-file) (add-to-list 'completion-at-point-functions #'cape-dabbrev) (add-to-list 'completion-at-point-functions #'cape-keyword) (add-to-list 'completion-at-point-functions #'cape-sgml) (add-to-list 'completion-at-point-functions #'cape-symbol)
Dired
(setq dired-recursive-copies 'always ; “always” means no asking dired-recursive-deletes 'top ; “top” means ask once dired-dwim-target t) (add-hook 'dired-mode-hook '(lambda () (dired-hide-details-mode 1))) (put 'dired-find-alternate-file 'disabled nil)
Web Development
(setq css-indent-offset 2) (add-hook 'js-mode-hook (lambda () (define-key js-mode-map (kbd "M-.") 'xref-find-definitions))) ;; Configure Eglot Eslint/Flymake with JSX and TSX ;; wraps `flymake-eslint-enable' to run only root dirs with `.eslintrc' file (defun me/flymake-eslint-enable-maybe () "Enable `flymake-eslint' based on the project configuration. Search for the project ESLint configuration to determine whether the buffer should be checked." (when-let* ((root (locate-dominating-file (buffer-file-name) "package.json")) (rc (locate-file ".eslintrc" (list root) '(".js" ".json")))) (make-local-variable 'exec-path) (push (file-name-concat root "node_modules" ".bin") exec-path) (flymake-eslint-enable))) (add-hook 'eglot-managed-mode-hook (lambda () (me/flymake-eslint-enable-maybe))) (add-hook 'typescript-mode-hook (lambda () (setq-local eglot-stay-out-of '(flymake)) (eglot-ensure))) (add-hook 'js-mode-hook (lambda () (setq-local eglot-stay-out-of '(flymake)) (eglot-ensure))) (add-hook 'js-jsx-mode-hook (lambda () (setq-local eglot-stay-out-of '(flymake)) (eglot-ensure)))
Clojure
(setq cider-clojure-cli-aliases "-M:dev" cider-eval-result-prefix "=>" cider-repl-display-help-banner nil) (remove-hook 'flymake-diagnostic-functions #'flymake-proc-legacy-flymake) (add-hook 'clojure-mode-hook #'flymake-kondor-setup) (add-hook 'clojure-mode-hook (lambda () (add-hook 'after-save-hook #'eglot-format-buffer nil 'make-it-local))) (add-hook 'clojure-mode-hook 'eglot-ensure)
Dev tools
;; Tree-sitter ;; activate tree-sitter on any buffer containing code for which it has a parser available (global-tree-sitter-mode) ;; you can easily see the difference tree-sitter-hl-mode makes for python, ts or tsx ;; by switching on and off (add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode) ;; we choose this instead of tsx-mode so that eglot can automatically figure out language for server ;; see https://github.com/joaotavora/eglot/issues/624 and https://github.com/joaotavora/eglot#handling-quirky-servers (define-derived-mode typescriptreact-mode typescript-mode "TypeScript TSX") ;; use our derived mode for tsx files (add-to-list 'auto-mode-alist '("\\.tsx?\\'" . typescriptreact-mode)) ;; by default, typescript-mode is mapped to the treesitter typescript parser ;; use our derived mode to map both .tsx AND .ts -> typescriptreact-mode -> treesitter tsx (add-to-list 'tree-sitter-major-mode-language-alist '(typescriptreact-mode . tsx))