summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/config-utilities.el4
-rw-r--r--modules/custom-case.el16
-rw-r--r--modules/custom-comments.el219
-rw-r--r--modules/custom-datetime.el5
-rw-r--r--modules/custom-file-buffer.el10
-rw-r--r--modules/custom-line-paragraph.el142
-rw-r--r--modules/undead-buffers.el28
-rw-r--r--tests/test-undead-buffers-kill-all-other-buffers-and-windows.el159
-rw-r--r--tests/test-undead-buffers-kill-buffer-and-window.el112
-rw-r--r--tests/test-undead-buffers-kill-buffer-or-bury-alive.el138
-rw-r--r--tests/test-undead-buffers-kill-other-window.el123
-rw-r--r--tests/test-undead-buffers-make-buffer-undead.el134
-rw-r--r--tests/test-undead-buffers-undead-buffer-p.el106
13 files changed, 993 insertions, 203 deletions
diff --git a/modules/config-utilities.el b/modules/config-utilities.el
index 60a8d602..d1538256 100644
--- a/modules/config-utilities.el
+++ b/modules/config-utilities.el
@@ -218,7 +218,6 @@ Recompile natively when supported, otherwise fall back to byte compilation."
(with-eval-after-load 'which-key
(which-key-add-key-based-replacements "C-c d i" "info on build/features/packages."))
-
(defvar cj--loaded-file-paths nil
"All file paths that are loaded.")
(defvar cj--loaded-packages-buffer "*loaded-packages*"
@@ -267,7 +266,6 @@ Recompile natively when supported, otherwise fall back to byte compilation."
(keymap-set cj/debug-config-keymap "i f" 'cj/info-loaded-features)
;; ------------------------------ Reload Init File -----------------------------
-;; it does what it says it does.
(defun cj/reload-init-file ()
"Reload the init file. Useful when modifying Emacs config."
@@ -287,7 +285,7 @@ Recompile natively when supported, otherwise fall back to byte compilation."
;; ------------------------ Validate Org Agenda Entries ------------------------
(defun cj/validate-org-agenda-timestamps ()
- "Scan all files in \='org-agenda-files\=' for invalid timestamps.
+ "Scan all files in `org-agenda-files' for invalid timestamps.
Checks DEADLINE, SCHEDULED, TIMESTAMP properties and inline timestamps in
headline contents. Generates an Org-mode report buffer with links to problematic
entries, property/type, and raw timestamp string."
diff --git a/modules/custom-case.el b/modules/custom-case.el
index 75ceb184..4fd9ac05 100644
--- a/modules/custom-case.el
+++ b/modules/custom-case.el
@@ -40,12 +40,11 @@
(defun cj/title-case-region ()
"Capitalize the region in title case format.
-Title case is a capitalization convention where major words
-are capitalized,and most minor words are lowercase. Nouns,
-verbs (including linking verbs), adjectives, adverbs,pronouns,
-and all words of four letters or more are considered major words.
-Short (i.e., three letters or fewer) conjunctions, short prepositions,
-and all articles are considered minor words."
+Title case is a capitalization convention where major words are capitalized,
+and most minor words are lowercase. Nouns, verbs (including linking verbs),
+adjectives, adverbs,pronouns, and all words of four letters or more are
+considered major words. Short (i.e., three letters or fewer) conjunctions,
+short prepositions, and all articles are considered minor words."
(interactive)
(let ((beg nil)
(end nil)
@@ -112,11 +111,14 @@ and all articles are considered minor words."
;; Case-change operations prefix and keymap
(defvar-keymap cj/case-map
- :doc "Keymap for case-change operations."
+ :doc "Keymap for case-change operations"
"t" #'cj/title-case-region
"u" #'cj/upcase-dwim
"l" #'cj/downcase-dwim)
(keymap-set cj/custom-keymap "c" cj/case-map)
+(with-eval-after-load 'which-key
+ (which-key-add-key-based-replacements "C-; c" "case change menu"))
+
(provide 'custom-case)
;;; custom-case.el ends here.
diff --git a/modules/custom-comments.el b/modules/custom-comments.el
index 16f10235..101ba092 100644
--- a/modules/custom-comments.el
+++ b/modules/custom-comments.el
@@ -13,7 +13,6 @@
;; These utilities help create consistent, well-formatted code comments and section headers.
;; Bound to keymap prefix: C-; C
;;
-;;
;;; Code:
(eval-when-compile (defvar cj/custom-keymap)) ;; cj/custom-keymap defined in keybindings.el
@@ -26,7 +25,7 @@
(interactive)
(goto-char (point-min))
(let (kill-ring)
- (comment-kill (count-lines (point-min) (point-max)))))
+ (comment-kill (count-lines (point-min) (point-max)))))
;; ------------------------------ Comment Reformat -----------------------------
@@ -34,14 +33,14 @@
"Reformat commented text into a single paragraph."
(interactive)
(if mark-active
- (let ((beg (region-beginning))
- (end (copy-marker (region-end)))
- (orig-fill-column fill-column))
- (uncomment-region beg end)
- (setq fill-column (- fill-column 3))
- (cj/join-line-or-region beg end)
- (comment-region beg end)
- (setq fill-column orig-fill-column )))
+ (let ((beg (region-beginning))
+ (end (copy-marker (region-end)))
+ (orig-fill-column fill-column))
+ (uncomment-region beg end)
+ (setq fill-column (- fill-column 3))
+ (cj/join-line-or-region beg end)
+ (comment-region beg end)
+ (setq fill-column orig-fill-column )))
;; if no region
(message "No region was selected. Select the comment lines to reformat."))
@@ -54,42 +53,42 @@ Use the lesser of `fill-column' or 80 to calculate the comment length.
Begin and end line with the appropriate comment symbols for the current mode."
(interactive)
(if (not (char-or-string-p comment-char))
- (setq comment-char "#"))
+ (setq comment-char "#"))
(let* ((comment (capitalize (string-trim (read-from-minibuffer "Comment: "))))
- (fill-column (min fill-column 80))
- (comment-length (length comment))
- ;; (comment-start-length (length comment-start))
- ;; (comment-end-length (length comment-end))
- (current-column-pos (current-column))
- (space-on-each-side (/ (- fill-column
- current-column-pos
- comment-length
- (length comment-start)
- (length comment-end)
- ;; Single space on each side of comment
- (if (> comment-length 0) 2 0)
- ;; Single space after comment syntax sting
- 1)
- 2)))
- (if (< space-on-each-side 2)
- (message "Comment string is too big to fit in one line")
- (progn
- (insert comment-start)
- (when (equal comment-start ";") ;; emacs-lisp line comments are ';;'
- (insert comment-start)) ;; so insert comment-char again
- (insert " ")
- (dotimes (_ space-on-each-side) (insert comment-char))
- (when (> comment-length 0) (insert " "))
- (insert comment)
- (when (> comment-length 0) (insert " "))
- (dotimes (_ (if (= (% comment-length 2) 0)
- (- space-on-each-side 1)
- space-on-each-side))
- (insert comment-char))
- ;; Only insert trailing space and comment-end if comment-end is not empty
- (when (not (string-empty-p comment-end))
- (insert " ")
- (insert comment-end))))))
+ (fill-column (min fill-column 80))
+ (comment-length (length comment))
+ ;; (comment-start-length (length comment-start))
+ ;; (comment-end-length (length comment-end))
+ (current-column-pos (current-column))
+ (space-on-each-side (/ (- fill-column
+ current-column-pos
+ comment-length
+ (length comment-start)
+ (length comment-end)
+ ;; Single space on each side of comment
+ (if (> comment-length 0) 2 0)
+ ;; Single space after comment syntax sting
+ 1)
+ 2)))
+ (if (< space-on-each-side 2)
+ (message "Comment string is too big to fit in one line")
+ (progn
+ (insert comment-start)
+ (when (equal comment-start ";") ;; emacs-lisp line comments are ';;'
+ (insert comment-start)) ;; so insert comment-char again
+ (insert " ")
+ (dotimes (_ space-on-each-side) (insert comment-char))
+ (when (> comment-length 0) (insert " "))
+ (insert comment)
+ (when (> comment-length 0) (insert " "))
+ (dotimes (_ (if (= (% comment-length 2) 0)
+ (- space-on-each-side 1)
+ space-on-each-side))
+ (insert comment-char))
+ ;; Only insert trailing space and comment-end if comment-end is not empty
+ (when (not (string-empty-p comment-end))
+ (insert " ")
+ (insert comment-end))))))
;; -------------------------------- Comment Box --------------------------------
@@ -100,81 +99,80 @@ mode's comment syntax at both the beginning and end of each line. The box
respects the current indentation level and avoids trailing whitespace."
(interactive)
(let* ((comment-char (if (equal comment-start ";") ";;"
- (string-trim comment-start)))
- (comment-end-char (if (string-empty-p comment-end)
- comment-char
- (string-trim comment-end)))
- (line-char (if (equal comment-char ";;") "-" "#"))
- (comment (capitalize (string-trim (read-from-minibuffer "Comment: "))))
- (comment-length (length comment))
- (current-column-pos (current-column))
- (max-width (min fill-column 80))
- ;; Calculate available width between comment markers
- (available-width (- max-width
- current-column-pos
- (length comment-char)
- (length comment-end-char)))
- ;; Inner width is the width without the spaces after comment start and before comment end
- (inner-width (- available-width 2))
- ;; Calculate padding for each side of the centered text
- (padding-each-side (max 1 (/ (- inner-width comment-length) 2)))
- ;; Adjust for odd-length comments
- (right-padding (if (= (% (- inner-width comment-length) 2) 0)
- padding-each-side
- (1+ padding-each-side))))
-
- ;; Check if we have enough space
- (if (< inner-width (+ comment-length 4)) ; minimum sensible width
- (message "Comment string is too big to fit in one line")
- (progn
- ;; Top line - fill entirely with line characters except for space after comment start
- (insert comment-char)
- (insert " ")
- (insert (make-string inner-width (string-to-char line-char)))
- (insert " ")
- (insert comment-end-char)
- (newline)
-
- ;; Add indentation on the new line to match current column
- (dotimes (_ current-column-pos) (insert " "))
-
- ;; Middle line with centered text
- (insert comment-char)
- (insert " ")
- ;; Left padding
- (dotimes (_ padding-each-side) (insert " "))
- ;; The comment text
- (insert comment)
- ;; Right padding
- (dotimes (_ right-padding) (insert " "))
- (insert " ")
- (insert comment-end-char)
- (newline)
-
- ;; Add indentation on the new line to match current column
- (dotimes (_ current-column-pos) (insert " "))
-
- ;; Bottom line - same as top line
- (insert comment-char)
- (insert " ")
- (dotimes (_ inner-width) (insert line-char))
- (insert " ")
- (insert comment-end-char)
- (newline)))))
+ (string-trim comment-start)))
+ (comment-end-char (if (string-empty-p comment-end)
+ comment-char
+ (string-trim comment-end)))
+ (line-char (if (equal comment-char ";;") "-" "#"))
+ (comment (capitalize (string-trim (read-from-minibuffer "Comment: "))))
+ (comment-length (length comment))
+ (current-column-pos (current-column))
+ (max-width (min fill-column 80))
+ ;; Calculate available width between comment markers
+ (available-width (- max-width
+ current-column-pos
+ (length comment-char)
+ (length comment-end-char)))
+ ;; Inner width is the width without the spaces after comment start and before comment end
+ (inner-width (- available-width 2))
+ ;; Calculate padding for each side of the centered text
+ (padding-each-side (max 1 (/ (- inner-width comment-length) 2)))
+ ;; Adjust for odd-length comments
+ (right-padding (if (= (% (- inner-width comment-length) 2) 0)
+ padding-each-side
+ (1+ padding-each-side))))
+
+ ;; Check if we have enough space
+ (if (< inner-width (+ comment-length 4)) ; minimum sensible width
+ (message "Comment string is too big to fit in one line")
+ (progn
+ ;; Top line - fill entirely with line characters except for space after comment start
+ (insert comment-char)
+ (insert " ")
+ (insert (make-string inner-width (string-to-char line-char)))
+ (insert " ")
+ (insert comment-end-char)
+ (newline)
+
+ ;; Add indentation on the new line to match current column
+ (dotimes (_ current-column-pos) (insert " "))
+
+ ;; Middle line with centered text
+ (insert comment-char)
+ (insert " ")
+ ;; Left padding
+ (dotimes (_ padding-each-side) (insert " "))
+ ;; The comment text
+ (insert comment)
+ ;; Right padding
+ (dotimes (_ right-padding) (insert " "))
+ (insert " ")
+ (insert comment-end-char)
+ (newline)
+
+ ;; Add indentation on the new line to match current column
+ (dotimes (_ current-column-pos) (insert " "))
+
+ ;; Bottom line - same as top line
+ (insert comment-char)
+ (insert " ")
+ (dotimes (_ inner-width) (insert line-char))
+ (insert " ")
+ (insert comment-end-char)
+ (newline)))))
;; ------------------------------- Comment Hyphen ------------------------------
(defun cj/comment-hyphen()
- "Insert a centered comment with '-' (hyphens) on each side.
+ "Insert a centered comment with `-' (hyphens) on each side.
Leverages cj/comment-centered."
(interactive)
(cj/comment-centered "-"))
;; ------------------------------- Comment Keymap ------------------------------
-;; Comment styles & comment removal keymap.
(defvar-keymap cj/comment-map
- :doc "Keymap for code comment operations."
+ :doc "Keymap for code comment operations"
"r" #'cj/comment-reformat
"c" #'cj/comment-centered
"-" #'cj/comment-hyphen
@@ -182,5 +180,8 @@ Leverages cj/comment-centered."
"D" #'cj/delete-buffer-comments)
(keymap-set cj/custom-keymap "C" cj/comment-map)
+(with-eval-after-load 'which-key
+ (which-key-add-key-based-replacements "C-; C" "code comment menu"))
+
(provide 'custom-comments)
;;; custom-comments.el ends here.
diff --git a/modules/custom-datetime.el b/modules/custom-datetime.el
index dd15daa0..c195ebc2 100644
--- a/modules/custom-datetime.el
+++ b/modules/custom-datetime.el
@@ -106,8 +106,6 @@ Use `readable-date-format' for formatting."
;; ------------------------------ Date Time Keymap -----------------------------
-;; Date/time insertion prefix and keymap
-
(defvar-keymap cj/datetime-map
:doc "Keymap for date/time insertions."
"r" #'cj/insert-readable-date-time
@@ -118,5 +116,8 @@ Use `readable-date-format' for formatting."
"D" #'cj/insert-readable-date )
(keymap-set cj/custom-keymap "d" cj/datetime-map)
+(with-eval-after-load 'which-key
+ (which-key-add-key-based-replacements "C-; d" "date/time insertion menu"))
+
(provide 'custom-datetime)
;;; custom-datetime.el ends here.
diff --git a/modules/custom-file-buffer.el b/modules/custom-file-buffer.el
index e722f734..6ed19d73 100644
--- a/modules/custom-file-buffer.el
+++ b/modules/custom-file-buffer.el
@@ -23,12 +23,11 @@
(declare-function ps-print-region-with-faces "ps-print")
;; ------------------------- Print Buffer As Postscript ------------------------
-;; prints using postscript for much nicer output
(defvar cj/print-spooler-command 'auto
"Command used to send PostScript to the system print spooler.
-Set to a string to force a specific command (e.g., lpr or lp).
-Set to \\='auto to auto-detect once per session.")
+Set to a string to force a specific command (e.g., lpr or lp). Set to `auto' to
+auto-detect once per session.")
(defvar cj/print--spooler-cache nil
"Cached spooler command detected for the current Emacs session.")
@@ -56,7 +55,6 @@ Set to \\='auto to auto-detect once per session.")
(user-error "Invalid value for cj/print-spooler-command: %S"
cj/print-spooler-command))))
-;;;###autoload
(defun cj/print-buffer-ps (&optional color)
"Print the buffer (or active region) as PostScript to the default printer.
With prefix argument COLOR, print in color; otherwise print in monochrome.
@@ -194,5 +192,9 @@ Do not save the deleted text in the kill ring."
"P" #'cj/copy-path-to-buffer-file-as-kill)
(keymap-set cj/custom-keymap "b" cj/buffer-and-file-map)
+(with-eval-after-load 'which-key
+ (which-key-add-key-based-replacements "C-; b" "buffer and file menu"))
+
+
(provide 'custom-file-buffer)
;;; custom-file-buffer.el ends here.
diff --git a/modules/custom-line-paragraph.el b/modules/custom-line-paragraph.el
index 4f8315e7..cb89a6f5 100644
--- a/modules/custom-line-paragraph.el
+++ b/modules/custom-line-paragraph.el
@@ -17,6 +17,8 @@
;;
;;; Code:
+
+
(use-package expand-region
:demand t) ;; used w/in join paragraph
@@ -24,17 +26,17 @@
"Join lines in the active region or join the current line with the previous one."
(interactive)
(if (use-region-p)
- (let ((beg (region-beginning))
- (end (copy-marker (region-end))))
- (goto-char beg)
- (while (< (point) end)
- (join-line 1))
- (goto-char end)
- (newline))
- ;; No region - only join if there's a previous line
- (when (> (line-number-at-pos) 1)
- (join-line))
- (newline)))
+ (let ((beg (region-beginning))
+ (end (copy-marker (region-end))))
+ (goto-char beg)
+ (while (< (point) end)
+ (join-line 1))
+ (goto-char end)
+ (newline))
+ ;; No region - only join if there's a previous line
+ (when (> (line-number-at-pos) 1)
+ (join-line))
+ (newline)))
(defun cj/join-paragraph ()
"Join all lines in the current paragraph using `cj/join-line-or-region'."
@@ -48,17 +50,17 @@
Comment the duplicated text when optional COMMENT is non-nil."
(interactive "P")
(let* ((b (if (region-active-p) (region-beginning) (line-beginning-position)))
- (e (if (region-active-p) (region-end) (line-end-position)))
- (lines (split-string (buffer-substring-no-properties b e) "\n")))
- (save-excursion
- (goto-char e)
- (dolist (line lines)
- (open-line 1)
- (forward-line 1)
- (insert line)
- ;; If the COMMENT prefix argument is non-nil, comment the inserted text
- (when comment
- (comment-region (line-beginning-position) (line-end-position)))))))
+ (e (if (region-active-p) (region-end) (line-end-position)))
+ (lines (split-string (buffer-substring-no-properties b e) "\n")))
+ (save-excursion
+ (goto-char e)
+ (dolist (line lines)
+ (open-line 1)
+ (forward-line 1)
+ (insert line)
+ ;; If the COMMENT prefix argument is non-nil, comment the inserted text
+ (when comment
+ (comment-region (line-beginning-position) (line-end-position)))))))
(defun cj/remove-duplicate-lines-region-or-buffer ()
"Remove duplicate lines in the region or buffer, keeping the first occurrence.
@@ -66,16 +68,15 @@ Operate on the active region when one exists; otherwise operate on the whole
buffer."
(interactive)
(let ((start (if (use-region-p) (region-beginning) (point-min)))
- (end (if (use-region-p) (region-end) (point-max))))
- (save-excursion
- (let ((end-marker (copy-marker end)))
- (while
- (progn
- (goto-char start)
- (re-search-forward "^\\(.*\\)\n\\(\\(.*\n\\)*\\)\\1\n"
- end-marker t))
- (replace-match "\\1\n\\2"))))))
-
+ (end (if (use-region-p) (region-end) (point-max))))
+ (save-excursion
+ (let ((end-marker (copy-marker end)))
+ (while
+ (progn
+ (goto-char start)
+ (re-search-forward "^\\(.*\\)\n\\(\\(.*\n\\)*\\)\\1\n"
+ end-marker t))
+ (replace-match "\\1\n\\2"))))))
(defun cj/remove-lines-containing (text)
"Remove all lines containing TEXT.
@@ -83,55 +84,58 @@ If region is active, operate only on the region, otherwise on entire buffer.
The operation is undoable."
(interactive "sRemove lines containing: ")
(save-excursion
- (save-restriction
- (let ((region-active (use-region-p))
- (count 0))
- (when region-active
- (narrow-to-region (region-beginning) (region-end)))
- (goto-char (point-min))
- ;; Count lines before deletion
- (while (re-search-forward (regexp-quote text) nil t)
- (setq count (1+ count))
- (beginning-of-line)
- (forward-line))
- ;; Go back and delete
- (goto-char (point-min))
- (delete-matching-lines (regexp-quote text))
- ;; Report what was done
- (message "Removed %d line%s containing '%s' from %s"
- count
- (if (= count 1) "" "s")
- text
- (if region-active "region" "buffer"))))))
+ (save-restriction
+ (let ((region-active (use-region-p))
+ (count 0))
+ (when region-active
+ (narrow-to-region (region-beginning) (region-end)))
+ (goto-char (point-min))
+ ;; Count lines before deletion
+ (while (re-search-forward (regexp-quote text) nil t)
+ (setq count (1+ count))
+ (beginning-of-line)
+ (forward-line))
+ ;; Go back and delete
+ (goto-char (point-min))
+ (delete-matching-lines (regexp-quote text))
+ ;; Report what was done
+ (message "Removed %d line%s containing '%s' from %s"
+ count
+ (if (= count 1) "" "s")
+ text
+ (if region-active "region" "buffer"))))))
(defun cj/underscore-line ()
"Underline the current line by inserting a row of characters below it.
If the line is empty or contains only whitespace, abort with a message."
(interactive)
(let ((line (buffer-substring-no-properties
- (line-beginning-position)
- (line-end-position))))
- (if (string-match-p "^[[:space:]]*$" line)
- (message "Line empty or only whitespace. Aborting.")
- (let* ((char (read-char "Enter character for underlining: "))
- (len (save-excursion
- (goto-char (line-end-position))
- (current-column))))
- (save-excursion
- (end-of-line)
- (insert "\n" (make-string len char)))))))
+ (line-beginning-position)
+ (line-end-position))))
+ (if (string-match-p "^[[:space:]]*$" line)
+ (message "Line empty or only whitespace. Aborting.")
+ (let* ((char (read-char "Enter character for underlining: "))
+ (len (save-excursion
+ (goto-char (line-end-position))
+ (current-column))))
+ (save-excursion
+ (end-of-line)
+ (insert "\n" (make-string len char)))))))
;; ------------------------- Line And Paragraph Keymap -------------------------
(defvar-keymap cj/line-and-paragraph-map
:doc "Keymap for line and paragraph operations."
- "j" #'cj/join-line-or-region
- "J" #'cj/join-paragraph
- "d" #'cj/duplicate-line-or-region
- "R" #'cj/remove-duplicate-lines-region-or-buffer
- "r" #'cj/remove-lines-containing
- "u" #'cj/underscore-line)
+ "j" #'cj/join-line-or-region
+ "J" #'cj/join-paragraph
+ "d" #'cj/duplicate-line-or-region
+ "R" #'cj/remove-duplicate-lines-region-or-buffer
+ "r" #'cj/remove-lines-containing
+ "u" #'cj/underscore-line)
(keymap-set cj/custom-keymap "l" cj/line-and-paragraph-map)
+(with-eval-after-load 'which-key
+ (which-key-add-key-based-replacements "C-; l" "line and paragraph menu"))
+
(provide 'custom-line-paragraph)
;;; custom-line-paragraph.el ends here.
diff --git a/modules/undead-buffers.el b/modules/undead-buffers.el
index a7584476..50c9bb9c 100644
--- a/modules/undead-buffers.el
+++ b/modules/undead-buffers.el
@@ -17,34 +17,43 @@
;;
;;; Code:
-(defvar cj/buffer-bury-alive-list
- '("*dashboard*" "*scratch*" "*EMMS-Playlist*" "*Messages*" "*ert*" "*AI-Assistant*")
+(defvar cj/undead-buffer-list
+ '("*scratch*" "*EMMS-Playlist*" "*Messages*" "*ert*"
+ "*AI-Assistant*")
"Buffers to bury instead of killing.")
+(defun cj/make-buffer-undead (name)
+ "Append NAME to `cj/undead-buffer-list' if not present.
+Signal an error if NAME is not a non-empty string. Return the updated list."
+ (unless (and (stringp name) (> (length name) 0))
+ (error "cj/bury-alive-add: NAME must be a non-empty string"))
+ (add-to-list 'cj/undead-buffer-list name t))
+
(defun cj/kill-buffer-or-bury-alive (buffer)
- "Kill BUFFER or bury it if it's in `cj/buffer-bury-alive-list'."
+ "Kill BUFFER or bury it if it's in `cj/undead-buffer-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))
+ (add-to-list 'cj/undead-buffer-list (buffer-name))
(message "Added %s to bury-alive-list" (buffer-name)))
- (if (member (buffer-name) cj/buffer-bury-alive-list)
+ (if (member (buffer-name) cj/undead-buffer-list)
(bury-buffer)
(kill-buffer)))))
(keymap-global-set "<remap> <kill-buffer>" #'cj/kill-buffer-or-bury-alive)
(defun cj/undead-buffer-p ()
- "Predicate for `save-some-buffers' that skips buffers in `cj/buffer-bury-alive-list'."
+ "Replacement for `save-some-buffers' skips undead-buffers.
+Undead-buffers are buffers in `cj/undead-buffer-list'."
(let* ((buf (current-buffer))
(name (buffer-name buf)))
(and
- (not (member name cj/buffer-bury-alive-list))
+ (not (member name 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/buffer-bury-alive-list'.
+ "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))
@@ -53,7 +62,8 @@ ARG is passed to `save-some-buffers'."
"Delete window and kill or bury its buffer."
(interactive)
(let ((buf (current-buffer)))
- (delete-window)
+ (unless (one-window-p)
+ (delete-window))
(cj/kill-buffer-or-bury-alive buf)))
(keymap-global-set "M-C" #'cj/kill-buffer-and-window)
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
new file mode 100644
index 00000000..dcd08e96
--- /dev/null
+++ b/tests/test-undead-buffers-kill-all-other-buffers-and-windows.el
@@ -0,0 +1,159 @@
+;;; test-undead-buffers-kill-all-other-buffers-and-windows.el --- Tests for cj/kill-all-other-buffers-and-windows -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the cj/kill-all-other-buffers-and-windows function from undead-buffers.el
+
+;;; Code:
+
+(require 'ert)
+(require 'undead-buffers)
+(require 'testutil-general)
+
+;;; Setup and Teardown
+
+(defun test-kill-all-other-buffers-and-windows-setup ()
+ "Setup for kill-all-other-buffers-and-windows tests."
+ (cj/create-test-base-dir)
+ (delete-other-windows))
+
+(defun test-kill-all-other-buffers-and-windows-teardown ()
+ "Teardown for kill-all-other-buffers-and-windows tests."
+ (delete-other-windows)
+ (cj/delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-kill-all-other-buffers-and-windows-should-kill-regular-buffers ()
+ "Should kill all regular buffers except current."
+ (test-kill-all-other-buffers-and-windows-setup)
+ (unwind-protect
+ (let ((main (current-buffer))
+ (buf1 (generate-new-buffer "*test-regular-1*"))
+ (buf2 (generate-new-buffer "*test-regular-2*")))
+ (unwind-protect
+ (progn
+ (should (buffer-live-p buf1))
+ (should (buffer-live-p buf2))
+ (cj/kill-all-other-buffers-and-windows)
+ (should (buffer-live-p main))
+ (should-not (buffer-live-p buf1))
+ (should-not (buffer-live-p buf2)))
+ (when (buffer-live-p buf1) (kill-buffer buf1))
+ (when (buffer-live-p buf2) (kill-buffer buf2))))
+ (test-kill-all-other-buffers-and-windows-teardown)))
+
+(ert-deftest test-kill-all-other-buffers-and-windows-should-bury-undead-buffers ()
+ "Should bury undead buffers instead of killing them."
+ (test-kill-all-other-buffers-and-windows-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (main (current-buffer))
+ (buf1 (generate-new-buffer "*test-undead-1*"))
+ (buf2 (generate-new-buffer "*test-undead-2*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-undead-1*")
+ (add-to-list 'cj/undead-buffer-list "*test-undead-2*")
+ (cj/kill-all-other-buffers-and-windows)
+ (should (buffer-live-p main))
+ (should (buffer-live-p buf1))
+ (should (buffer-live-p buf2)))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf1) (kill-buffer buf1))
+ (when (buffer-live-p buf2) (kill-buffer buf2))))
+ (test-kill-all-other-buffers-and-windows-teardown)))
+
+(ert-deftest test-kill-all-other-buffers-and-windows-should-keep-current-buffer ()
+ "Should always keep the current buffer alive."
+ (test-kill-all-other-buffers-and-windows-setup)
+ (unwind-protect
+ (let ((main (current-buffer)))
+ (cj/kill-all-other-buffers-and-windows)
+ (should (buffer-live-p main))
+ (should (eq main (current-buffer))))
+ (test-kill-all-other-buffers-and-windows-teardown)))
+
+(ert-deftest test-kill-all-other-buffers-and-windows-should-delete-all-other-windows ()
+ "Should delete all windows except current."
+ (test-kill-all-other-buffers-and-windows-setup)
+ (unwind-protect
+ (progn
+ (split-window)
+ (split-window)
+ (should (> (length (window-list)) 1))
+ (cj/kill-all-other-buffers-and-windows)
+ (should (one-window-p)))
+ (test-kill-all-other-buffers-and-windows-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-kill-all-other-buffers-and-windows-mixed-undead-and-regular-buffers ()
+ "With mix of undead and regular buffers, should handle both correctly."
+ (test-kill-all-other-buffers-and-windows-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (main (current-buffer))
+ (regular (generate-new-buffer "*test-regular*"))
+ (undead (generate-new-buffer "*test-undead*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-undead*")
+ (cj/kill-all-other-buffers-and-windows)
+ (should (buffer-live-p main))
+ (should-not (buffer-live-p regular))
+ (should (buffer-live-p undead)))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p regular) (kill-buffer regular))
+ (when (buffer-live-p undead) (kill-buffer undead))))
+ (test-kill-all-other-buffers-and-windows-teardown)))
+
+(ert-deftest test-kill-all-other-buffers-and-windows-all-undead-buffers-should-bury-all ()
+ "When all other buffers are undead, should bury all of them."
+ (test-kill-all-other-buffers-and-windows-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (main (current-buffer))
+ (undead1 (generate-new-buffer "*test-all-undead-1*"))
+ (undead2 (generate-new-buffer "*test-all-undead-2*"))
+ (undead3 (generate-new-buffer "*test-all-undead-3*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-all-undead-1*")
+ (add-to-list 'cj/undead-buffer-list "*test-all-undead-2*")
+ (add-to-list 'cj/undead-buffer-list "*test-all-undead-3*")
+ (cj/kill-all-other-buffers-and-windows)
+ (should (buffer-live-p main))
+ (should (buffer-live-p undead1))
+ (should (buffer-live-p undead2))
+ (should (buffer-live-p undead3)))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p undead1) (kill-buffer undead1))
+ (when (buffer-live-p undead2) (kill-buffer undead2))
+ (when (buffer-live-p undead3) (kill-buffer undead3))))
+ (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."
+ (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))))
+ (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))
+ (when (buffer-live-p buf)
+ (set-buffer-modified-p nil)
+ (kill-buffer buf))))))
+ (test-kill-all-other-buffers-and-windows-teardown)))
+
+(provide 'test-undead-buffers-kill-all-other-buffers-and-windows)
+;;; test-undead-buffers-kill-all-other-buffers-and-windows.el ends here
diff --git a/tests/test-undead-buffers-kill-buffer-and-window.el b/tests/test-undead-buffers-kill-buffer-and-window.el
new file mode 100644
index 00000000..b49969f6
--- /dev/null
+++ b/tests/test-undead-buffers-kill-buffer-and-window.el
@@ -0,0 +1,112 @@
+;;; test-undead-buffers-kill-buffer-and-window.el --- Tests for cj/kill-buffer-and-window -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the cj/kill-buffer-and-window function from undead-buffers.el
+
+;;; Code:
+
+(require 'ert)
+(require 'undead-buffers)
+(require 'testutil-general)
+
+;;; Setup and Teardown
+
+(defun test-kill-buffer-and-window-setup ()
+ "Setup for kill-buffer-and-window tests."
+ (cj/create-test-base-dir)
+ (delete-other-windows))
+
+(defun test-kill-buffer-and-window-teardown ()
+ "Teardown for kill-buffer-and-window tests."
+ (delete-other-windows)
+ (cj/delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-kill-buffer-and-window-multiple-windows-should-delete-window-and-kill-buffer ()
+ "With multiple windows, should delete window and kill buffer."
+ (test-kill-buffer-and-window-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-multi*")))
+ (unwind-protect
+ (progn
+ (split-window)
+ (switch-to-buffer buf)
+ (let ((win (selected-window)))
+ (cj/kill-buffer-and-window)
+ (should-not (window-live-p win))
+ (should-not (buffer-live-p buf))))
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-buffer-and-window-teardown)))
+
+(ert-deftest test-kill-buffer-and-window-multiple-windows-undead-buffer-should-delete-window-and-bury ()
+ "With multiple windows, undead buffer should be buried and window deleted."
+ (test-kill-buffer-and-window-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (buf (generate-new-buffer "*test-undead-multi*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-undead-multi*")
+ (split-window)
+ (switch-to-buffer buf)
+ (let ((win (selected-window)))
+ (cj/kill-buffer-and-window)
+ (should-not (window-live-p win))
+ (should (buffer-live-p buf))))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-buffer-and-window-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-kill-buffer-and-window-single-window-should-only-kill-buffer ()
+ "With single window, should only kill buffer, not delete window."
+ (test-kill-buffer-and-window-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-single*")))
+ (unwind-protect
+ (progn
+ (switch-to-buffer buf)
+ (should (one-window-p))
+ (cj/kill-buffer-and-window)
+ (should (one-window-p))
+ (should-not (buffer-live-p buf)))
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-buffer-and-window-teardown)))
+
+(ert-deftest test-kill-buffer-and-window-single-window-undead-buffer-should-only-bury ()
+ "With single window, undead buffer should only be buried."
+ (test-kill-buffer-and-window-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (buf (generate-new-buffer "*test-undead-single*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-undead-single*")
+ (switch-to-buffer buf)
+ (should (one-window-p))
+ (cj/kill-buffer-and-window)
+ (should (one-window-p))
+ (should (buffer-live-p buf)))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-buffer-and-window-teardown)))
+
+(ert-deftest test-kill-buffer-and-window-two-windows-should-leave-one ()
+ "With two windows, should leave one window after deletion."
+ (test-kill-buffer-and-window-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-two*")))
+ (unwind-protect
+ (progn
+ (split-window)
+ (set-window-buffer (selected-window) buf)
+ (should (= 2 (length (window-list))))
+ (cj/kill-buffer-and-window)
+ (should (= 1 (length (window-list)))))
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-buffer-and-window-teardown)))
+
+(provide 'test-undead-buffers-kill-buffer-and-window)
+;;; test-undead-buffers-kill-buffer-and-window.el ends here
diff --git a/tests/test-undead-buffers-kill-buffer-or-bury-alive.el b/tests/test-undead-buffers-kill-buffer-or-bury-alive.el
new file mode 100644
index 00000000..60b776e4
--- /dev/null
+++ b/tests/test-undead-buffers-kill-buffer-or-bury-alive.el
@@ -0,0 +1,138 @@
+;;; test-undead-buffers-kill-buffer-or-bury-alive.el --- Tests for cj/kill-buffer-or-bury-alive -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the cj/kill-buffer-or-bury-alive function from undead-buffers.el
+
+;;; Code:
+
+(require 'ert)
+(require 'undead-buffers)
+(require 'testutil-general)
+
+;;; Setup and Teardown
+
+(defun test-kill-buffer-or-bury-alive-setup ()
+ "Setup for kill-buffer-or-bury-alive tests."
+ (cj/create-test-base-dir))
+
+(defun test-kill-buffer-or-bury-alive-teardown ()
+ "Teardown for kill-buffer-or-bury-alive tests."
+ (cj/delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-kill-buffer-or-bury-alive-regular-buffer-should-kill ()
+ "Killing a regular buffer not in undead list should kill it."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-regular*")))
+ (should (buffer-live-p buf))
+ (cj/kill-buffer-or-bury-alive buf)
+ (should-not (buffer-live-p buf)))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+(ert-deftest test-kill-buffer-or-bury-alive-undead-buffer-should-bury ()
+ "Killing an undead buffer should bury it instead."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (buf (generate-new-buffer "*test-undead*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-undead*")
+ (should (buffer-live-p buf))
+ (cj/kill-buffer-or-bury-alive buf)
+ (should (buffer-live-p buf)))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+(ert-deftest test-kill-buffer-or-bury-alive-with-prefix-arg-should-add-to-undead-list ()
+ "Calling with prefix arg should add buffer to undead list."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (buf (generate-new-buffer "*test-prefix*")))
+ (unwind-protect
+ (progn
+ (with-current-buffer buf
+ (let ((current-prefix-arg '(4)))
+ (cj/kill-buffer-or-bury-alive buf)))
+ (should (member "*test-prefix*" cj/undead-buffer-list))
+ (should (buffer-live-p buf)))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+(ert-deftest test-kill-buffer-or-bury-alive-scratch-buffer-should-bury ()
+ "The *scratch* buffer (in default list) should be buried."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((scratch (get-buffer-create "*scratch*")))
+ (should (buffer-live-p scratch))
+ (cj/kill-buffer-or-bury-alive scratch)
+ (should (buffer-live-p scratch)))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-kill-buffer-or-bury-alive-buffer-by-name-string-should-work ()
+ "Passing buffer name as string should work."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-string*")))
+ (should (buffer-live-p buf))
+ (cj/kill-buffer-or-bury-alive "*test-string*")
+ (should-not (buffer-live-p buf)))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+(ert-deftest test-kill-buffer-or-bury-alive-buffer-by-buffer-object-should-work ()
+ "Passing buffer object should work."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-object*")))
+ (should (buffer-live-p buf))
+ (cj/kill-buffer-or-bury-alive buf)
+ (should-not (buffer-live-p buf)))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+(ert-deftest test-kill-buffer-or-bury-alive-modified-undead-buffer-should-bury-without-prompt ()
+ "Modified undead buffer should be buried without save prompt."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (buf (generate-new-buffer "*test-modified*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-modified*")
+ (with-current-buffer buf
+ (insert "some text")
+ (set-buffer-modified-p t))
+ (cj/kill-buffer-or-bury-alive buf)
+ (should (buffer-live-p buf)))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf)
+ (set-buffer-modified-p nil)
+ (kill-buffer buf))))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+;;; Error Cases
+
+(ert-deftest test-kill-buffer-or-bury-alive-nonexistent-buffer-should-error ()
+ "Passing a non-existent buffer name should error."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (should-error (cj/kill-buffer-or-bury-alive "*nonexistent-buffer-xyz*"))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+(ert-deftest test-kill-buffer-or-bury-alive-killed-buffer-object-should-error ()
+ "Passing a killed buffer object should error."
+ (test-kill-buffer-or-bury-alive-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-killed*")))
+ (kill-buffer buf)
+ (should-error (cj/kill-buffer-or-bury-alive buf)))
+ (test-kill-buffer-or-bury-alive-teardown)))
+
+(provide 'test-undead-buffers-kill-buffer-or-bury-alive)
+;;; test-undead-buffers-kill-buffer-or-bury-alive.el ends here
diff --git a/tests/test-undead-buffers-kill-other-window.el b/tests/test-undead-buffers-kill-other-window.el
new file mode 100644
index 00000000..e9371a0f
--- /dev/null
+++ b/tests/test-undead-buffers-kill-other-window.el
@@ -0,0 +1,123 @@
+;;; test-undead-buffers-kill-other-window.el --- Tests for cj/kill-other-window -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the cj/kill-other-window function from undead-buffers.el
+
+;;; Code:
+
+(require 'ert)
+(require 'undead-buffers)
+(require 'testutil-general)
+
+;;; Setup and Teardown
+
+(defun test-kill-other-window-setup ()
+ "Setup for kill-other-window tests."
+ (cj/create-test-base-dir)
+ (delete-other-windows))
+
+(defun test-kill-other-window-teardown ()
+ "Teardown for kill-other-window tests."
+ (delete-other-windows)
+ (cj/delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-kill-other-window-two-windows-should-delete-other-and-kill-buffer ()
+ "With two windows, should delete other window and kill its buffer."
+ (test-kill-other-window-setup)
+ (unwind-protect
+ (let ((buf1 (current-buffer))
+ (buf2 (generate-new-buffer "*test-other*")))
+ (unwind-protect
+ (progn
+ (split-window)
+ (let ((win1 (selected-window))
+ (win2 (next-window)))
+ (set-window-buffer win2 buf2)
+ (select-window win1)
+ (cj/kill-other-window)
+ (should-not (window-live-p win2))
+ (should-not (buffer-live-p buf2))))
+ (when (buffer-live-p buf2) (kill-buffer buf2))))
+ (test-kill-other-window-teardown)))
+
+(ert-deftest test-kill-other-window-two-windows-undead-buffer-should-delete-other-and-bury ()
+ "With two windows, undead buffer in other window should be buried."
+ (test-kill-other-window-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (buf1 (current-buffer))
+ (buf2 (generate-new-buffer "*test-undead-other*")))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list "*test-undead-other*")
+ (split-window)
+ (let ((win1 (selected-window))
+ (win2 (next-window)))
+ (set-window-buffer win2 buf2)
+ (select-window win1)
+ (cj/kill-other-window)
+ (should-not (window-live-p win2))
+ (should (buffer-live-p buf2))))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf2) (kill-buffer buf2))))
+ (test-kill-other-window-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-kill-other-window-single-window-should-only-kill-buffer ()
+ "With single window, should only kill the current buffer."
+ (test-kill-other-window-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-single-other*")))
+ (unwind-protect
+ (progn
+ (switch-to-buffer buf)
+ (should (one-window-p))
+ (cj/kill-other-window)
+ (should (one-window-p))
+ (should-not (buffer-live-p buf)))
+ (when (buffer-live-p buf) (kill-buffer buf))))
+ (test-kill-other-window-teardown)))
+
+(ert-deftest test-kill-other-window-three-windows-should-delete-one ()
+ "With three windows, should delete one window."
+ (test-kill-other-window-setup)
+ (unwind-protect
+ (let ((buf1 (current-buffer))
+ (buf2 (generate-new-buffer "*test-three-1*"))
+ (buf3 (generate-new-buffer "*test-three-2*")))
+ (unwind-protect
+ (progn
+ (split-window)
+ (split-window)
+ (set-window-buffer (nth 1 (window-list)) buf2)
+ (set-window-buffer (nth 2 (window-list)) buf3)
+ (select-window (car (window-list)))
+ (should (= 3 (length (window-list))))
+ (cj/kill-other-window)
+ (should (= 2 (length (window-list)))))
+ (when (buffer-live-p buf2) (kill-buffer buf2))
+ (when (buffer-live-p buf3) (kill-buffer buf3))))
+ (test-kill-other-window-teardown)))
+
+(ert-deftest test-kill-other-window-wraps-to-first-window-correctly ()
+ "Should correctly cycle through windows with other-window."
+ (test-kill-other-window-setup)
+ (unwind-protect
+ (let ((buf1 (current-buffer))
+ (buf2 (generate-new-buffer "*test-wrap*")))
+ (unwind-protect
+ (progn
+ (split-window)
+ (let ((win2 (next-window)))
+ (set-window-buffer win2 buf2)
+ (select-window (car (window-list)))
+ (cj/kill-other-window)
+ (should-not (window-live-p win2))))
+ (when (buffer-live-p buf2) (kill-buffer buf2))))
+ (test-kill-other-window-teardown)))
+
+(provide 'test-undead-buffers-kill-other-window)
+;;; test-undead-buffers-kill-other-window.el ends here
diff --git a/tests/test-undead-buffers-make-buffer-undead.el b/tests/test-undead-buffers-make-buffer-undead.el
new file mode 100644
index 00000000..823bb56e
--- /dev/null
+++ b/tests/test-undead-buffers-make-buffer-undead.el
@@ -0,0 +1,134 @@
+;;; test-undead-buffers-make-buffer-undead.el --- Tests for cj/make-buffer-undead -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the cj/make-buffer-undead function from undead-buffers.el
+
+;;; Code:
+
+(require 'ert)
+(require 'undead-buffers)
+(require 'testutil-general)
+
+;;; Setup and Teardown
+
+(defun test-make-buffer-undead-setup ()
+ "Setup for make-buffer-undead tests."
+ (cj/create-test-base-dir))
+
+(defun test-make-buffer-undead-teardown ()
+ "Teardown for make-buffer-undead tests."
+ (cj/delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-make-buffer-undead-valid-name-should-add-to-list ()
+ "Adding a valid buffer name should add it to the undead buffer list."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list)))
+ (unwind-protect
+ (progn
+ (cj/make-buffer-undead "*test-buffer*")
+ (should (member "*test-buffer*" cj/undead-buffer-list)))
+ (setq cj/undead-buffer-list orig)))
+ (test-make-buffer-undead-teardown)))
+
+(ert-deftest test-make-buffer-undead-existing-name-should-not-duplicate ()
+ "Adding an existing buffer name should not create duplicates."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list)))
+ (unwind-protect
+ (progn
+ (cj/make-buffer-undead "*test-dup*")
+ (cj/make-buffer-undead "*test-dup*")
+ (should (= 1 (cl-count "*test-dup*" cj/undead-buffer-list :test #'string=))))
+ (setq cj/undead-buffer-list orig)))
+ (test-make-buffer-undead-teardown)))
+
+(ert-deftest test-make-buffer-undead-multiple-additions-should-preserve-order ()
+ "Adding multiple buffer names should preserve order."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list)))
+ (unwind-protect
+ (progn
+ (cj/make-buffer-undead "*first*")
+ (cj/make-buffer-undead "*second*")
+ (cj/make-buffer-undead "*third*")
+ (let ((added-items (seq-drop cj/undead-buffer-list (length orig))))
+ (should (equal added-items '("*first*" "*second*" "*third*")))))
+ (setq cj/undead-buffer-list orig)))
+ (test-make-buffer-undead-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-make-buffer-undead-whitespace-only-name-should-add ()
+ "Adding a whitespace-only name should succeed."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list)))
+ (unwind-protect
+ (progn
+ (cj/make-buffer-undead " ")
+ (should (member " " cj/undead-buffer-list)))
+ (setq cj/undead-buffer-list orig)))
+ (test-make-buffer-undead-teardown)))
+
+(ert-deftest test-make-buffer-undead-very-long-name-should-add ()
+ "Adding a very long buffer name should succeed."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list))
+ (long-name (make-string 1000 ?x)))
+ (unwind-protect
+ (progn
+ (cj/make-buffer-undead long-name)
+ (should (member long-name cj/undead-buffer-list)))
+ (setq cj/undead-buffer-list orig)))
+ (test-make-buffer-undead-teardown)))
+
+(ert-deftest test-make-buffer-undead-unicode-name-should-add ()
+ "Adding a buffer name with Unicode characters should succeed."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (let ((orig (copy-sequence cj/undead-buffer-list)))
+ (unwind-protect
+ (progn
+ (cj/make-buffer-undead "*test-🚀-buffer*")
+ (should (member "*test-🚀-buffer*" cj/undead-buffer-list)))
+ (setq cj/undead-buffer-list orig)))
+ (test-make-buffer-undead-teardown)))
+
+;;; Error Cases
+
+(ert-deftest test-make-buffer-undead-empty-string-should-error ()
+ "Passing an empty string should signal an error."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (should-error (cj/make-buffer-undead ""))
+ (test-make-buffer-undead-teardown)))
+
+(ert-deftest test-make-buffer-undead-nil-should-error ()
+ "Passing nil should signal an error."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (should-error (cj/make-buffer-undead nil))
+ (test-make-buffer-undead-teardown)))
+
+(ert-deftest test-make-buffer-undead-number-should-error ()
+ "Passing a number should signal an error."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (should-error (cj/make-buffer-undead 42))
+ (test-make-buffer-undead-teardown)))
+
+(ert-deftest test-make-buffer-undead-symbol-should-error ()
+ "Passing a symbol should signal an error."
+ (test-make-buffer-undead-setup)
+ (unwind-protect
+ (should-error (cj/make-buffer-undead 'some-symbol))
+ (test-make-buffer-undead-teardown)))
+
+(provide 'test-undead-buffers-make-buffer-undead)
+;;; test-undead-buffers-make-buffer-undead.el ends here
diff --git a/tests/test-undead-buffers-undead-buffer-p.el b/tests/test-undead-buffers-undead-buffer-p.el
new file mode 100644
index 00000000..107256c9
--- /dev/null
+++ b/tests/test-undead-buffers-undead-buffer-p.el
@@ -0,0 +1,106 @@
+;;; test-undead-buffers-undead-buffer-p.el --- Tests for cj/undead-buffer-p -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the cj/undead-buffer-p function from undead-buffers.el
+
+;;; Code:
+
+(require 'ert)
+(require 'undead-buffers)
+(require 'testutil-general)
+
+;;; Setup and Teardown
+
+(defun test-undead-buffer-p-setup ()
+ "Setup for undead-buffer-p tests."
+ (cj/create-test-base-dir))
+
+(defun test-undead-buffer-p-teardown ()
+ "Teardown for undead-buffer-p tests."
+ (cj/delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-undead-buffer-p-modified-file-buffer-should-return-true ()
+ "A modified file-backed buffer not in undead list should return t."
+ (test-undead-buffer-p-setup)
+ (unwind-protect
+ (let* ((file (cj/create-temp-test-file-with-content "test content"))
+ (buf (find-file-noselect file)))
+ (unwind-protect
+ (progn
+ (with-current-buffer buf
+ (insert "more content")
+ (should (cj/undead-buffer-p))))
+ (when (buffer-live-p buf)
+ (set-buffer-modified-p nil)
+ (kill-buffer buf))))
+ (test-undead-buffer-p-teardown)))
+
+(ert-deftest test-undead-buffer-p-undead-modified-file-buffer-should-return-nil ()
+ "A modified file-backed undead buffer should return nil."
+ (test-undead-buffer-p-setup)
+ (unwind-protect
+ (let* ((orig (copy-sequence cj/undead-buffer-list))
+ (file (cj/create-temp-test-file-with-content "test content"))
+ (buf (find-file-noselect file)))
+ (unwind-protect
+ (progn
+ (add-to-list 'cj/undead-buffer-list (buffer-name buf))
+ (with-current-buffer buf
+ (insert "more content")
+ (should-not (cj/undead-buffer-p))))
+ (setq cj/undead-buffer-list orig)
+ (when (buffer-live-p buf)
+ (set-buffer-modified-p nil)
+ (kill-buffer buf))))
+ (test-undead-buffer-p-teardown)))
+
+(ert-deftest test-undead-buffer-p-scratch-buffer-should-return-nil ()
+ "The *scratch* buffer should return nil (it's undead)."
+ (test-undead-buffer-p-setup)
+ (unwind-protect
+ (with-current-buffer "*scratch*"
+ (should-not (cj/undead-buffer-p)))
+ (test-undead-buffer-p-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-undead-buffer-p-unmodified-file-buffer-should-return-nil ()
+ "An unmodified file buffer should return nil."
+ (test-undead-buffer-p-setup)
+ (unwind-protect
+ (let* ((file (cj/create-temp-test-file-with-content "test content"))
+ (buf (find-file-noselect file)))
+ (unwind-protect
+ (with-current-buffer buf
+ (should-not (cj/undead-buffer-p)))
+ (when (buffer-live-p buf)
+ (kill-buffer buf))))
+ (test-undead-buffer-p-teardown)))
+
+(ert-deftest test-undead-buffer-p-modified-buffer-without-file-should-return-nil ()
+ "A modified buffer without a backing file should return nil."
+ (test-undead-buffer-p-setup)
+ (unwind-protect
+ (let ((buf (generate-new-buffer "*test-no-file*")))
+ (unwind-protect
+ (with-current-buffer buf
+ (insert "content")
+ (set-buffer-modified-p t)
+ (should-not (cj/undead-buffer-p)))
+ (when (buffer-live-p buf)
+ (set-buffer-modified-p nil)
+ (kill-buffer buf))))
+ (test-undead-buffer-p-teardown)))
+
+(ert-deftest test-undead-buffer-p-temporary-buffer-should-return-nil ()
+ "A temporary buffer should return nil."
+ (test-undead-buffer-p-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (should-not (cj/undead-buffer-p)))
+ (test-undead-buffer-p-teardown)))
+
+(provide 'test-undead-buffers-undead-buffer-p)
+;;; test-undead-buffers-undead-buffer-p.el ends here