aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-11 15:35:43 -0500
committerCraig Jennings <c@cjennings.net>2026-05-11 15:35:43 -0500
commit0ddbcde1e9f17021377f4160b39cd0790afcbdcc (patch)
treed1872d799dc7554b128989411a23b9cad65a6671
parentf837e5f7464932fc49c10a7442dc1a23af61b257 (diff)
downloaddotemacs-0ddbcde1e9f17021377f4160b39cd0790afcbdcc.tar.gz
dotemacs-0ddbcde1e9f17021377f4160b39cd0790afcbdcc.zip
feat(window): kill the other window's buffer with C-; b K
`cj/kill-other-window-buffer' (in undead-buffers.el, on `C-; b K') kills or buries the buffer shown in the other window and leaves that window and the split alone. The window just shows whatever bury/kill surfaces next. It reuses `cj/kill-buffer-or-bury-alive', so buffers in `cj/undead-buffer-list' (like `*scratch*') get buried. With more than two windows it acts on `next-window'. Sibling of `cj/kill-other-window' (M-S-o), which deletes the other window. This one keeps it.
-rw-r--r--modules/custom-buffer-file.el7
-rw-r--r--modules/undead-buffers.el16
-rw-r--r--tests/test-undead-buffers--kill-other-window-buffer.el74
3 files changed, 96 insertions, 1 deletions
diff --git a/modules/custom-buffer-file.el b/modules/custom-buffer-file.el
index f4401d1f..ef4fc945 100644
--- a/modules/custom-buffer-file.el
+++ b/modules/custom-buffer-file.el
@@ -17,6 +17,8 @@
;;
;; Keybindings under ~C-; b~:
;; - ~C-; b k~ kill buffer and window (delete window, kill/bury buffer)
+;; - ~C-; b K~ kill the other window's buffer, keeping that window/split
+;; (cj/kill-other-window-buffer in undead-buffers.el)
;; - ~C-; b <arrow>~ move the active window's divider that way (via windsize);
;; bare arrows keep nudging until any other key (cj/window-resize-sticky in
;; ui-navigation.el)
@@ -37,8 +39,9 @@
(require 'mm-decode)
(require 'external-open) ;; for cj/xdg-open, cj/open-this-file-with
-;; cj/kill-buffer-and-window defined in undead-buffers.el
+;; cj/kill-buffer-and-window and cj/kill-other-window-buffer defined in undead-buffers.el
(declare-function cj/kill-buffer-and-window "undead-buffers")
+(declare-function cj/kill-other-window-buffer "undead-buffers")
;; cj/window-resize-sticky (C-; b <arrow>) defined in ui-navigation.el
(declare-function cj/window-resize-sticky "ui-navigation")
@@ -449,6 +452,7 @@ Signals an error if:
"n" #'cj/copy-buffer-name
"l" #'cj/copy-link-to-buffer-file
"k" #'cj/kill-buffer-and-window
+ "K" #'cj/kill-other-window-buffer
"P" #'cj/print-buffer-ps
"t" #'cj/clear-to-top-of-buffer
"b" #'cj/clear-to-bottom-of-buffer
@@ -486,6 +490,7 @@ Signals an error if:
"C-; b n" "copy buffer name"
"C-; b l" "copy file link"
"C-; b k" "kill buffer and window"
+ "C-; b K" "kill other window's buffer"
"C-; b P" "print to PS"
"C-; b t" "clear to top"
"C-; b b" "clear to bottom"
diff --git a/modules/undead-buffers.el b/modules/undead-buffers.el
index f79afb4b..c85bde94 100644
--- a/modules/undead-buffers.el
+++ b/modules/undead-buffers.el
@@ -8,6 +8,8 @@
;;
;; Additional helper commands and key bindings:
;; - C-; b k (=cj/kill-buffer-and-window=): delete this window and bury/kill its buffer.
+;; - C-; b K (=cj/kill-other-window-buffer=): bury/kill the other window's buffer,
+;; keeping that window and the split intact.
;; - 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.
@@ -77,6 +79,20 @@ ARG is passed to `save-some-buffers'."
(cj/kill-buffer-or-bury-alive buf)))
(keymap-global-set "M-S-o" #'cj/kill-other-window)
+(defun cj/kill-other-window-buffer ()
+ "Kill or bury the buffer shown in the other window, keeping that window
+and the split intact -- the window then shows whatever bury/kill surfaces
+next. With more than two windows, acts on `next-window'.
+
+Sibling of `cj/kill-other-window', which deletes the other window; here the
+split is preserved. Buffers in `cj/undead-buffer-list' are buried."
+ (interactive)
+ (if (one-window-p)
+ (user-error "No other window")
+ (with-selected-window (next-window)
+ (cj/kill-buffer-or-bury-alive (current-buffer)))))
+;; Keybinding in custom-buffer-file.el (C-; b K)
+
(defun cj/kill-all-other-buffers-and-windows ()
"Kill or bury all other buffers, then delete other windows."
(interactive)
diff --git a/tests/test-undead-buffers--kill-other-window-buffer.el b/tests/test-undead-buffers--kill-other-window-buffer.el
new file mode 100644
index 00000000..4dbbb710
--- /dev/null
+++ b/tests/test-undead-buffers--kill-other-window-buffer.el
@@ -0,0 +1,74 @@
+;;; test-undead-buffers--kill-other-window-buffer.el --- Tests for cj/kill-other-window-buffer -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; `cj/kill-other-window-buffer' kills (or buries, for `cj/undead-buffer-list'
+;; buffers) the buffer shown in the other window, leaving that window and the
+;; split intact -- the window then shows whatever bury/kill surfaces next.
+;; Sibling of `cj/kill-other-window' (which deletes the other window); the
+;; distinguishing trait here is that the split is preserved.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory))
+(require 'undead-buffers)
+
+(ert-deftest test-undead-buffers-kill-other-window-buffer-keeps-the-window ()
+ "Normal: kills the other window's (non-undead) buffer but keeps the window
+and the split -- the current window is left untouched."
+ (let ((other (generate-new-buffer "test-kill-other-window-buffer"))
+ (orig (copy-sequence cj/undead-buffer-list)))
+ (unwind-protect
+ (save-window-excursion
+ (delete-other-windows)
+ (split-window-right)
+ (let* ((win-a (selected-window))
+ (buf-a (window-buffer win-a))
+ (win-b (next-window win-a)))
+ (set-window-buffer win-b other)
+ (select-window win-a)
+ (cj/kill-other-window-buffer)
+ (should (window-live-p win-b))
+ (should-not (buffer-live-p other))
+ (should (eq (window-buffer win-a) buf-a))))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p other) (kill-buffer other)))))
+
+(ert-deftest test-undead-buffers-kill-other-window-buffer-buries-undead ()
+ "Boundary: an undead buffer in the other window is buried, not killed --
+still alive, no longer shown there; the window stays."
+ (let ((other (generate-new-buffer "test-kill-other-undead"))
+ (orig (copy-sequence cj/undead-buffer-list)))
+ (unwind-protect
+ (save-window-excursion
+ (add-to-list 'cj/undead-buffer-list (buffer-name other))
+ (delete-other-windows)
+ (split-window-right)
+ (let* ((win-a (selected-window))
+ (win-b (next-window win-a)))
+ (set-window-buffer win-b other)
+ (select-window win-a)
+ (cj/kill-other-window-buffer)
+ (should (window-live-p win-b))
+ (should (buffer-live-p other))
+ (should-not (eq (window-buffer win-b) other))))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p other) (kill-buffer other)))))
+
+(ert-deftest test-undead-buffers-kill-other-window-buffer-errors-with-one-window ()
+ "Error: with only one window there is no other window to act on."
+ (save-window-excursion
+ (delete-other-windows)
+ (should-error (cj/kill-other-window-buffer) :type 'user-error)))
+
+(ert-deftest test-undead-buffers-kill-other-window-buffer-bound-under-c-semicolon-b ()
+ "Normal: reachable via C-; b K."
+ (require 'custom-buffer-file)
+ (should (eq (keymap-lookup cj/buffer-and-file-map "K")
+ #'cj/kill-other-window-buffer)))
+
+(provide 'test-undead-buffers--kill-other-window-buffer)
+;;; test-undead-buffers--kill-other-window-buffer.el ends here