#+TITLE: Emacs config #+AUTHOR: Erwin Boskma #+OPTIONS: toc:nil h:4 #+STARTUP: overview #+PROPERTY: header-args:emacs-lisp :tangle init.el #+PROPERTY: header-args:emacs-lisp+ :noweb tangle This is my emacs configuration. It is structured as an org-mode file, to easily provide context and documentation. Last export: {{{modification-time(%Y-%m-%d %H:%M)}}} #+TOC: headlines 4 * Setup ** Early init This ends up in =early-init.el=. #+begin_src emacs-lisp :tangle early-init.el ;; -*- lexical-binding: t; -*- ;; Prevent package.el from loading packages until we're ready for it (setq package-enable-at-startup nil) ;; Don't resize the frame to preserve the number of columns or lines ;; being displayed when setting font, menu bar, tool bar, tab bar, ;; internal borders, fringes, or scroll bars. Since I use a tiling ;; window manager, this option is 1. useless anyways and 2. _terribly_ expensive. (setq frame-inhibit-implied-resize t) ;; Suppress warnings and errors during asynchronous native compilation (setq native-comp-async-report-warnings-errors nil) ;; Prevent unwanted runtime builds; packages are compiled ahead-of-time when ;; they are installed and site files are compiled when gccemacs is installed. ;; (setq comp-deferred-compilation nil) (setq native-comp-deferred-compilation nil) ;; Prevent unwanted runtime builds in gccemacs (native-comp); packages are ;; compiled ahead-of-time when they are installed and site files are compiled ;; when gccemacs is installed. (setq comp-deferred-compilation nil) ;; This should fix input lag by ~80% when using PGTK (setq-default pgtk-wait-for-event-timeout 0) #+end_src ** Enable lexical binding Setting =lexical-binding= to =t= can improve startup time. This has to be first! #+begin_src emacs-lisp ;; -*- lexical-binding: t; -*- #+end_src ** Personal variables This sets some variables with my personal preferences for easy customization #+begin_src emacs-lisp (defvar my/default-font "Iosevka Nerd Font") (defvar my/variable-width-font "Iosevka Aile") (defvar my/default-font-height 13) (defvar my/default-font-weight 'normal) (defvar my/default-font-width 'normal) (defvar my/variable-width-font-height 1.1) (defvar my/config-file-path (expand-file-name "config.org" user-emacs-directory)) (defvar my/snippets-dir (expand-file-name "snippets" user-emacs-directory)) (defvar my/local-init-file (expand-file-name "local-init.el" user-emacs-directory)) (defvar my/leader "C-c") #+end_src And my personal info. #+begin_src emacs-lisp (setq user-full-name "Erwin Boskma" user-mail-address "erwin@datarift.nl") #+end_src ** Garbage collector Increasing the garbage collector threshold should also improve startup time. This increases it from 800 kB to 128MB #+begin_src emacs-lisp (setq gc-cons-threshold (* 128 1024 1024)) #+end_src This resets the threshold back to it's default. This should not be done if =lsp-mode= is enabled, it needs the higher threshold. #+begin_src emacs-lisp :tangle no (add-hook 'after-init-hook (lambda () (setq gc-cons-threshold 800000) (message "gc-cons-threshold restored to %s" gc-cons-threshold))) #+end_src ** Increase process output buffer LSP responses can be rather large, in the 800KiB - 3MiB range. 2MiB is a decent value #+begin_src emacs-lisp (setq read-process-output-max (* 2 1024 1024)) #+end_src ** Package sources Add repositories where packages are installed from. #+begin_src emacs-lisp (setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/") ("melpa" . "https://melpa.org/packages/") ("org" . "https://orgmode.org/elpa/") ("onpa" . "https://olanilsson.bitbucket.io/packages/"))) #+end_src ** Bootstrap use-package If =use-package= is not installed, install it. *NOTE*: Disabled because it is builtin since emacs 29 #+begin_src emacs-lisp :tangle no (require 'package) (package-initialize) (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package) (cl-eval-when 'compile (require 'use-package))) #+end_src By default packages should always be installed from the package manager. This is equivalent to setting =:ensure t= on each call to =use-package=. To override this for a package (e.g. because it is builtin, or a subpackage), use =:ensure nil=. This is done automatically for packages that use =:load-path=. #+begin_src emacs-lisp (setq use-package-always-ensure t) #+end_src ** Bootstrap straight.el and use-package [DISABLED] [[https://github.com/radian-software/straight.el][straight.el]] is a pure functional package manager and installs packages from git instead of downloading tars #+begin_src emacs-lisp :tangle no (defvar bootstrap-version) ;; Workaround for flycheck. See https://github.com/radian-software/straight.el/issues/508 for more info (setq straight-fix-flycheck t) (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)) #+end_src Install =use-package= and make it use =straight.el= by default. #+begin_src emacs-lisp :tangle no (straight-use-package 'use-package) (setq straight-use-package-by-default t) #+end_src ** general.el [[https://github.com/noctuid/general.el][general.el]] provides a more convenient way for binding keys in emacs. It also integrates with =use-package= with the =:general= keyword. #+begin_src emacs-lisp (use-package general :demand t :config (progn (general-define-key :prefix my/leader "a" 'org-agenda "k" 'general-describe-keybindings) (general-define-key "C-M-z" 'zap-to-char "M-z" 'zap-up-to-char))) #+end_src ** Setup asdf [DISABLED] [[https://asdf-vm.com][asdf]] is a tool to install and use multiple versions of development tools and programming languages. #+begin_src emacs-lisp :tangle no (when (executable-find "asdf") (use-package asdf-vm :straight (:host github :repo "delonnewman/asdf-vm.el") ;; :load-path "~/.config/emacs/elisp/asdf-vm.el/" :config (asdf-vm-init))) #+end_src ** Set custom settings to load in temp file [DISABLED] Setting =custom-file= stops emacs from adding customised settings to =init.el=. I prefer to specify everything in this file, so this creates a temporary file where the customisations are stored. This effectively localises customisations to a session #+begin_src emacs-lisp :tangle no (setq custom-file (make-temp-file "emacs-custom")) #+end_src ** Disable the customize interface The =customize= functionality is annoying and messes up regularly. Stuff it has done so far: - Clobber the path to =mix= in Elixir projects This has been borrowed from [[https://github.com/doomemacs/doomemacs/blob/35865ef5e89442e3809b8095199977053dd4210f/core/core-ui.el#L628-L639][Doom Emacs]] #+begin_src emacs-lisp (dolist (sym '(customize-option customize-browse customize-group customize-face customize-rogue customize-saved customize-apropos customize-changed customize-unsaved customize-variable customize-set-value customize-customized customize-set-variable customize-apropos-faces customize-save-variable customize-apropos-groups customize-apropos-options customize-changed-options customize-save-customized)) (put sym 'disabled (concat "This config doesn't support `customize', configure Emacs from " user-emacs-directory "/config.org instead"))) (put 'customize-themes 'disabled (concat "Use `load-theme' in " user-emacs-directory "/config.org instead")) #+end_src ** Custom packages location Emacs only includes files directly under =user-emacs-directory=. I like to keep custom packages in a separate =elisp/= subdirectory. #+begin_src emacs-lisp (add-to-list 'load-path (expand-file-name "elisp/" user-emacs-directory)) #+end_src ** Record key frequency Records what keys are used the most, so I can see if I can optimise shortcuts #+begin_src emacs-lisp (use-package keyfreq :config (keyfreq-mode 1) (keyfreq-autosave-mode 1)) #+end_src ** Save minibuffer history #+begin_src emacs-lisp (use-package savehist :ensure nil :init (savehist-mode)) #+end_src * Preferences Don't display the help screen at startup #+begin_src emacs-lisp (setq inhibit-startup-screen t) #+end_src Enable line wrapping #+begin_src emacs-lisp (global-visual-line-mode 1) #+end_src Disable toolbar and scroll bar #+begin_src emacs-lisp (tool-bar-mode -1) (menu-bar-mode -1) (scroll-bar-mode -1) #+end_src Don't show the warnings buffer for anything lesser than an error. #+begin_src emacs-lisp (setq warning-minimum-level :error) #+end_src Keep buffers up-to-date automatically #+begin_src emacs-lisp (global-auto-revert-mode t) #+end_src Automatically scroll the compile buffer #+begin_src emacs-lisp (setq compilation-scroll-output 'first-error) #+end_src Always display line numbers, and the relative line number #+begin_src emacs-lisp (setq display-line-numbers-type 'relative) (global-display-line-numbers-mode) #+end_src Show matching parenthesese #+begin_src emacs-lisp (show-paren-mode 1) (setq show-paren-style 'expression) #+end_src Surround marked text with (), [] or {} #+begin_src emacs-lisp (electric-pair-mode 1) #+end_src Centralise backup files (the =filename~= files), so they don't pollute the project. #+begin_src emacs-lisp (setq backup-directory-alist '((".*" . "~/.local/share/emacs/backup")) backup-by-copying t ; Don't delink hardlinks version-control t ; Use version numbers on backup files delete-old-versions t ; Automatically delete obsolete backups kept-new-versions 20 ; How many of the newest versions to keep kept-old-versions 5 ; And how many of the oldest ) #+end_src Same for auto save files (the ones with the =#=) #+begin_src emacs-lisp (setq auto-save-file-name-transforms '((".*" "~/.local/share/emacs/autosave/" t))) #+end_src Default indenting to spaces. If tabs are necessary, this can be set buffer-local. A single tab character can be added using =C-q TAB= #+begin_src emacs-lisp (setq-default indent-tabs-mode nil) #+end_src Delete trailing whitespace on save. #+begin_src emacs-lisp (add-hook 'before-save-hook 'delete-trailing-whitespace) #+end_src Don't move files to trash when deleting. #+begin_src emacs-lisp (setq delete-by-moving-to-trash nil) #+end_src Restore cursor position when re-opening a file #+begin_src emacs-lisp (save-place-mode t) #+end_src Prefer to open frames in a horizontal split #+begin_src emacs-lisp (setq split-height-threshold nil) #+end_src Set fill column to 80 #+begin_src emacs-lisp (setq-default fill-column 80) #+end_src * Interface ** Easy edit config file #+begin_src emacs-lisp (defun find-config () "Edit config.org" (interactive) (find-file my/config-file-path)) (general-define-key :prefix my/leader "C" 'find-config) #+end_src ** Appearance I prefer the [[https://draculatheme.com][dracula theme]] #+begin_src emacs-lisp (use-package dracula-theme :init (load-theme 'dracula t)) #+end_src [[https://github.com/belak/emacs-monokai-pro-theme][Monokai Pro]] is also pretty. #+begin_src emacs-lisp :tangle no (use-package monokai-pro-theme :init (load-theme 'monokai-pro t)) #+end_src Use Iosevka as font. #+begin_src emacs-lisp (add-to-list 'default-frame-alist `(font . ,(format "%s-%d:weight=%s:width=%s" my/default-font my/default-font-height my/default-font-weight my/default-font-width))) #+end_src #+begin_src emacs-lisp :tangle no (set-face-attribute 'default nil :family my/default-font :weight my/default-font-weight :width my/default-font-width :height my/default-font-height) (set-face-attribute 'variable-pitch nil :family my/variable-width-font :weight my/default-font-weight :width my/default-font-width :height my/variable-width-font-height) #+end_src Emoji support #+begin_src emacs-lisp (defun set-emoji-font () (when (member "Twitter Color Emoji" (font-family-list)) (set-fontset-font t 'emoji (font-spec :family "Twitter Color Emoji") nil 'prepend))) (use-package emojify :defer t :init (setq emojify-display-style 'unicode) :config (set-emoji-font) :hook (server-after-make-frame . set-emoji-font)) #+end_src Enable ligatures #+begin_src emacs-lisp (use-package ligature :config ;; Enable all Iosevka ligatures in programming modes (ligature-set-ligatures 'prog-mode '("<---" "<--" "<<-" "<-" "->" "-->" "--->" "<->" "<-->" "<--->" "<---->" "