root/src/insdel.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. check_markers
  2. move_gap_both
  3. gap_left
  4. gap_right
  5. adjust_suspend_auto_hscroll
  6. adjust_markers_for_delete
  7. adjust_markers_for_insert
  8. adjust_point
  9. adjust_markers_for_replace
  10. count_bytes
  11. adjust_markers_bytepos
  12. buffer_overflow
  13. make_gap_larger
  14. make_gap_smaller
  15. make_gap
  16. make_gap_1
  17. copy_text
  18. insert
  19. insert_and_inherit
  20. insert_char
  21. insert_string
  22. insert_before_markers
  23. insert_before_markers_and_inherit
  24. count_combining_before
  25. count_combining_after
  26. insert_1_both
  27. insert_from_string
  28. insert_from_string_before_markers
  29. insert_from_string_1
  30. insert_from_gap_1
  31. insert_from_gap
  32. insert_from_buffer
  33. insert_from_buffer_1
  34. adjust_after_replace
  35. adjust_after_insert
  36. replace_range
  37. replace_range_2
  38. del_range
  39. safe_del_range_1
  40. safe_del_range_2
  41. safe_del_range
  42. del_range_1
  43. del_range_byte
  44. del_range_both
  45. del_range_2
  46. modify_text
  47. run_undoable_change
  48. prepare_to_modify_buffer_1
  49. prepare_to_modify_buffer
  50. invalidate_buffer_caches
  51. reset_var_on_error
  52. signal_before_change
  53. signal_after_change
  54. Fcombine_after_change_execute_1
  55. DEFUN
  56. syms_of_insdel

     1 /* Buffer insertion/deletion and gap motion for GNU Emacs. -*- coding: utf-8 -*-
     2    Copyright (C) 1985-1986, 1993-1995, 1997-2023 Free Software
     3    Foundation, Inc.
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 
    21 #include <config.h>
    22 
    23 #include <intprops.h>
    24 
    25 #include "lisp.h"
    26 #include "composite.h"
    27 #include "intervals.h"
    28 #include "character.h"
    29 #include "buffer.h"
    30 #include "window.h"
    31 #include "region-cache.h"
    32 #include "pdumper.h"
    33 
    34 #ifdef HAVE_TREE_SITTER
    35 #include "treesit.h"
    36 #endif
    37 
    38 static void insert_from_string_1 (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t,
    39                                   ptrdiff_t, bool, bool);
    40 static void insert_from_buffer_1 (struct buffer *, ptrdiff_t, ptrdiff_t, bool);
    41 static void gap_left (ptrdiff_t, ptrdiff_t, bool);
    42 static void gap_right (ptrdiff_t, ptrdiff_t);
    43 
    44 /* List of elements of the form (BEG-UNCHANGED END-UNCHANGED CHANGE-AMOUNT)
    45    describing changes which happened while combine_after_change_calls
    46    was non-nil.  We use this to decide how to call them
    47    once the deferral ends.
    48 
    49    In each element.
    50    BEG-UNCHANGED is the number of chars before the changed range.
    51    END-UNCHANGED is the number of chars after the changed range,
    52    and CHANGE-AMOUNT is the number of characters inserted by the change
    53    (negative for a deletion).  */
    54 static Lisp_Object combine_after_change_list;
    55 
    56 /* Buffer which combine_after_change_list is about.  */
    57 static Lisp_Object combine_after_change_buffer;
    58 
    59 static void signal_before_change (ptrdiff_t, ptrdiff_t, ptrdiff_t *);
    60 
    61 /* Also used in marker.c to enable expensive marker checks.  */
    62 
    63 #ifdef MARKER_DEBUG
    64 
    65 static void
    66 check_markers (void)
    67 {
    68   struct Lisp_Marker *tail;
    69   bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters));
    70 
    71   for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
    72     {
    73       if (tail->buffer->text != current_buffer->text)
    74         emacs_abort ();
    75       if (tail->charpos > Z)
    76         emacs_abort ();
    77       if (tail->bytepos > Z_BYTE)
    78         emacs_abort ();
    79       if (multibyte && ! CHAR_HEAD_P (FETCH_BYTE (tail->bytepos)))
    80         emacs_abort ();
    81     }
    82 }
    83 
    84 #else /* not MARKER_DEBUG */
    85 
    86 #define check_markers() do { } while (0)
    87 
    88 #endif /* MARKER_DEBUG */
    89 
    90 /* Move gap to byte position BYTEPOS, which is also char position CHARPOS.
    91    Note that this can quit!  */
    92 
    93 void
    94 move_gap_both (ptrdiff_t charpos, ptrdiff_t bytepos)
    95 {
    96   eassert (charpos == BYTE_TO_CHAR (bytepos)
    97            && bytepos == CHAR_TO_BYTE (charpos));
    98   if (bytepos < GPT_BYTE)
    99     gap_left (charpos, bytepos, 0);
   100   else if (bytepos > GPT_BYTE)
   101     gap_right (charpos, bytepos);
   102 }
   103 
   104 /* Move the gap to a position less than the current GPT.
   105    BYTEPOS describes the new position as a byte position,
   106    and CHARPOS is the corresponding char position.
   107    If NEWGAP, then don't update beg_unchanged and end_unchanged.  */
   108 
   109 static void
   110 gap_left (ptrdiff_t charpos, ptrdiff_t bytepos, bool newgap)
   111 {
   112   unsigned char *to, *from;
   113   ptrdiff_t i;
   114   ptrdiff_t new_s1;
   115 
   116   if (!newgap)
   117     BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
   118 
   119   i = GPT_BYTE;
   120   to = GAP_END_ADDR;
   121   from = GPT_ADDR;
   122   new_s1 = GPT_BYTE; /* May point in the middle of multibyte sequences.  */
   123 
   124   /* Now copy the characters.  To move the gap down,
   125      copy characters up.  */
   126 
   127   while (1)
   128     {
   129       /* I gets number of characters left to copy.  */
   130       i = new_s1 - bytepos;
   131       if (i == 0)
   132         break;
   133       /* If a quit is requested, stop copying now.
   134          Change BYTEPOS to be where we have actually moved the gap to.
   135          Note that this cannot happen when we are called to make the
   136          gap larger or smaller, since make_gap_larger and
   137          make_gap_smaller set inhibit-quit.  */
   138       if (QUITP)
   139         {
   140           /* FIXME: This can point in the middle of a multibyte character.  */
   141           bytepos = new_s1;
   142           charpos = BYTE_TO_CHAR (bytepos);
   143           break;
   144         }
   145       /* Move at most 32000 chars before checking again for a quit.  */
   146       /* FIXME: This 32KB chunk size dates back to before 1991.
   147          Maybe we should bump it to reflect the >1000x increase
   148          in memory size and bandwidth since that time.
   149          Is it even worthwhile checking `quit` within this loop?
   150          Especially since make_gap_smaller/larger binds inhibit-quit anyway!  */
   151       if (i > 32000)
   152         i = 32000;
   153       new_s1 -= i;
   154       from -= i, to -= i;
   155       memmove (to, from, i);
   156     }
   157 
   158   /* Adjust buffer data structure, to put the gap at BYTEPOS.
   159      BYTEPOS is where the loop above stopped, which may be what
   160      was specified or may be where a quit was detected.  */
   161   GPT_BYTE = bytepos;
   162   GPT = charpos;
   163   eassert (charpos <= bytepos);
   164   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
   165   maybe_quit ();
   166 }
   167 
   168 /* Move the gap to a position greater than the current GPT.
   169    BYTEPOS describes the new position as a byte position,
   170    and CHARPOS is the corresponding char position.  */
   171 
   172 static void
   173 gap_right (ptrdiff_t charpos, ptrdiff_t bytepos)
   174 {
   175   register unsigned char *to, *from;
   176   register ptrdiff_t i;
   177   ptrdiff_t new_s1; /* May point in the middle of multibyte sequences.  */
   178 
   179   BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
   180 
   181   i = GPT_BYTE;
   182   from = GAP_END_ADDR;
   183   to = GPT_ADDR;
   184   new_s1 = GPT_BYTE;
   185 
   186   /* Now copy the characters.  To move the gap up,
   187      copy characters down.  */
   188 
   189   while (1)
   190     {
   191       /* I gets number of characters left to copy.  */
   192       i = bytepos - new_s1;
   193       if (i == 0)
   194         break;
   195       /* If a quit is requested, stop copying now.
   196          Change BYTEPOS to be where we have actually moved the gap to.
   197          Note that this cannot happen when we are called to make the
   198          gap larger or smaller, since make_gap_larger and
   199          make_gap_smaller set inhibit-quit.  */
   200       if (QUITP)
   201         {
   202           /* FIXME: This can point in the middle of a multibyte character.  */
   203           bytepos = new_s1;
   204           charpos = BYTE_TO_CHAR (bytepos);
   205           break;
   206         }
   207       /* Move at most 32000 chars before checking again for a quit.  */
   208       if (i > 32000)
   209         i = 32000;
   210       new_s1 += i;
   211       memmove (to, from, i);
   212       from += i, to += i;
   213     }
   214 
   215   GPT = charpos;
   216   GPT_BYTE = bytepos;
   217   eassert (charpos <= bytepos);
   218   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
   219   maybe_quit ();
   220 }
   221 
   222 /* If the selected window's old pointm is adjacent or covered by the
   223    region from FROM to TO, unsuspend auto hscroll in that window.  */
   224 
   225 static void
   226 adjust_suspend_auto_hscroll (ptrdiff_t from, ptrdiff_t to)
   227 {
   228   if (WINDOWP (selected_window))
   229     {
   230       struct window *w = XWINDOW (selected_window);
   231 
   232       if (BUFFERP (w->contents)
   233           && XBUFFER (w->contents) == current_buffer
   234           && XMARKER (w->old_pointm)->charpos >= from
   235           && XMARKER (w->old_pointm)->charpos <= to)
   236         w->suspend_auto_hscroll = 0;
   237     }
   238 }
   239 
   240 
   241 /* Adjust all markers for a deletion
   242    whose range in bytes is FROM_BYTE to TO_BYTE.
   243    The range in charpos is FROM to TO.
   244 
   245    This function assumes that the gap is adjacent to
   246    or inside of the range being deleted.  */
   247 
   248 void
   249 adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
   250                            ptrdiff_t to, ptrdiff_t to_byte)
   251 {
   252   struct Lisp_Marker *m;
   253   ptrdiff_t charpos;
   254 
   255   adjust_suspend_auto_hscroll (from, to);
   256   for (m = BUF_MARKERS (current_buffer); m; m = m->next)
   257     {
   258       charpos = m->charpos;
   259       eassert (charpos <= Z);
   260 
   261       /* If the marker is after the deletion,
   262          relocate by number of chars / bytes deleted.  */
   263       if (charpos > to)
   264         {
   265           m->charpos -= to - from;
   266           m->bytepos -= to_byte - from_byte;
   267         }
   268       /* Here's the case where a marker is inside text being deleted.  */
   269       else if (charpos > from)
   270         {
   271           m->charpos = from;
   272           m->bytepos = from_byte;
   273         }
   274     }
   275   adjust_overlays_for_delete (from, to - from);
   276 }
   277 
   278 
   279 /* Adjust markers for an insertion that stretches from FROM / FROM_BYTE
   280    to TO / TO_BYTE.  We have to relocate the charpos of every marker
   281    that points after the insertion (but not their bytepos).
   282 
   283    When a marker points at the insertion point,
   284    we advance it if either its insertion-type is t
   285    or BEFORE_MARKERS is true.  */
   286 
   287 static void
   288 adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte,
   289                            ptrdiff_t to, ptrdiff_t to_byte, bool before_markers)
   290 {
   291   struct Lisp_Marker *m;
   292   ptrdiff_t nchars = to - from;
   293   ptrdiff_t nbytes = to_byte - from_byte;
   294 
   295   adjust_suspend_auto_hscroll (from, to);
   296   for (m = BUF_MARKERS (current_buffer); m; m = m->next)
   297     {
   298       eassert (m->bytepos >= m->charpos
   299                && m->bytepos - m->charpos <= Z_BYTE - Z);
   300 
   301       if (m->bytepos == from_byte)
   302         {
   303           if (m->insertion_type || before_markers)
   304             {
   305               m->bytepos = to_byte;
   306               m->charpos = to;
   307             }
   308         }
   309       else if (m->bytepos > from_byte)
   310         {
   311           m->bytepos += nbytes;
   312           m->charpos += nchars;
   313         }
   314     }
   315   adjust_overlays_for_insert (from, to - from, before_markers);
   316 }
   317 
   318 /* Adjust point for an insertion of NBYTES bytes, which are NCHARS characters.
   319 
   320    This is used only when the value of point changes due to an insert
   321    or delete; it does not represent a conceptual change in point as a
   322    marker.  In particular, point is not crossing any interval
   323    boundaries, so there's no need to use the usual SET_PT macro.  In
   324    fact it would be incorrect to do so, because either the old or the
   325    new value of point is out of sync with the current set of
   326    intervals.  */
   327 
   328 static void
   329 adjust_point (ptrdiff_t nchars, ptrdiff_t nbytes)
   330 {
   331   SET_BUF_PT_BOTH (current_buffer, PT + nchars, PT_BYTE + nbytes);
   332   /* In a single-byte buffer, the two positions must be equal.  */
   333   eassert (PT_BYTE >= PT && PT_BYTE - PT <= ZV_BYTE - ZV);
   334 }
   335 
   336 /* Adjust markers for a replacement of a text at FROM (FROM_BYTE) of
   337    length OLD_CHARS (OLD_BYTES) to a new text of length NEW_CHARS
   338    (NEW_BYTES).  It is assumed that OLD_CHARS > 0, i.e., this is not
   339    an insertion.  */
   340 
   341 static void
   342 adjust_markers_for_replace (ptrdiff_t from, ptrdiff_t from_byte,
   343                             ptrdiff_t old_chars, ptrdiff_t old_bytes,
   344                             ptrdiff_t new_chars, ptrdiff_t new_bytes)
   345 {
   346   register struct Lisp_Marker *m;
   347   ptrdiff_t prev_to_byte = from_byte + old_bytes;
   348   ptrdiff_t diff_chars = new_chars - old_chars;
   349   ptrdiff_t diff_bytes = new_bytes - old_bytes;
   350 
   351   adjust_suspend_auto_hscroll (from, from + old_chars);
   352 
   353   /* FIXME: When OLD_CHARS is 0, this "replacement" is really just an
   354      insertion, but the behavior we provide here in that case is that of
   355      `insert-before-markers` rather than that of `insert`.
   356      Maybe not a bug, but not a feature either.  */
   357   for (m = BUF_MARKERS (current_buffer); m; m = m->next)
   358     {
   359       if (m->bytepos >= prev_to_byte)
   360         {
   361           m->charpos += diff_chars;
   362           m->bytepos += diff_bytes;
   363         }
   364       else if (m->bytepos > from_byte)
   365         {
   366           m->charpos = from;
   367           m->bytepos = from_byte;
   368         }
   369     }
   370 
   371   check_markers ();
   372 
   373   adjust_overlays_for_insert (from + old_chars, new_chars, true);
   374   if (old_chars)
   375     adjust_overlays_for_delete (from, old_chars);
   376 }
   377 
   378 /* Starting at POS (BYTEPOS), find the byte position corresponding to
   379    ENDPOS, which could be either before or after POS.  */
   380 static ptrdiff_t
   381 count_bytes (ptrdiff_t pos, ptrdiff_t bytepos, ptrdiff_t endpos)
   382 {
   383   eassert (BEG_BYTE <= bytepos && bytepos <= Z_BYTE
   384            && BEG <= endpos && endpos <= Z);
   385 
   386   if (pos <= endpos)
   387     for ( ; pos < endpos; pos++)
   388       bytepos += next_char_len (bytepos);
   389   else
   390     for ( ; pos > endpos; pos--)
   391       bytepos -= prev_char_len (bytepos);
   392 
   393   return bytepos;
   394 }
   395 
   396 /* Adjust byte positions of markers when their character positions
   397    didn't change.  This is used in several places that replace text,
   398    but keep the character positions of the markers unchanged -- the
   399    byte positions could still change due to different numbers of bytes
   400    in the new text.
   401 
   402    FROM (FROM_BYTE) and TO (TO_BYTE) specify the region of text where
   403    changes have been done.  TO_Z, if non-zero, means all the markers
   404    whose positions are after TO should also be adjusted.  */
   405 void
   406 adjust_markers_bytepos (ptrdiff_t from, ptrdiff_t from_byte,
   407                         ptrdiff_t to, ptrdiff_t to_byte, int to_z)
   408 {
   409   register struct Lisp_Marker *m;
   410   ptrdiff_t beg = from, begbyte = from_byte;
   411 
   412   adjust_suspend_auto_hscroll (from, to);
   413 
   414   if (Z == Z_BYTE || (!to_z && to == to_byte))
   415     {
   416       /* Make sure each affected marker's bytepos is equal to
   417          its charpos.  */
   418       for (m = BUF_MARKERS (current_buffer); m; m = m->next)
   419         {
   420           if (m->bytepos > from_byte
   421               && (to_z || m->bytepos <= to_byte))
   422             m->bytepos = m->charpos;
   423         }
   424     }
   425   else
   426     {
   427       for (m = BUF_MARKERS (current_buffer); m; m = m->next)
   428         {
   429           /* Recompute each affected marker's bytepos.  */
   430           if (m->bytepos > from_byte
   431               && (to_z || m->bytepos <= to_byte))
   432             {
   433               if (m->charpos < beg
   434                   && beg - m->charpos > m->charpos - from)
   435                 {
   436                   beg = from;
   437                   begbyte = from_byte;
   438                 }
   439               m->bytepos = count_bytes (beg, begbyte, m->charpos);
   440               beg = m->charpos;
   441               begbyte = m->bytepos;
   442             }
   443         }
   444     }
   445 
   446   /* Make sure cached charpos/bytepos is invalid.  */
   447   clear_charpos_cache (current_buffer);
   448 }
   449 
   450 
   451 void
   452 buffer_overflow (void)
   453 {
   454   error ("Maximum buffer size exceeded");
   455 }
   456 
   457 /* Make the gap NBYTES_ADDED bytes longer.  */
   458 
   459 static void
   460 make_gap_larger (ptrdiff_t nbytes_added)
   461 {
   462   Lisp_Object tem;
   463   ptrdiff_t real_gap_loc;
   464   ptrdiff_t real_gap_loc_byte;
   465   ptrdiff_t old_gap_size;
   466   ptrdiff_t current_size = Z_BYTE - BEG_BYTE + GAP_SIZE;
   467 
   468   if (BUF_BYTES_MAX - current_size < nbytes_added)
   469     buffer_overflow ();
   470 
   471   /* If we have to get more space, get enough to last a while;
   472      but do not exceed the maximum buffer size.  */
   473   nbytes_added = min (nbytes_added + GAP_BYTES_DFL,
   474                       BUF_BYTES_MAX - current_size);
   475 
   476   enlarge_buffer_text (current_buffer, nbytes_added);
   477 
   478   /* Prevent quitting in gap_left.  We cannot allow a quit there,
   479      because that would leave the buffer text in an inconsistent
   480      state, with 2 gap holes instead of just one.  */
   481   tem = Vinhibit_quit;
   482   Vinhibit_quit = Qt;
   483 
   484   real_gap_loc = GPT;
   485   real_gap_loc_byte = GPT_BYTE;
   486   old_gap_size = GAP_SIZE;
   487 
   488   /* Call the newly allocated space a gap at the end of the whole space.  */
   489   GPT = Z + GAP_SIZE;
   490   GPT_BYTE = Z_BYTE + GAP_SIZE;
   491   GAP_SIZE = nbytes_added;
   492 
   493   /* Move the new gap down to be consecutive with the end of the old one.  */
   494   gap_left (real_gap_loc + old_gap_size, real_gap_loc_byte + old_gap_size, 1);
   495 
   496   /* Now combine the two into one large gap.  */
   497   GAP_SIZE += old_gap_size;
   498   GPT = real_gap_loc;
   499   GPT_BYTE = real_gap_loc_byte;
   500 
   501   /* Put an anchor.  */
   502   *(Z_ADDR) = 0;
   503 
   504   Vinhibit_quit = tem;
   505 }
   506 
   507 #if defined USE_MMAP_FOR_BUFFERS || defined REL_ALLOC || defined DOUG_LEA_MALLOC
   508 
   509 /* Make the gap NBYTES_REMOVED bytes shorter.  */
   510 
   511 static void
   512 make_gap_smaller (ptrdiff_t nbytes_removed)
   513 {
   514   Lisp_Object tem;
   515   ptrdiff_t real_gap_loc;
   516   ptrdiff_t real_gap_loc_byte;
   517   ptrdiff_t real_Z;
   518   ptrdiff_t real_Z_byte;
   519   ptrdiff_t real_beg_unchanged;
   520   ptrdiff_t new_gap_size;
   521 
   522   /* Make sure the gap is at least GAP_BYTES_MIN bytes.  */
   523   if (GAP_SIZE - nbytes_removed < GAP_BYTES_MIN)
   524     nbytes_removed = GAP_SIZE - GAP_BYTES_MIN;
   525 
   526   /* Prevent quitting in gap_right.  We cannot allow a quit there,
   527      because that would leave the buffer text in an inconsistent
   528      state, with 2 gap holes instead of just one.  */
   529   tem = Vinhibit_quit;
   530   Vinhibit_quit = Qt;
   531 
   532   real_gap_loc = GPT;
   533   real_gap_loc_byte = GPT_BYTE;
   534   new_gap_size = GAP_SIZE - nbytes_removed;
   535   real_Z = Z;
   536   real_Z_byte = Z_BYTE;
   537   real_beg_unchanged = BEG_UNCHANGED;
   538 
   539   /* Pretend that the last unwanted part of the gap is the entire gap,
   540      and that the first desired part of the gap is part of the buffer
   541      text.  */
   542   memset (GPT_ADDR, 0, new_gap_size);
   543   GPT += new_gap_size;
   544   GPT_BYTE += new_gap_size;
   545   Z += new_gap_size;
   546   Z_BYTE += new_gap_size;
   547   GAP_SIZE = nbytes_removed;
   548 
   549   /* Move the unwanted pretend gap to the end of the buffer.  */
   550   gap_right (Z, Z_BYTE);
   551 
   552   enlarge_buffer_text (current_buffer, -nbytes_removed);
   553 
   554   /* Now restore the desired gap.  */
   555   GAP_SIZE = new_gap_size;
   556   GPT = real_gap_loc;
   557   GPT_BYTE = real_gap_loc_byte;
   558   Z = real_Z;
   559   Z_BYTE = real_Z_byte;
   560   BEG_UNCHANGED = real_beg_unchanged;
   561 
   562   /* Put an anchor.  */
   563   *(Z_ADDR) = 0;
   564 
   565   Vinhibit_quit = tem;
   566 }
   567 
   568 #endif /* USE_MMAP_FOR_BUFFERS || REL_ALLOC || DOUG_LEA_MALLOC */
   569 
   570 void
   571 make_gap (ptrdiff_t nbytes_added)
   572 {
   573   if (nbytes_added >= 0)
   574     /* With set-buffer-multibyte on a large buffer, we can end up growing the
   575      * buffer *many* times.  Avoid an O(N^2) behavior by increasing by an
   576      * amount at least proportional to the size of the buffer.
   577      * On my test (a 223.9MB zip file on a Thinkpad T61):
   578      * With /5    =>  24s
   579      * With /32   =>  25s
   580      * With /64   =>  26s
   581      * With /128  =>  28s
   582      * With /1024 =>  51s
   583      * With /4096 => 131s
   584      * With /∞    => gave up after 858s
   585      * Of course, ideally we should never call set-buffer-multibyte on
   586      * a non-empty buffer (e.g. use buffer-swap-text instead).
   587      * We chose /64 because it already brings almost the best performance while
   588      * limiting the potential wasted memory to 1.5%.  */
   589     make_gap_larger (max (nbytes_added, (Z - BEG) / 64));
   590 #if defined USE_MMAP_FOR_BUFFERS || defined REL_ALLOC || defined DOUG_LEA_MALLOC
   591   else
   592     make_gap_smaller (-nbytes_added);
   593 #endif
   594 }
   595 
   596 /* Add NBYTES to B's gap.  It's enough to temporarily
   597    fake current_buffer and avoid real switch to B.  */
   598 
   599 void
   600 make_gap_1 (struct buffer *b, ptrdiff_t nbytes)
   601 {
   602   struct buffer *oldb = current_buffer;
   603 
   604   current_buffer = b;
   605   make_gap (nbytes);
   606   current_buffer = oldb;
   607 }
   608 
   609 /* Copy NBYTES bytes of text from FROM_ADDR to TO_ADDR.
   610    FROM_MULTIBYTE says whether the incoming text is multibyte.
   611    TO_MULTIBYTE says whether to store the text as multibyte.
   612    If FROM_MULTIBYTE != TO_MULTIBYTE, we convert.
   613 
   614    Return the number of bytes stored at TO_ADDR.  */
   615 
   616 ptrdiff_t
   617 copy_text (const unsigned char *from_addr, unsigned char *to_addr,
   618            ptrdiff_t nbytes, bool from_multibyte, bool to_multibyte)
   619 {
   620   if (from_multibyte == to_multibyte)
   621     {
   622       memcpy (to_addr, from_addr, nbytes);
   623       return nbytes;
   624     }
   625   else if (from_multibyte)
   626     {
   627       ptrdiff_t nchars = 0;
   628       ptrdiff_t bytes_left = nbytes;
   629 
   630       while (bytes_left > 0)
   631         {
   632           int thislen, c = string_char_and_length (from_addr, &thislen);
   633           if (! ASCII_CHAR_P (c))
   634             c &= 0xFF;
   635           *to_addr++ = c;
   636           from_addr += thislen;
   637           bytes_left -= thislen;
   638           nchars++;
   639         }
   640       return nchars;
   641     }
   642   else
   643     {
   644       unsigned char *initial_to_addr = to_addr;
   645 
   646       /* Convert single-byte to multibyte.  */
   647       while (nbytes > 0)
   648         {
   649           int c = *from_addr++;
   650 
   651           if (!ASCII_CHAR_P (c))
   652             {
   653               c = BYTE8_TO_CHAR (c);
   654               to_addr += CHAR_STRING (c, to_addr);
   655               nbytes--;
   656             }
   657           else
   658             /* Special case for speed.  */
   659             *to_addr++ = c, nbytes--;
   660         }
   661       return to_addr - initial_to_addr;
   662     }
   663 }
   664 
   665 /* Insert a string of specified length before point.
   666    This function judges multibyteness based on
   667    enable_multibyte_characters in the current buffer;
   668    it never converts between single-byte and multibyte.
   669 
   670    DO NOT use this for the contents of a Lisp string or a Lisp buffer!
   671    prepare_to_modify_buffer could relocate the text.  */
   672 
   673 void
   674 insert (const char *string, ptrdiff_t nbytes)
   675 {
   676   if (nbytes > 0)
   677     {
   678       ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
   679       insert_1_both (string, len, nbytes, 0, 1, 0);
   680       opoint = PT - len;
   681       signal_after_change (opoint, 0, len);
   682       update_compositions (opoint, PT, CHECK_BORDER);
   683     }
   684 }
   685 
   686 /* Likewise, but inherit text properties from neighboring characters.  */
   687 
   688 void
   689 insert_and_inherit (const char *string, ptrdiff_t nbytes)
   690 {
   691   if (nbytes > 0)
   692     {
   693       ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
   694       insert_1_both (string, len, nbytes, 1, 1, 0);
   695       opoint = PT - len;
   696       signal_after_change (opoint, 0, len);
   697       update_compositions (opoint, PT, CHECK_BORDER);
   698     }
   699 }
   700 
   701 /* Insert the character C before point.  Do not inherit text properties.  */
   702 
   703 void
   704 insert_char (int c)
   705 {
   706   unsigned char str[MAX_MULTIBYTE_LENGTH];
   707   int len;
   708 
   709   if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
   710     len = CHAR_STRING (c, str);
   711   else
   712     {
   713       len = 1;
   714       str[0] = c;
   715     }
   716 
   717   insert ((char *) str, len);
   718 }
   719 
   720 /* Insert the null-terminated string S before point.  */
   721 
   722 void
   723 insert_string (const char *s)
   724 {
   725   insert (s, strlen (s));
   726 }
   727 
   728 /* Like `insert' except that all markers pointing at the place where
   729    the insertion happens are adjusted to point after it.
   730    Don't use this function to insert part of a Lisp string,
   731    since gc could happen and relocate it.  */
   732 
   733 void
   734 insert_before_markers (const char *string, ptrdiff_t nbytes)
   735 {
   736   if (nbytes > 0)
   737     {
   738       ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
   739       insert_1_both (string, len, nbytes, 0, 1, 1);
   740       opoint = PT - len;
   741       signal_after_change (opoint, 0, len);
   742       update_compositions (opoint, PT, CHECK_BORDER);
   743     }
   744 }
   745 
   746 /* Likewise, but inherit text properties from neighboring characters.  */
   747 
   748 void
   749 insert_before_markers_and_inherit (const char *string,
   750                                    ptrdiff_t nbytes)
   751 {
   752   if (nbytes > 0)
   753     {
   754       ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
   755       insert_1_both (string, len, nbytes, 1, 1, 1);
   756       opoint = PT - len;
   757       signal_after_change (opoint, 0, len);
   758       update_compositions (opoint, PT, CHECK_BORDER);
   759     }
   760 }
   761 
   762 #ifdef BYTE_COMBINING_DEBUG
   763 
   764 /* See if the bytes before POS/POS_BYTE combine with bytes
   765    at the start of STRING to form a single character.
   766    If so, return the number of bytes at the start of STRING
   767    which combine in this way.  Otherwise, return 0.  */
   768 
   769 int
   770 count_combining_before (const unsigned char *string, ptrdiff_t length,
   771                         ptrdiff_t pos, ptrdiff_t pos_byte)
   772 {
   773   int len, combining_bytes;
   774   const unsigned char *p;
   775 
   776   if (NILP (current_buffer->enable_multibyte_characters))
   777     return 0;
   778 
   779   /* At first, we can exclude the following cases:
   780         (1) STRING[0] can't be a following byte of multibyte sequence.
   781         (2) POS is the start of the current buffer.
   782         (3) A character before POS is not a multibyte character.  */
   783   if (length == 0 || CHAR_HEAD_P (*string)) /* case (1) */
   784     return 0;
   785   if (pos_byte == BEG_BYTE)     /* case (2) */
   786     return 0;
   787   len = 1;
   788   p = BYTE_POS_ADDR (pos_byte - 1);
   789   while (! CHAR_HEAD_P (*p)) p--, len++;
   790   if (! LEADING_CODE_P (*p)) /* case (3) */
   791     return 0;
   792 
   793   combining_bytes = BYTES_BY_CHAR_HEAD (*p) - len;
   794   if (combining_bytes <= 0)
   795     /* The character preceding POS is, complete and no room for
   796        combining bytes (combining_bytes == 0), or an independent 8-bit
   797        character (combining_bytes < 0).  */
   798     return 0;
   799 
   800   /* We have a combination situation.  Count the bytes at STRING that
   801      may combine.  */
   802   p = string + 1;
   803   while (!CHAR_HEAD_P (*p) && p < string + length)
   804     p++;
   805 
   806   return (combining_bytes < p - string ? combining_bytes : p - string);
   807 }
   808 
   809 /* See if the bytes after POS/POS_BYTE combine with bytes
   810    at the end of STRING to form a single character.
   811    If so, return the number of bytes after POS/POS_BYTE
   812    which combine in this way.  Otherwise, return 0.  */
   813 
   814 int
   815 count_combining_after (const unsigned char *string,
   816                        ptrdiff_t length, ptrdiff_t pos, ptrdiff_t pos_byte)
   817 {
   818   ptrdiff_t opos_byte = pos_byte;
   819   ptrdiff_t i;
   820   ptrdiff_t bytes;
   821   unsigned char *bufp;
   822 
   823   if (NILP (current_buffer->enable_multibyte_characters))
   824     return 0;
   825 
   826   /* At first, we can exclude the following cases:
   827         (1) The last byte of STRING is an ASCII.
   828         (2) POS is the last of the current buffer.
   829         (3) A character at POS can't be a following byte of multibyte
   830             character.  */
   831   if (length > 0 && ASCII_CHAR_P (string[length - 1])) /* case (1) */
   832     return 0;
   833   if (pos_byte == Z_BYTE)       /* case (2) */
   834     return 0;
   835   bufp = BYTE_POS_ADDR (pos_byte);
   836   if (CHAR_HEAD_P (*bufp))      /* case (3) */
   837     return 0;
   838 
   839   i = length - 1;
   840   while (i >= 0 && ! CHAR_HEAD_P (string[i]))
   841     {
   842       i--;
   843     }
   844   if (i < 0)
   845     {
   846       /* All characters in STRING are not character head.  We must
   847          check also preceding bytes at POS.  We are sure that the gap
   848          is at POS.  */
   849       unsigned char *p = BEG_ADDR;
   850       i = pos_byte - 2;
   851       while (i >= 0 && ! CHAR_HEAD_P (p[i]))
   852         i--;
   853       if (i < 0 || !LEADING_CODE_P (p[i]))
   854         return 0;
   855 
   856       bytes = BYTES_BY_CHAR_HEAD (p[i]);
   857       return (bytes <= pos_byte - 1 - i + length
   858               ? 0
   859               : bytes - (pos_byte - 1 - i + length));
   860     }
   861   if (!LEADING_CODE_P (string[i]))
   862     return 0;
   863 
   864   bytes = BYTES_BY_CHAR_HEAD (string[i]) - (length - i);
   865   bufp++, pos_byte++;
   866   while (!CHAR_HEAD_P (*bufp)) bufp++, pos_byte++;
   867 
   868   return (bytes <= pos_byte - opos_byte ? bytes : pos_byte - opos_byte);
   869 }
   870 
   871 #endif
   872 
   873 
   874 /* Insert a sequence of NCHARS chars which occupy NBYTES bytes
   875    starting at STRING.  INHERIT non-zero means inherit the text
   876    properties from neighboring characters; zero means inserted text
   877    will have no text properties.  PREPARE non-zero means call
   878    prepare_to_modify_buffer, which checks that the region is not
   879    read-only, and calls before-change-function and any modification
   880    properties the text may have.  BEFORE_MARKERS non-zero means adjust
   881    all markers that point at the insertion place to point after it.  */
   882 
   883 void
   884 insert_1_both (const char *string,
   885                ptrdiff_t nchars, ptrdiff_t nbytes,
   886                bool inherit, bool prepare, bool before_markers)
   887 {
   888   if (nchars == 0)
   889     return;
   890 
   891   if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
   892     nchars = nbytes;
   893 
   894   if (prepare)
   895     /* Do this before moving and increasing the gap,
   896        because the before-change hooks might move the gap
   897        or make it smaller.  */
   898     prepare_to_modify_buffer (PT, PT, NULL);
   899 
   900   if (PT != GPT)
   901     move_gap_both (PT, PT_BYTE);
   902   if (GAP_SIZE < nbytes)
   903     make_gap (nbytes - GAP_SIZE);
   904 
   905 #ifdef BYTE_COMBINING_DEBUG
   906   if (count_combining_before (string, nbytes, PT, PT_BYTE)
   907       || count_combining_after (string, nbytes, PT, PT_BYTE))
   908     emacs_abort ();
   909 #endif
   910 
   911   /* Record deletion of the surrounding text that combines with
   912      the insertion.  This, together with recording the insertion,
   913      will add up to the right stuff in the undo list.  */
   914   record_insert (PT, nchars);
   915   modiff_incr (&MODIFF, nchars);
   916   CHARS_MODIFF = MODIFF;
   917 
   918   memcpy (GPT_ADDR, string, nbytes);
   919 
   920   GAP_SIZE -= nbytes;
   921   GPT += nchars;
   922   ZV += nchars;
   923   Z += nchars;
   924   GPT_BYTE += nbytes;
   925   ZV_BYTE += nbytes;
   926   Z_BYTE += nbytes;
   927   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
   928 
   929   eassert (GPT <= GPT_BYTE);
   930 
   931   /* The insert may have been in the unchanged region, so check again.  */
   932   if (Z - GPT < END_UNCHANGED)
   933     END_UNCHANGED = Z - GPT;
   934 
   935   adjust_markers_for_insert (PT, PT_BYTE,
   936                              PT + nchars, PT_BYTE + nbytes,
   937                              before_markers);
   938 
   939   offset_intervals (current_buffer, PT, nchars);
   940 
   941   if (!inherit && buffer_intervals (current_buffer))
   942     set_text_properties (make_fixnum (PT), make_fixnum (PT + nchars),
   943                          Qnil, Qnil, Qnil);
   944 
   945 #ifdef HAVE_TREE_SITTER
   946   eassert (nbytes >= 0);
   947   eassert (PT_BYTE >= 0);
   948   treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes);
   949 #endif
   950 
   951   adjust_point (nchars, nbytes);
   952 
   953   check_markers ();
   954 }
   955 
   956 /* Insert the part of the text of STRING, a Lisp object assumed to be
   957    of type string, consisting of the LENGTH characters (LENGTH_BYTE bytes)
   958    starting at position POS / POS_BYTE.  If the text of STRING has properties,
   959    copy them into the buffer.
   960 
   961    It does not work to use `insert' for this, because a GC could happen
   962    before we copy the stuff into the buffer, and relocate the string
   963    without insert noticing.  */
   964 
   965 void
   966 insert_from_string (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
   967                     ptrdiff_t length, ptrdiff_t length_byte, bool inherit)
   968 {
   969   ptrdiff_t opoint = PT;
   970 
   971   if (SCHARS (string) == 0)
   972     return;
   973 
   974   insert_from_string_1 (string, pos, pos_byte, length, length_byte,
   975                         inherit, 0);
   976   signal_after_change (opoint, 0, PT - opoint);
   977   update_compositions (opoint, PT, CHECK_BORDER);
   978 }
   979 
   980 /* Like `insert_from_string' except that all markers pointing
   981    at the place where the insertion happens are adjusted to point after it.  */
   982 
   983 void
   984 insert_from_string_before_markers (Lisp_Object string,
   985                                    ptrdiff_t pos, ptrdiff_t pos_byte,
   986                                    ptrdiff_t length, ptrdiff_t length_byte,
   987                                    bool inherit)
   988 {
   989   ptrdiff_t opoint = PT;
   990 
   991   if (SCHARS (string) == 0)
   992     return;
   993 
   994   insert_from_string_1 (string, pos, pos_byte, length, length_byte,
   995                         inherit, 1);
   996   signal_after_change (opoint, 0, PT - opoint);
   997   update_compositions (opoint, PT, CHECK_BORDER);
   998 }
   999 
  1000 /* Subroutine of the insertion functions above.  */
  1001 
  1002 static void
  1003 insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
  1004                       ptrdiff_t nchars, ptrdiff_t nbytes,
  1005                       bool inherit, bool before_markers)
  1006 {
  1007   ptrdiff_t outgoing_nbytes = nbytes;
  1008   INTERVAL intervals;
  1009 
  1010   /* Make OUTGOING_NBYTES describe the text
  1011      as it will be inserted in this buffer.  */
  1012 
  1013   if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
  1014     outgoing_nbytes = nchars;
  1015   else if (! STRING_MULTIBYTE (string))
  1016     outgoing_nbytes
  1017       = count_size_as_multibyte (SDATA (string) + pos_byte,
  1018                                  nbytes);
  1019 
  1020   /* Do this before moving and increasing the gap,
  1021      because the before-change hooks might move the gap
  1022      or make it smaller.  */
  1023   prepare_to_modify_buffer (PT, PT, NULL);
  1024 
  1025   if (PT != GPT)
  1026     move_gap_both (PT, PT_BYTE);
  1027   if (GAP_SIZE < outgoing_nbytes)
  1028     make_gap (outgoing_nbytes - GAP_SIZE);
  1029 
  1030   /* Copy the string text into the buffer, perhaps converting
  1031      between single-byte and multibyte.  */
  1032   copy_text (SDATA (string) + pos_byte, GPT_ADDR, nbytes,
  1033              STRING_MULTIBYTE (string),
  1034              ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
  1035 
  1036 #ifdef BYTE_COMBINING_DEBUG
  1037   /* We have copied text into the gap, but we have not altered
  1038      PT or PT_BYTE yet.  So we can pass PT and PT_BYTE
  1039      to these functions and get the same results as we would
  1040      have got earlier on.  Meanwhile, PT_ADDR does point to
  1041      the text that has been stored by copy_text.  */
  1042   if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
  1043       || count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
  1044     emacs_abort ();
  1045 #endif
  1046 
  1047   record_insert (PT, nchars);
  1048   modiff_incr (&MODIFF, nchars);
  1049   CHARS_MODIFF = MODIFF;
  1050 
  1051   GAP_SIZE -= outgoing_nbytes;
  1052   GPT += nchars;
  1053   ZV += nchars;
  1054   Z += nchars;
  1055   GPT_BYTE += outgoing_nbytes;
  1056   ZV_BYTE += outgoing_nbytes;
  1057   Z_BYTE += outgoing_nbytes;
  1058   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
  1059 
  1060   eassert (GPT <= GPT_BYTE);
  1061 
  1062   /* The insert may have been in the unchanged region, so check again.  */
  1063   if (Z - GPT < END_UNCHANGED)
  1064     END_UNCHANGED = Z - GPT;
  1065 
  1066   adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
  1067                              PT_BYTE + outgoing_nbytes,
  1068                              before_markers);
  1069 
  1070   offset_intervals (current_buffer, PT, nchars);
  1071 
  1072   intervals = string_intervals (string);
  1073   /* Get the intervals for the part of the string we are inserting.  */
  1074   if (nbytes < SBYTES (string))
  1075     intervals = copy_intervals (intervals, pos, nchars);
  1076 
  1077   /* Insert those intervals.  */
  1078   graft_intervals_into_buffer (intervals, PT, nchars,
  1079                                current_buffer, inherit);
  1080 
  1081 #ifdef HAVE_TREE_SITTER
  1082   eassert (nbytes >= 0);
  1083   eassert (PT_BYTE >= 0);
  1084   treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes);
  1085 #endif
  1086 
  1087   adjust_point (nchars, outgoing_nbytes);
  1088 
  1089   check_markers ();
  1090 }
  1091 
  1092 /* Insert a sequence of NCHARS chars which occupy NBYTES bytes
  1093    starting at GAP_END_ADDR - NBYTES (if text_at_gap_tail) and at
  1094    GPT_ADDR (if not text_at_gap_tail).
  1095    Contrary to insert_from_gap, this does not invalidate any cache,
  1096    nor update any markers, nor record any buffer modification information
  1097    of any sort.  */
  1098 void
  1099 insert_from_gap_1 (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail)
  1100 {
  1101   eassert (NILP (BVAR (current_buffer, enable_multibyte_characters))
  1102            ? nchars == nbytes : nchars <= nbytes);
  1103 
  1104 #ifdef HAVE_TREE_SITTER
  1105   ptrdiff_t ins_bytepos = GPT_BYTE;
  1106 #endif
  1107 
  1108   GAP_SIZE -= nbytes;
  1109   if (! text_at_gap_tail)
  1110     {
  1111       GPT += nchars;
  1112       GPT_BYTE += nbytes;
  1113     }
  1114   ZV += nchars;
  1115   Z += nchars;
  1116   ZV_BYTE += nbytes;
  1117   Z_BYTE += nbytes;
  1118 
  1119   /* Put an anchor to ensure multi-byte form ends at gap.  */
  1120   if (GAP_SIZE > 0) *(GPT_ADDR) = 0;
  1121   eassert (GPT <= GPT_BYTE);
  1122 
  1123 #ifdef HAVE_TREE_SITTER
  1124   eassert (nbytes >= 0);
  1125   eassert (ins_bytepos >= 0);
  1126   treesit_record_change (ins_bytepos, ins_bytepos, ins_bytepos + nbytes);
  1127 #endif
  1128 }
  1129 
  1130 /* Insert a sequence of NCHARS chars which occupy NBYTES bytes
  1131    starting at GAP_END_ADDR - NBYTES (if text_at_gap_tail) and at
  1132    GPT_ADDR (if not text_at_gap_tail).  */
  1133 
  1134 void
  1135 insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail)
  1136 {
  1137   ptrdiff_t ins_charpos = GPT, ins_bytepos = GPT_BYTE;
  1138 
  1139   if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
  1140     nchars = nbytes;
  1141 
  1142   /* No need to call prepare_to_modify_buffer, since this is called
  1143      from places that replace some region with a different text, so
  1144      prepare_to_modify_buffer was already called by the deletion part
  1145      of this dance.  */
  1146   invalidate_buffer_caches (current_buffer, GPT, GPT);
  1147   record_insert (GPT, nchars);
  1148   modiff_incr (&MODIFF, nchars);
  1149   CHARS_MODIFF = MODIFF;
  1150 
  1151   insert_from_gap_1 (nchars, nbytes, text_at_gap_tail);
  1152 
  1153   adjust_markers_for_insert (ins_charpos, ins_bytepos,
  1154                              ins_charpos + nchars, ins_bytepos + nbytes, false);
  1155 
  1156   if (buffer_intervals (current_buffer))
  1157     {
  1158       offset_intervals (current_buffer, ins_charpos, nchars);
  1159       graft_intervals_into_buffer (NULL, ins_charpos, nchars,
  1160                                    current_buffer, 0);
  1161     }
  1162 
  1163   if (ins_charpos < PT)
  1164     adjust_point (nchars, nbytes);
  1165 
  1166   check_markers ();
  1167 }
  1168 
  1169 /* Insert text from BUF, NCHARS characters starting at CHARPOS, into the
  1170    current buffer.  If the text in BUF has properties, they are absorbed
  1171    into the current buffer.
  1172 
  1173    It does not work to use `insert' for this, because a malloc could happen
  1174    and relocate BUF's text before the copy happens.  */
  1175 
  1176 void
  1177 insert_from_buffer (struct buffer *buf,
  1178                     ptrdiff_t charpos, ptrdiff_t nchars, bool inherit)
  1179 {
  1180   ptrdiff_t opoint = PT;
  1181 
  1182 #ifdef HAVE_TREE_SITTER
  1183   ptrdiff_t obyte = PT_BYTE;
  1184 #endif
  1185 
  1186   insert_from_buffer_1 (buf, charpos, nchars, inherit);
  1187   signal_after_change (opoint, 0, PT - opoint);
  1188   update_compositions (opoint, PT, CHECK_BORDER);
  1189 
  1190 #ifdef HAVE_TREE_SITTER
  1191   eassert (PT_BYTE >= BEG_BYTE);
  1192   eassert (obyte >= BEG_BYTE);
  1193   eassert (PT_BYTE >= obyte);
  1194   treesit_record_change (obyte, obyte, PT_BYTE);
  1195 #endif
  1196 }
  1197 
  1198 /* NOTE: If we ever make insert_from_buffer_1 public, make sure to
  1199    move the call to treesit_record_change into it.  */
  1200 
  1201 static void
  1202 insert_from_buffer_1 (struct buffer *buf,
  1203                       ptrdiff_t from, ptrdiff_t nchars, bool inherit)
  1204 {
  1205   ptrdiff_t chunk, chunk_expanded;
  1206   ptrdiff_t from_byte = buf_charpos_to_bytepos (buf, from);
  1207   ptrdiff_t to_byte = buf_charpos_to_bytepos (buf, from + nchars);
  1208   ptrdiff_t incoming_nbytes = to_byte - from_byte;
  1209   ptrdiff_t outgoing_nbytes = incoming_nbytes;
  1210   INTERVAL intervals;
  1211 
  1212   if (nchars == 0)
  1213     return;
  1214 
  1215   /* Make OUTGOING_NBYTES describe the text
  1216      as it will be inserted in this buffer.  */
  1217 
  1218   if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
  1219     outgoing_nbytes = nchars;
  1220   else if (NILP (BVAR (buf, enable_multibyte_characters)))
  1221     {
  1222       ptrdiff_t outgoing_before_gap = 0;
  1223       ptrdiff_t outgoing_after_gap = 0;
  1224 
  1225       if (from < BUF_GPT (buf))
  1226         {
  1227           chunk =  BUF_GPT_BYTE (buf) - from_byte;
  1228           if (chunk > incoming_nbytes)
  1229             chunk = incoming_nbytes;
  1230           outgoing_before_gap
  1231             = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, from_byte),
  1232                                        chunk);
  1233         }
  1234       else
  1235         chunk = 0;
  1236 
  1237       if (chunk < incoming_nbytes)
  1238         outgoing_after_gap
  1239           = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf,
  1240                                                        from_byte + chunk),
  1241                                      incoming_nbytes - chunk);
  1242 
  1243       outgoing_nbytes = outgoing_before_gap + outgoing_after_gap;
  1244     }
  1245 
  1246   /* Do this before moving and increasing the gap,
  1247      because the before-change hooks might move the gap
  1248      or make it smaller.  */
  1249   prepare_to_modify_buffer (PT, PT, NULL);
  1250 
  1251   if (PT != GPT)
  1252     move_gap_both (PT, PT_BYTE);
  1253   if (GAP_SIZE < outgoing_nbytes)
  1254     make_gap (outgoing_nbytes - GAP_SIZE);
  1255 
  1256   if (from < BUF_GPT (buf))
  1257     {
  1258       chunk = BUF_GPT_BYTE (buf) - from_byte;
  1259       if (chunk > incoming_nbytes)
  1260         chunk = incoming_nbytes;
  1261       /* Record number of output bytes, so we know where
  1262          to put the output from the second copy_text.  */
  1263       chunk_expanded
  1264         = copy_text (BUF_BYTE_ADDRESS (buf, from_byte),
  1265                      GPT_ADDR, chunk,
  1266                      ! NILP (BVAR (buf, enable_multibyte_characters)),
  1267                      ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
  1268     }
  1269   else
  1270     chunk_expanded = chunk = 0;
  1271 
  1272   if (chunk < incoming_nbytes)
  1273     copy_text (BUF_BYTE_ADDRESS (buf, from_byte + chunk),
  1274                GPT_ADDR + chunk_expanded, incoming_nbytes - chunk,
  1275                ! NILP (BVAR (buf, enable_multibyte_characters)),
  1276                ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
  1277 
  1278 #ifdef BYTE_COMBINING_DEBUG
  1279   /* We have copied text into the gap, but we have not altered
  1280      PT or PT_BYTE yet.  So we can pass PT and PT_BYTE
  1281      to these functions and get the same results as we would
  1282      have got earlier on.  Meanwhile, GPT_ADDR does point to
  1283      the text that has been stored by copy_text.  */
  1284   if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
  1285       || count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
  1286     emacs_abort ();
  1287 #endif
  1288 
  1289   record_insert (PT, nchars);
  1290   modiff_incr (&MODIFF, nchars);
  1291   CHARS_MODIFF = MODIFF;
  1292 
  1293   GAP_SIZE -= outgoing_nbytes;
  1294   GPT += nchars;
  1295   ZV += nchars;
  1296   Z += nchars;
  1297   GPT_BYTE += outgoing_nbytes;
  1298   ZV_BYTE += outgoing_nbytes;
  1299   Z_BYTE += outgoing_nbytes;
  1300   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
  1301 
  1302   eassert (GPT <= GPT_BYTE);
  1303 
  1304   /* The insert may have been in the unchanged region, so check again.  */
  1305   if (Z - GPT < END_UNCHANGED)
  1306     END_UNCHANGED = Z - GPT;
  1307 
  1308   adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
  1309                              PT_BYTE + outgoing_nbytes,
  1310                              false);
  1311 
  1312   offset_intervals (current_buffer, PT, nchars);
  1313 
  1314   /* Get the intervals for the part of the string we are inserting.  */
  1315   intervals = buffer_intervals (buf);
  1316   if (nchars < BUF_Z (buf) - BUF_BEG (buf))
  1317     {
  1318       if (buf == current_buffer && PT <= from)
  1319         from += nchars;
  1320       intervals = copy_intervals (intervals, from, nchars);
  1321     }
  1322 
  1323   /* Insert those intervals.  */
  1324   graft_intervals_into_buffer (intervals, PT, nchars, current_buffer, inherit);
  1325 
  1326   adjust_point (nchars, outgoing_nbytes);
  1327 }
  1328 
  1329 /* Record undo information and adjust markers and position keepers for
  1330    a replacement of a text PREV_TEXT at FROM to a new text of LEN
  1331    chars (LEN_BYTE bytes) which resides in the gap just after
  1332    GPT_ADDR.
  1333 
  1334    PREV_TEXT nil means the new text was just inserted.  */
  1335 
  1336 static void
  1337 adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte,
  1338                       Lisp_Object prev_text, ptrdiff_t len, ptrdiff_t len_byte)
  1339 {
  1340   ptrdiff_t nchars_del = 0, nbytes_del = 0;
  1341 
  1342 #ifdef BYTE_COMBINING_DEBUG
  1343   if (count_combining_before (GPT_ADDR, len_byte, from, from_byte)
  1344       || count_combining_after (GPT_ADDR, len_byte, from, from_byte))
  1345     emacs_abort ();
  1346 #endif
  1347 
  1348   if (STRINGP (prev_text))
  1349     {
  1350       nchars_del = SCHARS (prev_text);
  1351       nbytes_del = SBYTES (prev_text);
  1352     }
  1353 
  1354   /* Update various buffer positions for the new text.  */
  1355   GAP_SIZE -= len_byte;
  1356   ZV += len; Z += len;
  1357   ZV_BYTE += len_byte; Z_BYTE += len_byte;
  1358   GPT += len; GPT_BYTE += len_byte;
  1359   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
  1360 
  1361   if (nchars_del > 0)
  1362     adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
  1363                                 len, len_byte);
  1364   else
  1365     adjust_markers_for_insert (from, from_byte,
  1366                                from + len, from_byte + len_byte, false);
  1367 
  1368   if (nchars_del > 0)
  1369     record_delete (from, prev_text, false);
  1370   record_insert (from, len);
  1371 
  1372   offset_intervals (current_buffer, from, len - nchars_del);
  1373 
  1374   if (from < PT)
  1375     adjust_point (len - nchars_del, len_byte - nbytes_del);
  1376 
  1377   /* As byte combining will decrease Z, we must check this again.  */
  1378   if (Z - GPT < END_UNCHANGED)
  1379     END_UNCHANGED = Z - GPT;
  1380 
  1381   check_markers ();
  1382 
  1383   modiff_incr (&MODIFF, nchars_del + len);
  1384   CHARS_MODIFF = MODIFF;
  1385 }
  1386 
  1387 /* Record undo information, adjust markers and position keepers for an
  1388    insertion of a text from FROM (FROM_BYTE) to TO (TO_BYTE).  The
  1389    text already exists in the current buffer but character length (TO
  1390    - FROM) may be incorrect, the correct length is NEWLEN.  */
  1391 
  1392 void
  1393 adjust_after_insert (ptrdiff_t from, ptrdiff_t from_byte,
  1394                      ptrdiff_t to, ptrdiff_t to_byte, ptrdiff_t newlen)
  1395 {
  1396   ptrdiff_t len = to - from, len_byte = to_byte - from_byte;
  1397 
  1398   if (GPT != to)
  1399     move_gap_both (to, to_byte);
  1400   GAP_SIZE += len_byte;
  1401   GPT -= len; GPT_BYTE -= len_byte;
  1402   ZV -= len; ZV_BYTE -= len_byte;
  1403   Z -= len; Z_BYTE -= len_byte;
  1404   adjust_after_replace (from, from_byte, Qnil, newlen, len_byte);
  1405 }
  1406 
  1407 /* Replace the text from character positions FROM to TO with NEW,
  1408    If PREPARE, call prepare_to_modify_buffer.
  1409    If INHERIT, the newly inserted text should inherit text properties
  1410    from the surrounding non-deleted text.
  1411    If ADJUST_MATCH_DATA, then adjust the match data before calling
  1412    signal_after_change.  */
  1413 
  1414 /* Note that this does not yet handle markers quite right.
  1415    Also it needs to record a single undo-entry that does a replacement
  1416    rather than a separate delete and insert.
  1417    That way, undo will also handle markers properly.
  1418 
  1419    But if MARKERS is 0, don't relocate markers.  */
  1420 
  1421 void
  1422 replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
  1423                bool prepare, bool inherit, bool markers,
  1424                bool adjust_match_data, bool inhibit_mod_hooks)
  1425 {
  1426   ptrdiff_t inschars = SCHARS (new);
  1427   ptrdiff_t insbytes = SBYTES (new);
  1428   ptrdiff_t from_byte, to_byte;
  1429   ptrdiff_t nbytes_del, nchars_del;
  1430   INTERVAL intervals;
  1431   ptrdiff_t outgoing_insbytes = insbytes;
  1432   Lisp_Object deletion;
  1433 
  1434   check_markers ();
  1435 
  1436   deletion = Qnil;
  1437 
  1438   if (prepare)
  1439     {
  1440       ptrdiff_t range_length = to - from;
  1441       prepare_to_modify_buffer (from, to, &from);
  1442       to = from + range_length;
  1443     }
  1444 
  1445   /* Make args be valid.  */
  1446   if (from < BEGV)
  1447     from = BEGV;
  1448   if (to > ZV)
  1449     to = ZV;
  1450 
  1451   from_byte = CHAR_TO_BYTE (from);
  1452   to_byte = CHAR_TO_BYTE (to);
  1453 
  1454   nchars_del = to - from;
  1455   nbytes_del = to_byte - from_byte;
  1456 
  1457   if (nbytes_del <= 0 && insbytes == 0)
  1458     return;
  1459 
  1460   /* Make OUTGOING_INSBYTES describe the text
  1461      as it will be inserted in this buffer.  */
  1462 
  1463   if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
  1464     outgoing_insbytes = inschars;
  1465   else if (! STRING_MULTIBYTE (new))
  1466     outgoing_insbytes
  1467       = count_size_as_multibyte (SDATA (new), insbytes);
  1468 
  1469   /* Make sure the gap is somewhere in or next to what we are deleting.  */
  1470   if (from > GPT)
  1471     gap_right (from, from_byte);
  1472   if (to < GPT)
  1473     gap_left (to, to_byte, 0);
  1474 
  1475   /* Even if we don't record for undo, we must keep the original text
  1476      because we may have to recover it because of inappropriate byte
  1477      combining.  */
  1478   if (! EQ (BVAR (current_buffer, undo_list), Qt))
  1479     deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
  1480 
  1481   GAP_SIZE += nbytes_del;
  1482   ZV -= nchars_del;
  1483   Z -= nchars_del;
  1484   ZV_BYTE -= nbytes_del;
  1485   Z_BYTE -= nbytes_del;
  1486   GPT = from;
  1487   GPT_BYTE = from_byte;
  1488   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
  1489 
  1490   eassert (GPT <= GPT_BYTE);
  1491 
  1492   if (GPT - BEG < BEG_UNCHANGED)
  1493     BEG_UNCHANGED = GPT - BEG;
  1494   if (Z - GPT < END_UNCHANGED)
  1495     END_UNCHANGED = Z - GPT;
  1496 
  1497   if (GAP_SIZE < outgoing_insbytes)
  1498     make_gap (outgoing_insbytes - GAP_SIZE);
  1499 
  1500   /* Copy the string text into the buffer, perhaps converting
  1501      between single-byte and multibyte.  */
  1502   copy_text (SDATA (new), GPT_ADDR, insbytes,
  1503              STRING_MULTIBYTE (new),
  1504              ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
  1505 
  1506 #ifdef BYTE_COMBINING_DEBUG
  1507   /* We have copied text into the gap, but we have not marked
  1508      it as part of the buffer.  So we can use the old FROM and FROM_BYTE
  1509      here, for both the previous text and the following text.
  1510      Meanwhile, GPT_ADDR does point to
  1511      the text that has been stored by copy_text.  */
  1512   if (count_combining_before (GPT_ADDR, outgoing_insbytes, from, from_byte)
  1513       || count_combining_after (GPT_ADDR, outgoing_insbytes, from, from_byte))
  1514     emacs_abort ();
  1515 #endif
  1516 
  1517   /* Record the insertion first, so that when we undo,
  1518      the deletion will be undone first.  Thus, undo
  1519      will insert before deleting, and thus will keep
  1520      the markers before and after this text separate.  */
  1521   if (!NILP (deletion))
  1522     {
  1523       record_insert (from + SCHARS (deletion), inschars);
  1524       record_delete (from, deletion, false);
  1525     }
  1526 
  1527   GAP_SIZE -= outgoing_insbytes;
  1528   GPT += inschars;
  1529   ZV += inschars;
  1530   Z += inschars;
  1531   GPT_BYTE += outgoing_insbytes;
  1532   ZV_BYTE += outgoing_insbytes;
  1533   Z_BYTE += outgoing_insbytes;
  1534   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
  1535 
  1536   eassert (GPT <= GPT_BYTE);
  1537 
  1538   /* Adjust markers for the deletion and the insertion.  */
  1539   if (markers)
  1540     adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
  1541                                 inschars, outgoing_insbytes);
  1542   else
  1543     {
  1544       /* The character positions of the markers remain intact, but we
  1545          still need to update their byte positions, because the
  1546          deleted and the inserted text might have multibyte sequences
  1547          which make the original byte positions of the markers
  1548          invalid.  */
  1549       adjust_markers_bytepos (from, from_byte, from + inschars,
  1550                               from_byte + outgoing_insbytes, true);
  1551     }
  1552 
  1553   offset_intervals (current_buffer, from, inschars - nchars_del);
  1554 
  1555   /* Get the intervals for the part of the string we are inserting--
  1556      not including the combined-before bytes.  */
  1557   intervals = string_intervals (new);
  1558   /* Insert those intervals.  */
  1559   graft_intervals_into_buffer (intervals, from, inschars,
  1560                                current_buffer, inherit);
  1561 
  1562 #ifdef HAVE_TREE_SITTER
  1563   eassert (to_byte >= from_byte);
  1564   eassert (outgoing_insbytes >= 0);
  1565   eassert (from_byte >= 0);
  1566   treesit_record_change (from_byte, to_byte, from_byte + outgoing_insbytes);
  1567 #endif
  1568 
  1569   /* Relocate point as if it were a marker.  */
  1570   if (from < PT)
  1571     adjust_point ((from + inschars - (PT < to ? PT : to)),
  1572                   (from_byte + outgoing_insbytes
  1573                    - (PT_BYTE < to_byte ? PT_BYTE : to_byte)));
  1574 
  1575   check_markers ();
  1576 
  1577   modiff_incr (&MODIFF, nchars_del + inschars);
  1578   CHARS_MODIFF = MODIFF;
  1579 
  1580   if (adjust_match_data)
  1581     update_search_regs (from, to, from + SCHARS (new));
  1582 
  1583   if (!inhibit_mod_hooks)
  1584     {
  1585       signal_after_change (from, nchars_del, GPT - from);
  1586       update_compositions (from, GPT, CHECK_BORDER);
  1587     }
  1588 }
  1589 
  1590 /* Replace the text from character positions FROM to TO with
  1591    the text in INS of length INSCHARS.
  1592    Keep the text properties that applied to the old characters
  1593    (extending them to all the new chars if there are more new chars).
  1594 
  1595    Note that this does not yet handle markers quite right.
  1596 
  1597    If MARKERS, relocate markers.
  1598 
  1599    Unlike most functions at this level, never call
  1600    prepare_to_modify_buffer and never call signal_after_change.
  1601    Because this function is called in a loop, one character at a time.
  1602    The caller of 'replace_range_2' calls these hooks for the entire
  1603    region once.  Apart from signal_after_change, any caller of this
  1604    function should also call treesit_record_change.  */
  1605 
  1606 void
  1607 replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
  1608                  ptrdiff_t to, ptrdiff_t to_byte,
  1609                  const char *ins, ptrdiff_t inschars, ptrdiff_t insbytes,
  1610                  bool markers)
  1611 {
  1612   ptrdiff_t nbytes_del, nchars_del;
  1613 
  1614   check_markers ();
  1615 
  1616   nchars_del = to - from;
  1617   nbytes_del = to_byte - from_byte;
  1618 
  1619   if (nbytes_del <= 0 && insbytes == 0)
  1620     return;
  1621 
  1622   /* Make sure the gap is somewhere in or next to what we are deleting.  */
  1623   if (from > GPT)
  1624     gap_right (from, from_byte);
  1625   if (to < GPT)
  1626     gap_left (to, to_byte, 0);
  1627 
  1628   GAP_SIZE += nbytes_del;
  1629   ZV -= nchars_del;
  1630   Z -= nchars_del;
  1631   ZV_BYTE -= nbytes_del;
  1632   Z_BYTE -= nbytes_del;
  1633   GPT = from;
  1634   GPT_BYTE = from_byte;
  1635   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
  1636 
  1637   eassert (GPT <= GPT_BYTE);
  1638 
  1639   if (GPT - BEG < BEG_UNCHANGED)
  1640     BEG_UNCHANGED = GPT - BEG;
  1641   if (Z - GPT < END_UNCHANGED)
  1642     END_UNCHANGED = Z - GPT;
  1643 
  1644   if (GAP_SIZE < insbytes)
  1645     make_gap (insbytes - GAP_SIZE);
  1646 
  1647   /* Copy the replacement text into the buffer.  */
  1648   memcpy (GPT_ADDR, ins, insbytes);
  1649 
  1650 #ifdef BYTE_COMBINING_DEBUG
  1651   /* We have copied text into the gap, but we have not marked
  1652      it as part of the buffer.  So we can use the old FROM and FROM_BYTE
  1653      here, for both the previous text and the following text.
  1654      Meanwhile, GPT_ADDR does point to
  1655      the text that has been stored by copy_text.  */
  1656   if (count_combining_before (GPT_ADDR, insbytes, from, from_byte)
  1657       || count_combining_after (GPT_ADDR, insbytes, from, from_byte))
  1658     emacs_abort ();
  1659 #endif
  1660 
  1661   GAP_SIZE -= insbytes;
  1662   GPT += inschars;
  1663   ZV += inschars;
  1664   Z += inschars;
  1665   GPT_BYTE += insbytes;
  1666   ZV_BYTE += insbytes;
  1667   Z_BYTE += insbytes;
  1668   if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
  1669 
  1670   eassert (GPT <= GPT_BYTE);
  1671 
  1672   /* Adjust markers for the deletion and the insertion.  */
  1673   if (! (nchars_del == 1 && inschars == 1 && nbytes_del == insbytes))
  1674     {
  1675       if (markers)
  1676         adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
  1677                                     inschars, insbytes);
  1678       else
  1679         {
  1680           /* The character positions of the markers remain intact, but
  1681              we still need to update their byte positions, because the
  1682              deleted and the inserted text might have multibyte
  1683              sequences which make the original byte positions of the
  1684              markers invalid.  */
  1685           adjust_markers_bytepos (from, from_byte, from + inschars,
  1686                                   from_byte + insbytes, true);
  1687         }
  1688     }
  1689 
  1690   offset_intervals (current_buffer, from, inschars - nchars_del);
  1691 
  1692   /* Relocate point as if it were a marker.  */
  1693   if (from < PT && (nchars_del != inschars || nbytes_del != insbytes))
  1694     {
  1695       if (PT < to)
  1696         /* PT was within the deleted text.  Move it to FROM.  */
  1697         adjust_point (from - PT, from_byte - PT_BYTE);
  1698       else
  1699         adjust_point (inschars - nchars_del, insbytes - nbytes_del);
  1700     }
  1701 
  1702   check_markers ();
  1703 
  1704   modiff_incr (&MODIFF, nchars_del + inschars);
  1705   CHARS_MODIFF = MODIFF;
  1706 }
  1707 
  1708 /* Delete characters in current buffer
  1709    from FROM up to (but not including) TO.
  1710    If TO comes before FROM, we delete nothing.  */
  1711 
  1712 void
  1713 del_range (ptrdiff_t from, ptrdiff_t to)
  1714 {
  1715   del_range_1 (from, to, 1, 0);
  1716 }
  1717 
  1718 struct safe_del_range_context
  1719 {
  1720   /* From and to positions.  */
  1721   ptrdiff_t from, to;
  1722 };
  1723 
  1724 static Lisp_Object
  1725 safe_del_range_1 (void *ptr)
  1726 {
  1727   struct safe_del_range_context *context;
  1728 
  1729   context = ptr;
  1730   del_range (context->from, context->to);
  1731   return Qnil;
  1732 }
  1733 
  1734 static Lisp_Object
  1735 safe_del_range_2 (enum nonlocal_exit type, Lisp_Object value)
  1736 {
  1737   return Qt;
  1738 }
  1739 
  1740 /* Like del_range; however, catch all non-local exits.  Value is 0 if
  1741    the buffer contents were really deleted.  Otherwise, it is 1.  */
  1742 
  1743 int
  1744 safe_del_range (ptrdiff_t from, ptrdiff_t to)
  1745 {
  1746   struct safe_del_range_context context;
  1747 
  1748   context.from = from;
  1749   context.to = to;
  1750 
  1751   return !NILP (internal_catch_all (safe_del_range_1,
  1752                                     &context,
  1753                                     safe_del_range_2));
  1754 }
  1755 
  1756 /* Like del_range; PREPARE says whether to call prepare_to_modify_buffer.
  1757    RET_STRING says to return the deleted text. */
  1758 
  1759 Lisp_Object
  1760 del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
  1761 {
  1762   ptrdiff_t from_byte, to_byte;
  1763   Lisp_Object deletion;
  1764 
  1765   /* Make args be valid */
  1766   if (from < BEGV)
  1767     from = BEGV;
  1768   if (to > ZV)
  1769     to = ZV;
  1770 
  1771   if (to <= from)
  1772     return Qnil;
  1773 
  1774   if (prepare)
  1775     {
  1776       ptrdiff_t range_length = to - from;
  1777       prepare_to_modify_buffer (from, to, &from);
  1778       to = min (ZV, from + range_length);
  1779     }
  1780 
  1781   from_byte = CHAR_TO_BYTE (from);
  1782   to_byte = CHAR_TO_BYTE (to);
  1783 
  1784   deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
  1785   signal_after_change (from, to - from, 0);
  1786   update_compositions (from, from, CHECK_HEAD);
  1787   return deletion;
  1788 }
  1789 
  1790 /* Like del_range_1 but args are byte positions, not char positions.  */
  1791 
  1792 void
  1793 del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte)
  1794 {
  1795   ptrdiff_t from, to;
  1796 
  1797   /* Make args be valid.  */
  1798   if (from_byte < BEGV_BYTE)
  1799     from_byte = BEGV_BYTE;
  1800   if (to_byte > ZV_BYTE)
  1801     to_byte = ZV_BYTE;
  1802 
  1803   if (to_byte <= from_byte)
  1804     return;
  1805 
  1806   from = BYTE_TO_CHAR (from_byte);
  1807   to = BYTE_TO_CHAR (to_byte);
  1808 
  1809   {
  1810     ptrdiff_t old_from = from, old_to = Z - to;
  1811     ptrdiff_t range_length = to - from;
  1812     prepare_to_modify_buffer (from, to, &from);
  1813     to = from + range_length;
  1814 
  1815     if (old_from != from)
  1816       from_byte = CHAR_TO_BYTE (from);
  1817     if (to > ZV)
  1818       {
  1819         to = ZV;
  1820         to_byte = ZV_BYTE;
  1821       }
  1822     else if (old_to == Z - to)
  1823       to_byte = CHAR_TO_BYTE (to);
  1824   }
  1825 
  1826   del_range_2 (from, from_byte, to, to_byte, 0);
  1827   signal_after_change (from, to - from, 0);
  1828   update_compositions (from, from, CHECK_HEAD);
  1829 }
  1830 
  1831 /* Like del_range_1, but positions are specified both as charpos
  1832    and bytepos.  */
  1833 
  1834 void
  1835 del_range_both (ptrdiff_t from, ptrdiff_t from_byte,
  1836                 ptrdiff_t to, ptrdiff_t to_byte, bool prepare)
  1837 {
  1838   /* Make args be valid */
  1839   if (from_byte < BEGV_BYTE)
  1840     from_byte = BEGV_BYTE;
  1841   if (to_byte > ZV_BYTE)
  1842     to_byte = ZV_BYTE;
  1843 
  1844   if (to_byte <= from_byte)
  1845     return;
  1846 
  1847   if (from < BEGV)
  1848     from = BEGV;
  1849   if (to > ZV)
  1850     to = ZV;
  1851 
  1852   if (prepare)
  1853     {
  1854       ptrdiff_t old_from = from, old_to = Z - to;
  1855       ptrdiff_t range_length = to - from;
  1856       prepare_to_modify_buffer (from, to, &from);
  1857       to = from + range_length;
  1858 
  1859       if (old_from != from)
  1860         from_byte = CHAR_TO_BYTE (from);
  1861       if (to > ZV)
  1862         {
  1863           to = ZV;
  1864           to_byte = ZV_BYTE;
  1865         }
  1866       else if (old_to == Z - to)
  1867         to_byte = CHAR_TO_BYTE (to);
  1868     }
  1869 
  1870   del_range_2 (from, from_byte, to, to_byte, 0);
  1871   signal_after_change (from, to - from, 0);
  1872   update_compositions (from, from, CHECK_HEAD);
  1873 }
  1874 
  1875 /* Delete a range of text, specified both as character positions
  1876    and byte positions.  FROM and TO are character positions,
  1877    while FROM_BYTE and TO_BYTE are byte positions.
  1878    If RET_STRING, the deleted area is returned as a string.  */
  1879 
  1880 Lisp_Object
  1881 del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
  1882              ptrdiff_t to, ptrdiff_t to_byte, bool ret_string)
  1883 {
  1884   ptrdiff_t nbytes_del, nchars_del;
  1885   Lisp_Object deletion;
  1886 
  1887   check_markers ();
  1888 
  1889   nchars_del = to - from;
  1890   nbytes_del = to_byte - from_byte;
  1891 
  1892   /* Make sure the gap is somewhere in or next to what we are deleting.  */
  1893   if (from > GPT)
  1894     gap_right (from, from_byte);
  1895   if (to < GPT)
  1896     gap_left (to, to_byte, 0);
  1897 
  1898 #ifdef BYTE_COMBINING_DEBUG
  1899   if (count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
  1900                               Z_BYTE - to_byte, from, from_byte))
  1901     emacs_abort ();
  1902 #endif
  1903 
  1904   if (ret_string || ! EQ (BVAR (current_buffer, undo_list), Qt))
  1905     deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
  1906   else
  1907     deletion = Qnil;
  1908 
  1909   /* Record marker adjustments, and text deletion into undo
  1910      history.  */
  1911   record_delete (from, deletion, true);
  1912 
  1913   /* Relocate all markers pointing into the new, larger gap to point
  1914      at the end of the text before the gap.  */
  1915   adjust_markers_for_delete (from, from_byte, to, to_byte);
  1916 
  1917   modiff_incr (&MODIFF, nchars_del);
  1918   CHARS_MODIFF = MODIFF;
  1919 
  1920   /* Relocate point as if it were a marker.  */
  1921   if (from < PT)
  1922     adjust_point (from - (PT < to ? PT : to),
  1923                   from_byte - (PT_BYTE < to_byte ? PT_BYTE : to_byte));
  1924 
  1925   offset_intervals (current_buffer, from, - nchars_del);
  1926 
  1927   GAP_SIZE += nbytes_del;
  1928   ZV_BYTE -= nbytes_del;
  1929   Z_BYTE -= nbytes_del;
  1930   ZV -= nchars_del;
  1931   Z -= nchars_del;
  1932   GPT = from;
  1933   GPT_BYTE = from_byte;
  1934   if (GAP_SIZE > 0 && !current_buffer->text->inhibit_shrinking)
  1935     /* Put an anchor, unless called from decode_coding_object which
  1936        needs to access the previous gap contents.  */
  1937     *(GPT_ADDR) = 0;
  1938 
  1939   eassert (GPT <= GPT_BYTE);
  1940 
  1941   if (GPT - BEG < BEG_UNCHANGED)
  1942     BEG_UNCHANGED = GPT - BEG;
  1943   if (Z - GPT < END_UNCHANGED)
  1944     END_UNCHANGED = Z - GPT;
  1945 
  1946   check_markers ();
  1947 
  1948 #ifdef HAVE_TREE_SITTER
  1949   eassert (from_byte <= to_byte);
  1950   eassert (from_byte >= 0);
  1951   treesit_record_change (from_byte, to_byte, from_byte);
  1952 #endif
  1953 
  1954   return deletion;
  1955 }
  1956 
  1957 /* Call this if you're about to change the text of current buffer
  1958    from character positions START to END.  This checks the read-only
  1959    properties of the region, calls the necessary modification hooks,
  1960    and warns the next redisplay that it should pay attention to that
  1961    area.  */
  1962 
  1963 void
  1964 modify_text (ptrdiff_t start, ptrdiff_t end)
  1965 {
  1966   prepare_to_modify_buffer (start, end, NULL);
  1967 
  1968   BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end);
  1969   if (MODIFF <= SAVE_MODIFF)
  1970     record_first_change ();
  1971   modiff_incr (&MODIFF, end - start);
  1972   CHARS_MODIFF = MODIFF;
  1973 
  1974   bset_point_before_scroll (current_buffer, Qnil);
  1975 }
  1976 
  1977 /* Signal that we are about to make a change that may result in new
  1978    undo information.
  1979  */
  1980 static void
  1981 run_undoable_change (void)
  1982 {
  1983   if (EQ (BVAR (current_buffer, undo_list), Qt))
  1984     return;
  1985 
  1986   call0 (Qundo_auto__undoable_change);
  1987 }
  1988 
  1989 /* Check that it is okay to modify the buffer between START and END,
  1990    which are char positions.
  1991 
  1992    Run the before-change-function, if any.  If intervals are in use,
  1993    verify that the text to be modified is not read-only, and call
  1994    any modification properties the text may have.
  1995 
  1996    If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
  1997    by holding its value temporarily in a marker.
  1998 
  1999    This function runs Lisp, which means it can GC, which means it can
  2000    compact buffers, including the current buffer being worked on here.
  2001    So don't you dare calling this function while manipulating the gap,
  2002    or during some other similar "critical section".  */
  2003 
  2004 void
  2005 prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
  2006                             ptrdiff_t *preserve_ptr)
  2007 {
  2008   struct buffer *base_buffer;
  2009   Lisp_Object temp;
  2010 
  2011   XSETFASTINT (temp, start);
  2012   if (!NILP (BVAR (current_buffer, read_only)))
  2013     Fbarf_if_buffer_read_only (temp);
  2014 
  2015   /* If we're about to modify a buffer the contents of which come from
  2016      a dump file, copy the contents to private storage first so we
  2017      don't take a COW fault on the buffer text and keep it around
  2018      forever.  */
  2019   if (pdumper_object_p (BEG_ADDR))
  2020     enlarge_buffer_text (current_buffer, 0);
  2021   eassert (!pdumper_object_p (BEG_ADDR));
  2022 
  2023   run_undoable_change();
  2024 
  2025   bset_redisplay (current_buffer);
  2026 
  2027   if (buffer_intervals (current_buffer))
  2028     {
  2029       if (preserve_ptr)
  2030         {
  2031           Lisp_Object preserve_marker;
  2032           preserve_marker = Fcopy_marker (make_fixnum (*preserve_ptr), Qnil);
  2033           verify_interval_modification (current_buffer, start, end);
  2034           *preserve_ptr = marker_position (preserve_marker);
  2035           unchain_marker (XMARKER (preserve_marker));
  2036         }
  2037       else
  2038         verify_interval_modification (current_buffer, start, end);
  2039     }
  2040 
  2041   /* For indirect buffers, use the base buffer to check clashes.  */
  2042   if (current_buffer->base_buffer != 0)
  2043     base_buffer = current_buffer->base_buffer;
  2044   else
  2045     base_buffer = current_buffer;
  2046 
  2047   if (inhibit_modification_hooks)
  2048     return;
  2049 
  2050   if (!NILP (BVAR (base_buffer, file_truename))
  2051       /* Make binding buffer-file-name to nil effective.  */
  2052       && !NILP (BVAR (base_buffer, filename))
  2053       && SAVE_MODIFF >= MODIFF)
  2054     Flock_file (BVAR (base_buffer, file_truename));
  2055 
  2056   /* If `select-active-regions' is non-nil, save the region text.  */
  2057   /* FIXME: Move this to Elisp (via before-change-functions).  */
  2058   if (!NILP (BVAR (current_buffer, mark_active))
  2059       && XMARKER (BVAR (current_buffer, mark))->buffer
  2060       && NILP (Vsaved_region_selection)
  2061       && (EQ (Vselect_active_regions, Qonly)
  2062           ? EQ (CAR_SAFE (Vtransient_mark_mode), Qonly)
  2063           : (!NILP (Vselect_active_regions)
  2064              && !NILP (Vtransient_mark_mode))))
  2065     Vsaved_region_selection
  2066       = call1 (Vregion_extract_function, Qnil);
  2067 
  2068   signal_before_change (start, end, preserve_ptr);
  2069   Fset (Qdeactivate_mark, Qt);
  2070 }
  2071 
  2072 /* Like above, but called when we know that the buffer text
  2073    will be modified and region caches should be invalidated.  */
  2074 
  2075 void
  2076 prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
  2077                           ptrdiff_t *preserve_ptr)
  2078 {
  2079   prepare_to_modify_buffer_1 (start, end, preserve_ptr);
  2080   invalidate_buffer_caches (current_buffer, start, end);
  2081 }
  2082 
  2083 /* Invalidate the caches maintained by the buffer BUF, if any, for the
  2084    region between buffer positions START and END.  */
  2085 void
  2086 invalidate_buffer_caches (struct buffer *buf, ptrdiff_t start, ptrdiff_t end)
  2087 {
  2088   /* Indirect buffers usually have their caches set to NULL, but we
  2089      need to consider the caches of their base buffer.  */
  2090   if (buf->base_buffer)
  2091     buf = buf->base_buffer;
  2092   /* The bidi_paragraph_cache must be invalidated first, because doing
  2093      so might need to use the newline_cache (via find_newline_no_quit,
  2094      see below).  */
  2095   if (buf->bidi_paragraph_cache)
  2096     {
  2097       if (start > BUF_BEG (buf))
  2098         {
  2099           /* If we are deleting or replacing characters, we could
  2100              create a paragraph start, because all of the characters
  2101              from START to the beginning of START's line are
  2102              whitespace.  Therefore, we must extend the region to be
  2103              invalidated up to the newline before START.  Similarly,
  2104              if we are inserting characters immediately after a
  2105              newline, we could create a paragraph start if the
  2106              inserted characters start with a newline.  */
  2107           ptrdiff_t line_beg = start;
  2108           ptrdiff_t start_byte = buf_charpos_to_bytepos (buf, start);
  2109           int prev_char = BUF_FETCH_BYTE (buf, start_byte - 1);
  2110 
  2111           if ((start == end) == (prev_char == '\n'))
  2112             {
  2113               struct buffer *old = current_buffer;
  2114 
  2115               set_buffer_internal (buf);
  2116 
  2117               line_beg = find_newline_no_quit (start, start_byte, -1,
  2118                                                &start_byte);
  2119               set_buffer_internal (old);
  2120             }
  2121           start = line_beg - (line_beg > BUF_BEG (buf));
  2122         }
  2123       invalidate_region_cache (buf,
  2124                                buf->bidi_paragraph_cache,
  2125                                start - BUF_BEG (buf), BUF_Z (buf) - end);
  2126     }
  2127   if (buf->newline_cache)
  2128     invalidate_region_cache (buf,
  2129                              buf->newline_cache,
  2130                              start - BUF_BEG (buf), BUF_Z (buf) - end);
  2131   if (buf->width_run_cache)
  2132     invalidate_region_cache (buf,
  2133                              buf->width_run_cache,
  2134                              start - BUF_BEG (buf), BUF_Z (buf) - end);
  2135 }
  2136 
  2137 /* These macros work with an argument named `preserve_ptr'
  2138    and a local variable named `preserve_marker'.  */
  2139 
  2140 #define PRESERVE_VALUE                                                  \
  2141   if (preserve_ptr && NILP (preserve_marker))                           \
  2142     preserve_marker = Fcopy_marker (make_fixnum (*preserve_ptr), Qnil)
  2143 
  2144 #define RESTORE_VALUE                                           \
  2145   if (! NILP (preserve_marker))                                 \
  2146     {                                                           \
  2147       *preserve_ptr = marker_position (preserve_marker);        \
  2148       unchain_marker (XMARKER (preserve_marker));               \
  2149     }
  2150 
  2151 #define PRESERVE_START_END                      \
  2152   if (NILP (start_marker))                      \
  2153     start_marker = Fcopy_marker (start, Qnil);  \
  2154   if (NILP (end_marker))                        \
  2155     end_marker = Fcopy_marker (end, Qnil);
  2156 
  2157 #define FETCH_START                             \
  2158   (! NILP (start_marker) ? Fmarker_position (start_marker) : start)
  2159 
  2160 #define FETCH_END                               \
  2161   (! NILP (end_marker) ? Fmarker_position (end_marker) : end)
  2162 
  2163 /* Set a variable to nil if an error occurred.
  2164    Don't change the variable if there was no error.
  2165    VAL is a cons-cell (VARIABLE . NO-ERROR-FLAG).
  2166    VARIABLE is the variable to maybe set to nil.
  2167    NO-ERROR-FLAG is nil if there was an error,
  2168    anything else meaning no error (so this function does nothing).  */
  2169 struct rvoe_arg
  2170 {
  2171   Lisp_Object *location;
  2172   bool errorp;
  2173 };
  2174 
  2175 static void
  2176 reset_var_on_error (void *ptr)
  2177 {
  2178   struct rvoe_arg *p = ptr;
  2179   if (p->errorp)
  2180     *p->location = Qnil;
  2181 }
  2182 
  2183 /* Signal a change to the buffer immediately before it happens.
  2184    START_INT and END_INT are the bounds of the text to be changed.
  2185 
  2186    If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
  2187    by holding its value temporarily in a marker.  */
  2188 
  2189 static void
  2190 signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
  2191                       ptrdiff_t *preserve_ptr)
  2192 {
  2193   Lisp_Object start, end;
  2194   Lisp_Object start_marker, end_marker;
  2195   Lisp_Object preserve_marker;
  2196   specpdl_ref count = SPECPDL_INDEX ();
  2197   struct rvoe_arg rvoe_arg;
  2198 
  2199   start = make_fixnum (start_int);
  2200   end = make_fixnum (end_int);
  2201   preserve_marker = Qnil;
  2202   start_marker = Qnil;
  2203   end_marker = Qnil;
  2204 
  2205   specbind (Qinhibit_modification_hooks, Qt);
  2206 
  2207   /* If buffer is unmodified, run a special hook for that case.  The
  2208    check for Vfirst_change_hook is just a minor optimization.  */
  2209   if (SAVE_MODIFF >= MODIFF
  2210       && !NILP (Vfirst_change_hook))
  2211     {
  2212       PRESERVE_VALUE;
  2213       PRESERVE_START_END;
  2214       run_hook (Qfirst_change_hook);
  2215     }
  2216 
  2217   /* Now run the before-change-functions if any.  */
  2218   if (!NILP (Vbefore_change_functions))
  2219     {
  2220       rvoe_arg.location = &Vbefore_change_functions;
  2221       rvoe_arg.errorp = 1;
  2222 
  2223       PRESERVE_VALUE;
  2224       PRESERVE_START_END;
  2225 
  2226       /* Mark before-change-functions to be reset to nil in case of error.  */
  2227       record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
  2228 
  2229       /* Actually run the hook functions.  */
  2230       CALLN (Frun_hook_with_args, Qbefore_change_functions,
  2231              FETCH_START, FETCH_END);
  2232 
  2233       /* There was no error: unarm the reset_on_error.  */
  2234       rvoe_arg.errorp = 0;
  2235     }
  2236 
  2237   if (buffer_has_overlays ())
  2238     {
  2239       PRESERVE_VALUE;
  2240       report_overlay_modification (FETCH_START, FETCH_END, 0,
  2241                                    FETCH_START, FETCH_END, Qnil);
  2242     }
  2243 
  2244   if (! NILP (start_marker))
  2245     detach_marker (start_marker);
  2246   if (! NILP (end_marker))
  2247     detach_marker (end_marker);
  2248   RESTORE_VALUE;
  2249 
  2250   unbind_to (count, Qnil);
  2251 }
  2252 
  2253 /* Signal a change immediately after it happens.
  2254    CHARPOS is the character position of the start of the changed text.
  2255    LENDEL is the number of characters of the text before the change.
  2256    (Not the whole buffer; just the part that was changed.)
  2257    LENINS is the number of characters in that part of the text
  2258    after the change.  */
  2259 
  2260 void
  2261 signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
  2262 {
  2263   specpdl_ref count = SPECPDL_INDEX ();
  2264   struct rvoe_arg rvoe_arg;
  2265   Lisp_Object tmp, save_insert_behind_hooks, save_insert_in_from_hooks;
  2266 
  2267   if (inhibit_modification_hooks)
  2268     return;
  2269 
  2270   /* If we are deferring calls to the after-change functions
  2271      and there are no before-change functions,
  2272      just record the args that we were going to use.  */
  2273   if (! NILP (Vcombine_after_change_calls)
  2274       /* It's OK to defer after-changes even if syntax-ppss-flush-cache
  2275        * is on before-change-functions, which is common enough to be worth
  2276        * adding a special case for it.  */
  2277       && (NILP (Vbefore_change_functions)
  2278           || (CONSP (Vbefore_change_functions)
  2279               && EQ (Qt, XCAR (Vbefore_change_functions))
  2280               && NILP (Fdefault_value (Qbefore_change_functions))
  2281               && CONSP (tmp = XCDR (Vbefore_change_functions))
  2282               && NILP (XCDR (tmp))
  2283               && EQ (XCAR (tmp), Qsyntax_ppss_flush_cache)))
  2284       && !buffer_has_overlays ())
  2285     {
  2286       Lisp_Object elt;
  2287 
  2288       if (!NILP (combine_after_change_list)
  2289           && current_buffer != XBUFFER (combine_after_change_buffer))
  2290         Fcombine_after_change_execute ();
  2291 
  2292       elt = list3i (charpos - BEG, Z - (charpos - lendel + lenins),
  2293                     lenins - lendel);
  2294       combine_after_change_list
  2295         = Fcons (elt, combine_after_change_list);
  2296       combine_after_change_buffer = Fcurrent_buffer ();
  2297 
  2298       return;
  2299     }
  2300 
  2301   /* Save and restore the insert-*-hooks, because other hooks like
  2302      after-change-functions, called below, could clobber them if they
  2303      manipulate text properties.  */
  2304   save_insert_behind_hooks = interval_insert_behind_hooks;
  2305   save_insert_in_from_hooks = interval_insert_in_front_hooks;
  2306 
  2307   if (!NILP (combine_after_change_list))
  2308     Fcombine_after_change_execute ();
  2309 
  2310   specbind (Qinhibit_modification_hooks, Qt);
  2311 
  2312   if (!NILP (Vafter_change_functions))
  2313     {
  2314       rvoe_arg.location = &Vafter_change_functions;
  2315       rvoe_arg.errorp = 1;
  2316 
  2317       /* Mark after-change-functions to be reset to nil in case of error.  */
  2318       record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
  2319 
  2320       /* Actually run the hook functions.  */
  2321       CALLN (Frun_hook_with_args, Qafter_change_functions,
  2322              make_fixnum (charpos), make_fixnum (charpos + lenins),
  2323              make_fixnum (lendel));
  2324 
  2325       /* There was no error: unarm the reset_on_error.  */
  2326       rvoe_arg.errorp = 0;
  2327     }
  2328 
  2329   interval_insert_behind_hooks = save_insert_behind_hooks;
  2330   interval_insert_in_front_hooks = save_insert_in_from_hooks;
  2331 
  2332   if (buffer_has_overlays ())
  2333     report_overlay_modification (make_fixnum (charpos),
  2334                                  make_fixnum (charpos + lenins),
  2335                                  1,
  2336                                  make_fixnum (charpos),
  2337                                  make_fixnum (charpos + lenins),
  2338                                  make_fixnum (lendel));
  2339 
  2340   /* After an insertion, call the text properties
  2341      insert-behind-hooks or insert-in-front-hooks.  */
  2342   if (lendel == 0)
  2343     report_interval_modification (make_fixnum (charpos),
  2344                                   make_fixnum (charpos + lenins));
  2345 
  2346   unbind_to (count, Qnil);
  2347 }
  2348 
  2349 static void
  2350 Fcombine_after_change_execute_1 (Lisp_Object val)
  2351 {
  2352   Vcombine_after_change_calls = val;
  2353 }
  2354 
  2355 DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
  2356        Scombine_after_change_execute, 0, 0, 0,
  2357        doc: /* This function is for use internally in the function `combine-after-change-calls'.  */)
  2358   (void)
  2359 {
  2360   specpdl_ref count = SPECPDL_INDEX ();
  2361   ptrdiff_t beg, end, change;
  2362   ptrdiff_t begpos, endpos;
  2363   Lisp_Object tail;
  2364 
  2365   if (NILP (combine_after_change_list))
  2366     return Qnil;
  2367 
  2368   /* It is rare for combine_after_change_buffer to be invalid, but
  2369      possible.  It can happen when combine-after-change-calls is
  2370      non-nil, and insertion calls a file name handler (e.g. through
  2371      lock_file) which scribbles into a temp file -- cyd  */
  2372   if (!BUFFERP (combine_after_change_buffer)
  2373       || !BUFFER_LIVE_P (XBUFFER (combine_after_change_buffer)))
  2374     {
  2375       combine_after_change_list = Qnil;
  2376       return Qnil;
  2377     }
  2378 
  2379   record_unwind_current_buffer ();
  2380 
  2381   Fset_buffer (combine_after_change_buffer);
  2382 
  2383   /* # chars unchanged at beginning of buffer.  */
  2384   beg = Z - BEG;
  2385   /* # chars unchanged at end of buffer.  */
  2386   end = beg;
  2387   /* Total amount of insertion (negative for deletion).  */
  2388   change = 0;
  2389 
  2390   /* Scan the various individual changes,
  2391      accumulating the range info in BEG, END and CHANGE.  */
  2392   for (tail = combine_after_change_list; CONSP (tail);
  2393        tail = XCDR (tail))
  2394     {
  2395       Lisp_Object elt;
  2396       ptrdiff_t thisbeg, thisend, thischange;
  2397 
  2398       /* Extract the info from the next element.  */
  2399       elt = XCAR (tail);
  2400       if (! CONSP (elt))
  2401         continue;
  2402       thisbeg = XFIXNUM (XCAR (elt));
  2403 
  2404       elt = XCDR (elt);
  2405       if (! CONSP (elt))
  2406         continue;
  2407       thisend = XFIXNUM (XCAR (elt));
  2408 
  2409       elt = XCDR (elt);
  2410       if (! CONSP (elt))
  2411         continue;
  2412       thischange = XFIXNUM (XCAR (elt));
  2413 
  2414       /* Merge this range into the accumulated range.  */
  2415       change += thischange;
  2416       if (thisbeg < beg)
  2417         beg = thisbeg;
  2418       if (thisend < end)
  2419         end = thisend;
  2420     }
  2421 
  2422   /* Get the current start and end positions of the range
  2423      that was changed.  */
  2424   begpos = BEG + beg;
  2425   endpos = Z - end;
  2426 
  2427   /* We are about to handle these, so discard them.  */
  2428   combine_after_change_list = Qnil;
  2429 
  2430   /* Now run the after-change functions for real.
  2431      Turn off the flag that defers them.  */
  2432   record_unwind_protect (Fcombine_after_change_execute_1,
  2433                          Vcombine_after_change_calls);
  2434   signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
  2435   update_compositions (begpos, endpos, CHECK_ALL);
  2436 
  2437   return unbind_to (count, Qnil);
  2438 }
  2439 
  2440 void
  2441 syms_of_insdel (void)
  2442 {
  2443   staticpro (&combine_after_change_list);
  2444   staticpro (&combine_after_change_buffer);
  2445   combine_after_change_list = Qnil;
  2446   combine_after_change_buffer = Qnil;
  2447 
  2448   DEFSYM (Qundo_auto__undoable_change, "undo-auto--undoable-change");
  2449   DEFSYM (Qsyntax_ppss_flush_cache, "syntax-ppss-flush-cache");
  2450 
  2451   DEFVAR_LISP ("combine-after-change-calls", Vcombine_after_change_calls,
  2452                doc: /* Used internally by the function `combine-after-change-calls' macro.  */);
  2453   Vcombine_after_change_calls = Qnil;
  2454 
  2455   DEFVAR_BOOL ("inhibit-modification-hooks", inhibit_modification_hooks,
  2456                doc: /* Non-nil means don't run any of the hooks that respond to buffer changes.
  2457 This affects `before-change-functions' and `after-change-functions',
  2458 as well as hooks attached to text properties and overlays.
  2459 Setting this variable non-nil also inhibits file locks and checks
  2460 whether files are locked by another Emacs session, as well as
  2461 handling of the active region per `select-active-regions'.
  2462 
  2463 To delay change hooks during a series of changes, use
  2464 `combine-change-calls' or `combine-after-change-calls' instead of
  2465 binding this variable.
  2466 
  2467 See also the info node `(elisp) Change Hooks'.  */);
  2468   inhibit_modification_hooks = 0;
  2469   DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks");
  2470 
  2471   defsubr (&Scombine_after_change_execute);
  2472 }

/* [<][>][^][v][top][bottom][index][help] */