58 KiB
Emacs config
This is my emacs configuration. It is structured as an org-mode file, to easily provide context and documentation.
Last export:
- Setup
- Preferences
- Interface
- Programming
- Org
- Misc
Setup
Early init
This ends up in 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)
Enable lexical binding
Setting lexical-binding
to t
can improve startup time. This has to be first!
;; -*- lexical-binding: t; -*-
Personal variables
This sets some variables with my personal preferences for easy customization
(defvar my/default-font "RecMonoLinear Nerd Font")
(defvar my/variable-width-font "Iosevka Aile")
(defvar my/comment-font "RecMonoCasual 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")
And my personal info.
(setq user-full-name "Erwin Boskma"
user-mail-address "erwin@datarift.nl")
Garbage collector
Increasing the garbage collector threshold should also improve startup time. This increases it from 800 kB to 128MB
(setq gc-cons-threshold (* 128 1024 1024))
This resets the threshold back to it's default. This should not be done if lsp-mode
is enabled, it needs the higher threshold.
(add-hook 'after-init-hook
(lambda ()
(setq gc-cons-threshold 800000)
(message "gc-cons-threshold restored to %s"
gc-cons-threshold)))
Increase process output buffer
LSP responses can be rather large, in the 800KiB - 3MiB range. 2MiB is a decent value
(setq read-process-output-max (* 2 1024 1024))
Package sources
Add repositories where packages are installed from.
(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/")))
Bootstrap use-package
If use-package
is not installed, install it.
NOTE: Disabled because it is builtin since emacs 29
(require 'package)
(package-initialize)
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package)
(cl-eval-when 'compile (require 'use-package)))
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
.
(setq use-package-always-ensure t)
general.el
general.el provides a more convenient way for binding keys in emacs. It also integrates with use-package
with the :general
keyword.
(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)))
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 Doom Emacs
(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"))
Custom packages location
Emacs only includes files directly under user-emacs-directory
. I like to keep custom packages in a separate elisp/
subdirectory.
(add-to-list 'load-path (expand-file-name "elisp/" user-emacs-directory))
Record key frequency
Records what keys are used the most, so I can see if I can optimise shortcuts
(use-package keyfreq
:config
(keyfreq-mode 1)
(keyfreq-autosave-mode 1))
Save minibuffer history
(use-package savehist
:ensure nil
:init
(savehist-mode))
Preferences
Don't display the help screen at startup
(setq inhibit-startup-screen t)
Enable line wrapping
(global-visual-line-mode 1)
Disable title bar, toolbar and scroll bar
; 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)
Don't show the warnings buffer for anything lesser than an error.
(setq warning-minimum-level :error)
Keep buffers up-to-date automatically
(global-auto-revert-mode t)
Automatically scroll the compile buffer
(setq compilation-scroll-output 'first-error)
Always display line numbers, and the relative line number
(setq display-line-numbers-type 'relative)
(global-display-line-numbers-mode)
Show matching parenthesese
(show-paren-mode 1)
(setq show-paren-style 'expression)
Surround marked text with (), [] or {}
(electric-pair-mode 1)
Centralise backup files (the filename~
files), so they don't pollute the project.
(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
)
Same for auto save files (the ones with the #
)
(setq auto-save-file-name-transforms '((".*" "~/.local/share/emacs/autosave/" t)))
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
(setq-default indent-tabs-mode nil)
Show trailing whitespace
(setq show-trailing-whitespace t)
Delete trailing whitespace on save.
(add-hook 'before-save-hook 'delete-trailing-whitespace)
Sentences end in one space, not two. We're not using typewriters.
(setq sentence-end-double-space nil)
Don't move files to trash when deleting.
(setq delete-by-moving-to-trash nil)
Restore cursor position when re-opening a file
(save-place-mode t)
Prefer to open frames in a horizontal split and make sure they're of a decent width
(setq split-height-threshold nil
window-min-width 100)
Set fill column to 80
(setq-default fill-column 80)
Kill whole lines instead of clearing them
(setq kill-whole-line t)
Interface
Easy edit config file
Disabled because the configuration is handled by Nix using emacs-overlay
(defun find-config ()
"Edit config.org"
(interactive)
(find-file my/config-file-path))
(general-define-key
:prefix my/leader
"c" 'find-config)
Appearance
Enable pixel scrolling.
(setq pixel-scroll-precision-mode t)
I like the dracula theme
(use-package dracula-theme
:init
(load-theme 'dracula :no-confirm))
Monokai Pro is also pretty.
(use-package monokai-pro-theme
:init
(load-theme 'monokai-pro-spectrum :no-confirm))
So is Catppuccin
(use-package catppuccin-theme
:init
(setq catppuccin-flavor 'mocha)
(load-theme 'catppuccin :no-confirm))
Set fonts.
(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)
Emoji support
(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))
Enable ligatures
(setq iosevka-ligatures '("<---" "<--" "<<-" "<-" "->" "-->" "--->" "<->" "<-->" "<--->" "<---->" "<!--"
"<==" "<===" "<=" "=>" "=>>" "==>" "===>" ">=" "<=>" "<==>" "<===>" "<====>" "<!---"
"<~~" "<~" "~>" "~~>" "::" ":::" "==" "!=" "===" "!=="
":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "+:" "-:" "=:" "<******>" "++" "+++"))
(setq monaspace-ligatures '(
; ss01
"==" "===" "=/=" "!=" "!==" "/=" "/==" "~~" "=~" "!~"
; ss02
">=" "<="
; ss03
"->" "<-" "=>" "<!--" "-->" "<~" "<~~" "~>" "~~>" "<~>"
; ss04
"</" "/>" "</>" "/\\" "\\/"
; ss05
"|>" "<|"
; ss06
"##" "###"
; ss07
"***" "/*" "*/" "/*/" "(*" "*)" "(*)"
; ss08
".=" ".-" "..<"
; dlig & calt
"<!" "**" "::" "=:" "=!" "=/" "--" ".." "//" "&&" "||" ":=" ":>" ":<" "!!" ">:" "<:" "#=" "?:" "?." "??" ";;" "///" ":::" "..." "=!=" "=:=" "..=" "..-"))
(use-package ligature
:config
;; Enable all Iosevka ligatures in programming modes
(ligature-set-ligatures 'prog-mode iosevka-ligatures)
;; Ligatures for Monaspace
;; Enables ligature checks globally in all buffers. You can also do it
;; per mode with `ligature-mode'.
(global-ligature-mode t))
Add a dashboard on startup
(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)))))
Modeline
diminish hides modes from the modeline
(use-package diminish)
all-the-icons provides ALL the icons. Run `all-the-icons-install-fonts` after installing to download the actual font.
(use-package all-the-icons
:if (display-graphic-p))
minions adds a menu for minor modes to the modeline
(use-package minions
:config
(minions-mode 1))
Use doom-modeline for a nice and fancy modeline 2023-05-12 Disabled because it causes emacs to hang
(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)))))
moody cleans up the modeline a bit so it is nicer to look at
(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))
Command completion
Ivy / Counsel
Disabled in favor of vertico
Ivy is a generic completion framework which uses the minibuffer. ivy GitHub page
(use-package ivy
:diminish t
:bind
<<ivy-binds>>
:config
<<ivy-config>>
)
Ivy config
(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)
Ivy keybindings
("C-x s" . swiper)
("C-x C-r" . ivy-resume)
Counsel enhances emacs commands with ivy. counsel GitHub page
(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))
Company provides completion in the main buffer. company website
(use-package company
:diminish t
:hook
(after-init . global-company-mode))
Hydra allows you to group commands behind a custom prefix. hydra GitHub page
(use-package ivy-hydra)
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.
(use-package major-mode-hydra
:bind
("C-M-SPC" . major-mode-hydra)
:config
(major-mode-hydra-define org-mode
()
("Tools"
(("l" org-lint "lint")))))
Vertico / Corfu
vertico is a minimalistic completion UI based on the default completion system integrated in emacs.
(use-package vertico
:custom
(vertico-cycle t)
:init
(vertico-mode))
marginalia adds extra information to the minibuffer completions.
(use-package marginalia
:after vertico
:custom
(marginalia-annotaters '(marginalia-annotaters-heavy marginalia-annotaters-light nil))
:init
(marginalia-mode))
consult is an alternative to counsel for ivy, but for vertico.
(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)))))
corfu enhances the completion at point function popups
(use-package corfu
;; :bind
;; ("TAB" . corfu-insert)
:custom
(corfu-cycle t)
(corfu-auto t)
:init
(global-corfu-mode))
orderless is a flexible completion style that allows flexible matching using literal strings, regex and more.
(use-package orderless
:init
(setq completion-styles '(orderless partial-completion basic)
;; completion-category-defaults nil
completion-category-overrides '((file (styles basic partial-completion)))))
Use corfu
with dabbrev
(included with emacs)
(use-package dabbrev
:ensure nil
:general
("M-/" 'dabbrev-completion)
("C-M-/" 'dabbrev-expand)
:custom
(dabbrev-ignored-buffer-regexps '((rx ".(?:pdf|jpe?g|png)" eos))))
Misc
which-key shows suggestions when you type an incomplete command and wait for a bit (1 second by default). which-key GitHub page
(use-package which-key
:defer 0
:diminish which-key-mode
:config
(which-key-mode)
(setq which-key-idle-delay 1))
Possible candidates
The following packages look interesting, and are worth investigating further:
- avy: quick text navigation
- ace-window: window navigation
- expand-region: context aware text selection
- cape: A set of completion at point extensions
File tree
treemacs is Emacs' equivalent of neovim's NeoTree or vim's NERDTree.
(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)))
Enable integration with projectile
(use-package treemacs-projectile
:after (treemacs projectile))
Add icons
(use-package treemacs-icons-dired
:hook (dired-mode . treemacs-icons-dired-enable-once))
Enable magit
integration
(use-package treemacs-magit
:after (treemacs magit))
Programming
General settings
Auto balance parenthesese. Note: Disabled in favour of electric-pair-mode
(use-package smartparens
:ghook 'prog-mode-hook)
Rainbow delimiters FTW!
(use-package rainbow-delimiters
:ghook 'prog-mode-hook)
Paredit is a minor mode for editing parentheses
(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))
Project management
Projectile works great to manage projects. It includes fuzzy search and supports jumping between files (e.g. .h <-> .cpp). projectile GitHub page
(use-package projectile
:diminish projectile-mode
:config
;; (setq projectile-completion-system 'ivy)
(add-to-list 'project-vc-extra-root-markers ".jj")
(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")))
There is also an integration with counsel.
(use-package counsel-projectile
:after projectile
:general
("C-SPC" 'counsel-projectile-switch-project)
:config
(counsel-projectile-mode))
Enable ripgrep support
(use-package rg
:after projectile
:config
(rg-enable-default-bindings))
TRAMP
Set some connection properties
(with-eval-after-load "tramp" (add-to-list 'tramp-connection-properties
(list (regexp-quote "/sshx:hass:")
"remote-shell" "/bin/bash")))
Ollama
Let's evaluate this puppy.
(use-package gptel
:general
(:prefix my/leader
"m" 'gptel-menu
"C-m" 'gptel-send)
:config
(setq gptel-model "mistral-nemo"
gptel-backend (gptel-make-ollama "Ollama"
:host "100.119.162.110:11434"
:stream t
:models '("mistral" "mistral-nemo")))
(add-to-list 'gptel-directives '(commit . "You are an expert programmer summarizing a git diff.
Reminders about the git diff format:
For every file, there are a few metadata lines, like (for example):
```
diff --git a/lib/index.js b/lib/index.js
index aadf691..bfef603 100644
--- a/lib/index.js
+++ b/lib/index.js
```
This means that `lib/index.js` was modified in this commit. Note that this is only an example.
Then there is a specifier of the lines that were modified.
A line starting with `+` means it was added.
A line that starting with `-` means that line was deleted.
A line that starts with neither `+` nor `-` is code given for context and better understanding.
It is not part of the diff.
After the git diff of the first file, there will be an empty line, and then the git diff of the next file.
Do not include the file name as another part of the comment.
Do not use the characters `[` or `]` in the summary.
Write every summary comment in a new line.
Comments should be in a bullet point list, each line starting with a `-`.
The summary should not include comments copied from the code.
The output should be easily readable. When in doubt, write fewer comments and not more. Do not output comments that
simply repeat the contents of the file.
Readability is top priority. Write only the most important comments about the diff.
EXAMPLE SUMMARY COMMENTS:
```
- Raise the amount of returned recordings from `10` to `100`
- Fix a typo in the github action name
- Move the `octokit` initialization to a separate file
- Add an OpenAI API for completions
- Lower numeric tolerance for test files
- Add 2 tests for the inclusive string split function
```
Most commits will have less comments than this examples list.
The last comment does not include the file names,
because there were more than two relevant files in the hypothetical commit.
Do not include parts of the example in your summary.
It is given only as an example of appropriate comments.
")))
Git
Magit. Obviously. magit website
(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))
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. transient manual
(use-package transient)
Visualise git changes in the gutter, next to the line numbers git-gutter GitHub page
(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))
Show inline git-blame with blamer.el. Inspired by VS Code's Git Lens
(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))
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
.
website
(use-package flycheck
:diminish t
:init
(global-flycheck-mode))
Add eglot support for flycheck-mode
(use-package flycheck-eglot
:after (flycheck eglot)
:config
(global-flycheck-eglot-mode 1))
Tree-sitter
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 emacs-tree-sitter package while on Emacs 28.
(when (< emacs-major-version 29)
(use-package tree-sitter
:config
(global-tree-sitter-mode)
:ghook
('tree-sitter-after-on-hook #'tree-sitter-hl-mode)))
tree-sitter-langs provides tree-sitter
support for a bunch of languages.
(use-package tree-sitter-langs
:after tree-sitter)
Automatically use the <lang>-ts-mode
when it is available
(use-package treesit-auto
:config
(setq treesit-auto-install 'prompt)
(global-treesit-auto-mode))
eglot
eglot is an alternative to lsp-mode
that is builtin with emacs >= 29
(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-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-server-programs
'((html-mode mhtml-mode) . ("superhtml" "lsp")))
(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))))
eglot-x adds support for some LSP extensions to eglot
(use-package eglot-x
:vc (:fetcher github :repo nemethf/eglot-x)
:after eglot
:config
(eglot-x-setup))
consult-eglot adds an integration between consult
and eglot
(use-package consult-eglot)
Snippets
Snippets are predefined pieces of code that can be inserted and filled in. YASnippet uses syntax inspired by TextMate is the most popular, for good reason.
(use-package yasnippet
:init
(load "yasnippet.el")
:diminish t
:general
(:keymaps 'yas-minor-mode-map
"<tab>" nil
"TAB" nil
"<C-tab>" 'yas-expand)
:config
(add-to-list 'yas-snippet-dirs my/snippets-dir)
(yas-global-mode))
(use-package yasnippet-snippets)
Languages
JavaScript / TypeScript
Indent 2 spaces
(setq-default js-indent-level 2
typescript-indent-level 2)
js2-mode improves a lot on the builtin js-mode
(use-package js2-mode
:after eglot
:mode
((rx ".mjs" eos) . js2-mode)
((rx ".jsx?" eos) . js2-jsx-mode)
:hook
(js2-mode . eglot-ensure)
(js2-jsx-mode . eglot-ensure))
Prettier has my preference for formatting JavaScript and TypeScript
(use-package prettier
:config
(setq prettier-enabled-parsers '
(css html json markdown scss svelte toml typescript vue)))
TypeScript stuff
(use-package typescript-mode
:after eglot
:mode
((rx ".tsx?" eos) . typescript-mode)
:hook (typescript-mode . eglot-ensure))
Prefer local packages from node_modules
to global ones
(use-package add-node-modules-path)
Web mode
web-mode handles HTML/CSS and JavaScript
(use-package web-mode
:mode (rx ".svelte" eos)
: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" . (rx ".html.heex" eos))
("jinja2" . (rx ".jinja2" eos))
("python" . (rx ".pt" eos)) ; Chameleon templates
("svelte" . (rx ".svelte" eos))))
:hook
((mhtml-mode css-mode web-mode) . eglot-ensure))
Markdown
markdown-mode adds support for Markdown editing. gfm-mode
supports GitHub Flavoured Markdown.
(use-package markdown-mode
:after eglot
:mode
(((rx "README.md" eos) . gfm-mode)
((rx ".md" eos) . markdown-mode)
((rx ".markdown" eos) . markdown-mode))
:init
(setq markdown-command "multimarkdown")
:hook
(markdown-mode . display-fill-column-indicator-mode)
(markdown-mode . eglot-ensure))
impatient-mode live renders HTML, but it can be made to work with Markdown with a custom filter
(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)))
Elixir
Add support for Elixir with elixir-mode. The elixir-format
hook sets up the correct formatter configuration when in a projectile
project.
(use-package elixir-mode
:after eglot
:hook ((elixir-mode . eglot-ensure))
:config
(add-to-list 'auto-mode-alist '((rx ".[hl]eex") . elixir-mode)))
(use-package elixir-ts-mode
:after eglot
:hook ((elixir-ts-mode . eglot-ensure)))
Add a mix minor mode to call mix
tasks from emacs.
(use-package mix
:hook
(elixir-mode . mix-minor-mode))
Erlang
(use-package erlang
:mode
((rx ".P" eos) . erlang-mode)
((rx ".E" eos) . erlang-mode)
((rx ".S" eos) . erlang-mode)
:config
(require 'erlang-start))
Rust
Rust support with rust-mode.
(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")
)
Configure rust-analyzer
(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))))
Add cargo support
(use-package cargo
:hook
(rust-mode . cargo-minor-mode))
Flycheck support for Rust with flycheck-rust
(use-package flycheck-rust
:hook
(flycheck-mode . flycheck-rust-setup))
TOML
Support for TOML files with toml-mode
(use-package toml-mode
:mode ((rx ".toml" eos) . conf-toml-mode))
Docker
Add docker support with dockerfile-mode
(use-package dockerfile-mode
:after eglot
:hook (dockerfile-mode . eglot-ensure))
Bitbake / Yocto
(use-package bitbake-modes
:straight (:type git :repo "https://bitbucket.org/olanilsson/bitbake-modes.git"))
INI
ini-mode
provides highlighting for ini files
(use-package ini-mode)
JSON
json-mode extends the builtin js-mode
with better syntax highlighting for JSON and adds some editing keybindings
(use-package json-mode
:after eglot
:hook (json-mode . eglot-ensure))
CMake
Add cmake-mode
(use-package cmake-mode)
YAML
Use yaml-mode to handle YAML files
(use-package yaml-mode
:after eglot
:hook (yaml-mode . eglot-ensure))
C/C++
Enable clangd LSP for C and C++
(use-package cc-mode
:ensure nil
:after eglot
:hook
((c-mode c-ts-mode) . eglot-ensure)
((c++-mode c++-ts-mode) . eglot-ensure))
Add some flags to clangd
(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"))))
Add QML mode
(use-package qml-mode
:mode (rx ".qml" eos))
Enable and configure auto-insert-mode
for Horus projects
(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)
Meson
meson is a build system designed to be as fast and as user-friendly as possible.
(use-package meson-mode)
nix
Add nix-mode
(use-package nix-mode
:after eglot
:mode (rx ".nix" eos)
:hook (nix-mode . eglot-ensure))
Tell nil
to use nixfmt
for formatting nix files.
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
`(nix-mode . ("nil" :initializationOptions
(:formatting (:command ["nixfmt"]))))))
Common Lisp
Common Lisp does not use lsp-mode
, but has it's own environment: SLIME or Superior Lisp Interaction Mode for Emacs.
(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
(((rx ".cl" eos) . lisp-mode))
:hook
(lisp-mode . (lambda () (slime-mode t)))
(inferior-lisp-mode . (lambda () (inferior-slime-mode t))))
SLY is a fork of SLIME, by the same author as eglot
, with improved UX
(use-package sly
:mode
(((rx ".cl" eos) . lisp-mode))
:config
(setq sly-lisp-implementations
'((sbcl ("sbcl") :coding-system utf-8-unix))
sly-default-lisp 'sbcl))
Terraform
terraform-mode is a major mode for Terraform files
(use-package terraform-mode
:after eglot
:hook (terraform-mode . eglot-ensure))
Register terraform-ls
with eglot
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(terraform-mode . ("terraform-ls" "serve"))))
Fish
fish-mode provides a major mode for fish shell scripts
(use-package fish-mode
:init
(setq fish-enable-auto-indent t))
Zig
zig-mode provides a major mode for the zig programming language
(use-package zig-mode
:after eglot
:hook (zig-mode . eglot-ensure))
Yuck
(use-package yuck-mode)
Racket
(use-package racket-mode
:after eglot
:hook (racket-mode . eglot-ensure))
Ruby
Let ruby-mode
handle Ruby files
(use-package ruby-mode
:ensure nil
:after eglot
:hook (ruby-mode . eglot-ensure))
Go
It's better than nothing.
(use-package go-mode
:after eglot
:hook (go-mode . eglot-ensure))
Cucumber
feature-mode provides support for user stories written in Cucumber/Gherkin format.
(use-package feature-mode
:config
(setq feature-use-docker-compose nil)
:mode (rx ".feature" eos))
Protobuf
(use-package protobuf-mode)
Just
Python
Python
(use-package python-mode
:hook
((python-mode python-ts-mode) . eglot-ensure))
(use-package python
:ensure nil
:hook (python-base-mode . eglot-ensure)
:init (setq python-indent-guess-indent-offset nil))
jinja2
(use-package jinja2-mode)
Haskell
haskell-mode for Haskell files
(use-package haskell-mode
:hook
(((haskell-mode haskell-ts-mode) . eglot-ensure)
turn-on-haskell-unicode-input-method))
Dhall
Dhall is a programmable configuration language that you can think of as: JSON + functions + types + imports
(use-package dhall-mode
:mode (rx ".dhall" eos))
nushell
nushell is a new type of shell that operates on typed data
(use-package nushell-ts-mode)
Register nushell
LSP with eglot
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(nushell-ts-mode . ("nu" "--lsp"))))
Lua
lua-mode for Lua support.
(use-package lua-mode)
Device Tree
Highlighting for device tree configuration.
(use-package dts-mode)
OCaml
Gleam
Gleam has an official emacs mode: gleam-ts-mode
(use-package gleam-ts-mode
:mode (rx ".gleam" eos)
:hook (gleam-ts-mode . eglot-ensure))
Configure eglot to use the Gleam LSP server
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(gleam-ts-mode . ("gleam" "lsp"))))
PlantUML
PlantUML is a tool to generate a wide variety of diagrams, like sequence, usecase and class diagrams.
(use-package plantuml-mode
:config
(setq plantuml-default-exec-mode 'server
plantuml-server-url "http://localhost:5080/plantuml"))
Org
Main org setup
org-mode configuration
(use-package org
:general
<<org-binds>>
:custom
<<org-customisations>>
:hook
<<org-hooks>>
:config
<<org-config>>)
Some keybindings for often used functions
(:prefix my/leader
"r" 'org-capture
"a" 'org-agenda
"s" 'org-store-link
"L" 'org-store-link-global
"O" 'org-open-at-point-global)
Customisations
(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))
(add-to-list 'org-export-backends 'md)
Hooks
((org-mode . org-indent-mode)
(org-mode . turn-on-visual-line-mode)
(org-mode . variable-pitch-mode)
(org-mode . (lambda () (display-line-numbers-mode -1)))
(org-mode-indent . (lambda () (diminish 'org-indent-mode)))
(org-agenda-mode . (lambda () (hl-line-mode 1))))
Configuration
(add-to-list 'auto-mode-alist '("\\.org\\'" . org-mode))
(add-to-list
'org-src-lang-modes '("plantuml" . plantuml))
(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)
org-capture
allows creating and saving quick notes, links and TODOs
(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))))
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.
(use-package org-datetree
;; :straight org
:ensure org
:after org)
org-protocol
can be used to send data to emacs using org-protocol://
URLs.
(use-package org-protocol
;; :straight org
:ensure org
:after org
:config
(setq org-protocol-default-template-key "c"))
org-roam
org-roam helps with non-hierarchical note-taking. It uses the zettelkasten method to capture and organise notes and ideas.
(use-package org-roam
;; :after org
:custom
(org-roam-directory "~/org-roam")
(org-roam-completion-everywhere t)
<<org-roam-templates>>
: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)))
Org roam capture templates
(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)))
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.
(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))
org-chef
org-chef is used to manage recipes and import them from supported websites
(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")))
Misc
direnv
Writing
Enable flyspell
(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))
Use ivy for flyspell suggestions. Use M-o
to switch to the actions (e.g. saving a word to your dictionary).
(use-package flyspell-correct-ivy
:after flyspell-correct)
Load local init file
If local-init.el
exists, load it.
(when (file-readable-p my/local-init-file)
(load my/local-init-file))
DO NOT ADD ANYTHING BELOW THIS