#+TITLE: My Emacs Config #+AUTHOR: Venomade This is my Emacs config. There are many like it, but this one is mine. According to my archive, it has been rewritten 10+ times since I wrote the first iteration in 2022. My general philosophy for this config is to not use external packages where the built in functionality is enough and any external packages used should follow the Emacs philosophy and design style and not duplicate functionality as much as reasonably possible. In this vein I use *Eglot* and *Corfu* instead of *LSP-Mode* and *Company* as an example of this philosophy. Not all code written in this configuration is my own, though most of it is, with the rest being made up of other people's configs, stack overflow snippets and code from various blog sites from other Emacs users including [[https://xenodium.com][Xenodium]], [[http://www.xahlee.info/][Xah Lee]] and [[https://systemcrafters.net/][System Crafters]]. * Table of Contents :PROPERTIES: :TOC: :include all :ignore this :depth 2 :END: :CONTENTS: - [[#bootstrap][Bootstrap]] - [[#package-manager][Package Manager]] - [[#native-compilation][Native Compilation]] - [[#garbage-collection][Garbage Collection]] - [[#shell-path][Shell Path]] - [[#custom-file][Custom File]] - [[#file-hygiene][File Hygiene]] - [[#suppress-noise][Suppress Noise]] - [[#macos][MacOS]] - [[#appearance][Appearance]] - [[#theme][Theme]] - [[#font-configuration][Font Configuration]] - [[#mode-line][Mode Line]] - [[#icons][Icons]] - [[#highlight-changes][Highlight Changes]] - [[#highlight-keywords][Highlight Keywords]] - [[#highlight-and-dim-parentheses][Highlight and Dim Parentheses]] - [[#pretty-symbols][Pretty Symbols]] - [[#window-focus-flash][Window Focus Flash]] - [[#ui-behaviour][UI Behaviour]] - [[#frame-and-window][Frame and Window]] - [[#line-numbers][Line Numbers]] - [[#scrolling][Scrolling]] - [[#line-wrapping][Line Wrapping]] - [[#keybinding-help][Keybinding Help]] - [[#compilation][Compilation]] - [[#zen-mode][Zen Mode]] - [[#editing-behaviour][Editing Behaviour]] - [[#indentation][Indentation]] - [[#smart-line-beginning][Smart Line Beginning]] - [[#open-line][Open Line]] - [[#replace-selection][Replace Selection]] - [[#trailing-whitespace][Trailing Whitespace]] - [[#move-lines][Move Lines]] - [[#delimiter-pairing][Delimiter Pairing]] - [[#undo-history][Undo History]] - [[#short-answers][Short Answers]] - [[#quit-confirmation][Quit Confirmation]] - [[#session-and-navigation][Session and Navigation]] - [[#recent-files][Recent Files]] - [[#save-position][Save Position]] - [[#minibuffer-history][Minibuffer History]] - [[#dired][Dired]] - [[#buffer-list][Buffer List]] - [[#project-management][Project Management]] - [[#eshell][EShell]] - [[#info-pages][Info Pages]] - [[#completion-and-lsp][Completion and LSP]] - [[#completion-style][Completion Style]] - [[#minibuffer-completion][Minibuffer Completion]] - [[#completion-annotations][Completion Annotations]] - [[#consult][Consult]] - [[#in-buffer-completion][In-Buffer Completion]] - [[#snippets][Snippets]] - [[#completion-extensions][Completion Extensions]] - [[#lsp-client][LSP Client]] - [[#lsp-booster][LSP Booster]] - [[#lsp-transient-menu][LSP Transient Menu]] - [[#documentation-popups][Documentation Popups]] - [[#diagnostics][Diagnostics]] - [[#org-mode][Org Mode]] - [[#core-configuration][Core Configuration]] - [[#heading-bullets][Heading Bullets]] - [[#org-tempo-snippets][Org Tempo Snippets]] - [[#link-behaviour][Link Behaviour]] - [[#tables-of-contents][Tables of Contents]] - [[#disable-auto-indent][Disable Auto-Indent]] - [[#language-support][Language Support]] - [[#lua][Lua]] - [[#nix][Nix]] - [[#cc][C/C++]] - [[#emacs-lisp][Emacs Lisp]] - [[#common-lisp][Common Lisp]] - [[#fennel][Fennel]] - [[#markdown-notes][Markdown Notes]] - [[#keybindings][Keybindings]] :END: * Bootstrap :PROPERTIES: :CUSTOM_ID: bootstrap :END: ** Package Manager :PROPERTIES: :CUSTOM_ID: package-manager :END: Setup =use-package= and [[https://melpa.org/][Melpa]], the community Emacs package repository. #+begin_src emacs-lisp (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (package-initialize) #+end_src ** Native Compilation :PROPERTIES: :CUSTOM_ID: native-compilation :END: Setup Native Compilation if available. #+begin_src emacs-lisp (if (native-comp-available-p) (setq package-native-compile t)) #+end_src ** Garbage Collection :PROPERTIES: :CUSTOM_ID: garbage-collection :END: Improve Emacs' garbage collection by increasing the garbage size and collecting when idle. *Packages*: =gcmh= by [[https://web.archive.org/web/20250808164421/https://akrl.sdf.org/][Andrea Corallo]] #+begin_src emacs-lisp (use-package gcmh :ensure t :hook (after-init . gcmh-mode) :config (setq gcmh-high-cons-threshold (* 128 1024 1024) ; 128MB gcmh-idle-delay 10 gcmh-verbose nil)) #+end_src ** Shell Path :PROPERTIES: :CUSTOM_ID: shell-path :END: Append to the =exec-path= variable paths that are set in the =zsh= shell. #+begin_src emacs-lisp (use-package exec-path-from-shell :ensure t :config (setq exec-path-from-shell-shell-name "/bin/zsh" exec-path-from-shell-arguments '("-l" "-i" "-c")) (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize))) #+end_src ** Custom File :PROPERTIES: :CUSTOM_ID: custom-file :END: Use a =custom.el= file instead of the =init.el= to store uncommitable, device-local changes. #+begin_src emacs-lisp (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) (when (file-exists-p custom-file) (load custom-file)) #+end_src ** File Hygiene :PROPERTIES: :CUSTOM_ID: file-hygiene :END: Store backups, autosaves and lockfiles in the =.emacs.d= directory. #+begin_src emacs-lisp (let* ((backup-dir (expand-file-name "backups/" user-emacs-directory)) (autosave-dir (expand-file-name "autosaves/" user-emacs-directory)) (lock-dir (expand-file-name "lock/" user-emacs-directory))) (make-directory backup-dir t) (make-directory autosave-dir t) (make-directory lock-dir t) (setq backup-directory-alist `((".*" . ,(directory-file-name backup-dir))) auto-save-file-name-transforms `((".*" ,(directory-file-name autosave-dir) t)) lock-file-name-transforms `((".*" ,(directory-file-name lock-dir) t)))) #+end_src ** Suppress Noise :PROPERTIES: :CUSTOM_ID: suppress-noise :END: Don't show =*Warnings*= and =*Compile-Log*= buffers in a window. #+begin_src emacs-lisp (add-to-list 'display-buffer-alist '("\\`\\*\\(Warnings\\|Compile-Log\\)\\*\\'" (display-buffer-no-window) (allow-no-window . t))) #+end_src ** MacOS :PROPERTIES: :CUSTOM_ID: macos :END: Settings specific for use on MacOS. *** Disable Command Key :PROPERTIES: :CUSTOM_ID: disable-command-key :END: Disable the command key on MacOS. #+begin_src emacs-lisp (setq mac-command-modifier nil) #+end_src *** Matching Titlebar :PROPERTIES: :CUSTOM_ID: matching-titlebar :END: Match the titlebar to the current theme on MacOS. *Packages*: =ns-auto-titlebar= by [[https://github.com/purcell/ns-auto-titlebar][Steve Purcell]] #+begin_src emacs-lisp (use-package ns-auto-titlebar :ensure t :config (when (eq system-type 'darwin) (ns-auto-titlebar-mode))) #+end_src *** Block Pop-Ups :PROPERTIES: :CUSTOM_ID: block-pop-ups :END: Don't open files opened from an external source in a new frame on MacOS. #+begin_src emacs-lisp (setq ns-pop-up-frames nil) #+end_src * Appearance :PROPERTIES: :CUSTOM_ID: appearance :END: ** Theme :PROPERTIES: :CUSTOM_ID: theme :END: Use the =ef-dream= for a comfortable readable experience . *Packages*: =ef-themes= by [[https://protesilaos.com/emacs/ef-themes][Protesilaos]] #+begin_src emacs-lisp (use-package ef-themes :ensure t :config (ef-themes-take-over-modus-themes-mode 1)) (setq modus-themes-italic-constructs t modus-themes-bold-constructs t) (load-theme 'ef-dream t) #+end_src ** Font Configuration :PROPERTIES: :CUSTOM_ID: font-configuration :END: Setup both =monospace= and =variable-pitch= fonts along with their sizes in =org-mode=. #+begin_src emacs-lisp (defun font-setup (&optional frame) (with-selected-frame (or frame (selected-frame)) (let* ((fontconf '((fixed-font . "MonofurNerdFont") (variable-font . "MonofurNerdFont") (size . 13))) (fixed-font (cdr (assoc 'fixed-font fontconf))) (variable-font (cdr (assoc 'variable-font fontconf))) (pt-size (cdr (assoc 'size fontconf))) (height (* pt-size 10)) (frame-font (concat fixed-font "-" (number-to-string pt-size)))) ; Fixed-width faces (dolist (face '(default fixed-pitch)) (set-face-attribute face nil :font fixed-font :height height :weight 'regular)) ; Variable-pitch face (set-face-attribute 'variable-pitch nil :font variable-font :height height :weight 'regular) ; Syntax styling (set-face-attribute 'font-lock-comment-face nil :slant 'italic) (set-face-attribute 'font-lock-keyword-face nil :slant 'italic) ; Frame font (add-to-list 'default-frame-alist `(font . ,frame-font))) (let* ((variable-tuple (cond ((x-list-fonts "Google Sans") '(:font "Google Sans")) ((x-family-fonts "Sans Serif") '(:family "Sans Serif")) (nil (warn "Cannot find a Sans Serif Font. Install Google Sans.")))) (base-font-color (face-foreground 'default nil 'default)) (headline `(:inherit default :weight bold :foreground ,base-font-color))) ; TODO: Split these up (custom-theme-set-faces 'user `(org-level-8 ((t (,@headline ,@variable-tuple)))) `(org-level-7 ((t (,@headline ,@variable-tuple)))) `(org-level-6 ((t (,@headline ,@variable-tuple)))) `(org-level-5 ((t (,@headline ,@variable-tuple)))) `(org-level-4 ((t (,@headline ,@variable-tuple :height 1.0)))) `(org-level-3 ((t (,@headline ,@variable-tuple :height 1.2)))) `(org-level-2 ((t (,@headline ,@variable-tuple :height 1.4)))) `(org-level-1 ((t (,@headline ,@variable-tuple :height 1.6)))) `(org-document-title ((t (,@headline ,@variable-tuple :height 1.8 :underline nil)))))))) (if (daemonp) (add-hook 'after-make-frame-functions #'font-setup) (font-setup)) ; Theme the fonts and styles for various org elements (custom-theme-set-faces 'user '(org-block ((t (:inherit fixed-pitch)))) '(org-code ((t (:inherit (shadow fixed-pitch))))) '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch))))) '(org-indent ((t (:inherit (org-hide fixed-pitch))))) '(org-link ((t (:foreground "#89b4fa" :underline t)))) '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch))))) '(org-property-value ((t (:inherit fixed-pitch))) t) '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch))))) '(org-table ((t (:inherit fixed-pitch)))) '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8)))) '(org-verbatim ((t (:inherit (shadow fixed-pitch)))))) #+end_src ** Mode Line :PROPERTIES: :CUSTOM_ID: mode-line :END: Use a much more readable modeline compared to the default. *Packages*: =doom-modeline= from [[https://seagle0128.github.io/doom-modeline/][Centaur Emacs]] #+begin_src emacs-lisp (use-package doom-modeline :ensure t :hook (after-init . doom-modeline-mode) :config (setq doom-modeline-icon t doom-modeline-height 0 ; minimum doom-modeline-bar-width 0 ; disabled doom-modeline-project-detection 'project ; use project.el doom-modeline-buffer-state-icon nil doom-modeline-highlight-modified-buffer-name nil doom-modeline-percent-position nil line-number-mode nil doom-modeline-buffer-encoding nil doom-modeline-time-live-icon nil doom-modeline-time-icon nil display-time-default-load-average nil display-time-format "%a %d %b | %R" helm-ag-show-status-function nil)) ;HACK: Doom Modeline checks for this even though I don't use helm #+end_src ** Icons :PROPERTIES: :CUSTOM_ID: icons :END: Make use of the [[https://www.nerdfonts.com/][Nerd Fonts]] to show icons in Emacs. *Packages*: =nerd-icons=, =nerd-icons-dired= and =nerd-icons-completion= by [[https://github.com/rainstormstudio/nerd-icons.el][Daniel Ding]] and =nerd-icons-ibuffer= from [[https://github.com/seagle0128/nerd-icons-ibuffer][Centaur Emacs]] #+begin_src emacs-lisp (use-package nerd-icons :ensure t :if (display-graphic-p)) (use-package nerd-icons-dired :ensure t :after nerd-icons :hook (dired-mode . nerd-icons-dired-mode)) (use-package nerd-icons-completion :ensure t :after nerd-icons :hook (after-init . nerd-icons-completion-mode)) (use-package nerd-icons-ibuffer :ensure t :after nerd-icons :hook (ibuffer-mode . nerd-icons-ibuffer-mode)) #+end_src ** COMMENT Padding and Spacing :PROPERTIES: :CUSTOM_ID: padding-and-spacing :END: Spacious user interface elements with custom widths and colours. *Packages*: =spacious-padding= by [[https://protesilaos.com/emacs/spacious-padding][Protesilaos]] #+begin_src emacs-lisp (use-package spacious-padding :ensure t :hook (after-init . spacious-padding-mode) :config (setq spacious-padding-widths '( :internal-border-width 8 :header-line-width 4 :mode-line-width 6 :custom-button-width 3 :tab-width 4 :right-divider-width 30 :scroll-bar-width 8 :fringe-width 0)) (setq spacious-padding-subtle-frame-lines '( :mode-line-active spacious-padding-line-active :mode-line-inactive spacious-padding-line-inactive :header-line-active spacious-padding-line-active :header-line-inactive spacious-padding-line-inactive))) #+end_src ** Highlight Changes :PROPERTIES: :CUSTOM_ID: highlight-changes :END: Temporarily highlight modified regions to help the visual transition when making large changes via =undo= or =kill-region=. *Packages*: =goggles= by [[https://github.com/minad/goggles][Daniel Mendler]] #+begin_src emacs-lisp (use-package goggles :ensure t :hook ((prog-mode text-mode) . goggles-mode) :config (setq-default goggles-pulse t)) #+end_src ** Highlight Keywords :PROPERTIES: :CUSTOM_ID: highlight-keywords :END: Highlight actionable keywords such as =TODO=, =FIXME= and =ERROR=. *Packages*: =hl-todo= by [[https://github.com/tarsius/hl-todo][Jonas Bernoulli]] #+begin_src emacs-lisp (use-package hl-todo :ensure t :hook ((org-mode . hl-todo-mode) (prog-mode . hl-todo-mode)) :config (add-to-list 'hl-todo-keyword-faces '("ERROR" . "#8c5353"))) #+end_src ** COMMENT Rainbow Delimiters Colour brackets and other delimiters to improve visual grepping of scope while programming. *Packages*: =rainbow-delimiters= by [[https://github.com/Fanael/rainbow-delimiters][Fanael Linithien]] #+begin_src emacs-lisp (use-package rainbow-delimiters :ensure t :hook (prog-mode . rainbow-delimiters-mode)) #+end_src ** Highlight and Dim Parentheses :PROPERTIES: :CUSTOM_ID: highlight-and-dim-parentheses :END: For =lisp= modes, highlight parentheses that are local to the current point with decreasing brightness the less local they are, and set all parentheses to be dim in these modes by default. *Packages*: =highlight-parentheses= by [[https://sr.ht/~tsdh/highlight-parentheses.el/][Tassilo Horn]] #+begin_src emacs-lisp (use-package highlight-parentheses :ensure t :custom (hl-paren-colors '("white" "#cccccc" "#999999" "#666666" "#444444")) (hl-paren-background-colors '(nil nil nil nil nil)) :config (set-face-attribute 'hl-paren-face nil :weight 'bold)) (defface dim-parens-face '((t :foreground "#666666")) "Face for dimmed parentheses.") (defun dim-parens--enable () (font-lock-add-keywords nil '(("[()]" 0 'dim-parens-face t)) 'append) (font-lock-flush) (highlight-parentheses-mode 1)) (defun dim-parens--disable () (font-lock-remove-keywords nil '(("[()]" 0 'dim-parens-face t))) (font-lock-flush) (highlight-parentheses-mode -1)) (define-minor-mode dim-parens-mode "Dim all parens, highlighting the enclosing pair at point." :lighter " DimP" (if dim-parens-mode (dim-parens--enable) (dim-parens--disable))) #+end_src ** Pretty Symbols :PROPERTIES: :CUSTOM_ID: pretty-symbols :END: Shorten symbols into their single character equivalent. #+begin_src emacs-lisp (global-prettify-symbols-mode 1) #+end_src ** Window Focus Flash :PROPERTIES: :CUSTOM_ID: window-focus-flash :END: Flash between windows when switching to make obvious which window is active. *Packages*: =winpulse= by [[https://xenodium.com/introducing-winpulse][Álvaro Ramírez (Xenodium)]] #+begin_src emacs-lisp (use-package winpulse :vc (:url "https://github.com/xenodium/winpulse" :rev :newest) :config (winpulse-mode +1)) #+end_src * UI Behaviour :PROPERTIES: :CUSTOM_ID: ui-behaviour :END: ** Frame and Window :PROPERTIES: :CUSTOM_ID: frame-and-window :END: *** Resize GUI :PROPERTIES: :CUSTOM_ID: resize-gui :END: Allow any size to be set for the Emacs GUI. #+begin_src emacs-lisp (setq frame-resize-pixelwise t) #+end_src *** Focus Frames :PROPERTIES: :CUSTOM_ID: focus-frames :END: Focus new frames when they're created. #+begin_src emacs-lisp (add-hook 'after-make-frame-functions (lambda (f) (select-frame-set-input-focus f))) #+end_src *** Disable Default Annoyances :PROPERTIES: :CUSTOM_ID: disable-default-annoyances :END: Disable the Bell and other unwanted GUI features. #+begin_src emacs-lisp (setq ring-bell-function 'ignore) (tool-bar-mode -1) (scroll-bar-mode -1) (menu-bar-mode -1) #+end_src ** Line Numbers :PROPERTIES: :CUSTOM_ID: line-numbers :END: Configure line numbers display width and resizing, and display them from startup unless in =fundamental-mode= or =text-mode=. #+begin_src emacs-lisp (setq display-line-numbers-width-start t display-line-numbers-width 4 display-line-numbers-grow-only t) (add-hook 'emacs-startup-hook #'global-display-line-numbers-mode) (dolist (hk '(fundamental-mode-hook text-mode-hook Info-mode-hook)) (add-hook hk (lambda () (display-line-numbers-mode -1)))) #+end_src ** Scrolling :PROPERTIES: :CUSTOM_ID: scrolling :END: Add a scroll margin and improve scrolling. #+begin_src emacs-lisp (setq scroll-margin 10 scroll-conservatively 100 scroll-step 1) #+end_src ** Line Wrapping :PROPERTIES: :CUSTOM_ID: line-wrapping :END: Set the default to not wrap long lines but truncate with spaces. Enable it explicitly in =text-mode=. #+begin_src emacs-lisp (setq-default truncate-lines t) (set-display-table-slot standard-display-table 'truncation 32) (add-hook 'text-mode-hook 'visual-line-mode) #+end_src ** Keybinding Help :PROPERTIES: :CUSTOM_ID: keybinding-help :END: Enable =which-key= on startup. #+begin_src emacs-lisp (add-hook 'emacs-startup-hook #'which-key-mode) #+end_src ** Compilation :PROPERTIES: :CUSTOM_ID: compilation :END: Configure =compile-mode= by colourising it, having it scroll on compilation output and allow files and projects to set their own compile-commands. #+begin_src emacs-lisp (require 'ansi-color) (defun colorize-compilation-buffer () (ansi-color-apply-on-region compilation-filter-start (point))) (add-hook 'compilation-filter-hook 'colorize-compilation-buffer) (setq compilation-scroll-output t) (put 'compile-command 'safe-local-variable #'stringp) #+end_src ** Zen Mode :PROPERTIES: :CUSTOM_ID: zen-mode :END: Centre the buffer and disable line numbers for a calmer writing experience. *Packages*: =olivetti= by [[https://github.com/rnkn/olivetti][Paul W. Rankin]] #+begin_src emacs-lisp (use-package olivetti :ensure t :config ; TODO: Disable line numbers, probably in the line numbers section, but probably try and link the sections somehow (setq olivetti-minimum-body-width 100)) #+end_src * Editing Behaviour :PROPERTIES: :CUSTOM_ID: editing-behaviour :END: ** Indentation :PROPERTIES: :CUSTOM_ID: indentation :END: Indent code with 2 spaces by default. #+begin_src emacs-lisp (setq-default indent-tabs-mode nil tab-width 2 tab-stop-list (number-sequence 2 100 2) c-basic-offset 2) #+end_src ** Smart Line Beginning :PROPERTIES: :CUSTOM_ID: smart-line-beginning :END: Override the built-in =beginning-of-line= function to give respect to context when making the movement. #+begin_src emacs-lisp (progn (defun smart-beginning-of-line () "Move the cursor to the beginning of text on the line." (interactive) (let ((initial-point (point))) (back-to-indentation) (when (eq initial-point (point)) (move-beginning-of-line 1)))) (defun setup-prog-mode-c-a () (local-set-key (kbd "C-a") 'smart-beginning-of-line)) (add-hook 'prog-mode-hook 'setup-prog-mode-c-a) (add-hook 'conf-mode-hook 'setup-prog-mode-c-a)) #+end_src ** Open Line :PROPERTIES: :CUSTOM_ID: open-line :END: Functions to match vi's '=o=' and '=O=' motions, this helps to bypass whatever function is called on the =Return= key. #+begin_src emacs-lisp (cl-labels ((vi-open-line-above () "Open line above the current line." (interactive) (unless (bolp) (beginning-of-line)) (newline) (forward-line -1) (indent-according-to-mode)) (vi-open-line-below () "Open line below the current line." (interactive) (unless (eolp) (end-of-line)) (newline-and-indent))) ; TODO: Move to keybindings section (define-key (current-global-map) (kbd "C-c o") #'vi-open-line-above) (define-key (current-global-map) (kbd "C-c O") #'vi-open-line-below) (which-key-add-key-based-replacements "C-c o" "open-line-above") (which-key-add-key-based-replacements "C-c O" "open-line-below")) #+end_src ** Replace Selection :PROPERTIES: :CUSTOM_ID: replace-selection :END: Overwrite selection when typing while a selection is active. #+begin_src emacs-lisp (use-package delsel :hook (after-init . delete-selection-mode)) #+end_src ** Trailing Whitespace :PROPERTIES: :CUSTOM_ID: trailing-whitespace :END: Clean up trailing whitespace on save. #+begin_src emacs-lisp (progn (defun prog-nuke-trailing-whitespace () "Removes trailing whitespace at the end of the line." (when (or (derived-mode-p 'prog-mode) (derived-mode-p 'conf-mode)) (delete-trailing-whitespace))) (add-hook 'before-save-hook 'prog-nuke-trailing-whitespace)) #+end_src ** Move Lines :PROPERTIES: :CUSTOM_ID: move-lines :END: Move text up and down with a single keybind. *Packages*: =move-text= by [[https://github.com/emacsfodder/move-text][Jason Milkins at Emacs Fodder]] #+begin_src emacs-lisp (use-package move-text :ensure t :config (defun indent-region-advice (&rest ignored) (let ((deactivate deactivate-mark)) (if (region-active-p) (indent-region (region-beginning) (region-end)) (indent-region (line-beginning-position) (line-end-position))) (setq deactivate-mark deactivate))) (advice-add 'move-text-up :after 'indent-region-advice) (advice-add 'move-text-down :after 'indent-region-advice)) #+end_src ** Delimiter Pairing :PROPERTIES: :CUSTOM_ID: delimiter-pairing :END: Insert matching delimiters using the keybindings from =paredit=. *Packages*: =smartparens= by [[https://github.com/Fuco1/smartparens][Matus Goljer]] #+begin_src emacs-lisp (use-package smartparens :ensure t :hook (prog-mode text-mode markdown-mode comint-mode) :config (require 'smartparens-config) (sp-use-paredit-bindings)) #+end_src ** Undo History :PROPERTIES: :CUSTOM_ID: undo-history :END: Make the undo history persistent between sessions and create multi-branch undo history. *Packages*: =undo-tree= by [[https://www.dr-qubit.org/undo-tree.html][Tony 'qubit' Cubitt]] #+begin_src emacs-lisp (use-package undo-tree :ensure t :init (global-undo-tree-mode 1) :config (setq undo-tree-auto-save-history t undo-tree-history-directory-alist `(("." . ,(expand-file-name ".cache" user-emacs-directory))))) #+end_src ** Short Answers :PROPERTIES: :CUSTOM_ID: short-answers :END: Use =y= / =n= and other similar shortenings for dialogue responses. #+begin_src emacs-lisp (setopt use-short-answers t) #+end_src ** Quit Confirmation :PROPERTIES: :CUSTOM_ID: quit-confirmation :END: Ask for confirmation on =C-x C-c= before quitting using custom quit messages from the DOOM games and DOOM Emacs. #+begin_src emacs-lisp (setq quit-messages `(; from DOOM 1 ,(format "I wouldn't leave if I were you. %s is much worse." (if (featurep 'windows-nt) "DOS" "UNIX")) "Ya know, next time you come in here I'm gonna toast ya." "Go ahead and leave. See if I care." "Are you sure you want to quit this great editor?" ; from DOOM 2 "Get outta here and go back to your boring programs." "Just leave. When you come back, I'll be waiting with a bat." "You're lucky I don't smack you for thinking about leaving. " ; from DOOM Emacs "(setq nothing t everything 'permitted)" "Emacs will remember that." "Emacs, Emacs never changes." "Hey! Hey, M-x listen!" "Wake up, Mr. Stallman. Wake up and smell the ashes." "You are *not* prepared!" "Please don't go. The drones need you. They look up to you.")) (defun random-quit-message () "Return a randomly chosen quit message from `quit-messages'." (nth (random (length quit-messages)) quit-messages)) (defun message-confirm-kill-emacs (&rest _) "Prompt the user with a random message before quitting. Returns t to allow kill if the user answers yes; nil otherwise." (let* ((msg (random-quit-message)) (prompt (format "%s Really quit Emacs? " msg))) (yes-or-no-p (propertize prompt 'face '(italic default))))) (setq confirm-kill-emacs #'message-confirm-kill-emacs) #+end_src * Session and Navigation :PROPERTIES: :CUSTOM_ID: session-and-navigation :END: ** Recent Files :PROPERTIES: :CUSTOM_ID: recent-files :END: Keep track of recently accessed files with =recentf=. #+begin_src emacs-lisp (add-hook 'emacs-startup-hook #'recentf-mode) #+end_src ** Save Position :PROPERTIES: :CUSTOM_ID: save-position :END: Save the location of the cursor in files between sessions. #+begin_src emacs-lisp (add-hook 'emacs-startup-hook #'save-place-mode) #+end_src ** Minibuffer History :PROPERTIES: :CUSTOM_ID: minibuffer-history :END: Save the minibuffer history between sessions. #+begin_src emacs-lisp (use-package savehist :hook (after-init . savehist-mode)) #+end_src ** Dired :PROPERTIES: :CUSTOM_ID: dired :END: Configure =dired-mode= so that directories are always copied and deleted recursively, target's for functions are intuited and all files are listed and display human readable metadata. Allow recursive expansion with =TAB= to easily travel directories. *Packages*: =dired-subtree= by [[https://github.com/Fuco1/dired-hacks/blob/master/dired-subtree.el][Matus Goljer]] #+begin_src emacs-lisp (use-package dired :commands (dired) :hook ((dired-mode . dired-hide-details-mode) (dired-mode . hl-line-mode)) :config (setq dired-recursive-copies 'always dired-recursive-deletes 'always dired-dwim-target t dired-listing-switches "-lah")) (use-package dired-subtree :ensure t :after dired :bind (:map dired-mode-map ("TAB" . (lambda () (interactive) (dired-subtree-toggle) (revert-buffer))) ("" . dired-subtree-remove)) :config (setq dired-subtree-use-backgrounds nil)) #+end_src ** Buffer List :PROPERTIES: :CUSTOM_ID: buffer-list :END: Group =project.el= projects together in the =ibuffer=. *Packages*: =ibuffer-project= by [[https://github.com/muffinmad/emacs-ibuffer-project][Andrii Kolomoiets]] #+begin_src emacs-lisp (use-package ibuffer-project :ensure t :after project :config (add-hook 'ibuffer-hook (lambda () (setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups)) (unless (eq ibuffer-sorting-mode 'project-file-relative) (ibuffer-do-sort-by-project-file-relative))))) #+end_src ** Project Management :PROPERTIES: :CUSTOM_ID: project-management :END: A transient menu interface for =project.el=. *Packages*: =disproject= by [[https://github.com/aurtzy/disproject][aurtzy]] #+begin_src emacs-lisp (use-package disproject :ensure t :after project) #+end_src ** EShell :PROPERTIES: :CUSTOM_ID: eshell :END: Setup the =eshell= prompt to be both visually appealing and useful. #+begin_src emacs-lisp (progn ; TODO: Fix this, as is likely completely broken (No Colour) (dolist (spec '((eshell-prompt-user-face font-lock-keyword-face "Face for the username in the eshell prompt.") (eshell-prompt-venv-face font-lock-string-face "Face for the virtualenv name in the eshell prompt.") (eshell-prompt-dir-face dired-directory "Face for the directory path in the eshell prompt.") (eshell-prompt-git-face magit-tag "Face for the git branch in the eshell prompt.") (eshell-prompt-git-dirty-face eshell-prompt-git-face "Face for the git dirty status in the eshell prompt.") (eshell-prompt-symbol-face font-lock-builtin-face "Face for the prompt symbol in the eshell prompt."))) (cl-destructuring-bind (name inherit doc) spec (defface name `((t (:inherit ,inherit))) doc :group 'eshell-prompt))) (defun eshell-abbreviate-dir (dir) "Abbreviate directory to show only the first and the last two components." (let* ((abbrev (abbreviate-file-name dir)) (components (split-string abbrev "/" t)) (num-components (length components))) (if (>= num-components 4) (concat (car components) "/.../" (mapconcat #'identity (last components 2) "/")) abbrev))) (defun eshell-prompt-git-branch () "Return the current git branch or short commit hash, if available." (when (and (executable-find "git") (locate-dominating-file default-directory ".git")) (with-temp-buffer (let ((ret (call-process "git" nil t nil "symbolic-ref" "--short" "HEAD"))) (if (zerop ret) (string-trim (buffer-string)) (when (zerop (call-process "git" nil t nil "rev-parse" "--short" "HEAD")) (string-trim (buffer-string)))))))) (defun eshell-prompt-git-dirty () "Return a dirty flag (✗ if dirty, ✓ if clean) for the git repository." (when (eshell-prompt-git-branch) (with-temp-buffer (call-process "git" nil t nil "status" "--porcelain") (if (> (buffer-size) 0) "✗" "✓")))) (defun eshell-prompt () "Custom eshell prompt." (let* ((user (propertize (user-login-name) 'face 'eshell-prompt-user-face)) (venv (when-let ((venv (getenv "VIRTUAL_ENV"))) (concat (propertize "(" 'face 'eshell-prompt-venv-face) (propertize (file-name-nondirectory venv) 'face 'eshell-prompt-venv-face) (propertize ")" 'face 'eshell-prompt-venv-face)))) (path (propertize (eshell-abbreviate-dir (eshell/pwd)) 'face 'eshell-prompt-dir-face)) (git-branch (eshell-prompt-git-branch)) (git-info (when git-branch (concat " " (propertize "on" 'face 'font-lock-number-face) " " (propertize git-branch 'face 'eshell-prompt-git-face) (propertize (eshell-prompt-git-dirty) 'face 'eshell-prompt-git-dirty-face)))) (prompt (concat user " " (propertize "in" 'face 'font-lock-number-face) " " (if venv (concat venv " ") "") path git-info "\n" (propertize "λ" 'face 'eshell-prompt-symbol-face) " "))) prompt)) (setq eshell-prompt-function 'eshell-prompt eshell-prompt-regexp "^[^λ\n]*λ ")) #+end_src ** Info Pages :PROPERTIES: :CUSTOM_ID: info-pages :END: Configuration for =Info-mode= to set the location for local info files. #+begin_src emacs-lisp (add-to-list 'Info-directory-list (expand-file-name "~/Documents/Info/")) #+end_src * Completion and LSP :PROPERTIES: :CUSTOM_ID: completion-and-lsp :END: ** Completion Style :PROPERTIES: :CUSTOM_ID: completion-style :END: Make completion matching much more powerful. *Packages*: =orderless= by [[https://github.com/oantolin/orderless][Omar Antolín Camarena]] #+begin_src emacs-lisp (use-package orderless :ensure t :config (setq completion-styles '(orderless basic) completion-category-defaults nil completion-category-overrides '((file (styles . (partial-completion))) (eglot (styles . (orderless)))))) #+end_src ** Minibuffer Completion :PROPERTIES: :CUSTOM_ID: minibuffer-completion :END: Enable powerful minibuffer completion with fuzzy finding. *Packages*: =vertico= by [[https://github.com/minad/vertico][Daniel Mendler]] #+begin_src emacs-lisp (use-package vertico :ensure t :hook (after-init . vertico-mode) :bind (:map vertico-map ("DEL" . vertico-directory-delete-char))) #+end_src ** Completion Annotations :PROPERTIES: :CUSTOM_ID: completion-annotations :END: Give completions annotations to describe what a function does. *Packages*: =marginalia= by [[https://github.com/minad/marginalia][Daniel Mendler]] #+begin_src emacs-lisp (use-package marginalia :ensure t :after vertico consult nerd-icons :hook ((emacs-startup . marginalia-mode) (marginalia-mode . nerd-icons-completion-marginalia-setup))) #+end_src ** Consult :PROPERTIES: :CUSTOM_ID: consult :END: These packages provide replacements for built-in emacs functions that have support for completion. *Packages*: =consult= by [[https://github.com/minad/consult][Daniel Mendler]] and =affe= also by [[https://github.com/minad/affe][Daniel Mendler]] #+begin_src emacs-lisp (use-package consult :ensure t) (use-package affe :ensure t :after consult) #+end_src ** In-Buffer Completion :PROPERTIES: :CUSTOM_ID: in-buffer-completion :END: Lightweight completion popup package for an improved auto-completion experience along with Nerd Font icons for symbol representation. *Packages*: =corfu= by [[https://github.com/minad/corfu][Daniel Mendler]] and =nerd-icons-corfu= by [[https://github.com/LuigiPiucco/nerd-icons-corfu][Luigi Sartor Piucco]] #+begin_src emacs-lisp (use-package corfu :ensure t :hook (prog-mode eshell-mode markdown-mode comint-mode) :bind (:map corfu-map ("TAB" . corfu-insert) ("" . corfu-insert) ("RET" . nil) ("C-n" . corfu-next) ("C-p" . corfu-previous) ("C-g" . corfu-quit)) :config (setq corfu-cycle t corfu-auto t corfu-auto-delay 0.2 corfu-preselect 'first corfu-quit-at-boundary t corfu-quit-no-match t corfu-scroll-margin 5 corfu-tab-always-indent 'complete corfu-preview-current nil corfu-min-width 20 corfu-popupinfo-delay '(1.25 . 0.5) corfu-on-exact-match nil) (corfu-popupinfo-mode 1) (with-eval-after-load 'savehist (corfu-history-mode 1) (add-to-list 'savehist-additional-variables 'corfu-history))) (use-package nerd-icons-corfu :ensure t :after nerd-icons corfu :config (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)) #+end_src ** Snippets :PROPERTIES: :CUSTOM_ID: snippets :END: Snippet expansion with powerful multi-stage completion along with a collection of common snippets. *Packages*: =yasnippet= by [[https://joaotavora.github.io/yasnippet/][João Távora]] and =yasnippet-snippets= by [[https://github.com/AndreaCrotti/yasnippet-snippets][Andrea Crotti]] #+begin_src emacs-lisp (use-package yasnippet :ensure t :hook (prog-mode . yas-minor-mode) :config (yas-reload-all)) (use-package yasnippet-snippets :after yasnippet :ensure t) #+end_src ** Completion Extensions :PROPERTIES: :CUSTOM_ID: completion-extensions :END: Use =cape= to provide =completion-at-point= extensions. *Packages*: =cape= by [[https://github.com/minad/cape][Daniel Mendler]] #+begin_src emacs-lisp (use-package cape :ensure t :config ; TODO find out what the wrap buster actually does (caching or whatever) (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)) #+end_src *** YASnippet Capf :PROPERTIES: :CUSTOM_ID: yasnippet-capf :END: Display =yasnippet= in completions. *Packages*: =yasnippet-capf= by [[https://github.com/elken/yasnippet-capf][Ellis Kenyő]] #+begin_src emacs-lisp (use-package yasnippet-capf :ensure t :after cape) #+end_src *** Eglot and YASnippet - Together At Last! :PROPERTIES: :CUSTOM_ID: eglot-and-yasnippet---together-at-last :END: Combine =eglot= and =yasnippet= into a combined completion function. #+begin_src emacs-lisp (defun eglot-capf-with-yasnippet () (setq-local completion-at-point-functions (list (cape-capf-super #'eglot-completion-at-point #'yasnippet-capf)))) (with-eval-after-load 'eglot (add-hook 'eglot-managed-mode-hook #'eglot-capf-with-yasnippet)) #+end_src ** LSP Client :PROPERTIES: :CUSTOM_ID: lsp-client :END: Use =eglot= as a lightweight interface for LSP servers to enable smart completion, diagnostics and much more within Emacs. *Packages*: =eglot= by [[https://joaotavora.github.io/eglot/][João Távora]] #+begin_src emacs-lisp (use-package eglot :ensure t :after cape :config ; Disable progress indicator in the modeline (setq eglot-report-progress nil eglot-extend-to-xref t)) #+end_src *** Consult Integration :PROPERTIES: :CUSTOM_ID: consult-integration :END: Jump to workspace symbols from the LSP with a consult interface. *Packages*: =consult-eglot= by [[https://github.com/mohkale/consult-eglot][Mohsin Kaleem]] #+begin_src emacs-lisp (use-package consult-eglot :ensure t :after consult eglot) #+end_src ** LSP Booster :PROPERTIES: :CUSTOM_ID: lsp-booster :END: Use a rust-powered booster to communicate more efficiently between Emacs and the LSP server. The booster can be downloaded from [[https://github.com/blahgeek/emacs-lsp-booster][Yikai Zhao]]. *Packages*: =eglot-booster= by [[https://github.com/jdtsmith/eglot-booster][JD Smith]] #+begin_src emacs-lisp (use-package eglot-booster :vc (:url "https://github.com/jdtsmith/eglot-booster.git") :ensure t :after eglot :config ; Use emacs-30's built in JSON handler (setq eglot-booster-io-only t) (eglot-booster-mode)) #+end_src ** LSP Transient Menu :PROPERTIES: :CUSTOM_ID: lsp-transient-menu :END: A menu for =eglot= and related functions for easy access. #+begin_src emacs-lisp (require 'transient) (transient-define-prefix eglot-transient () "Transient menu for Eglot." [["Server" ("c" "Reconnect" eglot-reconnect) ("C" "Shutdown" eglot-shutdown) ("I" "Server Info" eglot-show-workspace-configuration)] ["Navigation" ("g" "Go to Definition" xref-find-definitions) ("d" "Describe Thing at Point" eldoc-box-help-at-point) ("D" "Go to Declaration" eglot-find-declaration) ("i" "Go to Implementation" eglot-find-implementation) ("f" "Find References" xref-find-references) ("t" "Type Definition" eglot-find-typeDefinition) ("s" "Search Symbols" (lambda () (interactive) (if (eq major-mode 'java-mode) (call-interactively 'consult-eglot-jdt-symbols) (call-interactively 'consult-eglot-symbols))))] ["Code Actions" ("a" "Code Actions" eglot-code-actions) ("q" "Quick Fix" eglot-code-action-quickfix) ("=" "Format Buffer" eglot-format-buffer) ("o" "Organize Imports" eglot-code-action-organize-imports) ("r" "Rename Symbol" eglot-rename)] ["Diagnostics" ("l" "List Diagnostics" flycheck-list-errors) ("L" "Project Diagnostics" flymake-show-project-diagnostics)]] (interactive) (transient-setup 'eglot-transient)) #+end_src ** Documentation Popups :PROPERTIES: :CUSTOM_ID: documentation-popups :END: View documentation with =eldoc= in a popup, correctly syntax highlighted in =markdown= and limited to only use =eglot= when available. *Packages*: =eldoc-box= by [[https://github.com/casouri/eldoc-box][Yuan Fu]] and =markdown-mode= by [[https://jblevins.org/projects/markdown-mode/][Jason Blevins]] #+begin_src emacs-lisp (use-package markdown-mode :ensure t) (use-package eldoc-box :ensure t) ; Follow markdown links inside eldoc boxes on selection (defun markdown-follow-help-or-link-at-point-advice (orig-fun &rest args) "Prefer to use the help-echo property as `browse-url' target." (let* ((event-win (posn-window (event-start last-input-event))) (help-echo (with-selected-frame (window-frame event-win) (with-current-buffer (window-buffer event-win) (get-text-property (point) 'help-echo)))) (help-is-url (url-type (url-generic-parse-url help-echo)))) (message "if %s (browse-url %S)" help-is-url help-echo) (if help-is-url (browse-url help-echo) (apply orig-fun args)))) (advice-add 'markdown-follow-link-at-point :around #'markdown-follow-help-or-link-at-point-advice) (add-hook 'eglot-managed-mode-hook (lambda () (setq-local eldoc-documentation-functions (list #'eglot-hover-eldoc-function)))) #+end_src ** Diagnostics :PROPERTIES: :CUSTOM_ID: diagnostics :END: Display diagnostics such as Information, Warnings and Errors within the relevant text buffer and in a separate combined buffer. *Packages*: =flycheck= by [[https://www.flycheck.org/en/latest/][Bozhidar Batsov and the Flycheck Maintainers]] and =flycheck-eglot= by [[https://github.com/flycheck/flycheck-eglot][Sergey Firsov and the Flycheck Maintainers]] #+begin_src emacs-lisp (use-package flycheck :ensure t :config (global-flycheck-mode 1)) (use-package flycheck-eglot :ensure t :after (flycheck eglot) :custom (flycheck-eglot-exclusive nil) :config (global-flycheck-eglot-mode 1)) #+end_src * Org Mode :PROPERTIES: :CUSTOM_ID: org-mode :END: ** Core Configuration :PROPERTIES: :CUSTOM_ID: core-configuration :END: Configuration for =org-mode= for code blocks and cleaner looks. #+begin_src emacs-lisp ; Open source blocks in the current window (setq org-src-window-setup 'current-window ; Don't confirm when evaluating a code block org-confirm-babel-evaluate nil ; Make org documents more seamless by hiding markup org-hide-emphasis-markers t) ; Enable a non-monospace font for org documents (add-hook 'org-mode-hook 'variable-pitch-mode) ; Enable line wrapping for org documents (add-hook 'org-mode-hook 'visual-line-mode) #+end_src ** Heading Bullets :PROPERTIES: :CUSTOM_ID: heading-bullets :END: Make the header =*= look like varying bullet points. *Packages*: =org-superstar= by [[https://github.com/integral-dw/org-superstar-mode][D. Williams]] #+begin_src emacs-lisp (use-package org-superstar :ensure t :hook (org-mode . org-superstar-mode) :custom (org-superstar-remove-leading-stars t) (org-superstar-headline-bullets-list '("•" "•" "•" "•" "•"))) #+end_src ** Org Tempo Snippets :PROPERTIES: :CUSTOM_ID: org-tempo-snippets :END: Quick snippets for common org templates. #+begin_src emacs-lisp (require 'org-tempo) #+end_src ** Link Behaviour :PROPERTIES: :CUSTOM_ID: link-behaviour :END: Open =org-mode= links in the current window. #+begin_src emacs-lisp (setq org-link-frame-setup '((file . find-file) (wl . wl-other-frame) (vm . vm-visit-folder-other-frame) (gnus . gnus))) #+end_src ** Tables of Contents :PROPERTIES: :CUSTOM_ID: tables-of-contents :END: Generate and update indexes automatically in Org documents. #+begin_src emacs-lisp (use-package org-make-toc :ensure t :hook org-mode :custom (org-make-toc-insert-custom-ids t)) #+end_src ** Disable Auto-Indent :PROPERTIES: :CUSTOM_ID: disable-auto-indent :END: Disable automatic indentation when editing org documents. #+begin_src emacs-lisp (add-hook 'org-mode-hook (lambda () (electric-indent-local-mode -1))) #+end_src * Language Support :PROPERTIES: :CUSTOM_ID: language-support :END: ** Lua :PROPERTIES: :CUSTOM_ID: lua :END: *Packages*: =lua-mode= by [[https://immerrr.github.io/lua-mode/][immerrr]] #+begin_src emacs-lisp (use-package lua-mode :ensure t :hook eglot-ensure :config ; Use a 2 space indent level for formatting (setq lua-indent-level 2 ; Use lua 5.4 for documentation lua-documentation-url "https://www.lua.org/manual/5.4/manual.html")) ;; Settings for lua_ls (add-hook 'lua-mode-hook (lambda () (setq-local eglot-workspace-configuration `(:Lua (:codeLens (:enable :json-false) :completion (:enable t :callSnippet "Replace" :autoRequire :json-false :showWord "Disable" :postfix "@") :diagnostics (:enable t :disable ["lowercase-global" "spell-check"] :severity (:undefined-field "Warning" :undefined-global "Warning")) :format (:enable t) :hint (:enable t :paramName "Disable" :paramType :json-false) :hover (:enable t) :runtime (:version "Lua 5.4" :path ["?.lua" "?/init.lua" "./stubs/?.lua" "./stubs/?/init.lua"]) :semantic (:enable :json-false) :workspace (:library [,(concat (getenv "HOME") "/.luarocks/share/lua/5.4") "./stubs"])))))) ; TODO Try fix this only for lua-mode as that is where the bug is (setq eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider)) #+end_src ** Nix :PROPERTIES: :CUSTOM_ID: nix :END: *Packages*: =nix-mode= by [[https://github.com/NixOS/nix-mode][The NixOS Maintainers]] #+begin_src emacs-lisp (use-package nix-mode :ensure t :mode "\\.nix\\'" :hook eglot-ensure :config (add-to-list 'eglot-server-programs '(nix-mode . ("nil")))) #+end_src ** C/C++ :PROPERTIES: :CUSTOM_ID: cc :END: *Packages*: =rmsbolt= by [[https://gitlab.com/jgkamat/rmsbolt][Kay Kamat]] #+begin_src emacs-lisp (add-hook 'c++-mode-hook #'eglot-ensure) (add-hook 'c-mode-hook #'eglot-ensure) (setq c-default-style "linux" c-basic-offset 2) (use-package rmsbolt :ensure t :custom (rmsbolt-asm-format nil)) (setq gdb-many-windows t gdb-show-main t) #+end_src ** Emacs Lisp :PROPERTIES: :CUSTOM_ID: emacs-lisp :END: #+begin_src emacs-lisp (add-hook 'emacs-lisp-mode-hook #'dim-parens-mode) (add-hook 'emacs-lisp-mode-hook (lambda () (local-set-key (kbd "M-p") #'ignore) (local-set-key (kbd "M-n") #'ignore))) ;; (defun mycommand ;; (when (and (this-predicate) ;; (that-predicate)) ;; (do-something) ;; (do-some-more) ;; (a-third-thing))) #+end_src ** Common Lisp :PROPERTIES: :CUSTOM_ID: common-lisp :END: *Packages*: =sly= by [[https://github.com/joaotavora/sly][João Távora]] #+begin_src emacs-lisp (use-package sly :ensure t :hook ((lisp-mode . dim-parens-mode)) :bind (:map lisp-mode-map ("M-p" . ignore) ("M-n" . ignore)) :custom (inferior-lisp-program "ros -Q run")) (require 'transient) (transient-define-prefix lisp-transient () "Transient menu for Sly." [["REPL" ("c" "Connect" sly) ("C" "Shutdown" sly-quit-lisp) ("I" "Sly Info" sly-info)] ["Navigation" ("g" "Go to Definition" sly-edit-definition) ("d" "Describe Thing at Point" sly-describe-symbol) ("f" "Find References" sly-who-references) ("i" "Inspect Evaluation" sly-inspect) ("h" "Hyperspec Lookup" hyperspec-lookup) ] ["Code Actions" ("a" "Code Actions" eglot-code-actions) ("q" "Quick Fix" eglot-code-action-quickfix) ("=" "Format Buffer" eglot-format-buffer) ("o" "Organize Imports" eglot-code-action-organize-imports) ("r" "Rename Symbol" eglot-rename) ]] (interactive) (transient-setup 'lisp-transient)) #+end_src ** Fennel :PROPERTIES: :CUSTOM_ID: fennel :END: *Packages*: =fennel-mode= by [[https://git.sr.ht/~technomancy/fennel-mode][Phil Hagelberg]] #+begin_src emacs-lisp (use-package fennel-mode :ensure t :hook ((fennel-mode . fennel-proto-repl-minor-mode) (fennel-mode . dim-parens-mode) (fennel-mode . eglot-ensure) (fennel-repl-mode . dim-parens-mode)) :bind (:map fennel-mode-map ("M-p" . ignore) ("M-n" . ignore))) (defun fennel-capf-after-eglot () (when (derived-mode-p 'fennel-mode) (setq-local completion-at-point-functions '(fennel-proto-repl-complete fennel-complete tags-completion-at-point-function)))) (add-hook 'eglot-managed-mode-hook #'fennel-capf-after-eglot t) (add-hook 'fennel-mode-hook (lambda () (push '("lambda" . 955) prettify-symbols-alist))) (require 'transient) (transient-define-prefix fennel-transient () "Transient menu for Fennel." [["Server" ("c" "Reconnect" eglot-reconnect) ("C" "Shutdown" eglot-shutdown)] ["Navigation" ("g" "Go to Definition" xref-find-definitions) ("d" "Describe Thing at Point" eldoc-box-help-at-point) ("D" "Print Documentation" fennel-show-documentation) ("f" "Find References" xref-find-references)] ["Code Actions" ("a" "Code Actions" eglot-code-actions) ("q" "Quick Fix" eglot-code-action-quickfix) ("=" "Format with fnlfmt" fennel-format) ("o" "Organize Imports" eglot-code-action-organize-imports) ("r" "Rename Symbol" eglot-rename)] ["Diagnostics" ("l" "List Diagnostics" flycheck-list-errors) ("L" "Project Diagnostics" flymake-show-project-diagnostics)] ["REPL" ("z" "Start Proto REPL" fennel-proto-repl) ("b" "Eval Buffer" fennel-proto-repl-eval-buffer)]] (interactive) (transient-setup 'fennel-transient)) #+end_src ** Markdown Notes :PROPERTIES: :CUSTOM_ID: markdown-notes :END: *Packages*: =zk4e= by [[https://codeberg.org/mcookly/zk-emacs][Maximilian Cook]] and =tomlparse= by [[https://github.com/johannes-mueller/tomlparse.el][Johannes Mueller]] #+begin_src emacs-lisp (use-package tomlparse :ensure t) (use-package zk4e :ensure t :after tomlparse :hook (markdown-mode . eglot-ensure) :vc ( :url "https://codeberg.org/mcookly/zk4e" :branch "main" :rev :newest) :config (setq zk4e-notebook-directories '(("Notes" . "~/Documents/Notes"))) (zk4e-select-notebook "Notes") (add-hook 'zk4e-edit-hook 'zk4e-maybe-name-buffer) (add-to-list 'eglot-server-programs '(markdown-mode . ("zk" "lsp")))) #+end_src ** COMMENT Markdown Notes (Denote) *Packages*: =denote=, =denote-org= and =consult-denote= by [[https://protesilaos.com/emacs/denote][Protesilaos]] and =denote-menu= by [[https://www.scss.tcd.ie/~sulimanm/posts/denote-menu.html][Mohamed Suliman]] #+begin_src emacs-lisp (use-package denote :ensure t :hook (dired-mode . denote-dired-mode) :config (setq denote-directory (expand-file-name "~/Documents/Denotes/")) (denote-rename-buffer-mode 1)) (use-package consult-denote :ensure t :config (consult-denote-mode 1)) ; TODO: Denote Explore https://lucidmanager.org/productivity/denote-explore/ (use-package denote-menu :ensure t :config (add-hook 'denote-menu-mode-hook (lambda () (display-line-numbers-mode -1)))) (use-package denote-org :ensure t) #+end_src * COMMENT Testing :PROPERTIES: :CUSTOM_ID: testing :END: #+begin_src emacs-lisp #+end_src * Keybindings :PROPERTIES: :CUSTOM_ID: keybindings :END: This section contains one large =emacs-lisp= block that contains all the custom keybindings for easy reference and modification. #+begin_src emacs-lisp (require 'bind-key) ;; Buffer Begin and End (bind-key* "C-," 'beginning-of-buffer) (bind-key* "C-." 'end-of-buffer) ;; Regex find and replace (keymap-global-set "C-c r" 'replace-regexp) ;; Recent files (keymap-global-set "C-x C-r" 'recentf-open) (which-key-add-key-based-replacements "C-x C-r" "recent-files") ;; Compile Mode (keymap-global-set "C-c m c" 'compile) (keymap-global-set "C-c m r" 'recompile) (keymap-global-set "C-c m g" 'rmsbolt) (which-key-add-key-based-replacements "C-c m" "make") ;; Move text up and down (keymap-global-set "M-p" 'move-text-up) (keymap-global-set "M-n" 'move-text-down) ;; Zen Mode (keymap-global-set "C-c z" 'olivetti-mode) ;; IBuffer (keymap-global-set "C-x C-b" 'ibuffer) ;; Project management (keymap-set ctl-x-map "p" 'disproject-dispatch) ;; Search lines (keymap-global-set "C-c s" 'consult-line) (which-key-add-key-based-replacements "C-c s" "search-lines") ;; Go to line (keymap-global-set "M-g M-g" 'consult-goto-line) (keymap-global-set "M-g g" 'consult-goto-line) (keymap-global-set "C-c g" 'consult-goto-line) (which-key-add-key-based-replacements "C-c g" "goto-line") ;; Find Files (keymap-global-set "C-c f f" 'affe-find) (keymap-global-set "C-c f g" 'affe-grep) (keymap-global-set "C-c f ." (lambda () (interactive) (affe-find default-directory))) (which-key-add-key-based-replacements "C-c f ." "affe-find-cwd") (keymap-global-set "C-c f c" (lambda () (interactive) (find-file "~/.config/emacs/config.org"))) (which-key-add-key-based-replacements "C-c f c" "find-config") (keymap-global-set "C-c f t" (lambda () (interactive) (find-file "~/Documents/Notes/TODO.md"))) (which-key-add-key-based-replacements "C-c f t" "find-todo") (which-key-add-key-based-replacements "C-c f" "find-file") ;; Denote ;; (keymap-global-set "C-c n n" 'denote) ;; (keymap-global-set "C-c n N" 'denote-type) ;; (keymap-global-set "C-c n r" 'denote-rename-file) ;; (keymap-global-set "C-c n l f" 'denote-link) ;; (keymap-global-set "C-c n l h" 'denote-org-link-to-heading) ;; (keymap-global-set "C-c n b" 'denote-backlinks) ;; (keymap-global-set "C-c n g" 'denote-grep) ;; (keymap-global-set "C-c n f" 'consult-denote-find) ;; (keymap-global-set "C-c n d" 'list-denotes) ;; (which-key-add-key-based-replacements "C-c n" "denotes") ;; ZK Notes (keymap-global-set "C-c n a" 'zk4e-alias) (keymap-global-set "C-c n e" 'zk4e-edit) (keymap-global-set "C-c n d" (lambda () (interactive) (zk4e-new "Diario"))) (which-key-add-key-based-replacements "C-c n d" "zk4e-diario") (which-key-add-key-based-replacements "C-c n" "zk4e") ;; Modus Themes (keymap-global-set "C-c v t" 'modus-themes-toggle) (keymap-global-set "C-c v s" 'modus-themes-select) (which-key-add-key-based-replacements "C-c v" "vivendi-theme") ;; Yasnippet (keymap-global-set "C-c y n" 'yas-new-snippet) (keymap-global-set "C-c y v" 'yas-visit-snippet-file) (keymap-global-set "C-c y i" 'yas-insert-snippet) (which-key-add-key-based-replacements "C-c y" "yasnippets") ;; Eglot (define-key eglot-mode-map (kbd "C-c l") (lambda () (interactive) (cond ((eq major-mode 'fennel-mode) (call-interactively #'fennel-transient)) (t (call-interactively #'eglot-transient))))) ;; Sly ; INFO: Can't use sly-mode-map because that doesn't get loaded till later on (define-key lisp-mode-map (kbd "C-c l") (lambda () (interactive) (call-interactively #'lisp-transient))) #+end_src