X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/d8e9122115b5ffcec342c841b81cb2d2b8217e4b..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;