summaryrefslogtreecommitdiff
path: root/.config/emacs/config.org
diff options
context:
space:
mode:
Diffstat (limited to '.config/emacs/config.org')
-rw-r--r--.config/emacs/config.org1396
1 files changed, 0 insertions, 1396 deletions
diff --git a/.config/emacs/config.org b/.config/emacs/config.org
deleted file mode 100644
index 82412f4..0000000
--- a/.config/emacs/config.org
+++ /dev/null
@@ -1,1396 +0,0 @@
-#+TITLE: Emacs Configuration
-#+AUTHOR: Thomas Voss
-#+DATE: <2023-08-09 Wed>
-#+DESCRIPTION: My Emacs configuration — before I ran ‘rm -rf ~/.config’ that is
-#+STARTUP: overview
-
-* Headers
-
-The first thing we need in any configuration is a header setting
-~lexical-binding~ to ~t~. This will allow us to have non-autistic behavior when
-scoping variables.
-
-#+BEGIN_SRC elisp :tangle early-init.el
- ;;; early-init.el --- Emacs early configuration file -*- lexical-binding: t -*-
-#+END_SRC
-
-#+BEGIN_SRC elisp :tangle init.el
- ;;; init.el --- Emacs configuration file -*- lexical-binding: t -*-
-#+END_SRC
-
-* Early Init File
-:PROPERTIES:
-:header-args: :tangle early-init.el
-:END:
-
-** Numerical Constants
-
-These are not really important, but nice to have to make the following
-configuration more readable. The reason they’re defined in the ~early-init.el~
-is simply because I need to use them here.
-
-#+BEGIN_SRC elisp
-
- (defconst 1-KiB 1024
- "The number of bytes in 1 kibibyte.")
-
- (defconst 1-MiB (* 1-KiB 1024)
- "The number of bytes in 1 mebibyte.")
-
- (defconst 1-GiB (* 1-MiB 1024)
- "The number of bytes in 1 gibibyte.")
-
-#+END_SRC
-
-** XDG Directories
-
-We want to define variables for the main XDG directories for Emacs to use.
-These are nice for keeping things organized and out of the way.
-
-#+BEGIN_SRC elisp
-
- (defconst mango-cache-directory
- (expand-file-name
- "emacs"
- (or (getenv "XDG_CACHE_HOME")
- (expand-file-name ".cache" (getenv "HOME"))))
- "The XDG-conformant cache directory that Emacs should use.")
-
- (defconst mango-config-directory
- (expand-file-name
- "emacs"
- (or (getenv "XDG_CONFIG_HOME")
- (expand-file-name ".config" (getenv "HOME"))))
- "The XDG-conformant config directory that Emacs should use.")
-
- (defconst mango-data-directory
- (expand-file-name
- "emacs"
- (or (getenv "XDG_DATA_HOME")
- (expand-file-name ".local/share" (getenv "HOME"))))
- "The XDG-conformant data directory that Emacs should use.")
-
-#+END_SRC
-
-We also need to ensure our directories actually exist.
-
-#+BEGIN_SRC elisp
-
- (mapc (lambda (x) (make-directory x t))
- (list mango-cache-directory
- mango-config-directory
- mango-data-directory))
-
-#+END_SRC
-
-We don’t want to have all sorts of random garbage populating the configuartion
-directory; let’s throw it all in the cache directory instead.
-
-#+BEGIN_SRC elisp
-
- (setq user-emacs-directory mango-cache-directory
- auto-save-list-file-prefix (expand-file-name
- "auto-save-list/"
- mango-cache-directory)
- backup-directory-alist `(("." . ,(expand-file-name
- "backups"
- mango-cache-directory))))
-
-#+END_SRC
-
-** Default Limits
-
-These are just some limits Emacs abides by as far as garbage-collection and
-process communication go. They’re pretty low by default though; any modern
-system is capable of much higher. To improve startup performance the garbage
-collection theshold is set to the max until Emacs is initialized. Additionally
-the documentation for ~read-process-output-max~ gives us a hint at how large we
-can set it:
-
-#+BEGIN_QUOTE
-
- On GNU/Linux systems, the value should not exceed
- =/proc/sys/fs/pipe-max-size=. See pipe(7) manpage for details.
-
-#+END_QUOTE
-
-#+BEGIN_SRC elisp
-
- (setq gc-cons-threshold most-positive-fixnum)
- (add-hook 'after-init-hook
- (lambda () (setq gc-cons-threshold (* 512 1-MiB))))
-
- (with-temp-buffer
- (insert-file-contents "/proc/sys/fs/pipe-max-size")
- (setq read-process-output-max (number-at-point)))
-
-#+END_SRC
-
-** Package Management
-
-Finally, we want to completely disable ~package.el~ since we’re going to be
-using ~straight.el~ in the main configuration.
-
-#+BEGIN_SRC elisp
-
- (setq package-enable-at-startup nil)
-
-#+END_SRC
-
-* Init File
-:PROPERTIES:
-:header-args: :tangle init.el
-:END:
-
-** Helper Functions and -Macros
-
-*** Shorter Lambdas
-
-It’s annoying to have to write out ~(lambda () BODY)~ every time I want to write
-a lambda that takes no args — a very common operation. The solution is to just
-use an actual lambda. If I ever forget how to enter a lambda, it’s =C-x 8 RET=.
-
-#+BEGIN_SRC elisp
-
- (defmacro λ (&rest body)
- "Convenience macro to create lambda functions that take no arguments with much
- short and concise syntax. Calling ‘λ’ with BODY is equivalent to calling
- ‘lambda’ with an empty argument list and BODY."
- (declare (pure t) (side-effect-free t))
- `(lambda () ,@body))
-
-#+END_SRC
-
-*** Hooks from Modes
-
-It is very often that I have a mode symbol and I want to extract the
-corresponding hook from it. Luckily there’s a pretty standard naming convention
-here.
-
-#+BEGIN_SRC elisp
-
- (defun mango-mode-to-hook (mode)
- "Get the hook corresponding to MODE."
- (declare (pure t) (side-effect-free t))
- (intern (concat (symbol-name mode) "-hook")))
-
-#+END_SRC
-
-*** Tree-Sitter Modes
-
-As I was writing this configuration, Emacs 29 released on the Arch repositories
-with native support for ~tree-sitter~. As a result many major-modes now have two
-versions — a regular version and a ~tree-sitter~ version. I should have pretty
-equal configurations for both versions of a mode so it’s useful to be able to
-grab one from the other.
-
-#+BEGIN_SRC elisp
-
- (defun mango-mode-to-ts-mode (mode)
- "Get the tree-sitter mode corresponding to MODE."
- (declare (pure t) (side-effect-free t))
- (intern (concat
- (string-remove-suffix "-mode" (symbol-name mode))
- "-ts-mode")))
-
-#+END_SRC
-
-** Package Management
-
-For package management I like to use ~straight.el~. Before setting that up
-though it’s probably best to disable native-compilation warnings; we’ll get a
-whole lot of those when ~straight.el~ is installing packages.
-
-#+BEGIN_SRC elisp
-
- (setq comp-async-report-warnings-errors nil
- native-comp-async-report-warnings-errors nil)
-
-#+END_SRC
-
-After doing that, we can bootstrap ~straight.el~. The two options enabled at
-the end are just configuring ~use-package~ to always use ~straight.el~ by
-default, and to always ensure packages unless stated otherwise.
-
-#+BEGIN_SRC elisp
-
- (defvar bootstrap-version)
- (let ((bootstrap-file
- (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
- (bootstrap-version 6))
- (unless (file-exists-p bootstrap-file)
- (with-current-buffer
- (url-retrieve-synchronously
- "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
- 'silent 'inhibit-cookies)
- (goto-char (point-max))
- (eval-print-last-sexp)))
- (load bootstrap-file nil 'nomessage))
-
- (setq straight-use-package-by-default t
- use-package-always-ensure t)
-
-#+END_SRC
-
-** Documentation
-
-Documentation is absolutely essential. The ~helpful~ package gives us much
-better documentation for things, so let’s use it. I’m mostly just overriding
-the bindings for the standard ~describe-*~ functions.
-
-#+BEGIN_SRC elisp
-
- (use-package helpful
- :bind (("C-h f" . #'helpful-callable)
- ("C-h v" . #'helpful-variable)
- ("C-h k" . #'helpful-key)
- ("C-h o" . #'helpful-symbol)
- ("C-c C-d" . #'helpful-at-point)))
-
-#+END_SRC
-
-** Key Bindings
-
-*** Editing This Config
-
-This configuration is one of the files I visit the most. Not just for making
-large customizations, but also for simply editing the tab-width for a mode I
-happen to be using. For that reason there should probably be a binding to get
-here.
-
-#+BEGIN_SRC elisp
-
- (keymap-global-set
- "C-c e"
- (λ (interactive)
- (find-file
- (expand-file-name "config.org" mango-config-directory))))
-
-#+END_SRC
-
-*** Evil Mode
-
-The default Emacs keybindings are horrible and dangerous. Feel free to use them
-if you want to develop genuine problems with your hands. For this reason,
-~evil-mode~ bindings are the way to go. Also as I was writing this, Bram
-Moolenaar, the creator of Vim died. RIP.
-
-Here we setup bindings in the ~:bind~ section; the functions bound to that don’t
-exist yet will be created shortly. You also need to set all these variables
-/before/ ~evil-mode~ is loaded — it’s just how ~evil~ works I suppose. Finally
-I like to have ~visual-line-mode~ enabled as I find it far more intuitive.
-
-#+BEGIN_SRC elisp
-
- (use-package evil
- :bind (:map evil-normal-state-map
- ("€" . #'evil-end-of-visual-line)
- ("<leader>h" . #'evil-window-left)
- ("<leader>j" . #'evil-window-down)
- ("<leader>k" . #'evil-window-up)
- ("<leader>l" . #'evil-window-right)
- ("<leader>a" . #'mango--evil-align-regexp)
- ("<leader>s" . #'mango--evil-sort-lines)
- ("<leader>;" . #'mango--evil-comment-or-uncomment-region))
- :init
- ;; We need to set these variables before loading ‘evil-mode’
- (setq evil-want-Y-yank-to-eol t
- evil-v$-excludes-newline t
- evil-respect-visual-line-mode t
- evil-split-window-below t
- evil-vsplit-window-right t
- evil-want-fine-undo t
- evil-undo-system 'undo-redo
- evil-flash-delay 1
- evil-want-keybinding nil)
- (evil-mode)
- (global-visual-line-mode)
- :config
- (evil-set-leader nil (kbd "SPC")))
-
-#+END_SRC
-
-*** Evil Surround
-
-This is probably one of the more useful Vim/Emacs extensions out there. It
-let’s you easy add-, remove-, and change surrounding pairs such as quotation
-marks and parenthesis from a /text object/. I like to use unicode single- and
-double quotation marks — so we also want to add support for those.
-
-#+BEGIN_SRC elisp
-
- (use-package evil-surround
- :after evil
- :config
- (global-evil-surround-mode))
-
-#+END_SRC
-
-Supporting custom pairs is a bit tricky since we need to define an evil /text
-object/ to make them work properly. Also we add some custom Jinja pairs!
-
-#+BEGIN_SRC elisp
-
- (defmacro mango--evil-define-and-bind-quoted-text-object (name key start-regex end-regex)
- (let ((inner-name (make-symbol (concat "evil-inner-" name)))
- (outer-name (make-symbol (concat "evil-a-" name))))
- `(progn
- (evil-define-text-object ,inner-name (count &optional beg end type)
- (evil-select-paren ,start-regex ,end-regex beg end type count nil))
- (evil-define-text-object ,outer-name (count &optional beg end type)
- (evil-select-paren ,start-regex ,end-regex beg end type count t))
- (define-key evil-inner-text-objects-map ,key #',inner-name)
- (define-key evil-outer-text-objects-map ,key #',outer-name))))
-
- (mango--evil-define-and-bind-quoted-text-object "single-quote-open" "‘" "‘" "’")
- (mango--evil-define-and-bind-quoted-text-object "single-quote-close" "’" "‘" "’")
- (mango--evil-define-and-bind-quoted-text-object "double-quote-open" "“" "“" "”")
- (mango--evil-define-and-bind-quoted-text-object "double-quote-close" "”" "“" "”")
-
- (setq-default
- evil-surround-pairs-alist
- (append
- '((?‘ . ("‘ " . " ’"))
- (?’ . ("‘" . "’"))
- (?“ . ("“ " . " ”"))
- (?“ . ("“" . "”")))
- evil-surround-pairs-alist))
-
- (add-hook
- 'html-mode-hook
- (λ (setq-local
- evil-surround-pairs-alist
- (append
- '((?% . ("{% " . " %}"))
- (?# . ("{# " . " #}"))
- (?{ . ("{{ " . " }}")))
- evil-surround-pairs-alist))
- (mango--evil-define-and-bind-quoted-text-object "jinja-action" "%" "{%" "%}")
- (mango--evil-define-and-bind-quoted-text-object "jinja-comment" "#" "{#" "#}")
- (mango--evil-define-and-bind-quoted-text-object "jinja-code" "{" "{{" "}}")))
-
-#+END_SRC
-
-*** Evil Collection
-
-This is a super-simple to setup package that adds ~evil~ bindings to many other
-custom packages like ~magit~ and ~mu4e~.
-
-#+BEGIN_SRC elisp
-
- (use-package evil-collection
- :after evil
- :config
- (evil-collection-init))
-
-#+END_SRC
-
-*** Aligning Text
-
-I absolutely love aligning text with ~align-regexp~. It is in my opinion one of
-the most underrated Emacs functions. Unfortunately, it aligns with tabs or
--spaces based on the setting of ~indent-tabs-mode~. Personally I am almost
-always indenting with tabs, but I prefer to align with spaces. Luckily we can
-use some advice to force the usage of spaces.
-
-#+BEGIN_SRC elisp
-
- (defun mango--align-regexp-with-spaces (old-function &rest args)
- "Advice to force ‘align-regexp’ to always align with spaces, regardless of the
- value of ‘indent-tabs-mode’."
- (let (indent-tabs-mode)
- (apply old-function args)))
-
- (advice-add 'align-regexp :around #'mango--align-regexp-with-spaces)
-
-#+END_SRC
-
-Now that it’s behaving properly, it should also be turned into an ~evil~
-operator so it can be used with Vim motions.
-
-#+BEGIN_SRC elisp
-
- (evil-define-operator mango--evil-align-regexp (beg end regexp repeat)
- "Wrapper around ‘align-regexp’ to allow for use as an ‘evil-mode’ operator."
- (interactive (let ((range (evil-operator-range)))
- (list (car range)
- (cadr range)
- (concat "\\(\\s-*\\)"
- (read-string "Align regexp: "))
- (y-or-n-p "Repeat? "))))
- (align-regexp beg end regexp 1 1 repeat))
-
-#+END_SRC
-
-*** Sorting Text
-
-Another very common operation is sorting text. So why not make an operator for
-that too?
-
-#+BEGIN_SRC elisp
-
- (evil-define-operator mango--evil-sort-lines (beg end)
- "Wrapper around ‘sort-lines’ to allow for use as an ‘evil-mode’ operator."
- (sort-lines nil beg end))
-
-#+END_SRC
-
-*** Commenting Code
-
-Commenting code is a super common task — so make it an operator!
-
-#+BEGIN_SRC elisp
-
- (evil-define-operator mango--evil-comment-or-uncomment-region (beg end)
- "Wrapper around ‘comment-or-uncomment-region’ to allow for use as
- an ‘evil-mode’ operator."
- (comment-or-uncomment-region beg end))
-
-#+END_SRC
-
-*** Tetris
-
-Tetris is epic, but the bindings are not so epic.
-
-#+BEGIN_SRC elisp
-
- (defun mango-tetris-rotate-mirror ()
- (interactive)
- (tetris-rotate-next)
- (tetris-rotate-next))
-
- (use-package tetris
- :hook (tetris-mode . (lambda () (evil-local-mode -1)))
- :bind (:map tetris-mode-map
- ("a" . #'tetris-move-left)
- ("d" . #'tetris-move-right)
- ("k" . #'tetris-rotate-next)
- (";" . #'tetris-rotate-prev)
- ("l" . #'tetris-move-down)
- ("o" . #'mango-tetris-rotate-mirror)
- ("SPC" . #'tetris-move-bottom)))
-
-#+END_SRC
-
-** Completions
-
-*** Savehist-Mode
-
-This mode is super handy. It let’s you preserve your history in minibuffer
-prompts.
-
-#+BEGIN_SRC elisp
-
- (savehist-mode)
-
-#+END_SRC
-
-*** Vertico & Marginalia
-
-Vertico is a great package for enhanced completions in the minibuffer. It’s
-minimal and works great. We also want to configure the highlighting of the
-current line to match up with what is used for ~hl-line-mode~. Vertico also
-doesn’t offer a builtin function to go up a directory when typing out a path, so
-that’s what the ~mango-minibuffer-backward-kill~ is for.
-
-#+BEGIN_SRC elisp
-
- (defun mango-minibuffer-backward-kill (arg)
- "When minibuffer is completing a file name delete up to parent folder,
- otherwise delete a word."
- (interactive "p")
- (if minibuffer-completing-file-name
- (if (string-match-p "/." (minibuffer-contents))
- (zap-up-to-char (- arg) ?/)
- (delete-minibuffer-contents))
- (backward-kill-word arg)))
-
- (use-package vertico
- :bind (:map vertico-map
- ("C-j" . vertico-next)
- ("C-k" . vertico-previous)
- ("C-l" . vertico-insert)
- :map minibuffer-local-map
- ("C-h" . mango-minibuffer-backward-kill))
- :custom
- (vertico-cycle t)
- :init
- (vertico-mode))
-
-#+END_SRC
-
-Marginalia is kind of a suppliment I like to use with Vertico. It adds, well…
-/marginalia/ to the completions in the minibuffer. This includes things like
-file sizes and permissions when looking at files, function docstrings when
-looking at those, etc.
-
-#+BEGIN_SRC elisp
-
- (use-package marginalia
- :after vertico
- :init
- (marginalia-mode))
-
-#+END_SRC
-
-*** Orderless
-
-Orderless is another pretty neat package. It allows for better completion
-candidate filtering. I personally prefer to use the ~orderless-prefixes~
-completion style where entering the string “foo bar baz” will match the options
-that have components beginning with /foo/, /bar/, and /baz/ in that order.
-
-#+BEGIN_SRC elisp
-
- (use-package orderless
- :custom
- (completion-styles '(orderless basic))
- (orderless-matching-styles '(orderless-prefixes))
- (completion-category-overrides '((file (styles basic partial-completion)))))
-
-#+END_SRC
-
-*** Company
-
-Company is a package to give me actual completion popups; it’s super useful for
-autocompleting code but has other uses too I guess.
-
-#+BEGIN_SRC elisp
-
- (defun mango--company-require-prefix (candidates)
- "Transformer for ‘company-mode’ that requires that all candidates begin with
- ‘company-prefix’."
- (seq-filter (lambda (s) (string-prefix-p company-prefix s)) candidates))
-
- (defun mango--company-select-candidate (pred)
- "Select either the next or previous candidate in the candidate list based on
- the comparison of the ‘company-pseudo-tooltip-overlay’ height and 0 using PRED."
- (let ((ov company-pseudo-tooltip-overlay))
- (if (and ov (apply pred (list (overlay-get ov 'company-height) 0)))
- (company-select-previous)
- (company-select-next))))
-
- (defun mango-company-next-candidate ()
- "Select the next available candidate, taking into account if the candidate
- list is flipped or not."
- (interactive)
- (mango--company-select-candidate #'<))
-
- (defun mango-company-previous-candidate ()
- "Select the previous available candidate, taking into account if the candidate
- list is flipped or not."
- (interactive)
- (mango--company-select-candidate #'>))
-
- (use-package company
- :bind (:map company-active-map
- ("C-j" . #'mango-company-next-candidate)
- ("C-k" . #'mango-company-previous-candidate))
- :hook ((conf-mode prog-mode) . company-mode)
- :custom
- (company-minimum-prefix-length 1)
- (company-idle-delay (lambda () (unless (company-in-string-or-comment) 0)))
- (company-selection-wrap-around t)
- (company-tooltip-align-annotations t)
- (company-tooltip-flip-when-above t)
- (company-frontends '(company-pseudo-tooltip-unless-just-one-frontend
- company-preview-frontend
- company-echo-metadata-frontend))
- (company-transformers '(mango--company-require-prefix
- company-sort-by-backend-importance)))
-
-#+END_SRC
-
-** Math and Numbers
-
-*** Calc
-
-The built-in emacs calculator ~calc~ is genuinely the best calculator program I
-have ever used. Annoyingly though, it has a ‘trail’ that is always around. I
-don’t like it.
-
-#+BEGIN_SRC elisp
-
- (setq calc-display-trail nil)
-
-#+END_SRC
-
-*** Increment- and Decrement Number at Point
-
-This is a pretty standard Vim feature that I dearly miss having.
-
-#+BEGIN_SRC elisp
-
- (defun mango-increment-number-at-point (&optional arg)
- "Increment the number at point by ARG or 1 if ARG is nil. If called
- interactively, the universal argument can be used to specify ARG. If the number
- at point has leading zeros then the width of the number is preserved."
- (interactive "p*")
- (save-excursion
- (save-match-data
- (skip-chars-backward "0123456789")
- (when (eq (char-before (point)) ?-)
- (goto-char (1- (point))))
- (when (re-search-forward "-?\\([0-9]+\\)" nil t)
- (let ((answer (+ (string-to-number (match-string 0) 10)
- (or arg 1)))
- (width (length (match-string 1))))
- (replace-match
- (format
- (concat "%0" (int-to-string (if (< answer 0) (1+ width) width)) "d")
- answer)))))))
-
- (defun mango-decrement-number-at-point (&optional arg)
- "The same as ‘mango-increment-number-at-point’, but ARG is negated."
- (interactive "p*")
- (mango-increment-number-at-point (- (or arg 1))))
-
- (keymap-global-set "C-c a" #'mango-increment-number-at-point)
- (keymap-global-set "C-c x" #'mango-decrement-number-at-point)
-
-#+END_SRC
-
-** Programming
-
-*** Indentation
-
-Indentation in Emacs is a royal pain in the ass. Not only is there a
-~tab-width~ variable and the ~indent-tabs-mode~ mode, but lots of modes just
-have an extra ~tab-width~-esque variable for some reason? I try to fix this all
-with a custom function that reads a list of mode-specific indentation settings.
-
-#+BEGIN_SRC elisp
-
- (setq-default tab-width 8
- evil-shift-width 8
- indent-tabs-mode t)
-
- (defvar mango-indentation-settings
- '((bash-ts-mode :width 4)
- (c-mode :width 4 :extra-vars (c-basic-offset))
- (css-mode :extra-vars (css-indent-offset))
- (emacs-lisp-mode :spaces t)
- (graphviz-dot-mode :extra-vars (graphviz-dot-indent-width))
- (lisp-mode :spaces t)
- (org-mode :spaces t)
- (python-mode :width 4 :extra-vars (python-indent-offset))
- (rust-mode :width 4)
- (sgml-mode :width 2 :extra-vars (sgml-basic-offset))
- (sh-mode :width 4 :extra-vars (sh-basic-offset))
- (xml-mode :width 4))
- "A list of per-mode indentation settings. Each list contains a major-mode and
- the 3 optional keyword arguments of :spaces, :width, and :extra-vars. When
- setting the settings for a given major-mode, the settings will also be applied
- for that modes tree-sitter variant.
-
- If :spaces is non-nil, then indentation will be performed with spaces instead of
- tabs characters.
-
- If :width is non-nil, then it will override the modes given tab-width.
-
- If :extra-vars is non-nill, then it shall be a list of additional mode-specific
- variables that need to be assigned the desired indentation-width.")
-
- (defun mango-set-indentation-settings ()
- "Apply the indentation settings specified by ‘mango-indentation-settings’."
- (interactive)
- (dolist (plist mango-indentation-settings)
- (let* ((mode (car plist))
- (args (cdr plist))
- (width (or (plist-get args :width) (default-value 'tab-width)))
- (spaces (or (plist-get args :spaces) (not (default-value 'indent-tabs-mode))))
- (extra (plist-get args :extra-vars))
- (callback
- (λ (indent-tabs-mode (when spaces -1))
- (setq-local tab-width width
- evil-shift-width width)
- (dolist (var extra) (set var width)))))
- (add-hook (mango-mode-to-hook mode) callback 95)
- (add-hook (mango-mode-to-hook (mango-mode-to-ts-mode mode)) callback 95))))
-
- (mango-set-indentation-settings)
-
-#+END_SRC
-
-*** Git Integration
-
-I like to use Magit for my ~git~ integration. I do believe it is the best ~git~
-client ever made for any editor ever. Anyone who disagrees has simply never
-used Emacs before. The only command that really needs binding is
-~magit-status~. All other commands I will end up executing from there with the
-transient commands. I also install ~magit-todos~. It’s a super minimal package
-that simply finds all the TODOs in a repository and displays them in the
-~magit-status~ buffer so that I don’t forget them.
-
-#+BEGIN_SRC elisp
-
- (use-package magit
- :bind ("C-c g" . magit-status)
- :custom
- (magit-display-buffer-function
- #'magit-display-buffer-same-window-except-diff-v1))
-
- (use-package magit-todos
- :after magit
- :init (magit-todos-mode))
-
- (defun mango--magit-status ()
- (interactive)
- (thread-last
- (project-current t)
- (project-root)
- (magit-status)))
-
- (require 'project)
- (add-to-list 'project-switch-commands '(mango--magit-status "Git Status" ?g))
-
-#+END_SRC
-
-*** Tree-Sitter
-
-Emacs 29 brings native support for Tree-Sitter! This doesn’t just mean better-
-and faster syntax highlighting, but also things such as structured editing. In
-order to make use of Tree-Sitter the language parsers /do/ need to be installed,
-so let’s do that. Tree-Sitter doesn’t check to see if the language grammars are
-already installed unfortunately, but it’s easy enough to do manually.
-
-#+BEGIN_SRC elisp
-
- (setq treesit-language-source-alist
- '((bash "https://github.com/tree-sitter/tree-sitter-bash")
- (c "https://github.com/tree-sitter/tree-sitter-c")
- (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
- (css "https://github.com/tree-sitter/tree-sitter-css")
- (elisp "https://github.com/Wilfred/tree-sitter-elisp")
- (go "https://github.com/tree-sitter/tree-sitter-go")
- (gomod "https://github.com/camdencheek/tree-sitter-go-mod.git")
- (html "https://github.com/tree-sitter/tree-sitter-html")
- (json "https://github.com/tree-sitter/tree-sitter-json")
- (make "https://github.com/alemuller/tree-sitter-make")
- (markdown "https://github.com/ikatyang/tree-sitter-markdown")
- (python "https://github.com/tree-sitter/tree-sitter-python")
- (toml "https://github.com/tree-sitter/tree-sitter-toml")
- (yaml "https://github.com/ikatyang/tree-sitter-yaml")))
-
- (defun mango-treesit-install ()
- "Install all the tree-sitter grammars in ‘treesit-language-source-alist’.
- This function does not assert whether or not the grammar is already installed,
- making it useful for updating existing grammars."
- (interactive)
- (dolist (spec treesit-language-source-alist)
- (treesit-install-language-grammar (car spec))))
-
- ;; Automatically install missing grammars
- (thread-last
- (mapcar #'car treesit-language-source-alist)
- (seq-remove #'treesit-language-available-p)
- (mapc #'treesit-install-language-grammar))
-
-#+END_SRC
-
-I also prefer to have as many things syntax highlighted as possible when using
-tree-sitter, with the ability to fully customize faces (or turn off faces I
-don’t want highlighted).
-
-#+BEGIN_SRC elisp
-
- (setq treesit-font-lock-level 4)
-
-#+END_SRC
-
-*** Language Server Protocol
-
-LSP is an absolute necessity when programming. Luckily ~lsp-mode~ has us
-covered. There is Eglot which is now built in to Emacs 29, but it was giving me
-some really weird issues so I won’t be using that for now. ~lsp-bridge~ also
-looked promising but I didn’t like it too much. Also ~yasnippet~ is dead, so
-why they won’t just move on from it I have no idea. I’ll just disable snippets.
-
-#+BEGIN_SRC elisp
-
- (use-package lsp-mode
- :commands (lsp lsp-deferred)
- :custom
- (lsp-enable-snippet nil)
- :init
- (setq lsp-keymap-prefix "C-c l"))
-
-#+END_SRC
-
-I use ~pyright~ as my Python LSP only because I know no better option. It does
-need some this custom package though.
-
-#+BEGIN_SRC elisp
-
- (use-package lsp-pyright
- :hook (python-ts-mode . (lambda () (require 'lsp-pyright) (lsp))))
-
-#+END_SRC
-
-*** Emmet-Mode
-
-This mode is super useful for writing HTML. It lets me expand something like
-‘figure>(figcaption>code)+pre’ into an actual HTML structure.
-
-#+BEGIN_SRC elisp
-
- (use-package emmet-mode
- :hook (html-mode . emmet-mode))
-
-#+END_SRC
-
-*** Compilation Buffer
-
-Emacs allows you to compile your project with =C-x p c= which is cool and all,
-but it annoyingly creates a compilation buffer that I need to manually close
-every time. I would like to have this buffer, but only when things go wrong.
-
-#+BEGIN_SRC elisp
-
- (defun mango--compilation-make-window ()
- "Make a new vertical split for the compilation buffer if such a buffer doesn’t
- already exist."
- (unless (get-buffer "*compilation*")
- (split-window-vertically)))
-
- (defun mango--compilation-exit-autoclose (status code msg)
- "Automatically bury the compilation buffer and delete the compilation window
- if compilation was successful."
- (when (and (eq status 'exit)
- (zerop code))
- (bury-buffer)
- (delete-window (get-buffer-window "*compilation*")))
- (cons msg code))
-
- (add-hook 'compilation-mode-hook #'mango--compilation-make-window)
- (setq compilation-exit-message-function #'mango--compilation-exit-autoclose)
-
-#+END_SRC
-
-*** Projects
-
-I want to have all my projects automatically added to the project list.
-
-#+BEGIN_SRC elisp
-
- (project-remember-projects-under
- (or (getenv "REPODIR")
- (expand-file-name "code/repo" (getenv "HOME")))
- 'recursive)
-
-#+END_SRC
-
-** User Interface
-
-The default Emacs UI is fucking atrocious — we need to make it better.
-
-*** Cursor Blink
-
-I personally don’t like cursors blinking, so let’s disable that.
-
-#+BEGIN_SRC elisp
-
- (blink-cursor-mode -1)
-
-#+END_SRC
-
-*** Shorter Prompts
-
-For some reason emacs has both the ~y-or-n-p~ and ~yes-or-no-p~ functions. I do
-not like having to spell out full words — /y/ or /n/ is good enough for me — so
-let’s just redefine ~yes-or-no-p~.
-
-#+BEGIN_SRC elisp
-
- (fset #'yes-or-no-p #'y-or-n-p)
-
-#+END_SRC
-
-*** Disable Basic UI Modes
-
-Emacs has a lot of UI modes that are enabled by default to make life easier for
-the novice user. I am not the novice user and would rather these modes never
-turned on ever again.
-
-#+BEGIN_SRC elisp
-
- (menu-bar-mode -1)
- (scroll-bar-mode -1)
- (tool-bar-mode -1)
- (tooltip-mode -1)
-
-#+END_SRC
-
-*** Warnings
-
-I am typically not a fan around being warned about things unless something is
-actually breaking or going wrong.
-
-In order, these options disable the following warnings:
-
-1. Opening large files
-2. Following symbolic links
-3. Adding advice to functions
-
-#+BEGIN_SRC elisp
-
- (setq large-file-warning-threshold nil
- vc-follow-symlinks t
- ad-redefinition-action 'accept)
-
-#+END_SRC
-
-*** Visible Bell
-
-Why not? I might disable this later.
-
-#+BEGIN_SRC elisp
-
- (setq visible-bell t)
-
-#+END_SRC
-
-*** Scrolling
-
-By default, scrolling is really bad. Luckily we can improve it a lot; there’s
-even a pixel-precision scrolling mode!
-
-#+BEGIN_SRC elisp
-
- (setq mouse-wheel-scroll-amount '(1 ((shift) . 1))
- mouse-wheel-progressive-speed nil
- mouse-wheel-follow-mouse t
- scroll-step 1)
- ;; (pixel-scroll-precision-mode)
-
-#+END_SRC
-
-*** Auto Reverting Buffers
-
-This is just good to have all of the time; you should never be looking at a file
-whose state was changed by an external process, and not see those changes *instantly*.
-
-#+BEGIN_SRC elisp
-
- (setq global-auto-revert-non-file-buffers t)
- (global-auto-revert-mode)
-
-#+END_SRC
-
-*** Highlight Matching Parenthesis
-
-This is generally a good thing — especially when writing lisp code — but I don’t
-want this /everywhere/.
-
-#+BEGIN_SRC elisp
-
- (defvar mango-highlight-matching-parenthesis-modes
- '(prog-mode conf-mode)
- "A list of modes for which the parenthesis that pairs up with the parenthesis
- at point should be highlighted.")
-
- (show-paren-mode -1)
-
- (dolist (mode mango-highlight-matching-parenthesis-modes)
- (add-hook (mango-mode-to-hook mode) #'show-paren-local-mode))
-
-#+END_SRC
-
-*** Line- and Column Numbers
-
-I like to have line- and column numbers in my modeline. I find them to be very
-useful to have. I used to also like to have the current line number on the left
-of the screen, but I don’t really need that information.
-
-#+BEGIN_SRC elisp
-
- (line-number-mode)
- (column-number-mode)
-
-#+END_SRC
-
-*** Fonts
-
-My favorite monospace font has got to be /Iosevka/. It’s good looking, it’s far
-more compact than the usual american-sized monospace fonts, and you can
-customize just about every character from loads of variants. I have my own
-custom compiled variant called /Iosevka Smooth/.
-
-On the proportional side of things, I am not really sure what font to use.
-/Vollkorn/ tends to be my go-to serif-font on the web, but I dunno how well it
-translates to Emacs. I am also a bit fan of Ysabeau for sans-serif. I need to
-play around with this.
-
-#+BEGIN_SRC elisp
-
- (defvar mango-monospace-font '("Iosevka Smooth" :weight regular :height 162)
- "The default monospace font to use. This is a list containing a font name,
- font weight, and font height in that order.")
-
- (defvar mango-proportional-font '("Ysabeau" :weight light :height 180)
- "The default proportional font to use. This is a list containing a font name,
- font weight, and font height in that order.")
-
-#+END_SRC
-
-Actually /setting/ the fonts is a bit tricky. I don’t really fully understand
-why it works like this, but something with the whole server/client model of
-Emacs is really fucking with this, so we need to add a hook to set the font for
-every frame. We also can’t forget the frame that’s actually running this code.
-
-#+BEGIN_SRC elisp
-
- (defun mango-set-fonts ()
- "Set the fonts specified by ‘mango-monospace-font’ and ‘mango-proportional-font’."
- (interactive)
- (let* ((mono-family (car mango-monospace-font))
- (mono-props (cdr mango-monospace-font))
- (prop-family (car mango-proportional-font))
- (prop-props (cdr mango-proportional-font))
- (mono-weight (plist-get mono-props :weight))
- (mono-height (plist-get mono-props :height))
- (prop-weight (plist-get prop-props :weight))
- (prop-height (plist-get prop-props :height)))
- (set-face-attribute 'default nil
- :font mono-family
- :weight mono-weight
- :height mono-height)
- (set-face-attribute 'fixed-pitch nil
- :font mono-family
- :weight mono-weight
- :height mono-height)
- (set-face-attribute 'variable-pitch nil
- :font prop-family
- :weight prop-weight
- :height prop-height)))
-
- (add-hook 'after-make-frame-functions (lambda (_) (mango-set-fonts)))
- (mango-set-fonts)
-
-#+END_SRC
-
-*** Emacs Theme
-
-I previously ran the ~sanityinc-tomorrow-eighties~ theme, but I now run my own
-custom theme. I do like to keep the older theme around though as a reference.
-
-#+BEGIN_SRC elisp
-
- (use-package color-theme-sanityinc-tomorrow)
- (load-theme 'mango t)
-
-#+END_SRC
-
-*** Fringes
-
-It’s also nice to have fringes!
-
-#+BEGIN_SRC elisp
-
- (set-fringe-style (cons 32 32))
-
-#+END_SRC
-
-*** Line Highlighting
-
-This is just something I personally like having. It makes it very easy for me
-to figure out where my point is at all times.
-
-#+BEGIN_SRC elisp
-
- (global-hl-line-mode)
-
-#+END_SRC
-
-*** Frame Management
-
-Prot has made a fantastic package known as ~beframe~. It allows you to have
-each frame have its own buffer list. This is insanely useful for keeping my
-buffers nice and organized. It’s also useful for having a way to (for example)
-kill all buffers you were using in a frame before closing it.
-
-#+BEGIN_SRC elisp
-
- (defun mango-beframe-delete-frame ()
- "Kill all the buffers within the current beframe buffer list and then destroy
- the frame."
- (interactive)
- (cl-loop with buffers = (cl-loop for frame in (frame-list)
- unless (eq frame (selected-frame))
- append (beframe-buffer-list frame))
- for buffer in (beframe-buffer-list)
- unless (member buffer buffers)
- do (kill-buffer buffer))
- (delete-frame))
-
- (use-package beframe
- :bind ([remap delete-frame] . mango-beframe-delete-frame)
- :init
- (beframe-mode))
-
-#+END_SRC
-
-*** Transparency
-
-Transparency is totally useless, but it looks cool. Luckily transparent
-background support was added in Emacs 29!
-
-#+BEGIN_SRC elisp
-
- (defvar mango-alpha-background 90
- "The opacity of a graphical Emacs frame, ranging from 0–100. A value of 0 is
- fully transparent while a value of 100 is fully opaque.")
-
- (defun mango-set-alpha-background (value)
- "Set the current frames background opacity to VALUE."
- (interactive "NOpacity: ")
- (set-frame-parameter nil 'alpha-background value))
-
- (add-to-list 'default-frame-alist (cons 'alpha-background mango-alpha-background))
-
-#+END_SRC
-
-** Extra Modes
-
-Some modes aren’t installed by default with Emacs, so let’s fetch them
-
-#+BEGIN_SRC elisp
-
- (use-package git-modes)
- (use-package graphviz-dot-mode)
- (use-package markdown-mode)
-
-#+END_SRC
-
-** Auto-Start Modes
-
-Some modes need to be manually configured to automatically start when opening a
-file with a certain file extension.
-
-#+BEGIN_SRC elisp
-
- (require 'go-ts-mode)
- (require 'rust-ts-mode)
- (require 'yaml-ts-mode)
-
- (dolist (pair '(("\\.go\\'" . go-ts-mode)
- ("\\.py\\'" . python-ts-mode)
- ("\\.rs\\'" . rust-ts-mode)
- ("\\.ya?ml\\'" . yaml-ts-mode)))
- (add-to-list 'auto-mode-alist pair))
-
-#+END_SRC
-
-** Customization Settings
-
-Emacs has a /customization variable/ that contains various configuration-related
-settings that are set by different commands, as well as the customization UI. I
-would rather have these saved in a temporary file since any customizations I
-make that I would like to have be persistent will be explicitly written into
-this file.
-
-#+BEGIN_SRC elisp
-
- (setq custom-file
- (expand-file-name
- (format "emacs-custom-%s.el" (user-uid))
- temporary-file-directory))
- (load custom-file t)
-
-#+END_SRC
-
-** Backup Files
-
-It’s always good to have backups. I would know — I’ve wiped both =~= and
-=~/.config= before! Despite the name, ~version-control~ actually just adds
-version numbers to the backup names — it doesn’t start using a VCS.
-
-#+BEGIN_SRC elisp
-
- (setq delete-old-versions t
- version-control t
- kept-new-versions 2
- kept-old-versions 6)
-
-#+END_SRC
-
-** Mode Specific Settings
-
-*** Org-Mode
-
-**** Fontify Blockquotes
-
-I want to be able to customize the faces of blockquotes, so we need to enable
-that setting with ~org-mode~
-
-#+BEGIN_SRC elisp
-
- (setopt org-fontify-quote-and-verse-blocks t)
-
-#+END_SRC
-
-**** Visual Intentation
-
-I really enjoy using ~org-indent-mode~. It indents the contents of the
-org-buffer to keep everything looking nice and structured.
-
-#+BEGIN_SRC elisp
-
- (add-hook 'org-mode-hook #'org-indent-mode)
-
-#+END_SRC
-
-*** Auto-Fill-Mode
-
-I do like to use this mode, especially when writing. I also want it when coding
-/sometimes/, but not always. Overall it’s a pretty useful mode — but I am super
-mixed on when I do and -don’t want it. I always want it in ~org-mode~ though.
-
-#+BEGIN_SRC elisp
-
- (setq-default fill-column 80)
- (add-hook 'org-mode-hook #'auto-fill-mode)
-
-#+END_SRC
-
-** Auto-Directories
-
-When creating new files, the parent directories often don’t exist and I need to
-make them manually with =M-x make-directory RET RET=. I would prefer that these
-directories just get created automatically.
-
-#+BEGIN_SRC elisp
-
- (defun mango--auto-create-directories (original-function filename &rest args)
- "Automatically create and delete parent directories of files. This is an
- ‘:override’ advice for ‘find-file’ and friends. It automatically creates the
- parent directory (or directories) of the file being visited, if necessary. It
- also sets a buffer-local variable so that the user will be prompted to delete
- the newly created directories if they kill the buffer without saving it."
- (let (dirs-to-delete)
- (let* ((dir-to-create (file-name-directory filename))
- (current-dir dir-to-create))
- ;; We want to go up each directory component and add them to
- ;; ‘dirs-to-delete’ individually.
- (while (not (file-exists-p current-dir))
- (push current-dir dirs-to-delete)
- (setq current-dir (file-name-directory
- (directory-file-name current-dir))))
-
- (unless (file-exists-p dir-to-create)
- (make-directory dir-to-create t)))
-
- ;; Use ‘prog1’ so that we maintain the original return value
- (prog1 (apply original-function filename args)
- (when dirs-to-delete
- (setq-local mango--dirs-to-delete (reverse dirs-to-delete))
-
- ;; When we kill the buffer we want to ask if we should delete parent
- ;; directories *unless* the buffer was saved, in which case we don’t
- ;; want to do anything.
- (add-hook 'kill-buffer-hook #'mango--delete-directories-if-appropriate
- t t)
- (add-hook 'after-save-hook #'mango--remove-auto-directory-hooks t t)))))
-
- (dolist (command #'(find-file
- find-alternate-file
- write-file))
- (advice-add command :around #'mango--auto-create-directories))
-
- (defun mango--delete-directories-if-appropriate ()
- "Delete parent directories if appropriate. This is a function for
- ‘kill-buffer-hook’. If ‘mango--auto-create-directories’ created the directory
- containing the file for the current buffer automatically, then offer to delete
- it. Otherwise, do nothing. Also clean up related hooks."
- (unless (file-exists-p buffer-file-name)
- (dolist (dir-to-delete mango--dirs-to-delete)
- (when (and (stringp dir-to-delete)
- (file-exists-p dir-to-delete)
- (y-or-n-p (format "Also delete directory ‘%s’?"
- (directory-file-name dir-to-delete))))
- (delete-directory dir-to-delete)))))
-
- (defun mango--remove-auto-directory-hooks ()
- "Clean up directory-deletion hooks, if necessary."
- (remove-hook 'kill-buffer-hook #'mango--delete-directories-if-appropriate t)
- (remove-hook 'after-save-hook #'mango--remove-auto-directory-hooks t))
-
-#+END_SRC
-
-** Email
-
-#+BEGIN_SRC elisp
-
- (use-package mu4e
- :ensure nil
- :custom
- (user-full-name "Thomas Voss")
- (mu4e-change-filenames-when-moving t)
- (mu4e-get-mail-command "mbsync -a -c /home/thomas/.config/isync/mbsyncrc")
- (mu4e-maildir "~/mail")
- (sendmail-program "/usr/bin/msmtp")
- (send-mail-function 'smtpmail-send-it)
- (message-sendmail-f-is-evil t)
- (message-sendmail-extra-arguments '("--read-envelope-from"))
- (message-send-mail-function 'message-send-mail-with-sendmail)
- :config
- (setq mango--mu4e-personal-context
- (make-mu4e-context
- :name "Personal"
- :match-func
- (lambda (msg)
- (when msg
- (string-prefix-p "/mail@thomasvoss.com" (mu4e-message-field msg :maildir))))
- :vars '((user-mail-address . "mail@thomasvoss.com")
- (mu4e-drafts-folder . "/mail@thomasvoss.com/Drafts")
- (mu4e-sent-folder . "/mail@thomasvoss.com/Sent")
- (mu4e-refile-folder . "/mail@thomasvoss.com/Archive")
- (mu4e-trash-folder . "/mail@thomasvoss.com/Junk")
- (mu4e-maildir-shortcuts . '((:name "Inbox" :maildir "/mail@thomasvoss.com/Inbox" :key ?i)
- (:name "Archive" :maildir "/mail@thomasvoss.com/Archive" :key ?a)
- (:name "Drafts" :maildir "/mail@thomasvoss.com/Drafts" :key ?d)
- (:name "Sent" :maildir "/mail@thomasvoss.com/Sent" :key ?s)
- (:name "Junk" :maildir "/mail@thomasvoss.com/Junk" :key ?j))))))
- (setq mango--mu4e-legacy-context
- (make-mu4e-context
- :name "Legacy"
- :match-func
- (lambda (msg)
- (when msg
- (string-prefix-p "/thomasvoss@live.com" (mu4e-message-field msg :maildir))))
- :vars '((user-mail-address . "thomasvoss@live.com")
- (user-full-name . "Thomas Voss")
- (mu4e-drafts-folder . "/thomasvoss@live.com/Drafts")
- (mu4e-sent-folder . "/thomasvoss@live.com/Sent")
- (mu4e-refile-folder . "/thomasvoss@live.com/Archive")
- (mu4e-trash-folder . "/thomasvoss@live.com/Junk")
- (mu4e-maildir-shortcuts . '((:name "Inbox" :maildir "/thomasvoss@live.com/Inbox" :key ?i)
- (:name "POP" :maildir "/thomasvoss@live.com/POP" :key ?p)
- (:name "Archive" :maildir "/thomasvoss@live.com/Archive" :key ?a)
- (:name "Drafts" :maildir "/thomasvoss@live.com/Drafts" :key ?d)
- (:name "Sent" :maildir "/thomasvoss@live.com/Sent" :key ?s)
- (:name "Junk" :maildir "/thomasvoss@live.com/Junk" :key ?j))))))
- (setq mango--mu4e-humanwave-context
- (make-mu4e-context
- :name "Humanwave"
- :match-func
- (lambda (msg)
- (when msg
- (string-prefix-p "/thomas.voss@humanwave.nl" (mu4e-message-field msg :maildir))))
- :vars '((user-mail-address . "thomas.voss@humanwave.nl")
- (user-full-name . "Thomas Voss")
- (mu4e-drafts-folder . "/thomas.voss@humanwave.nl/[Gmail]/Drafts")
- (mu4e-sent-folder . "/thomas.voss@humanwave.nl/[Gmail]/Sent Mail")
- (mu4e-refile-folder . "/thomas.voss@humanwave.nl/[Gmail]/All Mail")
- (mu4e-trash-folder . "/thomas.voss@humanwave.nl/[Gmail]/Trash")
- (mu4e-maildir-shortcuts . '((:name "Inbox" :maildir "/thomas.voss@humanwave.nl/Inbox" :key ?i)
- (:name "Archive" :maildir "/thomas.voss@humanwave.nl/[Gmail]/All Mail" :key ?a)
- (:name "Drafts" :maildir "/thomas.voss@humanwave.nl/[Gmail]/Drafts" :key ?d)
- (:name "Sent" :maildir "/thomas.voss@humanwave.nl/[Gmail]/Sent Mail" :key ?s)
- (:name "Junk" :maildir "/thomas.voss@humanwave.nl/[Gmail]/Junk" :key ?j))))))
-
- (setq mu4e-contexts (list mango--mu4e-personal-context
- mango--mu4e-legacy-context
- mango--mu4e-humanwave-context)))
-
-#+END_SRC