summaryrefslogtreecommitdiff
path: root/src/insdel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/insdel.c')
-rw-r--r--src/insdel.c137
1 files changed, 115 insertions, 22 deletions
diff --git a/src/insdel.c b/src/insdel.c
index fc3f19fd581..ed914ec6f75 100644
--- a/src/insdel.c
+++ b/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)
@@ -1400,6 +1472,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. */
@@ -1515,10 +1597,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. */
@@ -1596,7 +1690,7 @@ del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
/* Like del_range_1 but args are byte positions, not char positions. */
void
-del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte, bool prepare)
+del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte)
{
ptrdiff_t from, to;
@@ -1612,23 +1706,22 @@ del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte, bool prepare)
from = BYTE_TO_CHAR (from_byte);
to = BYTE_TO_CHAR (to_byte);
- if (prepare)
- {
- ptrdiff_t old_from = from, old_to = Z - to;
- ptrdiff_t range_length = to - from;
- prepare_to_modify_buffer (from, to, &from);
- to = from + range_length;
-
- if (old_from != from)
- from_byte = CHAR_TO_BYTE (from);
- if (to > ZV)
- {
- to = ZV;
- to_byte = ZV_BYTE;
- }
- else if (old_to == Z - to)
- to_byte = CHAR_TO_BYTE (to);
- }
+ {
+ ptrdiff_t old_from = from, old_to = Z - to;
+ ptrdiff_t range_length = to - from;
+ prepare_to_modify_buffer (from, to, &from);
+ to = from + range_length;
+
+ if (old_from != from)
+ from_byte = CHAR_TO_BYTE (from);
+ if (to > ZV)
+ {
+ to = ZV;
+ to_byte = ZV_BYTE;
+ }
+ else if (old_to == Z - to)
+ to_byte = CHAR_TO_BYTE (to);
+ }
del_range_2 (from, from_byte, to, to_byte, 0);
signal_after_change (from, to - from, 0);