diff options
Diffstat (limited to '.config/emacs/config.org')
-rw-r--r-- | .config/emacs/config.org | 1396 |
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 |