summaryrefslogtreecommitdiff
path: root/modules/undead-buffers.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-08-14 19:24:49 -0500
committerCraig Jennings <c@cjennings.net>2025-08-14 19:24:49 -0500
commit9278ddd4ea1a8b1a4c1edaa8894516e3f48d245b (patch)
tree1105519cd55a4ebbb1e91609e6aae7cc3929ddaf /modules/undead-buffers.el
parenta878e5ae99f750ecbbb723f98ef91d3404189a32 (diff)
downloaddotemacs-9278ddd4ea1a8b1a4c1edaa8894516e3f48d245b.tar.gz
dotemacs-9278ddd4ea1a8b1a4c1edaa8894516e3f48d245b.zip
refactor(system-utils): major refactoring / adding tests
Theme: Modularize system-utilities into separate modules. Clean up any typos, buts, and unused variables. Add some initial ERT tests for new modules created. Changes: - Extract file handling into its own module (file-config) - Extract keyboard macro management into its own module (keyboard-macros) - Extract buffer burying (instead of killing) into its own module (undead-buffers) - Extract all date/time config into its own module (chrono-tools) - Moved keybinding discovery functionality and help into keybindings module - Combine flyspell and abbrev (spell-check and autocorrect) to flyspell-and-abbrev.el - Rename epa-config.el to auth-config.el for auth-source and epa settings. - Refactor `cj/kill-other-window` for more accurate buffer handling. - Include "*ert*" in the default bury (don't kill) list as killing it kills test runs. - Bind C-c M-m to inhibit-mouse-mode - Remove the unused ledger-file variable in user-constants.el. - Removed obsolete C-x x m, C-x x r, and C-x x d key mappings. - C-; b r to call cj/rename-buffer-and-file instead of typo’d function - Other purely cosmetic comment changes to system-utils.el ERT tests: - Rename ERT test definitions to include module scopes (file-config, keyboard-macros) - Add an ERT test for the timer bell's existence. - Add ERT tests to cover `cj/kill-buffer-or-bury-alive`, prefix-arg behavior, window-killing commands, and bulk operations. - Add test `authinfo-file` exists Missing authinfo triggers a debug message - Add test that `gpg2` executable is on the user’s PATH - Remove outdated authinfo test. - Add “Run these tests” note where missing.
Diffstat (limited to 'modules/undead-buffers.el')
-rw-r--r--modules/undead-buffers.el167
1 files changed, 167 insertions, 0 deletions
diff --git a/modules/undead-buffers.el b/modules/undead-buffers.el
new file mode 100644
index 00000000..fbfc2782
--- /dev/null
+++ b/modules/undead-buffers.el
@@ -0,0 +1,167 @@
+;;; undead-buffers.el --- Bury Rather Than Kill These Buffers -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;;
+;; This library allows for “burying” selected buffers instead of killing them.
+;; Since they won't be killed, I'm calling them "undead buffers".
+;;
+;; The main function cj/kill-buffer-or-bury-alive replaces kill-buffer.
+;;
+;; Additional helper commands and key bindings:
+;; - M-C (=cj/kill-buffer-and-window=): delete this window and bury/kill its buffer.
+;; - M-O (=cj/kill-other-window=): delete the next window and bury/kill its buffer.
+;; - M-M (=cj/kill-all-other-buffers-and-windows=): kill or bury all buffers except
+;; the current one and delete all other windows.
+;;
+;; Add to the list of "undead buffers" by adding to the cj/buffer-bury-alive-list
+;; variable.
+;;
+;;; Code:
+
+(defvar cj/buffer-bury-alive-list
+ '("*dashboard*" "*scratch*" "*Messages*" "*ert*" "*AI-Assistant*")
+ "Buffers to bury instead of killing.")
+
+(defun cj/kill-buffer-or-bury-alive (buffer)
+ "Kill BUFFER or bury it if it's in `cj/buffer-bury-alive-list'."
+ (interactive "bBuffer to kill or bury: ")
+ (with-current-buffer buffer
+ (if current-prefix-arg
+ (progn
+ (add-to-list 'cj/buffer-bury-alive-list (buffer-name))
+ (message "Added %s to bury-alive-list" (buffer-name)))
+ (if (member (buffer-name) cj/buffer-bury-alive-list)
+ (bury-buffer)
+ (kill-buffer)))))
+(global-set-key [remap kill-buffer] #'cj/kill-buffer-or-bury-alive)
+
+(defun cj/kill-buffer-and-window ()
+ "Delete window and kill or bury its buffer."
+ (interactive)
+ (let ((buf (current-buffer)))
+ (delete-window)
+ (cj/kill-buffer-or-bury-alive buf)))
+(global-set-key (kbd "M-C") #'cj/kill-buffer-and-window)
+
+(defun cj/kill-other-window ()
+ "Delete the next window and kill or bury its buffer."
+ (interactive)
+ (other-window 1)
+ (let ((buf (current-buffer)))
+ (unless (one-window-p)
+ (delete-window))
+ (cj/kill-buffer-or-bury-alive buf)))
+(global-set-key (kbd "M-O") #'cj/kill-other-window)
+
+(defun cj/kill-all-other-buffers-and-windows ()
+ "Kill or bury all other buffers, then delete other windows."
+ (interactive)
+ (save-some-buffers)
+ (delete-other-windows)
+ (mapc #'cj/kill-buffer-or-bury-alive
+ (delq (current-buffer) (buffer-list))))
+(global-set-key (kbd "M-M") #'cj/kill-all-other-buffers-and-windows)
+
+(provide 'undead-buffers)
+;;; undead-buffers.el ends here.
+
+;; --------------------------------- ERT Tests ---------------------------------
+;; Run these tests with M-x ert RET t RET
+
+(require 'ert)
+(require 'cl-lib)
+
+(ert-deftest undead-buffers/kill-or-bury-when-not-in-list-kills ()
+ "cj/kill-buffer-or-bury-alive should kill a buffer not in `cj/buffer-bury-alive-list'."
+ (let* ((buf (generate-new-buffer "test-not-in-list"))
+ (orig (copy-sequence cj/buffer-bury-alive-list)))
+ (unwind-protect
+ (progn
+ (should (buffer-live-p buf))
+ (cj/kill-buffer-or-bury-alive (buffer-name buf))
+ (should-not (buffer-live-p buf)))
+ (setq cj/buffer-bury-alive-list orig)
+ (when (buffer-live-p buf) (kill-buffer buf)))))
+
+(ert-deftest undead-buffers/kill-or-bury-when-in-list-buries ()
+ "cj/kill-buffer-or-bury-alive should bury (not kill) a buffer in the list."
+ (let* ((name "*dashboard*") ; an element already in the default list
+ (buf (generate-new-buffer name))
+ (orig (copy-sequence cj/buffer-bury-alive-list))
+ win-was)
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/buffer-bury-alive-list name)
+ ;; show it in a temporary window so we can detect bury
+ (setq win-was (display-buffer buf))
+ (cj/kill-buffer-or-bury-alive name)
+ ;; bury should leave it alive
+ (should (buffer-live-p buf))
+ ;; note: Emacs’s `bury-buffer` does not delete windows by default,
+ ;; so we no longer assert that no window shows it.
+ )
+ ;; cleanup
+ (setq cj/buffer-bury-alive-list orig)
+ (delete-windows-on buf)
+ (kill-buffer buf))))
+
+(ert-deftest undead-buffers/kill-or-bury-adds-to-list-with-prefix ()
+ "Calling `cj/kill-buffer-or-bury-alive' with a prefix arg should add the buffer to the list."
+ (let* ((buf (generate-new-buffer "test-add-prefix"))
+ (orig (copy-sequence cj/buffer-bury-alive-list)))
+ (unwind-protect
+ (progn
+ (let ((current-prefix-arg '(4)))
+ (cj/kill-buffer-or-bury-alive (buffer-name buf)))
+ (should (member (buffer-name buf) cj/buffer-bury-alive-list)))
+ (setq cj/buffer-bury-alive-list orig)
+ (kill-buffer buf))))
+
+(ert-deftest undead-buffers/kill-buffer-and-window-removes-window ()
+ "cj/kill-buffer-and-window should delete the current window and kill/bury its buffer."
+ (let* ((buf (generate-new-buffer "test-kill-and-win"))
+ (orig (copy-sequence cj/buffer-bury-alive-list)))
+ (split-window) ; now two windows
+ (let ((win (next-window)))
+ (set-window-buffer win buf)
+ (select-window win)
+ (cj/kill-buffer-and-window)
+ (should-not (window-live-p win))
+ (unless (member (buffer-name buf) orig)
+ (should-not (buffer-live-p buf))))
+ (setq cj/buffer-bury-alive-list orig)))
+
+(ert-deftest undead-buffers/kill-other-window-deletes-that-window ()
+ "cj/kill-other-window should delete the *other* window and kill/bury its buffer."
+ (let* ((buf1 (current-buffer))
+ (buf2 (generate-new-buffer "test-other-window"))
+ (orig (copy-sequence cj/buffer-bury-alive-list)))
+ (split-window)
+ (let* ((win1 (selected-window))
+ (win2 (next-window win1)))
+ (set-window-buffer win2 buf2)
+ ;; stay on the original window
+ (select-window win1)
+ (cj/kill-other-window)
+ (should-not (window-live-p win2))
+ (unless (member (buffer-name buf2) orig)
+ (should-not (buffer-live-p buf2))))
+ (setq cj/buffer-bury-alive-list orig)))
+
+(ert-deftest undead-buffers/kill-all-other-buffers-and-windows-keeps-only-current ()
+ "cj/kill-all-other-buffers-and-windows should delete other windows and kill/bury all other buffers."
+ (let* ((main (current-buffer))
+ (extra (generate-new-buffer "test-all-others"))
+ (orig (copy-sequence cj/buffer-bury-alive-list)))
+ (split-window)
+ (set-window-buffer (next-window) extra)
+ (cj/kill-all-other-buffers-and-windows)
+ (should (one-window-p))
+ ;; main buffer still exists
+ (should (buffer-live-p main))
+ ;; extra buffer either buried or killed
+ (unless (member (buffer-name extra) orig)
+ (should-not (buffer-live-p extra)))
+ ;; cleanup
+ (setq cj/buffer-bury-alive-list orig)
+ (when (buffer-live-p extra) (kill-buffer extra))))