diff options
| -rw-r--r-- | modules/config-utilities.el | 4 | ||||
| -rw-r--r-- | modules/custom-case.el | 16 | ||||
| -rw-r--r-- | modules/custom-comments.el | 219 | ||||
| -rw-r--r-- | modules/custom-datetime.el | 5 | ||||
| -rw-r--r-- | modules/custom-file-buffer.el | 10 | ||||
| -rw-r--r-- | modules/custom-line-paragraph.el | 142 | ||||
| -rw-r--r-- | modules/undead-buffers.el | 28 | ||||
| -rw-r--r-- | tests/test-undead-buffers-kill-all-other-buffers-and-windows.el | 159 | ||||
| -rw-r--r-- | tests/test-undead-buffers-kill-buffer-and-window.el | 112 | ||||
| -rw-r--r-- | tests/test-undead-buffers-kill-buffer-or-bury-alive.el | 138 | ||||
| -rw-r--r-- | tests/test-undead-buffers-kill-other-window.el | 123 | ||||
| -rw-r--r-- | tests/test-undead-buffers-make-buffer-undead.el | 134 | ||||
| -rw-r--r-- | tests/test-undead-buffers-undead-buffer-p.el | 106 |
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 |
