diff options
| author | Jens Schmidt | 2025-12-17 22:20:15 +0100 |
|---|---|---|
| committer | Stefan Monnier | 2025-12-20 13:23:29 -0500 |
| commit | 0aabe62b64b4698340e8414d28b0fa0a3eabbf82 (patch) | |
| tree | b0dcd8b62c796fcc0a08f68b0b17dd2591ae7b3c /test/src | |
| parent | 875e42d501df262fcd9903528657997d025e5c68 (diff) | |
| download | emacs-0aabe62b64b4698340e8414d28b0fa0a3eabbf82.tar.gz emacs-0aabe62b64b4698340e8414d28b0fa0a3eabbf82.zip | |
Improve handling of non-ASCII characters in 'transpose-regions'
* src/editfns.c (Ftranspose_regions): Separate code related to character
semantics from that related to byte semantics and in that way leverage
optimizations for regions of equal length with respect to both
semantics. Move and update comments dating back to the initial
implementation.
* test/src/editfns-tests.el (editfns-tests--transpose-regions-tests)
(editfns-tests--transpose-regions-markups)
(editfns-tests--transpose-regions): New test and accompanying variables.
Diffstat (limited to 'test/src')
| -rw-r--r-- | test/src/editfns-tests.el | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index 2fce2315edb..4e0ca4c9d2a 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el | |||
| @@ -190,6 +190,210 @@ | |||
| 190 | (should (equal-including-properties | 190 | (should (equal-including-properties |
| 191 | str1 (buffer-substring (+ (point-min) 5) (+ (point-min) 7))))))) | 191 | str1 (buffer-substring (+ (point-min) 5) (+ (point-min) 7))))))) |
| 192 | 192 | ||
| 193 | (defconst editfns-tests--transpose-regions-tests | ||
| 194 | '(;; adjacent regions with one being empty | ||
| 195 | ("" "foo" "" "" "" [0 3 0 0 0]) | ||
| 196 | ("" "" "" "baz" "" [0 0 0 3 0]) | ||
| 197 | |||
| 198 | ;; For the following tests, assume that characters from the range | ||
| 199 | ;; [a-z] are 1 byte long in Emacs's internal text representation, | ||
| 200 | ;; while LATIN SMALL LETTER [AO] WITH DIAERESIS is 2 bytes long. | ||
| 201 | |||
| 202 | ;; (len1 == len2) && (end1 == start2) && (len1_byte == len2_byte) | ||
| 203 | ("" "fo(o" "" "b)az" "" [0 3 0 3 0]) | ||
| 204 | ;; (len1 == len2) && (end1 != start2) && (len1_byte == len2_byte) | ||
| 205 | ("" "fo(o" "[bar]" "b)az" "" [0 3 3 3 0]) | ||
| 206 | |||
| 207 | ;; (len1 != len2) && (end1 != start2) && (len1_byte < len2_byte) | ||
| 208 | ("" "fo(o" "[bar]" "baaz)" "" [0 3 3 4 0]) | ||
| 209 | ;; (len1 != len2) && (end1 != start2) && (len1_byte > len2_byte) | ||
| 210 | ("" "(fooo" "[bar]" "baz)" "" [0 4 3 3 0]) | ||
| 211 | |||
| 212 | ;; (len1 == len2) && (end1 == start2) && (len1_byte < len2_byte) | ||
| 213 | ("" "fo(o" "" "b)äz" "" [0 3 0 4 0]) | ||
| 214 | ;; (len1 == len2) && (end1 == start2) && (len1_byte > len2_byte) | ||
| 215 | ("" "fo(ö" "" "b)az" "" [0 4 0 3 0]) | ||
| 216 | ;; (len1 == len2) && (end1 != start2) && (len1_byte > len2_byte) | ||
| 217 | ("" "fo(o" "[bar]" "b)äz" "" [0 3 3 4 0]) | ||
| 218 | ;; (len1 == len2) && (end1 != start2) && (len1_byte > len2_byte) | ||
| 219 | ("" "fo(ö" "[bar]" "b)az" "" [0 4 3 3 0]) | ||
| 220 | |||
| 221 | ;; (len1 != len2) && (end1 == start2) && (len1_byte == len2_byte) | ||
| 222 | ("" "fo(ö" "" "baaz)" "" [0 4 0 4 0]) | ||
| 223 | ;; (len1 != len2) && (end1 == start2) && (len1_byte == len2_byte) | ||
| 224 | ("" "(fooo" "" "bäz)" "" [0 4 0 4 0]) | ||
| 225 | ;; (len1 != len2) && (end1 != start2) && (len1_byte == len2_byte) | ||
| 226 | ("" "fo(ö" "[bar]" "baaz)" "" [0 4 3 4 0]) | ||
| 227 | ;; (len1 != len2) && (end1 != start2) && (len1_byte == len2_byte) | ||
| 228 | ("" "(fooo" "[bar]" "bäz)" "" [0 4 3 4 0]) | ||
| 229 | |||
| 230 | ;; Going entirely non-ASCII. Assume plain greek small letters are | ||
| 231 | ;; two bytes long in Emacs's internal text representation, GREEK | ||
| 232 | ;; SMALL LETTER ALPHA WITH PSILI is three bytes long. | ||
| 233 | |||
| 234 | ;; To cover the initial patch from bug#70122, define a test | ||
| 235 | ;; consisting of three three-letter strings REG1 MID REG2, with | ||
| 236 | ;; (length REG1) == (length REG2) but (byte-length REG1) != | ||
| 237 | ;; (byte-length REG2) ... | ||
| 238 | ("ἀ(ρχή" "φ[ωω" "β){αρ" "β<ἀ]ζ}" "τέλ>ος" [9 6 6 7 10]) | ||
| 239 | ;; ... and a test with (length REG1) == (length REG2) and | ||
| 240 | ;; (byte-length REG1) == (byte-length REG2). | ||
| 241 | ("ἀ(ρχή" "φ[ωω" "β){αρ" "β<α]ζ}" "τέλ>ος" [9 6 6 6 10]) | ||
| 242 | |||
| 243 | ;; Define the moral equivalent of | ||
| 244 | ;; `editfns-tests--transpose-equal-but-not'. | ||
| 245 | (" " "(ab)" "[SPC]" "{é}" " " [1 2 3 2 1]) | ||
| 246 | |||
| 247 | ;; Likewise, for the testcase from bug#70122 in | ||
| 248 | ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=70122#5. | ||
| 249 | ("" "" "(a):\n[b]: \x2113\x2080\n" "{v}: scaling" "" [0 0 13 10 0]) | ||
| 250 | |||
| 251 | ;; Likewise, for the testcase from bug#70122 in | ||
| 252 | ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=70122#52. | ||
| 253 | ("(Query replace (default abc → d): )" "abc" "[ → ]" "d" "" [35 3 5 1 0])) | ||
| 254 | "List of test strings and their markup to test `transpose-regions'. | ||
| 255 | Each element of this list should be a list | ||
| 256 | |||
| 257 | HEAD REG1 MID REG2 TAIL BYTE-LENGTHS | ||
| 258 | |||
| 259 | where the first five elements are (possibly empty) string snippets and | ||
| 260 | the sixth element is a five-element vector providing the lengths of the | ||
| 261 | string snippets, counted in bytes in Emacs's internal text | ||
| 262 | representation. | ||
| 263 | |||
| 264 | Test `editfns-tests--transpose-regions' inserts the five snippets into | ||
| 265 | its temporary buffer, adds text properties to them as described for | ||
| 266 | variable `editfns-tests--transpose-regions-markups', transposes REG1 and | ||
| 267 | REG2, probably undoes the change, and at each stage ensures that all | ||
| 268 | involved entities look as expected.") | ||
| 269 | |||
| 270 | (defconst editfns-tests--transpose-regions-markups | ||
| 271 | '("()" "[]" "{}" "<>") | ||
| 272 | "List of two-characters strings \"BE\" describing text property markup. | ||
| 273 | For each element in this list, test `editfns-tests--transpose-regions' | ||
| 274 | searches once for regular expression \"B.+E\" in its temporary buffer, | ||
| 275 | adds a text property `markup' with value \"BE\" to the matching text, | ||
| 276 | and then removes the markup characters B and E around the matching text. | ||
| 277 | |||
| 278 | The test searches in the buffer with all test snippets already inserted, | ||
| 279 | so characters B and E can originate from different snippets, and the | ||
| 280 | various B's and E's of different markup items do not need to nest.") | ||
| 281 | |||
| 282 | (ert-deftest editfns-tests--transpose-regions () | ||
| 283 | "Test function `transpose-regions'. | ||
| 284 | Execute tests as described by `editfns-tests--transpose-regions-tests'." | ||
| 285 | (dolist (test editfns-tests--transpose-regions-tests) | ||
| 286 | (dolist (leave-markers '(nil t)) | ||
| 287 | (message "test: %S leave-markers: %S" test leave-markers) | ||
| 288 | (with-temp-buffer | ||
| 289 | (let ((test (take 5 test)) | ||
| 290 | (blengthv (nth 5 test)) | ||
| 291 | (smarkers nil) ; Separator markers. | ||
| 292 | (pmarkers nil) ; Property markers. | ||
| 293 | (pmpos nil) ; Their positions before transposing. | ||
| 294 | (strings nil) ; Net text snippets, propertized. | ||
| 295 | (blengths nil) ; Their lengths in bytes. | ||
| 296 | (tstrings nil) ; Net text snippets with REG1/2 transposed. | ||
| 297 | (test-undo nil) | ||
| 298 | p beg end beg2 end2) | ||
| 299 | (buffer-enable-undo) | ||
| 300 | ;; Insert text snippets. While doing so, create the separator | ||
| 301 | ;; markers which we need later to determine the net text | ||
| 302 | ;; snippets. | ||
| 303 | (cl-assert (eq (length test) 5)) | ||
| 304 | (setq p test) | ||
| 305 | (while (cdr p) | ||
| 306 | (insert (car p)) | ||
| 307 | (push (point-marker) smarkers) | ||
| 308 | (setq p (cdr p))) | ||
| 309 | (insert (car p)) | ||
| 310 | (setq smarkers (nreverse smarkers)) | ||
| 311 | ;; Propertize them according to markup, remove markup | ||
| 312 | ;; characters, add property markers. | ||
| 313 | (dolist (markup editfns-tests--transpose-regions-markups) | ||
| 314 | (cl-assert (eq (length markup) 2)) | ||
| 315 | (goto-char (point-min)) | ||
| 316 | (when (search-forward-regexp | ||
| 317 | (concat "\\(" | ||
| 318 | (regexp-quote (substring markup 0 1)) | ||
| 319 | ".+" | ||
| 320 | (regexp-quote (substring markup 1 2)) | ||
| 321 | "\\)") | ||
| 322 | nil t) | ||
| 323 | (setq beg (copy-marker (match-beginning 1)) | ||
| 324 | end (copy-marker (match-end 1))) | ||
| 325 | (delete-region beg (1+ beg)) | ||
| 326 | (delete-region (1- end) end) | ||
| 327 | (add-text-properties beg end (list 'markup markup)) | ||
| 328 | (push beg pmarkers) | ||
| 329 | (push end pmarkers))) | ||
| 330 | (setq pmarkers (sort pmarkers) | ||
| 331 | pmpos (mapcar #'marker-position pmarkers)) | ||
| 332 | ;; Determine net text snippets, plain and with transposed REG1 | ||
| 333 | ;; and REG2. Determine the byte lengths of the net text | ||
| 334 | ;; snippets and ensure they meet our expectation. | ||
| 335 | (setq p smarkers | ||
| 336 | beg (point-min)) | ||
| 337 | (while p | ||
| 338 | (push (buffer-substring beg (car p)) strings) | ||
| 339 | (push (- (position-bytes (car p)) (position-bytes beg)) | ||
| 340 | blengths) | ||
| 341 | (setq beg (car p) p (cdr p))) | ||
| 342 | (push (buffer-substring beg (point-max)) strings) | ||
| 343 | (push (- (position-bytes (point-max)) (position-bytes beg)) | ||
| 344 | blengths) | ||
| 345 | (setq strings (nreverse strings) | ||
| 346 | blengths (nreverse blengths)) | ||
| 347 | (setq tstrings (list (nth 0 strings) (nth 3 strings) | ||
| 348 | (nth 2 strings) (nth 1 strings) | ||
| 349 | (nth 4 strings))) | ||
| 350 | (should (equal blengthv (apply #'vector blengths))) | ||
| 351 | ;; Transpose REG1 and REG2. Some transpositions might not | ||
| 352 | ;; generate undo, keep track of that in flag `test-undo'. | ||
| 353 | (setq beg (+ 1 (length (nth 0 strings))) | ||
| 354 | end (+ beg (length (nth 1 strings))) | ||
| 355 | beg2 (+ end (length (nth 2 strings))) | ||
| 356 | end2 (+ beg2 (length (nth 3 strings)))) | ||
| 357 | (undo-boundary) | ||
| 358 | (transpose-regions beg end beg2 end2 leave-markers) | ||
| 359 | (when (car buffer-undo-list) | ||
| 360 | (setq test-undo t)) | ||
| 361 | (undo-boundary) | ||
| 362 | ;; Check resulting buffer text and its properties. | ||
| 363 | (should (equal-including-properties | ||
| 364 | (buffer-string) | ||
| 365 | (mapconcat #'identity tstrings))) | ||
| 366 | ;; Check property marker positions. | ||
| 367 | (if leave-markers | ||
| 368 | (should (equal (mapcar #'marker-position pmarkers) pmpos)) | ||
| 369 | ;; Meh. This more or less blindly duplicates function | ||
| 370 | ;; transpose_markers, since I have been too lazy to | ||
| 371 | ;; reproduce the arithmetics myself. | ||
| 372 | (setq pmpos | ||
| 373 | (mapcar | ||
| 374 | (lambda (pos) | ||
| 375 | (cond | ||
| 376 | ((< pos beg) pos) | ||
| 377 | ((>= pos end2) pos) | ||
| 378 | ((< pos end) (+ pos (+ (- end2 beg2) (- beg2 end)))) | ||
| 379 | ((< pos beg2) (+ pos (- (- end2 beg2) (- end beg)))) | ||
| 380 | (t (- pos (+ (- end beg) (- beg2 end)))))) | ||
| 381 | pmpos)) | ||
| 382 | (should (equal (mapcar #'marker-position pmarkers) pmpos))) | ||
| 383 | ;; Undo the transposition and check text and properties again, | ||
| 384 | ;; if needed. This does not undo any marker transpositions as | ||
| 385 | ;; per the comment before the call to transpose_markers in | ||
| 386 | ;; Ftranspose_regions, so nothing to check on the marker side | ||
| 387 | ;; after the undo. | ||
| 388 | (when test-undo | ||
| 389 | (undo) | ||
| 390 | (should (equal-including-properties | ||
| 391 | (buffer-string) | ||
| 392 | (mapconcat #'identity strings)))) | ||
| 393 | ;; Be nice and clean up markers. | ||
| 394 | (dolist (marker smarkers) (set-marker marker nil)) | ||
| 395 | (dolist (marker pmarkers) (set-marker marker nil))))))) | ||
| 396 | |||
| 193 | (ert-deftest format-c-float () | 397 | (ert-deftest format-c-float () |
| 194 | (should-error (format "%c" 0.5))) | 398 | (should-error (format "%c" 0.5))) |
| 195 | 399 | ||