config.org (35204B)
1 #+title: Fast (il)literate emacs config built on ~elpaca~ 2 #+author: Ethan Long 3 #+startup: overview 4 5 * Overview of this config 6 This is my (subjectively) simplistic literate config for Emacs. 7 I aim to keep this minimal, and to use as few large dependencies as possible. 8 9 I previously had a very complicated handwoven Emacs config with a beautiful ~org-mode~, but I have grown tired of the number of packages that is used, and I don't really use ~org~ that heavily. 10 11 The reader may disagree on the minimalism of this configuration, which is rather subjective. 12 If one wanted to be as minimal as possible, vanilla Emacs without any configuration technically functions, but that's not in the spirit of Emacs in my opinion. 13 14 As an added bonus of being more minimal, this config should also be more portable across systems (I hope). 15 16 This config has the following explicit package dependencies: 17 |--------------+--------------------------------------------------------------------| 18 | *Package* | *Use* | 19 |--------------+--------------------------------------------------------------------| 20 | ~org~ | Karthinks build, required for LaTeX previews. Declared in ~init.el~. | 21 |--------------+--------------------------------------------------------------------| 22 | ~meow~ | For modal keybinds. | 23 |--------------+--------------------------------------------------------------------| 24 | ~ligature~ | For font ligatures on regular Emacs builds. | 25 |--------------+--------------------------------------------------------------------| 26 | ~batppuccin~ | For the Catppuccin theme. | 27 |--------------+--------------------------------------------------------------------| 28 | ~ultra-scroll~ | For better smooth scrolling when navigating with a mouse. | 29 |--------------+--------------------------------------------------------------------| 30 | ~async~ | For the easy ability to start async processes. | 31 | | Used for async treesitter grammar building and installation. | 32 |--------------+--------------------------------------------------------------------| 33 | ~org-modern~ | For a nicer looking Org mode in only 900 lines of elisp. | 34 |--------------+--------------------------------------------------------------------| 35 | ~vterm~ | For a usable terminal emulator within Emacs. | 36 |--------------+--------------------------------------------------------------------| 37 | ~powershell~ | A small powershell mode for working with psh files over TRAMP. | 38 |--------------+--------------------------------------------------------------------| 39 There are a total of 9 packages installed by these dependencies. 40 * Keybinds 41 ** Meow (modal editing) 42 This package is very important to me, I have been poisoned by ~vi~ and rely on the modal editing model to function properly. 43 If you're interested in my config function, see ~meow-setup~. 44 I use mostly the recommended QWERTY binds given by the Meow devs: 45 :meow-setup: 46 #+begin_src emacs-lisp 47 (defun ethandl/meow-setup () 48 (require 'meow) 49 (setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty) 50 (setq meow-keypad-leader-dispatch "C-x") 51 (meow-motion-overwrite-define-key 52 '("j" . meow-next) 53 '("k" . meow-prev) 54 '("<escape>" . ignore)) 55 (meow-leader-define-key 56 ;; SPC j/k will run the original command in MOTION state. 57 '("j" . "H-j") 58 '("k" . "H-k") 59 ;; Use SPC (0-9) for digit arguments. 60 '("1" . meow-digit-argument) 61 '("2" . meow-digit-argument) 62 '("3" . meow-digit-argument) 63 '("4" . meow-digit-argument) 64 '("5" . meow-digit-argument) 65 '("6" . meow-digit-argument) 66 '("7" . meow-digit-argument) 67 '("8" . meow-digit-argument) 68 '("9" . meow-digit-argument) 69 '("0" . meow-digit-argument) 70 '("/" . meow-keypad-describe-key) 71 '("?" . meow-cheatsheet)) 72 (meow-normal-define-key 73 '("0" . meow-expand-0) 74 '("9" . meow-expand-9) 75 '("8" . meow-expand-8) 76 '("7" . meow-expand-7) 77 '("6" . meow-expand-6) 78 '("5" . meow-expand-5) 79 '("4" . meow-expand-4) 80 '("3" . meow-expand-3) 81 '("2" . meow-expand-2) 82 '("1" . meow-expand-1) 83 '("-" . negative-argument) 84 '(";" . meow-reverse) 85 '("," . meow-inner-of-thing) 86 '("." . meow-bounds-of-thing) 87 '("[" . meow-beginning-of-thing) 88 '("]" . meow-end-of-thing) 89 '("a" . meow-append) 90 '("A" . meow-open-below) 91 '("b" . meow-back-word) 92 '("B" . meow-back-symbol) 93 '("c" . meow-change) 94 '("d" . meow-delete) 95 '("D" . meow-backward-delete) 96 '("e" . meow-next-word) 97 '("E" . meow-next-symbol) 98 '("f" . meow-find) 99 '("g" . meow-cancel-selection) 100 '("G" . meow-grab) 101 '("h" . meow-left) 102 '("H" . meow-left-expand) 103 '("i" . meow-insert) 104 '("I" . meow-open-above) 105 '("j" . meow-next) 106 '("J" . meow-next-expand) 107 '("k" . meow-prev) 108 '("K" . meow-prev-expand) 109 '("l" . meow-right) 110 '("L" . meow-right-expand) 111 '("m" . meow-join) 112 '("n" . meow-search) 113 '("o" . meow-block) 114 '("O" . meow-to-block) 115 '("p" . meow-yank) 116 '("q" . meow-quit) 117 '("Q" . meow-goto-line) 118 '("r" . meow-replace) 119 '("R" . meow-swap-grab) 120 '("s" . meow-kill) 121 '("t" . meow-till) 122 '("u" . meow-undo) 123 '("U" . meow-undo-in-selection) 124 '("v" . meow-visit) 125 '("w" . meow-mark-word) 126 '("W" . meow-mark-symbol) 127 '("x" . meow-line) 128 '("X" . meow-goto-line) 129 '("y" . meow-save) 130 '("Y" . meow-sync-grab) 131 '("z" . meow-pop-selection) 132 '("'" . repeat) 133 '(">" . indent-rigidly-right-to-tab-stop) 134 '("<" . indent-rigidly-left-to-tab-stop) 135 '("<escape>" . ignore))) 136 #+end_src 137 :end: 138 139 #+begin_src emacs-lisp 140 (elpaca 'meow 141 (require 'meow) 142 (ethandl/meow-setup) 143 (meow-global-mode 1)) 144 #+end_src 145 Unfortunately in daemon mode, meow's fonts are not initialized correctly. Here is a workaround (very similar to [[https://github.com/meow-edit/meow/issues/491][this one on GitHub]]): 146 #+begin_src emacs-lisp 147 (defun ethandl/fix-meow-faces () 148 "Fixes the faces for meow when emacs is running in daemon mode" 149 (add-hook 'server-after-make-frame-hook #'meow--prepare-face)) 150 #+end_src 151 Now because we don't have any guarantee of ~meow--prepare-face~ being available until after elpaca has completed the installation of everything, we have to add this to the ~elpaca-after-init-hook~: 152 #+begin_src emacs-lisp 153 (add-hook 'elpaca-after-init-hook #'ethandl/fix-meow-faces) 154 #+end_src 155 ** Window navigation 156 When navigating windows, I find the binding ~C-x o~ to be rather slow when you have many buffers open in different arrangements, so I have adopted a set of bindings for the ~windmove-*~ functions that are left unused in the default configuration. These binds fit in quite nicely with the ~vim~-like keybinds that I use with ~meow~. 157 158 This is mostly just the work of [[https://dev.to/rajasegar/vim-style-repeatable-key-bindings-for-navigating-windows-in-emacs-5c4l][Rajasegar Chandran]]. 159 #+begin_src emacs-lisp 160 (global-set-key (kbd "C-x w h") 'windmove-left) 161 (global-set-key (kbd "C-x w j") 'windmove-down) 162 (global-set-key (kbd "C-x w k") 'windmove-up) 163 (global-set-key (kbd "C-x w l") 'windmove-right) 164 (repeat-mode t) 165 (defvar-keymap windmove-repeat-map 166 :repeat t 167 "h" #'windmove-left 168 "j" #'windmove-down 169 "k" #'windmove-up 170 "l" #'windmove-right) 171 #+end_src 172 ** Super as meta 173 For all systems that aren't MacOS (and even some MacOS emacs builds), the default meta key is Alt. Because I frequently use keyboards where I have swapped my meta and alt keys, I would like to keep this consistant across operating systems. 174 175 The following works on Linux, it is untested on Windows, and does nothing on my MacOS build. 176 #+begin_src emacs-lisp 177 (setq x-super-keysym 'meta) 178 #+end_src 179 * Emacs appearance 180 I think that vanilla emacs has a charming appearance that I aim to keep for the most part, I won't be using any packages to change the modeline or anything special. 181 ** Menu bar removal 182 The menu bar is /fine/ on MacOS or in an NS environment (see platform specific tweaks), but elsewhere it tends to add clutter. 183 Begone. 184 #+begin_src emacs-lisp 185 (menu-bar-mode 0) 186 #+end_src 187 ** Tool bar removal 188 Even worse than the menu bar: the tool bar. 189 Simply a waste of space in my opinion. 190 #+begin_src emacs-lisp 191 (tool-bar-mode 0) 192 #+end_src 193 ** Scroll bar removal 194 The scroll bar is unnecessary if you are navigating via keyboard, and takes up space. 195 #+begin_src emacs-lisp 196 (scroll-bar-mode 0) 197 #+end_src 198 ** Line numbers 199 When working with modal editing and in general programming, it is useful to be able to see distances to previous or subsequent lines in the buffer. 200 Emacs comes with a nice built-in mode that displays line numbers on the left-hand side of a buffer, much like in ~vi~. 201 #+begin_src emacs-lisp 202 (add-hook 'prog-mode-hook #'display-line-numbers-mode) 203 #+end_src 204 Optionally, you can also set the line numbers to be relative to the cursor position. 205 #+begin_src emacs-lisp :tangle no 206 (setq display-line-numbers-type 'relative) 207 #+end_src 208 209 By default, the size of the line-numbers column is only big enough for small files and will grow and shrink to accomodate the line numbers as you scroll, leading to the jarring movement of code horizontally as a buffer is scrolled. 210 This can be fixed: 211 #+begin_src emacs-lisp 212 (setq display-line-numbers-width-start 0) 213 (setq display-line-numbers-grow-only t) 214 #+end_src 215 ** Modeline customisation 216 *** Disable line number 217 The current line number is irrelevant to have on the modeline, given that we display the current line number in the buffer. 218 #+begin_src emacs-lisp 219 (line-number-mode 0) 220 #+end_src 221 *** Enable horizontal cursor position 222 For some reason, this is not displayed by default in the Emacs modeline. 223 It can be very useful to know the current position of the cursor in a line so that you can restrict line length in applications where that matters. 224 #+begin_src emacs-lisp 225 (column-number-mode 1) 226 #+end_src 227 ** Indentation indicators 228 Programming languages that use indentation to control scope such as Python are greatly enhanced by the ability to see indentation more clearly. 229 The ~indent-bars~ package by jdtsmith gives us the ability to see this indentation much more clearly. 230 #+begin_src emacs-lisp 231 (elpaca indent-bars 232 (setq indent-bars-no-descend-lists 'skip) ; prevent extra bars in nested lists + skip intermediate bars 233 (setq indent-bars-treesit-support t)) 234 #+end_src 235 236 Because I want all aspects of language configuration to be in [[The programming environment][the programming environment]] section, I don't want to set language-specific variables here. 237 The plist ~indent-bars-treesit-scope~ is one such language-specific variable, if there are ever any more that I want to configure, I will also add them here: 238 #+begin_src emacs-lisp 239 (defvar ethandl/indent-bars-treesit-scope nil) 240 (defun ethandl/set-indent-bars-treesit-scope () 241 (require 'indent-bars) 242 (setq indent-bars-treesit-scope ethandl/indent-bars-treesit-scope)) 243 (add-hook 'elpaca-after-init-hook #'ethandl/set-indent-bars-treesit-scope) 244 #+end_src 245 ** Fonts 246 *** Code fonts 247 These are the fonts used generally by the Emacs user interface, and in any buffer that doesn't use ~variable-pitch-mode~. 248 249 I like the fonts created by Toshi Omagari, primarily Codelia and Comic code. 250 As a fallback, I don't mind Monaco/Monego. 251 #+begin_src emacs-lisp 252 (defvar ethandl/font-name nil 253 "Font name for regular Emacs fonts 254 Used in all buffers.") 255 256 (defvar ethandl/font-size nil 257 "Size for regular Emacs fonts 258 Used in all buffers.") 259 260 (defvar ethandl/default-font-size 12 261 "Default size for regular Emacs fonts 262 Used in all buffers.") 263 264 (defvar ethandl/font-string nil 265 "Fully qualified string for use with `set-frame-font' 266 Describes the font used in all regular Emacs buffers.") 267 268 (defun ethandl/font-exists (fname) 269 "Returns nil if the font FNAME does not exist, else 270 returns the font list after and including FNAME" 271 (member fname (font-family-list))) 272 273 (defun ethandl/set-font-vars (&optional name size) 274 "Set font configuration variables 275 276 Use NAME as the font family and SIZE as the font size in 277 points. If NAME is nil, select the first available font 278 from a predefined list. If SIZE is nil, uses 279 `ethandl/default-font-size'. 280 281 Sets `ethandl/font-name', `ethandl/font-size', and 282 `ethandl/font-string'." 283 (setq ethandl/font-name 284 (or name 285 (seq-find #'ethandl/font-exists 286 '("Codelia Ligatures" 287 "Comic Code Ligatures" 288 "Monego" 289 "Monaco" 290 "monospace")))) 291 (setq ethandl/font-size 292 (or size ethandl/default-font-size)) 293 (setq ethandl/font-string 294 (format "%s-%d" ethandl/font-name ethandl/font-size))) 295 296 (defun ethandl/set-frame-font (&optional frame name size) 297 "Sets the font for a frame or all frames if FRAME is not 298 specified. 299 300 Use FRAME to specify the frame to change, NAME as the font family, 301 and SIZE as the font size in points. If FRAME is nil, will apply to 302 all open frames. If NAME or SIZE are nil, follows the same 303 convention as `ethandl/set-font-vars'." 304 (if (or name size) 305 (ethandl/set-font-vars name size) 306 (unless (and ethandl/font-name 307 ethandl/font-size 308 ethandl/font-string) 309 (ethandl/set-font-vars))) 310 311 (set-frame-font ethandl/font-string t (if frame (list frame) t))) 312 #+end_src 313 Now we ensure that these functions apply the fonts both when the config is loaded, and whenever a new frame is made. 314 It is particularly important to have the ~server-after-make-frame-hook~ if you are planning on running Emacs as a daemon: 315 #+begin_src emacs-lisp 316 (ethandl/set-font-vars) 317 (ethandl/set-frame-font) 318 319 (add-hook 'after-make-frame-functions #'ethandl/set-frame-font) 320 (add-hook 'server-after-make-frame-hook #'ethandl/set-frame-font) 321 #+end_src 322 *** Org fonts 323 Org documents are configured to start in variable pitch mode such that we have two sets of fonts: Body (variable-pitch), and verbatim (monospaced fixed-pitch): 324 #+begin_src emacs-lisp 325 (add-hook 'org-mode-hook #'variable-pitch-mode) 326 #+end_src 327 328 To define our desired fonts, we create functions as we did before in [[Code fonts]]: 329 #+begin_src emacs-lisp 330 (defvar ethandl/org-mono-font-name nil 331 "Font for code and any monospace blocks in Org documents") 332 (defvar ethandl/org-var-font-name nil 333 "Font for Org documents, variable width") 334 (defvar ethandl/org-default-font-height 140 335 "Default height for fonts in org documents") 336 (defvar ethandl/org-font-ht nil 337 "Font height in org documents") 338 339 (defun ethandl/org-set-font-vars (&optional height var mono) 340 "Sets the mono font variables for Org mode 341 342 Use HEIGHT to specify the font height in px, VAR as the 343 variable-pitch font family, and MONO as the monospace font 344 family. If HEIGHT is nil, uses the default height given by 345 `ethandl/org-default-font-height'. If VAR or MONO are nil, 346 selects the first available font for each from a predefined 347 list." 348 (setq ethandl/org-mono-font-name 349 (or mono 350 (seq-find #'ethandl/font-exists 351 '("Codelia Ligatures" 352 "Iosevka" 353 "Monego" 354 "Monaco" 355 "CMU Typewriter Text" 356 "DejaVu Sans Mono" 357 "monospace")))) 358 (setq ethandl/org-var-font-name 359 (or var 360 (seq-find #'ethandl/font-exists 361 '("CMU Serif" 362 "Iosevka Aile" 363 "Times New Roman" 364 "DejaVu Serif")))) 365 (setq ethandl/org-font-ht 366 (or height ethandl/org-default-font-height))) 367 #+end_src 368 369 Next, we define a way to easily apply these fonts. 370 This is not as simple as it should be, because ~variable-pitch-mode~ takes over all Org fonts. 371 #+begin_src emacs-lisp 372 (defun ethandl/org-set-fonts (&optional height var mono) 373 "Applies the mono fonts for org mode. Arguments are the 374 same as for `ethandl/set-org-font-vars'." 375 (if (or height var mono) 376 (ethandl/org-set-font-vars height var mono) 377 (unless (and ethandl/org-mono-font-name 378 ethandl/org-var-font-name 379 ethandl/org-font-ht) 380 (ethandl/org-set-font-vars))) 381 382 (custom-theme-set-faces 383 'user 384 `(variable-pitch ((t (:family ,ethandl/org-var-font-name :height ,ethandl/org-font-ht)))) 385 `(fixed-pitch ((t (:family ,ethandl/org-mono-font-name :height 0.9)))) 386 387 '(org-block ((t (:inherit fixed-pitch)))) 388 '(org-code ((t (:inherit (shadow fixed-pitch))))) 389 '(org-document-info ((t (:foreground "dark orange")))) 390 '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch))))) 391 '(org-indent ((t (:inherit (org-hide fixed-pitch))))) 392 '(org-link ((t (:foreground "royal blue" :underline t)))) 393 '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch))))) 394 '(org-property-value ((t (:inherit fixed-pitch))) t) 395 '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch))))) 396 '(org-table ((t (:inherit fixed-pitch :foreground "#83a598")))) 397 '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8)))) 398 '(org-verbatim ((t (:inherit (shadow fixed-pitch))))))) 399 #+end_src 400 401 Finally, we ensure that these functions are run whenever necessary: 402 #+begin_src emacs-lisp 403 ;; Evaluate available fonts when loading config 404 (ethandl/org-set-font-vars) 405 406 ;; Evaluate available fonts when making a new frame (relevant for daemon mode) 407 (add-hook 'after-make-frame-functions #'ethandl/org-set-font-vars) 408 (add-hook 'server-after-make-frame-hook #'ethandl/org-set-font-vars) 409 410 ;; Set the fonts when loading into an org buffer 411 (add-hook 'org-mode-hook #'ethandl/org-set-fonts) 412 #+end_src 413 *** Ligatures 414 In order to get ligatures to display in regular builds of emacs, we use ~ligature.el~: 415 #+begin_src emacs-lisp 416 (unless (eq system-type 'darwin) 417 (elpaca 'ligature 418 (ligature-set-ligatures 'prog-mode '("<---" "<--" "<<-" "<-" "->" "-->" "--->" "<->" "<-->" "<--->" "<---->" "<!--" 419 "<==" "<===" "<=" "=>" "=>>" "==>" "===>" ">=" "<=>" "<==>" "<===>" "<====>" "<!---" 420 "<~~" "<~" "~>" "~~>" "::" ":::" "==" "!=" "===" "!==" 421 ":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "+:" "-:" "=:" "<******>" "++" "+++")) 422 (global-ligature-mode t))) 423 #+end_src 424 ** Theme 425 I like the Catppuccin theme, but the official one kinda stinks. As such, I use the ~batppuccin~ theme: 426 #+begin_src emacs-lisp 427 (elpaca 'batppuccin 428 (require 'batppuccin) 429 (load-theme 'batppuccin-mocha t)) 430 #+end_src 431 432 Another nice theme is ~gruber-darker~ by tsoding, one can use it similarly: 433 #+begin_src emacs-lisp :tangle no 434 (elpaca 'gruber-darker-theme 435 (require 'gruber-darker-theme) 436 (load-theme 'gruber-darker t)) 437 #+end_src 438 * Emacs behaviour 439 ** Startup screen removal 440 Boot straight into a scratch buffer, removing the startup screen: 441 #+begin_src emacs-lisp 442 (setq-default inhibit-startup-screen t) 443 #+end_src 444 ** Line wrapping 445 The default line wrapping doesn't respect words and will arbitrarily wrap on the last character. 446 Luckily Emacs comes with an alternate wrapping method: 447 #+begin_src emacs-lisp 448 (global-visual-line-mode 1) 449 #+end_src 450 451 Futhermore, with vanilla Emacs 30.1 or newer the indentation of the wrapped line will be respected with the addition of the following mode: 452 #+begin_src emacs-lisp 453 (when (fboundp 'global-visual-wrap-prefix-mode) 454 (global-visual-wrap-prefix-mode 1)) 455 #+end_src 456 ** Scrolling 457 There are a collection of variables that make scrolling behave more like that in ~vi~, my poison. 458 #+begin_src emacs-lisp 459 (setq scroll-conservatively 10000 460 scroll-margin 0 461 auto-window-vscroll nil) 462 #+end_src 463 464 For smooth mouse and faster document scrolling, I use the package ~ultra-scroll~ by jdtsmith. 465 This works on all emacs builds to my knowledge, and is by far the best scrolling package. 466 #+begin_src emacs-lisp 467 (elpaca 'ultra-scroll 468 (require 'ultra-scroll) 469 (ultra-scroll-mode 1) 470 (add-hook 'ultra-scroll-hide-functions #'org-modern-mode)) 471 #+end_src 472 ** Suppress ~native-compiler~ warnings 473 These warnings are generally not useful. 474 It seems that there's a lot that can go wrong with native compilation that cause it to freak out and give warnings, but as long as we aren't gettings errors, everything is fine. 475 For debugging purposes, you can disable the tangling of this block: 476 #+begin_src emacs-lisp 477 (setq native-comp-async-report-warnings-errors `silent) 478 #+end_src 479 ** Asyncronous commands 480 The package ~async~ allows for us to run long, blocking commands in separate Emacs processes. 481 #+begin_src emacs-lisp 482 (defvar ethandl/async-job-queue nil) 483 (defvar ethandl/async-job-results nil) 484 485 (defun ethandl/eval-async-job (job) 486 (let ((form (car job)) 487 (inject-vars (cadr job))) 488 (async-start 489 (if inject-vars 490 `(lambda () 491 ,(async-inject-variables inject-vars) 492 ,form) 493 `(lambda () ,form)) 494 (lambda (result) 495 (push result ethandl/async-job-results))))) 496 497 498 (defun ethandl/eval-async-job-queue () 499 (mapc #'ethandl/eval-async-job ethandl/async-job-queue) 500 (setq ethandl/async-job-queue nil)) 501 502 (defun ethandl/async-job-queue-add (form &optional inject-vars) 503 (push (list form inject-vars) ethandl/async-job-queue)) 504 505 (elpaca 'async 506 (require 'async) 507 (ethandl/eval-async-job-queue) 508 (run-with-idle-timer 30 t 'ethandl/eval-async-job-queue)) 509 #+end_src 510 ** Ahead-of-time nativecomp 511 After just installing this Emacs config, things can be a little stuttery. We can speed this up by telling Emacs to nativecomp everything. 512 513 Here is a simple convenience function to compile everything in this config, including all packages installed with ~elpaca~: 514 #+begin_src emacs-lisp 515 (defun ethandl/nativecomp-aot () 516 (interactive) 517 (native-compile-async user-emacs-directory t)) 518 #+end_src 519 520 We add this to the ~elpaca-after-init-hook~ so that everything is compiled after initial installation. Any subsequent compilation can occur at runtime. 521 #+begin_src emacs-lisp 522 (add-hook 'elpaca-after-init-hook 523 #'ethandl/nativecomp-aot) 524 #+end_src 525 ** Indenting without tabs 526 Tabs make your code unreadable on editors with different tab settings if used incorrectly. 527 Unfortunately Emacs does this incorrectly by default, so I will disable this behaviour: 528 #+begin_src emacs-lisp 529 (indent-tabs-mode -1) 530 (setq-default indent-tabs-mode -1) 531 #+end_src 532 ** Autosave & backup directories 533 By default, Emacs puts backup files alongside regular files in the format of ~filename~~. 534 This clutters up directories with extra files, and can be problematic when working in Git repos or shared spaces. 535 536 Emacs additionally puts autosave files in the format of ~.#filename~. 537 Whilst these are less intrusive and disappear upon saving, it is still worth putting them in a dedicated directory. 538 539 First defining our dedicated directories for both of these files: 540 #+begin_src emacs-lisp 541 (defvar ethandl/backup-dir 542 (expand-file-name "backups/" user-emacs-directory)) 543 (defvar ethandl/autosave-dir 544 (expand-file-name "autosaves/" user-emacs-directory)) 545 #+end_src 546 Now we ensure the directories exist: 547 #+begin_src emacs-lisp 548 (unless (file-directory-p ethandl/backup-dir) 549 (make-directory ethandl/backup-dir t)) 550 (unless (file-directory-p ethandl/autosave-dir) 551 (make-directory ethandl/autosave-dir t)) 552 #+end_src 553 Finally we set the appropriate variables so that these directories are used: 554 #+begin_src emacs-lisp 555 (setq backup-directory-alist `(("." . ,ethandl/backup-dir))) 556 (setq auto-save-file-name-transforms 557 `((".*" ,ethandl/autosave-dir t))) 558 #+end_src 559 * Org mode 560 ** Section numbering 561 I like my sections numbered: 562 #+begin_src emacs-lisp 563 (add-hook 'org-mode-hook #'org-num-mode) 564 #+end_src 565 ** Better emphasis rendering 566 Emphasis markers (/italics/, *bold*, _underline_, ~verbatim~, =code=, and +strikethrough+) all render with the markers (~/~, ~*~, ~_~, ~~~, ~=~, and ~+~) included in the text by default. 567 I think that these markers work better when they aren't rendered: 568 #+begin_src emacs-lisp 569 (setq org-hide-emphasis-markers t) 570 #+end_src 571 ** Pretty appearance 572 I know that I said that I would keep things minimal... 573 574 I lied: this package is just for beauty because Org is somewhat ugly by default. 575 Unfortunately, this package conflicts with ~visual-wrap-prefix-mode~, so we need to explicitly disable that for org mode with a hook function. 576 #+begin_src emacs-lisp 577 (elpaca 'org-modern 578 (require 'org-modern) 579 (add-hook 'org-mode-hook #'org-modern-mode) 580 (add-hook 'org-agenda-finalize-hook #'org-modern-agenda) 581 (add-hook 'org-mode-hook (lambda () (visual-wrap-prefix-mode 0)))) 582 #+end_src 583 ** \(\text{\LaTeX}\) rendering 584 We are already secretly using a buggy Org branch to get \(\text{\LaTeX}\) to render correctly and automagically. 585 This is not ideal, but vanilla Org doesn't support live \(\text{\LaTeX}\) rendering and is slow. It's a tradeoff... 586 587 To enable this rendering automatically, as well as a live preview while writing: 588 #+begin_src emacs-lisp 589 (add-hook 'org-mode-hook #'org-latex-preview-mode) 590 (setq org-latex-preview-mode-display-live t) 591 #+end_src 592 593 By default, ~org-latex-preview-mode~ only renders \(\text{\LaTeX}\) in the visible regions, which is problematic for documents like this that start in ~overview~ on startup. 594 This can be fixed by rendering all \(\text{\LaTeX}\) in the buffer ahead of time when loading the buffer, this may be intensive for buffers with a lot of \(\text{\LaTeX}\), but at least this rewrite of ~org-latex-preview~ is asyncronous. 595 #+begin_src emacs-lisp 596 (add-hook 'org-mode-hook (lambda () (org-latex-preview 'buffer))) 597 #+end_src 598 599 By default, the \(\text{\LaTeX}\) previews are displayed with PNG for speed, but I will change this to use SVG for display sexiness: 600 #+begin_src emacs-lisp 601 (setq org-latex-preview-process-default 'dvisvgm) 602 #+end_src 603 ** Start with images 604 By default, Org will wait until ~org-link-preview~ is run to display images. The following sets images to automatically load on file opening: 605 #+begin_src emacs-lisp 606 (setq org-startup-with-inline-images t) 607 #+end_src 608 609 If we want images generated by the output of ~org-babel~ to automatically render, we need to add the following to the hook: 610 #+begin_src emacs-lisp 611 (add-hook 'org-babel-after-execute-hook #'org-link-preview-region) 612 #+end_src 613 This will save a ~C-c C-x C-v~ invocation every time we want to display newly generated images. 614 * LaTeX 615 \(\text{\LaTeX}\) takes up a large part of my work, so I would like to have a nice \(\text{\LaTeX}\) editing environment. 616 ** PDF Viewing 617 Whilst Emacs can view PDFs out of the box (wow), it rasterises the PDFs to something that is quite low resolution and doesn't look the best on all displays and is quite slow. 618 619 The ~pdf-tools~ package allows for much better PDF rendering: 620 #+begin_src emacs-lisp 621 (elpaca pdf-tools 622 (require 'pdf-tools) 623 (pdf-tools-install t t) 624 (add-to-list 'pdf-tools-enabled-modes #'pdf-view-midnight-minor-mode) 625 (add-to-list 'pdf-tools-enabled-modes #'auto-revert-mode)) 626 #+end_src 627 * The programming environment 628 This will be perpetually incomplete, my config cannot support /every/ language, nor do I really need it to. 629 630 The selection of languages may seem odd, but they are just what I have been bothered to put the effort into to-date. If I stop using one and it breaks, I will delete it rather than fix it until I need it again. 631 ** Org babel 632 In addition to being a useful Markdown alternative, Org mode can also serve as a literate programming environment and (shitty) Jupyter replacement. 633 634 The intended configuration for ~org-babel~ involves defining your list of languages to support all at once using the ~org-babel-do-load-languages~ function. 635 To allow for the language list (contained in ~ethandl/org-babel-langs~) to be defined dynamically (even in environment), the following function is defined: 636 #+begin_src emacs-lisp 637 (setq ethandl/org-babel-langs nil) 638 (defun ethandl/org-babel-load-langs () 639 (org-babel-do-load-languages 640 'org-babel-load-languages 641 ethandl/org-babel-langs)) 642 #+end_src 643 This list is then evaluated dynamically every time an Org document is opened through the ~org-mode-hook~: 644 #+begin_src emacs-lisp 645 (add-hook 'org-mode-hook #'ethandl/org-babel-load-langs) 646 #+end_src 647 ** The ~vterm~ terminal emulator 648 To make emacs a useful development environment, we need a good terminal. 649 In addition to using separate (good) terminals, we can use the cromulent ~vterm~ package: 650 #+begin_src emacs-lisp 651 (elpaca 'vterm) 652 #+end_src 653 ** Treesitter grammar async install 654 Treesitter grammar installation is slow and blocks up the emacs process. The following allows for asyncronous treesitter grammar installation on separate processes: 655 #+begin_src emacs-lisp 656 (defun ethandl/treesit-install-async (lang) 657 (unless (treesit-language-available-p lang) 658 (ethandl/async-job-queue-add 659 `(treesit-install-language-grammar ',lang) 660 "\\`treesit-language-source-alist\\'"))) 661 #+end_src 662 ** Markdown 663 For markdown, I will use ~markdown-ts-mode~, which utilises treesitter to provide highlighting for markdown. 664 To use this mode, we need to install the treesitter grammar(s): 665 #+begin_src emacs-lisp 666 (add-to-list 'treesit-language-source-alist 667 '(markdown "https://github.com/tree-sitter-grammars/tree-sitter-markdown" 668 "split_parser" 669 "tree-sitter-markdown/src")) 670 (add-to-list 'treesit-language-source-alist 671 '(markdown-inline "https://github.com/tree-sitter-grammars/tree-sitter-markdown" 672 "split_parser" 673 "tree-sitter-markdown-inline/src")) 674 675 (ethandl/treesit-install-async 'markdown) 676 (ethandl/treesit-install-async 'markdown-inline) 677 #+end_src 678 ** Emacs Lisp 679 Not much needs to be done here. 680 For verbosity, I will specify that we want Org babel to be able to evaluate elisp ~src~ blocks: 681 #+begin_src emacs-lisp 682 (add-to-list 'ethandl/org-babel-langs '(emacs-lisp . t)) 683 #+end_src 684 ** Python 685 There are two python modes installed by default with Emacs. 686 From my (admittedly limited) experience, ~python-ts-mode~ tends to have better syntax highlighting due to it having a better AST. 687 #+begin_src emacs-lisp 688 (add-to-list 'treesit-language-source-alist 689 '(python "https://github.com/tree-sitter/tree-sitter-python")) 690 691 (add-to-list 'major-mode-remap-alist 692 '(python-mode . python-ts-mode)) 693 694 (ethandl/treesit-install-async 'python) 695 696 (add-to-list 'ethandl/org-babel-langs '(python . t)) 697 #+end_src 698 699 For python, I like to have indentation visible with ~indent-bars~, 700 #+begin_src emacs-lisp 701 (add-hook 'python-mode-hook #'indent-bars-mode) 702 (add-hook 'python-ts-mode-hook #'indent-bars-mode) 703 #+end_src 704 A simple configuration for treesit-python is given on the github repo for ~indent-bars~: 705 #+begin_src emacs-lisp 706 (setq indent-bars-treesit-ignore-blank-lines-types '("module")) 707 (add-to-list 'ethandl/indent-bars-treesit-scope 708 '(python function_definition class_definition for_statement 709 if_statement with_statement while_statement)) 710 #+end_src 711 ** Powershell 712 I don't like or use Powershell, but seeing as I sometimes operate on Windows (and even have a sample in this config for TRAMP compatability), it /could/ be nice to have. 713 714 Due to my general distaste for all things Microslop, this may disappear if I deem it too much effort to keep around. That said, here is a mode to use when working with Powershell (the slowest shell known to mankind). 715 #+begin_src emacs-lisp 716 (elpaca 'powershell) 717 #+end_src 718 * Tramp 719 We all love tramp. Tramp is great. 720 ** Windows remote support 721 Disclaimer: This isn't my solution, though I got part of the way there are just so many reasons why Windows is broken and shit. I borrowed heavily on the PowerShell and ~sshw~ definition from [[https://emacs.stackexchange.com/a/85533][this stackexchange post]]. 722 723 Ensure first that the Windows remote has been de-crappified as much as possible, with Git and hence ~bash~ installed, and the OpenSSH Server feature enabled with the service started. 724 Then run the following in a PowerShell prompt on the remote to enable ~bash~ as the SSH shell (if it isn't already): 725 #+begin_src powershell 726 New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Program Files\Git\bin\bash.exe" -PropertyType String -Force 727 New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShellCommandOption -Value "-c" -PropertyType String -Force 728 #+end_src 729 730 Now that the remote is set up, we can use the following tramp method: 731 #+begin_src emacs-lisp 732 (require 'tramp) 733 (add-to-list 734 'tramp-methods 735 '("sshw" (tramp-login-program "ssh") 736 (tramp-login-args 737 (("-l" "%u") ("-p" "%p") ("%c") ("-e" "none") ("-T") 738 ("-o" "RemoteCommand=\"%l\"") ("%h"))) 739 (tramp-async-args (("-q"))) (tramp-remote-shell "/bin/sh") 740 (tramp-remote-shell-login ("-l")) (tramp-remote-shell-args ("-c")))) 741 #+end_src 742 * Platform specifics 743 I have structured the following sections such that each specific platform is tangled only if it is applicable. 744 Tangled outputs are saved to ~config-TARG.el~ where ~TARG~ is a property of your platform. 745 746 Any tangled outputs are automatically included in the final subsection of this section. 747 748 Generally only small deviations from the main config should be put in here. 749 This is the place to check if you're getting any strange bugs or different behaviour on different platforms. 750 ** MacOS 751 *** The menu bar 752 Because the menu bar exists regardless on MacOS, I like to keep ~menu-bar-mode~ enabled. 753 #+begin_src emacs-lisp :tangle (if (eq system-type 'darwin) "platform-config/darwin.el" "no") 754 (menu-bar-mode 1) 755 #+end_src 756 ** Work PC 757 My work PC has a lower resolution screen, so I like to have smaller fonts so that they take up less screen space. 758 #+begin_src emacs-lisp :tangle (if (equal system-name "lobotomite") "platform-config/work-system.el" "no") 759 (setq ethandl/org-default-font-height 110) 760 (ethandl/org-set-font-vars) 761 #+end_src 762 ** Windows 763 Windows is a very feculant platform to deal with, as almost nothing works as you would expect. 764 765 Firstly, there is no ~nativecomp~, so we have to redefine the function to be a no-op or everything will break: 766 #+begin_src emacs-lisp :tangle (if (eq system-type 'windows-nt) "platform-config/windows.el" "no") 767 (defun ethandl/nativecomp-aot () 768 (interactive) 769 (lwarn '(windows-is-shit) :debug "Nativecomp not supported on Windows NT. Use a real OS")) 770 #+end_src 771 ** Emacs <31 772 There are a few assumptions that are made in this configuration that do not hold for versions of Emacs less than 31. 773 *** ~markdown-ts-mode~ 774 ~markdown-ts-mode~ is only shipped with Emacs31 and newer, so we need to install it on older versions: 775 #+begin_src emacs-lisp :tangle (if (< (string-to-number emacs-version) 31) "platform-config/old-emacs.el" "no") 776 (elpaca markdown-ts-mode 777 (require 'markdown-ts-mode) 778 (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode))) 779 #+end_src 780 ** Including the platforms 781 Finally, we include any tangled platforms: 782 #+begin_src emacs-lisp 783 (dolist (file (directory-files ethandl/platform-config-dir t "\\.el$")) 784 (load file)) 785 #+end_src