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