diff options
| -rw-r--r-- | modules/custom-line-paragraph.el | 14 | ||||
| -rw-r--r-- | tests/test-custom-line-paragraph-join-line-or-region.el | 92 | ||||
| -rw-r--r-- | tests/test-custom-line-paragraph-join-paragraph.el | 29 |
3 files changed, 108 insertions, 27 deletions
diff --git a/modules/custom-line-paragraph.el b/modules/custom-line-paragraph.el index 32f9aaa1..4b0baa90 100644 --- a/modules/custom-line-paragraph.el +++ b/modules/custom-line-paragraph.el @@ -24,13 +24,19 @@ "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)))) + ;; Compute the join count up front from the region's line span. + ;; A position-based loop overshoots β after the final in-region join, + ;; point still sits before the end marker, so one extra `join-line 1` + ;; reaches past the region and pulls the next line in, leaving a stray + ;; space at its head when the trailing newline is reinserted. + (let* ((beg (region-beginning)) + (end (copy-marker (region-end))) + (n (count-lines beg end))) (goto-char beg) - (while (< (point) end) + (dotimes (_ (max 0 (1- n))) (join-line 1)) (goto-char end) - (newline) + (forward-line 1) (deactivate-mark)) ;; No region - only join if there's a previous line (when (> (line-number-at-pos) 1) diff --git a/tests/test-custom-line-paragraph-join-line-or-region.el b/tests/test-custom-line-paragraph-join-line-or-region.el index 0d28ab6c..f8738910 100644 --- a/tests/test-custom-line-paragraph-join-line-or-region.el +++ b/tests/test-custom-line-paragraph-join-line-or-region.el @@ -26,12 +26,15 @@ ;; Add modules directory to load path (add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) -;; Stub dependencies before loading the module -(defvar cj/custom-keymap (make-sparse-keymap) - "Stub keymap for testing.") - -;; Stub expand-region package -(provide 'expand-region) +;; Stub dependencies before loading the module. `eval-and-compile` is required +;; because the byte-compile pass `require`s custom-line-paragraph, which runs +;; the module's top-level `(keymap-set cj/custom-keymap ...)` form at load +;; time. A bare `defvar` here would only declare the symbol at compile time; +;; the keymap-set then sees a void value. +(eval-and-compile + (defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.") + (provide 'expand-region)) ;; Now load the actual production module (require 'custom-line-paragraph) @@ -84,8 +87,10 @@ (should (string-match-p "line one line two line three" (buffer-string)))) (test-join-line-or-region-teardown))) -(ert-deftest test-join-line-or-region-with-region-adds-newline-at-end () - "With region, should add newline at end." +(ert-deftest test-join-line-or-region-with-region-no-newline-added-at-end () + "Region branch must not add a trailing newline. The function lands point on +the next existing line via `forward-line 1`, so an input without a trailing +newline produces output without one." (test-join-line-or-region-setup) (unwind-protect (with-temp-buffer @@ -95,7 +100,7 @@ (goto-char (point-max)) (activate-mark) (cj/join-line-or-region) - (should (string-suffix-p "\n" (buffer-string)))) + (should (string= "line one line two line three" (buffer-string)))) (test-join-line-or-region-teardown))) (ert-deftest test-join-line-or-region-preserves-text-content () @@ -138,6 +143,70 @@ (should (string-match-p "two three four" (buffer-string)))) (test-join-line-or-region-teardown))) +(ert-deftest test-join-line-or-region-with-region-no-stray-space-on-following-line () + "Normal: joining a multi-line region must not leave a stray space at the start +of the line below the region. Regression: the original `while`-based loop ran +one iteration too many, so the final `join-line 1` reached past the region and +replaced the trailing newline of the region with a space, then `goto-char end` ++ `newline` reinserted the newline before that space, stranding it at BOL of +the next line." + (test-join-line-or-region-setup) + (unwind-protect + (with-temp-buffer + (insert "- /Confidence calibration/ Every source declares its\n" + " own confidence in [0..1], and those numbers don't mean the same\n" + " thing. Calibrating across sources is a downstream-of-schema\n" + " problem.\n" + "- /Detection-to-detection links/ within a single sensor scene.\n") + (goto-char (point-min)) + (set-mark (point)) + (search-forward "problem.") + (activate-mark) + (cj/join-line-or-region) + (goto-char (point-min)) + (search-forward "- /Detection") + (beginning-of-line) + ;; The next-bullet line must begin with the dash, not a leading space. + (should (eq (char-after) ?-))) + (test-join-line-or-region-teardown))) + +(ert-deftest test-join-line-or-region-with-region-cursor-lands-on-next-existing-line () + "Normal: after joining a multi-line region, point should land at BOL of the +existing line immediately after the region β no blank line inserted between." + (test-join-line-or-region-setup) + (unwind-protect + (with-temp-buffer + (insert "alpha line one\n" + " alpha line two\n" + " alpha line three\n" + "beta line\n") + (goto-char (point-min)) + (set-mark (point)) + (search-forward "alpha line three") + (activate-mark) + (cj/join-line-or-region) + (should (looking-at-p "beta line"))) + (test-join-line-or-region-teardown))) + +(ert-deftest test-join-line-or-region-with-region-no-blank-gap-after-paragraph () + "Normal: the joined paragraph must abut the next existing line directly. The +exact buffer shape is asserted to catch both the leading-space regression and +any spurious blank line from a residual trailing `(newline)`." + (test-join-line-or-region-setup) + (unwind-protect + (with-temp-buffer + (insert "alpha one\n" + " alpha two\n" + "beta\n") + (goto-char (point-min)) + (set-mark (point)) + (search-forward "alpha two") + (activate-mark) + (cj/join-line-or-region) + (should (string= (buffer-string) + "alpha one alpha two\nbeta\n"))) + (test-join-line-or-region-teardown))) + ;;; Boundary Cases (ert-deftest test-join-line-or-region-on-first-line-no-region-does-nothing-except-newline () @@ -166,7 +235,8 @@ (test-join-line-or-region-teardown))) (ert-deftest test-join-line-or-region-single-line-region () - "Should handle single-line region." + "Should handle single-line region. Region branch performs zero joins and +moves point to the next existing line; the buffer is left untouched." (test-join-line-or-region-setup) (unwind-protect (with-temp-buffer @@ -176,7 +246,7 @@ (goto-char (point-max)) (activate-mark) (cj/join-line-or-region) - (should (string= "only line\n" (buffer-string)))) + (should (string= "only line" (buffer-string)))) (test-join-line-or-region-teardown))) (ert-deftest test-join-line-or-region-region-with-only-whitespace-lines () diff --git a/tests/test-custom-line-paragraph-join-paragraph.el b/tests/test-custom-line-paragraph-join-paragraph.el index a84adc6c..9f68b3b1 100644 --- a/tests/test-custom-line-paragraph-join-paragraph.el +++ b/tests/test-custom-line-paragraph-join-paragraph.el @@ -40,9 +40,14 @@ ;; Add expand-region to load path explicitly (add-to-list 'load-path (expand-file-name "elpa/expand-region-1.0.0" user-emacs-directory)) -;; Stub dependencies before loading the module -(defvar cj/custom-keymap (make-sparse-keymap) - "Stub keymap for testing.") +;; Stub dependencies before loading the module. `eval-and-compile` is required +;; because the byte-compile pass `require`s custom-line-paragraph, which runs +;; the module's top-level `(keymap-set cj/custom-keymap ...)` form at load +;; time. A bare `defvar` here would only declare the symbol at compile time; +;; the keymap-set then sees a void value. +(eval-and-compile + (defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.")) ;; Load expand-region for real (needed by cj/join-paragraph) (require 'expand-region) @@ -73,7 +78,7 @@ (goto-char (point-min)) (cj/join-paragraph) (should (string= (buffer-substring-no-properties (point-min) (point-max)) - "line one line two line three\n"))) + "line one line two line three"))) (test-join-paragraph-teardown))) (ert-deftest test-join-paragraph-simple-multiline-cursor-in-middle () @@ -87,7 +92,7 @@ (forward-line 1) (cj/join-paragraph) (should (string= (buffer-substring-no-properties (point-min) (point-max)) - "line one line two line three\n"))) + "line one line two line three"))) (test-join-paragraph-teardown))) (ert-deftest test-join-paragraph-simple-multiline-cursor-at-end () @@ -100,7 +105,7 @@ (goto-char (point-max)) (cj/join-paragraph) (should (string= (buffer-substring-no-properties (point-min) (point-max)) - "line one line two line three\n"))) + "line one line two line three"))) (test-join-paragraph-teardown))) (ert-deftest test-join-paragraph-surrounded-by-other-paragraphs () @@ -134,7 +139,7 @@ (goto-char (point-min)) (cj/join-paragraph) (should (string= (buffer-substring-no-properties (point-min) (point-max)) - " indented line one indented line two indented line three\n"))) + " indented line one indented line two indented line three"))) (test-join-paragraph-teardown))) (ert-deftest test-join-paragraph-cursor-position-after () @@ -206,7 +211,7 @@ (cj/join-paragraph) ;; Should still work, even if nothing to join (should (string= (buffer-substring-no-properties (point-min) (point-max)) - "single line paragraph\n"))) + "single line paragraph"))) (test-join-paragraph-teardown))) (ert-deftest test-join-paragraph-at-buffer-start () @@ -230,7 +235,7 @@ (insert "other paragraph\n\nfirst line\nsecond line\nthird line") (goto-char (point-max)) (cj/join-paragraph) - (should (string-match-p "first line second line third line\n$" (buffer-string)))) + (should (string-match-p "first line second line third line\\'" (buffer-string)))) (test-join-paragraph-teardown))) (ert-deftest test-join-paragraph-very-long () @@ -288,7 +293,7 @@ (goto-char (point-min)) (cj/join-paragraph) (should (string= (buffer-substring-no-properties (point-min) (point-max)) - "line one line two line three\n"))) + "line one line two line three"))) (test-join-paragraph-teardown))) (ert-deftest test-join-paragraph-only-whitespace-lines () @@ -314,7 +319,7 @@ (goto-char (point-min)) (cj/join-paragraph) (should (string= (buffer-substring-no-properties (point-min) (point-max)) - "Hello π world γγγ«γ‘γ― δΈη π celebration π\n"))) + "Hello π world γγγ«γ‘γ― δΈη π celebration π"))) (test-join-paragraph-teardown))) ;; ---------------------------- Error Cases ------------------------------------ @@ -353,7 +358,7 @@ (goto-char (point-min)) (cj/join-paragraph) (should (string= (buffer-substring-no-properties (point-min) (point-max)) - "x\n"))) + "x"))) (test-join-paragraph-teardown))) (provide 'test-custom-line-paragraph-join-paragraph) |
