From b171e954b60558f175ea56d5015a23fa4fdeabaf Mon Sep 17 00:00:00 2001
From: Case Duckworth
Date: Wed, 26 Jun 2024 12:43:33 -0500
Subject: Emacs!

I copied my files over wrong or something... anyway here's an updated emacs config
---
 emacs                    | 515 +++++++++++++++++++++++++++++++++++++----------
 emacs.d/bookmarks        |  33 +++
 emacs.d/brianna-theme.el |   7 +-
 emacs.d/early-init.el    | 282 ++++++++++++++++++++++----
 4 files changed, 690 insertions(+), 147 deletions(-)
 create mode 100644 emacs.d/bookmarks

diff --git a/emacs b/emacs
index f0f0eb9..5066268 100644
--- a/emacs
+++ b/emacs
@@ -1,21 +1,25 @@
 ;;; ~/.emacs -*- mode: emacs-lisp; lexical-binding: t; -*-
-;; Author Case Duckworth <acdw@acdw.net>
+;; Author: Case Duckworth <acdw@acdw.net>
 ;; Bankruptcy: 12
 
 ;;; Initialization -- see also ~/.emacs.d/early-init.el
 
+(setq load-prefer-newer t)
+
 (setopt custom-file (locate-user-emacs-file "custom.el"))
 (load custom-file :no-error)
+(add-hook 'Custom-mode-hook
+          (lambda () (run-with-idle-timer 0.25 nil #'custom-show-all-widgets)))
 
 (defvar user-private-file (locate-user-emacs-file "private.el")
   "Private customizations")
 ;; make sure it's really private!
 (and (= (file-attribute-user-id (file-attributes user-private-file))
-        (user-uid))                     ; is it owned by this me?
+        (user-uid)) ; is it owned by this me?
      (set-file-modes user-private-file #o600))
 (load user-private-file :no-error)
 
-(load-theme 'brianna :no-confirm)       ; see ~/.emacs.d/brianna-theme.el
+(load-theme 'brianna :no-confirm) ; see ~/.emacs.d/brianna-theme.el
 (add-hook 'after-init-hook #'setup-faces)
 
 (define-advice startup-echo-area-message (:override ())
@@ -25,16 +29,22 @@
 
 ;;; Basic settings
 
+;; Auth
+(package-ensure '(keepassxc-shim
+                  :url "https://codeberg.org/acdw/keepassxc-shim.el"))
+(keepassxc-shim-activate)
+(setopt auth-sources '("secrets:default"))
+(add-hook 'auth-info-hook #'truncate-lines-local-mode)
+
 ;; Environment
-(setenv "PAGER" "cat")                  ; emacs is a pager
-(setenv "TERM" "dumb")                  ; no fancy graphics!
-(setenv "NO_COLOR" "1")                 ; no color!
+(setenv "PAGER" "cat")  ; emacs is a pager
+(setenv "TERM" "dumb")  ; no fancy graphics!
+(setenv "NO_COLOR" "1") ; no color!
 
 ;; Startup
 (setopt inhibit-startup-screen t)
 (setopt initial-buffer-choice #'eshell)
-(setopt initial-scratch-message ";; *scratch*")
-(setopt initial-major-mode #'emacs-lisp-mode)
+(setopt initial-scratch-message nil)
 
 ;; Dialogs
 (setopt use-dialog-box nil)
@@ -47,38 +57,138 @@
 (blink-cursor-mode -1)
 
 ;; Whitespace
-(setopt whitespace-style '(face trailing tabs tab-mark))
-(setopt whitespace-global-modes '(not rcirc-mode jabber-chat-mode))
-;; (global-whitespace-mode)
 (add-hook 'before-save-hook #'delete-trailing-whitespace-except-current-line)
-(set-face-attribute 'whitespace-tab nil :background nil :foreground "#888")
-(setf (alist-get 'tab-mark whitespace-display-mappings)
-      '(9 [?· 9] [?» 9] [?\\ 9]))
 
-(add-hook 'after-init-hook
-          (lambda () (add-hook 'before-save-hook #'indent-buffer+)))
+(setopt whitespace-style '(face trailing tabs tab-mark))
+(setopt whitespace-global-modes '(not rcirc-mode jabber-chat-mode))
+(global-whitespace-mode)
+(hide-minor-mode 'global-whitespace-mode)
+(with-eval-after-load 'whitespace
+  (setf/alist whitespace-display-mappings 'tab-mark
+              '(9 [?· 9] [?» 9] [?\\ 9])))
+
+;; Automatically indent buffer when saving
+(define-minor-mode indent-on-save-mode
+  "Automatically re-indent the buffer on save."
+  :lighter " >"
+  (if indent-on-save-mode
+      (add-hook 'before-save-hook #'fixup-whitespace nil t)
+    (remove-hook 'before-save-hook #'fixup-whitespace t)))
+
+(add-hook 'prog-mode-hook #'indent-on-save-mode)
+
+;; Comments
+(setopt comment-column 0)
+(setopt comment-indent-offset 1)
 
 ;;; UI stuff
 
-(setopt tab-bar-show 1)
-
-;; Elastic-modes
-(package-ensure 'dash)                  ; requirement for `elastic-modes'
-(package-ensure '(elastic-modes
-                  :url "https://github.com/jyp/elastic-modes"
-                  :main-file "elastic-pkg.el"))
-(require 'elastic-indent)
-(add-hook 'prog-mode-hook #'elastic-indent-mode)
 
 ;; Fixed-pitch
-(package-ensure '(fixed-pitch-mode
-                  :url "https://github.com/cstby/fixed-pitch-mode.git"))
-;; (require 'fixed-pitch)
+(define-minor-mode fixed-pitch-mode
+  "Use a monospace typeface."
+  :lighter " f"
+  (setq cursor-type (if fixed-pitch-mode 'box 'bar))
+  (buffer-face-set (and fixed-pitch-mode 'fixed-pitch)))
+(define-globalized-minor-mode auto-fixed-pitch-mode
+  fixed-pitch-mode fixed-pitch-mode
+  :predicate '(special-mode
+               prog-mode))
+
 (setopt cursor-type 'bar)
-;; (setopt fixed-pitch-use-extended-default t)
-;; (add-to-list 'fixed-pitch-whitelist-hooks 'vc-dir-mode-hook)
-;; (hide-minor-mode 'buffer-face-mode)
-;; (add-hook 'fixed-pitch-mode-hook #'display-fill-column-indicator-mode)
+(hide-minor-mode 'buffer-face-mode)
+(add-hook 'fixed-pitch-mode-hook #'display-fill-column-indicator-mode)
+(auto-fixed-pitch-mode)
+
+(package-ensure 'valign)                ; needed for variable-pitch org-mode
+(add-hook 'org-mode-hook #'valign-mode)
+(setopt valign-fancy-bar t)
+
+;;; Mode line
+
+(setopt mode-line-position-line-format '("%l"))
+(setopt mode-line-position-column-line-format '("%l:%c"))
+
+(package-ensure 'minions)
+
+(defvar mode-line-major-mode-keymap*
+  (let ((map (make-sparse-keymap)))
+    (bindings--define-key map [mode-line down-mouse-1]
+      `(menu-item "Menu Bar" ignore
+                  :filter ,(lambda (_) (mouse-menu-major-mode-map))))
+    (define-key map [mode-line mouse-2] #'describe-mode)
+    (define-key map [mode-line mouse-3] #'minions-minor-modes-menu)
+    map)
+  "Keymap to display on major mode.")
+
+(defun make-mode-line-mode-disabler (lighter help mode)
+  (propertize lighter
+              'help-echo (concat help "\n1:cancel")
+              'face 'italic
+              'mouse-face 'mode-line-highlight
+              'local-map
+              (make-mode-line-mouse-map
+               'mouse-1 (lambda (ev) (interactive "e")
+                          (with-selected-window
+                              (posn-window (event-start ev))
+                            (funcall mode -1)
+                            (force-mode-line-update))))))
+
+(setopt mode-line-format
+        `("%e"
+          mode-line-front-space
+          (:propertize ("" mode-line-modified mode-line-remote)
+                       display (min-width (5.0)))
+          " %[" mode-line-buffer-identification " %]"
+          (vc-mode (" [" vc-mode "]"))
+          " ( "
+          (:propertize ("" mode-name)
+                       help-echo "Major mode\n1:menu\n2:help\n3:minor"
+                       mouse-face mode-line-highlight
+                       local-map ,mode-line-major-mode-keymap*)
+          (auto-fill-function
+           ,(make-mode-line-mode-disabler "-f" "Auto-filling"
+                                          #'auto-fill-mode))
+          (visual-line-mode
+           ,(make-mode-line-mode-disabler "-v" "Visual lines"
+                                          #'visual-line-mode))
+          (truncate-lines-local-mode
+           ,(make-mode-line-mode-disabler "-t" "Truncating lines"
+                                          #'truncate-lines-local-mode))
+          " "
+          (defining-kbd-macro (:propertize "🔴" help-echo "Defining kbd macro"))
+          (isearch-mode (:propertize "🔍" help-echo "Searching"))
+          (overwrite-mode
+           ,(make-mode-line-mode-disabler "✒️" "Overwriting"
+                                          #'overwrite-mode))
+          (debug-on-error
+           ,(make-mode-line-mode-disabler "‼️" "Debug on error"
+                                          (lambda (_)
+                                            (setq debug-on-error nil))))
+          (debug-on-quit
+           ,(make-mode-line-mode-disabler "🚫" "Debug on quit"
+                                          (lambda (_)
+                                            (setq debug-on-quit nil))))
+          ") "
+          (mode-line-process (" " mode-line-process " "))
+          "  " (:propertize (line-number-mode
+                             (column-number-mode
+                              (:propertize "%l/%c" help-echo "Line/column")
+                              (:propertize "%l" help-echo "Line"))
+                             (column-number-mode
+                              (:propertize "/%c" help-echo "Column")
+                              ""))
+                            display (min-width (5.0)))
+          (:propertize (" [" (-3 "%o") "]")
+                       help-echo "Position in buffer"
+                       display (min-width (6.0)))
+          ,(propertize "%n"
+                       'help-echo "Narrowed\n1:widen"
+                       'mouse-face 'mode-line-highlight
+                       'local-map (make-mode-line-mouse-map
+                                   'mouse-1 #'mode-line-widen))
+          (so-long-mode-line-info
+           (" -- " so-long-mode-line-info))))
 
 ;;; Completions
 
@@ -99,15 +209,35 @@
 (setopt completions-format 'one-column)
 (setopt completions-max-height 10)
 
+(setf/alist display-buffer-alist "\\`\\*Completions\\*\\'"
+            '(nil (window-parameters (mode-line-format . " --- %b"))))
+
 (keymap-set minibuffer-local-map "C-p" #'minibuffer-previous-completion)
 (keymap-set minibuffer-local-map "C-n" #'minibuffer-next-completion)
 (keymap-set minibuffer-local-map "M-DEL" #'minibuffer-delete-directory)
+(keymap-set completion-list-mode-map "C-g" #'quit-window) ; is this a good idea?
+
+;; Corfu --- tryin this out
+
+(package-ensure 'corfu t)
+(keymap-set corfu-map "TAB" #'corfu-next)
+(keymap-set corfu-map "<tab>" #'corfu-next)
+(keymap-set corfu-map "S-TAB" #'corfu-previous)
+(keymap-set corfu-map "<backtab>" #'corfu-previous)
+(global-corfu-mode)
+
+;;; Minibuffer
 
 (setopt enable-recursive-minibuffers t)
 (setopt minibuffer-default-prompt-format " [%s]")
 (minibuffer-depth-indicate-mode)
 (minibuffer-electric-default-mode)
 
+(setopt minibuffer-prompt-properties '( read-only t
+                                        cursor-intangible t
+                                        face minibuffer-prompt))
+(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
+
 (setopt file-name-shadow-properties '(invisible t intangible t))
 (file-name-shadow-mode)
 
@@ -115,6 +245,18 @@
 (setopt history-delete-duplicates t)
 (setopt savehist-save-minibuffer-history t)
 (setopt savehist-autosave-interval 5)
+(setopt 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))
 (savehist-mode)
 
 (define-minor-mode truncate-lines-local-mode
@@ -131,12 +273,12 @@
 (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)
 
 (package-ensure 'adaptive-wrap)
-(add-hook 'visual-fill-column-mode #'adaptive-wrap-prefix-mode)
+(add-hook 'visual-line-mode #'adaptive-wrap-prefix-mode)
 
-;; Consult/Marginalia
+;;; Completing-read and friends
 
-(package-ensure 'consult)
-(require 'consult)
+;; Consult
+(package-ensure 'consult t)
 (keymap-global-set "C-x b" #'consult-buffer)
 (keymap-global-set "C-x 4 b" #'consult-buffer-other-window)
 (keymap-global-set "C-x 5 b" #'consult-buffer-other-frame)
@@ -160,9 +302,32 @@
 (setopt xref-show-definitions-function #'xref-show-definitions-completing-read)
 (setopt consult-preview-key "M-.")
 
+(define-advice completing-read-multiple (:filter-args (args) indicator)
+  (cons (format "[CRM%s] %s"
+                (replace-regexp-in-string
+                 "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
+                 crm-separator)
+                (car args))
+        (cdr args)))
+
+;; Marginalia
 (package-ensure 'marginalia)
 (marginalia-mode)
 
+;; Embark
+(package-ensure 'embark)
+(package-ensure 'embark-consult)
+(keymap-global-set "M-." #'embark-dwim)
+(keymap-global-set "C-." #'embark-act)
+(keymap-global-set "C-h B" #'embark-bindings)
+(add-to-list 'display-buffer-alist
+             '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" nil
+               (window-parameters (mode-line-format . none))))
+(add-hook 'embark-collect-mode #'consult-preview-at-point-mode)
+(setopt embark-indicators '(embark-mixed-indicator
+                            embark-highlight-indicator
+                            embark-isearch-highlight-indicator))
+
 ;;; Frames / Windows
 
 (winner-mode)
@@ -174,14 +339,14 @@
 (global-auto-revert-mode)
 
 (setopt create-lockfiles nil)
-(setopt mode-require-final-newline t)
+(setopt require-final-newline t)
 (setopt view-read-only t)
 (setopt save-silently t)
 (setopt delete-by-moving-to-trash t)
-(setopt auto-save-default nil)
+(setopt auto-save-default t)
 (setopt auto-save-no-message t)
-(setopt auto-save-interval 2)
-(setopt auto-save-timeout 2)
+(setopt auto-save-interval 30)
+(setopt auto-save-timeout 5)
 (setopt auto-save-visited-interval 5)
 (setopt remote-file-name-inhibit-auto-save t)
 (setopt remote-file-name-inhibit-auto-save-visited t)
@@ -189,6 +354,19 @@
              `(".*" ,(locate-user-emacs-file "auto-save/") t))
 (auto-save-visited-mode)
 
+(add-hook 'window-selection-change-functions
+          (defun save-old-selected-window-buffer (frame)
+            (with-current-buffer
+                (window-buffer (frame-old-selected-window))
+              (when (and (buffer-file-name) (buffer-modified-p))
+                (save-buffer)))))
+
+(add-hook 'buffer-list-update-hook
+          (defun save-other-buffer ()
+            (with-current-buffer (other-buffer)
+              (when (and (buffer-file-name) (buffer-modified-p))
+                (save-buffer)))))
+
 (add-function :after after-focus-change-function
               (defun focus-out-save ()
                 (save-some-buffers t)))
@@ -224,6 +402,8 @@
 
 ;; Unique names
 (setopt uniquify-buffer-name-style 'forward)
+(setopt uniquify-after-kill-buffer-p t)
+(setopt uniquify-ignore-buffers-re "^\\*")
 
 ;; Persistent undo
 (package-ensure 'undo-fu-session)
@@ -256,7 +436,7 @@
 (setopt isearch-lazy-count nil)
 (setopt isearch-regexp-lax-whitespace t)
 (setopt isearch-wrap-pause 'no)
-(setopt search-whitespace-regexp "[     ]+")
+(setopt search-whitespace-regexp "[       ]+")
 (setopt search-ring-max 256)
 (setopt regexp-search-ring-max 256)
 
@@ -265,35 +445,43 @@
   (unless (string-equal "" isearch-string)
     (isearch-update-ring isearch-string isearch-regexp)))
 
-(package-ensure 'isearch-mb)
-(with-eval-after-load 'isearch-mb
-  (with-eval-after-load 'consult
-    (add-to-list 'isearch-mb--with-buffer #'consult-isearch-history)
-    (keymap-set isearch-mb-minibuffer-map "M-r" #'consult-isearch-history)
-    (add-to-list 'isearch-mb--after-exit #'consult-line)
-    (keymap-set isearch-mb-minibuffer-map "M-s l" #'consult-line)))
+(package-ensure 'isearch-mb t)
+(add-to-list 'isearch-mb--with-buffer #'consult-isearch-history)
+(keymap-set isearch-mb-minibuffer-map "M-r" #'consult-isearch-history)
+(add-to-list 'isearch-mb--after-exit #'consult-line)
+(keymap-set isearch-mb-minibuffer-map "M-s l" #'consult-line)
 (isearch-mb-mode)
 
 ;; Default to regexen
-(setopt search-default-mode t)          ; Isearch
+(setopt search-default-mode t) ; Isearch
 (keymap-global-set "M-%" #'query-replace-regexp)
 (keymap-global-set "C-M-%" #'query-replace)
 
-;;; Keybinds
+;;; Keybindings
 
+(repeat-mode)
+
+;; Separate C-<key> and control keys from halcyon ascii days
+;; (define-key input-decode-map [?\C-i] [C-i])
+;; (define-key input-decode-map [?\C-m] [C-m])
+
+;; Modified default keybindings
 (keymap-global-set "C-x C-k" #'kill-buffer-dwim)
 (keymap-global-set "M-o" #'other-window-dwim)
 (keymap-global-set "C-x o" #'other-window-dwim)
 (keymap-global-set "C-x 0" #'delete-window-dwim)
 (keymap-global-set "M-SPC" #'cycle-spacing*)
-(keymap-global-set "C-M-\\" #'indent-buffer+)
-
 (keymap-global-set "C-x C-c" #'save-buffers-kill*)
+(keymap-global-set "C-g" #'keyboard-quit*)
+(keymap-global-set "C-M-\\" #'fixup-whitespace)
+
+;; New bindings for existing stuff
 (keymap-global-set "C-x C-b" #'ibuffer)
 (keymap-global-set "M-/" #'hippie-expand)
 (keymap-global-set "M-u" #'universal-argument)
 (keymap-set universal-argument-map "M-u" #'universal-argument-more)
 
+;; Prefix maps
 (keymap-global-set "C-c d"
                    (defun insert-current-iso8601 ()
                      (interactive)
@@ -309,6 +497,8 @@
                      "p" (find-user-file private)
                      "t" (find-user-file brianna
                            (locate-user-emacs-file "brianna-theme.el"))
+                     "x" (find-user-file exwm
+                           (expand-file-name "~/.exwm"))
                      "s" #'scratch-buffer))
 
 (keymap-global-set "C-c t"
@@ -319,14 +509,28 @@
                      "c" #'column-number-mode
                      "l" #'line-number-mode
                      "L" #'display-line-numbers-mode
-                     "t" #'truncate-lines-local-mode))
+                     "t" #'truncate-lines-local-mode
+                     "o" #'overwrite-mode
+                     "f" #'auto-fill-mode))
 
-;; Un-keybinds
+(keymap-global-set "M-c"
+                   (define-keymap
+                     :prefix 'case-map
+                     "M-u" #'upcase-dwim "u"    #'upcase-dwim
+                     "M-c" #'capitalize-dwim "c" #'capitalize-dwim
+                     "M-l" #'downcase-dwim "l" #'downcase-dwim))
+(put 'upcase-dwim 'repeat-map 'case-map)
+(put 'capitalize-dwim 'repeat-map 'case-map)
+(put 'downcase-dwim 'repeat-map 'case-map)
+
+;;; Un-keybinds
+;; Why do I want to zoom with the mouse?
 (keymap-global-unset "C-<wheel-down>" t)
 (keymap-global-unset "C-<wheel-up>" t)
-;; I only ever fat-finger this key and never want to change encoding
+;; These are ripe for re-binding
 (keymap-global-unset "C-\\" t)
-(keymap-global-unset "C-z" t)
+(keymap-global-unset "M-l" t)
+(keymap-global-unset "<f2>" t)
 
 ;; Key settings
 (setopt set-mark-command-repeat-pop t)
@@ -337,8 +541,11 @@
 (package-ensure 'hungry-delete)
 (setopt hungry-delete-chars-to-skip " \t")
 (with-eval-after-load 'hungry-delete
-  (add-to-list 'hungry-delete-except-modes 'eshell-mode)
-  (add-to-list 'hungry-delete-except-modes 'eww-mode))
+  (dolist (m '( eshell-mode
+                eww-mode
+                special-mode
+                jabber-chat-mode))
+    (add-to-list 'hungry-delete-except-modes m)))
 (global-hungry-delete-mode)
 
 ;;; Writing
@@ -364,12 +571,13 @@
                              css-mode)
   "Modes to indent with spaces, not tabs.")
 
-;; (add-hook 'prog-mode-hook
-;;           (defun indent-tabs-mode-maybe ()
-;;             (setq indent-tabs-mode
-;;                   (if (apply #'derived-mode-p space-indent-modes) nil t))))
+(add-hook 'prog-mode-hook
+          (defun indent-tabs-mode-maybe ()
+            (setq indent-tabs-mode
+                  (if (apply #'derived-mode-p space-indent-modes) nil t))))
 
-(indent-tabs-mode -1)
+;; Eldoc
+(setopt eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)
 
 ;; Elisp
 (keymap-set emacs-lisp-mode-map "C-c C-c" #'eval-defun)
@@ -385,11 +593,9 @@
 (setopt makefile-cleanup-continuations t)
 
 (add-hook 'makefile-mode-hook
-          (defun makefile-stop-complaining ()
-            (remove-hook 'write-file-functions
-                         'makefile-warn-suspicious-lines t)
-            (remove-hook 'write-file-functions
-                         'makefile-warn-continuations t)))
+          (^local-unhook 'write-file-functions 'makefile-warn-suspicious-lines))
+(add-hook 'makefile-mode-hook
+          (^local-unhook 'write-file-functions 'makefile-warn-continuations))
 
 ;; Scheme -- CHICKEN
 (setopt scheme-program-name (or (executable-find "csi")))
@@ -416,10 +622,7 @@
 (advice-add 'geiser-eval-region :after #'pulse@eval)
 
 ;; VC
-(keymap-global-set "C-x v j"
-                   (defun vc-jump ()
-                     (interactive)
-                     (vc-dir default-directory)))
+(keymap-global-set "C-x m" #'vc-jump)
 
 ;;; Compilation
 
@@ -430,37 +633,66 @@
 
 (package-ensure 'gemtext-mode)
 
-;;; Miscellaneous settings
+;;; Miscellaneous
 
+;; Settings
 (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
-;; (add-hook 'prog-mode-hook #'auto-fill-mode)
+(add-hook 'messages-buffer-mode-hook
+          (^turn-off 'display-fill-column-indicator-mode))
+(add-hook 'prog-mode-hook #'auto-fill-mode)
 (add-hook 'prog-mode-hook #'electric-pair-local-mode)
-(setopt x-underline-at-descent-line t)
-(setopt scroll-conservatively 101)
-(setopt display-fill-column-indicator-character ?·)
+(add-to-list 'warning-suppress-types 'comp)
+(context-menu-mode)
+(delete-selection-mode)
+(global-goto-address-mode)
+(global-so-long-mode)
+(pixel-scroll-precision-mode)
+(setopt bookmark-save-flag 1)
 (setopt disabled-command-function nil)
+(setopt display-fill-column-indicator-character ?·)
 (setopt electric-pair-skip-whitespace 'chomp)
+(setopt eval-expression-print-length nil)
+(setopt eval-expression-print-level nil)
 (setopt fill-column 80)
 (setopt finger-X.500-host-regexps '(".*tilde.*"))
+(setopt help-window-keep-selected t)
+(setopt help-window-select t)
+(setopt read-extended-command-predicate #'command-completion-default-include-p)
 (setopt recenter-positions '(top middle bottom))
-(setopt eval-expression-print-level nil)
-(setopt eval-expression-print-length nil)
+(setopt scroll-conservatively 101)
 (setopt show-paren-delay 0.01)
 (setopt show-paren-style 'parenthesis)
 (setopt show-paren-when-point-in-periphery t)
 (setopt show-paren-when-point-inside-paren t)
+(setopt switch-to-buffer-obey-display-actions t)
+(setopt tmm-completion-prompt nil)
+(setopt tmm-mid-prompt " -- ")
+(setopt x-underline-at-descent-line t)
 (show-paren-mode)
-(delete-selection-mode)
-(global-so-long-mode)
-(global-goto-address-mode)
-(context-menu-mode)
 (tooltip-mode -1)
 
+;; Advice & Hooks
+
+(define-advice canonically-space-region
+    (:around (orig &rest args) double-space-sentences)
+  "Always double-space sentences canonically."
+  (let ((sentence-end-double-space t))
+    (apply orig args)))
+
+(define-advice switch-to-buffer (:after (&rest _) normal-mode)
+  "Automatically determine the mode for non-file buffers."
+  (when-let ((_ (and (eq major-mode 'fundamental-mode)
+                     (not buffer-file-name)))
+             (buffer-file-name (buffer-name)))
+    (normal-mode)))
+
 (add-hook 'special-mode-hook
           (defun hl-line@special-mode ()
             (unless (derived-mode-p 'help-mode ; add other modes here
                                     'Info-mode
-                                    'eww-mode)
+                                    'Man-mode
+                                    'eww-mode
+                                    'elpher-mode)
               (hl-line-mode))))
 (add-hook 'dired-mode-hook #'hl-line-mode)
 
@@ -486,6 +718,26 @@
 (setopt jabber-groupchat-buffer-format "%b")
 (setopt jabber-muc-private-buffer-format "%n<%g>")
 
+(defun esc/mls (str)                      ; escape-mode-line-string
+  (string-replace "%" "%%" str))
+
+(setopt jabber-chat-header-line-format
+        '(" " (:eval (esc/mls (jabber-jid-displayname jabber-chatting-with)))
+          " :: " (:eval (esc/mls (jabber-fix-status
+                                  (get (jabber-jid-symbol jabber-chatting-with)
+                                       'status))))
+          " :: " (:eval (esc/mls jabber-events-message)) ;see jabber-events.el
+          " :: " (:eval (esc/mls jabber-chatstates-message))))
+(setopt jabber-muc-header-line-format
+        '(" " (:eval (esc/mls (jabber-jid-displayname jabber-group)))
+          " :: " (:eval (esc/mls jabber-muc-topic))))
+(setopt jabber-muc-private-header-line-format
+        '(" " (:eval (esc/mls (jabber-jid-resource jabber-chatting-with)))
+          " in " (:eval (esc/mls (jabber-jid-displayname
+                                  (jabber-jid-user jabber-chatting-with))))
+          " :: " (:eval (esc/mls jabber-events-message))
+          " :: " (:eval (esc/mls jabber-chatstates-message))))
+
 (add-hook 'jabber-post-connect-hooks #'jabber-enable-carbons)
 (add-hook 'jabber-chat-mode-hook #'visual-line-mode)
 (remove-hook 'jabber-alert-muc-hooks #'jabber-muc-echo)
@@ -493,8 +745,8 @@
 
 ;;; Eshell
 
-(setopt eshell-banner-message
-        (format "%s\n\n" (string-join (process-lines "fortune" "-s") "\n")))
+(setopt cookie-file (expand-file-name "~/cloud/oblique.txt"))
+(setopt eshell-banner-message (format "%s\n" (cookie cookie-file)))
 (setopt eshell-prompt-function
         (defun @eshell-prompt ()
           (let ((rootp (zerop (user-uid))))
@@ -514,17 +766,7 @@
 (setopt eshell-scroll-to-bottom-on-input 'this)
 (setopt eshell-history-size nil)
 
-(keymap-global-set "C-z"
-                   (lambda (arg) (interactive "P")
-                     (let ((dd default-directory))
-                       (eshell arg)
-                       (unless (equal dd default-directory)
-                         (setq default-directory dd)
-                         ;; Is this a good idea, really?
-                         (eshell-bol)
-                         (unless (eolp)
-                           (insert "# "))
-                         (eshell-send-input)))))
+(keymap-global-set "C-z" #'popup-eshell)
 (add-hook 'eshell-first-time-mode-hook
           (defun @eshell-once ()
             (keymap-set eshell-mode-map "C-z" #'quit-window)))
@@ -532,18 +774,22 @@
 ;;; Browsing
 
 ;; Dired (files)
+(add-hook 'dired-mode-hook #'dired-hide-details-mode)
+(add-hook 'dired-mode-hook #'truncate-lines-local-mode)
+(setopt dired-auto-revert-buffer t)
+(setopt dired-clean-confirm-killing-deleted-buffers nil)
+(setopt dired-create-destination-dirs 'always)
+(setopt dired-do-revert-buffer t)
 (setopt dired-dwim-target t)
-(setopt dired-listing-switches "-AlF")
+(setopt dired-hide-details-hide-symlink-targets nil)
+(setopt dired-listing-switches "-AlFhv --group-directories-first")
 (setopt dired-ls-F-marks-symlinks t)
+(setopt dired-no-confirm '(byte-compile load chgrp chmod chown copy move
+                                        hardlink symlink shell touch))
 (setopt dired-recursive-copies 'always)
 (setopt dired-recursive-deletes 'always)
-(setopt dired-auto-revert-buffer t)
-(setopt dired-hide-details-hide-symlink-targets nil)
-(add-hook 'dired-mode-hook #'dired-hide-details-mode)
-(add-hook 'dired-mode-hook #'truncate-lines-local-mode)
 (with-eval-after-load 'dired
   (require 'dired-x)
-  (keymap-set dired-mode-map "C-j" #'dired-up-directory)
   (setopt dired-omit-files (regexp-concat dired-omit-files
                                           "^\\..+$"
                                           ;; CHICKEN ... this may be overkill
@@ -551,18 +797,75 @@
                                           "\\.import\\.scm$"
                                           "\\.\\(build\\|install\\)\\.sh$"
                                           "\\.link$"))
+  (keymap-set dired-mode-map "C-j" #'dired-up-directory)
   (add-hook 'dired-mode-hook #'dired-omit-mode)
   (keymap-set dired-mode-map ")" #'dired-omit-mode))
 
 ;; Elpher (gemini/gopher)
 (package-ensure 'elpher)
+(with-eval-after-load 'elpher
+  ;; Try to emulate eww bindings if possible
+  (keymap-set elpher-mode-map "l" #'elpher-back)
+  (keymap-set elpher-mode-map "g" #'elpher-reload)
+  (keymap-set elpher-mode-map "G" #'elpher-go)
+  (keymap-set elpher-mode-map "v" #'elpher-view-raw)
+  (keymap-set elpher-mode-map "E" #'elpher-set-gopher-coding-system))
 
 ;;; HTTP browsing
 
-;; SHR
-(setopt shr-max-width (+ fill-column 10))
+;; Eww / Shr
+(setopt shr-max-width nil)                ; covered in hook below
+(setopt shr-max-image-proportion 0.9)
+(setopt shr-discard-aria-hidden t)
+(setopt eww-auto-rename-buffer
+        (defun title+url ()
+          (when (eq major-mode 'eww-mode)
+            (let ((title (plist-get eww-data :title))
+                  (url (plist-get eww-data :url)))
+              (cond
+               ((and title url) (format "*eww: %s :: %s" title url))
+               ((or title url) (format "*eww: %s") (or title url)))))))
+(add-hook 'eww-after-render-hook
+          (defun eww@visual-line ()
+            (visual-fill-column-mode)
+            (eww-reload t)))
+(with-eval-after-load 'eww
+  (setopt eww-use-browse-url ".")
+  (keymap-set eww-mode-map "B" #'bookmark-jump)
+  (keymap-set eww-mode-map "b" #'bookmark-set)
+  (keymap-unset eww-mode-map "M-n" t)
+  (keymap-unset eww-mode-map "M-p" t))
 
 ;; Browse-url
-(setopt browse-url-new-window-flag t)
+(setopt browse-url-browser-function #'eww-browse-url)
 (setopt browse-url-firefox-arguments '("--new-tab"))
 (setopt browse-url-firefox-new-window-is-tab t)
+(setopt browse-url-generic-args browse-url-firefox-arguments)
+(setopt browse-url-generic-program "firefox")
+
+(setopt browse-url-secondary-browser-function #'browse-url-firefox)
+
+(package-ensure 'link-hint)
+(keymap-global-set "M-l"
+                   (define-keymap
+                     :prefix 'link-map
+                     "M-l" #'link-hint-open-link "l" #'link-hint-open-link
+                     "M-w" #'link-hint-copy-link "w" #'link-hint-copy-link))
+;; With link-hint we get avy "for free"
+(keymap-global-set "M-j" #'avy-goto-char-timer)
+
+;; PDFs
+(package-ensure 'pdf-tools)
+(pdf-loader-install)
+
+;;; EXWM
+
+(setf/alist display-buffer-alist shell-command-buffer-name-async
+            '(display-buffer-no-window))
+
+(when (getenv "DISPLAY")
+  (package-ensure 'exwm t)
+  (load (expand-file-name "~/.exwm")))
+
+;;; Mu4e
+
diff --git a/emacs.d/bookmarks b/emacs.d/bookmarks
new file mode 100644
index 0000000..05ee99e
--- /dev/null
+++ b/emacs.d/bookmarks
@@ -0,0 +1,33 @@
+;;;; Emacs Bookmark Format Version 1;;;; -*- coding: utf-8; mode: lisp-data -*-
+;;; This format is meant to be slightly human-readable;
+;;; nevertheless, you probably don't want to edit it.
+;;; -*- End Of Bookmark File Format Version Stamp -*-
+(("~elly/pub"
+ (filename . "/sshx:town:/home/elly/pub/")
+ (front-context-string . "  /sshx:town:/ho")
+ (rear-context-string)
+ (position . 1)
+ (last-modified 26236 19926 867955 868000))
+("Planet ACDW"
+ (front-context-string . "mars, but with e")
+ (rear-context-string)
+ (position . 1)
+ (last-modified 26235 32833 754178 60000)
+ (location . "https://planet.acdw.net/")
+ (handler . eww-bookmark-jump))
+("CHICKEN API"
+ (front-context-string . "chickadee\n\nIdent")
+ (rear-context-string)
+ (position . 1)
+ (last-modified 26219 43014 969496 46000)
+ (location . "http://api.call-cc.org/5/doc/")
+ (handler . eww-bookmark-jump))
+(#1="set-face-attribute"
+    (position . 1)
+    (last-modified 26219 41950 249141 418000)
+    (help-fn . describe-function--helper)
+    (help-args set-face-attribute "brianna-theme.el")
+    (position . 1)
+    (handler . help-bookmark-jump)
+    (defaults #1# "*Help*"))
+)
diff --git a/emacs.d/brianna-theme.el b/emacs.d/brianna-theme.el
index 0b16a41..43223f6 100644
--- a/emacs.d/brianna-theme.el
+++ b/emacs.d/brianna-theme.el
@@ -93,12 +93,12 @@
  '(header-line ((t (:background "lavender" :inherit variable-pitch))))
  '(minibuffer-prompt ((t (:inherit brianna-prompt))))
  '(mode-line ((t (:background "lavender" :inherit variable-pitch))))
- '(mode-line-active ((t ( :box t :background "light goldenrod"
+ '(mode-line-active ((t ( :box "black" :background "light goldenrod"
                           :inherit mode-line))))
  '(mode-line-inactive ((t ( :box "pale goldenrod" :background "pale goldenrod"
                             :inherit mode-line))))
  '(tab-bar ((t (:inherit mode-line-inactive))))
- '(tab-bar-tab ((t ( :background "light goldenrod" :box t
+ '(tab-bar-tab ((t ( :weight bold :underline t
                      :inherit variable-pitch))))
  '(tab-bar-tab-inactive ((t ( :background "pale goldenrod"
                               :inherit variable-pitch))))
@@ -165,9 +165,12 @@
  ;; Sh
  '(sh-heredoc ((t ( :background "azure" :extend t
                     :inherit font-lock-string-face))))
+ '(sh-quoted-exec ((t ())))
  ;; Widgets
  '(widget-field ((t (:inherit brianna-input-field))))
  '(widget-single-line-field ((t (:inherit brianna-input-field))))
+ ;; Whitespace-mode
+ '(whitespace-tab ((t (:foreground "#888"))))
  )
 
 (provide-theme 'brianna)
diff --git a/emacs.d/early-init.el b/emacs.d/early-init.el
index 7374bd1..b2de2f2 100644
--- a/emacs.d/early-init.el
+++ b/emacs.d/early-init.el
@@ -10,18 +10,21 @@
           (vertical-scroll-bars)
           (horizontal-scroll-bars)))
 
+(when (getenv "IN_EXWM")
+  (add-to-list 'default-frame-alist '(fullscreen . fullboth)))
+
 (defvar *fonts*
-  '((default
-     :family ;;("Recursive Mono Casual Static" "DejaVu Sans Mono")
-     ("Public Sans" "DejaVu Sans")
-     :height 100)
-    (variable-pitch
-     :family ("Public Sans" "DejaVu Sans")
-     :height 1.0)
-    (fixed-pitch
-     :family ("Recursive Mono Casual Static" "DejaVu Sans Mono"))
-    (fixed-pitch-serif
-     :family ("Recursive Mono Linear Static" "DejaVu Sans Mono"))))
+  (let ((fixed "Recursive Mono Casual Static")
+        (variable "Recursive Sans Casual Static"))
+    `((default
+       :family ,variable
+       :height 100)
+      (variable-pitch
+       :family ,variable)
+      (fixed-pitch
+       :family ,fixed)
+      (fixed-pitch-serif
+       :family "Recursive Mono Linear Static"))))
 
 (require 'package)
 (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
@@ -47,24 +50,6 @@
     (delete-trailing-whitespace (line-end-position)
                                 (point-max))))
 
-(defun run-after-frame-init (func)
-  "Run FUNC after the first frame is initialized.
-If already so, run FUNC immediately."
-  (cond
-   ((daemonp)
-    (add-hook 'server-after-make-frame-hook func)
-    (advice-add func :after (lambda ()
-                              (remove-hook 'server-after-make-frame-hook
-                                           func)
-                              (advice-remove func
-                                             'after-frame-init-removing-advice))
-
-
-                '((name . after-frame-init-removing-advice))))
-   ((not after-init-time)
-    (add-hook 'after-init-hook func))
-   (:else (funcall func))))
-
 (defun first-found-font (&rest cands)
   "Return the first font of CANDS that is installed, or nil."
   (cl-loop with ffl = (font-family-list)
@@ -77,8 +62,7 @@ If already so, run FUNC immediately."
   ;; Default faces
   (cl-loop for (face . spec) in *fonts*
            do (set-face-attribute face nil
-                                  :family (apply #'first-found-font
-                                                 (plist-get spec :family))
+                                  :family (plist-get spec :family)
                                   :height (or (plist-get spec :height)
                                               'unspecified)))
   ;; Specialized fonts
@@ -171,20 +155,40 @@ With ARG, edit in the other window." file-name)
        (funcall (if arg #'find-file-other-window #'find-file)
                 ,file-name))))
 
-(defun indent-buffer+ ()
+(defun fixup-whitespace ()
   "Indent the current buffer and (un)`tabify'.
 Whether it tabifies or untabifies depends on `space-indent-modes'."
   (interactive)
   (save-mark-and-excursion
     (indent-region (point-min) (point-max))
-    (if (apply #'derived-mode-p space-indent-modes)
-        (untabify (point-min) (point-max))
-      (tabify (point-min) (point-max)))))
+    (if indent-tabs-mode
+        (tabify (point-min) (point-max))
+      (untabify (point-min) (point-max)))
+    (replace-regexp-in-region "
$" "" (point-min) (point-max))))
 
-(defun package-ensure (pkg)
-  "Install PKG if it's not already installed."
-  (unless (package-installed-p pkg)
-    (package-vc-install pkg)))
+(defun package-ensure (pkgspec &optional require)
+  "Install PKG if it's not already installed.
+REQUIRE means require it after ensuring it's installed."
+  (let ((pkg (if (listp pkgspec) (car pkgspec) pkgspec)))
+    (unless (package-installed-p pkg)
+      (if (symbolp pkgspec)
+          (or (ignore-errors
+                (package-install pkg)
+                t)
+              (ignore-errors
+                (message "Package `%s' not found, refreshing packages" pkg)
+                (package-refresh-contents)
+                (package-install pkg)
+                t)
+              (ignore-errors
+                (message "Package `%s' still not found, trying `%s'"
+                         pkg 'pkg-vc-install)
+                (package-vc-install pkgspec)
+                t)
+              (if no-error nil
+                (error "Can't find package: %s" pkg))))
+      (package-vc-install pkgspec))
+    (when require (require pkg))))
 
 (defun minibuffer-delete-directory ()
   "Delete the last directory in a file-completing minibuffer."
@@ -214,3 +218,203 @@ If ARG is 16, kill emacs without asking about processes."
 
 (defun regexp-concat (&rest regexps)
   (string-join regexps "\\|"))
+
+;; There is a bug in M-x finger
+(defun acdw/finger (user host)
+  "Finger USER on HOST.
+This command uses `finger-X.500-host-regexps'
+and `network-connection-service-alist', which see."
+  ;; One of those great interactive statements that's actually
+  ;; longer than the function call! The idea is that if the user
+  ;; uses a string like "pbreton@cs.umb.edu", we won't ask for the
+  ;; host name. If we don't see an "@", we'll prompt for the host.
+  (interactive
+   (let* ((answer (let ((default (ffap-url-at-point)))
+                    (read-string (format-prompt "Finger User" default) nil nil default)))
+          (index  (string-match (regexp-quote "@") answer)))
+     (if index
+         (list (substring answer 0 index)
+               (substring answer (1+ index)))
+       (list answer
+             (let ((default (ffap-machine-at-point)))
+               (read-string (format-prompt "At Host" default) nil nil default))))))
+  (let* ((user-and-host (concat user "@" host))
+         (process-name (concat "Finger [" user-and-host "]"))
+         (regexps finger-X.500-host-regexps)
+         ) ;; found
+    (and regexps
+         (while (not (string-match (car regexps) host))
+           (setq regexps (cdr regexps))))
+    (when regexps
+      (setq user-and-host user))
+    (run-network-program
+     process-name
+     host
+     (cdr (assoc 'finger network-connection-service-alist))
+     user-and-host)))
+
+(advice-add 'finger :override #'acdw-finger)
+
+(defun hide-minor-mode (mode &optional hook)
+  "Hide MODE from the mode-line.
+HOOK is used to trigger the action, and defaults to MODE-hook."
+  (setf (alist-get mode minor-mode-alist) (list ""))
+  (add-hook (intern (or hook (format "%s-hook" mode)))
+            (lambda () (hide-minor-mode mode))))
+
+(defun switch-to-other-buffer ()
+  "Switch to the `other-buffer'."
+  (interactive)
+  (switch-to-buffer nil))
+
+(defun popup-eshell (arg)
+  "Popup an eshell buffer in the current window."
+  (interactive "P")
+  (let ((dd default-directory))
+    (eshell arg)
+    (unless (equal dd default-directory)
+      (setq default-directory dd)
+      ;; Is this a good idea, really?
+      (eshell-bol)
+      (unless (eolp)
+        (insert "# "))
+      (eshell-send-input))))
+
+(defun vc-jump (arg)
+  "Jump to the current project's VC buffer.
+With ARG, prompt for the directory."
+  (interactive "P")
+  (if arg
+      (let ((current-prefix-arg nil))
+        (call-interactively #'vc-dir))
+    (project-vc-dir)))
+
+(defun custom-show-all-widgets ()
+  "toggle all \"More/Hide\" widgets in the current buffer."
+  ;; From unpackaged
+  (interactive)
+  (widget-map-buttons (lambda (widget _)
+                        (pcase (widget-get widget :off)
+                          ("More" (widget-apply-action widget)))
+                        nil)))
+
+(defun quit-minibuffer ()
+  (interactive)
+  (switch-to-minibuffer)
+  (minibuffer-keyboard-quit))
+
+(defun keyboard-quit* (arg)
+  (interactive "P")
+  (if arg
+      (quit-minibuffer)
+    (keyboard-quit)))
+
+(defun sort-sexps (beg end)
+  "Sort sexps in region.
+Comments stay with the code below."
+  ;; From unpackaged
+  (interactive "r")
+  (cl-flet ((skip-whitespace () (while (looking-at (rx (1+ (or space "\n"))))
+                                  (goto-char (match-end 0))))
+            (skip-both () (while (cond ((or (nth 4 (syntax-ppss))
+                                            (ignore-errors
+                                              (save-excursion
+                                                (forward-char 1)
+                                                (nth 4 (syntax-ppss)))))
+                                        (forward-line 1))
+                                       ((looking-at (rx (1+ (or space "\n"))))
+                                        (goto-char (match-end 0)))))))
+    (save-excursion
+      (save-restriction
+        (narrow-to-region beg end)
+        (goto-char beg)
+        (skip-both)
+        (cl-destructuring-bind (sexps markers)
+            (cl-loop do (skip-whitespace)
+                     for start = (point-marker)
+                     for sexp = (ignore-errors
+                                  (read (current-buffer)))
+                     for end = (point-marker)
+                     while sexp
+                     ;; Collect the real string, then one used for sorting.
+                     collect (cons (buffer-substring (marker-position start)
+                                                     (marker-position end))
+                                   (save-excursion
+                                     (goto-char (marker-position start))
+                                     (skip-both)
+                                     (buffer-substring (point)
+                                                       (marker-position end))))
+                     into sexps
+                     collect (cons start end)
+                     into markers
+                     finally return (list sexps markers))
+          (setq sexps (sort sexps (lambda (a b)
+                                    (string< (cdr a) (cdr b)))))
+          (cl-loop for (real . sort) in sexps
+                   for (start . end) in markers
+                   do (progn
+                        (goto-char (marker-position start))
+                        (insert-before-markers real)
+                        (delete-region (point) (marker-position end)))))))))
+
+(defun ^turn-off (mode)
+  "Higher-order function: returns a lambda to turn off MODE."
+  (lambda ()
+    (funcall mode -1)))
+
+(defun ^local-hook (hook fn)
+  "Hook FN to HOOK locally in a lambda.
+Good for adding to an add-hook."
+  (lambda () (add-hook hook fn t)))
+
+(defun ^local-unhook (hook fn)
+  "Remove FN from HOOK locally."
+  (lambda () (remove-hook hook fn t)))
+
+;; This needs to be a macro to take advantage of setf magic
+(defmacro setf/alist (alist key val &optional testfn)
+  `(setf (alist-get ,key ,alist nil nil (or ,testfn #'equal))
+         ,val))
+
+(defun unfill-region (beg end)
+  (interactive "*r")
+  (let ((fill-column most-positive-fixnum))
+    (fill-region beg end)))
+
+(defun unfill-paragraph ()
+  (interactive)
+  (let ((fill-column most-positive-fixnum))
+    (fill-paragraph beg end)))
+
+(defun unfill-buffer ()
+  (interactive)
+  (unfill-region (point-min) (point-max)))
+
+(defun unfill-buffer/force ()
+  (interactive)
+  (let ((buffer-read-only nil))
+    (unfill-buffer)
+    (visual-line-mode t)))
+
+(defmacro after (event &rest body)
+  "Do BODY after EVENT, which can be:
+- A feature
+- A hook -- if it requires arguments they'll be in the list `args'
+- The symbol 'init, which runs on after-init-hook"
+  (declare (indent 1))
+  (let ((lambda-form `(lambda (&rest args) ,@body)))
+    (pcase event
+      (`(timer ,ev) `(run-with-timer ,ev nil ,lambda-form))
+      (`(idle ,ev) `(run-with-idle-timer ,ev nil ,lambda-form))
+      (`(hook ,ev) `(add-hook ',ev ,lambda-form))
+      (`init `(after (hook after-init-hook) ,@body))
+      ((pred numberp) `(after (timer ,event) ,@body))
+      ((pred (lambda (ev)
+               (and (symbolp ev)
+                    (or (string-suffix-p "-hook" (symbol-name ev))
+                        (string-suffix-p "-function" (symbol-name ev))
+                        (string-suffix-p "-functions" (symbol-name ev))))))
+       `(after (hook ,event) ,@body))
+      ((pred symbolp) `(with-eval-after-load ',event ,@body))
+
+      (_ (error "Can't determine event type" event)))))
-- 
cgit 1.4.1-21-gabe81