X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/32a2c91658bd02c4e761030f93eb5f0415524104..00b6647651e4276ac5c47aa33e0fec6726469bc7:/src/insdel.c
diff --git a/src/insdel.c b/src/insdel.c
index 80650be25a..ec7bbb3e71 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -1,13 +1,13 @@
/* Buffer insertion/deletion and gap motion for GNU Emacs.
- Copyright (C) 1985-1986, 1993-1995, 1997-2015 Free Software
+ Copyright (C) 1985-1986, 1993-1995, 1997-2016 Free Software
Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -23,11 +23,11 @@ along with GNU Emacs. If not, see . */
#include
#include "lisp.h"
+#include "composite.h"
#include "intervals.h"
#include "character.h"
#include "buffer.h"
#include "window.h"
-#include "blockinput.h"
#include "region-cache.h"
static void insert_from_string_1 (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t,
@@ -126,7 +126,10 @@ gap_left (ptrdiff_t charpos, ptrdiff_t bytepos, bool newgap)
if (i == 0)
break;
/* If a quit is requested, stop copying now.
- Change BYTEPOS to be where we have actually moved the gap to. */
+ Change BYTEPOS to be where we have actually moved the gap to.
+ Note that this cannot happen when we are called to make the
+ gap larger or smaller, since make_gap_larger and
+ make_gap_smaller prevent QUIT by setting inhibit-quit. */
if (QUITP)
{
bytepos = new_s1;
@@ -179,7 +182,10 @@ gap_right (ptrdiff_t charpos, ptrdiff_t bytepos)
if (i == 0)
break;
/* If a quit is requested, stop copying now.
- Change BYTEPOS to be where we have actually moved the gap to. */
+ Change BYTEPOS to be where we have actually moved the gap to.
+ Note that this cannot happen when we are called to make the
+ gap larger or smaller, since make_gap_larger and
+ make_gap_smaller prevent QUIT by setting inhibit-quit. */
if (QUITP)
{
bytepos = new_s1;
@@ -358,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)
@@ -386,7 +464,9 @@ make_gap_larger (ptrdiff_t nbytes_added)
enlarge_buffer_text (current_buffer, nbytes_added);
- /* Prevent quitting in move_gap. */
+ /* Prevent quitting in gap_left. We cannot allow a QUIT there,
+ because that would leave the buffer text in an inconsistent
+ state, with 2 gap holes instead of just one. */
tem = Vinhibit_quit;
Vinhibit_quit = Qt;
@@ -432,7 +512,9 @@ make_gap_smaller (ptrdiff_t nbytes_removed)
if (GAP_SIZE - nbytes_removed < GAP_BYTES_MIN)
nbytes_removed = GAP_SIZE - GAP_BYTES_MIN;
- /* Prevent quitting in move_gap. */
+ /* Prevent quitting in gap_right. We cannot allow a QUIT there,
+ because that would leave the buffer text in an inconsistent
+ state, with 2 gap holes instead of just one. */
tem = Vinhibit_quit;
Vinhibit_quit = Qt;
@@ -892,7 +974,6 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
ptrdiff_t nchars, ptrdiff_t nbytes,
bool inherit, bool before_markers)
{
- struct gcpro gcpro1;
ptrdiff_t outgoing_nbytes = nbytes;
INTERVAL intervals;
@@ -906,7 +987,6 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
= count_size_as_multibyte (SDATA (string) + pos_byte,
nbytes);
- GCPRO1 (string);
/* Do this before moving and increasing the gap,
because the before-change hooks might move the gap
or make it smaller. */
@@ -916,7 +996,6 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
move_gap_both (PT, PT_BYTE);
if (GAP_SIZE < outgoing_nbytes)
make_gap (outgoing_nbytes - GAP_SIZE);
- UNGCPRO;
/* Copy the string text into the buffer, perhaps converting
between single-byte and multibyte. */
@@ -1278,14 +1357,12 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
ptrdiff_t insbytes = SBYTES (new);
ptrdiff_t from_byte, to_byte;
ptrdiff_t nbytes_del, nchars_del;
- struct gcpro gcpro1;
INTERVAL intervals;
ptrdiff_t outgoing_insbytes = insbytes;
Lisp_Object deletion;
check_markers ();
- GCPRO1 (new);
deletion = Qnil;
if (prepare)
@@ -1295,8 +1372,6 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
to = from + range_length;
}
- UNGCPRO;
-
/* Make args be valid. */
if (from < BEGV)
from = BEGV;
@@ -1321,8 +1396,6 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
outgoing_insbytes
= count_size_as_multibyte (SDATA (new), insbytes);
- GCPRO1 (new);
-
/* Make sure the gap is somewhere in or next to what we are deleting. */
if (from > GPT)
gap_right (from, from_byte);
@@ -1396,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. */
@@ -1424,7 +1507,6 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
MODIFF++;
CHARS_MODIFF = MODIFF;
- UNGCPRO;
signal_after_change (from, nchars_del, GPT - from);
update_compositions (from, GPT, CHECK_BORDER);
@@ -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. */
@@ -1561,7 +1655,6 @@ del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
{
ptrdiff_t from_byte, to_byte;
Lisp_Object deletion;
- struct gcpro gcpro1;
/* Make args be valid */
if (from < BEGV)
@@ -1583,10 +1676,8 @@ del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
to_byte = CHAR_TO_BYTE (to);
deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
- GCPRO1 (deletion);
signal_after_change (from, to - from, 0);
update_compositions (from, from, CHECK_HEAD);
- UNGCPRO;
return deletion;
}
@@ -1778,6 +1869,18 @@ modify_text (ptrdiff_t start, ptrdiff_t end)
bset_point_before_scroll (current_buffer, Qnil);
}
+/* Signal that we are about to make a change that may result in new
+ undo information.
+ */
+static void
+run_undoable_change (void)
+{
+ if (EQ (BVAR (current_buffer, undo_list), Qt))
+ return;
+
+ call0 (Qundo_auto__undoable_change);
+}
+
/* Check that it is okay to modify the buffer between START and END,
which are char positions.
@@ -1786,7 +1889,12 @@ modify_text (ptrdiff_t start, ptrdiff_t end)
any modification properties the text may have.
If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
- by holding its value temporarily in a marker. */
+ by holding its value temporarily in a marker.
+
+ This function runs Lisp, which means it can GC, which means it can
+ compact buffers, including the current buffer being worked on here.
+ So don't you dare calling this function while manipulating the gap,
+ or during some other similar "critical section". */
void
prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
@@ -1799,6 +1907,8 @@ prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
if (!NILP (BVAR (current_buffer, read_only)))
Fbarf_if_buffer_read_only (temp);
+ run_undoable_change();
+
bset_redisplay (current_buffer);
if (buffer_intervals (current_buffer))
@@ -1806,13 +1916,10 @@ prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
if (preserve_ptr)
{
Lisp_Object preserve_marker;
- struct gcpro gcpro1;
preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil);
- GCPRO1 (preserve_marker);
verify_interval_modification (current_buffer, start, end);
*preserve_ptr = marker_position (preserve_marker);
unchain_marker (XMARKER (preserve_marker));
- UNGCPRO;
}
else
verify_interval_modification (current_buffer, start, end);
@@ -1846,7 +1953,7 @@ prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
= call1 (Fsymbol_value (Qregion_extract_function), Qnil);
signal_before_change (start, end, preserve_ptr);
- Vdeactivate_mark = Qt;
+ Fset (Qdeactivate_mark, Qt);
}
/* Like above, but called when we know that the buffer text
@@ -1970,7 +2077,6 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
Lisp_Object start, end;
Lisp_Object start_marker, end_marker;
Lisp_Object preserve_marker;
- struct gcpro gcpro1, gcpro2, gcpro3;
ptrdiff_t count = SPECPDL_INDEX ();
struct rvoe_arg rvoe_arg;
@@ -1979,7 +2085,6 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
preserve_marker = Qnil;
start_marker = Qnil;
end_marker = Qnil;
- GCPRO3 (preserve_marker, start_marker, end_marker);
specbind (Qinhibit_modification_hooks, Qt);
@@ -2025,7 +2130,6 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
if (! NILP (end_marker))
free_marker (end_marker);
RESTORE_VALUE;
- UNGCPRO;
unbind_to (count, Qnil);
}
@@ -2206,6 +2310,8 @@ syms_of_insdel (void)
combine_after_change_list = Qnil;
combine_after_change_buffer = Qnil;
+ DEFSYM (Qundo_auto__undoable_change, "undo-auto--undoable-change");
+
DEFVAR_LISP ("combine-after-change-calls", Vcombine_after_change_calls,
doc: /* Used internally by the function `combine-after-change-calls' macro. */);
Vcombine_after_change_calls = Qnil;