* lisp/isearch.el (isearch-mode-map): Bind `C-x 8 RET' to
[bpt/emacs.git] / src / insdel.c
CommitLineData
b45433b3 1/* Buffer insertion/deletion and gap motion for GNU Emacs.
acaf905b 2 Copyright (C) 1985-1986, 1993-1995, 1997-2012
8cabe764 3 Free Software Foundation, Inc.
b45433b3
JB
4
5This file is part of GNU Emacs.
6
9ec0b715 7GNU Emacs is free software: you can redistribute it and/or modify
b45433b3 8it under the terms of the GNU General Public License as published by
9ec0b715
GM
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
b45433b3
JB
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
9ec0b715 18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
b45433b3
JB
19
20
18160b98 21#include <config.h>
06d6db33
PE
22
23#include <intprops.h>
24
b45433b3 25#include "lisp.h"
679194a6 26#include "intervals.h"
811f7370 27#include "character.h"
e5560ff7 28#include "buffer.h"
b45433b3 29#include "window.h"
d014bf88 30#include "blockinput.h"
dfcf069d 31#include "region-cache.h"
b45433b3 32
a08d4ba7
PE
33static void insert_from_string_1 (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t,
34 ptrdiff_t, bool, bool);
35static void insert_from_buffer_1 (struct buffer *, ptrdiff_t, ptrdiff_t, bool);
36static void gap_left (ptrdiff_t, ptrdiff_t, bool);
37static void gap_right (ptrdiff_t, ptrdiff_t);
395ec62e 38
fb2e7d14
RS
39/* List of elements of the form (BEG-UNCHANGED END-UNCHANGED CHANGE-AMOUNT)
40 describing changes which happened while combine_after_change_calls
a08d4ba7 41 was non-nil. We use this to decide how to call them
fb2e7d14
RS
42 once the deferral ends.
43
44 In each element.
45 BEG-UNCHANGED is the number of chars before the changed range.
46 END-UNCHANGED is the number of chars after the changed range,
47 and CHANGE-AMOUNT is the number of characters inserted by the change
48 (negative for a deletion). */
77382fcc 49static Lisp_Object combine_after_change_list;
fb2e7d14
RS
50
51/* Buffer which combine_after_change_list is about. */
77382fcc 52static Lisp_Object combine_after_change_buffer;
27ea0af2
GM
53
54Lisp_Object Qinhibit_modification_hooks;
4889fc82 55
d311d28c 56static void signal_before_change (ptrdiff_t, ptrdiff_t, ptrdiff_t *);
e2688e4a
DA
57
58/* Also used in marker.c to enable expensive marker checks. */
59
60#ifdef MARKER_DEBUG
60ea6052 61
85876d07 62static void
971de7fb 63check_markers (void)
60ea6052 64{
a08d4ba7
PE
65 struct Lisp_Marker *tail;
66 bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters));
60ea6052 67
dab0b04d 68 for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
60ea6052 69 {
dab0b04d 70 if (tail->buffer->text != current_buffer->text)
1088b922 71 emacs_abort ();
dab0b04d 72 if (tail->charpos > Z)
1088b922 73 emacs_abort ();
dab0b04d 74 if (tail->bytepos > Z_BYTE)
1088b922 75 emacs_abort ();
dab0b04d 76 if (multibyte && ! CHAR_HEAD_P (FETCH_BYTE (tail->bytepos)))
1088b922 77 emacs_abort ();
60ea6052
RS
78 }
79}
e2688e4a
DA
80
81#else /* not MARKER_DEBUG */
82
83#define check_markers() do { } while (0)
84
85#endif /* MARKER_DEBUG */
86
3be11131 87/* Move gap to position CHARPOS.
b45433b3
JB
88 Note that this can quit! */
89
c660b094 90void
d311d28c 91move_gap (ptrdiff_t charpos)
b45433b3 92{
3be11131 93 move_gap_both (charpos, charpos_to_bytepos (charpos));
b45433b3
JB
94}
95
3be11131
RS
96/* Move gap to byte position BYTEPOS, which is also char position CHARPOS.
97 Note that this can quit! */
98
99void
d311d28c 100move_gap_both (ptrdiff_t charpos, ptrdiff_t bytepos)
3be11131
RS
101{
102 if (bytepos < GPT_BYTE)
103 gap_left (charpos, bytepos, 0);
104 else if (bytepos > GPT_BYTE)
105 gap_right (charpos, bytepos);
106}
107
108/* Move the gap to a position less than the current GPT.
109 BYTEPOS describes the new position as a byte position,
110 and CHARPOS is the corresponding char position.
a08d4ba7 111 If NEWGAP, then don't update beg_unchanged and end_unchanged. */
b45433b3 112
2bcaed71 113static void
a08d4ba7 114gap_left (ptrdiff_t charpos, ptrdiff_t bytepos, bool newgap)
b45433b3 115{
a08d4ba7
PE
116 unsigned char *to, *from;
117 ptrdiff_t i;
d311d28c 118 ptrdiff_t new_s1;
b45433b3 119
b45433b3 120 if (!newgap)
a50df55b 121 BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
b45433b3 122
3be11131 123 i = GPT_BYTE;
b45433b3
JB
124 to = GAP_END_ADDR;
125 from = GPT_ADDR;
3be11131 126 new_s1 = GPT_BYTE;
b45433b3
JB
127
128 /* Now copy the characters. To move the gap down,
129 copy characters up. */
130
131 while (1)
132 {
133 /* I gets number of characters left to copy. */
3be11131 134 i = new_s1 - bytepos;
b45433b3
JB
135 if (i == 0)
136 break;
137 /* If a quit is requested, stop copying now.
3be11131 138 Change BYTEPOS to be where we have actually moved the gap to. */
b45433b3
JB
139 if (QUITP)
140 {
3be11131
RS
141 bytepos = new_s1;
142 charpos = BYTE_TO_CHAR (bytepos);
b45433b3
JB
143 break;
144 }
145 /* Move at most 32000 chars before checking again for a quit. */
146 if (i > 32000)
147 i = 32000;
72af86bd
AS
148 new_s1 -= i;
149 from -= i, to -= i;
150 memmove (to, from, i);
b45433b3
JB
151 }
152
f868cd8a
JB
153 /* Adjust buffer data structure, to put the gap at BYTEPOS.
154 BYTEPOS is where the loop above stopped, which may be what
155 was specified or may be where a quit was detected. */
3be11131
RS
156 GPT_BYTE = bytepos;
157 GPT = charpos;
e2688e4a 158 eassert (charpos <= bytepos);
469ff680 159 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
b45433b3
JB
160 QUIT;
161}
162
2b34df4e 163/* Move the gap to a position greater than the current GPT.
3be11131
RS
164 BYTEPOS describes the new position as a byte position,
165 and CHARPOS is the corresponding char position. */
166
2bcaed71 167static void
d311d28c 168gap_right (ptrdiff_t charpos, ptrdiff_t bytepos)
b45433b3
JB
169{
170 register unsigned char *to, *from;
d311d28c
PE
171 register ptrdiff_t i;
172 ptrdiff_t new_s1;
b45433b3 173
a50df55b 174 BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
b45433b3 175
3be11131 176 i = GPT_BYTE;
b45433b3
JB
177 from = GAP_END_ADDR;
178 to = GPT_ADDR;
3be11131 179 new_s1 = GPT_BYTE;
b45433b3
JB
180
181 /* Now copy the characters. To move the gap up,
182 copy characters down. */
183
184 while (1)
185 {
186 /* I gets number of characters left to copy. */
3be11131 187 i = bytepos - new_s1;
b45433b3
JB
188 if (i == 0)
189 break;
190 /* If a quit is requested, stop copying now.
3be11131 191 Change BYTEPOS to be where we have actually moved the gap to. */
b45433b3
JB
192 if (QUITP)
193 {
3be11131
RS
194 bytepos = new_s1;
195 charpos = BYTE_TO_CHAR (bytepos);
b45433b3
JB
196 break;
197 }
198 /* Move at most 32000 chars before checking again for a quit. */
199 if (i > 32000)
200 i = 32000;
72af86bd
AS
201 new_s1 += i;
202 memmove (to, from, i);
203 from += i, to += i;
b45433b3
JB
204 }
205
3be11131
RS
206 GPT = charpos;
207 GPT_BYTE = bytepos;
e2688e4a 208 eassert (charpos <= bytepos);
469ff680 209 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
b45433b3
JB
210 QUIT;
211}
2b083808 212\f
3be11131
RS
213/* Adjust all markers for a deletion
214 whose range in bytes is FROM_BYTE to TO_BYTE.
215 The range in charpos is FROM to TO.
216
217 This function assumes that the gap is adjacent to
218 or inside of the range being deleted. */
beecb55b 219
809f3d51 220void
d311d28c
PE
221adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
222 ptrdiff_t to, ptrdiff_t to_byte)
3be11131
RS
223{
224 Lisp_Object marker;
225 register struct Lisp_Marker *m;
d311d28c 226 register ptrdiff_t charpos;
3be11131 227
dab0b04d 228 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
3be11131 229 {
3be11131 230 charpos = m->charpos;
e2688e4a 231 eassert (charpos <= Z);
3be11131
RS
232
233 /* If the marker is after the deletion,
80f6e77c 234 relocate by number of chars / bytes deleted. */
3be11131 235 if (charpos > to)
80f6e77c
RS
236 {
237 m->charpos -= to - from;
238 m->bytepos -= to_byte - from_byte;
239 }
80f6e77c 240 /* Here's the case where a marker is inside text being deleted. */
3be11131
RS
241 else if (charpos > from)
242 {
00de2987 243 if (! m->insertion_type)
dab0b04d 244 { /* Normal markers will end up at the beginning of the
00de2987 245 re-inserted text after undoing a deletion, and must be
177c0ea7 246 adjusted to move them to the correct place. */
dab0b04d 247 XSETMISC (marker, m);
28259cac 248 record_marker_adjustment (marker, from - charpos);
dab0b04d 249 }
00de2987 250 else if (charpos < to)
dab0b04d 251 { /* Before-insertion markers will automatically move forward
00de2987
MB
252 upon re-inserting the deleted text, so we have to arrange
253 for them to move backward to the correct position. */
dab0b04d 254 XSETMISC (marker, m);
28259cac 255 record_marker_adjustment (marker, to - charpos);
dab0b04d 256 }
3be11131 257 m->charpos = from;
80f6e77c 258 m->bytepos = from_byte;
3be11131 259 }
00de2987
MB
260 /* Here's the case where a before-insertion marker is immediately
261 before the deleted region. */
262 else if (charpos == from && m->insertion_type)
263 {
264 /* Undoing the change uses normal insertion, which will
265 incorrectly make MARKER move forward, so we arrange for it
266 to then move backward to the correct place at the beginning
267 of the deleted region. */
dab0b04d 268 XSETMISC (marker, m);
00de2987
MB
269 record_marker_adjustment (marker, to - from);
270 }
3be11131
RS
271 }
272}
23b20a70 273
2b083808 274\f
432f78d2
RS
275/* Adjust markers for an insertion that stretches from FROM / FROM_BYTE
276 to TO / TO_BYTE. We have to relocate the charpos of every marker
277 that points after the insertion (but not their bytepos).
3be11131 278
3be11131
RS
279 When a marker points at the insertion point,
280 we advance it if either its insertion-type is t
281 or BEFORE_MARKERS is true. */
282
283static void
d311d28c 284adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte,
a08d4ba7 285 ptrdiff_t to, ptrdiff_t to_byte, bool before_markers)
beecb55b 286{
dab0b04d 287 struct Lisp_Marker *m;
a08d4ba7 288 bool adjusted = 0;
d311d28c
PE
289 ptrdiff_t nchars = to - from;
290 ptrdiff_t nbytes = to_byte - from_byte;
beecb55b 291
dab0b04d 292 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
beecb55b 293 {
86a56ef3
SM
294 eassert (m->bytepos >= m->charpos
295 && m->bytepos - m->charpos <= Z_BYTE - Z);
1f90a790 296
432f78d2 297 if (m->bytepos == from_byte)
469ff680 298 {
432f78d2
RS
299 if (m->insertion_type || before_markers)
300 {
b16afa45
KH
301 m->bytepos = to_byte;
302 m->charpos = to;
432f78d2
RS
303 if (m->insertion_type)
304 adjusted = 1;
305 }
ce97a2d7 306 }
80f6e77c
RS
307 else if (m->bytepos > from_byte)
308 {
309 m->bytepos += nbytes;
b16afa45 310 m->charpos += nchars;
80f6e77c 311 }
beecb55b 312 }
3be11131
RS
313
314 /* Adjusting only markers whose insertion-type is t may result in
409f2919 315 - disordered start and end in overlays, and
6b61353c 316 - disordered overlays in the slot `overlays_before' of current_buffer. */
469ff680 317 if (adjusted)
6b61353c 318 {
5e617bc2 319 fix_start_end_in_overlays (from, to);
6b61353c
KH
320 fix_overlays_before (current_buffer, from, to);
321 }
beecb55b
RS
322}
323
3be11131
RS
324/* Adjust point for an insertion of NBYTES bytes, which are NCHARS characters.
325
326 This is used only when the value of point changes due to an insert
327 or delete; it does not represent a conceptual change in point as a
328 marker. In particular, point is not crossing any interval
329 boundaries, so there's no need to use the usual SET_PT macro. In
330 fact it would be incorrect to do so, because either the old or the
331 new value of point is out of sync with the current set of
332 intervals. */
333
a27a38d8 334static void
d311d28c 335adjust_point (ptrdiff_t nchars, ptrdiff_t nbytes)
a27a38d8 336{
cffc6f3b 337 SET_BUF_PT_BOTH (current_buffer, PT + nchars, PT_BYTE + nbytes);
3be11131 338 /* In a single-byte buffer, the two positions must be equal. */
86a56ef3 339 eassert (PT_BYTE >= PT && PT_BYTE - PT <= ZV_BYTE - ZV);
a27a38d8 340}
b45433b3 341\f
652838b5
KH
342/* Adjust markers for a replacement of a text at FROM (FROM_BYTE) of
343 length OLD_CHARS (OLD_BYTES) to a new text of length NEW_CHARS
adee4f91
KH
344 (NEW_BYTES). It is assumed that OLD_CHARS > 0, i.e., this is not
345 an insertion. */
652838b5
KH
346
347static void
d311d28c
PE
348adjust_markers_for_replace (ptrdiff_t from, ptrdiff_t from_byte,
349 ptrdiff_t old_chars, ptrdiff_t old_bytes,
350 ptrdiff_t new_chars, ptrdiff_t new_bytes)
652838b5 351{
dab0b04d 352 register struct Lisp_Marker *m;
d311d28c
PE
353 ptrdiff_t prev_to_byte = from_byte + old_bytes;
354 ptrdiff_t diff_chars = new_chars - old_chars;
355 ptrdiff_t diff_bytes = new_bytes - old_bytes;
652838b5 356
dab0b04d 357 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
652838b5 358 {
adee4f91 359 if (m->bytepos >= prev_to_byte)
652838b5 360 {
adee4f91
KH
361 m->charpos += diff_chars;
362 m->bytepos += diff_bytes;
652838b5 363 }
adee4f91 364 else if (m->bytepos > from_byte)
652838b5 365 {
652838b5 366 m->charpos = from;
b16afa45 367 m->bytepos = from_byte;
652838b5 368 }
652838b5 369 }
3019692c 370
e2688e4a 371 check_markers ();
652838b5
KH
372}
373
374\f
99561444
PE
375void
376buffer_overflow (void)
377{
378 error ("Maximum buffer size exceeded");
379}
380
3be11131 381/* Make the gap NBYTES_ADDED bytes longer. */
b45433b3 382
85876d07 383static void
d311d28c 384make_gap_larger (ptrdiff_t nbytes_added)
b45433b3 385{
b45433b3 386 Lisp_Object tem;
d311d28c
PE
387 ptrdiff_t real_gap_loc;
388 ptrdiff_t real_gap_loc_byte;
389 ptrdiff_t old_gap_size;
390 ptrdiff_t current_size = Z_BYTE - BEG_BYTE + GAP_SIZE;
1c8e352f 391 enum { enough_for_a_while = 2000 };
b45433b3 392
1c8e352f
PE
393 if (BUF_BYTES_MAX - current_size < nbytes_added)
394 buffer_overflow ();
b45433b3 395
1c8e352f
PE
396 /* If we have to get more space, get enough to last a while;
397 but do not exceed the maximum buffer size. */
398 nbytes_added = min (nbytes_added + enough_for_a_while,
399 BUF_BYTES_MAX - current_size);
94056516 400
71a7bfa7 401 enlarge_buffer_text (current_buffer, nbytes_added);
b45433b3
JB
402
403 /* Prevent quitting in move_gap. */
404 tem = Vinhibit_quit;
405 Vinhibit_quit = Qt;
406
407 real_gap_loc = GPT;
3be11131 408 real_gap_loc_byte = GPT_BYTE;
b45433b3
JB
409 old_gap_size = GAP_SIZE;
410
411 /* Call the newly allocated space a gap at the end of the whole space. */
412 GPT = Z + GAP_SIZE;
7d92db58 413 GPT_BYTE = Z_BYTE + GAP_SIZE;
3be11131 414 GAP_SIZE = nbytes_added;
b45433b3
JB
415
416 /* Move the new gap down to be consecutive with the end of the old one.
417 This adjusts the markers properly too. */
3be11131 418 gap_left (real_gap_loc + old_gap_size, real_gap_loc_byte + old_gap_size, 1);
b45433b3
JB
419
420 /* Now combine the two into one large gap. */
421 GAP_SIZE += old_gap_size;
422 GPT = real_gap_loc;
3be11131 423 GPT_BYTE = real_gap_loc_byte;
b45433b3 424
469ff680
KH
425 /* Put an anchor. */
426 *(Z_ADDR) = 0;
427
b45433b3
JB
428 Vinhibit_quit = tem;
429}
8af5b8e7 430
b58c5c4a 431#if defined USE_MMAP_FOR_BUFFERS || defined REL_ALLOC || defined DOUG_LEA_MALLOC
8af5b8e7 432
7273faa1 433/* Make the gap NBYTES_REMOVED bytes shorter. */
8af5b8e7 434
85876d07 435static void
d311d28c 436make_gap_smaller (ptrdiff_t nbytes_removed)
8af5b8e7
AI
437{
438 Lisp_Object tem;
d311d28c
PE
439 ptrdiff_t real_gap_loc;
440 ptrdiff_t real_gap_loc_byte;
441 ptrdiff_t real_Z;
442 ptrdiff_t real_Z_byte;
443 ptrdiff_t real_beg_unchanged;
444 ptrdiff_t new_gap_size;
8af5b8e7
AI
445
446 /* Make sure the gap is at least 20 bytes. */
447 if (GAP_SIZE - nbytes_removed < 20)
448 nbytes_removed = GAP_SIZE - 20;
449
450 /* Prevent quitting in move_gap. */
451 tem = Vinhibit_quit;
452 Vinhibit_quit = Qt;
453
454 real_gap_loc = GPT;
455 real_gap_loc_byte = GPT_BYTE;
73df3b72 456 new_gap_size = GAP_SIZE - nbytes_removed;
8af5b8e7
AI
457 real_Z = Z;
458 real_Z_byte = Z_BYTE;
73df3b72 459 real_beg_unchanged = BEG_UNCHANGED;
8af5b8e7
AI
460
461 /* Pretend that the last unwanted part of the gap is the entire gap,
462 and that the first desired part of the gap is part of the buffer
463 text. */
72af86bd 464 memset (GPT_ADDR, 0, new_gap_size);
73df3b72
KS
465 GPT += new_gap_size;
466 GPT_BYTE += new_gap_size;
467 Z += new_gap_size;
468 Z_BYTE += new_gap_size;
8af5b8e7
AI
469 GAP_SIZE = nbytes_removed;
470
471 /* Move the unwanted pretend gap to the end of the buffer. This
472 adjusts the markers properly too. */
473 gap_right (Z, Z_BYTE);
474
475 enlarge_buffer_text (current_buffer, -nbytes_removed);
476
477 /* Now restore the desired gap. */
73df3b72 478 GAP_SIZE = new_gap_size;
8af5b8e7
AI
479 GPT = real_gap_loc;
480 GPT_BYTE = real_gap_loc_byte;
481 Z = real_Z;
482 Z_BYTE = real_Z_byte;
73df3b72 483 BEG_UNCHANGED = real_beg_unchanged;
8af5b8e7
AI
484
485 /* Put an anchor. */
486 *(Z_ADDR) = 0;
487
488 Vinhibit_quit = tem;
489}
490
b58c5c4a
PE
491#endif /* USE_MMAP_FOR_BUFFERS || REL_ALLOC || DOUG_LEA_MALLOC */
492
8af5b8e7 493void
d311d28c 494make_gap (ptrdiff_t nbytes_added)
8af5b8e7
AI
495{
496 if (nbytes_added >= 0)
497 make_gap_larger (nbytes_added);
d77fbc16 498#if defined USE_MMAP_FOR_BUFFERS || defined REL_ALLOC || defined DOUG_LEA_MALLOC
8af5b8e7
AI
499 else
500 make_gap_smaller (-nbytes_added);
501#endif
502}
b45433b3 503\f
2b083808
RS
504/* Copy NBYTES bytes of text from FROM_ADDR to TO_ADDR.
505 FROM_MULTIBYTE says whether the incoming text is multibyte.
506 TO_MULTIBYTE says whether to store the text as multibyte.
507 If FROM_MULTIBYTE != TO_MULTIBYTE, we convert.
508
509 Return the number of bytes stored at TO_ADDR. */
510
d311d28c 511ptrdiff_t
ae19ba7c 512copy_text (const unsigned char *from_addr, unsigned char *to_addr,
a08d4ba7 513 ptrdiff_t nbytes, bool from_multibyte, bool to_multibyte)
2b083808
RS
514{
515 if (from_multibyte == to_multibyte)
516 {
72af86bd 517 memcpy (to_addr, from_addr, nbytes);
2b083808
RS
518 return nbytes;
519 }
520 else if (from_multibyte)
521 {
d311d28c
PE
522 ptrdiff_t nchars = 0;
523 ptrdiff_t bytes_left = nbytes;
f44cbcd8 524
2b083808
RS
525 while (bytes_left > 0)
526 {
b16afa45 527 int thislen, c;
62a6e103 528 c = STRING_CHAR_AND_LENGTH (from_addr, thislen);
1ede3eb6
KH
529 if (! ASCII_CHAR_P (c))
530 c &= 0xFF;
f44cbcd8 531 *to_addr++ = c;
2b083808 532 from_addr += thislen;
7e79b8e0 533 bytes_left -= thislen;
2b083808
RS
534 nchars++;
535 }
536 return nchars;
537 }
538 else
539 {
540 unsigned char *initial_to_addr = to_addr;
541
542 /* Convert single-byte to multibyte. */
543 while (nbytes > 0)
544 {
545 int c = *from_addr++;
2b083808 546
4c0354d7 547 if (!ASCII_CHAR_P (c))
2b083808 548 {
4c0354d7 549 c = BYTE8_TO_CHAR (c);
0ef71121 550 to_addr += CHAR_STRING (c, to_addr);
2b083808
RS
551 nbytes--;
552 }
553 else
554 /* Special case for speed. */
555 *to_addr++ = c, nbytes--;
556 }
557 return to_addr - initial_to_addr;
558 }
559}
2b083808 560\f
b45433b3 561/* Insert a string of specified length before point.
2b083808
RS
562 This function judges multibyteness based on
563 enable_multibyte_characters in the current buffer;
564 it never converts between single-byte and multibyte.
565
ef29f213
KH
566 DO NOT use this for the contents of a Lisp string or a Lisp buffer!
567 prepare_to_modify_buffer could relocate the text. */
b45433b3 568
c660b094 569void
d311d28c 570insert (const char *string, ptrdiff_t nbytes)
b45433b3 571{
3be11131 572 if (nbytes > 0)
395ec62e 573 {
d311d28c 574 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
575 insert_1_both (string, len, nbytes, 0, 1, 0);
576 opoint = PT - len;
577 signal_after_change (opoint, 0, len);
0ef71121 578 update_compositions (opoint, PT, CHECK_BORDER);
cd11ef31
RS
579 }
580}
581
2b083808
RS
582/* Likewise, but inherit text properties from neighboring characters. */
583
c660b094 584void
d311d28c 585insert_and_inherit (const char *string, ptrdiff_t nbytes)
cd11ef31 586{
3be11131 587 if (nbytes > 0)
cd11ef31 588 {
d311d28c 589 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
590 insert_1_both (string, len, nbytes, 1, 1, 0);
591 opoint = PT - len;
592 signal_after_change (opoint, 0, len);
0ef71121 593 update_compositions (opoint, PT, CHECK_BORDER);
395ec62e
KH
594 }
595}
b45433b3 596
2b083808 597/* Insert the character C before point. Do not inherit text properties. */
3be11131 598
c660b094 599void
ae19ba7c 600insert_char (int c)
3be11131 601{
0ef71121 602 unsigned char str[MAX_MULTIBYTE_LENGTH];
2b083808
RS
603 int len;
604
4b4deea2 605 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
0ef71121 606 len = CHAR_STRING (c, str);
2b083808
RS
607 else
608 {
609 len = 1;
0ef71121 610 str[0] = c;
2b083808 611 }
3be11131 612
b68864e5 613 insert ((char *) str, len);
3be11131
RS
614}
615
2b083808 616/* Insert the null-terminated string S before point. */
3be11131
RS
617
618void
ae19ba7c 619insert_string (const char *s)
3be11131
RS
620{
621 insert (s, strlen (s));
622}
623
624/* Like `insert' except that all markers pointing at the place where
625 the insertion happens are adjusted to point after it.
626 Don't use this function to insert part of a Lisp string,
627 since gc could happen and relocate it. */
628
629void
d311d28c 630insert_before_markers (const char *string, ptrdiff_t nbytes)
3be11131
RS
631{
632 if (nbytes > 0)
633 {
d311d28c 634 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
635 insert_1_both (string, len, nbytes, 0, 1, 1);
636 opoint = PT - len;
637 signal_after_change (opoint, 0, len);
0ef71121 638 update_compositions (opoint, PT, CHECK_BORDER);
3be11131
RS
639 }
640}
641
2b083808
RS
642/* Likewise, but inherit text properties from neighboring characters. */
643
3be11131 644void
b68864e5 645insert_before_markers_and_inherit (const char *string,
d311d28c 646 ptrdiff_t nbytes)
3be11131
RS
647{
648 if (nbytes > 0)
649 {
d311d28c 650 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
651 insert_1_both (string, len, nbytes, 1, 1, 1);
652 opoint = PT - len;
653 signal_after_change (opoint, 0, len);
0ef71121 654 update_compositions (opoint, PT, CHECK_BORDER);
3be11131
RS
655 }
656}
1f90a790 657
3be11131
RS
658/* Subroutine used by the insert functions above. */
659
660void
d311d28c 661insert_1 (const char *string, ptrdiff_t nbytes,
a08d4ba7 662 bool inherit, bool prepare, bool before_markers)
395ec62e 663{
b68864e5
PE
664 insert_1_both (string, chars_in_text ((unsigned char *) string, nbytes),
665 nbytes, inherit, prepare, before_markers);
432f78d2 666}
b16afa45 667
1f90a790 668\f
b16afa45
KH
669#ifdef BYTE_COMBINING_DEBUG
670
432f78d2
RS
671/* See if the bytes before POS/POS_BYTE combine with bytes
672 at the start of STRING to form a single character.
ce97a2d7 673 If so, return the number of bytes at the start of STRING
432f78d2 674 which combine in this way. Otherwise, return 0. */
b45433b3 675
432f78d2 676int
d311d28c
PE
677count_combining_before (const unsigned char *string, ptrdiff_t length,
678 ptrdiff_t pos, ptrdiff_t pos_byte)
432f78d2 679{
cae184f2 680 int len, combining_bytes;
641a26d2 681 const unsigned char *p;
b45433b3 682
432f78d2
RS
683 if (NILP (current_buffer->enable_multibyte_characters))
684 return 0;
cae184f2
KH
685
686 /* At first, we can exclude the following cases:
687 (1) STRING[0] can't be a following byte of multibyte sequence.
688 (2) POS is the start of the current buffer.
689 (3) A character before POS is not a multibyte character. */
690 if (length == 0 || CHAR_HEAD_P (*string)) /* case (1) */
432f78d2 691 return 0;
cae184f2 692 if (pos_byte == BEG_BYTE) /* case (2) */
432f78d2 693 return 0;
cae184f2
KH
694 len = 1;
695 p = BYTE_POS_ADDR (pos_byte - 1);
696 while (! CHAR_HEAD_P (*p)) p--, len++;
409f2919 697 if (! LEADING_CODE_P (*p)) /* case (3) */
432f78d2 698 return 0;
cae184f2 699
cae184f2
KH
700 combining_bytes = BYTES_BY_CHAR_HEAD (*p) - len;
701 if (combining_bytes <= 0)
702 /* The character preceding POS is, complete and no room for
703 combining bytes (combining_bytes == 0), or an independent 8-bit
704 character (combining_bytes < 0). */
432f78d2 705 return 0;
ce97a2d7 706
cae184f2
KH
707 /* We have a combination situation. Count the bytes at STRING that
708 may combine. */
709 p = string + 1;
ce97a2d7
RS
710 while (!CHAR_HEAD_P (*p) && p < string + length)
711 p++;
712
cae184f2 713 return (combining_bytes < p - string ? combining_bytes : p - string);
432f78d2 714}
b45433b3 715
432f78d2
RS
716/* See if the bytes after POS/POS_BYTE combine with bytes
717 at the end of STRING to form a single character.
718 If so, return the number of bytes after POS/POS_BYTE
719 which combine in this way. Otherwise, return 0. */
679194a6 720
432f78d2 721int
ae19ba7c 722count_combining_after (const unsigned char *string,
d311d28c 723 ptrdiff_t length, ptrdiff_t pos, ptrdiff_t pos_byte)
432f78d2 724{
d311d28c
PE
725 ptrdiff_t opos_byte = pos_byte;
726 ptrdiff_t i;
727 ptrdiff_t bytes;
cae184f2 728 unsigned char *bufp;
3be11131 729
432f78d2
RS
730 if (NILP (current_buffer->enable_multibyte_characters))
731 return 0;
cae184f2
KH
732
733 /* At first, we can exclude the following cases:
734 (1) The last byte of STRING is an ASCII.
735 (2) POS is the last of the current buffer.
736 (3) A character at POS can't be a following byte of multibyte
737 character. */
738 if (length > 0 && ASCII_BYTE_P (string[length - 1])) /* case (1) */
739 return 0;
740 if (pos_byte == Z_BYTE) /* case (2) */
432f78d2 741 return 0;
cae184f2
KH
742 bufp = BYTE_POS_ADDR (pos_byte);
743 if (CHAR_HEAD_P (*bufp)) /* case (3) */
744 return 0;
745
432f78d2 746 i = length - 1;
b57a7b0b 747 while (i >= 0 && ! CHAR_HEAD_P (string[i]))
432f78d2
RS
748 {
749 i--;
750 }
b57a7b0b
KH
751 if (i < 0)
752 {
cae184f2
KH
753 /* All characters in STRING are not character head. We must
754 check also preceding bytes at POS. We are sure that the gap
755 is at POS. */
756 unsigned char *p = BEG_ADDR;
b57a7b0b 757 i = pos_byte - 2;
cae184f2 758 while (i >= 0 && ! CHAR_HEAD_P (p[i]))
b57a7b0b 759 i--;
409f2919 760 if (i < 0 || !LEADING_CODE_P (p[i]))
b57a7b0b 761 return 0;
0ef71121 762
cae184f2
KH
763 bytes = BYTES_BY_CHAR_HEAD (p[i]);
764 return (bytes <= pos_byte - 1 - i + length
765 ? 0
766 : bytes - (pos_byte - 1 - i + length));
b57a7b0b 767 }
409f2919 768 if (!LEADING_CODE_P (string[i]))
432f78d2
RS
769 return 0;
770
cae184f2
KH
771 bytes = BYTES_BY_CHAR_HEAD (string[i]) - (length - i);
772 bufp++, pos_byte++;
773 while (!CHAR_HEAD_P (*bufp)) bufp++, pos_byte++;
cd11ef31 774
cae184f2 775 return (bytes <= pos_byte - opos_byte ? bytes : pos_byte - opos_byte);
b45433b3 776}
2b083808 777
b16afa45 778#endif
9f3ede3c 779
1f90a790 780\f
2b083808
RS
781/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
782 starting at STRING. INHERIT, PREPARE and BEFORE_MARKERS
783 are the same as in insert_1. */
784
785void
b68864e5 786insert_1_both (const char *string,
d311d28c 787 ptrdiff_t nchars, ptrdiff_t nbytes,
a08d4ba7 788 bool inherit, bool prepare, bool before_markers)
2b083808 789{
da80a8d5
GM
790 if (nchars == 0)
791 return;
177c0ea7 792
4b4deea2 793 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
57404188
KH
794 nchars = nbytes;
795
35d63725
RS
796 if (prepare)
797 /* Do this before moving and increasing the gap,
798 because the before-change hooks might move the gap
799 or make it smaller. */
800 prepare_to_modify_buffer (PT, PT, NULL);
801
2b083808
RS
802 if (PT != GPT)
803 move_gap_both (PT, PT_BYTE);
804 if (GAP_SIZE < nbytes)
805 make_gap (nbytes - GAP_SIZE);
806
b16afa45
KH
807#ifdef BYTE_COMBINING_DEBUG
808 if (count_combining_before (string, nbytes, PT, PT_BYTE)
809 || count_combining_after (string, nbytes, PT, PT_BYTE))
1088b922 810 emacs_abort ();
b16afa45 811#endif
432f78d2
RS
812
813 /* Record deletion of the surrounding text that combines with
814 the insertion. This, together with recording the insertion,
b16afa45
KH
815 will add up to the right stuff in the undo list. */
816 record_insert (PT, nchars);
2b083808 817 MODIFF++;
3e145152 818 CHARS_MODIFF = MODIFF;
2b083808 819
72af86bd 820 memcpy (GPT_ADDR, string, nbytes);
2b083808 821
2b083808 822 GAP_SIZE -= nbytes;
1f90a790
RS
823 GPT += nchars;
824 ZV += nchars;
825 Z += nchars;
2b083808
RS
826 GPT_BYTE += nbytes;
827 ZV_BYTE += nbytes;
828 Z_BYTE += nbytes;
829 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1f90a790 830
e2688e4a 831 eassert (GPT <= GPT_BYTE);
1f90a790 832
9dde4e0c
KS
833 /* The insert may have been in the unchanged region, so check again. */
834 if (Z - GPT < END_UNCHANGED)
835 END_UNCHANGED = Z - GPT;
836
1f90a790 837 adjust_overlays_for_insert (PT, nchars);
432f78d2 838 adjust_markers_for_insert (PT, PT_BYTE,
1f90a790 839 PT + nchars, PT_BYTE + nbytes,
2b083808 840 before_markers);
ce97a2d7 841
2f294edf 842 offset_intervals (current_buffer, PT, nchars);
1f90a790 843
0c94c8d6 844 if (!inherit && buffer_intervals (current_buffer))
f2cab2ea
GM
845 set_text_properties (make_number (PT), make_number (PT + nchars),
846 Qnil, Qnil, Qnil);
ce97a2d7 847
b16afa45 848 adjust_point (nchars, nbytes);
828f1ed3 849
e2688e4a 850 check_markers ();
2b083808 851}
3be11131 852\f
679194a6 853/* Insert the part of the text of STRING, a Lisp object assumed to be
2b083808
RS
854 of type string, consisting of the LENGTH characters (LENGTH_BYTE bytes)
855 starting at position POS / POS_BYTE. If the text of STRING has properties,
856 copy them into the buffer.
679194a6
JA
857
858 It does not work to use `insert' for this, because a GC could happen
72af86bd 859 before we copy the stuff into the buffer, and relocate the string
7e1ea612 860 without insert noticing. */
679194a6 861
c660b094 862void
d311d28c 863insert_from_string (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
a08d4ba7 864 ptrdiff_t length, ptrdiff_t length_byte, bool inherit)
395ec62e 865{
d311d28c 866 ptrdiff_t opoint = PT;
d8bf4256
RS
867
868 if (SCHARS (string) == 0)
869 return;
870
62b82678
RS
871 insert_from_string_1 (string, pos, pos_byte, length, length_byte,
872 inherit, 0);
873 signal_after_change (opoint, 0, PT - opoint);
0ef71121 874 update_compositions (opoint, PT, CHECK_BORDER);
395ec62e
KH
875}
876
2b083808
RS
877/* Like `insert_from_string' except that all markers pointing
878 at the place where the insertion happens are adjusted to point after it. */
3be11131
RS
879
880void
ae19ba7c 881insert_from_string_before_markers (Lisp_Object string,
d311d28c
PE
882 ptrdiff_t pos, ptrdiff_t pos_byte,
883 ptrdiff_t length, ptrdiff_t length_byte,
a08d4ba7 884 bool inherit)
3be11131 885{
d311d28c 886 ptrdiff_t opoint = PT;
d8bf4256
RS
887
888 if (SCHARS (string) == 0)
889 return;
890
62b82678
RS
891 insert_from_string_1 (string, pos, pos_byte, length, length_byte,
892 inherit, 1);
893 signal_after_change (opoint, 0, PT - opoint);
0ef71121 894 update_compositions (opoint, PT, CHECK_BORDER);
3be11131
RS
895}
896
897/* Subroutine of the insertion functions above. */
898
899static void
d311d28c
PE
900insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
901 ptrdiff_t nchars, ptrdiff_t nbytes,
a08d4ba7 902 bool inherit, bool before_markers)
b45433b3 903{
b45433b3 904 struct gcpro gcpro1;
d311d28c 905 ptrdiff_t outgoing_nbytes = nbytes;
ce97a2d7 906 INTERVAL intervals;
2b083808
RS
907
908 /* Make OUTGOING_NBYTES describe the text
909 as it will be inserted in this buffer. */
910
4b4deea2 911 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
2b083808 912 outgoing_nbytes = nchars;
2a1d8be0 913 else if (! STRING_MULTIBYTE (string))
2b083808 914 outgoing_nbytes
f7e233a8 915 = count_size_as_multibyte (SDATA (string) + pos_byte,
2b083808 916 nbytes);
b45433b3 917
b45433b3 918 GCPRO1 (string);
35d63725
RS
919 /* Do this before moving and increasing the gap,
920 because the before-change hooks might move the gap
921 or make it smaller. */
d206af14 922 prepare_to_modify_buffer (PT, PT, NULL);
b45433b3 923
2bcaed71 924 if (PT != GPT)
3be11131 925 move_gap_both (PT, PT_BYTE);
534b9840 926 if (GAP_SIZE < outgoing_nbytes)
2b083808 927 make_gap (outgoing_nbytes - GAP_SIZE);
b45433b3
JB
928 UNGCPRO;
929
2b083808
RS
930 /* Copy the string text into the buffer, perhaps converting
931 between single-byte and multibyte. */
d5db4077 932 copy_text (SDATA (string) + pos_byte, GPT_ADDR, nbytes,
2a1d8be0 933 STRING_MULTIBYTE (string),
4b4deea2 934 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
b45433b3 935
b16afa45 936#ifdef BYTE_COMBINING_DEBUG
432f78d2
RS
937 /* We have copied text into the gap, but we have not altered
938 PT or PT_BYTE yet. So we can pass PT and PT_BYTE
939 to these functions and get the same results as we would
940 have got earlier on. Meanwhile, PT_ADDR does point to
941 the text that has been stored by copy_text. */
b16afa45
KH
942 if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
943 || count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
1088b922 944 emacs_abort ();
b16afa45 945#endif
432f78d2 946
b16afa45 947 record_insert (PT, nchars);
432f78d2 948 MODIFF++;
3e145152 949 CHARS_MODIFF = MODIFF;
432f78d2 950
7792090e 951 GAP_SIZE -= outgoing_nbytes;
1f90a790
RS
952 GPT += nchars;
953 ZV += nchars;
954 Z += nchars;
2b083808
RS
955 GPT_BYTE += outgoing_nbytes;
956 ZV_BYTE += outgoing_nbytes;
957 Z_BYTE += outgoing_nbytes;
469ff680 958 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
3be11131 959
e2688e4a 960 eassert (GPT <= GPT_BYTE);
679194a6 961
9dde4e0c
KS
962 /* The insert may have been in the unchanged region, so check again. */
963 if (Z - GPT < END_UNCHANGED)
964 END_UNCHANGED = Z - GPT;
965
1f90a790
RS
966 adjust_overlays_for_insert (PT, nchars);
967 adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
968 PT_BYTE + outgoing_nbytes,
1f90a790 969 before_markers);
ce97a2d7 970
1f90a790
RS
971 offset_intervals (current_buffer, PT, nchars);
972
0c94c8d6 973 intervals = string_intervals (string);
b16afa45 974 /* Get the intervals for the part of the string we are inserting. */
d5db4077 975 if (nbytes < SBYTES (string))
1f90a790 976 intervals = copy_intervals (intervals, pos, nchars);
177c0ea7 977
ce97a2d7 978 /* Insert those intervals. */
1f90a790 979 graft_intervals_into_buffer (intervals, PT, nchars,
9391e591 980 current_buffer, inherit);
ce97a2d7 981
b16afa45 982 adjust_point (nchars, outgoing_nbytes);
8f924df7 983
e2688e4a 984 check_markers ();
8f924df7
KH
985}
986\f
987/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
988 starting at GPT_ADDR. */
989
990void
d311d28c 991insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes)
8f924df7 992{
4b4deea2 993 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
8f924df7
KH
994 nchars = nbytes;
995
996 record_insert (GPT, nchars);
997 MODIFF++;
998
999 GAP_SIZE -= nbytes;
1000 GPT += nchars;
1001 ZV += nchars;
1002 Z += nchars;
1003 GPT_BYTE += nbytes;
1004 ZV_BYTE += nbytes;
1005 Z_BYTE += nbytes;
1006 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1007
e2688e4a 1008 eassert (GPT <= GPT_BYTE);
8f924df7 1009
c669988b
KH
1010 adjust_overlays_for_insert (GPT - nchars, nchars);
1011 adjust_markers_for_insert (GPT - nchars, GPT_BYTE - nbytes,
1012 GPT, GPT_BYTE, 0);
8f924df7 1013
0c94c8d6 1014 if (buffer_intervals (current_buffer))
c669988b
KH
1015 {
1016 offset_intervals (current_buffer, GPT - nchars, nchars);
77c7bcb1 1017 graft_intervals_into_buffer (NULL, GPT - nchars, nchars,
c669988b
KH
1018 current_buffer, 0);
1019 }
8f924df7
KH
1020
1021 if (GPT - nchars < PT)
1022 adjust_point (nchars, nbytes);
1023
e2688e4a 1024 check_markers ();
b45433b3 1025}
3be11131
RS
1026\f
1027/* Insert text from BUF, NCHARS characters starting at CHARPOS, into the
ef29f213
KH
1028 current buffer. If the text in BUF has properties, they are absorbed
1029 into the current buffer.
1030
1031 It does not work to use `insert' for this, because a malloc could happen
72af86bd 1032 and relocate BUF's text before the copy happens. */
ef29f213
KH
1033
1034void
ae19ba7c 1035insert_from_buffer (struct buffer *buf,
a08d4ba7 1036 ptrdiff_t charpos, ptrdiff_t nchars, bool inherit)
ef29f213 1037{
d311d28c 1038 ptrdiff_t opoint = PT;
3be11131 1039
62b82678
RS
1040 insert_from_buffer_1 (buf, charpos, nchars, inherit);
1041 signal_after_change (opoint, 0, PT - opoint);
0ef71121 1042 update_compositions (opoint, PT, CHECK_BORDER);
ef29f213
KH
1043}
1044
1045static void
ae19ba7c 1046insert_from_buffer_1 (struct buffer *buf,
a08d4ba7 1047 ptrdiff_t from, ptrdiff_t nchars, bool inherit)
ef29f213 1048{
d311d28c
PE
1049 ptrdiff_t chunk, chunk_expanded;
1050 ptrdiff_t from_byte = buf_charpos_to_bytepos (buf, from);
1051 ptrdiff_t to_byte = buf_charpos_to_bytepos (buf, from + nchars);
1052 ptrdiff_t incoming_nbytes = to_byte - from_byte;
1053 ptrdiff_t outgoing_nbytes = incoming_nbytes;
ce97a2d7 1054 INTERVAL intervals;
2b083808
RS
1055
1056 /* Make OUTGOING_NBYTES describe the text
1057 as it will be inserted in this buffer. */
1058
4b4deea2 1059 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
2b083808 1060 outgoing_nbytes = nchars;
4b4deea2 1061 else if (NILP (BVAR (buf, enable_multibyte_characters)))
4468c4f1 1062 {
d311d28c
PE
1063 ptrdiff_t outgoing_before_gap = 0;
1064 ptrdiff_t outgoing_after_gap = 0;
ef29f213 1065
4468c4f1
KH
1066 if (from < BUF_GPT (buf))
1067 {
1068 chunk = BUF_GPT_BYTE (buf) - from_byte;
1069 if (chunk > incoming_nbytes)
1070 chunk = incoming_nbytes;
1071 outgoing_before_gap
1072 = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, from_byte),
1073 chunk);
1074 }
1075 else
1076 chunk = 0;
1077
1078 if (chunk < incoming_nbytes)
1079 outgoing_after_gap
177c0ea7 1080 = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf,
4468c4f1
KH
1081 from_byte + chunk),
1082 incoming_nbytes - chunk);
1083
1084 outgoing_nbytes = outgoing_before_gap + outgoing_after_gap;
1085 }
177c0ea7 1086
35d63725
RS
1087 /* Do this before moving and increasing the gap,
1088 because the before-change hooks might move the gap
1089 or make it smaller. */
d206af14 1090 prepare_to_modify_buffer (PT, PT, NULL);
ef29f213
KH
1091
1092 if (PT != GPT)
3be11131 1093 move_gap_both (PT, PT_BYTE);
2b083808
RS
1094 if (GAP_SIZE < outgoing_nbytes)
1095 make_gap (outgoing_nbytes - GAP_SIZE);
ef29f213 1096
3be11131 1097 if (from < BUF_GPT (buf))
ef29f213 1098 {
3be11131 1099 chunk = BUF_GPT_BYTE (buf) - from_byte;
2b083808
RS
1100 if (chunk > incoming_nbytes)
1101 chunk = incoming_nbytes;
4468c4f1
KH
1102 /* Record number of output bytes, so we know where
1103 to put the output from the second copy_text. */
1104 chunk_expanded
1105 = copy_text (BUF_BYTE_ADDRESS (buf, from_byte),
1106 GPT_ADDR, chunk,
4b4deea2
TT
1107 ! NILP (BVAR (buf, enable_multibyte_characters)),
1108 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
ef29f213
KH
1109 }
1110 else
4468c4f1
KH
1111 chunk_expanded = chunk = 0;
1112
2b083808
RS
1113 if (chunk < incoming_nbytes)
1114 copy_text (BUF_BYTE_ADDRESS (buf, from_byte + chunk),
4468c4f1 1115 GPT_ADDR + chunk_expanded, incoming_nbytes - chunk,
4b4deea2
TT
1116 ! NILP (BVAR (buf, enable_multibyte_characters)),
1117 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
ef29f213 1118
b16afa45 1119#ifdef BYTE_COMBINING_DEBUG
432f78d2
RS
1120 /* We have copied text into the gap, but we have not altered
1121 PT or PT_BYTE yet. So we can pass PT and PT_BYTE
1122 to these functions and get the same results as we would
1f90a790 1123 have got earlier on. Meanwhile, GPT_ADDR does point to
432f78d2 1124 the text that has been stored by copy_text. */
b16afa45
KH
1125 if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
1126 || count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
1088b922 1127 emacs_abort ();
b16afa45 1128#endif
432f78d2 1129
b16afa45 1130 record_insert (PT, nchars);
432f78d2 1131 MODIFF++;
3e145152 1132 CHARS_MODIFF = MODIFF;
432f78d2 1133
2b083808 1134 GAP_SIZE -= outgoing_nbytes;
1f90a790
RS
1135 GPT += nchars;
1136 ZV += nchars;
1137 Z += nchars;
2b083808
RS
1138 GPT_BYTE += outgoing_nbytes;
1139 ZV_BYTE += outgoing_nbytes;
1140 Z_BYTE += outgoing_nbytes;
469ff680 1141 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1f90a790 1142
e2688e4a 1143 eassert (GPT <= GPT_BYTE);
1f90a790 1144
9dde4e0c
KS
1145 /* The insert may have been in the unchanged region, so check again. */
1146 if (Z - GPT < END_UNCHANGED)
1147 END_UNCHANGED = Z - GPT;
1148
1f90a790
RS
1149 adjust_overlays_for_insert (PT, nchars);
1150 adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
432f78d2 1151 PT_BYTE + outgoing_nbytes,
b16afa45 1152 0);
ce97a2d7 1153
2f294edf 1154 offset_intervals (current_buffer, PT, nchars);
ce97a2d7 1155
b16afa45 1156 /* Get the intervals for the part of the string we are inserting. */
0c94c8d6 1157 intervals = buffer_intervals (buf);
f1a6b216 1158 if (nchars < BUF_Z (buf) - BUF_BEG (buf))
06d8227a
GM
1159 {
1160 if (buf == current_buffer && PT <= from)
1161 from += nchars;
1162 intervals = copy_intervals (intervals, from, nchars);
1163 }
177c0ea7 1164
ce97a2d7 1165 /* Insert those intervals. */
1f90a790 1166 graft_intervals_into_buffer (intervals, PT, nchars, current_buffer, inherit);
ce97a2d7 1167
b16afa45 1168 adjust_point (nchars, outgoing_nbytes);
b45433b3
JB
1169}
1170\f
652838b5
KH
1171/* Record undo information and adjust markers and position keepers for
1172 a replacement of a text PREV_TEXT at FROM to a new text of LEN
1173 chars (LEN_BYTE bytes) which resides in the gap just after
1174 GPT_ADDR.
1175
1176 PREV_TEXT nil means the new text was just inserted. */
2d9eea44 1177
77382fcc 1178static void
d311d28c
PE
1179adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte,
1180 Lisp_Object prev_text, ptrdiff_t len, ptrdiff_t len_byte)
1e9c7b7d 1181{
d311d28c 1182 ptrdiff_t nchars_del = 0, nbytes_del = 0;
61415a25 1183
b16afa45
KH
1184#ifdef BYTE_COMBINING_DEBUG
1185 if (count_combining_before (GPT_ADDR, len_byte, from, from_byte)
1186 || count_combining_after (GPT_ADDR, len_byte, from, from_byte))
1088b922 1187 emacs_abort ();
b16afa45
KH
1188#endif
1189
828f1ed3
KH
1190 if (STRINGP (prev_text))
1191 {
d5db4077
KR
1192 nchars_del = SCHARS (prev_text);
1193 nbytes_del = SBYTES (prev_text);
828f1ed3
KH
1194 }
1195
61415a25
KH
1196 /* Update various buffer positions for the new text. */
1197 GAP_SIZE -= len_byte;
1198 ZV += len; Z+= len;
1199 ZV_BYTE += len_byte; Z_BYTE += len_byte;
1200 GPT += len; GPT_BYTE += len_byte;
1201 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1202
adee4f91
KH
1203 if (nchars_del > 0)
1204 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1205 len, len_byte);
1206 else
1207 adjust_markers_for_insert (from, from_byte,
1208 from + len, from_byte + len_byte, 0);
b16afa45 1209
4b4deea2 1210 if (! EQ (BVAR (current_buffer, undo_list), Qt))
828f1ed3 1211 {
828f1ed3 1212 if (nchars_del > 0)
b16afa45
KH
1213 record_delete (from, prev_text);
1214 record_insert (from, len);
828f1ed3 1215 }
652838b5
KH
1216
1217 if (len > nchars_del)
1218 adjust_overlays_for_insert (from, len - nchars_del);
1219 else if (len < nchars_del)
1220 adjust_overlays_for_delete (from, nchars_del - len);
8707c1e5 1221
2f294edf 1222 offset_intervals (current_buffer, from, len - nchars_del);
61415a25 1223
b16afa45
KH
1224 if (from < PT)
1225 adjust_point (len - nchars_del, len_byte - nbytes_del);
61415a25 1226
9f3ede3c 1227 /* As byte combining will decrease Z, we must check this again. */
a50df55b
GM
1228 if (Z - GPT < END_UNCHANGED)
1229 END_UNCHANGED = Z - GPT;
9f3ede3c 1230
e2688e4a 1231 check_markers ();
60ea6052 1232
1e9c7b7d 1233 if (len == 0)
b58e3ca1
RS
1234 evaporate_overlays (from);
1235 MODIFF++;
3e145152 1236 CHARS_MODIFF = MODIFF;
b58e3ca1
RS
1237}
1238
652838b5
KH
1239/* Record undo information, adjust markers and position keepers for an
1240 insertion of a text from FROM (FROM_BYTE) to TO (TO_BYTE). The
1241 text already exists in the current buffer but character length (TO
1242 - FROM) may be incorrect, the correct length is NEWLEN. */
1243
1244void
d311d28c
PE
1245adjust_after_insert (ptrdiff_t from, ptrdiff_t from_byte,
1246 ptrdiff_t to, ptrdiff_t to_byte, ptrdiff_t newlen)
652838b5 1247{
d311d28c 1248 ptrdiff_t len = to - from, len_byte = to_byte - from_byte;
652838b5
KH
1249
1250 if (GPT != to)
1251 move_gap_both (to, to_byte);
1252 GAP_SIZE += len_byte;
1253 GPT -= len; GPT_BYTE -= len_byte;
1254 ZV -= len; ZV_BYTE -= len_byte;
1255 Z -= len; Z_BYTE -= len_byte;
1256 adjust_after_replace (from, from_byte, Qnil, newlen, len_byte);
1257}
085db7de 1258\f
3be11131 1259/* Replace the text from character positions FROM to TO with NEW,
a08d4ba7 1260 If PREPARE, call prepare_to_modify_buffer.
c5ca4d3a
RS
1261 If INHERIT, the newly inserted text should inherit text properties
1262 from the surrounding non-deleted text. */
1263
1264/* Note that this does not yet handle markers quite right.
1265 Also it needs to record a single undo-entry that does a replacement
1266 rather than a separate delete and insert.
60aa777a
RS
1267 That way, undo will also handle markers properly.
1268
1269 But if MARKERS is 0, don't relocate markers. */
c5ca4d3a
RS
1270
1271void
d311d28c 1272replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
a08d4ba7 1273 bool prepare, bool inherit, bool markers)
c5ca4d3a 1274{
d311d28c
PE
1275 ptrdiff_t inschars = SCHARS (new);
1276 ptrdiff_t insbytes = SBYTES (new);
1277 ptrdiff_t from_byte, to_byte;
1278 ptrdiff_t nbytes_del, nchars_del;
c5ca4d3a 1279 struct gcpro gcpro1;
ce97a2d7 1280 INTERVAL intervals;
d311d28c 1281 ptrdiff_t outgoing_insbytes = insbytes;
23b20a70 1282 Lisp_Object deletion;
c5ca4d3a 1283
e2688e4a 1284 check_markers ();
60ea6052 1285
c5ca4d3a 1286 GCPRO1 (new);
6bbd7a29 1287 deletion = Qnil;
c5ca4d3a
RS
1288
1289 if (prepare)
1290 {
d311d28c 1291 ptrdiff_t range_length = to - from;
c5ca4d3a
RS
1292 prepare_to_modify_buffer (from, to, &from);
1293 to = from + range_length;
1294 }
1295
3be11131
RS
1296 UNGCPRO;
1297
b50a28de 1298 /* Make args be valid. */
c5ca4d3a
RS
1299 if (from < BEGV)
1300 from = BEGV;
1301 if (to > ZV)
1302 to = ZV;
1303
3be11131
RS
1304 from_byte = CHAR_TO_BYTE (from);
1305 to_byte = CHAR_TO_BYTE (to);
c5ca4d3a 1306
3be11131
RS
1307 nchars_del = to - from;
1308 nbytes_del = to_byte - from_byte;
1309
1310 if (nbytes_del <= 0 && insbytes == 0)
1311 return;
c5ca4d3a 1312
1f90a790
RS
1313 /* Make OUTGOING_INSBYTES describe the text
1314 as it will be inserted in this buffer. */
1315
4b4deea2 1316 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
1f90a790 1317 outgoing_insbytes = inschars;
2a1d8be0 1318 else if (! STRING_MULTIBYTE (new))
1f90a790 1319 outgoing_insbytes
d5db4077 1320 = count_size_as_multibyte (SDATA (new), insbytes);
1f90a790 1321
c5ca4d3a
RS
1322 GCPRO1 (new);
1323
1324 /* Make sure the gap is somewhere in or next to what we are deleting. */
1325 if (from > GPT)
3be11131 1326 gap_right (from, from_byte);
c5ca4d3a 1327 if (to < GPT)
3be11131 1328 gap_left (to, to_byte, 0);
c5ca4d3a 1329
039af53a
KH
1330 /* Even if we don't record for undo, we must keep the original text
1331 because we may have to recover it because of inappropriate byte
1332 combining. */
4b4deea2 1333 if (! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1334 deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
c5ca4d3a 1335
3be11131
RS
1336 GAP_SIZE += nbytes_del;
1337 ZV -= nchars_del;
1338 Z -= nchars_del;
1339 ZV_BYTE -= nbytes_del;
1340 Z_BYTE -= nbytes_del;
c5ca4d3a 1341 GPT = from;
3be11131 1342 GPT_BYTE = from_byte;
017e09ac 1343 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
c5ca4d3a 1344
e2688e4a 1345 eassert (GPT <= GPT_BYTE);
3be11131 1346
a50df55b
GM
1347 if (GPT - BEG < BEG_UNCHANGED)
1348 BEG_UNCHANGED = GPT - BEG;
1349 if (Z - GPT < END_UNCHANGED)
1350 END_UNCHANGED = Z - GPT;
c5ca4d3a 1351
599a9e4f
PE
1352 if (GAP_SIZE < outgoing_insbytes)
1353 make_gap (outgoing_insbytes - GAP_SIZE);
c5ca4d3a 1354
1f90a790
RS
1355 /* Copy the string text into the buffer, perhaps converting
1356 between single-byte and multibyte. */
d5db4077 1357 copy_text (SDATA (new), GPT_ADDR, insbytes,
2a1d8be0 1358 STRING_MULTIBYTE (new),
4b4deea2 1359 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
1f90a790 1360
b16afa45 1361#ifdef BYTE_COMBINING_DEBUG
255c7dae
RS
1362 /* We have copied text into the gap, but we have not marked
1363 it as part of the buffer. So we can use the old FROM and FROM_BYTE
1364 here, for both the previous text and the following text.
1365 Meanwhile, GPT_ADDR does point to
432f78d2 1366 the text that has been stored by copy_text. */
b16afa45
KH
1367 if (count_combining_before (GPT_ADDR, outgoing_insbytes, from, from_byte)
1368 || count_combining_after (GPT_ADDR, outgoing_insbytes, from, from_byte))
1088b922 1369 emacs_abort ();
b16afa45 1370#endif
432f78d2 1371
4b4deea2 1372 if (! EQ (BVAR (current_buffer, undo_list), Qt))
23b20a70 1373 {
bec1aca2
RS
1374 /* Record the insertion first, so that when we undo,
1375 the deletion will be undone first. Thus, undo
1376 will insert before deleting, and thus will keep
1377 the markers before and after this text separate. */
1378 record_insert (from + SCHARS (deletion), inschars);
b16afa45 1379 record_delete (from, deletion);
23b20a70 1380 }
c5ca4d3a 1381
1f90a790
RS
1382 GAP_SIZE -= outgoing_insbytes;
1383 GPT += inschars;
1384 ZV += inschars;
1385 Z += inschars;
1386 GPT_BYTE += outgoing_insbytes;
1387 ZV_BYTE += outgoing_insbytes;
1388 Z_BYTE += outgoing_insbytes;
c5ca4d3a
RS
1389 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1390
e2688e4a 1391 eassert (GPT <= GPT_BYTE);
3be11131 1392
048a0fbb 1393 /* Adjust markers for the deletion and the insertion. */
60aa777a 1394 if (markers)
048a0fbb
RS
1395 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1396 inschars, outgoing_insbytes);
c5ca4d3a 1397
2f294edf
DA
1398 /* Adjust the overlay center as needed. This must be done after
1399 adjusting the markers that bound the overlays. */
1400 adjust_overlays_for_delete (from, nchars_del);
1401 adjust_overlays_for_insert (from, inschars);
1402
255c7dae 1403 offset_intervals (current_buffer, from, inschars - nchars_del);
ce97a2d7
RS
1404
1405 /* Get the intervals for the part of the string we are inserting--
1406 not including the combined-before bytes. */
0c94c8d6 1407 intervals = string_intervals (new);
ce97a2d7 1408 /* Insert those intervals. */
1f90a790 1409 graft_intervals_into_buffer (intervals, from, inschars,
ce97a2d7 1410 current_buffer, inherit);
c5ca4d3a 1411
1f90a790
RS
1412 /* Relocate point as if it were a marker. */
1413 if (from < PT)
8bedbe9d 1414 adjust_point ((from + inschars - (PT < to ? PT : to)),
1f90a790 1415 (from_byte + outgoing_insbytes
8bedbe9d 1416 - (PT_BYTE < to_byte ? PT_BYTE : to_byte)));
c5ca4d3a 1417
1f90a790
RS
1418 if (outgoing_insbytes == 0)
1419 evaporate_overlays (from);
93b882e8 1420
e2688e4a 1421 check_markers ();
60ea6052 1422
c5ca4d3a 1423 MODIFF++;
3e145152 1424 CHARS_MODIFF = MODIFF;
c5ca4d3a
RS
1425 UNGCPRO;
1426
255c7dae 1427 signal_after_change (from, nchars_del, GPT - from);
0ef71121 1428 update_compositions (from, GPT, CHECK_BORDER);
c5ca4d3a
RS
1429}
1430\f
085db7de
RS
1431/* Replace the text from character positions FROM to TO with
1432 the text in INS of length INSCHARS.
1433 Keep the text properties that applied to the old characters
1434 (extending them to all the new chars if there are more new chars).
1435
1436 Note that this does not yet handle markers quite right.
1437
a08d4ba7 1438 If MARKERS, relocate markers.
085db7de
RS
1439
1440 Unlike most functions at this level, never call
1441 prepare_to_modify_buffer and never call signal_after_change. */
1442
1443void
d311d28c
PE
1444replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
1445 ptrdiff_t to, ptrdiff_t to_byte,
1446 const char *ins, ptrdiff_t inschars, ptrdiff_t insbytes,
a08d4ba7 1447 bool markers)
085db7de 1448{
d311d28c 1449 ptrdiff_t nbytes_del, nchars_del;
085db7de 1450
e2688e4a 1451 check_markers ();
085db7de
RS
1452
1453 nchars_del = to - from;
1454 nbytes_del = to_byte - from_byte;
1455
1456 if (nbytes_del <= 0 && insbytes == 0)
1457 return;
1458
085db7de
RS
1459 /* Make sure the gap is somewhere in or next to what we are deleting. */
1460 if (from > GPT)
1461 gap_right (from, from_byte);
1462 if (to < GPT)
1463 gap_left (to, to_byte, 0);
1464
1465 GAP_SIZE += nbytes_del;
1466 ZV -= nchars_del;
1467 Z -= nchars_del;
1468 ZV_BYTE -= nbytes_del;
1469 Z_BYTE -= nbytes_del;
1470 GPT = from;
1471 GPT_BYTE = from_byte;
1472 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1473
e2688e4a 1474 eassert (GPT <= GPT_BYTE);
085db7de
RS
1475
1476 if (GPT - BEG < BEG_UNCHANGED)
1477 BEG_UNCHANGED = GPT - BEG;
1478 if (Z - GPT < END_UNCHANGED)
1479 END_UNCHANGED = Z - GPT;
1480
1481 if (GAP_SIZE < insbytes)
1482 make_gap (insbytes - GAP_SIZE);
1483
1484 /* Copy the replacement text into the buffer. */
72af86bd 1485 memcpy (GPT_ADDR, ins, insbytes);
085db7de
RS
1486
1487#ifdef BYTE_COMBINING_DEBUG
1488 /* We have copied text into the gap, but we have not marked
1489 it as part of the buffer. So we can use the old FROM and FROM_BYTE
1490 here, for both the previous text and the following text.
1491 Meanwhile, GPT_ADDR does point to
1492 the text that has been stored by copy_text. */
1493 if (count_combining_before (GPT_ADDR, insbytes, from, from_byte)
1494 || count_combining_after (GPT_ADDR, insbytes, from, from_byte))
1088b922 1495 emacs_abort ();
085db7de
RS
1496#endif
1497
1498 GAP_SIZE -= insbytes;
1499 GPT += inschars;
1500 ZV += inschars;
1501 Z += inschars;
1502 GPT_BYTE += insbytes;
1503 ZV_BYTE += insbytes;
1504 Z_BYTE += insbytes;
1505 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1506
e2688e4a 1507 eassert (GPT <= GPT_BYTE);
085db7de 1508
2f294edf
DA
1509 /* Adjust markers for the deletion and the insertion. */
1510 if (markers
1511 && ! (nchars_del == 1 && inschars == 1 && nbytes_del == insbytes))
1512 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1513 inschars, insbytes);
1514
085db7de
RS
1515 /* Adjust the overlay center as needed. This must be done after
1516 adjusting the markers that bound the overlays. */
1517 if (nchars_del != inschars)
1518 {
1519 adjust_overlays_for_insert (from, inschars);
1520 adjust_overlays_for_delete (from + inschars, nchars_del);
1521 }
1522
085db7de
RS
1523 offset_intervals (current_buffer, from, inschars - nchars_del);
1524
1525 /* Relocate point as if it were a marker. */
7077904e
KH
1526 if (from < PT && (nchars_del != inschars || nbytes_del != insbytes))
1527 {
1528 if (PT < to)
1529 /* PT was within the deleted text. Move it to FROM. */
1530 adjust_point (from - PT, from_byte - PT_BYTE);
1531 else
1532 adjust_point (inschars - nchars_del, insbytes - nbytes_del);
1533 }
085db7de
RS
1534
1535 if (insbytes == 0)
1536 evaporate_overlays (from);
1537
e2688e4a 1538 check_markers ();
085db7de
RS
1539
1540 MODIFF++;
3e145152 1541 CHARS_MODIFF = MODIFF;
085db7de
RS
1542}
1543\f
b45433b3 1544/* Delete characters in current buffer
3be11131
RS
1545 from FROM up to (but not including) TO.
1546 If TO comes before FROM, we delete nothing. */
b45433b3 1547
c660b094 1548void
d311d28c 1549del_range (ptrdiff_t from, ptrdiff_t to)
47c64747 1550{
7dae4502 1551 del_range_1 (from, to, 1, 0);
47c64747
RS
1552}
1553
7dae4502
SM
1554/* Like del_range; PREPARE says whether to call prepare_to_modify_buffer.
1555 RET_STRING says to return the deleted text. */
47c64747 1556
7dae4502 1557Lisp_Object
a08d4ba7 1558del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
b45433b3 1559{
d311d28c 1560 ptrdiff_t from_byte, to_byte;
7dae4502
SM
1561 Lisp_Object deletion;
1562 struct gcpro gcpro1;
3be11131
RS
1563
1564 /* Make args be valid */
1565 if (from < BEGV)
1566 from = BEGV;
1567 if (to > ZV)
1568 to = ZV;
1569
1570 if (to <= from)
7dae4502 1571 return Qnil;
3be11131
RS
1572
1573 if (prepare)
1574 {
d311d28c 1575 ptrdiff_t range_length = to - from;
3be11131 1576 prepare_to_modify_buffer (from, to, &from);
0a411995 1577 to = min (ZV, from + range_length);
3be11131
RS
1578 }
1579
1580 from_byte = CHAR_TO_BYTE (from);
1581 to_byte = CHAR_TO_BYTE (to);
1582
7dae4502 1583 deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
5e617bc2 1584 GCPRO1 (deletion);
9c36993a 1585 signal_after_change (from, to - from, 0);
233cc02d 1586 update_compositions (from, from, CHECK_HEAD);
7dae4502
SM
1587 UNGCPRO;
1588 return deletion;
3be11131
RS
1589}
1590
1591/* Like del_range_1 but args are byte positions, not char positions. */
1592
1593void
a08d4ba7 1594del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte, bool prepare)
3be11131 1595{
d311d28c 1596 ptrdiff_t from, to;
3be11131
RS
1597
1598 /* Make args be valid */
1599 if (from_byte < BEGV_BYTE)
1600 from_byte = BEGV_BYTE;
1601 if (to_byte > ZV_BYTE)
1602 to_byte = ZV_BYTE;
1603
1604 if (to_byte <= from_byte)
1605 return;
1606
1607 from = BYTE_TO_CHAR (from_byte);
1608 to = BYTE_TO_CHAR (to_byte);
b45433b3 1609
d206af14
RS
1610 if (prepare)
1611 {
d311d28c
PE
1612 ptrdiff_t old_from = from, old_to = Z - to;
1613 ptrdiff_t range_length = to - from;
d206af14
RS
1614 prepare_to_modify_buffer (from, to, &from);
1615 to = from + range_length;
3be11131
RS
1616
1617 if (old_from != from)
1618 from_byte = CHAR_TO_BYTE (from);
0a411995
GM
1619 if (to > ZV)
1620 {
1621 to = ZV;
1622 to_byte = ZV_BYTE;
1623 }
1624 else if (old_to == Z - to)
3be11131 1625 to_byte = CHAR_TO_BYTE (to);
d206af14
RS
1626 }
1627
7dae4502 1628 del_range_2 (from, from_byte, to, to_byte, 0);
9c36993a 1629 signal_after_change (from, to - from, 0);
0ef71121 1630 update_compositions (from, from, CHECK_HEAD);
3be11131
RS
1631}
1632
1633/* Like del_range_1, but positions are specified both as charpos
1634 and bytepos. */
1635
1636void
d311d28c 1637del_range_both (ptrdiff_t from, ptrdiff_t from_byte,
a08d4ba7 1638 ptrdiff_t to, ptrdiff_t to_byte, bool prepare)
3be11131 1639{
b45433b3 1640 /* Make args be valid */
3be11131
RS
1641 if (from_byte < BEGV_BYTE)
1642 from_byte = BEGV_BYTE;
1643 if (to_byte > ZV_BYTE)
1644 to_byte = ZV_BYTE;
1645
1646 if (to_byte <= from_byte)
1647 return;
1648
b45433b3
JB
1649 if (from < BEGV)
1650 from = BEGV;
1651 if (to > ZV)
1652 to = ZV;
1653
3be11131
RS
1654 if (prepare)
1655 {
d311d28c
PE
1656 ptrdiff_t old_from = from, old_to = Z - to;
1657 ptrdiff_t range_length = to - from;
3be11131
RS
1658 prepare_to_modify_buffer (from, to, &from);
1659 to = from + range_length;
1660
1661 if (old_from != from)
1662 from_byte = CHAR_TO_BYTE (from);
0a411995
GM
1663 if (to > ZV)
1664 {
1665 to = ZV;
1666 to_byte = ZV_BYTE;
1667 }
1668 else if (old_to == Z - to)
3be11131
RS
1669 to_byte = CHAR_TO_BYTE (to);
1670 }
1671
7dae4502 1672 del_range_2 (from, from_byte, to, to_byte, 0);
9c36993a 1673 signal_after_change (from, to - from, 0);
0ef71121 1674 update_compositions (from, from, CHECK_HEAD);
3be11131
RS
1675}
1676
1677/* Delete a range of text, specified both as character positions
1678 and byte positions. FROM and TO are character positions,
7dae4502 1679 while FROM_BYTE and TO_BYTE are byte positions.
a08d4ba7 1680 If RET_STRING, the deleted area is returned as a string. */
3be11131 1681
7dae4502 1682Lisp_Object
d311d28c 1683del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
a08d4ba7 1684 ptrdiff_t to, ptrdiff_t to_byte, bool ret_string)
3be11131 1685{
a08d4ba7 1686 ptrdiff_t nbytes_del, nchars_del;
628cea90 1687 Lisp_Object deletion;
3be11131 1688
e2688e4a 1689 check_markers ();
60ea6052 1690
3be11131
RS
1691 nchars_del = to - from;
1692 nbytes_del = to_byte - from_byte;
b45433b3
JB
1693
1694 /* Make sure the gap is somewhere in or next to what we are deleting. */
1695 if (from > GPT)
3be11131 1696 gap_right (from, from_byte);
b45433b3 1697 if (to < GPT)
3be11131 1698 gap_left (to, to_byte, 0);
b45433b3 1699
b16afa45
KH
1700#ifdef BYTE_COMBINING_DEBUG
1701 if (count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
1702 Z_BYTE - to_byte, from, from_byte))
1088b922 1703 emacs_abort ();
b16afa45 1704#endif
628cea90 1705
4b4deea2 1706 if (ret_string || ! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1707 deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
7dae4502
SM
1708 else
1709 deletion = Qnil;
1710
8948d317
RS
1711 /* Relocate all markers pointing into the new, larger gap
1712 to point at the end of the text before the gap.
3be11131
RS
1713 Do this before recording the deletion,
1714 so that undo handles this after reinserting the text. */
1715 adjust_markers_for_delete (from, from_byte, to, to_byte);
b16afa45 1716
4b4deea2 1717 if (! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1718 record_delete (from, deletion);
be09561e 1719 MODIFF++;
3e145152 1720 CHARS_MODIFF = MODIFF;
be09561e 1721
b45433b3 1722 /* Relocate point as if it were a marker. */
2bcaed71 1723 if (from < PT)
3be11131
RS
1724 adjust_point (from - (PT < to ? PT : to),
1725 from_byte - (PT_BYTE < to_byte ? PT_BYTE : to_byte));
b45433b3 1726
3be11131 1727 offset_intervals (current_buffer, from, - nchars_del);
16032db6 1728
adde4858 1729 /* Adjust the overlay center as needed. This must be done after
a7f38d28 1730 adjusting the markers that bound the overlays. */
e3a87305 1731 adjust_overlays_for_delete (from, nchars_del);
adde4858 1732
3be11131
RS
1733 GAP_SIZE += nbytes_del;
1734 ZV_BYTE -= nbytes_del;
1735 Z_BYTE -= nbytes_del;
1736 ZV -= nchars_del;
1737 Z -= nchars_del;
b45433b3 1738 GPT = from;
3be11131 1739 GPT_BYTE = from_byte;
b3b58c01
AS
1740 if (GAP_SIZE > 0 && !current_buffer->text->inhibit_shrinking)
1741 /* Put an anchor, unless called from decode_coding_object which
1742 needs to access the previous gap contents. */
1743 *(GPT_ADDR) = 0;
e3a87305 1744
e2688e4a 1745 eassert (GPT <= GPT_BYTE);
3be11131 1746
a50df55b
GM
1747 if (GPT - BEG < BEG_UNCHANGED)
1748 BEG_UNCHANGED = GPT - BEG;
1749 if (Z - GPT < END_UNCHANGED)
1750 END_UNCHANGED = Z - GPT;
b45433b3 1751
e2688e4a 1752 check_markers ();
60ea6052 1753
d386034e 1754 evaporate_overlays (from);
7dae4502
SM
1755
1756 return deletion;
b45433b3 1757}
20edc1c9
DA
1758
1759/* Call this if you're about to change the region of current buffer
1760 from character positions START to END. This checks the read-only
3be11131
RS
1761 properties of the region, calls the necessary modification hooks,
1762 and warns the next redisplay that it should pay attention to that
3e145152
CY
1763 area.
1764
a08d4ba7 1765 If PRESERVE_CHARS_MODIFF, do not update CHARS_MODIFF.
3e145152 1766 Otherwise set CHARS_MODIFF to the new value of MODIFF. */
3be11131 1767
c660b094 1768void
20edc1c9 1769modify_region_1 (ptrdiff_t start, ptrdiff_t end, bool preserve_chars_modiff)
b45433b3 1770{
d206af14 1771 prepare_to_modify_buffer (start, end, NULL);
b45433b3 1772
20edc1c9 1773 BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end);
83010cd6 1774
9fbf87cd 1775 if (MODIFF <= SAVE_MODIFF)
83010cd6 1776 record_first_change ();
b45433b3 1777 MODIFF++;
3e145152
CY
1778 if (! preserve_chars_modiff)
1779 CHARS_MODIFF = MODIFF;
04a759c8 1780
20edc1c9 1781 bset_point_before_scroll (current_buffer, Qnil);
b45433b3 1782}
20edc1c9 1783
3be11131
RS
1784/* Check that it is okay to modify the buffer between START and END,
1785 which are char positions.
1786
679194a6
JA
1787 Run the before-change-function, if any. If intervals are in use,
1788 verify that the text to be modified is not read-only, and call
d206af14
RS
1789 any modification properties the text may have.
1790
1791 If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
1792 by holding its value temporarily in a marker. */
b45433b3 1793
c660b094 1794void
d311d28c
PE
1795prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
1796 ptrdiff_t *preserve_ptr)
b45433b3 1797{
234fb773
CY
1798 struct buffer *base_buffer;
1799
4b4deea2 1800 if (!NILP (BVAR (current_buffer, read_only)))
b45433b3
JB
1801 Fbarf_if_buffer_read_only ();
1802
c6afe371
DA
1803 /* If we're modifying the buffer other than shown in a selected window,
1804 let redisplay consider other windows if this buffer is visible. */
1805 if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer
1806 && buffer_window_count (current_buffer))
2e9f55fd
GM
1807 ++windows_or_buffers_changed;
1808
0c94c8d6 1809 if (buffer_intervals (current_buffer))
d206af14
RS
1810 {
1811 if (preserve_ptr)
1812 {
1813 Lisp_Object preserve_marker;
1814 struct gcpro gcpro1;
1815 preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil);
1816 GCPRO1 (preserve_marker);
1817 verify_interval_modification (current_buffer, start, end);
1818 *preserve_ptr = marker_position (preserve_marker);
dab0b04d 1819 unchain_marker (XMARKER (preserve_marker));
d206af14
RS
1820 UNGCPRO;
1821 }
1822 else
1823 verify_interval_modification (current_buffer, start, end);
1824 }
b45433b3 1825
234fb773
CY
1826 /* For indirect buffers, use the base buffer to check clashes. */
1827 if (current_buffer->base_buffer != 0)
1828 base_buffer = current_buffer->base_buffer;
1829 else
1830 base_buffer = current_buffer;
1831
b45433b3 1832#ifdef CLASH_DETECTION
4b4deea2 1833 if (!NILP (BVAR (base_buffer, file_truename))
ab6c5c0c 1834 /* Make binding buffer-file-name to nil effective. */
4b4deea2 1835 && !NILP (BVAR (base_buffer, filename))
9fbf87cd 1836 && SAVE_MODIFF >= MODIFF)
4b4deea2 1837 lock_file (BVAR (base_buffer, file_truename));
b45433b3
JB
1838#else
1839 /* At least warn if this file has changed on disk since it was visited. */
4b4deea2 1840 if (!NILP (BVAR (base_buffer, filename))
9fbf87cd 1841 && SAVE_MODIFF >= MODIFF
d427b66a 1842 && NILP (Fverify_visited_file_modtime (Fcurrent_buffer ()))
4b4deea2 1843 && !NILP (Ffile_exists_p (BVAR (base_buffer, filename))))
b45433b3 1844 call1 (intern ("ask-user-about-supersession-threat"),
4b4deea2 1845 BVAR (base_buffer,filename));
b45433b3
JB
1846#endif /* not CLASH_DETECTION */
1847
9852377f 1848 /* If `select-active-regions' is non-nil, save the region text. */
4b4deea2 1849 if (!NILP (BVAR (current_buffer, mark_active))
8b78d5e3 1850 && !inhibit_modification_hooks
4b4deea2 1851 && XMARKER (BVAR (current_buffer, mark))->buffer
7c23dd44
CY
1852 && NILP (Vsaved_region_selection)
1853 && (EQ (Vselect_active_regions, Qonly)
1854 ? EQ (CAR_SAFE (Vtransient_mark_mode), Qonly)
1855 : (!NILP (Vselect_active_regions)
1856 && !NILP (Vtransient_mark_mode))))
9852377f 1857 {
4c1acb95 1858 ptrdiff_t b = marker_position (BVAR (current_buffer, mark));
d311d28c 1859 ptrdiff_t e = PT;
746812d9
CY
1860 if (b < e)
1861 Vsaved_region_selection = make_buffer_string (b, e, 0);
1862 else if (b > e)
1863 Vsaved_region_selection = make_buffer_string (e, b, 0);
9852377f
CY
1864 }
1865
d206af14 1866 signal_before_change (start, end, preserve_ptr);
2f545eea 1867
56e1065e
JB
1868 if (current_buffer->newline_cache)
1869 invalidate_region_cache (current_buffer,
1870 current_buffer->newline_cache,
1871 start - BEG, Z - end);
1872 if (current_buffer->width_run_cache)
1873 invalidate_region_cache (current_buffer,
1874 current_buffer->width_run_cache,
1875 start - BEG, Z - end);
1876
2f545eea 1877 Vdeactivate_mark = Qt;
b45433b3
JB
1878}
1879\f
d206af14
RS
1880/* These macros work with an argument named `preserve_ptr'
1881 and a local variable named `preserve_marker'. */
1882
1883#define PRESERVE_VALUE \
1884 if (preserve_ptr && NILP (preserve_marker)) \
1885 preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil)
1886
1887#define RESTORE_VALUE \
1888 if (! NILP (preserve_marker)) \
1889 { \
1890 *preserve_ptr = marker_position (preserve_marker); \
dab0b04d 1891 unchain_marker (XMARKER (preserve_marker)); \
d206af14
RS
1892 }
1893
b86e0aaf
RS
1894#define PRESERVE_START_END \
1895 if (NILP (start_marker)) \
1896 start_marker = Fcopy_marker (start, Qnil); \
1897 if (NILP (end_marker)) \
1898 end_marker = Fcopy_marker (end, Qnil);
1899
1900#define FETCH_START \
1901 (! NILP (start_marker) ? Fmarker_position (start_marker) : start)
1902
1903#define FETCH_END \
1904 (! NILP (end_marker) ? Fmarker_position (end_marker) : end)
1905
4a181359 1906/* Set a variable to nil if an error occurred.
e6d3f090
SM
1907 Don't change the variable if there was no error.
1908 VAL is a cons-cell (VARIABLE . NO-ERROR-FLAG).
1909 VARIABLE is the variable to maybe set to nil.
1910 NO-ERROR-FLAG is nil if there was an error,
1911 anything else meaning no error (so this function does nothing). */
85876d07 1912static Lisp_Object
971de7fb 1913reset_var_on_error (Lisp_Object val)
4a181359
SM
1914{
1915 if (NILP (XCDR (val)))
1916 Fset (XCAR (val), Qnil);
1917 return Qnil;
1918}
1919
eb8c3be9 1920/* Signal a change to the buffer immediately before it happens.
d206af14
RS
1921 START_INT and END_INT are the bounds of the text to be changed.
1922
1923 If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
1924 by holding its value temporarily in a marker. */
b45433b3 1925
4889fc82 1926static void
d311d28c
PE
1927signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
1928 ptrdiff_t *preserve_ptr)
b45433b3 1929{
fb4ee5cd 1930 Lisp_Object start, end;
b86e0aaf 1931 Lisp_Object start_marker, end_marker;
d206af14 1932 Lisp_Object preserve_marker;
b86e0aaf 1933 struct gcpro gcpro1, gcpro2, gcpro3;
d311d28c 1934 ptrdiff_t count = SPECPDL_INDEX ();
fb4ee5cd 1935
fd16a4c6
KH
1936 if (inhibit_modification_hooks)
1937 return;
1938
fb4ee5cd
RS
1939 start = make_number (start_int);
1940 end = make_number (end_int);
d206af14 1941 preserve_marker = Qnil;
b86e0aaf
RS
1942 start_marker = Qnil;
1943 end_marker = Qnil;
1944 GCPRO3 (preserve_marker, start_marker, end_marker);
fb4ee5cd 1945
4a181359
SM
1946 specbind (Qinhibit_modification_hooks, Qt);
1947
dee091a3
JD
1948 /* If buffer is unmodified, run a special hook for that case. The
1949 check for Vfirst_change_hook is just a minor optimization. */
9fbf87cd 1950 if (SAVE_MODIFF >= MODIFF
dee091a3 1951 && !NILP (Vfirst_change_hook))
d206af14
RS
1952 {
1953 PRESERVE_VALUE;
b86e0aaf 1954 PRESERVE_START_END;
dee091a3 1955 Frun_hooks (1, &Qfirst_change_hook);
d206af14 1956 }
dbc4e1c1 1957
3d1e2d9c 1958 /* Now run the before-change-functions if any. */
e45fb8bf
RS
1959 if (!NILP (Vbefore_change_functions))
1960 {
3d1e2d9c 1961 Lisp_Object args[3];
4a181359 1962 Lisp_Object rvoe_arg = Fcons (Qbefore_change_functions, Qnil);
3d1e2d9c 1963
d206af14 1964 PRESERVE_VALUE;
b86e0aaf 1965 PRESERVE_START_END;
d206af14 1966
4a181359
SM
1967 /* Mark before-change-functions to be reset to nil in case of error. */
1968 record_unwind_protect (reset_var_on_error, rvoe_arg);
3d1e2d9c
RS
1969
1970 /* Actually run the hook functions. */
1971 args[0] = Qbefore_change_functions;
b86e0aaf
RS
1972 args[1] = FETCH_START;
1973 args[2] = FETCH_END;
4a181359 1974 Frun_hook_with_args (3, args);
3d1e2d9c 1975
4a181359
SM
1976 /* There was no error: unarm the reset_on_error. */
1977 XSETCDR (rvoe_arg, Qt);
e45fb8bf 1978 }
d07c0804 1979
4cb3e6b3 1980 if (buffer_has_overlays ())
d206af14
RS
1981 {
1982 PRESERVE_VALUE;
b86e0aaf
RS
1983 report_overlay_modification (FETCH_START, FETCH_END, 0,
1984 FETCH_START, FETCH_END, Qnil);
d206af14
RS
1985 }
1986
b86e0aaf
RS
1987 if (! NILP (start_marker))
1988 free_marker (start_marker);
1989 if (! NILP (end_marker))
1990 free_marker (end_marker);
d206af14
RS
1991 RESTORE_VALUE;
1992 UNGCPRO;
4a181359
SM
1993
1994 unbind_to (count, Qnil);
b45433b3
JB
1995}
1996
eb8c3be9 1997/* Signal a change immediately after it happens.
3be11131 1998 CHARPOS is the character position of the start of the changed text.
b45433b3
JB
1999 LENDEL is the number of characters of the text before the change.
2000 (Not the whole buffer; just the part that was changed.)
8b09e5d0
RS
2001 LENINS is the number of characters in that part of the text
2002 after the change. */
b45433b3 2003
c660b094 2004void
d311d28c 2005signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
b45433b3 2006{
d311d28c 2007 ptrdiff_t count = SPECPDL_INDEX ();
fd16a4c6
KH
2008 if (inhibit_modification_hooks)
2009 return;
2010
fb2e7d14
RS
2011 /* If we are deferring calls to the after-change functions
2012 and there are no before-change functions,
2013 just record the args that we were going to use. */
2014 if (! NILP (Vcombine_after_change_calls)
1675d086 2015 && NILP (Vbefore_change_functions)
4cb3e6b3 2016 && !buffer_has_overlays ())
fb2e7d14
RS
2017 {
2018 Lisp_Object elt;
2019
2020 if (!NILP (combine_after_change_list)
2021 && current_buffer != XBUFFER (combine_after_change_buffer))
2022 Fcombine_after_change_execute ();
2023
3be11131
RS
2024 elt = Fcons (make_number (charpos - BEG),
2025 Fcons (make_number (Z - (charpos - lendel + lenins)),
fb2e7d14
RS
2026 Fcons (make_number (lenins - lendel), Qnil)));
2027 combine_after_change_list
2028 = Fcons (elt, combine_after_change_list);
2029 combine_after_change_buffer = Fcurrent_buffer ();
2030
2031 return;
2032 }
2033
177c0ea7 2034 if (!NILP (combine_after_change_list))
fb2e7d14
RS
2035 Fcombine_after_change_execute ();
2036
4a181359
SM
2037 specbind (Qinhibit_modification_hooks, Qt);
2038
e45fb8bf
RS
2039 if (!NILP (Vafter_change_functions))
2040 {
3d1e2d9c 2041 Lisp_Object args[4];
4a181359
SM
2042 Lisp_Object rvoe_arg = Fcons (Qafter_change_functions, Qnil);
2043
2044 /* Mark after-change-functions to be reset to nil in case of error. */
2045 record_unwind_protect (reset_var_on_error, rvoe_arg);
3d1e2d9c
RS
2046
2047 /* Actually run the hook functions. */
2048 args[0] = Qafter_change_functions;
3be11131
RS
2049 XSETFASTINT (args[1], charpos);
2050 XSETFASTINT (args[2], charpos + lenins);
3d1e2d9c 2051 XSETFASTINT (args[3], lendel);
4a181359 2052 Frun_hook_with_args (4, args);
3d1e2d9c 2053
4a181359
SM
2054 /* There was no error: unarm the reset_on_error. */
2055 XSETCDR (rvoe_arg, Qt);
e45fb8bf 2056 }
d07c0804 2057
4cb3e6b3 2058 if (buffer_has_overlays ())
3be11131
RS
2059 report_overlay_modification (make_number (charpos),
2060 make_number (charpos + lenins),
d07c0804 2061 1,
3be11131
RS
2062 make_number (charpos),
2063 make_number (charpos + lenins),
d07c0804 2064 make_number (lendel));
c5ca0786
RS
2065
2066 /* After an insertion, call the text properties
2067 insert-behind-hooks or insert-in-front-hooks. */
2068 if (lendel == 0)
d6b81c0f
AS
2069 report_interval_modification (make_number (charpos),
2070 make_number (charpos + lenins));
4a181359
SM
2071
2072 unbind_to (count, Qnil);
b45433b3 2073}
fb2e7d14 2074
85876d07 2075static Lisp_Object
971de7fb 2076Fcombine_after_change_execute_1 (Lisp_Object val)
fb2e7d14
RS
2077{
2078 Vcombine_after_change_calls = val;
2079 return val;
2080}
2081
2082DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
335c5470
PJ
2083 Scombine_after_change_execute, 0, 0, 0,
2084 doc: /* This function is for use internally in `combine-after-change-calls'. */)
5842a27b 2085 (void)
fb2e7d14 2086{
d311d28c
PE
2087 ptrdiff_t count = SPECPDL_INDEX ();
2088 ptrdiff_t beg, end, change;
2089 ptrdiff_t begpos, endpos;
fb2e7d14
RS
2090 Lisp_Object tail;
2091
101e446f
KH
2092 if (NILP (combine_after_change_list))
2093 return Qnil;
2094
c47a9ed1
CY
2095 /* It is rare for combine_after_change_buffer to be invalid, but
2096 possible. It can happen when combine-after-change-calls is
2097 non-nil, and insertion calls a file handler (e.g. through
2098 lock_file) which scribbles into a temp file -- cyd */
2099 if (!BUFFERP (combine_after_change_buffer)
e578f381 2100 || !BUFFER_LIVE_P (XBUFFER (combine_after_change_buffer)))
c47a9ed1
CY
2101 {
2102 combine_after_change_list = Qnil;
2103 return Qnil;
2104 }
2105
66322887 2106 record_unwind_current_buffer ();
fb2e7d14
RS
2107
2108 Fset_buffer (combine_after_change_buffer);
2109
2110 /* # chars unchanged at beginning of buffer. */
2111 beg = Z - BEG;
2112 /* # chars unchanged at end of buffer. */
2113 end = beg;
2114 /* Total amount of insertion (negative for deletion). */
2115 change = 0;
2116
2117 /* Scan the various individual changes,
2118 accumulating the range info in BEG, END and CHANGE. */
2119 for (tail = combine_after_change_list; CONSP (tail);
03699b14 2120 tail = XCDR (tail))
fb2e7d14 2121 {
e688a080 2122 Lisp_Object elt;
d311d28c 2123 ptrdiff_t thisbeg, thisend, thischange;
fb2e7d14
RS
2124
2125 /* Extract the info from the next element. */
03699b14 2126 elt = XCAR (tail);
fb2e7d14
RS
2127 if (! CONSP (elt))
2128 continue;
03699b14 2129 thisbeg = XINT (XCAR (elt));
fb2e7d14 2130
03699b14 2131 elt = XCDR (elt);
fb2e7d14
RS
2132 if (! CONSP (elt))
2133 continue;
03699b14 2134 thisend = XINT (XCAR (elt));
fb2e7d14 2135
03699b14 2136 elt = XCDR (elt);
fb2e7d14
RS
2137 if (! CONSP (elt))
2138 continue;
03699b14 2139 thischange = XINT (XCAR (elt));
fb2e7d14
RS
2140
2141 /* Merge this range into the accumulated range. */
2142 change += thischange;
2143 if (thisbeg < beg)
2144 beg = thisbeg;
2145 if (thisend < end)
2146 end = thisend;
2147 }
2148
2149 /* Get the current start and end positions of the range
2150 that was changed. */
2151 begpos = BEG + beg;
2152 endpos = Z - end;
177c0ea7 2153
fb2e7d14
RS
2154 /* We are about to handle these, so discard them. */
2155 combine_after_change_list = Qnil;
2156
2157 /* Now run the after-change functions for real.
2158 Turn off the flag that defers them. */
2159 record_unwind_protect (Fcombine_after_change_execute_1,
2160 Vcombine_after_change_calls);
2161 signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
0ef71121 2162 update_compositions (begpos, endpos, CHECK_ALL);
fb2e7d14 2163
101e446f 2164 return unbind_to (count, Qnil);
fb2e7d14
RS
2165}
2166\f
dfcf069d 2167void
971de7fb 2168syms_of_insdel (void)
fb2e7d14
RS
2169{
2170 staticpro (&combine_after_change_list);
bf0bf758 2171 staticpro (&combine_after_change_buffer);
fb2e7d14 2172 combine_after_change_list = Qnil;
101e446f 2173 combine_after_change_buffer = Qnil;
fb2e7d14 2174
29208e82 2175 DEFVAR_LISP ("combine-after-change-calls", Vcombine_after_change_calls,
335c5470 2176 doc: /* Used internally by the `combine-after-change-calls' macro. */);
fb2e7d14
RS
2177 Vcombine_after_change_calls = Qnil;
2178
29208e82 2179 DEFVAR_BOOL ("inhibit-modification-hooks", inhibit_modification_hooks,
335c5470
PJ
2180 doc: /* Non-nil means don't run any of the hooks that respond to buffer changes.
2181This affects `before-change-functions' and `after-change-functions',
2182as well as hooks attached to text properties and overlays. */);
7baf49cf 2183 inhibit_modification_hooks = 0;
cd3520a4 2184 DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks");
7baf49cf 2185
fb2e7d14
RS
2186 defsubr (&Scombine_after_change_execute);
2187}