diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-30 15:42:56 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-30 15:42:56 -0400 |
| commit | 3cb2790a391e708e9db63dbcd469cfa10afce98e (patch) | |
| tree | 674f0a57494b482be6c754b60981985835267b75 | |
| parent | 582ed54ed9a3c600e7e1e68beeea8b16ce1ed613 (diff) | |
| download | dotemacs-3cb2790a391e708e9db63dbcd469cfa10afce98e.tar.gz dotemacs-3cb2790a391e708e9db63dbcd469cfa10afce98e.zip | |
fix(undead-buffers): drop cj/save-some-buffers name collision
Emacs crashed at launch with wrong-number-of-arguments on cj/save-some-buffers, down the startup path (dashboard-only to kill-all-other-buffers to save). Two modules defined a function by that name: custom-buffer-file.el's legible save prompt (arg + pred), installed as an override on save-some-buffers, and undead-buffers.el's older one-arg wrapper that called save-some-buffers internally. custom-buffer-file loads first, undead-buffers second. The one-arg version won the shared symbol, so the override re-entered it with two args.
I removed undead-buffers.el's wrapper. cj/kill-all-other-buffers-and-windows now calls the standard save-some-buffers with the undead predicate, which routes through the override when loaded and the built-in otherwise, so undead-buffers no longer depends on custom-buffer-file. The legible override keeps the cj/save-some-buffers name.
A regression test loads both modules in launch order and guards the call and the arity so a one-arg shadow can't return.
| -rw-r--r-- | modules/undead-buffers.el | 8 | ||||
| -rw-r--r-- | tests/test-undead-buffers--save-some-buffers-override-no-conflict.el | 34 | ||||
| -rw-r--r-- | tests/test-undead-buffers-kill-all-other-buffers-and-windows.el | 17 |
3 files changed, 45 insertions, 14 deletions
diff --git a/modules/undead-buffers.el b/modules/undead-buffers.el index 4780ef227..cbd2c0d7e 100644 --- a/modules/undead-buffers.el +++ b/modules/undead-buffers.el @@ -87,12 +87,6 @@ Undead-buffers are buffers in `cj/undead-buffer-list'." (buffer-file-name buf) (buffer-modified-p buf)))) -(defun cj/save-some-buffers (&optional arg) - "Save some buffers, omitting those in `cj/undead-buffer-list'. -ARG is passed to `save-some-buffers'." - (interactive "P") - (save-some-buffers arg #'cj/undead-buffer-p)) - (defun cj/kill-buffer-and-window () "Delete window and kill or bury its buffer." (interactive) @@ -129,7 +123,7 @@ split is preserved. Buffers in `cj/undead-buffer-list' are buried." (defun cj/kill-all-other-buffers-and-windows () "Kill or bury all other buffers, then delete other windows." (interactive) - (cj/save-some-buffers) + (save-some-buffers nil #'cj/undead-buffer-p) (delete-other-windows) (mapc #'cj/kill-buffer-or-bury-alive (delq (current-buffer) (buffer-list)))) diff --git a/tests/test-undead-buffers--save-some-buffers-override-no-conflict.el b/tests/test-undead-buffers--save-some-buffers-override-no-conflict.el new file mode 100644 index 000000000..c0dca6e0a --- /dev/null +++ b/tests/test-undead-buffers--save-some-buffers-override-no-conflict.el @@ -0,0 +1,34 @@ +;;; test-undead-buffers--save-some-buffers-override-no-conflict.el --- Regression: save override vs undead -*- lexical-binding: t; -*- + +;;; Commentary: +;; Regression guard for a launch crash. custom-buffer-file.el installs a +;; legible `save-some-buffers' override named `cj/save-some-buffers' (arity +;; arg + pred). undead-buffers.el used to define its own 1-arg +;; `cj/save-some-buffers' that called `save-some-buffers' internally. In prod +;; load order (custom-buffer-file before undead-buffers) the 1-arg version won +;; the symbol, so the override re-entered it with two args and signalled +;; wrong-number-of-arguments — crashing the kill-all-other-buffers startup path. +;; +;; The requires below reproduce that prod order on purpose. + +;;; Code: + +(require 'ert) +;; Prod order: override installed first, undead-buffers loaded second. +(require 'custom-buffer-file) +(require 'undead-buffers) + +(ert-deftest test-undead-buffers-save-override-accepts-predicate () + "Normal: calling `save-some-buffers' with the undead predicate (the +kill-all-other-buffers path) goes through the legible override without a +wrong-number-of-arguments crash. No modified file buffers exist in batch, so +the override returns a count rather than prompting." + (should (numberp (save-some-buffers nil #'cj/undead-buffer-p)))) + +(ert-deftest test-undead-buffers-save-some-buffers-not-arity-shadowed () + "Boundary: `cj/save-some-buffers' must accept the PRED argument the override +forwards (max arity >= 2), so undead-buffers can't reintroduce a 1-arg shadow." + (should (>= (cdr (func-arity #'cj/save-some-buffers)) 2))) + +(provide 'test-undead-buffers--save-some-buffers-override-no-conflict) +;;; test-undead-buffers--save-some-buffers-override-no-conflict.el ends here diff --git a/tests/test-undead-buffers-kill-all-other-buffers-and-windows.el b/tests/test-undead-buffers-kill-all-other-buffers-and-windows.el index dcd08e966..36d82add0 100644 --- a/tests/test-undead-buffers-kill-all-other-buffers-and-windows.el +++ b/tests/test-undead-buffers-kill-all-other-buffers-and-windows.el @@ -133,23 +133,26 @@ (test-kill-all-other-buffers-and-windows-teardown))) (ert-deftest test-kill-all-other-buffers-and-windows-should-prompt-for-modified-buffers () - "Should call cj/save-some-buffers to handle modified buffers." + "Should call save-some-buffers with the undead predicate to handle modified buffers." (test-kill-all-other-buffers-and-windows-setup) (unwind-protect (let ((main (current-buffer)) (file (cj/create-temp-test-file-with-content "original")) - save-called) - ;; Mock cj/save-some-buffers to track if it's called - (cl-letf (((symbol-function 'cj/save-some-buffers) - (lambda (&optional arg) - (setq save-called t)))) + save-called save-pred) + ;; Mock save-some-buffers (the standard API the override hooks) to track + ;; the call and the predicate passed. + (cl-letf (((symbol-function 'save-some-buffers) + (lambda (&optional _arg pred &rest _) + (setq save-called t + save-pred pred)))) (let ((buf (find-file-noselect file))) (unwind-protect (progn (with-current-buffer buf (insert "modified")) (cj/kill-all-other-buffers-and-windows) - (should save-called)) + (should save-called) + (should (eq save-pred #'cj/undead-buffer-p))) (when (buffer-live-p buf) (set-buffer-modified-p nil) (kill-buffer buf)))))) |
