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