summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--emacs/init.el1023
1 files changed, 531 insertions, 492 deletions
diff --git a/emacs/init.el b/emacs/init.el
index fa1a872..1700883 100644
--- a/emacs/init.el
+++ b/emacs/init.el
@@ -4,14 +4,7 @@
 
 ;; (profiler-start 'cpu+mem)
 
-;; Things we have, or want to have, in this configuration:
-;; - Make a nice interface
-;; - Programming
-;; - Offline web browsing
-;; - Reading mail
-;; - Sending mail
-;; - Bibliography management
-;; - Taking notes
+;;; Getting started
 
 (add-to-list 'load-path (expand-file-name (concat user-emacs-directory "site-lisp")))
 
@@ -30,9 +23,7 @@
 ;; keep the emacs directory clean
 (use-package no-littering)
 
-;; It is often useful to be able to run a command while i am already in the process of running a command in the minibuffer.  This is rarely two extended commands; usually it is completion.
-(use-package mb-depth
-  :hook (after-init . minibuffer-depth-indicate-mode))
+;;; Make a nice interface
 
 (use-package tubthumping-theme
   :disabled t
@@ -40,7 +31,6 @@
   :config
   (load-theme 'tubthumping t))
 
-;;; Fonts
 ;; Using a proportional font is the right way to do things, but emacs is very old and comes from a time before the innovation of legibility.  As a result, there are some things that require a monospaced font, so i set one here.
 (custom-set-faces
  '(default ((t (:family "Noto Sans Mono" :height 120))))
@@ -80,55 +70,24 @@
   :bind (([remap scroll-up-command] . pixel-scroll-interpolate-down)
          ([remap scroll-down-command] . pixel-scroll-interpolate-up)))
 
-;; Update the calendar.  We want weeks to start on a monday, the first day of the week.  Holidays should be highlighted, and the date format should put the year first.
-(use-package calendar
-  :defer t
-  :custom
-  (calendar-week-start-day 1)
-  (calendar-date-style 'iso)
+;; Nicer terminal emacs
+(set-display-table-slot standard-display-table 'vertical-border (make-glyph-code ?│))
+(setq mode-line-end-spaces nil)
 
-  :config
-  ;; Ensure the calendar always displays at the bottom of the screen, rather than wrapping weirdly and looking bad when it shows up in a side window.
-  (add-to-list 'display-buffer-alist
-             '("\\*Calendar\\*"
-               (display-buffer-in-side-window)
-               (side . bottom))))
+(use-package xclip)
+(unless (display-graphic-p) (xclip-mode 1))
 
-;; Squeeze text into a more legible sliver.
-(use-package visual-fill-column
-  :custom
-  (visual-fill-column-enable-sensible-window-split t)
-  (visual-fill-column-fringes-outside-margins nil) ; Keep fringes on the inside so relevant icons are in the right place
-  (visual-fill-column-center-text t)
-  :hook
-  ((text-mode . visual-fill-column-mode)
-   (eww-after-render . visual-line-fill-column-mode))
-  )
 
-(use-package simple
-  :hook ((text-mode . visual-line-mode)
-         (after-init . auto-save-mode))
-  :bind ([remap kill-buffer] . kill-current-buffer)
-  :custom
-  (save-interprogram-paste-before-kill t)
-  (kill-ring-max 25)
-  ;; Hide commands in M-x which do not work in the current mode.
-  (read-extended-command-predicate 'command-completion-default-include-p)
-  ;; My brain is not ready for advanced emacs undo
-  (undo-no-redo t)
-  :bind (("C-z" . undo)
-         ("C-S-z" . undo-redo)))
 
-;; (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)
+;; Don't advertise gnu on startup
+(setq inhibit-startup-echo-area-message "noa")  ;; #userfreedom
 
-;; Doesn't indent nicely with proportional fonts
-;; (use-package adaptive-wrap :ensure t)
-;; (add-hook 'visual-fill-column-mode-hook #'visual-wrap-prefix-mode)
 
 ;; Make tooltip information appear consistently in the echo area
 (use-package tooltip
   :custom (tooltip-mode nil))
 
+
 ;; startup.el does not provide itself, cannot be used with use-package
 (setopt inhibit-startup-screen t)
 
@@ -146,290 +105,30 @@
 ;; Doesn't work great with a proportional font but could still be useful.
 (global-set-key (kbd "C-S-<down-mouse-1>") #'mouse-drag-region-rectangle)
 
-;; Markdown
-(use-package markdown-mode
-  :mode ("\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" . markdown-mode)
-  :custom
-  (markdown-disable-tooltip-prompt t) ; When inserting a link, only prompt for url and link text
-  (markdown-enable-html nil) ; I don't believe markdown should have html
-  (markdown-hide-urls t) ; Make inline urls look a bit neater
-  (markdown-url-compose-char ?~)
-  (markdown-max-image-size '(640 . 480))
-
-  ;; :hook ((markdown-mode . noa/set-buffer-name-to-markdown-title))
-  )
-
-;; Use abbrev as a simple autocorrect mode.  M-x list-abbrevs for stats.
-;; Check  ~/.config/emacs/abbrev_defs to update any mistakes
-(use-package abbrev
-  :hook (text-mode . abbrev-mode))
-
-(use-package flyspell
-  :hook (text-mode . flyspell-mode)
-  :custom
-  (flyspell-abbrev-p t))
-
-(use-package writegood-mode
-  :hook (text-mode . writegood-mode))
-
-;; A nicer package for spellchecking
-(use-package jinx
-  :disabled t
-  :hook (text-mode . jinx-mode)
-  :commands (jinx-correct jinx-correct-all)
-  :config
-  (put 'jinx-overlay 'mouse-face '(jinx-misspelled highlight)) ;; Use standard mouse face for jinx overlays
-  (add-to-list 'jinx-exclude-regexps '(t "\\cc")) ;; Don't check chinese characters
-  )
-
-;; Automatically creates an abbrev for words i correct, so i don't make the same mistake twice
-(defun jinx--add-to-abbrev (overlay word)
-  "Add abbreviation to `global-abbrev-table'.
-The misspelled word is taken from OVERLAY.  WORD is the corrected word."
-  (let ((abbrev (buffer-substring-no-properties
-                 (overlay-start overlay)
-                 (overlay-end overlay))))
-    (message "Abbrev: %s -> %s" abbrev word)
-    (define-abbrev global-abbrev-table abbrev word)))
-(advice-add 'jinx--correct-replace :before #'jinx--add-to-abbrev)
-
-(use-package help
-  :defer t
-  :custom
-  ;; When opening source files from a help window, use the same window
-  (help-window-keep-selected t))
-
-;; Despite the name, eww is a delight to use for text-heavy websites.
-(use-package browse-url
-  :defer t
-  :custom
-  (browse-url-browser-function 'noa/record-url)
-	(browse-url-secondary-browser-function 'browse-url-default-browser))
-
-(use-package shr
-  :defer t
-  :custom
-  (shr-cookie-policy nil)
-  (shr-use-colors nil) ;; I like the colours i already have set
-  (shr-max-width nil) ;; Don't insert hard linebreaks to wrap paragraphs
-  (shr-bullet " • ")
-  (shr-discard-aria-hidden t))
-
-(use-package eww
-  :defer t
-
-  :custom
-  ;; Better buffer naming for eww buffers.  See the help for a function which gives the page title and the url.
-  (eww-auto-rename-buffer 'title))
-
-;; Goto address mode makes urls and email address in a buffer clickable.  I want these clickable links to look like links, because that's what they are.  The two mouse face variables are what face is used on hover, which at the moment i ignore.  It might also be worth setting them to 'highlight.
-(use-package goto-addr
-  :hook (after-init . global-goto-address-mode)
-  :custom
-  (goto-address-mail-face 'link)
-  (goto-address-mail-mouse-face 'highlight)
-  (goto-address-url-face 'link)
-  (goto-address-url-mouse-face 'highlight))
-
-(use-package flymake
-  :hook (prog-mode . flymake-mode))
-
-(use-package emacs
-  :custom
-  (window-combination-resize t)
-  (window-resize-pixelwise t)
-  (frame-resize-pixelwise t)
-  (shell-file-name "/bin/sh")
-  (completion-ignore-case t)
-  (read-buffer-completion-ignore-case t)
-  ;; yes-or-no-p uses y-or-no-p, and read-answer accepts single key press answers
-  (use-short-answers t)
-  (history-length 250)
-  ;; Use a bar cursor and blink it and don't stop blinking it.
-  (cursor-type 'bar)
-  ;; Support opening new minibuffers from inside existing minibuffers.
-  (enable-recursive-minibuffers t)
-  ;; Whether to drop into the debugger on any error.  This seems cool, but in practice is a bit annoying.
-  (debug-on-error nil)
-  (create-lockfiles nil)
-  (auto-save-interval 6)  ;; every six keystrokes
-  (auto-save-timeout 5) ;; every 5 seconds
-  (auto-save-no-message t)
-  ;; Always give me a line of text when i'm near the edges of the screen
-  (scroll-margin 2))
-
-(use-package dired
-  :defer t
-  :custom
-  (dired-recursive-deletes 'always) ;; Don't ask me things
-  (dired-recursive-copies 'always) ;; Don't ask me things
-  (dired-dwim-target t) ;; Synergy between two dired windows
-  ;; I have quite a bit of storage and also make bad decisions regularly
-  ;; People who have used computers in the last forty years or so will likely be familiar with this innovation.
-  (delete-by-moving-to-trash t)
-
+;;; Minibuffer
 
+;; It is often useful to be able to run a command while i am already in the process of running a command in the minibuffer.  This is rarely two extended commands; usually it is completion.
 
-  ;; -l is required by dired
-  ;; -a to also show dotfiles
-  ;; -v to sort numbers properly
-  ;; -F to visually distinguish different types of file
-  ;; -h for human readable file sizes
-  ;; I won't explain the meaning of the long flag.
-  (dired-listing-switches "-alvFh --group-directories-first")
-
-;; By default, don't show dired details
-  :hook ((dired-mode . dired-hide-details-mode)
-         (dired-mode . hl-line-mode))
-  )
-
-;; Show the recursive size of directories
-(use-package dired-du
-  :disabled t
-  :hook (dired-mode . dired-du-mode)
-  :after dired
-  :custom
-  (dired-du-size-format t))
-
-;; Some tramp settings.
-(use-package files
-  :custom
-  (view-read-only t)
-  (confirm-kill-emacs 'y-or-n-p)
-  ;; We are on a unix system, so it makes sense to end files in the unix system way.  I'm surprised this isn't the default.
-  (require-final-newline t)
-
-  ;; This behaviour changes how we visit symlinks.
-  (find-file-visit-truename t)
-
-  (remote-file-name-inhibit-locks t)
-  (remote-file-name-inhibit-auto-save t)
-  (remote-file-name-inhibit-auto-save-visited t)
-  (backup-directory-alist '(("." . "~/.config/emacs/backups/")))
-  (make-backup-files nil)
-  (backup-by-copying t)
-  (version-control t)
-  ;; this will auto save to the current file
-  (auto-save-visited-mode t)
-  (auto-save-visited-interval 0.1)
-  (save-silently t) ;; don't message when saving
-  )
-(use-package tramp
-  :defer t
-  :custom
-  (tramp-inline-compress-start-size 1000)
-  (tramp-verbose 3))
-;; (add-to-list 'tramp-remote-path 'tramp-own-remote-path)
-
-;; I only use git, and this apparently speeds up tramp.
-(use-package vc-hooks
-  :after vc
-  :custom
-  (vc-handled-backends '(Git)))
-
-;;  It seems that tramp can also be made faster with these .ssh/config settings.
-
-;; Host *
-;;      ControlMaster auto
-;;      ControlPath ~/.ssh/master-%h:%p
-;;      ControlPersist 10m
-;;      ForwardAgent yes
-;;      ServerAliveInterval 60
-
-;; Don't override display actions when i manually initiate a buffer switch.
-(use-package window
-  :custom
-  ;; My computer has a small screen, so i find that it's more beneficial for me to split the frame into columns, so i get more context.  However, splitting in this way only gives me a (window-width) of 61, so emacs will always split into vertically stacked windows.  By setting this to 80, the first split should always be vertical.
-  (split-width-threshold 80)
-  (switch-to-buffer-obey-display-actions t)
-  ;; C-l from top to bottom
-  (recenter-positions '(top middle bottom)))
-
-(use-package frame
-  :hook (after-init . window-divider-mode)
-  :custom
-  (window-divider-default-right-width 1)
-  (window-divider-default-bottom-width 1)
-  (window-divider-default-places t))
-
-;; Taken from configuration for the vertico stack:
-;; Add prompt indicator to `completing-read-multiple'.
-;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
-(defun crm-indicator (args)
-  (cons (format "[CRM%s] %s"
-		(replace-regexp-in-string
-		 "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
-		 crm-separator)
-		(car args))
-	(cdr args)))
-(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
-
-(use-package recentf
-  :hook (after-init . recentf-mode)
-  :custom
-  (recentf-max-menu-items 50))
-
-(use-package bookmark
-  :defer t
-  :custom
-  (bookmark-default-file "~/.config/emacs/bookmarks"))
-
-;; Unfill commands
-(defun unfill-paragraph ()
-  "Takes a multi-line paragraph and makes it into a single line of text."
-  (interactive)
-  (let ((fill-column (point-max)))
-    (fill-paragraph nil)))
-(global-set-key (kbd "M-Q") #'unfill-paragraph)
-
-;; Remember my position in files
-(use-package saveplace
-  :hook (after-init . save-place-mode))
-
-;; Disable disabled commands
-(setq disabled-command-function nil)
-
-;; Don't save changes in the customize interface
-(use-package cus-edit
-  :custom
-  (custom-file (make-temp-file "custom")))
-
-;; Scroll along with text in compilation mode, and stop scrolling at the first error.
-(use-package compile
-  :defer t
-  :custom
-  (compilation-scroll-output 'first-error))
-
-;; Don't advertise gnu on startup
-(setq inhibit-startup-echo-area-message "noa")  ;; #userfreedom
-
-;; Better buffer naming
-(use-package uniquify
-  :custom
-  (uniquify-after-kill-buffer-p t)
-  (uniquify-buffer-name-style 'forward)
-  (uniquify-ignore-buffers-re "^\\*")
-  (uniquify-separator "/"))
-
-;; Emacs server
-;; (unless (server-running-p) (server-start)))
-
-;; Better support for long lines.
-(use-package so-long
-  :hook (after-init . global-so-long-mode))
-
-;; Automatically revert buffers when they change on disk.  This doesn't apply to tramp.
-(use-package autorevert
-  :hook (after-init . global-auto-revert-mode)
+(use-package savehist
   :custom
-  (global-auto-revert-non-file-buffers t))
+  (savehist-file "~/.config/emacs/savehist")
+  (savehist-additional-variables
+        '(kill-ring
+          command-history
+          set-variable-value-history
+          custom-variable-history
+          query-replace-history
+          read-expression-history
+          minibuffer-history
+          read-char-history
+          face-name-history
+          bookmark-history
+          file-name-history))
+  :hook (after-init . savehist-mode))
 
-(use-package vc-hooks
-  :custom
-  (vc-follow-symlinks 'ask))
+(use-package mb-depth
+  :hook (after-init . minibuffer-depth-indicate-mode))
 
-;; Minibuffer candidate completion
-;; Display completion candidates vertically
 (use-package vertico
   :hook (after-init . vertico-mode)
   :custom
@@ -464,6 +163,19 @@ The misspelled word is taken from OVERLAY.  WORD is the corrected word."
                cand)))
 
 ;; Annotations for completing-read
+
+;; Taken from configuration for the vertico stack:
+;; Add prompt indicator to `completing-read-multiple'.
+;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
+(defun crm-indicator (args)
+  (cons (format "[CRM%s] %s"
+		(replace-regexp-in-string
+		 "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
+		 crm-separator)
+		(car args))
+	(cdr args)))
+(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
+
 (use-package marginalia
   :defer t
   :hook (after-init . marginalia-mode)
@@ -485,24 +197,7 @@ The misspelled word is taken from OVERLAY.  WORD is the corrected word."
   (completion-styles '(orderless basic))
   (completion-category-overrides '((file (styles basic partial-completion)))))
 
-;; History
 
-(use-package savehist
-  :custom
-  (savehist-file "~/.config/emacs/savehist")
-  (savehist-additional-variables
-        '(kill-ring
-          command-history
-          set-variable-value-history
-          custom-variable-history
-          query-replace-history
-          read-expression-history
-          minibuffer-history
-          read-char-history
-          face-name-history
-          bookmark-history
-          file-name-history))
-  :hook (after-init . savehist-mode))
 
 (use-package consult
   :defer t
@@ -524,23 +219,79 @@ The misspelled word is taken from OVERLAY.  WORD is the corrected word."
   ;; (completion-in-region-function 'consult-completion-in-region)
   )
 
-(use-package consult-recoll
-  :bind ("<menu>" . consult-recoll)
+;;; Dictionary
+;; Emacs has built in support for interfacing with dictd.  With dictd and some dictionaries installed on debian, this works fine out of the box.
+;; Unfortunately, dictionaries in this format tend to be hard wrapped and there isn't a lot of coverage outside of english
+(use-package dictionary
+  :defer t
   :custom
-  (consult-recoll-inline-snippets nil)
-  (consult-recoll-group-by-mime nil))
+  (dictionary-search-interface nil))
 
-;; Some settings for nicer completion with the default emacs completion buffer.  I don't use this, because i use vertico.
-;; (setopt completion-auto-help 'lazy
-;;         completion-auto-select 'second-tab
-;;         completion-show-help nil
-;;         completions-sort nil
-;;         completions-header-format nil)
+;;; Offline web browsing
 
-;; Completion styles
-;; When given a prompt to select from a list of candidates, there are quite a lot of things we can tweak to improve the experience.
 
-;; Make leading whitespace a uniform width, so indentation still works with proportional fonts
+;; Despite the name, eww is a delight to use for text-heavy websites.
+(use-package browse-url
+  :defer t
+  :custom
+  (browse-url-browser-function 'noa/record-url)
+	(browse-url-secondary-browser-function 'browse-url-default-browser))
+
+(use-package shr
+  :defer t
+  :custom
+  (shr-cookie-policy nil)
+  (shr-use-colors nil) ;; I like the colours i already have set
+  (shr-max-width nil) ;; Don't insert hard linebreaks to wrap paragraphs
+  (shr-bullet " • ")
+  (shr-discard-aria-hidden t))
+
+(use-package eww
+  :defer t
+
+  :custom
+  ;; Better buffer naming for eww buffers.  See the help for a function which gives the page title and the url.
+  (eww-auto-rename-buffer 'title))
+
+;; Goto address mode makes urls and email address in a buffer clickable.  I want these clickable links to look like links, because that's what they are.  The two mouse face variables are what face is used on hover, which at the moment i ignore.  It might also be worth setting them to 'highlight.
+(use-package goto-addr
+  :hook (after-init . global-goto-address-mode)
+  :custom
+  (goto-address-mail-face 'link)
+  (goto-address-mail-mouse-face 'highlight)
+  (goto-address-url-face 'link)
+  (goto-address-url-mouse-face 'highlight))
+
+
+(defvar noa/record-url-file "~/Documents/Library/urls")
+(defun noa/record-url (url &rest args)
+  "An alternative browser function that appends the url to a file.  We do not pay attention to any arguments."
+  (write-region (concat url "\n") nil noa/record-url-file 'append))
+
+;;; Programming
+
+(use-package flymake
+  :hook (prog-mode . flymake-mode))
+
+;; If i write a script, i will always run chmod +x after saving it.  This command means i don't have to do that.
+(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
+
+(use-package sly
+  :disabled t
+  :if (executable-find "sbcl")
+  :ensure t
+  :commands 'sly
+  :custom ((inferior-lisp-program (executable-find "sbcl"))
+           (common-lisp-hyperspec-root "file:///usr/share/doc/hyperspec/"))
+  :mode ((rx (or ".lisp" ".cl") eos) . common-lisp-mode))
+
+;; Write url to text file
+(use-package ediff
+  :defer t
+  :custom
+  (ediff-window-setup-function 'ediff-setup-windows-plain)
+  (ediff-split-window-function 'split-window-horizontally))
+
 (use-package elastic-indent
   :disabled t
   :hook (prog-mode . elastic-indent-mode))
@@ -562,74 +313,69 @@ The misspelled word is taken from OVERLAY.  WORD is the corrected word."
             (setq indent-tabs-mode
                   (if (apply #'derived-mode-p space-indent-modes) nil t))))
 
-;; Make sentence navigation commands work for people who are wrong
-;; NOTE: paragraphs does not provide itself
-(use-package paragraphs
-  :disabled t
+;; Scroll along with text in compilation mode, and stop scrolling at the first error.
+(use-package compile
+  :defer t
   :custom
-  (sentence-end-double-space nil))
+  (compilation-scroll-output 'first-error))
 
-;; If i write a script, i will always run chmod +x after saving it.  This command means i don't have to do that.
-(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
 
-;; Zap up to char
-(global-set-key (kbd "M-z") 'zap-up-to-char)
+;;; Writing prose
 
-;; Insert date and time at point
-;; Sometimes it's useful to be able to add a timestamp.  I use this for notes.  Use the same binding as notepad.exe.
-(defun noa/insert-date (&optional time)
-  "Insert the current date into the buffer.
-With prefix argument TIME, also add the current time."
-  (interactive "P")
-  (if time
-      (insert (format-time-string "[%Y-%m-%d %H:%M]"))
-    (insert (format-time-string "[%Y-%m-%d]"))))
-(defun noa/insert-datetime ()
-  (interactive)
-  (noa/insert-date t))
 
-(global-set-key (kbd "<f5>") 'noa/insert-date-time)
+;; Use abbrev as a simple autocorrect mode.  M-x list-abbrevs for stats.
+;; Check  ~/.config/emacs/abbrev_defs to update any mistakes
+(use-package abbrev
+  :hook (text-mode . abbrev-mode))
 
-;; Improve incremental search
-(use-package isearch
-  :bind (:map isearch-mode-map
-              ("<return>" . noa/isearch-done-goto-start) ; always put point at the beginning of the match
-              ("C-g" . isearch-cancel) ; always quit on C-g
-              ("C-o" . isearch-occur))
-  :init (defun noa/isearch-done-goto-start (&optional nopush edit)
-          "End isearch at the start of the match."
-          (interactive)
-          (funcall #'isearch-done nopush edit)
-          (when isearch-forward (goto-char isearch-other-end)))
+(use-package flyspell
+  :hook (text-mode . flyspell-mode)
   :custom
-  (isearch-lax-whitespace t)
-  (isearch-lazy-count t)
-  (isearch-allow-motion t)
-  (isearch-repeat-on-direction-change t)
-  (isearch-wrap-pause 'no))
+  (flyspell-abbrev-p t))
 
-(use-package avy
-  :bind (("M-j" . avy-goto-char-timer)
-	 :map isearch-mode-map
-	 ("M-j" . avy-isearch))
-  :custom
-  (avy-keys (number-sequence ?a ?z))
-  (avy-timeout-seconds 0.2))
+(use-package writegood-mode
+  :hook (text-mode . writegood-mode))
 
-;; Make window management commands easier to press
-(use-package ace-window
-  :bind ([remap other-window] . ace-window))
+;; A nicer package for spellchecking
+(use-package jinx
+  :disabled t
+  :hook (text-mode . jinx-mode)
+  :commands (jinx-correct jinx-correct-all)
+  :config
+  (put 'jinx-overlay 'mouse-face '(jinx-misspelled highlight)) ;; Use standard mouse face for jinx overlays
+  (add-to-list 'jinx-exclude-regexps '(t "\\cc")) ;; Don't check chinese characters
+  )
+
+;; Automatically creates an abbrev for words i correct, so i don't make the same mistake twice
+(defun jinx--add-to-abbrev (overlay word)
+  "Add abbreviation to `global-abbrev-table'.
+The misspelled word is taken from OVERLAY.  WORD is the corrected word."
+  (let ((abbrev (buffer-substring-no-properties
+                 (overlay-start overlay)
+                 (overlay-end overlay))))
+    (message "Abbrev: %s -> %s" abbrev word)
+    (define-abbrev global-abbrev-table abbrev word)))
+(advice-add 'jinx--correct-replace :before #'jinx--add-to-abbrev)
 
-(defun snarf-song (url)
-  (interactive "sYoutube url:")
-  (async-shell-command
-   (concat "yt-dlp -x --audio-format=mp3 -o "
-           (shell-quote-argument "~/media/music/%(title)s [%(id)s].%(ext)s")
-           " "
-           (shell-quote-argument url))))
 
 
-;;; Email
+
+;; Make sentence navigation commands work for people who are wrong
+;; NOTE: paragraphs does not provide itself
+(use-package paragraphs
+  :disabled t
+  :custom
+  (sentence-end-double-space nil))
+
+;; Unfill commands
+(defun unfill-paragraph ()
+  "Takes a multi-line paragraph and makes it into a single line of text."
+  (interactive)
+  (let ((fill-column (point-max)))
+    (fill-paragraph nil)))
+(global-set-key (kbd "M-Q") #'unfill-paragraph)
+
+;;; Reading mail
 
 ;; I keep coming back to rmail, despite its many, many warts.
 ;; Currently i fetch mail into Spool.mbox with fdm, then rmail moves it into Inbox.mbox.
@@ -656,6 +402,8 @@ With prefix argument TIME, also add the current time."
   ;; ((rmail-show-message . visual-line-fill-column-mode))
   )
 
+;;; Sending mail
+
 (use-package message
   :defer t
   :init
@@ -680,20 +428,6 @@ With prefix argument TIME, also add the current time."
   (message-interactive t)
   (message-auto-save-directory "~/Documents/Library/Mail/drafts"))
 
-;; Environment variables
-(setenv "PAGER" "cat")
-(setenv "TERM" "dumb")
-(setenv "NO_COLOR")
-(setenv "GPG_AGENT_INFO" nil)
-
-;; Dictionary
-;; Emacs has built in support for interfacing with dictd.  With dictd and some dictionaries installed on debian, this works fine out of the box.
-;; Unfortunately, dictionaries in this format tend to be hard wrapped and there isn't a lot of coverage outside of english
-(use-package dictionary
-  :defer t
-  :custom
-  (dictionary-search-interface nil))
-
 ;;; Bibliography management
 ;; TODO: update this to fit with the new single org file notes system.  This way should be easier than the previous method i think.
 (use-package ebib
@@ -767,11 +501,7 @@ With prefix argument TIME, also add the current time."
   (biblio-bibtex-use-autokey t)
   (biblio-download-directory "~/Documents/Library/Papers"))
 
-
-(defun noa/yank-buffer-file-name ()
-  (interactive)
-  (when (buffer-file-name)
-    (kill-new (file-name-nondirectory (buffer-file-name)))))
+;;; Taking notes
 
 (use-package org
   :defer t
@@ -865,6 +595,182 @@ With prefix argument TIME, also add the current time."
   (consult-ripgrep "~/Documents/Notes"))
 (global-set-key (kbd "C-j C-s") #'noa/consult-rg-notes)
 
+
+(defvar note/directory "~/Documents/Notes"
+  "The directory in which notes are stored.")
+(defvar note/file-ending "md"
+  "The file ending for new notes.")
+
+(defun note/new (&optional other-window)
+  (interactive "P")
+  (let* ((uid (format-time-string "%Y-%m-%d-%H%M%S"))
+         (note (concat note/directory "/" uid "." note/file-ending)))
+    (if other-window
+        (progn
+          (insert "[[./" uid ".org]]")
+          (find-file-other-window note))
+      (find-file note))
+    ))
+
+
+;; Markdown
+(use-package markdown-mode
+  :mode ("\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" . markdown-mode)
+  :custom
+  (markdown-disable-tooltip-prompt t) ; When inserting a link, only prompt for url and link text
+  (markdown-enable-html nil) ; I don't believe markdown should have html
+  (markdown-hide-urls t) ; Make inline urls look a bit neater
+  (markdown-url-compose-char ?~)
+  (markdown-max-image-size '(640 . 480))
+
+  ;; :hook ((markdown-mode . noa/set-buffer-name-to-markdown-title))
+  )
+
+;;; System administration
+
+(use-package proced
+  :defer t
+  :custom
+  (proced-auto-update-flag t)
+  (proced-auto-update-interval 1)
+  (proced-enable-color-flag nil)
+  (proced-show-remote-processes t))
+
+
+;;; Completion
+
+;; Some settings for nicer completion with the default emacs completion buffer.  I don't use this, because i use vertico.
+;; (setopt completion-auto-help 'lazy
+;;         completion-auto-select 'second-tab
+;;         completion-show-help nil
+;;         completions-sort nil
+;;         completions-header-format nil)
+
+;; Completion styles
+;; When given a prompt to select from a list of candidates, there are quite a lot of things we can tweak to improve the experience.
+
+;;; File management
+
+(use-package consult-recoll
+  :bind ("<menu>" . consult-recoll)
+  :custom
+  (consult-recoll-inline-snippets nil)
+  (consult-recoll-group-by-mime nil))
+
+(use-package dired
+  :defer t
+  :custom
+  (dired-recursive-deletes 'always) ;; Don't ask me things
+  (dired-recursive-copies 'always) ;; Don't ask me things
+  (dired-dwim-target t) ;; Synergy between two dired windows
+  ;; I have quite a bit of storage and also make bad decisions regularly
+  ;; People who have used computers in the last forty years or so will likely be familiar with this innovation.
+  (delete-by-moving-to-trash t)
+
+
+
+  ;; -l is required by dired
+  ;; -a to also show dotfiles
+  ;; -v to sort numbers properly
+  ;; -F to visually distinguish different types of file
+  ;; -h for human readable file sizes
+  ;; I won't explain the meaning of the long flag.
+  (dired-listing-switches "-alvFh --group-directories-first")
+
+;; By default, don't show dired details
+  :hook ((dired-mode . dired-hide-details-mode)
+         (dired-mode . hl-line-mode))
+  )
+
+;; Show the recursive size of directories
+(use-package dired-du
+  :disabled t
+  :hook (dired-mode . dired-du-mode)
+  :after dired
+  :custom
+  (dired-du-size-format t))
+
+
+(use-package recentf
+  :hook (after-init . recentf-mode)
+  :custom
+  (recentf-max-menu-items 50))
+
+(use-package bookmark
+  :defer t
+  :custom
+  (bookmark-default-file "~/.config/emacs/bookmarks"))
+
+;; Remember my position in files
+(use-package saveplace
+  :hook (after-init . save-place-mode))
+
+
+;;; Window and buffer navigation
+
+(use-package isearch
+  :bind (:map isearch-mode-map
+              ("<return>" . noa/isearch-done-goto-start) ; always put point at the beginning of the match
+              ("C-g" . isearch-cancel) ; always quit on C-g
+              ("C-o" . isearch-occur))
+  :init (defun noa/isearch-done-goto-start (&optional nopush edit)
+          "End isearch at the start of the match."
+          (interactive)
+          (funcall #'isearch-done nopush edit)
+          (when isearch-forward (goto-char isearch-other-end)))
+  :custom
+  (isearch-lax-whitespace t)
+  (isearch-lazy-count t)
+  (isearch-allow-motion t)
+  (isearch-repeat-on-direction-change t)
+  (isearch-wrap-pause 'no))
+
+(use-package avy
+  :bind (("M-j" . avy-goto-char-timer)
+	 :map isearch-mode-map
+	 ("M-j" . avy-isearch))
+  :custom
+  (avy-keys (number-sequence ?a ?z))
+  (avy-timeout-seconds 0.2))
+
+;; Make window management commands easier to press
+(use-package ace-window
+  :bind ([remap other-window] . ace-window))
+
+
+;; Better buffer naming
+(use-package uniquify
+  :custom
+  (uniquify-after-kill-buffer-p t)
+  (uniquify-buffer-name-style 'forward)
+  (uniquify-ignore-buffers-re "^\\*")
+  (uniquify-separator "/"))
+
+;;; Embark
+
+(use-package embark
+  :bind
+  (("C-." . embark-act)
+   ([remap describe-bindings] . embark-bindings))
+
+  :init
+  (setq prefix-help-command #'embark-prefix-help-command))
+
+(use-package embark-consult
+  :after (embark consult)
+  :hook
+  (embark-collect-mode . consult-preview-at-point-mode))
+
+;;; Reading pdfs and epub files
+
+(use-package pdf-tools
+  :when (display-graphic-p)
+  :magic ("%PDF" . pdf-view-mode)
+  :config
+  (pdf-tools-install :no-query))
+
+;;; Music
+
 (use-package emms
   :disabled t
  :config
@@ -885,78 +791,211 @@ With prefix argument TIME, also add the current time."
  (global-set-key (kbd "C-c e r") 'emms-toggle-repeat-playlist)
  (global-set-key (kbd "C-c e s") 'emms-toggle-random-playlist))
 
-;; Experiment with embark
-(use-package embark
-  :bind
-  (("C-." . embark-act)
-   ([remap describe-bindings] . embark-bindings))
+;;; Useful functions
+(defun noa/yank-buffer-file-name ()
+  (interactive)
+  (when (buffer-file-name)
+    (kill-new (file-name-nondirectory (buffer-file-name)))))
 
-  :init
-  (setq prefix-help-command #'embark-prefix-help-command))
+;; Insert date and time at point
+;; Sometimes it's useful to be able to add a timestamp.  I use this for notes.  Use the same binding as notepad.exe.
+(defun noa/insert-date (&optional time)
+  "Insert the current date into the buffer.
+With prefix argument TIME, also add the current time."
+  (interactive "P")
+  (if time
+      (insert (format-time-string "[%Y-%m-%d %H:%M]"))
+    (insert (format-time-string "[%Y-%m-%d]"))))
+(defun noa/insert-datetime ()
+  (interactive)
+  (noa/insert-date t))
 
-(use-package embark-consult
-  :after (embark consult)
-  :hook
-  (embark-collect-mode . consult-preview-at-point-mode))
+(global-set-key (kbd "<f5>") 'noa/insert-date-time)
 
-(use-package pdf-tools
-  :when (display-graphic-p)
-  :magic ("%PDF" . pdf-view-mode)
-  :config
-  (pdf-tools-install :no-query))
+(defun snarf-song (url)
+  (interactive "sYoutube url:")
+  (async-shell-command
+   (concat "yt-dlp -x --audio-format=mp3 -o "
+           (shell-quote-argument "~/media/music/%(title)s [%(id)s].%(ext)s")
+           " "
+           (shell-quote-argument url))))
 
-;; Common lisp development
+;;; Tramp
+(use-package tramp
+  :defer t
+  :custom
+  (tramp-inline-compress-start-size 1000)
+  (tramp-verbose 3))
+;; (add-to-list 'tramp-remote-path 'tramp-own-remote-path)
 
-(use-package sly
-  :disabled t
-  :if (executable-find "sbcl")
-  :ensure t
-  :commands 'sly
-  :custom ((inferior-lisp-program (executable-find "sbcl"))
-           (common-lisp-hyperspec-root "file:///usr/share/doc/hyperspec/"))
-  :mode ((rx (or ".lisp" ".cl") eos) . common-lisp-mode))
+;; I only use git, and this apparently speeds up tramp.
+(use-package vc-hooks
+  :after vc
+  :custom
+  (vc-handled-backends '(Git)))
 
-;; Write url to text file
-(defvar noa/record-url-file "~/Documents/Library/urls")
-(defun noa/record-url (url &rest args)
-  "An alternative browser function that appends the url to a file.  We do not pay attention to any arguments."
-  (write-region (concat url "\n") nil noa/record-url-file 'append))
+;;  It seems that tramp can also be made faster with these .ssh/config settings.
 
-(defvar note/directory "~/Documents/Notes"
-  "The directory in which notes are stored.")
-(defvar note/file-ending "md"
-  "The file ending for new notes.")
+;; Host *
+;;      ControlMaster auto
+;;      ControlPath ~/.ssh/master-%h:%p
+;;      ControlPersist 10m
+;;      ForwardAgent yes
+;;      ServerAliveInterval 60
+;;; Window management
+;; Don't override display actions when i manually initiate a buffer switch.
+(use-package window
+  :custom
+  ;; My computer has a small screen, so i find that it's more beneficial for me to split the frame into columns, so i get more context.  However, splitting in this way only gives me a (window-width) of 61, so emacs will always split into vertically stacked windows.  By setting this to 80, the first split should always be vertical.
+  (split-width-threshold 80)
+  (switch-to-buffer-obey-display-actions t)
+  ;; C-l from top to bottom
+  (recenter-positions '(top middle bottom)))
 
-(defun note/new (&optional other-window)
-  (interactive "P")
-  (let* ((uid (format-time-string "%Y-%m-%d-%H%M%S"))
-         (note (concat note/directory "/" uid "." note/file-ending)))
-    (if other-window
-        (progn
-          (insert "[[./" uid ".org]]")
-          (find-file-other-window note))
-      (find-file note))
-    ))
+(use-package frame
+  :hook (after-init . window-divider-mode)
+  :custom
+  (window-divider-default-right-width 1)
+  (window-divider-default-bottom-width 1)
+  (window-divider-default-places t))
 
-(use-package ediff
+;;; Unsorted
+
+;; Update the calendar.  We want weeks to start on a monday, the first day of the week.  Holidays should be highlighted, and the date format should put the year first.
+(use-package calendar
   :defer t
   :custom
-  (ediff-window-setup-function 'ediff-setup-windows-plain)
-  (ediff-split-window-function 'split-window-horizontally))
+  (calendar-week-start-day 1)
+  (calendar-date-style 'iso)
 
-(use-package proced
+  :config
+  ;; Ensure the calendar always displays at the bottom of the screen, rather than wrapping weirdly and looking bad when it shows up in a side window.
+  (add-to-list 'display-buffer-alist
+             '("\\*Calendar\\*"
+               (display-buffer-in-side-window)
+               (side . bottom))))
+
+;; Squeeze text into a more legible sliver.
+(use-package visual-fill-column
+  :custom
+  (visual-fill-column-enable-sensible-window-split t)
+  (visual-fill-column-fringes-outside-margins nil) ; Keep fringes on the inside so relevant icons are in the right place
+  (visual-fill-column-center-text t)
+  :hook
+  ((text-mode . visual-fill-column-mode)
+   (eww-after-render . visual-line-fill-column-mode))
+  )
+
+;; (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)
+
+;; Doesn't indent nicely with proportional fonts
+;; (use-package adaptive-wrap :ensure t)
+;; (add-hook 'visual-fill-column-mode-hook #'visual-wrap-prefix-mode)
+
+(use-package simple
+  :hook ((text-mode . visual-line-mode)
+         (after-init . auto-save-mode))
+  :bind ([remap kill-buffer] . kill-current-buffer)
+  :custom
+  (save-interprogram-paste-before-kill t)
+  (kill-ring-max 25)
+  ;; Hide commands in M-x which do not work in the current mode.
+  (read-extended-command-predicate 'command-completion-default-include-p)
+  ;; My brain is not ready for advanced emacs undo
+  (undo-no-redo t)
+  :bind (("C-z" . undo)
+         ("C-S-z" . undo-redo)))
+
+(use-package help
   :defer t
   :custom
-  (proced-auto-update-flag t)
-  (proced-auto-update-interval 1)
-  (proced-enable-color-flag nil)
-  (proced-show-remote-processes t))
+  ;; When opening source files from a help window, use the same window
+  (help-window-keep-selected t))
 
-;; Nicer terminal emacs
-(set-display-table-slot standard-display-table 'vertical-border (make-glyph-code ?│))
-(setq mode-line-end-spaces nil)
+(use-package emacs
+  :custom
+  (window-combination-resize t)
+  (window-resize-pixelwise t)
+  (frame-resize-pixelwise t)
+  (shell-file-name "/bin/sh")
+  (completion-ignore-case t)
+  (read-buffer-completion-ignore-case t)
+  ;; yes-or-no-p uses y-or-no-p, and read-answer accepts single key press answers
+  (use-short-answers t)
+  (history-length 250)
+  ;; Use a bar cursor and blink it and don't stop blinking it.
+  (cursor-type 'bar)
+  ;; Support opening new minibuffers from inside existing minibuffers.
+  (enable-recursive-minibuffers t)
+  ;; Whether to drop into the debugger on any error.  This seems cool, but in practice is a bit annoying.
+  (debug-on-error nil)
+  (create-lockfiles nil)
+  (auto-save-interval 6)  ;; every six keystrokes
+  (auto-save-timeout 5) ;; every 5 seconds
+  (auto-save-no-message t)
+  ;; Always give me a line of text when i'm near the edges of the screen
+  (scroll-margin 2))
 
-(use-package xclip)
-(unless (display-graphic-p) (xclip-mode 1))
+(use-package files
+  :custom
+  (view-read-only t)
+  (confirm-kill-emacs 'y-or-n-p)
+  ;; We are on a unix system, so it makes sense to end files in the unix system way.  I'm surprised this isn't the default.
+  (require-final-newline t)
+
+  ;; This behaviour changes how we visit symlinks.
+  (find-file-visit-truename t)
+
+  (remote-file-name-inhibit-locks t)
+  (remote-file-name-inhibit-auto-save t)
+  (remote-file-name-inhibit-auto-save-visited t)
+  (backup-directory-alist '(("." . "~/.config/emacs/backups/")))
+  (make-backup-files nil)
+  (backup-by-copying t)
+  (version-control t)
+  ;; this will auto save to the current file
+  (auto-save-visited-mode t)
+  (auto-save-visited-interval 0.1)
+  (save-silently t) ;; don't message when saving
+  )
+
+
+;; Disable disabled commands
+(setq disabled-command-function nil)
+
+;; Don't save changes in the customize interface
+(use-package cus-edit
+  :custom
+  (custom-file (make-temp-file "custom")))
+
+
+;; Emacs server
+;; (unless (server-running-p) (server-start)))
+
+;; Better support for long lines.
+(use-package so-long
+  :hook (after-init . global-so-long-mode))
+
+;; Automatically revert buffers when they change on disk.  This doesn't apply to tramp.
+(use-package autorevert
+  :hook (after-init . global-auto-revert-mode)
+  :custom
+  (global-auto-revert-non-file-buffers t))
+
+(use-package vc-hooks
+  :custom
+  (vc-follow-symlinks 'ask))
+
+
+;; Zap up to char
+(global-set-key (kbd "M-z") 'zap-up-to-char)
+
+(setenv "PAGER" "cat")
+(setenv "TERM" "dumb")
+(setenv "NO_COLOR")
+(setenv "GPG_AGENT_INFO" nil)
+
+;;; Cleanup
 
 ;; (profiler-stop)
+