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. del_range_1
  40. del_range_byte
  41. del_range_both
  42. del_range_2
  43. modify_text
  44. run_undoable_change
  45. prepare_to_modify_buffer_1
  46. prepare_to_modify_buffer
  47. invalidate_buffer_caches
  48. reset_var_on_error
  49. signal_before_change
  50. signal_after_change
  51. Fcombine_after_change_execute_1
  52. DEFUN
  53. 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 /* Like del_range; PREPARE says whether to call prepare_to_modify_buffer.
  1719    RET_STRING says to return the deleted text. */
  1720 
  1721 Lisp_Object
  1722 del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
  1723 {
  1724   ptrdiff_t from_byte, to_byte;
  1725   Lisp_Object deletion;
  1726 
  1727   /* Make args be valid */
  1728   if (from < BEGV)
  1729     from = BEGV;
  1730   if (to > ZV)
  1731     to = ZV;
  1732 
  1733   if (to <= from)
  1734     return Qnil;
  1735 
  1736   if (prepare)
  1737     {
  1738       ptrdiff_t range_length = to - from;
  1739       prepare_to_modify_buffer (from, to, &from);
  1740       to = min (ZV, from + range_length);
  1741     }
  1742 
  1743   from_byte = CHAR_TO_BYTE (from);
  1744   to_byte = CHAR_TO_BYTE (to);
  1745 
  1746   deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
  1747   signal_after_change (from, to - from, 0);
  1748   update_compositions (from, from, CHECK_HEAD);
  1749   return deletion;
  1750 }
  1751 
  1752 /* Like del_range_1 but args are byte positions, not char positions.  */
  1753 
  1754 void
  1755 del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte)
  1756 {
  1757   ptrdiff_t from, to;
  1758 
  1759   /* Make args be valid.  */
  1760   if (from_byte < BEGV_BYTE)
  1761     from_byte = BEGV_BYTE;
  1762   if (to_byte > ZV_BYTE)
  1763     to_byte = ZV_BYTE;
  1764 
  1765   if (to_byte <= from_byte)
  1766     return;
  1767 
  1768   from = BYTE_TO_CHAR (from_byte);
  1769   to = BYTE_TO_CHAR (to_byte);
  1770 
  1771   {
  1772     ptrdiff_t old_from = from, old_to = Z - to;
  1773     ptrdiff_t range_length = to - from;
  1774     prepare_to_modify_buffer (from, to, &from);
  1775     to = from + range_length;
  1776 
  1777     if (old_from != from)
  1778       from_byte = CHAR_TO_BYTE (from);
  1779     if (to > ZV)
  1780       {
  1781         to = ZV;
  1782         to_byte = ZV_BYTE;
  1783       }
  1784     else if (old_to == Z - to)
  1785       to_byte = CHAR_TO_BYTE (to);
  1786   }
  1787 
  1788   del_range_2 (from, from_byte, to, to_byte, 0);
  1789   signal_after_change (from, to - from, 0);
  1790   update_compositions (from, from, CHECK_HEAD);
  1791 }
  1792 
  1793 /* Like del_range_1, but positions are specified both as charpos
  1794    and bytepos.  */
  1795 
  1796 void
  1797 del_range_both (ptrdiff_t from, ptrdiff_t from_byte,
  1798                 ptrdiff_t to, ptrdiff_t to_byte, bool prepare)
  1799 {
  1800   /* Make args be valid */
  1801   if (from_byte < BEGV_BYTE)
  1802     from_byte = BEGV_BYTE;
  1803   if (to_byte > ZV_BYTE)
  1804     to_byte = ZV_BYTE;
  1805 
  1806   if (to_byte <= from_byte)
  1807     return;
  1808 
  1809   if (from < BEGV)
  1810     from = BEGV;
  1811   if (to > ZV)
  1812     to = ZV;
  1813 
  1814   if (prepare)
  1815     {
  1816       ptrdiff_t old_from = from, old_to = Z - to;
  1817       ptrdiff_t range_length = to - from;
  1818       prepare_to_modify_buffer (from, to, &from);
  1819       to = from + range_length;
  1820 
  1821       if (old_from != from)
  1822         from_byte = CHAR_TO_BYTE (from);
  1823       if (to > ZV)
  1824         {
  1825           to = ZV;
  1826           to_byte = ZV_BYTE;
  1827         }
  1828       else if (old_to == Z - to)
  1829         to_byte = CHAR_TO_BYTE (to);
  1830     }
  1831 
  1832   del_range_2 (from, from_byte, to, to_byte, 0);
  1833   signal_after_change (from, to - from, 0);
  1834   update_compositions (from, from, CHECK_HEAD);
  1835 }
  1836 
  1837 /* Delete a range of text, specified both as character positions
  1838    and byte positions.  FROM and TO are character positions,
  1839    while FROM_BYTE and TO_BYTE are byte positions.
  1840    If RET_STRING, the deleted area is returned as a string.  */
  1841 
  1842 Lisp_Object
  1843 del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
  1844              ptrdiff_t to, ptrdiff_t to_byte, bool ret_string)
  1845 {
  1846   ptrdiff_t nbytes_del, nchars_del;
  1847   Lisp_Object deletion;
  1848 
  1849   check_markers ();
  1850 
  1851   nchars_del = to - from;
  1852   nbytes_del = to_byte - from_byte;
  1853 
  1854   /* Make sure the gap is somewhere in or next to what we are deleting.  */
  1855   if (from > GPT)
  1856     gap_right (from, from_byte);
  1857   if (to < GPT)
  1858     gap_left (to, to_byte, 0);
  1859 
  1860 #ifdef BYTE_COMBINING_DEBUG
  1861   if (count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
  1862                               Z_BYTE - to_byte, from, from_byte))
  1863     emacs_abort ();
  1864 #endif
  1865 
  1866   if (ret_string || ! EQ (BVAR (current_buffer, undo_list), Qt))
  1867     deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
  1868   else
  1869     deletion = Qnil;
  1870 
  1871   /* Record marker adjustments, and text deletion into undo
  1872      history.  */
  1873   record_delete (from, deletion, true);
  1874 
  1875   /* Relocate all markers pointing into the new, larger gap to point
  1876      at the end of the text before the gap.  */
  1877   adjust_markers_for_delete (from, from_byte, to, to_byte);
  1878 
  1879   modiff_incr (&MODIFF, nchars_del);
  1880   CHARS_MODIFF = MODIFF;
  1881 
  1882   /* Relocate point as if it were a marker.  */
  1883   if (from < PT)
  1884     adjust_point (from - (PT < to ? PT : to),
  1885                   from_byte - (PT_BYTE < to_byte ? PT_BYTE : to_byte));
  1886 
  1887   offset_intervals (current_buffer, from, - nchars_del);
  1888 
  1889   GAP_SIZE += nbytes_del;
  1890   ZV_BYTE -= nbytes_del;
  1891   Z_BYTE -= nbytes_del;
  1892   ZV -= nchars_del;
  1893   Z -= nchars_del;
  1894   GPT = from;
  1895   GPT_BYTE = from_byte;
  1896   if (GAP_SIZE > 0 && !current_buffer->text->inhibit_shrinking)
  1897     /* Put an anchor, unless called from decode_coding_object which
  1898        needs to access the previous gap contents.  */
  1899     *(GPT_ADDR) = 0;
  1900 
  1901   eassert (GPT <= GPT_BYTE);
  1902 
  1903   if (GPT - BEG < BEG_UNCHANGED)
  1904     BEG_UNCHANGED = GPT - BEG;
  1905   if (Z - GPT < END_UNCHANGED)
  1906     END_UNCHANGED = Z - GPT;
  1907 
  1908   check_markers ();
  1909 
  1910 #ifdef HAVE_TREE_SITTER
  1911   eassert (from_byte <= to_byte);
  1912   eassert (from_byte >= 0);
  1913   treesit_record_change (from_byte, to_byte, from_byte);
  1914 #endif
  1915 
  1916   return deletion;
  1917 }
  1918 
  1919 /* Call this if you're about to change the text of current buffer
  1920    from character positions START to END.  This checks the read-only
  1921    properties of the region, calls the necessary modification hooks,
  1922    and warns the next redisplay that it should pay attention to that
  1923    area.  */
  1924 
  1925 void
  1926 modify_text (ptrdiff_t start, ptrdiff_t end)
  1927 {
  1928   prepare_to_modify_buffer (start, end, NULL);
  1929 
  1930   BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end);
  1931   if (MODIFF <= SAVE_MODIFF)
  1932     record_first_change ();
  1933   modiff_incr (&MODIFF, end - start);
  1934   CHARS_MODIFF = MODIFF;
  1935 
  1936   bset_point_before_scroll (current_buffer, Qnil);
  1937 }
  1938 
  1939 /* Signal that we are about to make a change that may result in new
  1940    undo information.
  1941  */
  1942 static void
  1943 run_undoable_change (void)
  1944 {
  1945   if (EQ (BVAR (current_buffer, undo_list), Qt))
  1946     return;
  1947 
  1948   call0 (Qundo_auto__undoable_change);
  1949 }
  1950 
  1951 /* Check that it is okay to modify the buffer between START and END,
  1952    which are char positions.
  1953 
  1954    Run the before-change-function, if any.  If intervals are in use,
  1955    verify that the text to be modified is not read-only, and call
  1956    any modification properties the text may have.
  1957 
  1958    If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
  1959    by holding its value temporarily in a marker.
  1960 
  1961    This function runs Lisp, which means it can GC, which means it can
  1962    compact buffers, including the current buffer being worked on here.
  1963    So don't you dare calling this function while manipulating the gap,
  1964    or during some other similar "critical section".  */
  1965 
  1966 void
  1967 prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
  1968                             ptrdiff_t *preserve_ptr)
  1969 {
  1970   struct buffer *base_buffer;
  1971   Lisp_Object temp;
  1972 
  1973   XSETFASTINT (temp, start);
  1974   if (!NILP (BVAR (current_buffer, read_only)))
  1975     Fbarf_if_buffer_read_only (temp);
  1976 
  1977   /* If we're about to modify a buffer the contents of which come from
  1978      a dump file, copy the contents to private storage first so we
  1979      don't take a COW fault on the buffer text and keep it around
  1980      forever.  */
  1981   if (pdumper_object_p (BEG_ADDR))
  1982     enlarge_buffer_text (current_buffer, 0);
  1983   eassert (!pdumper_object_p (BEG_ADDR));
  1984 
  1985   run_undoable_change();
  1986 
  1987   bset_redisplay (current_buffer);
  1988 
  1989   if (buffer_intervals (current_buffer))
  1990     {
  1991       if (preserve_ptr)
  1992         {
  1993           Lisp_Object preserve_marker;
  1994           preserve_marker = Fcopy_marker (make_fixnum (*preserve_ptr), Qnil);
  1995           verify_interval_modification (current_buffer, start, end);
  1996           *preserve_ptr = marker_position (preserve_marker);
  1997           unchain_marker (XMARKER (preserve_marker));
  1998         }
  1999       else
  2000         verify_interval_modification (current_buffer, start, end);
  2001     }
  2002 
  2003   /* For indirect buffers, use the base buffer to check clashes.  */
  2004   if (current_buffer->base_buffer != 0)
  2005     base_buffer = current_buffer->base_buffer;
  2006   else
  2007     base_buffer = current_buffer;
  2008 
  2009   if (inhibit_modification_hooks)
  2010     return;
  2011 
  2012   if (!NILP (BVAR (base_buffer, file_truename))
  2013       /* Make binding buffer-file-name to nil effective.  */
  2014       && !NILP (BVAR (base_buffer, filename))
  2015       && SAVE_MODIFF >= MODIFF)
  2016     Flock_file (BVAR (base_buffer, file_truename));
  2017 
  2018   /* If `select-active-regions' is non-nil, save the region text.  */
  2019   /* FIXME: Move this to Elisp (via before-change-functions).  */
  2020   if (!NILP (BVAR (current_buffer, mark_active))
  2021       && XMARKER (BVAR (current_buffer, mark))->buffer
  2022       && NILP (Vsaved_region_selection)
  2023       && (EQ (Vselect_active_regions, Qonly)
  2024           ? EQ (CAR_SAFE (Vtransient_mark_mode), Qonly)
  2025           : (!NILP (Vselect_active_regions)
  2026              && !NILP (Vtransient_mark_mode))))
  2027     Vsaved_region_selection
  2028       = call1 (Vregion_extract_function, Qnil);
  2029 
  2030   signal_before_change (start, end, preserve_ptr);
  2031   Fset (Qdeactivate_mark, Qt);
  2032 }
  2033 
  2034 /* Like above, but called when we know that the buffer text
  2035    will be modified and region caches should be invalidated.  */
  2036 
  2037 void
  2038 prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
  2039                           ptrdiff_t *preserve_ptr)
  2040 {
  2041   prepare_to_modify_buffer_1 (start, end, preserve_ptr);
  2042   invalidate_buffer_caches (current_buffer, start, end);
  2043 }
  2044 
  2045 /* Invalidate the caches maintained by the buffer BUF, if any, for the
  2046    region between buffer positions START and END.  */
  2047 void
  2048 invalidate_buffer_caches (struct buffer *buf, ptrdiff_t start, ptrdiff_t end)
  2049 {
  2050   /* Indirect buffers usually have their caches set to NULL, but we
  2051      need to consider the caches of their base buffer.  */
  2052   if (buf->base_buffer)
  2053     buf = buf->base_buffer;
  2054   /* The bidi_paragraph_cache must be invalidated first, because doing
  2055      so might need to use the newline_cache (via find_newline_no_quit,
  2056      see below).  */
  2057   if (buf->bidi_paragraph_cache)
  2058     {
  2059       if (start > BUF_BEG (buf))
  2060         {
  2061           /* If we are deleting or replacing characters, we could
  2062              create a paragraph start, because all of the characters
  2063              from START to the beginning of START's line are
  2064              whitespace.  Therefore, we must extend the region to be
  2065              invalidated up to the newline before START.  Similarly,
  2066              if we are inserting characters immediately after a
  2067              newline, we could create a paragraph start if the
  2068              inserted characters start with a newline.  */
  2069           ptrdiff_t line_beg = start;
  2070           ptrdiff_t start_byte = buf_charpos_to_bytepos (buf, start);
  2071           int prev_char = BUF_FETCH_BYTE (buf, start_byte - 1);
  2072 
  2073           if ((start == end) == (prev_char == '\n'))
  2074             {
  2075               struct buffer *old = current_buffer;
  2076 
  2077               set_buffer_internal (buf);
  2078 
  2079               line_beg = find_newline_no_quit (start, start_byte, -1,
  2080                                                &start_byte);
  2081               set_buffer_internal (old);
  2082             }
  2083           start = line_beg - (line_beg > BUF_BEG (buf));
  2084         }
  2085       invalidate_region_cache (buf,
  2086                                buf->bidi_paragraph_cache,
  2087                                start - BUF_BEG (buf), BUF_Z (buf) - end);
  2088     }
  2089   if (buf->newline_cache)
  2090     invalidate_region_cache (buf,
  2091                              buf->newline_cache,
  2092                              start - BUF_BEG (buf), BUF_Z (buf) - end);
  2093   if (buf->width_run_cache)
  2094     invalidate_region_cache (buf,
  2095                              buf->width_run_cache,
  2096                              start - BUF_BEG (buf), BUF_Z (buf) - end);
  2097 }
  2098 
  2099 /* These macros work with an argument named `preserve_ptr'
  2100    and a local variable named `preserve_marker'.  */
  2101 
  2102 #define PRESERVE_VALUE                                                  \
  2103   if (preserve_ptr && NILP (preserve_marker))                           \
  2104     preserve_marker = Fcopy_marker (make_fixnum (*preserve_ptr), Qnil)
  2105 
  2106 #define RESTORE_VALUE                                           \
  2107   if (! NILP (preserve_marker))                                 \
  2108     {                                                           \
  2109       *preserve_ptr = marker_position (preserve_marker);        \
  2110       unchain_marker (XMARKER (preserve_marker));               \
  2111     }
  2112 
  2113 #define PRESERVE_START_END                      \
  2114   if (NILP (start_marker))                      \
  2115     start_marker = Fcopy_marker (start, Qnil);  \
  2116   if (NILP (end_marker))                        \
  2117     end_marker = Fcopy_marker (end, Qnil);
  2118 
  2119 #define FETCH_START                             \
  2120   (! NILP (start_marker) ? Fmarker_position (start_marker) : start)
  2121 
  2122 #define FETCH_END                               \
  2123   (! NILP (end_marker) ? Fmarker_position (end_marker) : end)
  2124 
  2125 /* Set a variable to nil if an error occurred.
  2126    Don't change the variable if there was no error.
  2127    VAL is a cons-cell (VARIABLE . NO-ERROR-FLAG).
  2128    VARIABLE is the variable to maybe set to nil.
  2129    NO-ERROR-FLAG is nil if there was an error,
  2130    anything else meaning no error (so this function does nothing).  */
  2131 struct rvoe_arg
  2132 {
  2133   Lisp_Object *location;
  2134   bool errorp;
  2135 };
  2136 
  2137 static void
  2138 reset_var_on_error (void *ptr)
  2139 {
  2140   struct rvoe_arg *p = ptr;
  2141   if (p->errorp)
  2142     *p->location = Qnil;
  2143 }
  2144 
  2145 /* Signal a change to the buffer immediately before it happens.
  2146    START_INT and END_INT are the bounds of the text to be changed.
  2147 
  2148    If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
  2149    by holding its value temporarily in a marker.  */
  2150 
  2151 static void
  2152 signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
  2153                       ptrdiff_t *preserve_ptr)
  2154 {
  2155   Lisp_Object start, end;
  2156   Lisp_Object start_marker, end_marker;
  2157   Lisp_Object preserve_marker;
  2158   specpdl_ref count = SPECPDL_INDEX ();
  2159   struct rvoe_arg rvoe_arg;
  2160 
  2161   start = make_fixnum (start_int);
  2162   end = make_fixnum (end_int);
  2163   preserve_marker = Qnil;
  2164   start_marker = Qnil;
  2165   end_marker = Qnil;
  2166 
  2167   specbind (Qinhibit_modification_hooks, Qt);
  2168 
  2169   /* If buffer is unmodified, run a special hook for that case.  The
  2170    check for Vfirst_change_hook is just a minor optimization.  */
  2171   if (SAVE_MODIFF >= MODIFF
  2172       && !NILP (Vfirst_change_hook))
  2173     {
  2174       PRESERVE_VALUE;
  2175       PRESERVE_START_END;
  2176       run_hook (Qfirst_change_hook);
  2177     }
  2178 
  2179   /* Now run the before-change-functions if any.  */
  2180   if (!NILP (Vbefore_change_functions))
  2181     {
  2182       rvoe_arg.location = &Vbefore_change_functions;
  2183       rvoe_arg.errorp = 1;
  2184 
  2185       PRESERVE_VALUE;
  2186       PRESERVE_START_END;
  2187 
  2188       /* Mark before-change-functions to be reset to nil in case of error.  */
  2189       record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
  2190 
  2191       /* Actually run the hook functions.  */
  2192       CALLN (Frun_hook_with_args, Qbefore_change_functions,
  2193              FETCH_START, FETCH_END);
  2194 
  2195       /* There was no error: unarm the reset_on_error.  */
  2196       rvoe_arg.errorp = 0;
  2197     }
  2198 
  2199   if (buffer_has_overlays ())
  2200     {
  2201       PRESERVE_VALUE;
  2202       report_overlay_modification (FETCH_START, FETCH_END, 0,
  2203                                    FETCH_START, FETCH_END, Qnil);
  2204     }
  2205 
  2206   if (! NILP (start_marker))
  2207     detach_marker (start_marker);
  2208   if (! NILP (end_marker))
  2209     detach_marker (end_marker);
  2210   RESTORE_VALUE;
  2211 
  2212   unbind_to (count, Qnil);
  2213 }
  2214 
  2215 /* Signal a change immediately after it happens.
  2216    CHARPOS is the character position of the start of the changed text.
  2217    LENDEL is the number of characters of the text before the change.
  2218    (Not the whole buffer; just the part that was changed.)
  2219    LENINS is the number of characters in that part of the text
  2220    after the change.  */
  2221 
  2222 void
  2223 signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
  2224 {
  2225   specpdl_ref count = SPECPDL_INDEX ();
  2226   struct rvoe_arg rvoe_arg;
  2227   Lisp_Object tmp, save_insert_behind_hooks, save_insert_in_from_hooks;
  2228 
  2229   if (inhibit_modification_hooks)
  2230     return;
  2231 
  2232   /* If we are deferring calls to the after-change functions
  2233      and there are no before-change functions,
  2234      just record the args that we were going to use.  */
  2235   if (! NILP (Vcombine_after_change_calls)
  2236       /* It's OK to defer after-changes even if syntax-ppss-flush-cache
  2237        * is on before-change-functions, which is common enough to be worth
  2238        * adding a special case for it.  */
  2239       && (NILP (Vbefore_change_functions)
  2240           || (CONSP (Vbefore_change_functions)
  2241               && EQ (Qt, XCAR (Vbefore_change_functions))
  2242               && NILP (Fdefault_value (Qbefore_change_functions))
  2243               && CONSP (tmp = XCDR (Vbefore_change_functions))
  2244               && NILP (XCDR (tmp))
  2245               && EQ (XCAR (tmp), Qsyntax_ppss_flush_cache)))
  2246       && !buffer_has_overlays ())
  2247     {
  2248       Lisp_Object elt;
  2249 
  2250       if (!NILP (combine_after_change_list)
  2251           && current_buffer != XBUFFER (combine_after_change_buffer))
  2252         Fcombine_after_change_execute ();
  2253 
  2254       elt = list3i (charpos - BEG, Z - (charpos - lendel + lenins),
  2255                     lenins - lendel);
  2256       combine_after_change_list
  2257         = Fcons (elt, combine_after_change_list);
  2258       combine_after_change_buffer = Fcurrent_buffer ();
  2259 
  2260       return;
  2261     }
  2262 
  2263   /* Save and restore the insert-*-hooks, because other hooks like
  2264      after-change-functions, called below, could clobber them if they
  2265      manipulate text properties.  */
  2266   save_insert_behind_hooks = interval_insert_behind_hooks;
  2267   save_insert_in_from_hooks = interval_insert_in_front_hooks;
  2268 
  2269   if (!NILP (combine_after_change_list))
  2270     Fcombine_after_change_execute ();
  2271 
  2272   specbind (Qinhibit_modification_hooks, Qt);
  2273 
  2274   if (!NILP (Vafter_change_functions))
  2275     {
  2276       rvoe_arg.location = &Vafter_change_functions;
  2277       rvoe_arg.errorp = 1;
  2278 
  2279       /* Mark after-change-functions to be reset to nil in case of error.  */
  2280       record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
  2281 
  2282       /* Actually run the hook functions.  */
  2283       CALLN (Frun_hook_with_args, Qafter_change_functions,
  2284              make_fixnum (charpos), make_fixnum (charpos + lenins),
  2285              make_fixnum (lendel));
  2286 
  2287       /* There was no error: unarm the reset_on_error.  */
  2288       rvoe_arg.errorp = 0;
  2289     }
  2290 
  2291   interval_insert_behind_hooks = save_insert_behind_hooks;
  2292   interval_insert_in_front_hooks = save_insert_in_from_hooks;
  2293 
  2294   if (buffer_has_overlays ())
  2295     report_overlay_modification (make_fixnum (charpos),
  2296                                  make_fixnum (charpos + lenins),
  2297                                  1,
  2298                                  make_fixnum (charpos),
  2299                                  make_fixnum (charpos + lenins),
  2300                                  make_fixnum (lendel));
  2301 
  2302   /* After an insertion, call the text properties
  2303      insert-behind-hooks or insert-in-front-hooks.  */
  2304   if (lendel == 0)
  2305     report_interval_modification (make_fixnum (charpos),
  2306                                   make_fixnum (charpos + lenins));
  2307 
  2308   unbind_to (count, Qnil);
  2309 }
  2310 
  2311 static void
  2312 Fcombine_after_change_execute_1 (Lisp_Object val)
  2313 {
  2314   Vcombine_after_change_calls = val;
  2315 }
  2316 
  2317 DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
  2318        Scombine_after_change_execute, 0, 0, 0,
  2319        doc: /* This function is for use internally in the function `combine-after-change-calls'.  */)
  2320   (void)
  2321 {
  2322   specpdl_ref count = SPECPDL_INDEX ();
  2323   ptrdiff_t beg, end, change;
  2324   ptrdiff_t begpos, endpos;
  2325   Lisp_Object tail;
  2326 
  2327   if (NILP (combine_after_change_list))
  2328     return Qnil;
  2329 
  2330   /* It is rare for combine_after_change_buffer to be invalid, but
  2331      possible.  It can happen when combine-after-change-calls is
  2332      non-nil, and insertion calls a file name handler (e.g. through
  2333      lock_file) which scribbles into a temp file -- cyd  */
  2334   if (!BUFFERP (combine_after_change_buffer)
  2335       || !BUFFER_LIVE_P (XBUFFER (combine_after_change_buffer)))
  2336     {
  2337       combine_after_change_list = Qnil;
  2338       return Qnil;
  2339     }
  2340 
  2341   record_unwind_current_buffer ();
  2342 
  2343   Fset_buffer (combine_after_change_buffer);
  2344 
  2345   /* # chars unchanged at beginning of buffer.  */
  2346   beg = Z - BEG;
  2347   /* # chars unchanged at end of buffer.  */
  2348   end = beg;
  2349   /* Total amount of insertion (negative for deletion).  */
  2350   change = 0;
  2351 
  2352   /* Scan the various individual changes,
  2353      accumulating the range info in BEG, END and CHANGE.  */
  2354   for (tail = combine_after_change_list; CONSP (tail);
  2355        tail = XCDR (tail))
  2356     {
  2357       Lisp_Object elt;
  2358       ptrdiff_t thisbeg, thisend, thischange;
  2359 
  2360       /* Extract the info from the next element.  */
  2361       elt = XCAR (tail);
  2362       if (! CONSP (elt))
  2363         continue;
  2364       thisbeg = XFIXNUM (XCAR (elt));
  2365 
  2366       elt = XCDR (elt);
  2367       if (! CONSP (elt))
  2368         continue;
  2369       thisend = XFIXNUM (XCAR (elt));
  2370 
  2371       elt = XCDR (elt);
  2372       if (! CONSP (elt))
  2373         continue;
  2374       thischange = XFIXNUM (XCAR (elt));
  2375 
  2376       /* Merge this range into the accumulated range.  */
  2377       change += thischange;
  2378       if (thisbeg < beg)
  2379         beg = thisbeg;
  2380       if (thisend < end)
  2381         end = thisend;
  2382     }
  2383 
  2384   /* Get the current start and end positions of the range
  2385      that was changed.  */
  2386   begpos = BEG + beg;
  2387   endpos = Z - end;
  2388 
  2389   /* We are about to handle these, so discard them.  */
  2390   combine_after_change_list = Qnil;
  2391 
  2392   /* Now run the after-change functions for real.
  2393      Turn off the flag that defers them.  */
  2394   record_unwind_protect (Fcombine_after_change_execute_1,
  2395                          Vcombine_after_change_calls);
  2396   signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
  2397   update_compositions (begpos, endpos, CHECK_ALL);
  2398 
  2399   return unbind_to (count, Qnil);
  2400 }
  2401 
  2402 void
  2403 syms_of_insdel (void)
  2404 {
  2405   staticpro (&combine_after_change_list);
  2406   staticpro (&combine_after_change_buffer);
  2407   combine_after_change_list = Qnil;
  2408   combine_after_change_buffer = Qnil;
  2409 
  2410   DEFSYM (Qundo_auto__undoable_change, "undo-auto--undoable-change");
  2411   DEFSYM (Qsyntax_ppss_flush_cache, "syntax-ppss-flush-cache");
  2412 
  2413   DEFVAR_LISP ("combine-after-change-calls", Vcombine_after_change_calls,
  2414                doc: /* Used internally by the function `combine-after-change-calls' macro.  */);
  2415   Vcombine_after_change_calls = Qnil;
  2416 
  2417   DEFVAR_BOOL ("inhibit-modification-hooks", inhibit_modification_hooks,
  2418                doc: /* Non-nil means don't run any of the hooks that respond to buffer changes.
  2419 This affects `before-change-functions' and `after-change-functions',
  2420 as well as hooks attached to text properties and overlays.
  2421 Setting this variable non-nil also inhibits file locks and checks
  2422 whether files are locked by another Emacs session, as well as
  2423 handling of the active region per `select-active-regions'.
  2424 
  2425 To delay change hooks during a series of changes, use
  2426 `combine-change-calls' or `combine-after-change-calls' instead of
  2427 binding this variable.
  2428 
  2429 See also the info node `(elisp) Change Hooks'.  */);
  2430   inhibit_modification_hooks = 0;
  2431   DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks");
  2432 
  2433   defsubr (&Scombine_after_change_execute);
  2434 }

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