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 +;; Author: Case Duckworth ;; 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 "" #'corfu-next) +(keymap-set corfu-map "S-TAB" #'corfu-previous) +(keymap-set corfu-map "" #'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- 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-" t) (keymap-global-unset "C-" 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 "" 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