diff options
Diffstat (limited to '.emacs.d/init.el')
-rw-r--r-- | .emacs.d/init.el | 1299 |
1 files changed, 543 insertions, 756 deletions
diff --git a/.emacs.d/init.el b/.emacs.d/init.el index 87dde3cd..f633c397 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -23,11 +23,6 @@ ;; matching ~/.emacs.d/*.el, since the "spw-" and "spw--" prefixes would be ;; for a file called "spw.el" with a defined API, providing an `spw' feature. ;; -;; Prefer using the customise interface for variables, faces and enabling and -;; disabling global minor modes, except when we want to associate an extended -;; comment to a number of settings (typically we use the customise interface's -;; facility for adding comments for only short comments). -;; ;; Aim for compatibility with the version of Emacs included in Debian stable. ;; ;; Bind only key sequences reserved for users or which are already bound to @@ -82,40 +77,49 @@ ;;;; Customisation & appearance -(custom-set-faces - ;; custom-set-faces was added by Custom. - ;; If you edit it by hand, you could mess it up, so be careful. - ;; Your init file should contain only one such instance. - ;; If there is more than one, they won't work right. +;; The customisation system is additionally used to save safe local variable +;; values, SSL certificate overrides etc. (cf. `customize-push-and-save'). +;; Most of these should not be committed to git: they're often host-specific, +;; usually only relevant for a short time, and sometimes private. +;; So we use a disposable, local custom file, and `custom-theme-set-faces', +;; `customize-set-variable' and `custom-theme-set-variables' in this file. +;; (Re `setopt' vs. `customize-set-variable': former is for when we're making +;; a dynamic change that is not just host-specific but session-specific.) +(load (setq custom-file (expand-file-name "custom" user-emacs-directory)) + 'noerror 'nomessage 'nosuffix) + +(custom-theme-set-faces + 'user '(default ((t (:weight medium :height 105 :foundry "SRC" :family "Hack")))) + '(fixed-pitch ((t (:foundry "SRC" :family "Hack")))) + '(variable-pitch ((t (:weight regular :height 120 + :foundry "bitstream" :family "Bitstream Charter")))) + ;; We want to handle the `variable-pitch'/`variable-pitch-text' distinction + ;; using `spw/maybe-scale-basic-faces' instead. + '(variable-pitch-text ((t (:inherit variable-pitch)))) + '(comint-highlight-prompt ((t (:inherit minibuffer-prompt :weight bold)))) '(fill-column-indicator ((t (:background "light gray")))) - '(fixed-pitch ((t (:foundry "SRC" :family "Hack")))) - '(org-code ((t (:inherit (shadow fixed-pitch))))) - '(org-date ((t (:inherit fixed-pitch :foreground "Purple" :underline t)))) - '(org-verbatim ((t (:inherit (shadow fixed-pitch))))) - '(region ((t (:extend t :background "#EECD82"))) nil "Colour is from the Lucid build.") - '(variable-pitch ((t (:weight regular :height 120 :foundry "bitstream" :family "Bitstream Charter")))) - '(variable-pitch-text ((t (:inherit variable-pitch))) nil "Handled by `spw/maybe-scale-basic-faces' instead.")) - -;; Set background colour but don't touch text terminals. -(dolist (ws '(x pgtk w32 ns)) - (add-to-list - 'window-system-default-frame-alist - ;; If we were not started with --daemon or by 'emacsclient -a ""', then - ;; we're probably a shortlived instance of Emacs just to test something. - ;; Set a different background colour to more easily distinguish frames - ;; belonging to shortlived instances from those belonging to main instance. - `(,ws - . ((background-color - . ,(pcase (daemonp) - ('nil "honeydew") ("gdbmacs" "linen") (_ "#FFFFF6"))))))) + + ;; The colour is from the Lucid build of Emacs. + '(region ((t (:extend t :background "#EECD82"))))) + +;; Set background colours for graphical frames; leave TUI frames alone. +;; If we were not started with --daemon or by 'emacsclient -a ""', then +;; we're probably a shortlived instance of Emacs just to test something. +;; Set a different background colour to more easily distinguish frames +;; belonging to shortlived instances from those belonging to main instance. +(let ((colour (pcase (daemonp) + ('nil "#F0FFF0") ("gdbmacs" "#F5F5DC") (_ "#FFFFF6")))) + (dolist (ws '(x pgtk w32 ns)) + (add-to-list 'window-system-default-frame-alist + `(,ws . ((background-color . ,colour)))))) (defun spw/maybe-scale-basic-faces (frame) - "Entry for `window-size-change-functions' to increase font sizes -from those set by `custom-set-faces' for frames on wide monitors, -except where doing so would itself prevent fitting two 80-column -windows side-by-side in the frame." + "Entry for `window-size-change-functions' to increase font sizes, +relative to those set by the call to `custom-theme-set-faces' above, for +frames on wide monitors, except where doing so would itself prevent fitting +two 80-column windows side-by-side in the frame." (when (display-graphic-p frame) (let ((wide-monitor-p (> (cadddr (assoc 'geometry (frame-monitor-attributes frame))) @@ -167,32 +171,22 @@ windows side-by-side in the frame." (set-terminal-parameter nil 'background-mode 'light))) (add-hook 'tty-setup-hook #'spw/set-tmux-background-mode) -(custom-set-variables - ;; custom-set-variables was added by Custom. - ;; If you edit it by hand, you could mess it up, so be careful. - ;; Your init file should contain only one such instance. - ;; If there is more than one, they won't work right. +(custom-theme-set-variables + 'user '(Man-notify-method 'aggressive) '(after-save-hook '(executable-make-buffer-file-executable-if-script-p)) - '(appt-display-diary nil) - '(appt-display-interval 6) '(async-shell-command-buffer 'rename-buffer) + + ;; Always update buffers when files change on disk -- if we want to go back + ;; to the version of the file we had in Emacs, we can just hit C-/. + '(auto-revert-use-notify nil) + '(global-auto-revert-mode 1) + '(auth-source-save-behavior nil) '(auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-saves/" t))) '(backup-by-copying-when-linked t) '(backup-directory-alist '(("." . "~/.emacs.d/backups/"))) - '(bongo-default-directory "~/annex/music/") - '(bongo-insert-album-covers t) - '(bongo-insert-whole-directory-trees t) - '(bongo-mode-line-indicator-mode nil) - '(bongo-prefer-library-buffers nil) - '(c-default-style "linux") '(calc-kill-line-numbering nil) - '(calendar-date-display-form - '((format "%s-%.2d-%.2d %.3s" year (string-to-number month) - (string-to-number day) dayname))) - '(calendar-date-style 'iso) - '(calendar-week-start-day 1) '(column-number-mode t) '(comint-prompt-read-only t) '(compilation-scroll-output 'first-error) @@ -202,223 +196,67 @@ windows side-by-side in the frame." '(copy-region-blink-delay 0) '(copyright-names-regexp "Sean Whitton") '(copyright-year-ranges t) - '(cperl-close-paren-offset -4 nil nil "See `cperl-indent-parens-as-block'.") - '(cperl-indent-level 4) - '(cperl-indent-parens-as-block t nil nil "Makes it easier to use longer names for subroutines.") - '(cperl-lineup-step 1) '(cursor-type 'box) - '(cycle-spacing-actions '(just-one-space) nil nil "Restore Emacs 28 behaviour of M-SPC.") + + ;; C-u M-\ to delete only before point, M-SPC M-SPC to delete only after. + '(cycle-spacing-actions '(just-one-space (delete-space-after -))) + '(dabbrev-case-fold-search t) - '(diary-file "~/doc/emacs-diary") - '(diary-list-entries-hook '(diary-include-other-diary-files diary-sort-entries)) - '(diary-mark-entries-hook '(diary-mark-included-diary-files)) - '(dired-clean-up-buffers-too nil) - '(dired-dwim-target t) - '(dired-free-space 'separate) - '(dired-isearch-filenames t) - '(dired-listing-switches "--group-directories-first -alh") - '(dired-omit-files "\\`[.]?#\\|\\`[.][.]?\\'\\|\\`\\.git\\'") - '(dired-recursive-copies 'always) '(display-fill-column-indicator-character 32) '(ediff-split-window-function 'split-window-horizontally) '(eldoc-minor-mode-string nil) '(emacs-lisp-docstring-fill-column 75) '(enable-recursive-minibuffers t) - '(eshell-cmpl-cycle-completions nil nil nil "This makes Eshell completions a bit more like bash's.") - '(eshell-cmpl-ignore-case t) - '(eshell-hist-ignoredups 'erase) - '(eshell-history-append t) - '(eshell-history-size 5000) - '(eshell-save-history-on-exit nil) - '(eshell-visual-commands - '("vi" "screen" "tmux" "top" "htop" "less" "more" "mutt" "locmaint" "gen-DSA" - "gen-DLA" "gen-ELA")) '(fill-column 78) - '(font-lock-maximum-decoration '((lisp-mode . 1) (consfigurator-lisp-mode . 1) (t . t))) + '(font-lock-maximum-decoration + '((lisp-mode . 1) (consfigurator-lisp-mode . 1) (t . t))) '(gc-cons-threshold 16777216) '(gdb-many-windows t) - '(gdb-show-main t nil nil "This is helpful when gdb-many-windows is turned off.") - '(git-rebase-confirm-cancel nil) + + ;; This is helpful when gdb-many-windows is turned off. + '(gdb-show-main t) + '(global-so-long-mode t) - '(gnus-article-skip-boring t) - '(gnus-auto-center-summary nil) - '(gnus-auto-select-next 'slightly-quietly) - '(gnus-buttonized-mime-types - '("text/x-\\(?:diff\\|patch\\)" "multipart/\\(?:alternative\\|signed\\)")) - '(gnus-directory "~/local/News/") - '(gnus-extra-headers '(To Cc List-Id)) - '(gnus-gcc-mark-as-read t) - '(gnus-global-score-files '("~/doc/News/")) - '(gnus-interactive-exit 'quiet) - '(gnus-kill-files-directory "~/src/athpriv/News/") - '(gnus-kill-summary-on-exit t nil nil "Would prefer nil but t seems advisable for notmuch groups.") - '(gnus-large-ephemeral-newsgroup 8000) - '(gnus-large-newsgroup 8000) - '(gnus-mark-article-hook '(spw/gnus-mark-article-hook)) - '(gnus-message-archive-group "sent") - '(gnus-message-archive-method '(nnmaildir "fmail" (directory "~/.fmail/"))) - '(gnus-permanently-visible-groups "^nnmaildir\\+fmail:\\(?:notes\\|sent\\)$") - '(gnus-read-newsrc-file nil) - '(gnus-refer-thread-use-search '(("nnmaildir:fmail"))) - '(gnus-save-killed-list - "^\\(?:[^n]\\|n[^n]\\|nn[^s]\\|nns[^e]\\|nnse[^l]\\|nnsel[^e]\\|nnsele[^c]\\|nnselec[^t]\\|nnselect[^:]\\)") - '(gnus-save-newsrc-file nil) - '(gnus-search-default-engines '((nnmaildir . notmuch))) - '(gnus-search-notmuch-remove-prefix "~/.fmail/") - '(gnus-secondary-select-methods '((nnmaildir "fmail" (directory "~/.fmail/")))) - '(gnus-sum-thread-tree-false-root "") - '(gnus-sum-thread-tree-indent " ") - '(gnus-sum-thread-tree-leaf-with-other "├► ") - '(gnus-sum-thread-tree-root "") - '(gnus-sum-thread-tree-single-leaf "╰► ") - '(gnus-sum-thread-tree-vertical "│") - '(gnus-summary-line-format "%U%R%z %(%12&user-date; %*%-23,23f%) %B%s\12") - '(gnus-summary-mode-line-format "Gnus: %u&summary;%g [%A] %Z") - '(gnus-summary-thread-gathering-function 'gnus-gather-threads-by-references) - '(gnus-suppress-duplicates t) - '(gnus-thread-sort-functions - '(gnus-thread-sort-by-number gnus-thread-sort-by-total-score)) - '(gnus-topic-display-empty-topics nil) - '(gnus-update-message-archive-method t) - '(gnus-user-date-format-alist - '((32042 . "%2l:%M%#p") (118823 . "Yest %2l:%M%#p") (604800 . "%a %2l:%M%#p") - (16102447 . "%d %B") (t . "%Y-%b-%d"))) - '(haskell-indentation-layout-offset 4) - '(haskell-indentation-left-offset 4) + '(help-display-function-type nil) '(help-window-keep-selected t) - '(holiday-bahai-holidays nil) - '(holiday-hebrew-holidays nil) - '(holiday-islamic-holidays nil) - '(howm-directory "~/doc/howm/") - '(howm-file-name-format "%Y/%Y-%m-%d-%H%M.org") - '(howm-keyword-file "~/doc/howm/.howm-keys") - '(howm-view-use-grep t) - '(icomplete-hide-common-prefix nil) - '(icomplete-in-buffer t) - '(icomplete-mode t) - '(icomplete-show-matches-on-no-input t) - '(icomplete-tidy-shadowed-file-names t) '(imenu-auto-rescan t) + '(inferior-lisp-program "sbcl") '(initial-major-mode 'spw/scratch-lisp-interaction-mode) '(kill-read-only-ok t) - '(log-edit-hook - '(log-edit-insert-message-template log-edit-insert-cvs-template - log-edit-insert-changelog - spw/log-edit-show-diff) nil nil "Drop log-edit-show-files to avoid its window becoming most recently used for C-x o.") - '(mail-envelope-from 'header nil nil "Bypass MTA rewriting user@localhost.") - '(mail-specify-envelope-from t nil nil "Bypass MTA rewriting user@localhost.") + + ;; Bypass MTA rewriting user@localhost. + '(mail-envelope-from 'header) + '(mail-specify-envelope-from t) + '(mail-user-agent 'gnus-user-agent) '(mailscripts-detach-head-from-existing-branch 'ask) '(mailscripts-extract-patches-branch-prefix "mail/") '(mailscripts-project-library 'project) - '(major-mode-remap-alist '((perl-mode . cperl-mode)) nil nil "cperl-mode doesn't try to indent lines within a POD, and usefully font locks scalars that are members of hashes and arrays.") - '(make-pointer-invisible t nil nil "Works only for self-insert chars and undone by changes in window manager focus, but less annoying than `mouse-avoidance-mode'.") - '(message-auto-save-directory "~/tmp/" nil nil "So locmaint will catch them.") - '(message-citation-line-format "On %a %d %b %Y at %I:%M%p %Z, %N wrote:\12") - '(message-citation-line-function 'message-insert-formatted-citation-line) - '(message-forward-as-mime nil nil nil "For compatibility.") - '(message-forward-before-signature nil nil nil "For compatibility.") - '(message-forward-included-headers - '("^From:" "^Subject:" "^Date:" "^To:" "^Cc:" "^Message-ID:") nil nil "For compatibility.") - '(message-ignored-resent-headers - "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:\\|^>?From \\|^Delivered-To:\\|^\\(?:X-\\)?Content-Length:\\|^X-UIDL:\\|^X-TUID:\\|^\\(?:X-\\)?Status:\\|^Lines:") - '(message-make-forward-subject-function '(message-forward-subject-fwd) nil nil "For compatibility.") - '(message-sendmail-envelope-from 'header nil nil "Bypass MTA rewriting user@localhost.") - '(message-wash-forwarded-subjects t) + + ;; `cperl-mode' doesn't try to indent lines within a POD, and usefully font + ;; locks scalars that are members of hashes and arrays. + '(major-mode-remap-alist '((perl-mode . cperl-mode))) + + ;; Works only for self-insert chars and undone by changes in window manager + ;; focus, but less annoying than `mouse-avoidance-mode'. + '(make-pointer-invisible t) + '(minibuffer-follows-selected-frame nil) - '(mm-decrypt-option 'known) - '(mm-default-directory "~/tmp/") - '(mm-file-name-rewrite-functions - '(mm-file-name-delete-control mm-file-name-delete-gotchas - mm-file-name-trim-whitespace - mm-file-name-collapse-whitespace - mm-file-name-replace-whitespace)) - '(mml-secure-openpgp-encrypt-to-self t nil nil "So I can read copies in my sent mail directory.") - '(mml-secure-openpgp-sign-with-sender t) '(mode-line-compact 'long) - '(mouse-drag-copy-region t nil nil "X primary selection-like behaviour within Emacs even when not available outside.") - '(mouse-highlight 1 nil nil "See `make-pointer-invisible'.") + + ;; X primary selection-like behaviour within Emacs even when not available + ;; outside. + '(mouse-drag-copy-region t) + + ;; See `make-pointer-invisible'. + '(mouse-highlight 1) + '(mouse-yank-at-point t) '(native-comp-async-jobs-number 1) '(native-comp-async-report-warnings-errors 'silent) - '(nnmail-extra-headers '(To Cc List-Id)) - '(notmuch-address-use-company nil) - '(nov-text-width 78) - '(org-adapt-indentation t nil nil "Sometimes set to nil in .dir-locals.el, e.g. in ~/doc/newpapers.") - '(org-agenda-entry-text-maxlines 3) - '(org-agenda-files "~/doc/emacs-org-agenda-files") - '(org-agenda-persistent-filter t) - '(org-agenda-remove-times-when-in-prefix 'beg) - '(org-agenda-restore-windows-after-quit nil nil nil "Interacts badly with `tab-bar-history-mode'.") - '(org-agenda-skip-deadline-if-done t) - '(org-agenda-skip-deadline-prewarning-if-scheduled 3) - '(org-agenda-skip-scheduled-if-deadline-is-shown 'not-today) - '(org-agenda-skip-scheduled-if-done t) - '(org-agenda-skip-timestamp-if-done t) - '(org-agenda-start-on-weekday nil) - '(org-agenda-sticky t) - '(org-agenda-timegrid-use-ampm t) - '(org-agenda-todo-ignore-scheduled 'future) - '(org-agenda-window-setup 'current-window) - '(org-archive-location "~/doc/archive/howm/archive.org::* From %s") - '(org-archive-save-context-info '(time file olpath)) - '(org-archive-subtree-save-file-p t) - '(org-blank-before-new-entry '((heading . t) (plain-list-item . auto))) - '(org-bookmark-names-plist nil nil nil "Turn off to avoid git merge conflicts.") - '(org-cycle-separator-lines 0) - '(org-deadline-warning-days 60) - '(org-default-notes-file "~/doc/howm/refile.org") - '(org-directory "~/doc/howm/") - '(org-enforce-todo-checkbox-dependencies t) - '(org-enforce-todo-dependencies t) - '(org-fold-catch-invisible-edits 'show) - '(org-fold-show-context-detail - '((agenda . local) (bookmark-jump . lineage) (isearch . lineage) - (default . ancestors-full))) - '(org-footnote-section "Notes") - '(org-imenu-depth 4) - '(org-list-allow-alphabetical nil nil nil "So I can start lines with \"P. 211 - \" to refer to p. 211 not start a list.") - '(org-list-demote-modify-bullet - '(("-" . "+") ("+" . "*") ("*" . "-") ("1." . "-") ("1)" . "-"))) - '(org-list-use-circular-motion t) - '(org-log-done 'time) - '(org-log-into-drawer t) - '(org-log-repeat nil nil nil "Cluttering, and information probably in git.") - '(org-log-states-order-reversed nil) - '(org-outline-path-complete-in-steps nil nil nil "Desirable with `icomplete-mode'.") - '(org-read-date-prefer-future 'time) - '(org-refile-allow-creating-parent-nodes 'confirm) - '(org-refile-targets '((org-agenda-files :maxlevel . 5) (nil :maxlevel . 5))) - '(org-refile-use-outline-path 'file) - '(org-special-ctrl-a/e t) - '(org-special-ctrl-k t) - '(org-startup-folded nil) - '(org-startup-indented nil nil nil "Ensures buffer text doesn't go beyond 80 columns.") - '(org-tags-match-list-sublevels 'indented) - '(org-todo-keyword-faces - '(("SOMEDAY" :foreground "#0000FF" :weight bold) - ("NEXT" :foreground "#DD0000" :weight bold))) - '(org-todo-keywords - '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") - (sequence "WAITING(w@)" "SOMEDAY(s)" "|" "CANCELLED(c)"))) - '(org-treat-S-cursor-todo-selection-as-state-change nil) - '(org-treat-insert-todo-heading-as-state-change t) - '(org-use-fast-todo-selection 'expert) - '(org-yank-folded-subtrees nil) '(proced-enable-color-flag t) '(proced-show-remote-processes t) '(project-switch-commands 'project-prefix-or-any-command) - '(rcirc-default-full-name "Sean Whitton [spwhitton@spwhitton.name]") - '(rcirc-default-nick "spwhitton") - '(rcirc-default-user-name "spwhitton") - '(rcirc-display-server-buffer nil) - '(rcirc-log-directory "~/local/irclogs") - '(rcirc-log-filename-function 'spw/rcirc-generate-log-filename) - '(rcirc-log-flag t) - '(rcirc-time-format "%b/%d %H:%M ") - '(rcirc-track-abbreviate-flag nil) - '(rcirc-track-ignore-server-buffer-flag t) - '(rcirc-track-minor-mode t) '(read-buffer-completion-ignore-case t) '(read-file-name-completion-ignore-case t) '(read-mail-command 'gnus) @@ -427,38 +265,55 @@ windows side-by-side in the frame." '(remember-notes-buffer-name "*scratch*") '(remote-file-name-inhibit-delete-by-moving-to-trash t) '(require-final-newline t) - '(save-interprogram-paste-before-kill nil nil nil "See <https://debbugs.gnu.org/53728>.") - '(save-place-mode t nil nil "If quitting Emacs is slow, set `save-place-forget-unreadable-files' to nil.") + + ;; See <https://debbugs.gnu.org/53728>. + '(save-interprogram-paste-before-kill nil) + + ;; If quitting Emacs is slow set `save-place-forget-unreadable-files' to nil. + '(save-place-mode t) + '(savehist-additional-variables '(compile-history log-edit-comment-ring)) '(savehist-mode t) '(select-active-regions 'only) '(select-enable-primary t) '(send-mail-function 'sendmail-send-it) '(shell-command-prompt-show-cwd t) - '(show-paren-when-point-in-periphery t nil nil "Useful for C-M-d.") + + ;; Useful for C-M-d. + '(show-paren-when-point-in-periphery t) + '(shr-max-width 78) '(slime-load-failed-fasl 'never) '(tab-bar-history-mode t) '(tab-bar-show 1) - '(tramp-auto-save-directory "~/.emacs.d/auto-saves/" nil nil "Put TRAMP auto-saves under local `user-emacs-directory'.") - '(tramp-backup-directory-alist '(("." . "~/.emacs.d/backups/")) nil nil "Put TRAMP backups under remote ~/.emacs.d/.") + '(text-mode-ispell-word-completion nil) + + ;; Put TRAMP auto-saves under local `user-emacs-directory'. + '(tramp-auto-save-directory "~/.emacs.d/auto-saves/") + ;; Put TRAMP backups under remote ~/.emacs.d/. + '(tramp-backup-directory-alist '(("." . "~/.emacs.d/backups/"))) + '(tramp-copy-size-limit nil) '(tramp-default-method "rsync") - '(tramp-use-connection-share nil nil nil "Rely on my ~/.ssh/config.") - '(tramp-verbose 1 nil nil "Manual says this should improve performance.") + + ;; Rely on my ~/.ssh/config. + '(tramp-use-connection-share nil) + + ;; Manual says this should improve performance. + '(tramp-verbose 1) + '(transient-cycles-buffer-siblings-mode t) '(transient-cycles-tab-bar-mode t) - '(transient-cycles-window-buffers-cycle-backwards-key [134217777] nil nil "M-1.") - '(transient-cycles-window-buffers-cycle-forwards-key [134217780] nil nil "M-4.") + '(transient-cycles-window-buffers-cycle-backwards-key [134217777]) ; M-1 + '(transient-cycles-window-buffers-cycle-forwards-key [134217780]) ; M-4 '(transient-cycles-window-buffers-mode t) '(uniquify-buffer-name-style 'forward nil (uniquify)) '(use-short-answers t) - '(vc-deduce-backend-nonvc-modes t) - '(vc-find-revision-no-save t) - '(vc-follow-symlinks t) - '(vc-git-diff-switches '("--patch-with-stat" "-M" "-C") nil nil "We might also consider -B.") - '(vc-git-print-log-follow t) - '(view-read-only t nil nil "Rebind otherwise useless self-insert keys, and means existing C-x C-r, C-x 4 r etc. usable for getting into mode.") + + ;; Rebind otherwise useless self-insert keys, and means existing C-x C-r, + ;; C-x 4 r etc. usable for getting into the mode. + '(view-read-only t) + '(warning-suppress-types '((comp))) '(window-combination-resize t) '(x-stretch-cursor t)) @@ -522,6 +377,11 @@ available are present in the `load-path'." for form = `(add-hook ',hook #',function) if name collect `(with-eval-after-load ',name ,form) else collect form))) +(defmacro spw/feature-add-to-list (list-var feature &rest elements) + (declare (indent 2)) + `(with-eval-after-load ',feature + ,@(cl-loop for el in elements collect `(add-to-list ',list-var ,el)))) + (cl-defmacro spw/define-skeleton (command (mode &key abbrev key (file `',mode) (map (intern (concat (symbol-name mode) "-map"))) @@ -1004,11 +864,6 @@ To be used only when it seems to be necessary." ;; calling emacsclient(1), this is like '<ESC>ZZ' in vi. (global-set-key "\C-cz" "\C-x\C-s\C-x#") -;; always update buffers when files change on disk -- if we want to go back to -;; the version of the file we had in Emacs, we can just hit undo -(setq auto-revert-use-notify nil) -(global-auto-revert-mode 1) - ;; C-x x g should not ask for confirmation. (global-set-key "\C-xxg" (lambda () (interactive) @@ -1056,10 +911,30 @@ To be used only when it seems to be necessary." (add-hook 'text-mode-hook #'goto-address-mode) (add-hook 'prog-mode-hook #'goto-address-prog-mode) +;; message-mode is sensitive to trailing whitespace in sig dashes and empty +;; headers. markdown-mode is sensitive in empty headers (e.g. "# " which I +;; use in writing essays) and newlines that indicate paragraph flow (obscure +;; Markdown feature) +;; +;; The message-mode case is handled by `spw/normalise-message', which is +;; better than setting `ws-butler-trim-predicate' to a complicated function +;; because the code in `spw/normalise-message' gets called less often. Could +;; try setting `ws-butler-trim-predicate' to handle the markdown-mode case, +;; but chances are someday I'll want to use that obscure markdown-mode feature +(define-globalized-minor-mode spw/ws-butler-global-mode ws-butler-mode + (lambda () (when (buffer-file-name) (ws-butler-mode 1))) + :predicate '((not markdown-mode + message-mode + lisp-interaction-mode) + prog-mode text-mode)) +(spw/ws-butler-global-mode 1) + +(spw/feature-add-hook rainbow-mode (sgml-mode html-mode-hook) css-mode) + (global-set-key "\C-cih" #'add-file-local-variable-prop-line) ;; don't do anything with abbrevs if ~/doc is not checked out -(defvar spw/doc-abbrevs-file (expand-file-name "~/doc/emacs-abbrevs")) +(defvar spw/doc-abbrevs-file (expand-file-name "~/doc/abbrevs")) (when (file-exists-p spw/doc-abbrevs-file) (setq abbrev-file-name spw/doc-abbrevs-file) (quietly-read-abbrev-file) @@ -1070,7 +945,7 @@ To be used only when it seems to be necessary." (setf (cadr (assq 'abbrev-mode minor-mode-alist)) nil)) ;; similar -(defvar spw/doc-bookmarks-file (expand-file-name "~/doc/emacs-bookmarks")) +(defvar spw/doc-bookmarks-file (expand-file-name "~/doc/bookmarks")) (when (file-exists-p spw/doc-bookmarks-file) (setq bookmark-default-file spw/doc-bookmarks-file bookmark-save-flag 1)) @@ -1187,6 +1062,14 @@ To be used only when it seems to be necessary." ;;; Icomplete +(custom-theme-set-variables + 'user + '(icomplete-hide-common-prefix nil) + '(icomplete-in-buffer t) + '(icomplete-mode t) + '(icomplete-show-matches-on-no-input t) + '(icomplete-tidy-shadowed-file-names t)) + ;; Possibly we could call `minibuffer-complete-word' if we know we're ;; completing the name of a Lisp symbol. (spw/reclaim-keys-from minibuffer minibuffer-local-completion-map " " "?") @@ -1294,45 +1177,6 @@ To be used only when it seems to be necessary." ;;;; Buffers and windows -(defvar spw/arrow-keys-mode-map - (let ((map (make-sparse-keymap))) - (dolist (key '(up down left right - S-up S-down S-left S-right - M-up M-down M-left M-right)) - (define-key map (vector ?\C-z key) #'spw/arrow-keys-mode-passthru)) - map) - "Keymap for `spw/arrow-keys-mode'.") - -(define-minor-mode spw/arrow-keys-mode - "Apply the bindings in `spw/arrow-keys-mode-map', but -additionally bind the sequences of C-z followed by each of the -four arrow keys to activate a transient map in which the four -arrow keys have the bindings they would have absent this mode. - -Permits globally re-binding the four arrow keys without rendering -it impossible to access mode-specific bindings for those four -keys (e.g. the use of the left and right arrow keys in -`fido-mode' minibuffers)." - ;; :init-value t - :lighter nil :keymap spw/arrow-keys-mode-map :global t) - -(defun spw/arrow-keys-mode-passthru () - (interactive) - ;; Possibly we could cache the map in a buffer-local variable. It'd get - ;; cleared if the major mode changes, but we'd also need to figure out - ;; clearing it (but not recomputing it until and unless this function is - ;; called) if the minor modes change. - (let ((map (make-sparse-keymap)) - (cell (cl-find 'spw/arrow-keys-mode minor-mode-map-alist :key #'car))) - (cl-letf (((car cell) nil)) - (dolist (key '([up] [down] [left] [right] - [S-up] [S-down] [S-left] [S-right] - [M-up] [M-down] [M-left] [M-right])) - (define-key map key (key-binding key)))) - (let ((key (vector last-command-event))) - (call-interactively (lookup-key map key) t key)) - (set-transient-map map t))) - (defun spw/get-mru-window (&optional exclude) "Like `get-mru-window' but also consider the minibuffer, and don't consider windows satisfying the predicate EXCLUDE." @@ -1376,8 +1220,8 @@ don't consider windows satisfying the predicate EXCLUDE." ;;; <left>/<right>. We could still put something on unmodified <down>/<up>, ;;; which I used to use for `tab-bar-history-mode' forward & back commands. ;;; (`spw/arrow-keys-mode' made it feasible to bind things to unmodified arrow -;;; keys in the global map. That's disabled at present, as the unmodified -;;; arrow keys are not in use.) +;;; keys in the global map. That's archived to git history at present, as the +;;; unmodified arrow keys are not in use.) ;;; ;;; We might put one of the other sets of windmove commands, such as ;;; windmove-swap-states-* commands, on C-z M-7/8/9/0, or possibly @@ -1554,9 +1398,8 @@ state, attempt to produce some useful side window(s)." (let ((default-directory (expand-file-name "~/"))) (slime)))) (t (error "No side windows state & no heuristic"))))))) -;; Possibly this command should go on M-5 or M-6. -(global-set-key [remap window-toggle-side-windows] - #'spw/window-toggle-side-windows) +(define-key spw/personal-bindings-mode-map "\M-6" + #'spw/window-toggle-side-windows) (defun spw/delete-other-windows--toggle-side-windows (&optional window &rest _ignore) @@ -1583,54 +1426,20 @@ the non-side windows deleted by `delete-other-windows' will also reappear." (define-key ctl-x-5-map "\C-j" "\C-x55\C-x\C-j") (define-key tab-prefix-map "\C-j" "\C-xtt\C-x\C-j") -;;; For when the buffer's name isn't much help for switching to it, as is -;;; often the case with `notmuch-show' buffers. We select the most recent -;;; buffer but then transient cycling can take us to other buffers of the same -;;; major mode. - -(defun spw/read-major-mode-recent-buffer () - (let ((buffers (make-hash-table))) - (dolist (buffer (buffer-list)) - (with-current-buffer buffer - (unless (gethash major-mode buffers) - (puthash major-mode buffer buffers)))) - (list - (gethash - (intern - (completing-read - "Most recent buffer of major mode: " (hash-table-keys buffers) nil t)) - buffers)))) - -(spw/transient-cycles-define-buffer-switch - ((("\C-zb" . spw/switch-to-recent-major-mode-buffer) (buffer) - (interactive (spw/read-major-mode-recent-buffer)) - (switch-to-buffer buffer t)) - - (("\C-z4b" . spw/switch-to-recent-major-mode-buffer-other-window) (buffer) - (interactive (spw/read-major-mode-recent-buffer)) - (switch-to-buffer-other-window buffer t)) - - (("\C-z5b" . spw/switch-to-recent-major-mode-buffer-other-frame) (buffer) - (interactive (spw/read-major-mode-recent-buffer)) - (switch-to-buffer-other-frame buffer t)) - - (("\C-z4\C-o" . spw/display-recent-major-mode-buffer) (buffer) - (interactive (spw/read-major-mode-recent-buffer)) - (display-buffer buffer)))) +(autoload 'redtick "redtick") +(global-set-key "\C-cP" #'redtick) +(autoload 'redtick-mode "redtick") +(global-set-key "\C-cgP" #'redtick-mode) ;;;; TRAMP -(with-eval-after-load 'tramp - (add-to-list 'tramp-connection-properties - ;; Activate direct-async-process for all non-multihop SSH - ;; connections. - '("/ssh:" "direct-async-process" t) - ;; session-timeout is about dropping a connection for security - ;; reasons alone: never do that. - '(nil "session-timeout" nil)) - - (add-to-list 'tramp-remote-path 'tramp-own-remote-path)) +(spw/feature-add-to-list tramp-connection-properties tramp + ;; Activate `direct-async-process' for all non-multihop SSH connections. + '("/ssh:" "direct-async-process" t) + ;; Don't drop connections for security reasons alone. + '(nil "session-timeout" nil)) +(spw/feature-add-to-list tramp-remote-path tramp 'tramp-own-remote-path) (unless (string-match ; Emacs 28: unquote and `string-search' (regexp-quote tramp-file-name-regexp) vc-ignore-dir-regexp) @@ -1641,6 +1450,21 @@ the non-side windows deleted by `delete-other-windows' will also reappear." ;;;; The Emacs shell +(custom-theme-set-variables + 'user + ;; This makes Eshell completions a bit more like bash's. + '(eshell-cmpl-cycle-completions nil) + + '(eshell-cmpl-ignore-case t) + '(eshell-hist-ignoredups 'erase) + '(eshell-history-append t) + '(eshell-history-size 5000) + '(eshell-save-history-on-exit nil)) +(spw/feature-add-to-list eshell-modules-list esh-module + 'eshell-elecslash 'eshell-tramp 'eshell-xtra) +(spw/feature-add-to-list eshell-visual-commands em-term + "locmaint" "gen-DSA" "gen-ELA") + (with-eval-after-load 'esh-cmd (add-hook 'eshell-pre-command-hook (lambda () @@ -1675,19 +1499,6 @@ the non-side windows deleted by `delete-other-windows' will also reappear." ;; history of recent dirs is effectively buffer-local. (setq eshell-last-dir-ring-file-name nil) -(defun spw/eshell-cd-project-root () - (interactive) - (if-let ((project (project-current))) - (spw/eshell-cd (project-root project)) - (user-error "No current project"))) -(with-eval-after-load 'esh-mode - (define-key eshell-mode-map "\C-zp" #'spw/eshell-cd-project-root)) - -;; Work around Emacs bug #54977. -(with-eval-after-load 'esh-module - (mapc (apply-partially #'add-to-list 'eshell-modules-list) - '(eshell-elecslash eshell-tramp eshell-xtra))) - (spw/define-skeleton spw/eshell-libexec (eshell-mode :abbrev "le" :file "esh-mode") "" "" "~/" '(eshell-electric-forward-slash) "src/dotfiles/scripts/") @@ -1884,7 +1695,11 @@ Some ideas behind these behaviours are as follows. (spw/transient-cycles-define-buffer-switch ((("e" . spw/project-eshell) () (interactive) - (spw/eshell-jump 'project 'interactive))) + (prog1 (spw/eshell-jump 'project 'interactive) + ;; Make it possible to use M-& to repeat C-x p e. + (let ((map (make-sparse-keymap))) + (define-key map "\M-&" #'transient-cycles-cmd-spw/project-eshell) + (set-transient-map map))))) ;; Bind into project-prefix-map, rather than adding a remap, so that we ;; have it under C-x 4 p, C-x 5 p etc. too. :keymap project-prefix-map))) @@ -1892,8 +1707,6 @@ Some ideas behind these behaviours are as follows. ;;;; Miscellaneous functions & commands -;;;; This is meant to be like ~/src/dotfiles/bin & ~/src/dotfiles/scripts, not -;;;; so much about Emacs startup, except that we do want them always loaded. ;; This is an alternative way to activate the mark temporarily when ;; `transient-mark-mode' is off, and whether it's on or off, makes it @@ -1937,15 +1750,6 @@ Some ideas behind these behaviours are as follows. (add-hook 'isearch-mode-end-hook hook nil t))) (define-key spw/ctl-z-map "\s" #'spw/ensure-whole-lines-mode) -;; If know the name of group might try `gnus-read-ephemeral-gmane-group' (and -;; if that works well, might want to make this function prompt for a group to -;; pass to that function, and if blank, do what function does now). -(defun spw/browse-gmane () - (interactive) - (gnus-no-server) - (gnus-group-browse-foreign-server '(nntp "news.gmane.io"))) -(global-set-key "\C-cgG" #'spw/browse-gmane) - (defun spw/org-reformat-subtree () (interactive) ;; we have to set the mark, rather than just narrowing to the subtree, or @@ -2022,14 +1826,6 @@ Some ideas behind these behaviours are as follows. (global-set-key "\C-cD" #'spw/delete-visited-file) (global-set-key "\C-cvD" #'spw/vc-delete-visited-file) -(defun spw/link-stat-block (start end) - (interactive "r") - (when-let ((region-text (buffer-substring start end))) - (org-insert-link - nil - (concat "file:~/annex/gaming/5eblocks/" region-text ".png") - region-text))) - ;; Possibly this should be replaced with something like `project-find-regexp' ;; ;; Input e.g.: lisp "Emacs configuration" @@ -2043,21 +1839,6 @@ Some ideas behind these behaviours are as follows. nil (expand-file-name "~/doc"))) (global-set-key "\C-cog" #'spw/git-grep-docs) -;; this is called by .dir-locals.el in ~/doc/{pres,papers} -(defun spw/set-pandoc-compile-command (&rest exts) - (setq-local compile-command - (concat "make " - (mapconcat - (lambda (ext) - (file-name-nondirectory - (concat (file-name-sans-extension - (buffer-file-name)) - "." - ext))) - (or exts '("pdf")) - " "))) - (local-set-key "\C-z\C-c" #'compile)) - (defun spw/all-programming-projects () (call-process "src-register-all") (let ((default-directory (expand-file-name "~/src"))) @@ -2133,36 +1914,6 @@ Some ideas behind these behaviours are as follows. (display-buffer buffer)))) (global-set-key "\C-cvc" #'spw/clone-repo) -;; author unknown -(defun spw/toggle-frame-split () - "Toggle the orientation of a two-window split. - -Useful after resizing the frame." - (interactive) - (when (= (count-windows) 2) - (let* ((this-win-buffer (window-buffer)) - (next-win-buffer (window-buffer (next-window))) - (this-win-edges (window-edges (selected-window))) - (next-win-edges (window-edges (next-window))) - (this-win-2nd (not (and (<= (car this-win-edges) - (car next-win-edges)) - (<= (cadr this-win-edges) - (cadr next-win-edges))))) - (splitter - (if (= (car this-win-edges) - (car (window-edges (next-window)))) - 'split-window-horizontally - 'split-window-vertically))) - (delete-other-windows) - (let ((first-win (selected-window))) - (funcall splitter) - (when this-win-2nd (other-window 1)) - (set-window-buffer (selected-window) this-win-buffer) - (set-window-buffer (next-window) next-win-buffer) - (select-window first-win) - (when this-win-2nd (other-window 1)))))) -(define-key spw/personal-bindings-mode-map "\M-5" #'spw/toggle-frame-split) - (defun spw/maybe-toggle-split-after-resize (frame) (when (and (framep frame) (frame-size-changed-p frame) @@ -2250,78 +2001,6 @@ Useful after resizing the frame." (interactive) (spw/myrepos-global-action "sync"))) -;; There are many variations on this online. This one by Robert Bost, based -;; on work by Steve Yegge, Colin Doering and others -(defun spw/rotate-windows (arg) - "Rotate your windows, reversing direction if ARG." - (interactive "P") - (if (not (> (count-windows) 1)) - (message "You can't rotate a single window!") - (let* ((rotate-times (prefix-numeric-value arg)) - (direction (if (or (< rotate-times 0) (equal arg '(4))) - 'reverse 'identity))) - (dotimes (_ (abs rotate-times)) - (dotimes (i (- (count-windows) 1)) - (let* ((w1 (elt (funcall direction (window-list)) i)) - (w2 (elt (funcall direction (window-list)) (+ i 1))) - (b1 (window-buffer w1)) - (b2 (window-buffer w2)) - (s1 (window-start w1)) - (s2 (window-start w2)) - (p1 (window-point w1)) - (p2 (window-point w2))) - (set-window-buffer-start-and-point w1 b2 s2 p2) - (set-window-buffer-start-and-point w2 b1 s1 p1))))))) -;; This gets this key because we're likely to want to invoke it repeatedly. -(define-key spw/personal-bindings-mode-map "\M-6" #'spw/rotate-windows) - -;; some influence here from Michael Stapelberg's config -- we both had a -;; function to do this, I discovered -(defun spw/recipient-first-name () - "Attempt to extract the first name of the recipient of a `message-mode' message. - -Used in my `message-mode' yasnippets." - (if-let ((to (save-excursion - (save-restriction - (message-narrow-to-headers-or-head) - (message-fetch-field "To"))))) - (let ((full-name (car (mail-extract-address-components to)))) - (if (string-match "\\([^ ]+\\)" full-name) - (let ((first-name (match-string 0 full-name))) - (cond - ;; some names which may be in a longer form in the From header - ;; but which I would never type out in full in a salutation - ((string= first-name "Nathaniel") "Nathan") - ((string= first-name "Thomas") "Tom") - (t first-name))) - ;; no spaces -- assume whole thing is an alias and use it - full-name)) - "")) - -(spw/define-skeleton spw/message-dear - (message-mode :abbrev "dear" :file "message") - "" - (completing-read "Dear " (ignore-errors (list (spw/recipient-first-name)))) - '(when (setq v1 (looking-at ">")) (forward-line -2)) - "Dear " str "," \n \n - '(when v1 (forward-line 2))) - -(spw/define-skeleton spw/message-hello - (message-mode :abbrev "hl" :file "message") - "" - (completing-read "Hello " (ignore-errors (list (spw/recipient-first-name)))) - '(when (setq v1 (looking-at ">")) (forward-line -2)) - "Hello " str '(when (zerop (length str)) (delete-backward-char 1)) "," \n \n - '(when v1 (forward-line 2))) - -(spw/define-skeleton spw/message-thanks - (message-mode :abbrev "ty" :file "message") - "" - (completing-read "Dear " (ignore-errors (list (spw/recipient-first-name)))) - '(when (setq v1 (looking-at ">")) (forward-line -2)) - "Dear " str "," \n \n "Thank you for your e-mail." \n \n - '(when v1 (forward-line 2))) - (defun spw/copy-to-annotated () (interactive) (let* ((source (expand-file-name (dired-file-name-at-point))) @@ -2571,56 +2250,6 @@ Called by '~/src/dotfiles/bin/emacsclient --spw/update-environment'." (not (string= (daemonp) "gdbmacs")) (eq (server-running-p "gdbmacs") t))) -;; open a frame on a new workspace with only the relevant dired buffer open, -;; eval this form: (global-set-key "\C-cG" #'spw/grading-advance) -;; and then use C-c G to open the first item (will need C-c f t after just -;; this first one, and also maybe C-i =) -(defun spw/grading-advance () - (interactive) - (unless (eq major-mode 'dired-mode) - (when (eq major-mode 'org-mode) - (ignore-errors (org-ctrl-c-ctrl-c))) - (save-buffer) - (other-window 1)) - (dired-display-file) - (dired-next-line 1) - (let ((pdf (dired-get-filename))) - (dired-next-line 1) - (other-window 1) - (goto-char (point-min)) - - ;; assignment-specific - (search-forward "Grammar") - (org-cycle) - (set-goal-column nil) - ;; (overwrite-mode 1) - - (start-process "pdf" "pdf" "xdg-open" pdf) - (sleep-for 1) - (call-process-shell-command - (concat (if (executable-find "i3-msg") "i3-msg" "swaymsg") - " move right")) - (let ((pdf-words (substring (with-temp-buffer - (call-process-shell-command - (concat "pdftotext " - (shell-quote-argument pdf) - " - | wc -w") - nil - (current-buffer)) - (buffer-string)) - 0 - -1))) - (message (concat pdf-words " words"))))) - -(defun spw/untabify-project () - (interactive) - (save-window-excursion - (dolist (file (project-files - (project-current nil (project-prompt-project-dir)))) - - (find-file file) - (untabify (point-min) (point-max))))) - (defun spw/go-to-consfig () (interactive) ;; (let ((repo (expand-file-name "~/src/cl/consfig"))) @@ -2706,32 +2335,6 @@ Called by '~/src/dotfiles/bin/emacsclient --spw/update-environment'." (string-join (cons "rm -f config.cache; ./configure -C" (cdr conf)) " ") nil :system t))) -(defun spw/read-athenet-lxc () - (let (lxcs - (file (expand-file-name "~/src/cl/consfig/hosts.lisp"))) - (unless (file-exists-p file) - (user-error "Looks like consfig not checked out")) - (with-current-buffer (find-file-noselect file) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward - "(define-athenet-container \\([a-z0-9-.]+\\) -\\s-*\"\\([a-z0-9-.]+\\)" - nil t) - (push (list (substring-no-properties (match-string 1)) - (substring-no-properties (match-string 2))) - lxcs))))) - (assoc (completing-read "LXC: " lxcs nil t) lxcs #'string=))) - -(defun spw/ssh-and-lxc-attach-term (container host) - (interactive (spw/read-athenet-lxc)) - (start-process "ssh-and-tmux" nil "foot" "ssh-and-tmux" host - (format "--container-name=%s" container) -"--container-cmd=lxc-unpriv-attach -n %s --keep-var TERM --clear-env -vHOME=/root")) -(global-set-key "\C-cgL" #'spw/ssh-and-lxc-attach-term) - (defun spw/proced-root () (interactive) (require 'proced) @@ -2796,6 +2399,7 @@ Called by '~/src/dotfiles/bin/emacsclient --spw/update-environment'." ;; `compile' or `project-compile' is called, and one Emacs frame/tab for GUD. (defun spw/run-or-restore-gud (arg) (interactive "P") + (require 'gdb-mi) (if (or arg (not (and (bound-and-true-p gud-comint-buffer) (get-buffer-process gud-comint-buffer)))) ;; Start a new debugging session even if one already exists. @@ -2824,7 +2428,7 @@ Called by '~/src/dotfiles/bin/emacsclient --spw/update-environment'." (interactive) (let ((string (ignore-errors (cdr (bounds-of-thing-at-point 'string))))) (fill-region-as-paragraph - (car (bounds-of-thing-at-point 'sentence)) + (point) (cond (string (min string (cdr (bounds-of-thing-at-point 'paragraph)))) ((cl-fifth (syntax-ppss)) (min (cdr (bounds-of-thing-at-point 'paragraph)) @@ -2847,6 +2451,7 @@ Called by '~/src/dotfiles/bin/emacsclient --spw/update-environment'." (global-set-key "\C-ce" #'spw/fill-rest-of-paragraph) (global-set-key "\C-cj" "\M-j\C-ce") +;;;; "Miscellaneous functions & commands" page ends here ;;;; Terminal emulation @@ -2875,6 +2480,43 @@ Called by '~/src/dotfiles/bin/emacsclient --spw/update-environment'." ;;;; Composing mail +(custom-theme-set-variables + 'user + ;; So locmaint will catch them. + '(message-auto-save-directory "~/tmp/") + + '(message-citation-line-format "On %a %d %b %Y at %I:%M%p %Z, %N wrote:\12") + '(message-citation-line-function 'message-insert-formatted-citation-line) + + ;; For compatibility. + '(message-forward-as-mime nil) + '(message-forward-before-signature nil) + '(message-forward-included-headers + '("^From:" "^Subject:" "^Date:" "^To:" "^Cc:" "^Message-ID:")) + '(message-make-forward-subject-function '(message-forward-subject-fwd)) + + '(message-ignored-resent-headers + "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:\\|^>?From \\|^Delivered-To:\\|^\\(?:X-\\)?Content-Length:\\|^X-UIDL:\\|^X-TUID:\\|^\\(?:X-\\)?Status:\\|^Lines:") + + ;; Bypass MTA rewriting user@localhost. + '(message-sendmail-envelope-from 'header) + + '(message-wash-forwarded-subjects t) + '(mm-decrypt-option 'known) + '(mm-default-directory "~/tmp/") + '(mm-file-name-rewrite-functions + '(mm-file-name-delete-control mm-file-name-delete-gotchas + mm-file-name-trim-whitespace + mm-file-name-collapse-whitespace + mm-file-name-replace-whitespace)) + + ;; So I can read copies in my sent mail directory. + '(mml-secure-openpgp-encrypt-to-self t) + + '(mml-secure-openpgp-sign-with-sender t) + '(nnmail-extra-headers '(To Cc List-Id)) + '(notmuch-address-use-company nil)) + (defvar spw/debian-bts-pseudoheader-regexp ;; "^\\([A-Za-z][a-z]+: [^ ]+\\|[cC]ontrol: .+\\)$" "^[A-Za-z][a-z]+: [^ ]+" @@ -3063,6 +2705,9 @@ mutt's review view, after exiting EDITOR." (require 'notmuch-address) (notmuch-address-setup)) (add-hook 'message-mode-hook #'footnote-mode) + (spw/when-library-available orgalist + (add-hook 'message-mode-hook #'orgalist-mode)) + (add-hook 'message-mode-hook #'orgtbl-mode) (define-key message-mode-map [remap message-newline-and-reformat] #'spw/message-newline-and-reformat) @@ -3075,6 +2720,15 @@ mutt's review view, after exiting EDITOR." ;;;; Dired +(custom-theme-set-variables + 'user + '(dired-clean-up-buffers-too nil) + '(dired-dwim-target t) + '(dired-free-space 'separate) + '(dired-listing-switches "--group-directories-first -alh") + '(dired-omit-files "\\`[.]?#\\|\\`[.][.]?\\'\\|\\`\\.git\\'") + '(dired-recursive-copies 'always)) + ;; this is the way you're meant to request dired-aux, not just dired-x, ;; according to (info "(dired-x) Installation") (with-eval-after-load 'dired (require 'dired-x)) @@ -3151,7 +2805,7 @@ mutt's review view, after exiting EDITOR." "&" #'spw/dired-copy-filename-as-kill) -;;;; EWW +;;;; EWW / shr ;; this should ensure that M-a and M-e work for most webpages (add-hook 'eww-mode-hook (lambda () @@ -3181,9 +2835,66 @@ mutt's review view, after exiting EDITOR." (setq-local bookmark-make-record-function #'spw/bookmark-eww-bookmark-make-record))) +(customize-set-variable 'nov-text-width 78) +(spw/when-library-available nov + (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))) + ;;;; Gnus +(custom-theme-set-variables + 'user + '(gnus-article-skip-boring t) + '(gnus-auto-center-summary nil) + '(gnus-auto-select-next 'slightly-quietly) + '(gnus-buttonized-mime-types + '("text/x-\\(?:diff\\|patch\\)" "multipart/\\(?:alternative\\|signed\\)")) + '(gnus-directory "~/local/News/") + '(gnus-extra-headers '(To Cc List-Id)) + '(gnus-gcc-mark-as-read t) + '(gnus-global-score-files '("~/doc/News/")) + '(gnus-interactive-exit 'quiet) + '(gnus-kill-files-directory "~/src/athpriv/News/") + + ;; Would prefer nil but t seems advisable for notmuch groups. + '(gnus-kill-summary-on-exit t) + + '(gnus-large-ephemeral-newsgroup 8000) + '(gnus-large-newsgroup 8000) + '(gnus-mark-article-hook '(spw/gnus-mark-article-hook)) + '(gnus-message-archive-group "sent") + '(gnus-message-archive-method '(nnmaildir "fmail" (directory "~/.fmail/"))) + '(gnus-permanently-visible-groups "^nnmaildir\\+fmail:\\(?:notes\\|sent\\)$") + '(gnus-read-newsrc-file nil) + '(gnus-refer-thread-use-search '(("nnmaildir:fmail"))) + '(gnus-save-killed-list + "^\\(?:[^n]\\|n[^n]\\|nn[^s]\\|nns[^e]\\|nnse[^l]\\|nnsel[^e]\\|nnsele[^c]\\|nnselec[^t]\\|nnselect[^:]\\)") + '(gnus-save-newsrc-file nil) + '(gnus-search-default-engines '((nnmaildir . notmuch))) + '(gnus-search-notmuch-remove-prefix "~/.fmail/") + '(gnus-secondary-select-methods + '((nnmaildir "fmail" (directory "~/.fmail/")))) + '(gnus-sum-thread-tree-false-root "") + '(gnus-sum-thread-tree-indent " ") + '(gnus-sum-thread-tree-leaf-with-other "├► ") + '(gnus-sum-thread-tree-root "") + '(gnus-sum-thread-tree-single-leaf "╰► ") + '(gnus-sum-thread-tree-vertical "│") + '(gnus-summary-line-format "%U%R%z %(%12&user-date; %*%-23,23f%) %B%s\12") + '(gnus-summary-mode-line-format "Gnus: %u&summary;%g [%A] %Z") + '(gnus-summary-thread-gathering-function 'gnus-gather-threads-by-references) + '(gnus-suppress-duplicates t) + '(gnus-thread-sort-functions + '(gnus-thread-sort-by-number gnus-thread-sort-by-total-score)) + '(gnus-topic-display-empty-topics nil) + '(gnus-update-message-archive-method t) + '(gnus-user-date-format-alist + '((32042 . "%2l:%M%#p") + (118823 . "Yest %2l:%M%#p") + (604800 . "%a %2l:%M%#p") + (16102447 . "%d %B") + (t . "%Y-%b-%d")))) + (with-eval-after-load 'gnus (cond ((spw/on-host-p "chiark.greenend.org.uk") (setq gnus-select-method @@ -3889,7 +3600,8 @@ unread." ;; `gnus-summary-save-parts' has some alternative ways to get the handles ;; if `gnus-article-mime-handles' is nil. (let ((handles gnus-article-mime-handles)) - (when (stringp (car handles)) (pop handles)) + (when (stringp (car handles)) + (setq handles (cdr handles))) (mapc #'mm-save-part (cl-remove-if-not #'mm-handle-filename handles))))) (with-eval-after-load 'gnus-sum @@ -3905,25 +3617,39 @@ unread." (gnus-summary-goto-article article))) (advice-add 'org-gnus-follow-link :around #'spw/org-gnus-follow-link) -(defun spw/gnus-fastmail-trash (n) +(defun spw/gnus-purelymail-trash (n) (interactive "p") (gnus-summary-move-article n "nnmaildir+fmail:trash")) (with-eval-after-load 'gnus-sum (define-key gnus-summary-mode-map - [remap gnus-summary-delete-article] #'spw/gnus-fastmail-trash)) + [remap gnus-summary-delete-article] #'spw/gnus-purelymail-trash)) -(defun spw/gnus-fastmail-learn-spam (n) +(defun spw/gnus-purelymail-learn-spam (n) (interactive "p") (save-excursion (gnus-summary-mark-forward n)) - (gnus-summary-move-article n "nnmaildir+fmail:spam") + (gnus-summary-move-article n "nnmaildir+fmail:junk") (gnus-summary-next-unread-article)) (with-eval-after-load 'gnus-sum - (define-key gnus-summary-mode-map [f5] #'spw/gnus-fastmail-learn-spam) - (define-key gnus-summary-mode-map "\C-z\C-s" #'spw/gnus-fastmail-learn-spam)) + (define-key gnus-summary-mode-map [f5] #'spw/gnus-purelymail-learn-spam) + (define-key gnus-summary-mode-map "\C-z\C-s" #'spw/gnus-purelymail-learn-spam)) ;;;; rcirc +(custom-theme-set-variables + 'user + '(rcirc-default-full-name "Sean Whitton [spwhitton@spwhitton.name]") + '(rcirc-default-nick "spwhitton") + '(rcirc-default-user-name "spwhitton") + '(rcirc-display-server-buffer nil) + '(rcirc-log-directory "~/local/irclogs") + '(rcirc-log-filename-function 'spw/rcirc-generate-log-filename) + '(rcirc-log-flag t) + '(rcirc-time-format "%b/%d %H:%M ") + '(rcirc-track-abbreviate-flag nil) + '(rcirc-track-ignore-server-buffer-flag t) + '(rcirc-track-minor-mode t)) + (defun spw/rcirc-generate-log-filename (process target) (concat (file-name-concat (format-time-string "%Y/%m") (process-name process) (or target "server")) @@ -4003,6 +3729,30 @@ unread." ;;;; VC +(custom-theme-set-variables + 'user + '(git-rebase-confirm-cancel nil) + '(vc-deduce-backend-nonvc-modes t) + '(vc-find-revision-no-save t) + '(vc-follow-symlinks t) + + ;; We might also consider -B. + '(vc-git-diff-switches '("--patch-with-stat" "-M" "-C")) + + '(vc-git-print-log-follow t) + + ;; Drop the name & date to make more of the commit message visible. + '(vc-git-root-log-format + '("%d%h..: %s" + "^\\(?:[*/\\| ]+ \\)?\\(?2: ([^)]+)\\)?\\(?1:[0-9a-z]+\\)\\.\\.: " + ((1 'log-view-message) (2 'change-log-list nil lax))))) + +;; Avoid `log-edit-show-files' window becoming most recently used for C-x o. +(with-eval-after-load 'log-edit + (customize-set-variable 'log-edit-hook + (cons 'spw/log-edit-show-diff + (delete 'log-edit-show-files log-edit-hook)))) + (require 'git-commit nil t) (spw/when-library-available mailscripts @@ -4015,7 +3765,7 @@ unread." "vt" notmuch-extract-thread-patches-to-project "vw" mailscripts-extract-message-patches-to-project)) -;; Emacs 30: move into `custom-set-variables'. +;; Emacs 30: move into `custom-theme-set-variables' call. (when (>= emacs-major-version 30) (setopt vc-git-log-switches '("--format=fuller" "--stat"))) @@ -4268,70 +4018,12 @@ unread." (global-set-key "\C-cvf" 'spw/vc-next-action-for-git-fixup) -;;;; Assorted packages - -;; message-mode is sensitive to trailing whitespace in sig dashes and empty -;; headers. markdown-mode is sensitive in empty headers (e.g. "# " which I -;; use in writing essays) and newlines that indicate paragraph flow (obscure -;; Markdown feature) -;; -;; The message-mode case is handled by `spw/normalise-message', which is -;; better than setting `ws-butler-trim-predicate' to a complicated function -;; because the code in `spw/normalise-message' gets called less often. Could -;; try setting `ws-butler-trim-predicate' to handle the markdown-mode case, -;; but chances are someday I'll want to use that obscure markdown-mode feature -(define-globalized-minor-mode spw/ws-butler-global-mode ws-butler-mode - (lambda () (when (buffer-file-name) (ws-butler-mode 1))) - :predicate '((not markdown-mode - message-mode - lisp-interaction-mode) - prog-mode text-mode)) -(spw/ws-butler-global-mode 1) - -(autoload 'redtick "redtick") -(global-set-key "\C-cP" #'redtick) -(autoload 'redtick-mode "redtick") -(global-set-key "\C-cgP" #'redtick-mode) - -(with-eval-after-load 'org-d20 - (setq org-d20-dice-sound - "~/annex/media/sounds/147531__ziembee__diceland.wav" - org-d20-display-rolls-buffer t - ;; the roll20 tokens I'm using for NPCs are lettered - org-d20-letter-monsters t - ;; ... and they come in only two colours, so let's just have - ;; one monster per letter - org-d20-continue-monster-numbering t) - - (define-key org-d20-mode-map [f5] #'org-d20-initiative-dwim) - (define-key org-d20-mode-map [f6] #'org-d20-damage) - - (define-key org-d20-mode-map [f7] (lambda (arg) - (interactive "P") - (call-interactively - (if arg - #'org-d20-roll-last - #'org-d20-roll)))) - (define-key org-d20-mode-map [f8] #'org-d20-roll-at-point) - (define-key org-d20-mode-map [f9] (lambda (arg) - (interactive "P") - (call-interactively - (if arg - #'org-d20-d% - #'org-d20-d20))))) +;;;; Haskell -(spw/when-library-available nov - (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))) - -(setq ggtags-mode-line-project-name nil) - -(spw/when-library-available ggtags - (dolist (hook '(cperl-mode-hook c-mode-hook)) - (add-hook hook #'ggtags-mode))) - -(spw/when-library-available rainbow-mode - (dolist (hook '(html-mode-hook css-mode-hook)) - (add-hook hook 'rainbow-mode))) +(custom-theme-set-variables + 'user + '(haskell-indentation-layout-offset 4) + '(haskell-indentation-left-offset 4)) (spw/feature-add-hook subword-mode haskell-mode) @@ -4348,10 +4040,16 @@ unread." nil t)) haskell-mode)) -(spw/when-library-available orgalist - (spw/feature-add-hook orgalist-mode message)) + +;;;; Bongo -(spw/feature-add-hook orgtbl-mode message) +(custom-theme-set-variables + 'user + '(bongo-default-directory "~/annex/music/") + '(bongo-insert-album-covers t) + '(bongo-insert-whole-directory-trees t) + '(bongo-mode-line-indicator-mode nil) + '(bongo-prefer-library-buffers nil)) (spw/feature-add-hook (lambda () (dired-hide-details-mode @@ -4387,14 +4085,6 @@ unread." ;; 'v' again to exit (global-set-key "\C-cgv" #'volume) -(with-eval-after-load 'elpher - ;; standard Emacs conventions - (define-key elpher-mode-map "l" #'elpher-back) - (define-key elpher-mode-map "d" #'elpher-back-to-start) - (define-key elpher-mode-map "<" #'elpher-root-dir) - - (add-hook 'elpher-mode-hook (lambda () (variable-pitch-mode 1)))) - ;;;; Lisp @@ -4464,15 +4154,6 @@ unread." (with-temp-buffer (call-interactively #'hyperspec-lookup))) (global-set-key "\C-cgh" #'spw/hyperspec-lookup) -;; `inf-lisp' says this is a defcustom and `slime' says it is a defvar, so -;; `custom-save-variables' will print the NOW field in the corresponding -;; argument to `custom-set-variables' as t or nil depending on whether or not -;; `inf-lisp' and/or `slime' happen to be loaded, and possibly even depending -;; on the order in which they were loaded. To prevent spurious changes to the -;; NOW field randomly showing up in git diffs of init.el, set the variable -;; without using the customisation interface. -(setq inferior-lisp-program "sbcl") - (defvar spw/last-command-was-slime-async-eval nil) (defvar spw/last-slime-async-eval-command-frame nil) @@ -4536,22 +4217,6 @@ that the user is expecting that it might pop up." (defslime-repl-shortcut nil ("clear-source-registry") (:handler #'spw/slime-clear-source-registry))) -(defun spw/comment-form (n) - "Replacement for \\[comment-line] in Lisp modes which is more -likely to keep parentheses balanced." - (interactive "p") - (if (use-region-p) - (comment-line n) - (let ((begin (point)) - (end (line-end-position))) - (skip-chars-forward "; \t" end) - (forward-sexp) - (unless (> (point) (line-end-position)) - (comment-or-uncomment-region begin (point)))))) -(define-key lisp-mode-shared-map [?\C-x ?\C-\;] #'spw/comment-form) -(when (boundp 'lisp-data-mode-map) ; Emacs 27 - (define-key lisp-data-mode-map [?\C-x ?\C-\;] #'spw/comment-form)) - ;; Loading `slime' puts `slime-macrostep' on `load-path'. ;; `slime-macrostep' knows how to load an embedded copy of `macrostep'. (with-eval-after-load 'slime (require 'slime-macrostep)) @@ -4786,6 +4451,97 @@ before uploading to NEW again." \n \n ;;;; Org-mode +(custom-theme-set-faces + 'user + '(org-code ((t (:inherit (shadow fixed-pitch))))) + '(org-date ((t (:inherit fixed-pitch :foreground "Purple" :underline t)))) + '(org-verbatim ((t (:inherit (shadow fixed-pitch)))))) + +(custom-theme-set-variables + 'user + ;; Sometimes set to nil in .dir-locals.el, e.g. in ~/doc/newpapers. + '(org-adapt-indentation t nil) + + '(org-agenda-entry-text-maxlines 3) + '(org-agenda-files "~/doc/org-agenda-files") + '(org-agenda-persistent-filter t) + '(org-agenda-remove-times-when-in-prefix 'beg) + + ;; Interacts badly with `tab-bar-history-mode'. + '(org-agenda-restore-windows-after-quit nil) + + '(org-agenda-skip-deadline-if-done t) + '(org-agenda-skip-deadline-prewarning-if-scheduled 3) + '(org-agenda-skip-scheduled-if-deadline-is-shown 'not-today) + '(org-agenda-skip-scheduled-if-done t) + '(org-agenda-skip-timestamp-if-done t) + '(org-agenda-start-on-weekday nil) + '(org-agenda-sticky t) + '(org-agenda-timegrid-use-ampm t) + '(org-agenda-todo-ignore-scheduled 'future) + '(org-agenda-window-setup 'current-window) + '(org-archive-location "~/doc/archive/howm/archive.org::* From %s") + '(org-archive-save-context-info '(time file olpath)) + '(org-archive-subtree-save-file-p t) + '(org-blank-before-new-entry '((heading . t) (plain-list-item . auto))) + + ;; Turn off to avoid git merge conflicts. + '(org-bookmark-names-plist nil) + + '(org-cycle-separator-lines 0) + '(org-deadline-warning-days 60) + '(org-default-notes-file "~/doc/howm/refile.org") + '(org-directory "~/doc/howm/") + '(org-enforce-todo-checkbox-dependencies t) + '(org-enforce-todo-dependencies t) + '(org-fold-catch-invisible-edits 'show) + '(org-fold-show-context-detail + '((agenda . local) (bookmark-jump . lineage) (isearch . lineage) + (default . ancestors-full))) + '(org-footnote-section "Notes") + '(org-imenu-depth 4) + + ;; So I can start lines with \"P. 211 - \" to refer to p. 211, rather than + ;; starting a list. + '(org-list-allow-alphabetical nil) + + '(org-list-demote-modify-bullet + '(("-" . "+") ("+" . "*") ("*" . "-") ("1." . "-") ("1)" . "-"))) + '(org-list-use-circular-motion t) + '(org-log-done 'time) + '(org-log-into-drawer t) + + ;; Cluttering, and information probably in git. + '(org-log-repeat nil) + + '(org-log-states-order-reversed nil) + + ;; Desirable with `icomplete-mode'. + '(org-outline-path-complete-in-steps nil) + + '(org-read-date-prefer-future 'time) + '(org-refile-allow-creating-parent-nodes 'confirm) + '(org-refile-targets '((org-agenda-files :maxlevel . 5) (nil :maxlevel . 5))) + '(org-refile-use-outline-path 'file) + '(org-special-ctrl-a/e t) + '(org-special-ctrl-k t) + '(org-startup-folded nil) + + ;; Ensures buffer text doesn't go beyond 80 columns. + '(org-startup-indented nil) + + '(org-tags-match-list-sublevels 'indented) + '(org-todo-keyword-faces + '(("SOMEDAY" :foreground "#0000FF" :weight bold) + ("NEXT" :foreground "#DD0000" :weight bold))) + '(org-todo-keywords + '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") + (sequence "WAITING(w@)" "SOMEDAY(s)" "|" "CANCELLED(c)"))) + '(org-treat-S-cursor-todo-selection-as-state-change nil) + '(org-treat-insert-todo-heading-as-state-change t) + '(org-use-fast-todo-selection 'expert) + '(org-yank-folded-subtrees nil)) + (global-set-key "\C-coc" #'org-capture) (global-set-key "\C-col" #'org-store-link) (global-set-key "\C-coa" #'org-agenda) @@ -4840,17 +4596,17 @@ before uploading to NEW again." \n \n ("m" ((in-mode . "gnus-summary-mode")))) org-capture-templates '(("t" "Task to be refiled" entry (file org-default-notes-file) - "* TODO %^{Title}\n%?") + "* TODO %^{Title}\n%?" + :empty-lines 1) ("T" "Task to be refiled" entry (file org-default-notes-file) - "* TODO %^{Title}\n%?") + "* TODO %^{Title}\n%?" + :empty-lines 1) ("n" "Information to be refiled" entry (file org-default-notes-file) - "* %^{Title}\n%?") + "* %^{Title}\n%?" + :empty-lines 1) ("m" "Task from mail to be refiled" entry (file org-default-notes-file) - ;; Lisp is to filter square brackets out of the subject as these mean that - ;; the Org-mode link does not properly form. In Org 9.3, the escaping - ;; syntax for links has changed, so might be able to do something smarter - ;; than this - "* TODO [[gnus:%:group#%:message-id][%^{Title|\"%(replace-regexp-in-string \"\\\\\\[\\\\\\|\\\\\\]\" \"\" \"%:subject\")\" from %:fromname}]]\n%?") + "* TODO [[gnus:%:group#%:message-id][%^{Title|%:fromname ⁘ %:subject}]]\n%?" + :empty-lines 1) ;; ("a" "Appointment" entry (file+datetree "~/doc/howm/diary.org") ;; "* %^{Time} %^{Title & location} @@ -4860,11 +4616,13 @@ before uploading to NEW again." \n \n ;; %^t" :immediate-finish t) ("s" "Task for the future to be refiled" entry (file org-default-notes-file) - "* SOMEDAY %^{Title}\n%?") - ("d" "Diary entry" entry (file+datetree "~/.labbook.gpg") - "* %^{Title}\n%U\n\n%?") + "* SOMEDAY %^{Title}\n%?" + :empty-lines 1) + ("d" "Diary entry" entry (file+olp+datetree "~/.labbook.gpg") "* %U\n\n%?" + :jump-to-captured t :empty-lines 1 :tree-type month) ("u" "URI on clipboard" entry (file org-default-notes-file) - "* SOMEDAY [[%^{URI|%x}][%^{Title}]]" :immediate-finish t))) + "* SOMEDAY [[%^{URI|%x}][%^{Title}]]" + :immediate-finish t :empty-lines 1))) ;; `org-forward-paragraph', `org-backward-paragraph' and `org-mark-element' do ;; not leave point where someone who uses `forward-paragraph', @@ -4987,8 +4745,7 @@ Called by doccheckin script." ;; setting this means if we type C-c C-e o O then the PDF opens for inspection (setq org-odt-preferred-output-format "pdf") -(with-eval-after-load 'org - (add-to-list 'org-file-apps '(system . "xdg-open %s"))) +(spw/feature-add-to-list org-file-apps org '(system . "xdg-open %s")) ;; ... but also ensure we get a .docx (would be better to make ;; `org-odt-preferred-output-format' accept a list) @@ -4998,6 +4755,29 @@ Called by doccheckin script." (org-odt-convert org-input "docx"))) (advice-add 'org-odt-export-to-odt :after #'spw/org-odt-export-docx) +(custom-theme-set-variables + 'user + '(org-d20-dice-sound "~/annex/media/sounds/147531__ziembee__diceland.wav") + '(org-d20-display-rolls-buffer t) + ;; The Roll20 tokens I'm using for NPCs are lettered, and they come in only + ;; two colours, so one monster per letter. + '(org-d20-letter-monsters t) + '(org-d20-continue-monster-numbering t)) +(spw/feature-define-keys org-d20 + [f5] org-d20-initiative-dwim + [f6] org-d20-damage + [f7] (lambda (arg) + (interactive "P") + (call-interactively (if arg + #'org-d20-roll-last + #'org-d20-roll))) + [f8] org-d20-roll-at-point + [f9] (lambda (arg) + (interactive "P") + (call-interactively (if arg + #'org-d20-d% + #'org-d20-d20)))) + ;;;; Org-mode agenda @@ -5328,17 +5108,34 @@ different occasions." ;;;; Diary +(custom-theme-set-variables + 'user + '(appt-display-diary nil) + '(appt-display-interval 6) + '(calendar-date-display-form + '((format "%s-%.2d-%.2d %.3s" year (string-to-number month) + (string-to-number day) dayname))) + '(calendar-date-style 'iso) + '(calendar-week-start-day 1) + '(diary-file "~/doc/diary") + '(diary-list-entries-hook + '(diary-include-other-diary-files diary-sort-entries)) + '(diary-mark-entries-hook '(diary-mark-included-diary-files)) + '(holiday-bahai-holidays nil) + '(holiday-hebrew-holidays nil) + '(holiday-islamic-holidays nil)) + ;; Don't bind `diary' globally as for viewing purposes we use Org agenda ;; bindings, and for editing purposes just C-x b suffices. (global-set-key "\C-cC" #'calendar) -(when (file-readable-p "~/doc/emacs-diary") +(when (file-readable-p "~/doc/diary") (require 'org-agenda) ; for `org-class' (unless (bound-and-true-p appt-timer) ; avoid msgs when `eval-buffer' init.el (appt-activate 1)) (add-to-list 'auto-mode-alist - `(,(format "\\`%s\\'" (expand-file-name "~/doc/emacs-diary")) + `(,(format "\\`%s\\'" (expand-file-name "~/doc/diary")) . diary-mode))) (defun spw/diary-archive-entry (year) @@ -5349,7 +5146,7 @@ different occasions." (let ((start (point))) (forward-line 1) (while (looking-at "[[:blank:]]+") (forward-line 1)) - (append-to-file start (point) (format "~/doc/archive/emacs-diary-%d" year)) + (append-to-file start (point) (format "~/doc/archive/diary-%d" year)) (delete-region start (point)) (when (and (bolp) (eolp)) (delete-blank-lines)))) (with-eval-after-load 'diary-lib @@ -5422,6 +5219,13 @@ We don't use the FILES parameter in the entry for \"d\" in ;;;; Howm +(custom-theme-set-variables + 'user + '(howm-directory "~/doc/howm/") + '(howm-file-name-format "%Y/%Y-%m-%d-%H%M.org") + '(howm-keyword-file "~/doc/howm/.howm-keys") + '(howm-view-use-grep t)) + (setq howm-prefix "\C-cc" ;; Use Org-mode style because our notes are in `org-mode'. @@ -5490,59 +5294,52 @@ We don't use the FILES parameter in the entry for \"d\" in (when (file-directory-p "~/doc/howm/") (require 'howm nil t)) -;;;; C and friends - -;; following setting also part of Linux kernel style, but it's from -;; newcomment.el, not cc-mode, so must be set in addition to -;; `c-default-style' -- and it's my preference in general -(setq comment-style 'extra-line) +;;;; CC mode -(with-eval-after-load 'cc-mode - ;; Use the mode-specific paren binding. Default M-( binding will insert - ;; spaces before the paren which is not called for by all C styles - (define-key c-mode-base-map "\M-(" #'c-electric-paren) - - ;; I've seen this interact badly with electric-indent-mode (which is now on - ;; globally by default, and has been on locally in c-mode for longer I - ;; believe) outside of comments, but I cannot currently reproduce the - ;; problem. Can always just use C-M-j and M-q within comments - (define-key c-mode-base-map (kbd "RET") #'c-context-line-break) +(custom-theme-set-variables + 'user + '(c-default-style "linux") + '(comment-style 'extra-line)) - ;; would be nice to have a global version of this - (define-key c-mode-base-map "\C-o" #'c-context-open-line)) +(spw/feature-define-keys ((cc-mode c-mode-base-map)) + "
" c-context-line-break + "\C-o" c-context-open-line) -;; M-; is adequate for GNU-style comments. This is for other styles. +;; M-; is enough for GNU-style comments. This is for other styles. (spw/define-skeleton spw/cc-com (c-mode :abbrev "comm" :file 'cc-mode) "" nil "/*" \n " * " '(c-indent-line-or-region) - \n "*/" '(c-indent-line-or-region)) +(setq ggtags-mode-line-project-name nil) +(spw/when-library-available ggtags + (spw/feature-add-hook ggtags-mode (cc-mode c-mode-hook) cperl-mode)) + ;;;; Perl -(defun spw/perl-add-use (module) - (interactive "suse ") - (let ((line (concat "use " module - (and (not (string-match ";$" module)) ";")))) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "^use " nil t)) - (forward-line 1) - (open-line 1) - (insert line) - (message (concat "Inserted: " line))))) +(custom-theme-set-variables + 'user + ;; See `cperl-indent-parens-as-block'. + '(cperl-close-paren-offset -4) + + '(cperl-indent-level 4) + + ;; Makes it easier to use longer names for subroutines. + '(cperl-indent-parens-as-block t) + + '(cperl-lineup-step 1)) (defun spw/perltidy-region (begin end) (interactive "r") - (let ((perltidy-env (getenv "PERLTIDY"))) - (setenv "PERLTIDY" - (or (concat (expand-file-name - (locate-dominating-file - (buffer-file-name) - ".perltidyrc")) ".perltidyrc") - perltidy-env)) - (shell-command-on-region begin end "perltidy -q" nil t) - (font-lock-ensure) - (setenv "PERLTIDY" perltidy-env))) + (let* ((dominating (locate-dominating-file default-directory ".perltidyrc")) + (process-environment + (if dominating + (cons (format "PERLTIDY=%s" + (expand-file-name ".perltidyrc" dominating)) + process-environment) + process-environment))) + (shell-command-on-region begin end "perltidy -q" nil t)) + (font-lock-ensure)) ;; an older version of this would use the region if it's active, but that ;; rarely produces good results -- perltidy would get the indentation wrong @@ -5575,11 +5372,9 @@ We don't use the FILES parameter in the entry for \"d\" in (forward-sexp) (forward-line) (spw/perltidy-region begin (point)))))))) +(spw/feature-define-keys cperl-mode "\C-z\C-c" spw/perltidy-block-or-buffer) -(spw/feature-define-keys cperl-mode - "\C-ciu" spw/perl-add-use "\C-z\C-c" spw/perltidy-block-or-buffer) - -;; TODO Take "head" as input too so that we can do =method and =func too. +;; Might adapt this so that it can do =func and =method too. (spw/define-skeleton spw/cperl-headsub (cperl-mode :abbrev "headsub") "" "Name and arguments: " @@ -5590,14 +5385,6 @@ We don't use the FILES parameter in the entry for \"d\" in > _ \n "}") -(spw/define-skeleton spw/cperl-trytiny (cperl-mode :abbrev "try") - "" - nil - "#<<<" \n - "try {" \n _ ?\n - "} catch {" '(cperl-indent-line) \n _ \n "};" '(cperl-indent-line) - \n "#>>>") - ;; This is for turning one-liners into small scripts. (spw/define-skeleton spw/cperl-shebang (cperl-mode :abbrev "shebang") "" (read-string "Command line options: " "-w") ; e.g. -wln |