Fix 'transpose-regions' when LEAVE-MARKERS arg is non-nil
* src/insdel.c (adjust_markers_bytepos): New function. * src/lisp.h (adjust_markers_bytepos): Add prototype. * src/insdel.c (replace_range, replace_range_2): * src/editfns.c (Ftranspose_regions): Call adjust_markers_bytepos. (Bug#5131) * test/src/editfns-tests.el (transpose-test-reverse-word) (transpose-test-get-byte-positions): New functions. (transpose-ascii-regions-test) (transpose-nonascii-regions-test-1) (transpose-nonascii-regions-test-2): New tests.
This commit is contained in:
parent
439f3c3e56
commit
00b6647651
4 changed files with 153 additions and 4 deletions
|
@ -5058,6 +5058,14 @@ Transposing beyond buffer boundaries is an error. */)
|
|||
start2_byte, start2_byte + len2_byte);
|
||||
fix_start_end_in_overlays (start1, end2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The character positions of the markers remain intact, but we
|
||||
still need to update their byte positions, because the
|
||||
transposed regions might include multibyte sequences which
|
||||
make some original byte positions of the markers invalid. */
|
||||
adjust_markers_bytepos (start1, start1_byte, end2, end2_byte, 0);
|
||||
}
|
||||
|
||||
signal_after_change (start1, end2 - start1, end2 - start1);
|
||||
return Qnil;
|
||||
|
|
102
src/insdel.c
102
src/insdel.c
|
@ -364,6 +364,78 @@ adjust_markers_for_replace (ptrdiff_t from, ptrdiff_t from_byte,
|
|||
check_markers ();
|
||||
}
|
||||
|
||||
/* Starting at POS (BYTEPOS), find the byte position corresponding to
|
||||
ENDPOS, which could be either before or after POS. */
|
||||
static ptrdiff_t
|
||||
count_bytes (ptrdiff_t pos, ptrdiff_t bytepos, ptrdiff_t endpos)
|
||||
{
|
||||
eassert (BEG_BYTE <= bytepos && bytepos <= Z_BYTE
|
||||
&& BEG <= endpos && endpos <= Z);
|
||||
|
||||
if (pos <= endpos)
|
||||
for ( ; pos < endpos; pos++)
|
||||
INC_POS (bytepos);
|
||||
else
|
||||
for ( ; pos > endpos; pos--)
|
||||
DEC_POS (bytepos);
|
||||
|
||||
return bytepos;
|
||||
}
|
||||
|
||||
/* Adjust byte positions of markers when their character positions
|
||||
didn't change. This is used in several places that replace text,
|
||||
but keep the character positions of the markers unchanged -- the
|
||||
byte positions could still change due to different numbers of bytes
|
||||
in the new text.
|
||||
|
||||
FROM (FROM_BYTE) and TO (TO_BYTE) specify the region of text where
|
||||
changes have been done. TO_Z, if non-zero, means all the markers
|
||||
whose positions are after TO should also be adjusted. */
|
||||
void
|
||||
adjust_markers_bytepos (ptrdiff_t from, ptrdiff_t from_byte,
|
||||
ptrdiff_t to, ptrdiff_t to_byte, int to_z)
|
||||
{
|
||||
register struct Lisp_Marker *m;
|
||||
ptrdiff_t beg = from, begbyte = from_byte;
|
||||
|
||||
adjust_suspend_auto_hscroll (from, to);
|
||||
|
||||
if (Z == Z_BYTE || (!to_z && to == to_byte))
|
||||
{
|
||||
/* Make sure each affected marker's bytepos is equal to
|
||||
its charpos. */
|
||||
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
|
||||
{
|
||||
if (m->bytepos > from_byte
|
||||
&& (to_z || m->bytepos <= to_byte))
|
||||
m->bytepos = m->charpos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
|
||||
{
|
||||
/* Recompute each affected marker's bytepos. */
|
||||
if (m->bytepos > from_byte
|
||||
&& (to_z || m->bytepos <= to_byte))
|
||||
{
|
||||
if (m->charpos < beg
|
||||
&& beg - m->charpos > m->charpos - from)
|
||||
{
|
||||
beg = from;
|
||||
begbyte = from_byte;
|
||||
}
|
||||
m->bytepos = count_bytes (beg, begbyte, m->charpos);
|
||||
beg = m->charpos;
|
||||
begbyte = m->bytepos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure cached charpos/bytepos is invalid. */
|
||||
clear_charpos_cache (current_buffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
buffer_overflow (void)
|
||||
|
@ -1397,6 +1469,16 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
|
|||
if (markers)
|
||||
adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
|
||||
inschars, outgoing_insbytes);
|
||||
else
|
||||
{
|
||||
/* The character positions of the markers remain intact, but we
|
||||
still need to update their byte positions, because the
|
||||
deleted and the inserted text might have multibyte sequences
|
||||
which make the original byte positions of the markers
|
||||
invalid. */
|
||||
adjust_markers_bytepos (from, from_byte, from + inschars,
|
||||
from_byte + outgoing_insbytes, 1);
|
||||
}
|
||||
|
||||
/* Adjust the overlay center as needed. This must be done after
|
||||
adjusting the markers that bound the overlays. */
|
||||
|
@ -1509,10 +1591,22 @@ replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
|
|||
eassert (GPT <= GPT_BYTE);
|
||||
|
||||
/* Adjust markers for the deletion and the insertion. */
|
||||
if (markers
|
||||
&& ! (nchars_del == 1 && inschars == 1 && nbytes_del == insbytes))
|
||||
adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
|
||||
inschars, insbytes);
|
||||
if (! (nchars_del == 1 && inschars == 1 && nbytes_del == insbytes))
|
||||
{
|
||||
if (markers)
|
||||
adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
|
||||
inschars, insbytes);
|
||||
else
|
||||
{
|
||||
/* The character positions of the markers remain intact, but
|
||||
we still need to update their byte positions, because the
|
||||
deleted and the inserted text might have multibyte
|
||||
sequences which make the original byte positions of the
|
||||
markers invalid. */
|
||||
adjust_markers_bytepos (from, from_byte, from + inschars,
|
||||
from_byte + insbytes, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust the overlay center as needed. This must be done after
|
||||
adjusting the markers that bound the overlays. */
|
||||
|
|
|
@ -3528,6 +3528,8 @@ extern void adjust_after_insert (ptrdiff_t, ptrdiff_t, ptrdiff_t,
|
|||
ptrdiff_t, ptrdiff_t);
|
||||
extern void adjust_markers_for_delete (ptrdiff_t, ptrdiff_t,
|
||||
ptrdiff_t, ptrdiff_t);
|
||||
extern void adjust_markers_bytepos (ptrdiff_t, ptrdiff_t,
|
||||
ptrdiff_t, ptrdiff_t, int);
|
||||
extern void replace_range (ptrdiff_t, ptrdiff_t, Lisp_Object, bool, bool, bool);
|
||||
extern void replace_range_2 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t,
|
||||
const char *, ptrdiff_t, ptrdiff_t, bool);
|
||||
|
|
|
@ -89,3 +89,48 @@
|
|||
(propertize "23" 'face 'underline)
|
||||
(propertize "45" 'face 'italic)))
|
||||
#("012345 " 0 2 (face bold) 2 4 (face underline) 4 10 (face italic)))))
|
||||
|
||||
;; Tests for bug#5131.
|
||||
(defun transpose-test-reverse-word (start end)
|
||||
"Reverse characters in a word by transposing pairs of characters."
|
||||
(let ((begm (make-marker))
|
||||
(endm (make-marker)))
|
||||
(set-marker begm start)
|
||||
(set-marker endm end)
|
||||
(while (> endm begm)
|
||||
(progn (transpose-regions begm (1+ begm) endm (1+ endm) t)
|
||||
(set-marker begm (1+ begm))
|
||||
(set-marker endm (1- endm))))))
|
||||
|
||||
(defun transpose-test-get-byte-positions (len)
|
||||
"Validate character position to byte position translation."
|
||||
(let ((bytes '()))
|
||||
(dotimes (pos len)
|
||||
(setq bytes (add-to-list 'bytes (position-bytes (1+ pos)) t)))
|
||||
bytes))
|
||||
|
||||
(ert-deftest transpose-ascii-regions-test ()
|
||||
(with-temp-buffer
|
||||
(erase-buffer)
|
||||
(insert "abcd")
|
||||
(transpose-test-reverse-word 1 4)
|
||||
(should (string= (buffer-string) "dcba"))
|
||||
(should (equal (transpose-test-get-byte-positions 5) '(1 2 3 4 5)))))
|
||||
|
||||
(ert-deftest transpose-nonascii-regions-test-1 ()
|
||||
(with-temp-buffer
|
||||
(erase-buffer)
|
||||
(insert "÷bcd")
|
||||
(transpose-test-reverse-word 1 4)
|
||||
(should (string= (buffer-string) "dcb÷"))
|
||||
(should (equal (transpose-test-get-byte-positions 5) '(1 2 3 4 6)))))
|
||||
|
||||
(ert-deftest transpose-nonascii-regions-test-2 ()
|
||||
(with-temp-buffer
|
||||
(erase-buffer)
|
||||
(insert "÷ab\"äé")
|
||||
(transpose-test-reverse-word 1 6)
|
||||
(should (string= (buffer-string) "éä\"ba÷"))
|
||||
(should (equal (transpose-test-get-byte-positions 7) '(1 3 5 6 7 8 10)))))
|
||||
|
||||
;;; editfns-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue