commit ba9fbf6795f35b5cce4f85aebf461342c891156e
parent 7494b38f8f9bf9441a80347419453d44424e41a2
Author: Ethan Long <ethandavidlong@gmail.com>
Date: Fri, 19 Jun 2026 12:54:35 +1000
Complete re-write of the config
I am now much more happy with the general structure. This new config
should have less external dependencies by the time I'm done tinkering.
Diffstat:
| M | config.org | | | 1037 | ++++++++++++++++++++++++++----------------------------------------------------- |
| M | custom.el | | | 13 | +++++++++++++ |
| M | early-init.el | | | 3 | +++ |
| M | init.el | | | 57 | +++++++++++++++++++++++++++------------------------------ |
4 files changed, 386 insertions(+), 724 deletions(-)
diff --git a/config.org b/config.org
@@ -1,408 +1,36 @@
-#+Startup: overview
-#+Title:My emacs config
-#+Author:Ethan Long
-* What is this document/config/file?
-This is a literate config for emacs, using the built-in ~org~ package for Emacs.
-* Elpaca
-The Elpaca package manager is an alternative to ~use-package~ that works asynchronously in the background. Beyond the initial set-up, it is faster and more reliable than using ~package.el~.
+#+title: Fast (il)literate emacs config built on ~elpaca~
+#+author: Ethan Long
+#+startup: overview
-Its asynchronous nature is also what makes package initialisation a little more involved; packages that need to be set up after others must explicitly specify this. This is mostly a non-issue for my setup, and I try to avoid issues with variable declarations by using ~use-package~ whenever possible.
+* Overview of this config
+This is my (subjectively) simplistic literate config for Emacs.
+I aim to keep this minimal, and to use as few large dependencies as possible.
-Alternatively, if you already have a setup with extensive use of ~use-package~ with some form of synchronous package manager, then you might want to use the ~elpaca-wait~ function, which forces it to complete all package evaluation up until that point. Using this function too often will defeat the purpose of elpaca, as the asynchronicity is exactly what makes it faster than alternatives.
+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.
-Another thing of note, if you ever need to use ~after-init-hook~, then make sure to use ~elpaca-after-init-hook~ instead, as the regular ~after-init-hook~ can be triggered before Elpaca has finished its evaluation unless you use the afforementioned ~elpaca-wait~ function.
+The reader may disagree on the minimalism of this configuration, which is rather subjective.
+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.
-Elpaca is installed in =init.el=, as we need to install the latest version of org before we enter here.
-** ~init.el~
-#+include: "./init.el" src elisp :tangle no
-* Configuration functions
-Here I define all of my helper functions in the one place just for convenience. Unfortunately this can have the effect of breaking up some of my configuration, especially for things like ~org~.
-** General programming configuration
-Firstly, here are some convenience functions that I find useful for general programming, making the editor act the way that I want it to.
-*** Auto buffer formatting
-I quite like how the LSP (or some other kind of parser/prettifier) can automatically format the buffer. In other editors, I typically like to have this happen whenever I save.
+As an added bonus of being more minimal, this config should also be more portable across systems (I hope).
-This functionality is especially useful for languages like Rust or Haskell where the formatting is handled quite well by the LSP.
-#+begin_src emacs-lisp
- (defun ethandl/format-buffer ()
- (when (and eglot--managed-mode
- (eglot-server-capable :documentFormattingProvider))
- (eglot-format-buffer)))
-#+end_src
-In the future, I may extend this function to utilise prettifiers, but I don't really have the need to at the moment.
-*** Line numbers
-Line numbering in emacs is still a bit of a third class citizen; the primary reason I say this is because the width of the line number column is prone to change for seemingly no good reason. Example: scrolling any file below 80 lines will increase the size to accomodate line numbers greater than 1oo for no reason; the same thing happens around 850 lines for numbers greater than 1000.
-
-I find that currently I am mostly satisfied by using the variable ~display-line-numbers-grow-only~. This effectively stops most of the annoyance around the width changing when scrolling up and down.
-
-Following is a function that sets up the requisite variables for the behaviour that I like:
-#+begin_src emacs-lisp
- (defun ethandl/config-line-numbers ()
- ;; Set line numbers to relative
- (setq display-line-numbers 'relative
- display-line-numbers-width 2
- display-line-numbers-current-absolute t
- display-line-numbers-grow-only t))
-#+end_src
-I would recommend adding this to a hook, which I have done in [[*General Emacs/Editor settings]], specifically the ~hook~ section of the ~use-package~ declaration.
-*** Code fonts
-For convenience, I have defined a helper function that can be called to configure the code fonts.
-In typical one-frame setups not using the daemon functionality of emacs, you only need to use this once with the ~default~ parameter set as ~t~.
-
-With daemon usage, it is not so simple. When using emacs as a daemon, emacs will initially read over this config file in terminal mode, which will stop it from setting the fonts correctly; the only solution to this is to set this function to run every time you create a new frame. I have this exact functionality set up in [[*Font & Ligature Definitions]].
-#+begin_src emacs-lisp
- (defun ethandl/set-fonts (fontname &optional default)
- (set-face-attribute 'default default :font fontname)
- (set-face-attribute 'font-lock-comment-face default :slant 'italic)
- (set-face-attribute 'font-lock-keyword-face default :slant 'italic)
- (set-face-attribute 'mode-line default :font fontname))
-#+end_src
-*** Tree sitter grammar compilation
-I wanted a function that, given an ~alist~ of tree sitter grammars and their associated sources, would clone, build, and set up those grammars.
-This function avoids recompiling grammars that are already installed.
-#+begin_src emacs-lisp
- (defun ethandl/install-treesit-grammars (lang-alist)
- (require 'cl-lib)
- (when (treesit-available-p)
- (mapc #'treesit-install-language-grammar
- (cl-remove-if #'treesit-language-available-p (mapcar #'car lang-alist)))))
-#+end_src
-I use this function in [[*Tree Sitter Setup]].
-** Org mode configuration
-Org mode is quite ugly out of the box, at least in my opinion. I use this config, which was inspired by a config that I found online.
-
-I have used the following guides in informing this setup; it is largely based on the work of Diego Zamboni:
- * [[https://zzamboni.org/post/beautifying-org-mode-in-emacs/][Beautifying Org Mode in Emacs by Diego Zamboni]]
- * [[https://lepisma.xyz/2017/10/28/ricing-org-mode/][Ricing up Org Mode]]
-
-I think that the latter guide is by far the prettier, more /"aesthetically pleasing"/ setup deserving of Unixporn or something, but personally I like it slightly less minimalistic.
-
-*** Basic behavioral configuration
-For starters, I define the function ~ethandl/org-base~ as the function to set up org in the way that I generally like. Of primary concern: it sets org to indent on section depth, sets the font to be variable pitch, and turns on visual lines for wrapped lines.
-#+begin_src emacs-lisp
- (defun ethandl/org-base ()
- (require 'org)
- (org-indent-mode t)
- (variable-pitch-mode t)
- (visual-line-mode t)
- (setq org-src-fontify-natively t
- org-src-tab-acts-natively t
- org-hide-leading-stars nil
- org-indent-mode-turns-on-hiding-stars nil
- org-hide-emphasis-markers t)
- (font-lock-add-keywords 'org-mode ;; HACK to fix the catppuccin theme
- '(("^ *\\([-]\\) "
- (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))))
-#+end_src
-This function should be called when you are setting up org mode, personally I do it when I'm [[*
-*** Fonts
-The following function is the config for org to set all the fonts. It is still a bit rough around the edges, I'd like to programatically set the heading sizes.
-#+begin_src emacs-lisp
- (defun ethandl/org-font ()
- (require 'org)
- (let*
- ;; Variable bindings
- ((variable-font (if (x-list-fonts "P052")
- '(:font "P052")
- '(:family "Serif")))
- (mono-font (if (x-list-fonts ethandl/code-font)
- `(:font ,ethandl/code-font)
- '(:family "Mono")))
- (base-font-color (face-foreground 'default nil 'default))
- (headline `(:inherit default :weight bold :foreground ,base-font-color))
- (body `(:inherit default :weight thin :foreground ,base-font-color :height 200)) ;; 20 pt
- (code `(:weight regular :height 0.8 :inherit t)))
-
- ;; Actual function body
- (custom-theme-set-faces
- 'user
- `(variable-pitch ((t (,@variable-font ,@body))) t "Variable width font")
- `(fixed-pitch ((t (,@mono-font ,@code))) t "Fixed width font")
- `(org-level-8 ((t (,@variable-font ,@headline :height 1.1))) t "Heading level 8")
- `(org-level-7 ((t (,@variable-font ,@headline :height 1.2))) t "Heading level 7")
- `(org-level-6 ((t (,@variable-font ,@headline :height 1.3))) t "Heading level 6")
- `(org-level-5 ((t (,@variable-font ,@headline :height 1.4))) t "Heading level 5")
- `(org-level-4 ((t (,@variable-font ,@headline :height 1.5))) t "Heading level 4")
- `(org-level-3 ((t (,@variable-font ,@headline :height 1.6))) t "Heading level 3")
- `(org-level-2 ((t (,@variable-font ,@headline :height 1.7))) t "Heading level 2")
- `(org-level-1 ((t (,@variable-font ,@headline :height 1.8))) t "Heading level 1")
- `(org-document-title ((t (,@variable-font ,@headline :height 2.0 :underline nil))) t "Document title")
- '(org-block ((t (:inherit fixed-pitch))) t "Verbatim block")
- '(org-code ((t (:inherit (shadow fixed-pitch)))) t "Code block")
- '(org-document-info ((t (:foreground "dark orange"))) t "Document info")
- '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))) t "Document info keys")
- '(org-indent ((t (:inherit (org-hide fixed-pitch)))) t "Trailing Org Indentation")
- '(org-link ((t (:foreground "royal blue" :underline t))) t "Links")
- '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))) t "Metadata")
- '(org-property-value ((t (:inherit fixed-pitch))) t "Org value")
- '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))) t "Org keyword")
- '(org-table ((t (:inherit fixed-pitch :foreground "#83a598"))) t "Org table")
- '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))) t "Org tag")
- '(org-verbatim ((t (:inherit (shadow fixed-pitch)))) t "Verbatim"))))
-#+end_src
-*** LaTeX
-Some configuration for the LaTeX that appears in Org mode. We want to make sure that it's SVG, and also if we have the ability to, we should auto-render it.
-#+begin_src emacs-lisp
- (defun ethandl/org-latex ()
- (setq org-preview-latex-default-process 'dvisvgm))
-#+end_src
-** Hooks
-*** Before saving
-I like to format my buffers every time that I save in other editors, for now this function just calls my other ~format-buffer~ function. Any other action that you might like to do whenever you save can be put here too.
-#+begin_src emacs-lisp
- (defun ethandl/before-save-hook ()
- (ethandl/format-buffer))
-#+end_src
-A good example of something that you might want to put here would be a ~git~ commit and push every time that you save within an Overleaf git project. This can be useful for when you're trying to avoid actually using the god-forsaken web editor of Overleaf.
-* Theme
-There are many themes for Emacs that you can use. It can be nice to spice things up every once in a while.
-One day I might make my own theme, only if I get too lost in the ricing.
-*** Catppuccin
-I use the Catppuccin theme normally.
-Unfortunately, this theme has some issues and I'm not sure whether they will ever be fixed; I have the fixes all here.
-#+begin_src emacs-lisp
- ;; Catppuccin Mocha theme
- (use-package catppuccin-theme
- :config
- (load-theme 'catppuccin t)
- (setq catppuccin-flavor 'mocha)
- ;;(setq catppuccin-flavor 'latte)
- (catppuccin-reload)
- (add-to-list 'org-src-block-faces
- (list "" (list :foreground (catppuccin-get-color 'green))))
- ;; HACK FIX: https://github.com/catppuccin/emacs/issues/61
- (defun ctp/text-org-blocks ()
- (face-remap-add-relative 'org-block (list :foreground (catppuccin-get-color 'text))))
- ;; FIXME: Move this hook to the Org mode configuration so that it is obvious that we are adding this hook.
- (add-hook 'org-mode-hook #'ctp/text-org-blocks)
- (setq pdf-view-midnight-colors (cons (catppuccin-get-color 'text)
- (catppuccin-get-color 'base))))
-#+end_src
-*** Gruber darker
-Sometimes I like to use Gruber Darker, following is an example snippet that is not tangled into my actual config.
-#+begin_src emacs-lisp :tangle no
- (use-package gruber-darker-theme
- :config
- (load-theme 'gruber-darker t))
-#+end_src
-*** Emacs titlebar colour
-FIXME: Move all configuration of the titlebar colour here for all versions of emacs like the mac port and gtk+.
-It's nice to have the titlebar blend for the NS port of emacs:
-#+begin_src emacs-lisp
- (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
- (add-to-list 'default-frame-alist '(ns-appearance . dark))
-#+end_src
-#+begin_src emacs-lisp
- (add-to-list 'default-frame-alist '(mac-transparent-titlebar . t))
-#+end_src
-
-* Font & Ligature Definitions
-FIXME: Bring in the ~org~ and variable font definitions here!
-*** Code fonts
-Here I pick the code font and declare it as the variable ~ethandl/code-font~.
-#+begin_src emacs-lisp
- (setq ethandl/code-font "Codelia Ligatures-16")
-#+end_src
-Currently I use Codelia, but I also recommend Comic Code, Julia Mono, or Victor Mono. In the end this is entirely personal preference.
-Some more examples:
-#+begin_src emacs-lisp :tangle no
- (setq ethandl/code-font "Comic Code Ligatures-16")
- (setq ethandl/code-font "JuliaMono-16")
- (setq ethandl/code-font "Victor Mono-16")
-#+end_src
-
-Now we use [[*Code fonts][the font helper function that I defined earlier]] to actually set the fonts.
-#+begin_src emacs-lisp
- ;; Set default font
- (ethandl/set-fonts ethandl/code-font t)
- (add-hook 'elpaca-after-init-hook (apply-partially #'ethandl/set-fonts ethandl/code-font))
-#+end_src
-
-We also want this to run after we make a new frame, in case we are running emacs as a daemon.
-#+begin_src emacs-lisp
- (add-hook 'after-make-frame-functions (apply-partially #'ethandl/set-fonts ethandl/code-font))
- (add-hook 'after-make-frame-functions
- #'(lambda (frame) (run-with-timer 1 nil #'meow--prepare-face)))
-#+end_src
-**** Font supported ligatures for ~ligature.el~
-Each font has its own set of supported ligatures.
-Unfortunately on Linux systems you must use ~ligature.el~ to set ligatures, and this requires manually defining which ligatures are supported. This is either really cool or a pain in the ass.
-
-Here are some examples:
-#+begin_src emacs-lisp
- (setq
- comic-code-ligs
- '(("-" (rx (+ "-"))) ("-" (rx (* "-") ">"))
- ("+" (rx (+ "+"))) ("<" (rx (+ "=")))
- ("<" (rx (+ "=") ">")) ("<" (rx (+ "~")))
- ("<" (rx (+ "~") ">")) ("<" (rx "!" (+ "-")))
- ("<" (rx (+ "-"))) ("<" (rx (+ "-") ">")) ("<" (rx "|"))
- (">" (rx (+ "="))) (">" (rx ">" (+ "=")))
- (">" (rx ">" (+ "=") ">")) (">" (rx (+ "-")))
- (">" (rx (+ "-") "<")) ("~" (rx (+ "~")))
- ("~" (rx (+ "~") ">")) ("=" (rx (* "=") ">"))
- ("=" (rx (+ (or ">" "<" "|" "/" "~" ":" "!" "="))))
- "!=" "!==" "[|" "|]" "{|" "|}" "|>" "||" "&&")
-
- victor-mono-ligs
- '("</" "</>" "/>" "~-" "-~" "~@" "<~" "<~>" "<~~" "~>" "~~" "~~>" ">=" "<="
- "<!--" "##" "###" "####" "|-" "-|" "|->" "<-|" ">-|" "|-<" "|=" "|=>"
- ">-" "<-" "<--" "-->" "->" "-<" ">->" ">>-" "<<-" "<->" "->>" "-<<" "<-<"
- "==>" "=>" "=/=" "!==" "!=" "<==" ">>=" "=>>" ">=>" "<=>" "<=<"
- "=<=" "=>=" "<<=" "=<<" ".-" ".=" "=:=" "=!=" "==" "===" "::" ":=" ":>"
- ":<" ">:" ";;" "<|" "<|>" "|>" "<>" "<$" "<$>" "$>" "<+"
- "<+>" "+>" "?=" "/=" "/==" "/\\" "\\/" "__" "&&" "++" "+++")
-
- julia-mono-ligs
- '("->" "=>" "|>" "<|" "::" "<--" "-->" "<-->"
- ("=" (rx (+ "=")))))
-#+end_src
-In my configuration, I declare the variable ~ethandl/ligs~ to use in the ~ligature.el~ setup.
-#+begin_src emacs-lisp
- (setq ethandl/ligs julia-mono-ligs)
-#+end_src
-
-All of this configuration is unnecessary if you don't want ligatures, or if you are using ~emacs-mac~ by Mitsuharu Yamamoto.
-* Custom Keybinds
-Here I have made a little section for setting up some additional keybinds.
-** Vim-style window navigation
-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~.
-
-This is mostly just the work of [[https://dev.to/rajasegar/vim-style-repeatable-key-bindings-for-navigating-windows-in-emacs-5c4l][Rajasegar Chandran]].
-#+begin_src emacs-lisp
- (global-set-key (kbd "C-x w h") 'windmove-left)
- (global-set-key (kbd "C-x w j") 'windmove-down)
- (global-set-key (kbd "C-x w k") 'windmove-up)
- (global-set-key (kbd "C-x w l") 'windmove-right)
- (repeat-mode t)
- (defvar-keymap windmove-repeat-map
- :repeat t
- "h" #'windmove-left
- "j" #'windmove-down
- "k" #'windmove-up
- "l" #'windmove-right)
-#+end_src
-** Super as meta
-I like having the super key also function as a meta key, as I frequently switch between MacOS and Linux with different window managers and keyboards.
-#+begin_src emacs-lisp
- (setq x-super-keysym 'meta)
-#+end_src
-* General Emacs/Editor settings
-FIXME: Continue from here (remembering that I have other FIXMEs above)
+This config has the following explicit package dependencies:
+ - Org: Karthinks build, required for LaTeX previews. Declared in ~init.el~.
+ - Meow: For modal keybinds.
+ - ~org-modern~: For a nicer looking Org mode, only 900 lines of elisp.
+ - ~org-modern-indent~: To fix the indented codeblocks with ~org-modern~.
+ - ~ultra-scroll~: For better smooth scrolling and faster general text scrolling when navigating by keyboard.
-FIXME: Bring as many of these changes into standalone functions as possible to reduce fragmentation.
-We can use ~use-package~ to configure emacs at startup.
-#+begin_src emacs-lisp
- ;; Emacs init config
- (use-package emacs
- :ensure nil
- :hook
- (prog-mode . display-line-numbers-mode)
- (display-line-numbers-mode . ethandl/config-line-numbers)
- (before-save . ethandl/before-save-hook)
- :init
- ;; Get rid of default crud
- (setq inhibit-startup-screen t)
- (if (eq system-type 'darwin)
- (menu-bar-mode 1)
- (menu-bar-mode 0))
- (tool-bar-mode -1)
- (setq-default tool-bar-mode -1)
- (add-to-list 'default-frame-alist '(tool-bar-lines . 0))
- ;; (add-to-list 'after-make-frame-functions 'ethandl/tool-bar-padding-remove)
- (when (fboundp 'scroll-bar-mode)
- (scroll-bar-mode 0))
- ;; Note, the following two are already set in early_init.el,
- ;; so the initial frame has good resizing
- (setq frame-resize-pixelwise t)
- (setq window-resize-pixelwise t)
- ;; Modeline
- (line-number-mode 1) ;; Line no.
- (column-number-mode 1) ;; Col. no.
- ;; Indentation
- (setq-default indent-tabs-mode nil)
- (setq-default tab-width 4)
- ;; windmove wraps around
- (setq windmove-wrap-around t)
- ;; Get rid of the bell
- (setq ring-bell-function 'ignore)
- ;; Fuck Control+Scroll to zoom, that's terrible and seems to be bound by something in emacs-plus?
- (global-unset-key (kbd "<C-wheel-up>"))
- (global-unset-key (kbd "<C-wheel-down>"))
- ;; Autosave files need to piss off
- (setq backup-directory-alist
- `((".*" . ,temporary-file-directory)))
- (setq auto-save-file-name-transforms
- `((".*" ,temporary-file-directory t)))
- ;; Ensure that the command key is the emacs meta key
- (when (eq system-type 'darwin)
- (setq mac-option-modifier nil
- mac-command-modifier 'meta))
- ;; Emacs frame title format default is bad:
- (setq frame-title-format "%b"))
-#+end_src
-* Packages
-** Ivy and Counsel (Better Text Navigation)
-[[https://github.com/abo-abo/swiper][Ivy and Counsel]] are replacements for the default ~find-file~ and =M-x= menus.
-#+begin_src emacs-lisp
- ;; ivy mode instead of ido mode:
- (use-package ivy
- :hook (elpaca-after-init . ivy-mode))
- ;; counsel extends ivy:
- (use-package counsel
- :hook (elpaca-after-init . counsel-mode))
-#+end_src
-** Ligature.el / Ligature set up
-Ligatures are native on =emacs-mac=, so we just call the built in helper function if we are using that. If not, we will use [[https://github.com/mickeynp/ligature.el][ligature.el]] to give ligatures with Harfbuzz.
-#+begin_src emacs-lisp
- ;; Font ligatures:
- (if (eq window-system 'mac)
- ;; We must be in emacs-mac
- (mac-auto-operator-composition-mode)
- ;; Else, we're in a vanilla emacs
- (use-package ligature
- :config
- (ligature-set-ligatures 'prog-mode ethandl/ligs)
- (global-ligature-mode t)))
-#+end_src
-** Rainbow Delimiters
-Rainbow delimiters are essential for working with LISP, and are just nice to have elsewhere. This package has apparently got a very small footprint, and I can't be arsed actually benchmarking anything in this emacs config so I trust them.
-#+begin_src emacs-lisp
- ;; Rainbow delimiters:
- (use-package rainbow-delimiters
- :hook (prog-mode . rainbow-delimiters-mode))
-#+end_src
-** Doom Modeline
-This modeline is supposedly sexier than the original, honestly I don't mind the original either, it's also very nice. If there's ever a reason to ditch this because of some bug or performance issue, etc. then don't be afraid to.
-#+begin_src emacs-lisp
- ;; Doom modeline:
- (use-package doom-modeline
- :hook (elpaca-after-init . doom-modeline-mode)
- :config
- ;; simpc-mode icon
- (push '(simpc-mode nerd-icons-sucicon "nf-custom-c"
- :face nerd-icons-blue)
- nerd-icons-mode-icon-alist))
-#+end_src
-** Which Key?
-Fix my ineptitude in this keyboard chord hell.
-#+begin_src emacs-lisp
- ;; Which key (this is the thing responsible for coming up with the minibuffer of keys that you can pick):
- (use-package which-key
- :init (which-key-mode)
- :diminish which-key-mode
- :config
- (setq which-key-idle-delay 0.3))
-#+end_src
-** Meow
-Meow takes some getting used to, but I want to try.
-The beauty is that I have full control explicitly over every binding in a really simple interface, and the rest of the binds are just emacs' default with some enhancement:
-#+begin_src emacs-lisp
- ;; meow :D
- (use-package meow
- :config
- ;; General meow config:
+There are a total of 6 packages installed by these dependencies.
+* Keybinds
+** Meow (modal editing)
+This package is very important to me, I have been poisoned by ~vi~ and rely on the modal editing model to function properly.
+If you're interested in my config function, see ~meow-setup~.
+I use mostly the recommended QWERTY binds given by the Meow devs:
+:meow-setup:
+#+begin_src emacs-lisp
+ (defun ethandl/meow-setup ()
(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
+ (setq meow-keypad-leader-dispatch "C-x")
(meow-motion-overwrite-define-key
'("j" . meow-next)
'("k" . meow-prev)
@@ -487,326 +115,347 @@ The beauty is that I have full control explicitly over every binding in a really
'("'" . repeat)
'(">" . indent-rigidly-right-to-tab-stop)
'("<" . indent-rigidly-left-to-tab-stop)
- '("<escape>" . ignore))
- (setq meow-keypad-leader-dispatch "C-x")
- (meow-global-mode t))
+ '("<escape>" . ignore)))
#+end_src
-** Corfu & Cape (Completion)
-Based completion. This is an overhaul of the autocomplete. Company mode sucks because when you resize a buffer with C-x-+ everything breaks.
-#+begin_src emacs-lisp
- ;; Cape
- (use-package cape)
+:end:
- ;; Completion & Modernisation
- (use-package corfu
- :after cape
- :hook
- (meow-insert-exit . corfu-quit)
- :custom
- (corfu-cycle t)
- (corfu-auto t)
- (corfu-preselect 'prompt)
- (corfu-scroll-margin 5)
- (completion-cycle-threshold 3)
- :init
- (global-corfu-mode))
-#+end_src
-** Scrolling Patches
-JDT makes a nice scroll package, this should be good regardless of the emacs build that you are using.
-#+begin_src emacs-lisp
- (use-package ultra-scroll
- :ensure `(ultra-scroll
- :host github
- :repo "jdtsmith/ultra-scroll")
- :init
- (setq scroll-conservatively 10
- scroll-margin 0)
- :config
- (ultra-scroll-mode 1))
-#+end_src
-** Exec path from shell (Fix Path issues)
-When running emacs as a daemon, we have a notable lack of things in our ~PATH~ because daemons are started in a minimal environment, at least on MacOS.
-This solution will increase startup time, but will fix any path issues for most shells. May need tinkering. This will also only be activated when using a daemon.
-#+begin_src emacs-lisp
- (use-package exec-path-from-shell
- :config
- (exec-path-from-shell-initialize))
-#+end_src
-** PDF-tools
-#+begin_src emacs-lisp
- (use-package pdf-tools
- :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode)
- :hook
- (pdf-view-mode . auto-revert-mode)
- (pdf-view-mode . pdf-view-midnight-minor-mode)
- :config
- (setq-default pdf-view-display-size 'fit-page)
- (setq pdf-annot-activate-created-annotations t))
-#+end_src
-** Emacs vterm
-In emacs' included packages, there are only 2 shells that are good to use: ~eshell~ and ~shell~.
-I have had poor experiences with ~term~, as it doesn't properly emulate all escape sequences and is a bit slow.
+#+begin_src emacs-lisp
+ (elpaca meow
+ (require 'meow)
+ (ethandl/meow-setup)
+ (meow-global-mode 1))
+#+end_src
+** Window navigation
+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~.
-The solution to this is the ~vterm~ package, which is a package written mostly in C with a much better terminal emulation. It is fast and more reliable.
-#+begin_src emacs-lisp
- (use-package vterm)
-#+end_src
-* Org Mode
-My org mode is based on [[https://orgmode.org/guide/Hyperlinks.html][a post by Diego Zamboni]]. The general idea is that we want headings to be bigger and more noticable than text, all regular text should be non-monospace, and all code should be in the regular monospace font.
-The org configuration uses a lot of custom functions, see the
-#+begin_src emacs-lisp
- (use-package org
- :ensure nil
- :hook
- (org-mode . ethandl/org-base)
- (org-mode . ethandl/org-font)
- (org-mode . ethandl/org-latex))
-#+end_src
-Other auxillary packages for Org mode:
-#+begin_src emacs-lisp
- (use-package org-superstar ;; Nicer bullet fonts
- :hook (org-mode . org-superstar-mode)
- :config (setq org-superstar-leading-bullet ?\s))
- (use-package org-autolist ;; Automatic bulleting
- :hook (org-mode . org-autolist-mode))
- (use-package org-download ;; Seamless image pasting
- :after org
- :defer nil
- :custom
- (org-download-method 'directory)
- (org-download-image-dir "pasted-images")
- (org-download-heading-lvl nil)
- (org-download-timestamp "%Y%m%d-%H%M%S_")
- (org-image-actual-width t)
- (org-download-screenshot-method "/opt/homebrew/bin/pngpaste %s")
- :bind
- ("M-p" . org-download-screenshot)
- :config
- (require 'org-download))
-#+end_src
-* Languages
-** LSP Setup
-LSP, the Language Server Protocol. Very nice for debugging code live as you write it. It depends on the language as to how useful this really is.
+This is mostly just the work of [[https://dev.to/rajasegar/vim-style-repeatable-key-bindings-for-navigating-windows-in-emacs-5c4l][Rajasegar Chandran]].
+#+begin_src emacs-lisp
+ (global-set-key (kbd "C-x w h") 'windmove-left)
+ (global-set-key (kbd "C-x w j") 'windmove-down)
+ (global-set-key (kbd "C-x w k") 'windmove-up)
+ (global-set-key (kbd "C-x w l") 'windmove-right)
+ (repeat-mode t)
+ (defvar-keymap windmove-repeat-map
+ :repeat t
+ "h" #'windmove-left
+ "j" #'windmove-down
+ "k" #'windmove-up
+ "l" #'windmove-right)
+#+end_src
+** Super as meta
+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.
-*Warning:* Some LSPs are godawful slow and can chew through CPU and memory. Be careful, tinker, and don't be afraid to disable the training wheels.
-We use the built-in package eglot (requires emacs 29) if possible as there seems to occasionally be issues with installing eglot.
-#+begin_src emacs-lisp
- (when (>= emacs-major-version 29)
- (use-package eglot
- :ensure nil))
- (use-package markdown-mode) ; needed for formatting the stuff in the eldoc buffers
-#+end_src
-** Tree Sitter Setup
-Since Emacs 29, we don't need to install tree sitter separately anymore, it comes with Emacs!
-#+begin_src emacs-lisp
- (setq treesit-language-source-alist
- '((bash "https://github.com/tree-sitter/tree-sitter-bash")
- (css "https://github.com/tree-sitter/tree-sitter-css")
- (go "https://github.com/tree-sitter/tree-sitter-go")
- (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
- (html "https://github.com/tree-sitter/tree-sitter-html")
- (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
- (json "https://github.com/tree-sitter/tree-sitter-json")
- (yaml "https://github.com/ikatyang/tree-sitter-yaml")
- (python "https://github.com/tree-sitter/tree-sitter-python")
- (toml "https://github.com/tree-sitter/tree-sitter-toml")
- (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
- (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
- (rust "https://github.com/tree-sitter/tree-sitter-rust")
- (java "https://github.com/tree-sitter/tree-sitter-java")
- ;;(haskell "https://github.com/tree-sitter/tree-sitter-haskell") ; There is no haskell-ts-mode yet
- ;;(ocaml "https://github.com/tree-sitter/tree-sitter-ocaml" "master" "ocaml/src") ; There is no ocaml-ts-mode
- (c "https://github.com/tree-sitter/tree-sitter-c")
- (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
- (zig "https://github.com/maxxnino/tree-sitter-zig")))
+The following works on Linux, it is untested on Windows, and does nothing on my MacOS build.
+#+begin_src emacs-lisp
+ (setq x-super-keysym 'meta)
+#+end_src
+* Emacs appearance
+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.
+** Menu bar removal
+The menu bar is /fine/ on MacOS or in an NS environment (see platform specific tweaks), but elsewhere it tends to add clutter.
+Begone.
+#+begin_src emacs-lisp
+ (menu-bar-mode 0)
+#+end_src
+** Tool bar removal
+Even worse than the menu bar: the tool bar.
+Simply a waste of space in my opinion.
+#+begin_src emacs-lisp
+ (tool-bar-mode 0)
+#+end_src
+** Scroll bar removal
+The scroll bar is unnecessary if you are navigating via keyboard, and takes up space.
+#+begin_src emacs-lisp
+ (scroll-bar-mode 0)
+#+end_src
+** Relative line numbers
+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.
+Emacs comes with a nice built-in mode that displays line numbers on the left-hand side of a buffer, much like in ~vi~.
+#+begin_src emacs-lisp
+ (setq display-line-numbers-type 'relative)
+ (add-hook 'prog-mode-hook #'display-line-numbers-mode)
+#+end_src
- ;; Fuck windows, this won't work on there and I don't care
- (ethandl/install-treesit-grammars treesit-language-source-alist)
+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.
+This can be fixed:
+#+begin_src emacs-lisp
+ (setq display-line-numbers-width-start 0)
+ (setq display-line-numbers-grow-only t)
#+end_src
-** Eldoc Setup
-I like to have eldoc appear as a box below where I'm typing, rather than having it appear in the minibuffer. This is done with =eldoc-box=:
+** Modeline customisation
+*** Disable line number
+The current line number is irrelevant to have on the modeline, given that we display the current line number in the buffer.
#+begin_src emacs-lisp
- (use-package eldoc-box
- :hook
- (eglot-managed-mode . eldoc-box-hover-mode))
+ (line-number-mode 0)
#+end_src
-** C
-I was using the C treesitter mode, but that mode is still based on C-mode and is incredibly slow for large files.
-#+begin_src emacs-lisp :tangle no
- (push '(c-mode . c-ts-mode) major-mode-remap-alist))
+*** Enable horizontal cursor position
+For some reason, this is not displayed by default in the Emacs modeline.
+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.
+#+begin_src emacs-lisp
+ (column-number-mode 1)
#+end_src
+** Fonts
+*** Code fonts
+These are the fonts used generally by the Emacs user interface, and in any buffer that doesn't use ~variable-pitch-mode~.
+
+I like the fonts created by Toshi Omagari, primarily Codelia and Comic code.
+As a fallback, I don't mind Monaco/Monego.
+#+begin_src emacs-lisp
+ (defun ethandl/font-exists (fname)
+ "Returns nil if the font FNAME does not exist, else returns the font"
+ (member fname (font-family-list)))
+
+ (defun ethandl/set-mono-font-vars (&optional name size)
+ "Set mono-font configuration variables
+
+ Use NAME as the font family and SIZE as the font size in points.
+ If NAME is nil, select the first available font from a predefined
+ list. If SIZE is nil, uses 12.
-Instead, I now use Alexey Kutepov's ~simp-c~ mode, which performs far better.
-#+begin_src emacs-lisp
- (use-package simpc-mode
- :ensure '(simpc-mode
- :host github
- :repo "rexim/simpc-mode")
- :config
- (push '(c-mode . simpc-mode) major-mode-remap-alist))
-#+end_src
-** Haskell
-This setup is just a combination of =haskell-mode= and the Haskell Language Server.
-Haskell mode is really good for the indenting and overall behaviour of the editor, and the Haskell language server is definitely there... (It can be slow, so can be turned off on larger projects)
-#+begin_src emacs-lisp
- (executable-find "haskell-language-server-wrapper")
- ;; Haskell setup
- (use-package haskell-mode)
- (use-package eglot
- :ensure nil
- :after eglot
- :hook (haskell-mode . eglot-ensure)
- :config
- (setq-default eglot-workplace-configuration
- '((haskell
- (plugin
- (stan
- (globalOn . :json-false))))))
- :custom
- (eglot-autoshutdown t)
- (eglot-confirm-server-initiated-edits nil))
-#+end_src
-NOTE: This is not using tree sitter, as there is no haskell-ts-mode yet or probably for a while into the future.
-** Rust
-Rust mode alongside LSP again. The rust LSP is very good as far as LSPs go, very helpful. Though sometimes it's better to compile and see the rustc errors as they tend to be more verbose.
-#+begin_src emacs-lisp
- (use-package eglot
- :ensure nil
- :after eglot
- :hook (rust-ts-mode . eglot-ensure)
- :config
- (add-to-list 'eglot-server-programs
- `(rust-mode . ("rust-analyzer" :initializationOptions
- (:procmacro (:enable t)))))
- (push '("\\.rs\\'" . rust-ts-mode) auto-mode-alist))
-#+end_src
-** Go
-This basic LSP setup is based on the golang guide: https://cs.opensource.google/go/x/tools/+/refs/tags/gopls/v0.14.2:gopls/doc/emacs.md
-#+begin_src emacs-lisp
- (push '("\\.go\\'" . go-ts-mode) auto-mode-alist)
- (push '("\\.mod\\'" . go-mod-ts-mode) auto-mode-alist)
- (defun golang/project-find-go-module (dir)
- (when-let ((root (locate-dominating-file dir "go.mod")))
- (cons 'go-module root)))
- (cl-defmethod project-root ((project (head go-module)))
- (cdr project))
+ Sets `ethandl/mono-font-name', `ethandl/mono-font-size', and
+ `ethandl/mono-font-string'."
+ (setq ethandl/mono-font-name
+ (or name
+ (seq-find #'ethandl/font-exists
+ '("Codelia Ligatures"
+ "Comic Code Ligatures"
+ "Monego"
+ "Monaco"
+ "monospace"))))
+ (setq ethandl/mono-font-size
+ (or size 12))
+ (setq ethandl/mono-font-string
+ (format "%s-%d" ethandl/mono-font-name ethandl/mono-font-size)))
- (use-package project
- :ensure nil
- :config
- (add-hook 'project-find-functions #'golang/project-find-go-module))
+ (defun ethandl/set-frame-mono-font (&optional frame name size)
+ "Sets the mono font for frames
- (use-package eglot
- :ensure nil
- :after eglot
- :hook (go-ts-mode . eglot-ensure)
- :config
- (setq-default eglot-workspace-configuration
- '((:gopls .
- ((staticcheck . t)
- (matcher . "CaseSensitive")))))
- (push '("\\.go\\'" . go-ts-mode) auto-mode-alist)
- (push '("\\.mod\\'" . go-mod-ts-mode) auto-mode-alist))
-#+end_src
-** Java
-OO hell 😌
-If you want a language server, maybe just use IntelliJ? I don't use Java and the ~eglot-java~ package has broken for my config, complaining about the eglot version.
-#+begin_src emacs-lisp
- ;; Java setup
- (use-package emacs
- :ensure nil
- :config
- (push '(java-mode . java-ts-mode) major-mode-remap-alist))
-#+end_src
-** JavaScript/TypeScript
-JavaScript and TypeScript are easy thanks to tree sitter!
-#+begin_src emacs-lisp
- (use-package eglot
- :ensure nil
- :after eglot
- :hook
- (js-ts-mode . eglot-ensure)
- (typescript-ts-mode . eglot-ensure)
- (tsx-ts-mode . eglot-ensure)
- :config
- (setq typescript-ts-mode-indent-offset tab-width)
- (push '(js-mode . js-ts-mode) major-mode-remap-alist)
- (push '(javascript-mode . js-ts-mode) major-mode-remap-alist)
- (push '("\\.ts\\'" . typescript-ts-mode) auto-mode-alist)
- (push '("\\.tsx\\'" . tsx-ts-mode) auto-mode-alist))
+ Use FRAME to specify the frame to change, NAME as the font family,
+ and SIZE as the font size in points. If FRAME is nil, will apply to
+ all open frames. If NAME or SIZE are nil, follows the same
+ convention as `ethandl/set-mono-font-vars'."
+ (ethandl/set-mono-font-vars name size)
+ (set-frame-font ethandl/mono-font-string t (if (bound-and-true-p frame) (list frame) t)))
+
+ (ethandl/set-frame-mono-font (selected-frame) "Codelia Ligatures" 12)
+ (add-hook 'after-make-frame-functions #'ethandl/set-frame-mono-font)
#+end_src
-** Python
-A.K.A. the most overused and overhyped language. This language is incredibly slow, which is why its language server is not written in Python LMAO. Anywho the =pyright= LSP is made by Microshit so maybe this is proprietary software or telemetry, idk.
-#+begin_src emacs-lisp
- (use-package eglot
- :ensure nil
- :after eglot
- :hook ((python-mode python-ts-mode) . eglot-ensure)
- :config
- (add-to-list 'eglot-server-programs
- `(python-mode . ("pyright-langserver" "--stdio")))
- (add-to-list 'eglot-server-programs
- `(python-ts-mode . ("pyright-langserver" "--stdio")))
- ;(push ".dir-locals.el" project-vc-extra-root-markers)
- (push '(python-mode . python-ts-mode) major-mode-remap-alist))
-#+end_src
-The following lets me set up a venv for use with pyright (https://robbmann.io/posts/emacs-eglot-pyrightconfig/):
-#+begin_src emacs-lisp
- (defun pyrightconfig-write (virtualenv)
- (interactive "DEnv: ")
+*** Org fonts
+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):
+#+begin_src emacs-lisp
+ (add-hook 'org-mode-hook #'variable-pitch-mode)
+#+end_src
+
+To define our desired fonts, we create functions as we did before in [[Code fonts]]:
+#+begin_src emacs-lisp
+ (defun ethandl/set-org-font-vars (&optional height var mono)
+ "Sets the mono font variables for Org mode
+
+ Use HEIGHT to specify the font height in px, VAR as the variable-pitch
+ font family, and MONO as the monospace font family. If HEIGHT is nil,
+ uses a height of 140. If VAR or MONO are nil, selects the first
+ available font for each from a predefined list."
+ (setq ethandl/org-mono-font-name
+ (or mono
+ (seq-find #'ethandl/font-exists
+ '("CMU Typewriter Text"
+ "Iosevka"
+ "Codelia Ligatures"
+ "Monego"
+ "Monaco"
+ "DejaVu Sans Mono"
+ "monospace"))))
+ (setq ethandl/org-var-font-name
+ (or var
+ (seq-find #'ethandl/font-exists
+ '("CMU Serif"
+ "Iosevka Aile"
+ "Times New Roman"
+ "DejaVu Serif"))))
+ (setq ethandl/org-font-ht
+ (if (bound-and-true-p height) height 140)))
+#+end_src
+
+Next, we define a way to easily apply these fonts.
+This is not as simple as it should be, because ~variable-pitch-mode~ takes over all Org fonts.
+#+begin_src emacs-lisp
+ (defun ethandl/set-org-fonts (&optional height var mono)
+ "Applies the mono fonts for org mode. Arguments are the same as for `ethandl/set-org-font-vars'."
+ (ethandl/set-org-font-vars height var mono)
- (let* (;; file-truename and tramp-file-local-name ensure that neither `~' nor
- ;; the Tramp prefix (e.g. "/ssh:my-host:") wind up in the final
- ;; absolute directory path.
- (venv-dir (tramp-file-local-name (file-truename virtualenv)))
-
- ;; Given something like /path/to/.venv/, this strips off the trailing `/'.
- (venv-file-name (directory-file-name venv-dir))
-
- ;; Naming convention for venvPath matches the field for
- ;; pyrightconfig.json. `file-name-directory' gets us the parent path
- ;; (one above .venv).
- (venvPath (file-name-directory venv-file-name))
-
- ;; Grabs just the `.venv' off the end of the venv-file-name.
- (venv (file-name-base venv-file-name))
-
- ;; Eglot demands that `pyrightconfig.json' is in the project root
- ;; folder.
- (base-dir (vc-git-root default-directory))
- (out-file (expand-file-name "pyrightconfig.json" base-dir))
+ (custom-theme-set-faces
+ 'user
+ `(variable-pitch ((t (:family ,ethandl/org-var-font-name :height ,ethandl/org-font-ht))))
+ `(fixed-pitch ((t (:family ,ethandl/org-mono-font-name :height ,ethandl/org-font-ht))))
+
+ '(org-block ((t (:inherit fixed-pitch))))
+ '(org-code ((t (:inherit (shadow fixed-pitch)))))
+ '(org-document-info ((t (:foreground "dark orange"))))
+ '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
+ '(org-indent ((t (:inherit (org-hide fixed-pitch)))))
+ '(org-link ((t (:foreground "royal blue" :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 :foreground "#83a598"))))
+ '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
+ '(org-verbatim ((t (:inherit (shadow fixed-pitch)))))))
+#+end_src
- ;; Finally, get a string with the JSON payload.
- (out-contents (json-encode (list :venvPath venvPath :venv venv))))
+Finally, we apply these fonts every time we enter org mode.
+#+begin_src emacs-lisp
+ (add-hook 'org-mode-hook #'ethandl/set-org-fonts)
+#+end_src
+** Theme
+I like the Catppuccin theme, but the official one kinda stinks. As such, I use the ~batppuccin~ theme:
+#+begin_src emacs-lisp
+ (elpaca batppuccin
+ (load-theme 'batppuccin-mocha t))
+#+end_src
- ;; Emacs uses buffers for everything. This creates a temp buffer, inserts
- ;; the JSON payload, then flushes that content to final `pyrightconfig.json'
- ;; location
- (with-temp-file out-file (insert out-contents))))
+Another nice theme is ~gruber-darker~ by tsoding, one can use it similarly:
+#+begin_src emacs-lisp :tangle no
+ (elpaca gruber-darker-theme
+ (load-theme 'gruber-darker t))
#+end_src
-** LaTeX
-LaTeX isn't really a language, but we should set up stuff for it.
-Firstly, we should get CDLaTeX:
+* Emacs behaviour
+** Startup screen removal
+Boot straight into a scratch buffer, removing the startup screen:
#+begin_src emacs-lisp
- (use-package cdlatex
- :hook ((latex-mode LaTeX-mode) . turn-on-cdlatex)
- :hook (latex-mode . (lambda () (setq tab-width 2))))
+ (setq-default inhibit-startup-screen t)
#+end_src
-* Native Comp
-Supress all warnings:
+** Line wrapping
+The default line wrapping doesn't respect words and will arbitrarily wrap on the last character.
+Luckily Emacs comes with an alternate wrapping method:
#+begin_src emacs-lisp
- (setq native-comp-async-report-warnings-errors 'silent)
+ (global-visual-line-mode 1)
#+end_src
-I used to always try to compile all the emacs packages in our config, I find that this just results in a build job running forever, even when we're done. Not sure what it's doing or what package it's for, but I'm just going to make this compilation process manual.
+Futhermore, with vanilla Emacs 30.1 or newer the indentation of the wrapped line will be respected with the addition of the following mode:
#+begin_src emacs-lisp :tangle no
- (when (fboundp 'native-comp-available-p)
- (add-hook 'elpaca-after-init-hook (lambda () (native-compile-async user-emacs-directory t))))
+ (when (fboundp 'global-visual-wrap-prefix-mode)
+ (global-visual-wrap-prefix-mode 1))
+#+end_src
+** Scrolling
+There are a collection of variables that make scrolling behave more like that in ~vi~, my poison.
+#+begin_src emacs-lisp
+ (setq scroll-conservatively 10000
+ scroll-margin 0
+ auto-window-vscroll nil)
+#+end_src
+
+For smooth mouse and faster document scrolling, I use the package ~ultra-scroll~ by jdtsmith.
+This works on all emacs builds to my knowledge, and is by far the best scrolling package.
+#+begin_src emacs-lisp
+ (elpaca ultra-scroll
+ (ultra-scroll-mode 1)
+ (add-hook 'ultra-scroll-hide-functions #'org-modern-mode))
#+end_src
+** Suppress ~native-compiler~ warnings
+These warnings are generally not useful.
+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.
+For debugging purposes, you can disable the tangling of this block:
+#+begin_src emacs-lisp
+ (setq native-comp-async-report-warnings-errors `silent)
+#+end_src
+** Asyncronous commands
+The package ~async~ allows for us to run long, blocking commands in separate Emacs processes.
+#+begin_src emacs-lisp
+ (elpaca async)
+#+end_src
+* Org mode
+** Section numbering
+I like my sections numbered:
+#+begin_src emacs-lisp
+ (add-hook 'org-mode-hook #'org-num-mode)
+#+end_src
+** Better emphasis rendering
+Emphasis markers (/italics/, *bold*, _underline_, ~verbatim~, =code=, and +strikethrough+) all render with the markers (~/~, ~*~, ~_~, ~~~, ~=~, and ~+~) included in the text by default.
+I think that these markers work better when they aren't rendered:
+#+begin_src emacs-lisp
+ (setq org-hide-emphasis-markers t)
+#+end_src
+** Pretty appearance
+I know that I said that I would keep things minimal...
-Manual compile function:
+I lied: this package is just for beauty because Org is somewhat ugly by default.
+Unfortunately, this package conflicts with ~visual-wrap-prefix-mode~, so we need to explicitly disable that for org mode with a hook function.
#+begin_src emacs-lisp
- (defun ethandl/nativecomp-all ()
- (interactive)
- (native-compile-async user-emacs-directory 'recursively))
+ (elpaca org-modern
+ (add-hook 'org-mode-hook #'org-modern-mode)
+ (add-hook 'org-agenda-finalize-hook #'org-modern-agenda)
+ (add-hook 'org-mode-hook (lambda () (visual-wrap-prefix-mode 0))))
+#+end_src
+** \(\text{\LaTeX}\) rendering
+We are already secretly using a buggy Org branch to get \(\text{\LaTeX}\) to render correctly and automagically.
+This is not ideal, but vanilla Org doesn't support live \(\text{\LaTeX}\) rendering and is slow. It's a tradeoff...
+
+To enable this rendering automatically, as well as a live preview while writing:
+#+begin_src emacs-lisp
+ (add-hook 'org-mode-hook #'org-latex-preview-mode)
+ (setq org-latex-preview-mode-display-live t)
+#+end_src
+
+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.
+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.
+#+begin_src emacs-lisp
+ (add-hook 'org-mode-hook (lambda () (org-latex-preview 'buffer)))
+#+end_src
+* The programming environment
+** The ~vterm~ terminal emulator
+To make emacs a useful development environment, we need a good terminal. Instead of using a separate terminal emulator like a Unix puritan, we use the ~vterm~ package:
+#+begin_src emacs-lisp
+ (elpaca vterm)
+#+end_src
+** Treesitter grammar async install
+Treesitter grammar installation is slow and blocks up the emacs process. The following allows for asyncronous treesitter grammar installation on separate processes:
+#+begin_src emacs-lisp
+ (elpaca-wait)
+ (defun ethandl/treesit-install-async (lang)
+ (unless (treesit-language-available-p lang)
+ (async-start
+ `(lambda ()
+ ,(async-inject-variables "\\`treesit-language-source-alist\\'")
+ (treesit-install-language-grammar ',lang))
+ 'ignore)))
+#+end_src
+** Markdown mode
+Emacs comes with ~markdown-ts-mode~, which utilises treesitter to provide highlighting for markdown. This means that we need the treesitter grammar installed:
+#+begin_src emacs-lisp
+ (add-to-list 'treesit-language-source-alist
+ '(markdown "https://github.com/tree-sitter-grammars/tree-sitter-markdown" "split_parser" "tree-sitter-markdown/src"))
+ (add-to-list 'treesit-language-source-alist
+ '(markdown-inline "https://github.com/tree-sitter-grammars/tree-sitter-markdown" "split_parser" "tree-sitter-markdown-inline/src"))
+
+ (ethandl/treesit-install-async 'markdown)
+ (ethandl/treesit-install-async 'markdown-inline)
+#+end_src
+** Python
+#+begin_src emacs-lisp
+ (add-to-list 'treesit-language-source-alist
+ '(python "https://github.com/tree-sitter/tree-sitter-python"))
+
+ (ethandl/treesit-install-async 'python)
+#+end_src
+** \(\text{\LaTeX}\)
+I like to keep it simple; we have a built-in \(\text{\LaTeX}\) programming environment, and I won't install anything extra.
+* Platform specifics
+I have structured the following sections such that each specific platform is tangled only if it is applicable.
+Tangled outputs are saved to ~config-TARG.el~ where ~TARG~ is a property of your platform.
+
+Any tangled outputs are automatically included in the final subsection of this section.
+
+Generally only small deviations from the main config should be put in here.
+This is the place to check if you're getting any strange bugs or different behaviour on different platforms.
+** MacOS
+*** The menu bar
+Because the menu bar exists regardless on MacOS, I like to keep ~menu-bar-mode~ enabled.
+#+begin_src emacs-lisp :tangle (if (eq system-type 'darwin) "platform-config/darwin.el")
+ (menu-bar-mode 1)
+#+end_src
+** Including your platforms
+Finally, we include any tangled platforms:
+#+begin_src emacs-lisp
+ (dolist (file (directory-files ethandl/platform-config-dir t "\\.el$"))
+ (load file))
#+end_src
diff --git a/custom.el b/custom.el
@@ -0,0 +1,13 @@
+;;; -*- lexical-binding: t -*-
+(custom-set-variables
+ ;; custom-set-variables was added by Custom.
+ ;; If you edit it by hand, you could mess it up, so be careful.
+ ;; Your init file should contain only one such instance.
+ ;; If there is more than one, they won't work right.
+ )
+(custom-set-faces
+ ;; custom-set-faces was added by Custom.
+ ;; If you edit it by hand, you could mess it up, so be careful.
+ ;; Your init file should contain only one such instance.
+ ;; If there is more than one, they won't work right.
+ )
diff --git a/early-init.el b/early-init.el
@@ -1,3 +1,6 @@
+;; -*- lexical-binding: t; -*-
(setq package-enable-at-startup nil)
(setq frame-resize-pixelwise t)
(setq window-resize-pixelwise t)
+
+(setq elpaca-core-date '(2026 . 5))
diff --git a/init.el b/init.el
@@ -1,16 +1,14 @@
-;; elpaca build date, CHANGE EVERY BUILD
-(setq elpaca-core-date '(20250616))
-
+;; -*- lexical-binding: t; -*-
;; Start elpaca bootstrap
-(defvar elpaca-installer-version 0.11)
+(defvar elpaca-installer-version 0.12)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
-(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
+(defvar elpaca-sources-directory (expand-file-name "sources/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil :depth 1 :inherit ignore
:files (:defaults "elpaca-test.el" (:exclude "extensions"))
- :build (:not elpaca--activate-package)))
-(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
+ :build (:not elpaca-activate)))
+(let* ((repo (expand-file-name "elpaca/" elpaca-sources-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
@@ -42,37 +40,36 @@
(elpaca `(,@elpaca-order))
;; End elpaca bootstrap
-;; Install use-package support
-(elpaca elpaca-use-package
- ;; Enable :elpaca use-package keyword.
- (elpaca-use-package-mode)
- ;; Assume :elpaca t unless otherwise specified.
- (setq elpaca-use-package-by-default t))
+;; Installing the latest Org package so that we have live LaTeX previews:
+(elpaca (org :repo "https://code.tecosaur.net/tec/org-mode" :branch "dev"))
-(elpaca-wait)
+;; The main literate Emacs config is stored in config.org:
+(setq ethandl/config-fname (expand-file-name "config.org" user-emacs-directory))
-;; Install a more up-to-date version of org
-;; (use-package org
-;; :defer
-;; :ensure `(org :repo "https://code.tecosaur.net/tec/org-mode.git/"
-;; :branch "dev")
-;; :hook
-;; (org-mode . org-latex-preview-auto-mode)
-;; :config
-;; ;; Ensure that live previews are on
-;; (setq org-latex-preview-live t)
-;; (setq org-latex-preview-live-debounce 0.25))
-(use-package org)
-;; There are many reasons you might want to do this, but the git version is quite unstable as well
+;; We will also generate platform-specific files in platform-config
+(setq ethandl/platform-config-dir
+ (expand-file-name "platform-config/" user-emacs-directory))
+;; Ensuring we have this folder created
+(unless (file-directory-p ethandl/platform-config-dir)
+ (make-directory ethandl/platform-config-dir t))
-;; We then need to wait for the download of org to complete before we can use org.
+;; Waiting for latest version of Org to install:
(elpaca-wait)
-(setq ethandl/config-fname (expand-file-name "config.org" user-emacs-directory))
+;; We want all tangled files to have the lexical binding header:
+(defun ethandl/insert-lexical-binding ()
+ (when (derived-mode-p 'emacs-lisp-mode)
+ (goto-char (point-min))
+ (unless (string-match-p "lexical-binding" (buffer-string))
+ (insert ";; -*- lexical-binding: t -*-\n")
+ (basic-save-buffer))))
+(add-hook 'org-babel-post-tangle-hook #'ethandl/insert-lexical-binding)
+
+;; Finally we can tangle the main config
(when (file-readable-p ethandl/config-fname)
(org-babel-load-file ethandl/config-fname))
-;; Emacs crap that it does automatically:
+;; Custom.el file for storing automatically generated emacs vars:
(setq ethandl/custom-file (expand-file-name "custom.el" user-emacs-directory))
(setq custom-file ethandl/custom-file)
(load custom-file)