diff options
| author | Craig Jennings <c@cjennings.net> | 2025-08-14 19:24:49 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-08-14 19:24:49 -0500 |
| commit | 9278ddd4ea1a8b1a4c1edaa8894516e3f48d245b (patch) | |
| tree | 1105519cd55a4ebbb1e91609e6aae7cc3929ddaf /modules/undead-buffers.el | |
| parent | a878e5ae99f750ecbbb723f98ef91d3404189a32 (diff) | |
| download | dotemacs-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.el | 167 |
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)))) |
