2023-03-04 14:25:40 +01:00
#+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= .
2023-04-05 13:21:29 +02:00
#+begin_src emacs-lisp :tangle early-init.el
2023-03-04 14:25:40 +01:00
;; -*- 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)
2023-04-05 13:21:29 +02:00
;; 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; -* -
2023-03-04 14:25:40 +01:00
#+end_src
** Personal variables
This sets some variables with my personal preferences for easy customization
#+begin_src emacs-lisp
2024-02-07 08:37:09 +01:00
(defvar my/default-font "MonaspiceNe Nerd Font")
2023-11-14 14:44:16 +01:00
(defvar my/variable-width-font "Iosevka Aile")
2024-02-07 08:37:09 +01:00
(defvar my/comment-font "MonaspiceRn Nerd Font")
2023-11-14 14:44:16 +01:00
(defvar my/default-font-height 120)
(defvar my/default-font-weight 'light)
2023-03-04 14:25:40 +01:00
(defvar my/default-font-width 'normal)
2023-09-07 16:08:06 +02:00
(defvar my/variable-width-font-height 1.1)
2023-03-04 14:25:40 +01:00
(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
2023-11-10 16:45:56 +01:00
Add repositories where packages are installed from.
2023-03-04 14:25:40 +01:00
#+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.
2023-08-10 16:44:48 +02:00
*NOTE* : Disabled because it is builtin since emacs 29
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(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
2024-04-22 17:43:34 +02:00
Disable title bar, toolbar and scroll bar
2023-03-04 14:25:40 +01:00
#+begin_src emacs-lisp
2024-04-22 17:43:34 +02:00
; This needs to go before scroll-bar-mode
(setq-default default-frame-alist '((undecorated . t)))
2023-03-04 14:25:40 +01:00
(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
2023-08-10 16:44:48 +02:00
Surround marked text with (), [] or {}
#+begin_src emacs-lisp
(electric-pair-mode 1)
#+end_src
2023-03-04 14:25:40 +01:00
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
2023-10-19 08:36:28 +02:00
Show trailing whitespace
#+begin_src emacs-lisp
(setq show-trailing-whitespace t)
#+end_src
2023-03-04 14:25:40 +01:00
Delete trailing whitespace on save.
#+begin_src emacs-lisp
(add-hook 'before-save-hook 'delete-trailing-whitespace)
#+end_src
2023-10-19 08:36:28 +02:00
Sentences end in one space, not two. We're not using typewriters.
#+begin_src emacs-lisp
(setq sentence-end-double-space nil)
#+end_src
2023-03-04 14:25:40 +01:00
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
2023-11-03 12:30:59 +01:00
Prefer to open frames in a horizontal split and make sure they're of a decent width
2023-03-04 14:25:40 +01:00
#+begin_src emacs-lisp
2023-11-03 12:30:59 +01:00
(setq split-height-threshold nil
2023-11-14 14:44:16 +01:00
window-min-width 100)
2023-03-04 14:25:40 +01:00
#+end_src
Set fill column to 80
#+begin_src emacs-lisp
(setq-default fill-column 80)
#+end_src
2023-10-19 08:36:28 +02:00
Kill whole lines instead of clearing them
#+begin_src emacs-lisp
(setq kill-whole-line t)
#+end_src
2023-03-04 14:25:40 +01:00
* Interface
** Easy edit config file
2023-10-19 08:36:28 +02:00
Disabled because the configuration is handled by Nix using [[https://github.com/nix-community/emacs-overlay ][emacs-overlay ]]
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(defun find-config ()
"Edit config.org"
(interactive)
(find-file my/config-file-path))
(general-define-key
:prefix my/leader
"C" 'find-config)
#+end_src
2023-11-30 08:57:33 +01:00
** Appearance
2023-03-04 14:25:40 +01:00
2023-10-19 08:36:28 +02:00
Enable pixel scrolling.
#+begin_src emacs-lisp
(setq pixel-scroll-precision-mode t)
#+end_src
2023-11-21 20:20:32 +01:00
I like the [[https://draculatheme.com ][dracula theme ]]
2023-03-04 14:25:40 +01:00
2023-11-21 20:20:32 +01:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(use-package dracula-theme
:init
2024-03-20 18:33:33 +01:00
(load-theme 'dracula :no-confirm))
2023-03-04 14:25:40 +01:00
#+end_src
[[https://github.com/belak/emacs-monokai-pro-theme ][Monokai Pro ]] is also pretty.
2024-03-20 18:33:33 +01:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(use-package monokai-pro-theme
:init
2024-03-20 18:33:33 +01:00
(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))
2023-03-04 14:25:40 +01:00
#+end_src
2023-11-10 16:45:56 +01:00
Set fonts.
2023-03-04 14:25:40 +01:00
#+begin_src emacs-lisp
2023-11-14 14:44:16 +01:00
(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)
2023-11-16 10:57:49 +01:00
(add-hook 'after-make-frame-functions 'my/set-font-size)
2023-11-14 14:44:16 +01:00
2023-11-10 16:45:56 +01:00
(defun my/setup-fonts ()
2023-11-14 14:44:16 +01:00
(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))
2023-11-10 16:45:56 +01:00
(add-hook 'after-init-hook 'my/setup-fonts)
2023-03-04 14:25:40 +01:00
#+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
2023-11-10 16:45:56 +01:00
;; (ligature-set-ligatures 'prog-mode '("<---" "<--" "<<-" "<-" "- >" "-->" "--->" "<- >" "<-- >" "<--- >" "<---- >" "<!--"
;; "<==" "<= ==" "<= " "=>" "= >>" "==>" "= ==>" ">= " "<= >" "<== >" "<=== >" "<==== >" "<!---"
;; "<~~" "<~ " "~>" "~ ~>" "::" ":::" "==" "!= " "===" "!= ="
;; ":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "+:" "-:" "= :" "<****** >" "++" "+ ++"))
;; Ligatures for Monaspace
(ligature-set-ligatures 'prog-mode '(
; ss01
"==" "= ==" "= /=" "!=" "!==" "/ =" "/= =" "~~" "= ~" "!~ "
; ss02
">=" "<= "
; ss03
"->" "<-" "= >" "<!--" "-- >" "<~" "<~ ~" "~ >" "~~>" "<~ >"
; ss04
"</" "/ >" "</ >" "/\\" "\\/ "
; ss05
"|>" "<|"
; ss06
"##" "###"
; ss07
"***" "/* " "*/" "/* /" "(*" "* )" "(*)"
; ss08
".=" ".-" "..<"
; dlig & calt
"<!" "**" "::" "=:" "=!" "=/" "--" ".." "//" "&&" "||" ":=" ": >" ":<" "!!" " >:" "<:" "#=" "?:" "?." "??" ";;" "///" ":::" "..." "= !=" "= :=" "..= " "..-"))
2023-03-04 14:25:40 +01:00
;; 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
2023-05-16 09:13:08 +02:00
(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))
2023-03-04 14:25:40 +01:00
(setq dashboard-navigator-buttons
`(((,(all-the-icons-octicon "search" :height 0.9 :v-adjust -0.1)
2023-05-16 09:13:08 +02:00
" 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 "))))
2023-03-04 14:25:40 +01:00
(setq
dashboard-projects-backend 'projectile
dashboard-items '((recents . 5)
(projects . 5)
(registers . 5)))
2023-05-16 09:13:08 +02:00
(dashboard-setup-startup-hook)
2023-03-04 14:25:40 +01:00
;; 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
2023-05-12 15:06:44 +02:00
(use-package minions
:config
(minions-mode 1))
2023-03-04 14:25:40 +01:00
#+end_src
Use [[https://github.com/seagle0128/doom-modeline ][doom-modeline ]] for a nice and fancy modeline
2023-05-12 15:06:44 +02:00
*2023-05-12* Disabled because it causes emacs to hang
2023-03-04 14:25:40 +01:00
2023-05-12 15:06:44 +02:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(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
2023-05-12 15:06:44 +02:00
[[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
2023-03-04 14:25:40 +01:00
** 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
<<ivy-binds >>
:config
<<ivy-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
2023-09-07 08:44:37 +02:00
Auto balance parenthesese. *Note:* Disabled in favour of =electric-pair-mode=
2023-03-04 14:25:40 +01:00
2023-09-06 16:56:52 +02:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(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)
2023-11-30 08:57:33 +01:00
(lisp-mode . paredit-mode)
2023-03-04 14:25:40 +01:00
(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
2024-02-06 09:45:02 +01:00
** TRAMP
Set some connection properties
#+begin_src emacs-lisp
2024-02-09 20:37:41 +01:00
(with-eval-after-load "tramp" (add-to-list 'tramp-connection-properties
(list (regexp-quote "/sshx:hass:")
"remote-shell" "/bin/bash")))
2024-02-06 09:45:02 +01:00
#+end_src
2023-03-04 14:25:40 +01:00
** 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
2023-11-03 12:30:59 +01:00
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
2023-03-04 14:25:40 +01:00
** 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
*** Tree-sitter
2023-03-31 14:41:09 +02:00
[[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.
2023-03-04 14:25:40 +01:00
#+begin_src emacs-lisp
2023-03-31 14:41:09 +02:00
(when (< emacs-major-version 29)
(use-package tree-sitter
2023-03-04 14:25:40 +01:00
:config
(global-tree-sitter-mode)
:ghook
2023-03-31 14:41:09 +02:00
('tree-sitter-after-on-hook #'tree-sitter-hl-mode)))
2023-03-04 14:25:40 +01:00
#+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
2023-10-05 08:22:14 +02:00
Automatically use the =<lang>-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
2023-05-21 22:14:27 +02:00
*** lsp-mode
2023-03-04 14:25:40 +01:00
[[https://emacs-lsp.github.io/lsp-mode ][lsp-mode ]] adds Language Server Protocol support to emacs.
2023-05-21 22:14:27 +02:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(use-package lsp-mode
:init
(setq lsp-keymap-prefix "C-c l"
lsp-use-plists nil)
(defun eb/lsp-mode-setup-completion ()
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(orderless)))
:hook ((conf-toml-mode
python-mode
sh-mode) . lsp-deferred)
(lsp-mode . lsp-enable-which-key-integration)
(lsp-completion-mode . eb/lsp-mode-setup-completion)
:commands
(lsp lsp-deferred)
:custom
(lsp-completion-provider :none) ;; I'm using corfu
:config
(setq lsp-typescript-surveys-enabled nil
lsp-completion-enable t
lsp-enable-suggest-server-download nil))
#+end_src
[[https://emacs-lsp.github.io/lsp-ui/ ][lsp-ui ]] provides higher level UI elements for =lsp-mode= , like code lenses and flycheck support.
2023-05-21 22:14:27 +02:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(use-package lsp-ui
:ghook 'lsp-mode-hook
:config
(setq lsp-ui-doc-enable t
lsp-ui-peek-enable t
lsp-ui-sideline-enable t
lsp-ui-imenu-enable t)
:custom
(lsp-ui-doc-position 'bottom))
#+end_src
[[https://github.com/emacs-lsp/lsp-ivy ][lsp-ivy ]] integrates =ivy= into =lsp-mode=
#+begin_src emacs-lisp :tangle no
(use-package lsp-ivy
:commands lsp-ivy-workspace-symbol)
#+end_src
[[https://github.com/gagbo/consult-lsp ][consult-lsp ]] integrates =consult= into =lsp-mode=
2023-05-21 22:14:27 +02:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(use-package consult-lsp)
#+end_src
[[https://github.com/emacs-lsp/lsp-treemacs ][lsp-treemacs ]] provides an integration between =lsp-mode= and [[https://github.com/Alexander-Miller/treemacs ][treemacs ]].
2023-05-21 22:14:27 +02:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(use-package lsp-treemacs
:commands lsp-treemacs-errors-list)
#+end_src
2023-05-21 22:14:27 +02:00
*** dap-mode
2023-03-04 14:25:40 +01:00
[[http://emacs-lsp.github.io/dap-mode ][dap-mode ]] provides debugging facilities using the [[https://microsoft.github.io/debug-adapter-protocol/ ][Debug Adapter Protocol ]]
2023-05-21 22:14:27 +02:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(use-package dap-mode
:defer t
:custom
(dap-auto-configure-mode t)
(dap-auto-configure-features '(sessions locals breakpoints expressions tooltip))
:config
(require 'dap-lldb)
(require 'dap-cpptools)
(setq dap-lldb-debugged-program-function (lambda () (read-file-name "Select program executable to debug")))
(dap-register-debug-template "C++ LLDB"
(list :type "lldb-vscode"
:dap-server-path (executable-find "lldb-vscode")
:cwd nil
:args nil
:request "launch"
:program nil))
(defun dap-debug-create-or-edit-json-template ()
"Edit C++ debugging configuration or create and edit if none exists"
(interactive)
(let ((filename (concat (lsp-workspace-root) "/launch.json"))
(default (concat user-emacs-directory "/default-launch.json")))
(unless (file-exists-p filename)
(copy-file default filename))
(find-file-existing filename))))
#+end_src
2023-05-21 22:14:27 +02:00
*** eglot
[[https://joaotavora.github.io/eglot/ ][eglot ]] is an alternative to =lsp-mode= that is builtin with emacs >= 29
#+begin_src emacs-lisp
2023-06-22 14:20:31 +02:00
(use-package eglot
:config
2023-09-06 16:56:52 +02:00
(fset #'json--log-event #'ignore) ;; Performance boost by not logging every event
2023-06-22 14:20:31 +02:00
(add-to-list 'eglot-server-programs
2024-02-09 20:37:41 +01:00
'((toml-mode toml-ts-mode conf-toml-mode) . ("taplo" "lsp" "stdio")))
2024-03-06 08:16:11 +01:00
;; (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"))))
;;
2023-06-22 14:20:31 +02:00
(add-to-list 'eglot-server-programs
2024-03-06 08:16:11 +01:00
`((elixir-ts-mode heex-ts-mode) .
,(eglot-alternatives '("lexical" "elixir-ls"))))
2024-01-17 09:38:52 +01:00
(add-to-list 'eglot-server-programs
'(dhall-mode . ("dhall-lsp-server")))
2023-10-05 14:58:08 +02:00
(add-to-list 'eglot-stay-out-of 'flymake)
2023-06-22 14:20:31 +02:00
(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))))
2023-05-21 22:14:27 +02:00
#+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
2023-03-31 14:41:09 +02:00
** Snippets
2023-03-04 14:25:40 +01:00
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
2023-03-31 14:41:09 +02:00
:init
(load "yasnippet.el")
2023-03-04 14:25:40 +01:00
: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)
2023-03-31 14:41:09 +02:00
(yas-global-mode))
2023-03-04 14:25:40 +01:00
(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
2023-05-21 22:14:27 +02:00
:after eglot
2023-03-04 14:25:40 +01:00
:mode
2023-03-04 17:07:41 +01:00
("\\.mjs\\'" . js2-mode)
2023-03-04 14:25:40 +01:00
("\\.jsx?\\'" . js2-jsx-mode)
:hook
2023-05-21 22:14:27 +02:00
(js2-mode . eglot-ensure)
(js2-jsx-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
Prettier has my preference for formatting JavaScript and TypeScript
#+begin_src emacs-lisp
2023-08-10 16:44:48 +02:00
(use-package prettier
:config
(setq prettier-enabled-parsers '
(css html json markdown scss svelte toml typescript vue)))
2023-03-04 14:25:40 +01:00
#+end_src
TypeScript stuff
#+begin_src emacs-lisp
(use-package typescript-mode
2023-05-21 22:14:27 +02:00
:after eglot
2023-03-04 14:25:40 +01:00
:mode
("\\.tsx?\\'" . typescript-mode)
2023-05-21 22:14:27 +02:00
:hook (typescript-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+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
2023-05-21 22:14:27 +02:00
:after eglot
2023-03-04 14:25:40 +01:00
: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)
2023-06-15 10:06:50 +02:00
(add-to-list 'web-mode-engines-alist '(("elixir" . "\\.html.heex\\'")
2023-06-19 08:16:08 +02:00
("jinja2" . "\\.jinja2\\'")
("python" . "\\.pt\\'")))
2023-03-04 14:25:40 +01:00
:hook
2023-05-21 22:14:27 +02:00
((html-mode css-mode web-mode) . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+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
2023-05-21 22:14:27 +02:00
:after eglot
2023-03-04 14:25:40 +01:00
:mode
(("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init
(setq markdown-command "multimarkdown")
:hook
(markdown-mode . display-fill-column-indicator-mode)
2023-05-21 22:14:27 +02:00
(markdown-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
2023-05-17 08:05:12 +02:00
[[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
2023-03-04 14:25:40 +01:00
*** 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.
2024-03-06 08:16:11 +01:00
#+begin_src emacs-lisp :tangle no
2024-02-15 08:36:05 +01:00
(use-package elixir-mode
:after eglot
:hook ((elixir-mode . eglot-ensure))
:config
(add-to-list 'auto-mode-alist '("\\.[hl]eex\\'" . elixir-mode)))
2023-03-04 14:25:40 +01:00
#+end_src
2024-03-06 08:16:11 +01:00
#+begin_src emacs-lisp
(use-package elixir-ts-mode
:after eglot
:hook ((elixir-ts-mode . eglot-ensure)))
#+end_src
2023-03-04 14:25:40 +01:00
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
2023-03-21 17:21:33 +01:00
*** Erlang
#+begin_src emacs-lisp
(use-package erlang
:mode
("\\.P\\'" . erlang-mode)
("\\.E\\'" . erlang-mode)
("\\.S\\'" . erlang-mode)
:config
(require 'erlang-start))
#+end_src
2023-03-04 14:25:40 +01:00
*** Rust
Rust support with [[https://github.com/rust-lang/rust-mode ][rust-mode ]].
#+begin_src emacs-lisp
(use-package rust-mode
2023-05-21 22:14:27 +02:00
:after eglot
2023-03-04 14:25:40 +01:00
:hook
2023-05-21 22:14:27 +02:00
(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)
2024-04-10 21:34:27 +02:00
:preferPrelude t
2023-05-21 22:14:27 +02:00
:prefix "plain")
:checkOnSave (:enable t
:command "clippy"
:allTargets t)
:inlayHints (:bindingModeHints t
:chainingHints t
2024-04-10 21:34:27 +02:00
:lifetimeElisionHints (:enable "always" :useParameterNames t)
2023-05-21 22:14:27 +02:00
: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))))
2023-03-04 14:25:40 +01:00
#+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
2023-03-08 17:07:47 +01:00
(use-package dockerfile-mode
2023-05-21 22:14:27 +02:00
:after eglot
:hook (dockerfile-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+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
2023-03-08 17:07:47 +01:00
(use-package json-mode
2023-05-21 22:14:27 +02:00
:after eglot
:hook (json-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
*** CMake
Add [[https://melpa.org/#/cmake-mode ][cmake-mode ]]
#+begin_src emacs-lisp
2023-03-08 17:07:47 +01:00
(use-package cmake-mode
2023-05-21 22:14:27 +02:00
:after eglot
:hook (cmake-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
*** YAML
Use [[https://github.com/yoshiki/yaml-mode ][yaml-mode ]] to handle YAML files
#+begin_src emacs-lisp
2023-03-08 17:07:47 +01:00
(use-package yaml-mode
2023-05-21 22:14:27 +02:00
:after eglot
:hook (yaml-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
*** C/C++
Enable clangd LSP for C and C++
#+begin_src emacs-lisp
(use-package cc-mode
:ensure nil
2023-05-21 22:14:27 +02:00
:after eglot
2023-03-04 14:25:40 +01:00
:hook
2023-11-21 20:20:32 +01:00
((c-mode c-ts-mode) . eglot-ensure)
((c++-mode c+ +-ts-mode) . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
2024-02-15 08:36:05 +01:00
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
2023-03-04 14:25:40 +01:00
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
2023-05-21 22:14:27 +02:00
:after eglot
2023-03-04 14:25:40 +01:00
:mode "\\.nix\\'"
2023-05-21 22:14:27 +02:00
:hook (nix-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
2024-02-23 11:35:56 +01:00
Tell =nil= to use =nixfmt= for formatting nix files.
2023-05-23 11:09:55 +02:00
#+begin_src emacs-lisp
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
2023-11-14 14:44:16 +01:00
`(nix-mode . ("nil" :initializationOptions
2024-02-23 11:35:56 +01:00
(:formatting (:command ["nixfmt"]))))))
2023-05-23 11:09:55 +02:00
#+end_src
2023-03-04 14:25:40 +01:00
*** 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.
2023-11-14 14:44:16 +01:00
#+begin_src emacs-lisp :tangle no
2023-03-04 14:25:40 +01:00
(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
2023-11-14 14:44:16 +01:00
[[https://github.com/joaotavora/sly ][SLY ]] is a fork of SLIME, by the same author as =eglot= , with improved UX
#+begin_src emacs-lisp
2023-11-30 08:57:33 +01:00
(use-package sly
:mode
(("\\.cl\\'" . lisp-mode))
:config
(setq sly-lisp-implementations
'((sbcl ("sbcl") :coding-system utf-8-unix))
sly-default-lisp 'sbcl))
2023-11-14 14:44:16 +01:00
#+end_src
2023-03-04 14:25:40 +01:00
*** 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
2023-05-21 22:14:27 +02:00
:after eglot
:hook (terraform-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
2023-05-23 17:12:50 +02:00
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
2023-03-04 14:25:40 +01:00
*** 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
2023-05-21 22:14:27 +02:00
:after eglot
:hook (zig-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
*** Yuck
#+begin_src emacs-lisp
(use-package yuck-mode)
#+end_src
*** Racket
#+begin_src emacs-lisp
(use-package racket-mode
2023-05-21 22:14:27 +02:00
:after eglot
:hook (racket-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
*** Ruby
Let =ruby-mode= handle Ruby files
#+begin_src emacs-lisp
(use-package ruby-mode
:ensure nil
2023-05-21 22:14:27 +02:00
:after eglot
:hook (ruby-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+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
2023-05-21 22:14:27 +02:00
:after eglot
:hook (go-mode . eglot-ensure))
2023-03-04 14:25:40 +01:00
#+end_src
2023-04-09 23:22:11 +02:00
*** 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
2023-04-19 09:33:37 +02:00
*** 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
2023-06-15 10:06:50 +02:00
*** Python
Python
2023-06-19 08:16:08 +02:00
#+begin_src emacs-lisp :tangle no
2023-06-15 10:06:50 +02:00
(use-package python-mode
:hook
((python-mode python-ts-mode) . eglot-ensure))
#+end_src
2023-06-19 08:16:08 +02:00
#+begin_src emacs-lisp
2023-06-23 14:11:44 +02:00
(use-package python
:ensure nil
:hook (python-base-mode . eglot-ensure)
:init (setq python-indent-guess-indent-offset nil))
2023-06-19 08:16:08 +02:00
#+end_src
2023-06-15 10:06:50 +02:00
jinja2
#+begin_src emacs-lisp
(use-package jinja2-mode)
#+end_src
2023-08-10 16:44:48 +02:00
*** 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
2023-09-06 16:56:52 +02:00
*** 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
2023-09-29 11:52:45 +02:00
*** 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
2023-11-20 17:05:09 +01:00
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
2024-03-14 09:25:26 +01:00
*** Lua
2024-01-29 10:18:41 +01:00
2024-03-14 09:25:26 +01:00
[[https://github.com/immerr/lua-mode ][lua-mode ]] for Lua support.
2024-01-29 10:18:41 +01:00
#+begin_src emacs-lisp
2024-03-14 09:25:26 +01:00
(use-package lua-mode)
2024-01-29 10:18:41 +01:00
#+end_src
2023-03-04 14:25:40 +01:00
* Org
** Main org setup
[[https://orgmode.org ][org-mode ]] configuration
#+begin_src emacs-lisp
(use-package org
:general
<<org-binds >>
:custom
<<org-customisations >>
:hook
<<org-hooks >>
:config
<<org-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)
<<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)))
#+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
2023-08-10 16:44:48 +02:00
** 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
2023-11-03 12:30:59 +01:00
:after org-capture
2023-08-10 16:44:48 +02:00
: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
2023-03-04 14:25:40 +01:00
* 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
<<envrc-init >>
#+end_src