diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-30 08:11:02 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-30 08:11:02 -0400 |
| commit | e88391f252ba038d9f824308ce2b873d7b29ab79 (patch) | |
| tree | 67ca9bdc0e7108f570d40eb65192537f280bda34 /tests/test-custom-buffer-file--diff-whitespace-only.el | |
| parent | 7e93212a5fe39985dd8dc6c0368a0c662d3cdf08 (diff) | |
| download | dotemacs-e88391f252ba038d9f824308ce2b873d7b29ab79.tar.gz dotemacs-e88391f252ba038d9f824308ce2b873d7b29ab79.zip | |
feat(buffer-file): legible save prompts for save-some-buffers and disk-changed saves
Replace the cryptic single-key save prompts with read-multiple-choice menus whose options are labeled on screen instead of recalled as keys.
save-some-buffers (C-x s and save-on-exit) is overridden with a reimplementation that reuses the stock candidate selection and abbrev-save tail but swaps map-y-or-n-p's terse key list for a labeled menu, and adds a clean-whitespace-and-save action.
C-x C-s gains a wrapper that fast-paths normal saves to save-buffer; only when the buffer has unsaved edits and the file changed on disk does it show a labeled menu (save / diff / clean / revert / cancel) instead of the bare "Save anyway?" yes/no.
Both menus share a diff toggle, and whitespace-only diffs route to a plain unified diff with trailing whitespace highlighted, since difftastic normalizes trailing whitespace and renders such changes blank.
The interactive wrappers stay thin over pure, tested helpers: the save-loop planner, the key-to-action maps, the whitespace detector, and the renderer choice.
Diffstat (limited to 'tests/test-custom-buffer-file--diff-whitespace-only.el')
| -rw-r--r-- | tests/test-custom-buffer-file--diff-whitespace-only.el | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/tests/test-custom-buffer-file--diff-whitespace-only.el b/tests/test-custom-buffer-file--diff-whitespace-only.el new file mode 100644 index 000000000..e792637b5 --- /dev/null +++ b/tests/test-custom-buffer-file--diff-whitespace-only.el @@ -0,0 +1,146 @@ +;;; test-custom-buffer-file--diff-whitespace-only.el --- whitespace-only diff detection -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for cj/--diff-whitespace-only-p, the route-1 detector behind the +;; buffer-differs save prompt: two files differ ONLY in whitespace when a plain +;; diff finds changes but `diff -w' (ignore all whitespace) finds none. Uses +;; real temp files and the real diff(1) binary (a system boundary we keep), so +;; nothing is mocked. + +;;; Code: + +(require 'ert) +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'custom-buffer-file) + +(declare-function cj/--diff-whitespace-only-p "custom-buffer-file" (file-a file-b)) + +(defun test-cbf-ws--two-files (content-a content-b fn) + "Write CONTENT-A and CONTENT-B to temp files, call FN with their paths, clean up." + (let ((a (make-temp-file "cbf-ws-a-")) + (b (make-temp-file "cbf-ws-b-"))) + (unwind-protect + (progn + (with-temp-file a (insert content-a)) + (with-temp-file b (insert content-b)) + (funcall fn a b)) + (delete-file a) + (delete-file b)))) + +(ert-deftest test-cbf-diff-whitespace-only-trailing () + "Normal: files differing only by trailing whitespace are whitespace-only." + (test-cbf-ws--two-files + "alpha\nbeta\n" "alpha \nbeta\n" + (lambda (a b) (should (cj/--diff-whitespace-only-p a b))))) + +(ert-deftest test-cbf-diff-whitespace-only-indentation () + "Normal: files differing only by leading indentation are whitespace-only." + (test-cbf-ws--two-files + "(foo)\n(bar)\n" "(foo)\n (bar)\n" + (lambda (a b) (should (cj/--diff-whitespace-only-p a b))))) + +(ert-deftest test-cbf-diff-whitespace-only-real-content () + "Normal: files differing in actual content are NOT whitespace-only." + (test-cbf-ws--two-files + "alpha\nbeta\n" "alpha\nGAMMA\n" + (lambda (a b) (should-not (cj/--diff-whitespace-only-p a b))))) + +(ert-deftest test-cbf-diff-whitespace-only-identical () + "Boundary: identical files do not differ at all, so not whitespace-only." + (test-cbf-ws--two-files + "alpha\nbeta\n" "alpha\nbeta\n" + (lambda (a b) (should-not (cj/--diff-whitespace-only-p a b))))) + +(ert-deftest test-cbf-diff-whitespace-only-mixed () + "Boundary: whitespace change plus a real content change is NOT whitespace-only." + (test-cbf-ws--two-files + "alpha\nbeta\n" "alpha \nGAMMA\n" + (lambda (a b) (should-not (cj/--diff-whitespace-only-p a b))))) + +;;; -------------------- cj/--diff-buffer-renderer ----------------------------- +;; Which renderer the diff command uses: whitespace-only diffs go to a plain +;; unified diff (trailing whitespace highlighted, so it is actually visible) +;; because difftastic treats trailing-whitespace as no change and renders it +;; blank. Real content diffs use difftastic when available, else plain diff. + +(declare-function cj/--diff-buffer-renderer "custom-buffer-file" (ws-only difft-available)) + +(ert-deftest test-cbf-diff-renderer-whitespace-over-difftastic () + "Normal: a whitespace-only diff uses the whitespace renderer even when difft is present." + (should (eq (cj/--diff-buffer-renderer t t) 'whitespace))) + +(ert-deftest test-cbf-diff-renderer-whitespace-no-difft () + "Boundary: whitespace-only still uses the whitespace renderer without difft." + (should (eq (cj/--diff-buffer-renderer t nil) 'whitespace))) + +(ert-deftest test-cbf-diff-renderer-content-uses-difftastic () + "Normal: a content diff uses difftastic when it is available." + (should (eq (cj/--diff-buffer-renderer nil t) 'difftastic))) + +(ert-deftest test-cbf-diff-renderer-content-no-difft-regular () + "Boundary: a content diff falls back to the regular renderer without difft." + (should (eq (cj/--diff-buffer-renderer nil nil) 'regular))) + +;;; --------------- cj/--buffer-file-whitespace-only-p (buffer) ---------------- +;; Buffer-vs-its-file variant: writes the buffer to a temp file and reuses the +;; detector against the buffer's visited file. Uses a real visited-file buffer. + +(declare-function cj/--buffer-file-whitespace-only-p "custom-buffer-file" (buffer)) + +(defun test-cbf-ws--with-visited (disk-content edit-fn body-fn) + "Visit a temp file holding DISK-CONTENT, apply EDIT-FN in it, call BODY-FN with the buffer." + (let ((f (make-temp-file "cbf-bws-" nil ".txt"))) + (unwind-protect + (progn + (with-temp-file f (insert disk-content)) + (let ((buf (find-file-noselect f))) + (unwind-protect + (with-current-buffer buf + (funcall edit-fn) + (funcall body-fn buf)) + (with-current-buffer buf (set-buffer-modified-p nil)) + (kill-buffer buf)))) + (when (file-exists-p f) (delete-file f))))) + +(ert-deftest test-cbf-buffer-file-ws-only-trailing () + "Normal: an unsaved trailing-whitespace edit is whitespace-only vs the file." + (test-cbf-ws--with-visited + "alpha\nbeta\n" + (lambda () (goto-char (point-min)) (end-of-line) (insert " ")) + (lambda (buf) (should (cj/--buffer-file-whitespace-only-p buf))))) + +(ert-deftest test-cbf-buffer-file-ws-only-content () + "Normal: an unsaved content edit is NOT whitespace-only vs the file." + (test-cbf-ws--with-visited + "alpha\nbeta\n" + (lambda () (goto-char (point-max)) (insert "gamma\n")) + (lambda (buf) (should-not (cj/--buffer-file-whitespace-only-p buf))))) + +;;; --------- cj/diff-buffer-with-file return value (for the toggle) ----------- + +(ert-deftest test-cbf-diff-returns-buffer-when-differs () + "Normal: cj/diff-buffer-with-file returns the live diff buffer when the buffer differs." + (test-cbf-ws--with-visited + "x\ny\n" + (lambda () (goto-char (point-max)) (insert "ADDED\n")) + (lambda (buf) + (let ((db (with-current-buffer buf (cj/diff-buffer-with-file)))) + (should (bufferp db)) + (should (buffer-live-p db)))))) + +(ert-deftest test-cbf-diff-returns-nil-when-identical () + "Boundary: with no differences, cj/diff-buffer-with-file returns nil." + (test-cbf-ws--with-visited + "x\ny\n" + (lambda () nil) + (lambda (buf) + (should-not (with-current-buffer buf (cj/diff-buffer-with-file)))))) + +(ert-deftest test-cbf-buffer-file-ws-only-non-file () + "Boundary: a buffer not visiting a file is not whitespace-only." + (with-temp-buffer + (insert "scratch") + (should-not (cj/--buffer-file-whitespace-only-p (current-buffer))))) + +(provide 'test-custom-buffer-file--diff-whitespace-only) +;;; test-custom-buffer-file--diff-whitespace-only.el ends here |
