#+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 "MonaspiceNe Nerd Font") (defvar my/variable-width-font "Iosevka Aile") (defvar my/comment-font "MonaspiceRn Nerd Font") (defvar my/default-font-height 120) (defvar my/default-font-weight 'light) (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 ** 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 ** 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 title bar, toolbar and scroll bar #+begin_src emacs-lisp ; This needs to go before scroll-bar-mode (setq-default default-frame-alist '((undecorated . t))) (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 Show trailing whitespace #+begin_src emacs-lisp (setq show-trailing-whitespace t) #+end_src Delete trailing whitespace on save. #+begin_src emacs-lisp (add-hook 'before-save-hook 'delete-trailing-whitespace) #+end_src Sentences end in one space, not two. We're not using typewriters. #+begin_src emacs-lisp (setq sentence-end-double-space nil) #+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 and make sure they're of a decent width #+begin_src emacs-lisp (setq split-height-threshold nil window-min-width 100) #+end_src Set fill column to 80 #+begin_src emacs-lisp (setq-default fill-column 80) #+end_src Kill whole lines instead of clearing them #+begin_src emacs-lisp (setq kill-whole-line t) #+end_src * Interface ** Easy edit config file Disabled because the configuration is handled by Nix using [[https://github.com/nix-community/emacs-overlay][emacs-overlay]] #+begin_src emacs-lisp :tangle no (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 Enable pixel scrolling. #+begin_src emacs-lisp (setq pixel-scroll-precision-mode t) #+end_src I like the [[https://draculatheme.com][dracula theme]] #+begin_src emacs-lisp :tangle no (use-package dracula-theme :init (load-theme 'dracula :no-confirm)) #+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-spectrum :no-confirm)) #+end_src So is [[https://github.com/catppuccin/emacs][Catppuccin]] #+begin_src emacs-lisp (use-package catppuccin-theme :init (load-theme 'catppuccin :no-confirm)) #+end_src Set fonts. #+begin_src emacs-lisp (defun my/set-font-size (&optional frame) (let* ((frame (or frame (selected-frame))) (geometry (frame-monitor-attribute 'geometry frame)) (mm-size (frame-monitor-attribute 'mm-size frame)) (width-px (caddr geometry)) (width-mm (car mm-size)) (width-in (/ width-mm 25.4)) (display-dpi (/ width-px width-in)) (font-height (cond ((< display-dpi 110) 120) ((< display-dpi 130) 140) ((< display-dpi 160) 160) (t 160)))) (set-face-attribute 'default frame :height font-height))) (add-hook 'server-after-make-frame-hook 'my/set-font-size) (add-hook 'after-make-frame-functions 'my/set-font-size) (defun my/setup-fonts () (set-face-attribute 'default nil :family my/default-font :weight 'light) (set-face-attribute 'font-lock-comment-face nil :font my/comment-font) (set-face-attribute 'variable-pitch nil :font my/variable-width-font :height my/variable-width-font-height)) (add-hook 'after-init-hook 'my/setup-fonts) #+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 '("<---" "<--" "<<-" "<-" "->" "-->" "--->" "<->" "<-->" "<--->" "<---->" "" "<~" "<~~" "~>" "~~>" "<~>" ; ss04 "" "" "/\\" "\\/" ; ss05 "|>" "<|" ; ss06 "##" "###" ; ss07 "***" "/*" "*/" "/*/" "(*" "*)" "(*)" ; ss08 ".=" ".-" "..<" ; dlig & calt "" ":<" "!!" ">:" "<:" "#=" "?:" "?." "??" ";;" "///" ":::" "..." "=!=" "=:=" "..=" "..-")) ;; Enables ligature checks globally in all buffers. You can also do it ;; per mode with `ligature-mode'. (global-ligature-mode t)) #+end_src Add a dashboard on startup #+begin_src emacs-lisp (use-package dashboard :after all-the-icons :hook (dashboard-mode . (lambda () (setq show-trailing-whitespace nil))) :config (setq dashboard-set-navigator t dashboard-center-content t dashboard-set-file-icons t dashboard-set-heading-icons t dashboard-set-init-info t dashboard-image-banner-max-height 250 dashboard-banner-logo-title "Bûter, brea en griene tsiis, wa dat net sizze kin is gjin oprjochte Fries." dashboard-startup-banner (concat user-emacs-directory "images/ue-colorful.png") dashboard-footer-icon (all-the-icons-octicon "dashboard" :height 1.1 :v-adjust -0.05 :face 'font-lock-keyword-face)) (setq dashboard-navigator-buttons `(((,(all-the-icons-octicon "search" :height 0.9 :v-adjust -0.1) " Find file" nil (lambda (&rest _) (find-file)) nil "" " C-x C-f")) ((,(all-the-icons-octicon "file-directory" :height 1.0 :v-adjust -0.1) " Open project" nil (lambda (&rest _) (projectile-switch-project)) nil "" " C-c p p")) ((,(all-the-icons-octicon "three-bars" :height 1.1 :v-adjust -0.1) " File explorer" nil (lambda (&rest _) (projectile-dired)) nil "" " C-c p D")) ((,(all-the-icons-octicon "settings" :height 0.9 :v-adjust -0.1) " Open settings" nil (lambda (&rest _) (find-config)) nil "" " C-c C ")))) (setq dashboard-projects-backend 'projectile dashboard-items '((recents . 5) (projects . 5) (registers . 5))) (dashboard-setup-startup-hook) ;; Also show dashboard on new emacsclient window (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))) :custom-face (dashboard-heading ((t (:weight bold))))) #+end_src ** Modeline [[https://github.com/myrjola/diminish.el][diminish]] hides modes from the modeline #+begin_src emacs-lisp (use-package diminish) #+end_src [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] provides ALL the icons. Run `all-the-icons-install-fonts` after installing to download the actual font. #+begin_src emacs-lisp (use-package all-the-icons :if (display-graphic-p)) #+end_src [[https://github.com/tarsius/minions][minions]] adds a menu for minor modes to the modeline #+begin_src emacs-lisp (use-package minions :config (minions-mode 1)) #+end_src Use [[https://github.com/seagle0128/doom-modeline][doom-modeline]] for a nice and fancy modeline *2023-05-12* Disabled because it causes emacs to hang #+begin_src emacs-lisp :tangle no (use-package doom-modeline :init (doom-modeline-mode 1) :config (setq doom-modeline-height 25 doom-modeline-bar-width 6 doom-modeline-lsp t doom-modeline-github nil doom-modeline-mu4e nil doom-modeline-irc nil doom-modeline-minor-modes t doom-modeline-persp-name nil doom-modeline-buffer-file-name-style 'truncate-except-project doom-modeline-icon t) :custom-face (mode-line ((t (:height 0.85)))) (mode-line-inactive ((t (:height 0.85))))) #+end_src [[https://github.com/tarsius/moody][moody]] cleans up the modeline a bit so it is nicer to look at #+begin_src emacs-lisp (use-package moody :config (setq x-underline-at-descent-line t) (moody-replace-mode-line-buffer-identification) (moody-replace-vc-mode) (moody-replace-eldoc-minibuffer-message-function)) #+end_src ** Command completion *** Ivy / Counsel *Disabled in favor of [[https://github.com/minad/vertico][vertico]]* Ivy is a generic completion framework which uses the minibuffer. [[https://github.com/abo-abo/swiper][ivy GitHub page]] #+begin_src emacs-lisp :tangle no (use-package ivy :diminish t :bind <> :config <> ) #+end_src Ivy config #+name: ivy-config #+begin_src emacs-lisp :tangle no (ivy-mode t) (setq ivy-initial-inputs-alist nil ivy-use-virtual-buffers nil) (define-key read-expression-map (kbd "C-r") 'counsel-expression-history) #+end_src Ivy keybindings #+name: ivy-binds #+begin_src emacs-lisp :tangle no ("C-x s" . swiper) ("C-x C-r" . ivy-resume) #+end_src Counsel enhances emacs commands with ivy. [[https://github.com/abo-abo/swiper][counsel GitHub page]] #+begin_src emacs-lisp :tangle no (use-package counsel :bind ("M-x" . counsel-M-x) ("C-x C-m" . counsel-M-x) ("C-x C-f" . counsel-find-file) ("C-x c k" . counsel-yank-pop)) #+end_src Company provides completion in the main buffer. [[https://company-mode.github.io/][company website]] #+begin_src emacs-lisp :tangle no (use-package company :diminish t :hook (after-init . global-company-mode)) #+end_src Hydra allows you to group commands behind a custom prefix. [[https://github.com/abo-abo/hydra][hydra GitHub page]] #+begin_src emacs-lisp :tangle no (use-package ivy-hydra) #+end_src =major-mode-hydra= binds a single key to open a context sensitive hydra based on the current major mode. Hydras can be defined in =use-package= definitions via the =:mode-hydra= integration. #+begin_src emacs-lisp :tangle no (use-package major-mode-hydra :bind ("C-M-SPC" . major-mode-hydra) :config (major-mode-hydra-define org-mode () ("Tools" (("l" org-lint "lint"))))) #+end_src *** Vertico / Corfu [[https://github.com/minad/vertico][vertico]] is a minimalistic completion UI based on the default completion system integrated in emacs. #+begin_src emacs-lisp (use-package vertico :custom (vertico-cycle t) :init (vertico-mode)) #+end_src [[https://github.com/minad/marginalia][marginalia]] adds extra information to the minibuffer completions. #+begin_src emacs-lisp (use-package marginalia :after vertico :custom (marginalia-annotaters '(marginalia-annotaters-heavy marginalia-annotaters-light nil)) :init (marginalia-mode)) #+end_src [[https://github.com/minad/consult][consult]] is an alternative to counsel for ivy, but for vertico. #+begin_src emacs-lisp (use-package consult :general ("C-s" 'consult-line) ("C-x c k" 'consult-yank-pop) :config (setq consult-project-function (lambda () (when (fboundp 'projectile-project-root) (projectile-project-root))))) #+end_src [[https://github.com/minad/corfu][corfu]] enhances the completion at point function popups #+begin_src emacs-lisp (use-package corfu ;; :bind ;; ("TAB" . corfu-insert) :custom (corfu-cycle t) (corfu-auto t) :init (global-corfu-mode)) #+end_src [[https://github.com/oantolin/orderless][orderless]] is a flexible completion style that allows flexible matching using literal strings, regex and more. #+begin_src emacs-lisp (use-package orderless :init (setq completion-styles '(orderless partial-completion basic) ;; completion-category-defaults nil completion-category-overrides '((file (styles basic partial-completion))))) #+end_src Use =corfu= with =dabbrev= (included with emacs) #+begin_src emacs-lisp (use-package dabbrev :ensure nil :general ("M-/" 'dabbrev-completion) ("C-M-/" 'dabbrev-expand) :custom (dabbrev-ignored-buffer-regexps '("\\.\\(?:pdf\\|jpe?g\\|png\\)\\'"))) #+end_src *** Misc which-key shows suggestions when you type an incomplete command and wait for a bit (1 second by default). [[https://github.com/justbur/emacs-which-key][which-key GitHub page]] #+begin_src emacs-lisp (use-package which-key :defer 0 :diminish which-key-mode :config (which-key-mode) (setq which-key-idle-delay 1)) #+end_src *** Possible candidates The following packages look interesting, and are worth investigating further: - [[https://github.com/abo-abo/avy][avy]]: quick text navigation - [[https://github.com/abo-abo/ace-window][ace-window]]: window navigation - [[https://github.com/magnars/expang-region.el][expand-region]]: context aware text selection - [[https://github.com/minad/cape][cape]]: A set of completion at point extensions ** File tree [[https://github.com/Alexander-Miller/treemacs][treemacs]] is Emacs' equivalent of neovim's NeoTree or vim's NERDTree. #+begin_src emacs-lisp (use-package treemacs :defer t :init (with-eval-after-load 'winum (define-key winum-keymap (kbd "M-0") #'treemacs-select-window)) :general ("M-0" 'treemacs-select-window) (:prefix "C-c" "t 1" 'treemacs-delete-other-windows "t t" 'treemacs "t d" 'treemacs-select-directory "t B" 'treemacs-bookmark "t C-t" 'treemacs-find-file "t M-t" 'treemacs-find-tag) :config (progn (treemacs-project-follow-mode))) #+end_src Enable integration with =projectile= #+begin_src emacs-lisp (use-package treemacs-projectile :after (treemacs projectile)) #+end_src Add icons #+begin_src emacs-lisp (use-package treemacs-icons-dired :hook (dired-mode . treemacs-icons-dired-enable-once)) #+end_src Enable =magit= integration #+begin_src emacs-lisp (use-package treemacs-magit :after (treemacs magit)) #+end_src * Programming ** General settings Auto balance parenthesese. *Note:* Disabled in favour of =electric-pair-mode= #+begin_src emacs-lisp :tangle no (use-package smartparens :ghook 'prog-mode-hook) #+end_src Rainbow delimiters FTW! #+begin_src emacs-lisp (use-package rainbow-delimiters :ghook 'prog-mode-hook) #+END_src Paredit is a minor mode for editing parentheses #+begin_src emacs-lisp (use-package paredit :hook (emacs-lisp-mode . paredit-mode) (lisp-mode . paredit-mode) (racket-mode . paredit-mode) (racket-repl-mode . paredit-mode) :general (:keymaps 'paredit-mode-map "{" 'paredit-open-curly "}" 'paredit-close-curly "M-[" 'paredit-wrap-square "M-{" 'paredit-wrap-curly)) #+end_src ** Project management Projectile works great to manage projects. It includes fuzzy search and supports jumping between files (e.g. .h <-> .cpp). [[https://github.com/bbatsov/projectile][projectile GitHub page]] #+begin_src emacs-lisp (use-package projectile :diminish projectile-mode :config ;; (setq projectile-completion-system 'ivy) (projectile-mode) :general (:prefix "C-c" "p" '(:keymap projectile-command-map)) :init (setq projectile-switch-project-action #'projectile-dired projectile-project-search-path '("~/workspace" "~/workspace/horus" "~/workspace/horus/web" "~/workspace/horus/horus-vr"))) #+end_src There is also an integration with counsel. #+begin_src emacs-lisp :tangle no (use-package counsel-projectile :after projectile :general ("C-SPC" 'counsel-projectile-switch-project) :config (counsel-projectile-mode)) #+end_src Enable [[https://github.com/BurntSushi/ripgrep][ripgrep]] support #+begin_src emacs-lisp (use-package rg :after projectile :config (rg-enable-default-bindings)) #+end_src ** TRAMP Set some connection properties #+begin_src emacs-lisp (with-eval-after-load "tramp" (add-to-list 'tramp-connection-properties (list (regexp-quote "/sshx:hass:") "remote-shell" "/bin/bash"))) #+end_src ** Git Magit. Obviously. [[https://magit.vc][magit website]] #+begin_src emacs-lisp (use-package magit :config ;; (setq magit-completing-read-function 'ivy-completing-read) :general (:prefix my/leader "g s" 'magit-status "g x" 'magit-checkout "g c" 'magit-commit "g p" 'magit-push "g u" 'magit-pull "g e" 'magit-ediff-resolve "g r" 'magit-rebase-interactive "g i" 'magit-init)) #+end_src transient can be used to create command dispatchers. Magit uses it to easily add options to git commands, and it displays a nice popup with the possible flags and commands. [[https://magit.vc/manual/transient/][transient manual]] #+begin_src emacs-lisp (use-package transient) #+end_src Visualise git changes in the gutter, next to the line numbers [[https://github.com/emacsorphanage/git-gutter][git-gutter GitHub page]] #+begin_src emacs-lisp (use-package git-gutter :diminish t :ghook 'prog-mode-hook 'org-mode-hook :config (setq git-gutter:update-interval 0.02)) (use-package git-gutter-fringe :config (define-fringe-bitmap 'git-gutter-fr:added [224] nil nil '(center repeated)) (define-fringe-bitmap 'git-gutter-fr:modified [224] nil nil '(center repeated)) (define-fringe-bitmap 'git-gutter-fr:deleted [128 192 224 240] nil nil 'bottom)) #+end_src Show inline git-blame with [[https://github.com/Artawower/blamer.el][blamer.el]]. Inspired by VS Code's Git Lens #+begin_src emacs-lisp (use-package blamer :after bind-key :bind (("C-c C-i" . blamer-show-commit-info) ("C-c i" . blamer-show-posframe-commit-info)) :config (global-blamer-mode 1)) #+end_src ** Syntax checking and highlighting *** Flycheck =Flycheck= is a general purpose syntax highlighting framework that provides hooks for other packages and an improvement of the builtin =flymake=. [[https://www.flycheck.org][website]] #+begin_src emacs-lisp (use-package flycheck :diminish t :init (global-flycheck-mode)) #+end_src Add eglot support for flycheck-mode #+begin_src emacs-lisp (use-package flycheck-eglot :after (flycheck eglot) :config (global-flycheck-eglot-mode 1)) #+end_src *** Tree-sitter [[https://tree-sitter.github.io/][tree-sitter]] is a new development in parsing and syntax highlighting. It has been merged into Emacs 29, but until that's released we're using the [[https://emacs-tree-sitter.github.io/][emacs-tree-sitter]] package while on Emacs 28. #+begin_src emacs-lisp (when (< emacs-major-version 29) (use-package tree-sitter :config (global-tree-sitter-mode) :ghook ('tree-sitter-after-on-hook #'tree-sitter-hl-mode))) #+end_src [[https://github.com/emacs-tree-sitter/tree-sitter-langs][tree-sitter-langs]] provides =tree-sitter= support for a bunch of languages. #+begin_src emacs-lisp (use-package tree-sitter-langs :after tree-sitter) #+end_src Automatically use the =-ts-mode= when it is available #+begin_src emacs-lisp (use-package treesit-auto :config (setq treesit-auto-install 'prompt) (global-treesit-auto-mode)) #+end_src *** eglot [[https://joaotavora.github.io/eglot/][eglot]] is an alternative to =lsp-mode= that is builtin with emacs >= 29 #+begin_src emacs-lisp (use-package eglot :config (fset #'json--log-event #'ignore) ;; Performance boost by not logging every event (add-to-list 'eglot-server-programs '((toml-mode toml-ts-mode conf-toml-mode) . ("taplo" "lsp" "stdio"))) ;; (add-to-list 'eglot-server-programs ;; `((elixir-mode elixir-ts-mode heex-ts-mode) . ;; ,(eglot-alternatives '(("nextls" "--stdio=true" ;; :initializationOptions (:experimental (:completions (:enable t)))) ;; "elixir-ls")))) ;; (add-to-list 'eglot-server-programs `((elixir-ts-mode heex-ts-mode) . ,(eglot-alternatives '("lexical" "elixir-ls")))) (add-to-list 'eglot-server-programs '(dhall-mode . ("dhall-lsp-server"))) (add-to-list 'eglot-stay-out-of 'flymake) (setq eglot-autoshutdown t eldoc-echo-area-use-multiline-p 0.1) :hook (eglot-managed-mode . (lambda () (eglot-inlay-hints-mode 1) (define-key eglot-mode-map (kbd "C-c l a") 'eglot-code-actions) (define-key eglot-mode-map (kbd "C-c l f") 'eglot-format) (define-key eglot-mode-map (kbd "C-c l h") 'eldoc) (define-key eglot-mode-map (kbd "C-c l i") 'eglot-find-implementation) (define-key eglot-mode-map (kbd "C-c l r") 'eglot-rename) (define-key eglot-mode-map (kbd "C-c l t") 'eglot-find-typeDefinition) (define-key eglot-mode-map (kbd "C-c l w d") 'eglot-list-connections) (define-key eglot-mode-map (kbd "C-c l w r") 'eglot-reconnect) (define-key eglot-mode-map (kbd "C-c l w q") 'eglot-shutdown) (define-key eglot-mode-map (kbd "C-c l y") 'eglot-inlay-hints-mode)))) #+end_src [[https://github.com/nemethf/eglot-x][eglot-x]] adds support for some LSP extensions to =eglot= #+begin_src emacs-lisp :tangle no (use-package eglot-x :vc (:fetcher github :repo nemethf/eglot-x) :after eglot :config (eglot-x-setup)) #+end_src [[https://github.com/mohkale/consult-eglot][consult-eglot]] adds an integration between =consult= and =eglot= #+begin_src emacs-lisp (use-package consult-eglot) #+end_src ** Snippets Snippets are predefined pieces of code that can be inserted and filled in. [[https://github.com/joaotavora/yasnippet][YASnippet]] uses syntax inspired by TextMate is the most popular, for good reason. #+begin_src emacs-lisp (use-package yasnippet :init (load "yasnippet.el") :diminish t :general (:keymaps 'yas-minor-mode-map "" nil "TAB" nil "" 'yas-expand) :config (add-to-list 'yas-snippet-dirs my/snippets-dir) (yas-global-mode)) (use-package yasnippet-snippets) #+end_src ** Languages *** JavaScript / TypeScript Indent 2 spaces #+begin_src emacs-lisp (setq-default js-indent-level 2 typescript-indent-level 2) #+end_src [[https://github.com/mooz/js2-mode/][js2-mode]] improves a lot on the builtin =js-mode= #+begin_src emacs-lisp (use-package js2-mode :after eglot :mode ("\\.mjs\\'" . js2-mode) ("\\.jsx?\\'" . js2-jsx-mode) :hook (js2-mode . eglot-ensure) (js2-jsx-mode . eglot-ensure)) #+end_src Prettier has my preference for formatting JavaScript and TypeScript #+begin_src emacs-lisp (use-package prettier :config (setq prettier-enabled-parsers ' (css html json markdown scss svelte toml typescript vue))) #+end_src TypeScript stuff #+begin_src emacs-lisp (use-package typescript-mode :after eglot :mode ("\\.tsx?\\'" . typescript-mode) :hook (typescript-mode . eglot-ensure)) #+end_src Prefer local packages from =node_modules= to global ones #+begin_src emacs-lisp (use-package add-node-modules-path) #+end_src *** Web mode [[https://web-mode.org/][web-mode]] handles HTML/CSS and JavaScript #+begin_src emacs-lisp (use-package web-mode :mode "\\.svelte\\'" :after eglot :config (setq web-mode-markup-indent-offset 2 web-mode-css-indent-offset 2 web-mode-code-indent-offset 2 web-mode-enable-auto-pairing t web-mode-enable-css-colorization t web-mode-enable-current-element-highlight t web-mode-enable-current-column-highlight t) (add-to-list 'web-mode-engines-alist '(("elixir" . "\\.html.heex\\'") ("jinja2" . "\\.jinja2\\'") ("python" . "\\.pt\\'") ("svelte" . "\\.svelte\\'"))) :hook ((html-mode css-mode web-mode) . eglot-ensure)) #+end_src *** Markdown [[https://jblevins.org/projects/markdown-mode/][markdown-mode]] adds support for Markdown editing. =gfm-mode= supports GitHub Flavoured Markdown. #+begin_src emacs-lisp (use-package markdown-mode :after eglot :mode (("README\\.md\\'" . gfm-mode) ("\\.md\\'" . markdown-mode) ("\\.markdown\\'" . markdown-mode)) :init (setq markdown-command "multimarkdown") :hook (markdown-mode . display-fill-column-indicator-mode) (markdown-mode . eglot-ensure)) #+end_src [[https://github.com/skeeto/impatient-mode][impatient-mode]] live renders HTML, but it can be made to work with Markdown with a custom filter #+begin_src emacs-lisp (use-package impatient-mode :after markdown-mode :config (imp-set-user-filter 'markdown-filter)) (defun markdown-filter (buffer) (princ (with-temp-buffer (let ((tmpname (buffer-name))) (set-buffer buffer) (set-buffer (markdown tmpname)) ; the function markdown is in `markdown-mode.el' (buffer-string))) (current-buffer))) #+end_src *** Elixir Add support for Elixir with [[https://github.com/elixir-editors/emacs-elixir][elixir-mode]]. The =elixir-format= hook sets up the correct formatter configuration when in a =projectile= project. #+begin_src emacs-lisp :tangle no (use-package elixir-mode :after eglot :hook ((elixir-mode . eglot-ensure)) :config (add-to-list 'auto-mode-alist '("\\.[hl]eex\\'" . elixir-mode))) #+end_src #+begin_src emacs-lisp (use-package elixir-ts-mode :after eglot :hook ((elixir-ts-mode . eglot-ensure))) #+end_src Add a [[https://github.com/ayrat555/mix.el][mix]] minor mode to call =mix= tasks from emacs. #+begin_src emacs-lisp (use-package mix :hook (elixir-mode . mix-minor-mode)) #+end_src *** Erlang #+begin_src emacs-lisp (use-package erlang :mode ("\\.P\\'" . erlang-mode) ("\\.E\\'" . erlang-mode) ("\\.S\\'" . erlang-mode) :config (require 'erlang-start)) #+end_src *** Rust Rust support with [[https://github.com/rust-lang/rust-mode][rust-mode]]. #+begin_src emacs-lisp (use-package rust-mode :after eglot :hook (rust-mode . eglot-ensure) (rust-ts-mode . eglot-ensure) (before-save . eglot-format-buffer) ;; :init ;; (setq lsp-rust-analyzer-cargo-watch-command "clippy" ;; lsp-rust-analyzer-server-display-inlay-hints t ;; lsp-rust-analyzer-binding-mode-hints t ;; lsp-rust-analyzer-display-lifetime-elision-hints-enable "always") ) #+end_src Configure [[https://rust-analyzer.github.io][rust-analyzer]] #+begin_src emacs-lisp (defun eb/ra-eglot-config (server) "initializationOptions for rust-analyzer" `(:diagnostics (:enable t) :imports (:granularity (:enforce :json-false :group "crate") :group t :merge (:glob t) :preferPrelude t :prefix "plain") :checkOnSave (:enable t :command "clippy" :allTargets t) :inlayHints (:bindingModeHints t :chainingHints t :lifetimeElisionHints (:enable "always" :useParameterNames t) :maxLength nil :typeHints (:enable t :hideClosureInitialization :json-false :hideNamedConstructor :json-false)) :procMacro (:enable t))) (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs '(rust-mode . ("rust-analyzer" :initializationOptions eb/ra-eglot-config)))) #+end_src Add [[https://github.com/kwrooijen/cargo.el][cargo]] support #+begin_src emacs-lisp (use-package cargo :hook (rust-mode . cargo-minor-mode)) #+end_src Flycheck support for Rust with [[https://github.com/flycheck/flycheck-rust][flycheck-rust]] #+begin_src emacs-lisp (use-package flycheck-rust :hook (flycheck-mode . flycheck-rust-setup)) #+end_src *** TOML Support for TOML files with [[https://github.com/dryman/toml-mode.el][toml-mode]] #+begin_src emacs-lisp :tangle no (use-package toml-mode :mode ("\\.toml\\'" . conf-toml-mode)) #+end_src *** Docker Add docker support with [[https://github.com/spotify/dockerfile-mode][dockerfile-mode]] #+begin_src emacs-lisp (use-package dockerfile-mode :after eglot :hook (dockerfile-mode . eglot-ensure)) #+end_src *** Bitbake / Yocto #+begin_src emacs-lisp :tangle no (use-package bitbake-modes :straight (:type git :repo "https://bitbucket.org/olanilsson/bitbake-modes.git")) #+end_src *** INI =ini-mode= provides highlighting for ini files #+begin_src emacs-lisp (use-package ini-mode) #+end_src *** JSON [[https://github.com/joshwnj/json-mode][json-mode]] extends the builtin =js-mode= with better syntax highlighting for JSON and adds some editing keybindings #+begin_src emacs-lisp (use-package json-mode :after eglot :hook (json-mode . eglot-ensure)) #+end_src *** CMake Add [[https://melpa.org/#/cmake-mode][cmake-mode]] #+begin_src emacs-lisp (use-package cmake-mode :after eglot :hook (cmake-mode . eglot-ensure)) #+end_src *** YAML Use [[https://github.com/yoshiki/yaml-mode][yaml-mode]] to handle YAML files #+begin_src emacs-lisp (use-package yaml-mode :after eglot :hook (yaml-mode . eglot-ensure)) #+end_src *** C/C++ Enable clangd LSP for C and C++ #+begin_src emacs-lisp (use-package cc-mode :ensure nil :after eglot :hook ((c-mode c-ts-mode) . eglot-ensure) ((c++-mode c++-ts-mode) . eglot-ensure)) #+end_src Add some flags to clangd #+begin_src emacs-lisp (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs '((c-mode c-ts-mode c++-mode c++-ts-mode) . ("clangd" "--malloc-trim" "--log=error" "--clang-tidy" "--completion-style=detailed")))) #+end_src Add QML mode #+begin_src emacs-lisp (use-package qml-mode :mode "\\.qml\\'") #+end_src Enable and configure =auto-insert-mode= for Horus projects #+begin_src emacs-lisp (defconst my/generate-cpp-file-executable "~/workspace/horus/development/code-generation/generate-cpp-file.py" "Python program to generate a C++ boilerplate") (when (file-executable-p my/generate-cpp-file-executable) (define-auto-insert "\\.[ch]pp\\'" (lambda nil (call-process my/generate-cpp-file-executable nil t nil buffer-file-name)))) (auto-insert-mode) #+end_src *** Meson [[https://mesonbuild.com][meson]] is a build system designed to be as fast and as user-friendly as possible. #+begin_src emacs-lisp (use-package meson-mode) #+end_src *** nix Add [[https://github.com/NixOS/nix-mode][nix-mode]] #+begin_src emacs-lisp (use-package nix-mode :after eglot :mode "\\.nix\\'" :hook (nix-mode . eglot-ensure)) #+end_src Tell =nil= to use =nixfmt= for formatting nix files. #+begin_src emacs-lisp (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs `(nix-mode . ("nil" :initializationOptions (:formatting (:command ["nixfmt"])))))) #+end_src *** Common Lisp Common Lisp does not use =lsp-mode=, but has it's own environment: [[https://github.com/slime/slime][SLIME]] or Superior Lisp Interaction Mode for Emacs. #+begin_src emacs-lisp :tangle no (use-package slime :init (setq slime-lisp-implementations '((sbcl ("sbcl" "--load ~/.quicklisp/setup.lisp") :coding-system utf-8-unix)) slime-default-lisp 'sbcl) :config (slime-setup '(slime-fancy slime-quicklisp slime-asdf)) :mode (("\\.cl\\'" . lisp-mode)) :hook (lisp-mode . (lambda () (slime-mode t))) (inferior-lisp-mode . (lambda () (inferior-slime-mode t)))) #+end_src [[https://github.com/joaotavora/sly][SLY]] is a fork of SLIME, by the same author as =eglot=, with improved UX #+begin_src emacs-lisp (use-package sly :mode (("\\.cl\\'" . lisp-mode)) :config (setq sly-lisp-implementations '((sbcl ("sbcl") :coding-system utf-8-unix)) sly-default-lisp 'sbcl)) #+end_src *** Clojure Similar to =lisp=, there is [[https://github.com/clojure-emacs/cider][CIDER]] (Clojure Interactive Development Environment that Rocks) for Clojure(Script) #+begin_src emacs-lisp (use-package cider) #+end_src *** Terraform [[https://github.com/emacsorphanage/terraform-mode][terraform-mode]] is a major mode for Terraform files #+begin_src emacs-lisp (use-package terraform-mode :after eglot :hook (terraform-mode . eglot-ensure)) #+end_src Register =terraform-ls= with eglot #+begin_src emacs-lisp (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs '(terraform-mode . ("terraform-ls" "serve")))) #+end_src *** Fish [[https://github.com/wwwjfy/emacs-fish][fish-mode]] provides a major mode for fish shell scripts #+begin_src emacs-lisp (use-package fish-mode :init (setq fish-enable-auto-indent t)) #+end_src *** Zig [[https://github.com/ziglang/zig-mode][zig-mode]] provides a major mode for the [[https://ziglang.org][zig programming language]] #+begin_src emacs-lisp (use-package zig-mode :after eglot :hook (zig-mode . eglot-ensure)) #+end_src *** Yuck #+begin_src emacs-lisp (use-package yuck-mode) #+end_src *** Racket #+begin_src emacs-lisp (use-package racket-mode :after eglot :hook (racket-mode . eglot-ensure)) #+end_src *** Ruby Let =ruby-mode= handle Ruby files #+begin_src emacs-lisp (use-package ruby-mode :ensure nil :after eglot :hook (ruby-mode . eglot-ensure)) #+end_src *** MapServer Add highlighting for MapServer .map files #+begin_src emacs-lisp :tangle no (use-package mapserver-mode :straight (:type git :host github :repo "AxxL/mapserver-emacs-mode") :mode ("\\.map\\'" . mapserver-mode)) #+end_src *** Go It's better than nothing. #+begin_src emacs-lisp (use-package go-mode :after eglot :hook (go-mode . eglot-ensure)) #+end_src *** Cucumber [[https://github.com/michaelklishin/cucumber.el][feature-mode]] provides support for user stories written in Cucumber/Gherkin format. #+begin_src emacs-lisp (use-package feature-mode :config (setq feature-use-docker-compose nil) :mode "\\.feature\\'") #+end_src *** Protobuf [[https://github.com/protocolbuffers/protobuf/blob/main/editors/protobuf-mode.el][protobuf-mode]] #+begin_src emacs-lisp (use-package protobuf-mode) #+end_src *** Just [[https://github.com/leon-barrett/just-mode.el][just-mode]] provides syntax highlighting for Justfiles, for the command runner [[https://github.com/casey/just][just]] #+begin_src emacs-lisp (use-package just-mode) #+end_src *** Python Python #+begin_src emacs-lisp :tangle no (use-package python-mode :hook ((python-mode python-ts-mode) . eglot-ensure)) #+end_src #+begin_src emacs-lisp (use-package python :ensure nil :hook (python-base-mode . eglot-ensure) :init (setq python-indent-guess-indent-offset nil)) #+end_src jinja2 #+begin_src emacs-lisp (use-package jinja2-mode) #+end_src *** Haskell [[https://github.com/haskell/haskell-mode][haskell-mode]] for Haskell files #+begin_src emacs-lisp (use-package haskell-mode :hook (((haskell-mode haskell-ts-mode) . eglot-ensure) turn-on-haskell-unicode-input-method)) #+end_src *** Dhall Dhall is a programmable configuration language that you can think of as: JSON + functions + types + imports #+begin_src emacs-lisp (use-package dhall-mode :mode "\\.dhall\\'") #+end_src *** nushell [[http://www.nushell.sh/][nushell]] is a new type of shell that operates on typed data #+begin_src emacs-lisp (use-package nushell-ts-mode) #+end_src Register =nushell= LSP with eglot #+begin_src emacs-lisp (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs '(nushell-ts-mode . ("nu" "--lsp")))) #+end_src *** Lua [[https://github.com/immerr/lua-mode][lua-mode]] for Lua support. #+begin_src emacs-lisp (use-package lua-mode) #+end_src *** Svelte #+begin_src emacs-lisp (use-package svelte-mode) #+end_src Use =svelteserver= as LSP #+begin_src emacs-lisp (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs '(svelte-mode . ("svelteserver" "--stdio")))) #+end_src * Org ** Main org setup [[https://orgmode.org][org-mode]] configuration #+begin_src emacs-lisp (use-package org :general <> :custom <> :hook <> :config <>) #+end_src Some keybindings for often used functions #+name: org-binds #+begin_src emacs-lisp :tangle no (:prefix my/leader "r" 'org-capture "a" 'org-agenda "s" 'org-store-link "L" 'org-store-link-global "O" 'org-open-at-point-global) #+end_src Customisations #+name: org-customisations #+begin_src emacs-lisp :tangle no (org-directory "~/org") (org-log-done t) (org-indent-indentation-per-level 2) (org-startup-indented t) (org-log-into-drawer t) (org-default-notes-file (expand-file-name "notes.org" org-directory)) (org-return-follows-link t) (org-pretty-entities t) (org-hide-emphasis-markers t) (org-startup-with-inline-images t) (org-startup-with-latex-previews t) (org-image-actual-width '(300)) #+end_src Hooks #+name: org-hooks #+begin_src emacs-lisp :tangle no ((org-mode . org-indent-mode) (org-mode . turn-on-visual-line-mode) (org-mode . variable-pitch-mode) (org-mode-indent . (lambda () (diminish 'org-indent-mode))) (org-agenda-mode . (lambda () (hl-line-mode 1)))) #+end_src Configuration #+name: org-config #+begin_src emacs-lisp :tangle no (add-to-list 'auto-mode-alist '("\\.org\\'" . org-mode)) (dolist (face '((org-level-1 . 1.2) (org-level-2 . 1.1) (org-level-3 . 1.05) (org-level-4 . 1.0) (org-level-5 . 1.0) (org-level-6 . 1.0) (org-level-7 . 1.0) (org-level-8 . 1.0))) (set-face-attribute (car face) nil :family my/variable-width-font :weight 'medium :height (cdr face))) (set-face-attribute 'org-document-title nil :family my/variable-width-font :weight 'bold :height 1.3) (set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch) (set-face-attribute 'org-table nil :inherit 'fixed-pitch) (set-face-attribute 'org-formula nil :inherit 'fixed-pitch) (set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch)) (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch)) (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch)) (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch) #+end_src =org-capture= allows creating and saving quick notes, links and TODOs #+begin_src emacs-lisp (use-package org-capture ;; :straight org :ensure org :after org :config (setq org-capture-templates '(("t" "Todo" entry (file+headline "gtd.org") "* TODO %?\n %i\n %a") ("n" "Note" entry (file+olp+datetree "notes.org") "* %?\nEntered on %U\n %i\n %a") ("N" "Note (work)" entry (file+olp+datetree "work/notes.org") "* %?\nEntered on %U\n %i\n %a") ("c" "org-protocol-capture" entry (file+headline "inbox.org" "Links") "* %^{Title}\n\n[[%:link][%:description]]\n\n %i" :immediate-finish t)))) #+end_src =org-datetree= allows you to organise captures by date. It is basically a set of headings representing the date, with the first level for the year, the second level for the month and the third for the day. #+begin_src emacs-lisp (use-package org-datetree ;; :straight org :ensure org :after org) #+end_src =org-protocol= can be used to send data to emacs using =org-protocol://= URLs. #+begin_src emacs-lisp (use-package org-protocol ;; :straight org :ensure org :after org :config (setq org-protocol-default-template-key "c")) #+end_src ** org-roam [[https://www.orgroam.com][org-roam]] helps with non-hierarchical note-taking. It uses the [[https://en.wikipedia.org/wiki/Zettelkasten][zettelkasten method]] to capture and organise notes and ideas. #+begin_src emacs-lisp (use-package org-roam ;; :after org :custom (org-roam-directory "~/org-roam") (org-roam-completion-everywhere t) <> :config (require 'org-roam-dailies) (org-roam-db-autosync-mode) :general (:prefix my/leader :keymaps 'global "n f" 'org-roam-node-find "n r" 'org-roam-node-random "n i" 'org-roam-node-insert "n l" 'org-roam-buffer-toggle :keymaps 'org-mode-map "n o" 'org-id-get-create "n t" 'org-roam-tag-add "n a" 'org-roam-alias-add "n l" 'org-roam-buffer-toggle) ("C-M-i" 'completion-at-point :keymaps 'org-roam-dailies-map "Y" 'org-roam-dailies-capture-yesterday "T" 'org-roam-dailies-capture-tomorrow) (:prefix my/leader "n d" '(:keymap org-roam-dailies-map))) #+end_src Org roam capture templates #+name: org-roam-templates #+begin_src emacs-lisp :tangle no (org-roam-capture-templates '(("d" "default" plain "%?" :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+date: %U\n") :unnarrowed t) ("w" "work note" plain "%?" :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: :work:\n") :unnarrowed t) ("p" "project" plain "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n" :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: :project:\n") :unnarrowed t))) #+end_src [[https://github.com/org-roam/org-roam-ui][org-roam-ui]] provides a frontend to explore and interact with =org-roam= notes. It can be started with =M-x org-roam-ui-mode RET=, and is then available on [[http://127.0.0.1:35901/]]. Updates are sent real-time through a WebSocket. The settings provided here are also the defaults, set any to =nil= to disable. #+begin_src emacs-lisp (use-package org-roam-ui :after org-roam :config (setq org-roam-ui-sync-theme t org-roam-ui-follow t org-roam-ui-update-on-save t org-roam-ui-open-on-start t)) #+end_src ** org-chef [[https://github.com/Chobbes/org-chef][org-chef]] is used to manage recipes and import them from supported websites #+begin_src emacs-lisp (use-package org-chef :after org-capture :config (add-to-list 'org-capture-templates '("r" "Recipes")) (add-to-list 'org-capture-templates '("rr" "Recipe" entry (file "~/org/cookbook.org") "%(org-chef-get-recipe-from-url)" :empty-lines 1)) (add-to-list 'org-capture-templates '("rm" "Manual recipe" entry (file "~/org/cookbook.org") "* %^{Recipe title: }\n :PROPERTIES:\n :source-url:\n :servings:\n :prep-time:\n :cook-time:\n :ready-in:\n :END:\n** Ingredients\n %?\n** Directions\n\n"))) #+end_src * Misc ** direnv [[https://github.com/purcell/envrc][envrc]] adds support for [[https://direnv.net][direnv]] to emacs. =envrc= works on a per-buffer basis, so when you open multiple projects the environments don't pollute eachother. NOTE: this should be loaded last. #+name: envrc-init #+begin_src emacs-lisp :tangle no (use-package envrc :init (envrc-global-mode) :general (:prefix my/leader "e" '(:keymap envrc-command-map))) #+end_src ** Writing Enable flyspell #+begin_src emacs-lisp (use-package flyspell :ensure nil :init (setq ispell-list-command "--list") :hook (text-mode . flyspell-mode)) (use-package flyspell-correct :after flyspell :general (:keymaps 'flyspell-mode-map "C-;" 'flyspell-correct-wrapper)) #+end_src Use ivy for flyspell suggestions. Use =M-o= to switch to the actions (e.g. saving a word to your dictionary). #+begin_src emacs-lisp :tangle no (use-package flyspell-correct-ivy :after flyspell-correct) #+end_src ** Load local init file If =local-init.el= exists, load it. #+begin_src emacs-lisp (when (file-readable-p my/local-init-file) (load my/local-init-file)) #+end_src #+begin_comment DO NOT ADD ANYTHING BELOW THIS #+end_comment #+begin_src emacs-lisp :exports none <> #+end_src