* buffer.h (BSET): Remove.
[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
0c94c8d6 847 if (buffer_intervals (current_buffer))
1f90a790
RS
848 offset_intervals (current_buffer, PT, nchars);
849
0c94c8d6 850 if (!inherit && buffer_intervals (current_buffer))
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
0c94c8d6 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 1019
0c94c8d6 1020 if (buffer_intervals (current_buffer))
c669988b
KH
1021 {
1022 offset_intervals (current_buffer, GPT - nchars, nchars);
77c7bcb1 1023 graft_intervals_into_buffer (NULL, GPT - nchars, nchars,
c669988b
KH
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
0c94c8d6 1160 if (buffer_intervals (current_buffer))
1f90a790 1161 offset_intervals (current_buffer, PT, nchars);
ce97a2d7 1162
b16afa45 1163 /* Get the intervals for the part of the string we are inserting. */
0c94c8d6 1164 intervals = buffer_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);
8707c1e5 1228
0c94c8d6 1229 if (buffer_intervals (current_buffer))
8707c1e5 1230 offset_intervals (current_buffer, from, len - nchars_del);
61415a25 1231
b16afa45
KH
1232 if (from < PT)
1233 adjust_point (len - nchars_del, len_byte - nbytes_del);
61415a25 1234
9f3ede3c 1235 /* As byte combining will decrease Z, we must check this again. */
a50df55b
GM
1236 if (Z - GPT < END_UNCHANGED)
1237 END_UNCHANGED = Z - GPT;
9f3ede3c 1238
e2688e4a 1239 check_markers ();
60ea6052 1240
1e9c7b7d 1241 if (len == 0)
b58e3ca1
RS
1242 evaporate_overlays (from);
1243 MODIFF++;
3e145152 1244 CHARS_MODIFF = MODIFF;
b58e3ca1
RS
1245}
1246
652838b5
KH
1247/* Record undo information, adjust markers and position keepers for an
1248 insertion of a text from FROM (FROM_BYTE) to TO (TO_BYTE). The
1249 text already exists in the current buffer but character length (TO
1250 - FROM) may be incorrect, the correct length is NEWLEN. */
1251
1252void
d311d28c
PE
1253adjust_after_insert (ptrdiff_t from, ptrdiff_t from_byte,
1254 ptrdiff_t to, ptrdiff_t to_byte, ptrdiff_t newlen)
652838b5 1255{
d311d28c 1256 ptrdiff_t len = to - from, len_byte = to_byte - from_byte;
652838b5
KH
1257
1258 if (GPT != to)
1259 move_gap_both (to, to_byte);
1260 GAP_SIZE += len_byte;
1261 GPT -= len; GPT_BYTE -= len_byte;
1262 ZV -= len; ZV_BYTE -= len_byte;
1263 Z -= len; Z_BYTE -= len_byte;
1264 adjust_after_replace (from, from_byte, Qnil, newlen, len_byte);
1265}
085db7de 1266\f
3be11131 1267/* Replace the text from character positions FROM to TO with NEW,
c5ca4d3a
RS
1268 If PREPARE is nonzero, call prepare_to_modify_buffer.
1269 If INHERIT, the newly inserted text should inherit text properties
1270 from the surrounding non-deleted text. */
1271
1272/* Note that this does not yet handle markers quite right.
1273 Also it needs to record a single undo-entry that does a replacement
1274 rather than a separate delete and insert.
60aa777a
RS
1275 That way, undo will also handle markers properly.
1276
1277 But if MARKERS is 0, don't relocate markers. */
c5ca4d3a
RS
1278
1279void
d311d28c 1280replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
ae19ba7c 1281 int prepare, int inherit, int markers)
c5ca4d3a 1282{
d311d28c
PE
1283 ptrdiff_t inschars = SCHARS (new);
1284 ptrdiff_t insbytes = SBYTES (new);
1285 ptrdiff_t from_byte, to_byte;
1286 ptrdiff_t nbytes_del, nchars_del;
c5ca4d3a 1287 struct gcpro gcpro1;
ce97a2d7 1288 INTERVAL intervals;
d311d28c 1289 ptrdiff_t outgoing_insbytes = insbytes;
23b20a70 1290 Lisp_Object deletion;
c5ca4d3a 1291
e2688e4a 1292 check_markers ();
60ea6052 1293
c5ca4d3a 1294 GCPRO1 (new);
6bbd7a29 1295 deletion = Qnil;
c5ca4d3a
RS
1296
1297 if (prepare)
1298 {
d311d28c 1299 ptrdiff_t range_length = to - from;
c5ca4d3a
RS
1300 prepare_to_modify_buffer (from, to, &from);
1301 to = from + range_length;
1302 }
1303
3be11131
RS
1304 UNGCPRO;
1305
b50a28de 1306 /* Make args be valid. */
c5ca4d3a
RS
1307 if (from < BEGV)
1308 from = BEGV;
1309 if (to > ZV)
1310 to = ZV;
1311
3be11131
RS
1312 from_byte = CHAR_TO_BYTE (from);
1313 to_byte = CHAR_TO_BYTE (to);
c5ca4d3a 1314
3be11131
RS
1315 nchars_del = to - from;
1316 nbytes_del = to_byte - from_byte;
1317
1318 if (nbytes_del <= 0 && insbytes == 0)
1319 return;
c5ca4d3a 1320
1f90a790
RS
1321 /* Make OUTGOING_INSBYTES describe the text
1322 as it will be inserted in this buffer. */
1323
4b4deea2 1324 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
1f90a790 1325 outgoing_insbytes = inschars;
2a1d8be0 1326 else if (! STRING_MULTIBYTE (new))
1f90a790 1327 outgoing_insbytes
d5db4077 1328 = count_size_as_multibyte (SDATA (new), insbytes);
1f90a790 1329
c5ca4d3a
RS
1330 GCPRO1 (new);
1331
1332 /* Make sure the gap is somewhere in or next to what we are deleting. */
1333 if (from > GPT)
3be11131 1334 gap_right (from, from_byte);
c5ca4d3a 1335 if (to < GPT)
3be11131 1336 gap_left (to, to_byte, 0);
c5ca4d3a 1337
039af53a
KH
1338 /* Even if we don't record for undo, we must keep the original text
1339 because we may have to recover it because of inappropriate byte
1340 combining. */
4b4deea2 1341 if (! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1342 deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
c5ca4d3a 1343
3be11131
RS
1344 GAP_SIZE += nbytes_del;
1345 ZV -= nchars_del;
1346 Z -= nchars_del;
1347 ZV_BYTE -= nbytes_del;
1348 Z_BYTE -= nbytes_del;
c5ca4d3a 1349 GPT = from;
3be11131 1350 GPT_BYTE = from_byte;
017e09ac 1351 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
c5ca4d3a 1352
e2688e4a 1353 eassert (GPT <= GPT_BYTE);
3be11131 1354
a50df55b
GM
1355 if (GPT - BEG < BEG_UNCHANGED)
1356 BEG_UNCHANGED = GPT - BEG;
1357 if (Z - GPT < END_UNCHANGED)
1358 END_UNCHANGED = Z - GPT;
c5ca4d3a 1359
599a9e4f
PE
1360 if (GAP_SIZE < outgoing_insbytes)
1361 make_gap (outgoing_insbytes - GAP_SIZE);
c5ca4d3a 1362
1f90a790
RS
1363 /* Copy the string text into the buffer, perhaps converting
1364 between single-byte and multibyte. */
d5db4077 1365 copy_text (SDATA (new), GPT_ADDR, insbytes,
2a1d8be0 1366 STRING_MULTIBYTE (new),
4b4deea2 1367 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
1f90a790 1368
b16afa45 1369#ifdef BYTE_COMBINING_DEBUG
255c7dae
RS
1370 /* We have copied text into the gap, but we have not marked
1371 it as part of the buffer. So we can use the old FROM and FROM_BYTE
1372 here, for both the previous text and the following text.
1373 Meanwhile, GPT_ADDR does point to
432f78d2 1374 the text that has been stored by copy_text. */
b16afa45
KH
1375 if (count_combining_before (GPT_ADDR, outgoing_insbytes, from, from_byte)
1376 || count_combining_after (GPT_ADDR, outgoing_insbytes, from, from_byte))
1377 abort ();
1378#endif
432f78d2 1379
4b4deea2 1380 if (! EQ (BVAR (current_buffer, undo_list), Qt))
23b20a70 1381 {
bec1aca2
RS
1382 /* Record the insertion first, so that when we undo,
1383 the deletion will be undone first. Thus, undo
1384 will insert before deleting, and thus will keep
1385 the markers before and after this text separate. */
1386 record_insert (from + SCHARS (deletion), inschars);
b16afa45 1387 record_delete (from, deletion);
23b20a70 1388 }
c5ca4d3a 1389
1f90a790
RS
1390 GAP_SIZE -= outgoing_insbytes;
1391 GPT += inschars;
1392 ZV += inschars;
1393 Z += inschars;
1394 GPT_BYTE += outgoing_insbytes;
1395 ZV_BYTE += outgoing_insbytes;
1396 Z_BYTE += outgoing_insbytes;
c5ca4d3a
RS
1397 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1398
e2688e4a 1399 eassert (GPT <= GPT_BYTE);
3be11131 1400
c5ca4d3a
RS
1401 /* Adjust the overlay center as needed. This must be done after
1402 adjusting the markers that bound the overlays. */
3be11131 1403 adjust_overlays_for_delete (from, nchars_del);
1f90a790 1404 adjust_overlays_for_insert (from, inschars);
048a0fbb
RS
1405
1406 /* Adjust markers for the deletion and the insertion. */
60aa777a 1407 if (markers)
048a0fbb
RS
1408 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1409 inschars, outgoing_insbytes);
c5ca4d3a 1410
255c7dae 1411 offset_intervals (current_buffer, from, inschars - nchars_del);
ce97a2d7
RS
1412
1413 /* Get the intervals for the part of the string we are inserting--
1414 not including the combined-before bytes. */
0c94c8d6 1415 intervals = string_intervals (new);
ce97a2d7 1416 /* Insert those intervals. */
1f90a790 1417 graft_intervals_into_buffer (intervals, from, inschars,
ce97a2d7 1418 current_buffer, inherit);
c5ca4d3a 1419
1f90a790
RS
1420 /* Relocate point as if it were a marker. */
1421 if (from < PT)
8bedbe9d 1422 adjust_point ((from + inschars - (PT < to ? PT : to)),
1f90a790 1423 (from_byte + outgoing_insbytes
8bedbe9d 1424 - (PT_BYTE < to_byte ? PT_BYTE : to_byte)));
c5ca4d3a 1425
1f90a790
RS
1426 if (outgoing_insbytes == 0)
1427 evaporate_overlays (from);
93b882e8 1428
e2688e4a 1429 check_markers ();
60ea6052 1430
c5ca4d3a 1431 MODIFF++;
3e145152 1432 CHARS_MODIFF = MODIFF;
c5ca4d3a
RS
1433 UNGCPRO;
1434
255c7dae 1435 signal_after_change (from, nchars_del, GPT - from);
0ef71121 1436 update_compositions (from, GPT, CHECK_BORDER);
c5ca4d3a
RS
1437}
1438\f
085db7de
RS
1439/* Replace the text from character positions FROM to TO with
1440 the text in INS of length INSCHARS.
1441 Keep the text properties that applied to the old characters
1442 (extending them to all the new chars if there are more new chars).
1443
1444 Note that this does not yet handle markers quite right.
1445
1446 If MARKERS is nonzero, relocate markers.
1447
1448 Unlike most functions at this level, never call
1449 prepare_to_modify_buffer and never call signal_after_change. */
1450
1451void
d311d28c
PE
1452replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
1453 ptrdiff_t to, ptrdiff_t to_byte,
1454 const char *ins, ptrdiff_t inschars, ptrdiff_t insbytes,
ae19ba7c 1455 int markers)
085db7de 1456{
d311d28c 1457 ptrdiff_t nbytes_del, nchars_del;
085db7de 1458
e2688e4a 1459 check_markers ();
085db7de
RS
1460
1461 nchars_del = to - from;
1462 nbytes_del = to_byte - from_byte;
1463
1464 if (nbytes_del <= 0 && insbytes == 0)
1465 return;
1466
085db7de
RS
1467 /* Make sure the gap is somewhere in or next to what we are deleting. */
1468 if (from > GPT)
1469 gap_right (from, from_byte);
1470 if (to < GPT)
1471 gap_left (to, to_byte, 0);
1472
1473 GAP_SIZE += nbytes_del;
1474 ZV -= nchars_del;
1475 Z -= nchars_del;
1476 ZV_BYTE -= nbytes_del;
1477 Z_BYTE -= nbytes_del;
1478 GPT = from;
1479 GPT_BYTE = from_byte;
1480 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1481
e2688e4a 1482 eassert (GPT <= GPT_BYTE);
085db7de
RS
1483
1484 if (GPT - BEG < BEG_UNCHANGED)
1485 BEG_UNCHANGED = GPT - BEG;
1486 if (Z - GPT < END_UNCHANGED)
1487 END_UNCHANGED = Z - GPT;
1488
1489 if (GAP_SIZE < insbytes)
1490 make_gap (insbytes - GAP_SIZE);
1491
1492 /* Copy the replacement text into the buffer. */
72af86bd 1493 memcpy (GPT_ADDR, ins, insbytes);
085db7de
RS
1494
1495#ifdef BYTE_COMBINING_DEBUG
1496 /* We have copied text into the gap, but we have not marked
1497 it as part of the buffer. So we can use the old FROM and FROM_BYTE
1498 here, for both the previous text and the following text.
1499 Meanwhile, GPT_ADDR does point to
1500 the text that has been stored by copy_text. */
1501 if (count_combining_before (GPT_ADDR, insbytes, from, from_byte)
1502 || count_combining_after (GPT_ADDR, insbytes, from, from_byte))
1503 abort ();
1504#endif
1505
1506 GAP_SIZE -= insbytes;
1507 GPT += inschars;
1508 ZV += inschars;
1509 Z += inschars;
1510 GPT_BYTE += insbytes;
1511 ZV_BYTE += insbytes;
1512 Z_BYTE += insbytes;
1513 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1514
e2688e4a 1515 eassert (GPT <= GPT_BYTE);
085db7de
RS
1516
1517 /* Adjust the overlay center as needed. This must be done after
1518 adjusting the markers that bound the overlays. */
1519 if (nchars_del != inschars)
1520 {
1521 adjust_overlays_for_insert (from, inschars);
1522 adjust_overlays_for_delete (from + inschars, nchars_del);
1523 }
1524
1525 /* Adjust markers for the deletion and the insertion. */
1526 if (markers
7077904e 1527 && ! (nchars_del == 1 && inschars == 1 && nbytes_del == insbytes))
085db7de
RS
1528 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1529 inschars, insbytes);
1530
1531 offset_intervals (current_buffer, from, inschars - nchars_del);
1532
1533 /* Relocate point as if it were a marker. */
7077904e
KH
1534 if (from < PT && (nchars_del != inschars || nbytes_del != insbytes))
1535 {
1536 if (PT < to)
1537 /* PT was within the deleted text. Move it to FROM. */
1538 adjust_point (from - PT, from_byte - PT_BYTE);
1539 else
1540 adjust_point (inschars - nchars_del, insbytes - nbytes_del);
1541 }
085db7de
RS
1542
1543 if (insbytes == 0)
1544 evaporate_overlays (from);
1545
e2688e4a 1546 check_markers ();
085db7de
RS
1547
1548 MODIFF++;
3e145152 1549 CHARS_MODIFF = MODIFF;
085db7de
RS
1550}
1551\f
b45433b3 1552/* Delete characters in current buffer
3be11131
RS
1553 from FROM up to (but not including) TO.
1554 If TO comes before FROM, we delete nothing. */
b45433b3 1555
c660b094 1556void
d311d28c 1557del_range (ptrdiff_t from, ptrdiff_t to)
47c64747 1558{
7dae4502 1559 del_range_1 (from, to, 1, 0);
47c64747
RS
1560}
1561
7dae4502
SM
1562/* Like del_range; PREPARE says whether to call prepare_to_modify_buffer.
1563 RET_STRING says to return the deleted text. */
47c64747 1564
7dae4502 1565Lisp_Object
d311d28c 1566del_range_1 (ptrdiff_t from, ptrdiff_t to, int prepare, int ret_string)
b45433b3 1567{
d311d28c 1568 ptrdiff_t from_byte, to_byte;
7dae4502
SM
1569 Lisp_Object deletion;
1570 struct gcpro gcpro1;
3be11131
RS
1571
1572 /* Make args be valid */
1573 if (from < BEGV)
1574 from = BEGV;
1575 if (to > ZV)
1576 to = ZV;
1577
1578 if (to <= from)
7dae4502 1579 return Qnil;
3be11131
RS
1580
1581 if (prepare)
1582 {
d311d28c 1583 ptrdiff_t range_length = to - from;
3be11131 1584 prepare_to_modify_buffer (from, to, &from);
0a411995 1585 to = min (ZV, from + range_length);
3be11131
RS
1586 }
1587
1588 from_byte = CHAR_TO_BYTE (from);
1589 to_byte = CHAR_TO_BYTE (to);
1590
7dae4502 1591 deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
5e617bc2 1592 GCPRO1 (deletion);
9c36993a 1593 signal_after_change (from, to - from, 0);
233cc02d 1594 update_compositions (from, from, CHECK_HEAD);
7dae4502
SM
1595 UNGCPRO;
1596 return deletion;
3be11131
RS
1597}
1598
1599/* Like del_range_1 but args are byte positions, not char positions. */
1600
1601void
d311d28c 1602del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte, int prepare)
3be11131 1603{
d311d28c 1604 ptrdiff_t from, to;
3be11131
RS
1605
1606 /* Make args be valid */
1607 if (from_byte < BEGV_BYTE)
1608 from_byte = BEGV_BYTE;
1609 if (to_byte > ZV_BYTE)
1610 to_byte = ZV_BYTE;
1611
1612 if (to_byte <= from_byte)
1613 return;
1614
1615 from = BYTE_TO_CHAR (from_byte);
1616 to = BYTE_TO_CHAR (to_byte);
b45433b3 1617
d206af14
RS
1618 if (prepare)
1619 {
d311d28c
PE
1620 ptrdiff_t old_from = from, old_to = Z - to;
1621 ptrdiff_t range_length = to - from;
d206af14
RS
1622 prepare_to_modify_buffer (from, to, &from);
1623 to = from + range_length;
3be11131
RS
1624
1625 if (old_from != from)
1626 from_byte = CHAR_TO_BYTE (from);
0a411995
GM
1627 if (to > ZV)
1628 {
1629 to = ZV;
1630 to_byte = ZV_BYTE;
1631 }
1632 else if (old_to == Z - to)
3be11131 1633 to_byte = CHAR_TO_BYTE (to);
d206af14
RS
1634 }
1635
7dae4502 1636 del_range_2 (from, from_byte, to, to_byte, 0);
9c36993a 1637 signal_after_change (from, to - from, 0);
0ef71121 1638 update_compositions (from, from, CHECK_HEAD);
3be11131
RS
1639}
1640
1641/* Like del_range_1, but positions are specified both as charpos
1642 and bytepos. */
1643
1644void
d311d28c
PE
1645del_range_both (ptrdiff_t from, ptrdiff_t from_byte,
1646 ptrdiff_t to, ptrdiff_t to_byte, int prepare)
3be11131 1647{
b45433b3 1648 /* Make args be valid */
3be11131
RS
1649 if (from_byte < BEGV_BYTE)
1650 from_byte = BEGV_BYTE;
1651 if (to_byte > ZV_BYTE)
1652 to_byte = ZV_BYTE;
1653
1654 if (to_byte <= from_byte)
1655 return;
1656
b45433b3
JB
1657 if (from < BEGV)
1658 from = BEGV;
1659 if (to > ZV)
1660 to = ZV;
1661
3be11131
RS
1662 if (prepare)
1663 {
d311d28c
PE
1664 ptrdiff_t old_from = from, old_to = Z - to;
1665 ptrdiff_t range_length = to - from;
3be11131
RS
1666 prepare_to_modify_buffer (from, to, &from);
1667 to = from + range_length;
1668
1669 if (old_from != from)
1670 from_byte = CHAR_TO_BYTE (from);
0a411995
GM
1671 if (to > ZV)
1672 {
1673 to = ZV;
1674 to_byte = ZV_BYTE;
1675 }
1676 else if (old_to == Z - to)
3be11131
RS
1677 to_byte = CHAR_TO_BYTE (to);
1678 }
1679
7dae4502 1680 del_range_2 (from, from_byte, to, to_byte, 0);
9c36993a 1681 signal_after_change (from, to - from, 0);
0ef71121 1682 update_compositions (from, from, CHECK_HEAD);
3be11131
RS
1683}
1684
1685/* Delete a range of text, specified both as character positions
1686 and byte positions. FROM and TO are character positions,
7dae4502
SM
1687 while FROM_BYTE and TO_BYTE are byte positions.
1688 If RET_STRING is true, the deleted area is returned as a string. */
3be11131 1689
7dae4502 1690Lisp_Object
d311d28c
PE
1691del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
1692 ptrdiff_t to, ptrdiff_t to_byte, int ret_string)
3be11131 1693{
d311d28c 1694 register ptrdiff_t nbytes_del, nchars_del;
628cea90 1695 Lisp_Object deletion;
3be11131 1696
e2688e4a 1697 check_markers ();
60ea6052 1698
3be11131
RS
1699 nchars_del = to - from;
1700 nbytes_del = to_byte - from_byte;
b45433b3
JB
1701
1702 /* Make sure the gap is somewhere in or next to what we are deleting. */
1703 if (from > GPT)
3be11131 1704 gap_right (from, from_byte);
b45433b3 1705 if (to < GPT)
3be11131 1706 gap_left (to, to_byte, 0);
b45433b3 1707
b16afa45
KH
1708#ifdef BYTE_COMBINING_DEBUG
1709 if (count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
1710 Z_BYTE - to_byte, from, from_byte))
1711 abort ();
1712#endif
628cea90 1713
4b4deea2 1714 if (ret_string || ! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1715 deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
7dae4502
SM
1716 else
1717 deletion = Qnil;
1718
8948d317
RS
1719 /* Relocate all markers pointing into the new, larger gap
1720 to point at the end of the text before the gap.
3be11131
RS
1721 Do this before recording the deletion,
1722 so that undo handles this after reinserting the text. */
1723 adjust_markers_for_delete (from, from_byte, to, to_byte);
b16afa45 1724
4b4deea2 1725 if (! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1726 record_delete (from, deletion);
be09561e 1727 MODIFF++;
3e145152 1728 CHARS_MODIFF = MODIFF;
be09561e 1729
b45433b3 1730 /* Relocate point as if it were a marker. */
2bcaed71 1731 if (from < PT)
3be11131
RS
1732 adjust_point (from - (PT < to ? PT : to),
1733 from_byte - (PT_BYTE < to_byte ? PT_BYTE : to_byte));
b45433b3 1734
3be11131 1735 offset_intervals (current_buffer, from, - nchars_del);
16032db6 1736
adde4858 1737 /* Adjust the overlay center as needed. This must be done after
a7f38d28 1738 adjusting the markers that bound the overlays. */
e3a87305 1739 adjust_overlays_for_delete (from, nchars_del);
adde4858 1740
3be11131
RS
1741 GAP_SIZE += nbytes_del;
1742 ZV_BYTE -= nbytes_del;
1743 Z_BYTE -= nbytes_del;
1744 ZV -= nchars_del;
1745 Z -= nchars_del;
b45433b3 1746 GPT = from;
3be11131 1747 GPT_BYTE = from_byte;
b3b58c01
AS
1748 if (GAP_SIZE > 0 && !current_buffer->text->inhibit_shrinking)
1749 /* Put an anchor, unless called from decode_coding_object which
1750 needs to access the previous gap contents. */
1751 *(GPT_ADDR) = 0;
e3a87305 1752
e2688e4a 1753 eassert (GPT <= GPT_BYTE);
3be11131 1754
a50df55b
GM
1755 if (GPT - BEG < BEG_UNCHANGED)
1756 BEG_UNCHANGED = GPT - BEG;
1757 if (Z - GPT < END_UNCHANGED)
1758 END_UNCHANGED = Z - GPT;
b45433b3 1759
e2688e4a 1760 check_markers ();
60ea6052 1761
d386034e 1762 evaporate_overlays (from);
7dae4502
SM
1763
1764 return deletion;
b45433b3
JB
1765}
1766\f
3be11131
RS
1767/* Call this if you're about to change the region of BUFFER from
1768 character positions START to END. This checks the read-only
1769 properties of the region, calls the necessary modification hooks,
1770 and warns the next redisplay that it should pay attention to that
3e145152
CY
1771 area.
1772
1773 If PRESERVE_CHARS_MODIFF is non-zero, do not update CHARS_MODIFF.
1774 Otherwise set CHARS_MODIFF to the new value of MODIFF. */
3be11131 1775
c660b094 1776void
d311d28c 1777modify_region (struct buffer *buffer, ptrdiff_t start, ptrdiff_t end,
ae19ba7c 1778 int preserve_chars_modiff)
b45433b3 1779{
04a759c8
JB
1780 struct buffer *old_buffer = current_buffer;
1781
1782 if (buffer != old_buffer)
1783 set_buffer_internal (buffer);
1784
d206af14 1785 prepare_to_modify_buffer (start, end, NULL);
b45433b3 1786
a50df55b 1787 BUF_COMPUTE_UNCHANGED (buffer, start - 1, end);
83010cd6 1788
9fbf87cd 1789 if (MODIFF <= SAVE_MODIFF)
83010cd6 1790 record_first_change ();
b45433b3 1791 MODIFF++;
3e145152
CY
1792 if (! preserve_chars_modiff)
1793 CHARS_MODIFF = MODIFF;
04a759c8 1794
39eb03f1 1795 bset_point_before_scroll (buffer, Qnil);
069cdc4f 1796
04a759c8
JB
1797 if (buffer != old_buffer)
1798 set_buffer_internal (old_buffer);
b45433b3 1799}
d206af14 1800\f
3be11131
RS
1801/* Check that it is okay to modify the buffer between START and END,
1802 which are char positions.
1803
679194a6
JA
1804 Run the before-change-function, if any. If intervals are in use,
1805 verify that the text to be modified is not read-only, and call
d206af14
RS
1806 any modification properties the text may have.
1807
1808 If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
1809 by holding its value temporarily in a marker. */
b45433b3 1810
c660b094 1811void
d311d28c
PE
1812prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
1813 ptrdiff_t *preserve_ptr)
b45433b3 1814{
234fb773
CY
1815 struct buffer *base_buffer;
1816
4b4deea2 1817 if (!NILP (BVAR (current_buffer, read_only)))
b45433b3
JB
1818 Fbarf_if_buffer_read_only ();
1819
2e9f55fd
GM
1820 /* Let redisplay consider other windows than selected_window
1821 if modifying another buffer. */
d3d50620 1822 if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer)
2e9f55fd
GM
1823 ++windows_or_buffers_changed;
1824
0c94c8d6 1825 if (buffer_intervals (current_buffer))
d206af14
RS
1826 {
1827 if (preserve_ptr)
1828 {
1829 Lisp_Object preserve_marker;
1830 struct gcpro gcpro1;
1831 preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil);
1832 GCPRO1 (preserve_marker);
1833 verify_interval_modification (current_buffer, start, end);
1834 *preserve_ptr = marker_position (preserve_marker);
dab0b04d 1835 unchain_marker (XMARKER (preserve_marker));
d206af14
RS
1836 UNGCPRO;
1837 }
1838 else
1839 verify_interval_modification (current_buffer, start, end);
1840 }
b45433b3 1841
234fb773
CY
1842 /* For indirect buffers, use the base buffer to check clashes. */
1843 if (current_buffer->base_buffer != 0)
1844 base_buffer = current_buffer->base_buffer;
1845 else
1846 base_buffer = current_buffer;
1847
b45433b3 1848#ifdef CLASH_DETECTION
4b4deea2 1849 if (!NILP (BVAR (base_buffer, file_truename))
ab6c5c0c 1850 /* Make binding buffer-file-name to nil effective. */
4b4deea2 1851 && !NILP (BVAR (base_buffer, filename))
9fbf87cd 1852 && SAVE_MODIFF >= MODIFF)
4b4deea2 1853 lock_file (BVAR (base_buffer, file_truename));
b45433b3
JB
1854#else
1855 /* At least warn if this file has changed on disk since it was visited. */
4b4deea2 1856 if (!NILP (BVAR (base_buffer, filename))
9fbf87cd 1857 && SAVE_MODIFF >= MODIFF
d427b66a 1858 && NILP (Fverify_visited_file_modtime (Fcurrent_buffer ()))
4b4deea2 1859 && !NILP (Ffile_exists_p (BVAR (base_buffer, filename))))
b45433b3 1860 call1 (intern ("ask-user-about-supersession-threat"),
4b4deea2 1861 BVAR (base_buffer,filename));
b45433b3
JB
1862#endif /* not CLASH_DETECTION */
1863
9852377f 1864 /* If `select-active-regions' is non-nil, save the region text. */
4b4deea2 1865 if (!NILP (BVAR (current_buffer, mark_active))
8b78d5e3 1866 && !inhibit_modification_hooks
4b4deea2 1867 && XMARKER (BVAR (current_buffer, mark))->buffer
7c23dd44
CY
1868 && NILP (Vsaved_region_selection)
1869 && (EQ (Vselect_active_regions, Qonly)
1870 ? EQ (CAR_SAFE (Vtransient_mark_mode), Qonly)
1871 : (!NILP (Vselect_active_regions)
1872 && !NILP (Vtransient_mark_mode))))
9852377f 1873 {
d311d28c
PE
1874 ptrdiff_t b = XMARKER (BVAR (current_buffer, mark))->charpos;
1875 ptrdiff_t e = PT;
746812d9
CY
1876 if (b < e)
1877 Vsaved_region_selection = make_buffer_string (b, e, 0);
1878 else if (b > e)
1879 Vsaved_region_selection = make_buffer_string (e, b, 0);
9852377f
CY
1880 }
1881
d206af14 1882 signal_before_change (start, end, preserve_ptr);
2f545eea 1883
56e1065e
JB
1884 if (current_buffer->newline_cache)
1885 invalidate_region_cache (current_buffer,
1886 current_buffer->newline_cache,
1887 start - BEG, Z - end);
1888 if (current_buffer->width_run_cache)
1889 invalidate_region_cache (current_buffer,
1890 current_buffer->width_run_cache,
1891 start - BEG, Z - end);
1892
2f545eea 1893 Vdeactivate_mark = Qt;
b45433b3
JB
1894}
1895\f
d206af14
RS
1896/* These macros work with an argument named `preserve_ptr'
1897 and a local variable named `preserve_marker'. */
1898
1899#define PRESERVE_VALUE \
1900 if (preserve_ptr && NILP (preserve_marker)) \
1901 preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil)
1902
1903#define RESTORE_VALUE \
1904 if (! NILP (preserve_marker)) \
1905 { \
1906 *preserve_ptr = marker_position (preserve_marker); \
dab0b04d 1907 unchain_marker (XMARKER (preserve_marker)); \
d206af14
RS
1908 }
1909
b86e0aaf
RS
1910#define PRESERVE_START_END \
1911 if (NILP (start_marker)) \
1912 start_marker = Fcopy_marker (start, Qnil); \
1913 if (NILP (end_marker)) \
1914 end_marker = Fcopy_marker (end, Qnil);
1915
1916#define FETCH_START \
1917 (! NILP (start_marker) ? Fmarker_position (start_marker) : start)
1918
1919#define FETCH_END \
1920 (! NILP (end_marker) ? Fmarker_position (end_marker) : end)
1921
4a181359 1922/* Set a variable to nil if an error occurred.
e6d3f090
SM
1923 Don't change the variable if there was no error.
1924 VAL is a cons-cell (VARIABLE . NO-ERROR-FLAG).
1925 VARIABLE is the variable to maybe set to nil.
1926 NO-ERROR-FLAG is nil if there was an error,
1927 anything else meaning no error (so this function does nothing). */
85876d07 1928static Lisp_Object
971de7fb 1929reset_var_on_error (Lisp_Object val)
4a181359
SM
1930{
1931 if (NILP (XCDR (val)))
1932 Fset (XCAR (val), Qnil);
1933 return Qnil;
1934}
1935
eb8c3be9 1936/* Signal a change to the buffer immediately before it happens.
d206af14
RS
1937 START_INT and END_INT are the bounds of the text to be changed.
1938
1939 If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
1940 by holding its value temporarily in a marker. */
b45433b3 1941
4889fc82 1942static void
d311d28c
PE
1943signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
1944 ptrdiff_t *preserve_ptr)
b45433b3 1945{
fb4ee5cd 1946 Lisp_Object start, end;
b86e0aaf 1947 Lisp_Object start_marker, end_marker;
d206af14 1948 Lisp_Object preserve_marker;
b86e0aaf 1949 struct gcpro gcpro1, gcpro2, gcpro3;
d311d28c 1950 ptrdiff_t count = SPECPDL_INDEX ();
fb4ee5cd 1951
fd16a4c6
KH
1952 if (inhibit_modification_hooks)
1953 return;
1954
fb4ee5cd
RS
1955 start = make_number (start_int);
1956 end = make_number (end_int);
d206af14 1957 preserve_marker = Qnil;
b86e0aaf
RS
1958 start_marker = Qnil;
1959 end_marker = Qnil;
1960 GCPRO3 (preserve_marker, start_marker, end_marker);
fb4ee5cd 1961
4a181359
SM
1962 specbind (Qinhibit_modification_hooks, Qt);
1963
dee091a3
JD
1964 /* If buffer is unmodified, run a special hook for that case. The
1965 check for Vfirst_change_hook is just a minor optimization. */
9fbf87cd 1966 if (SAVE_MODIFF >= MODIFF
dee091a3 1967 && !NILP (Vfirst_change_hook))
d206af14
RS
1968 {
1969 PRESERVE_VALUE;
b86e0aaf 1970 PRESERVE_START_END;
dee091a3 1971 Frun_hooks (1, &Qfirst_change_hook);
d206af14 1972 }
dbc4e1c1 1973
3d1e2d9c 1974 /* Now run the before-change-functions if any. */
e45fb8bf
RS
1975 if (!NILP (Vbefore_change_functions))
1976 {
3d1e2d9c 1977 Lisp_Object args[3];
4a181359 1978 Lisp_Object rvoe_arg = Fcons (Qbefore_change_functions, Qnil);
3d1e2d9c 1979
d206af14 1980 PRESERVE_VALUE;
b86e0aaf 1981 PRESERVE_START_END;
d206af14 1982
4a181359
SM
1983 /* Mark before-change-functions to be reset to nil in case of error. */
1984 record_unwind_protect (reset_var_on_error, rvoe_arg);
3d1e2d9c
RS
1985
1986 /* Actually run the hook functions. */
1987 args[0] = Qbefore_change_functions;
b86e0aaf
RS
1988 args[1] = FETCH_START;
1989 args[2] = FETCH_END;
4a181359 1990 Frun_hook_with_args (3, args);
3d1e2d9c 1991
4a181359
SM
1992 /* There was no error: unarm the reset_on_error. */
1993 XSETCDR (rvoe_arg, Qt);
e45fb8bf 1994 }
d07c0804 1995
4cb3e6b3 1996 if (buffer_has_overlays ())
d206af14
RS
1997 {
1998 PRESERVE_VALUE;
b86e0aaf
RS
1999 report_overlay_modification (FETCH_START, FETCH_END, 0,
2000 FETCH_START, FETCH_END, Qnil);
d206af14
RS
2001 }
2002
b86e0aaf
RS
2003 if (! NILP (start_marker))
2004 free_marker (start_marker);
2005 if (! NILP (end_marker))
2006 free_marker (end_marker);
d206af14
RS
2007 RESTORE_VALUE;
2008 UNGCPRO;
4a181359
SM
2009
2010 unbind_to (count, Qnil);
b45433b3
JB
2011}
2012
eb8c3be9 2013/* Signal a change immediately after it happens.
3be11131 2014 CHARPOS is the character position of the start of the changed text.
b45433b3
JB
2015 LENDEL is the number of characters of the text before the change.
2016 (Not the whole buffer; just the part that was changed.)
8b09e5d0
RS
2017 LENINS is the number of characters in that part of the text
2018 after the change. */
b45433b3 2019
c660b094 2020void
d311d28c 2021signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
b45433b3 2022{
d311d28c 2023 ptrdiff_t count = SPECPDL_INDEX ();
fd16a4c6
KH
2024 if (inhibit_modification_hooks)
2025 return;
2026
fb2e7d14
RS
2027 /* If we are deferring calls to the after-change functions
2028 and there are no before-change functions,
2029 just record the args that we were going to use. */
2030 if (! NILP (Vcombine_after_change_calls)
1675d086 2031 && NILP (Vbefore_change_functions)
4cb3e6b3 2032 && !buffer_has_overlays ())
fb2e7d14
RS
2033 {
2034 Lisp_Object elt;
2035
2036 if (!NILP (combine_after_change_list)
2037 && current_buffer != XBUFFER (combine_after_change_buffer))
2038 Fcombine_after_change_execute ();
2039
3be11131
RS
2040 elt = Fcons (make_number (charpos - BEG),
2041 Fcons (make_number (Z - (charpos - lendel + lenins)),
fb2e7d14
RS
2042 Fcons (make_number (lenins - lendel), Qnil)));
2043 combine_after_change_list
2044 = Fcons (elt, combine_after_change_list);
2045 combine_after_change_buffer = Fcurrent_buffer ();
2046
2047 return;
2048 }
2049
177c0ea7 2050 if (!NILP (combine_after_change_list))
fb2e7d14
RS
2051 Fcombine_after_change_execute ();
2052
4a181359
SM
2053 specbind (Qinhibit_modification_hooks, Qt);
2054
e45fb8bf
RS
2055 if (!NILP (Vafter_change_functions))
2056 {
3d1e2d9c 2057 Lisp_Object args[4];
4a181359
SM
2058 Lisp_Object rvoe_arg = Fcons (Qafter_change_functions, Qnil);
2059
2060 /* Mark after-change-functions to be reset to nil in case of error. */
2061 record_unwind_protect (reset_var_on_error, rvoe_arg);
3d1e2d9c
RS
2062
2063 /* Actually run the hook functions. */
2064 args[0] = Qafter_change_functions;
3be11131
RS
2065 XSETFASTINT (args[1], charpos);
2066 XSETFASTINT (args[2], charpos + lenins);
3d1e2d9c 2067 XSETFASTINT (args[3], lendel);
4a181359 2068 Frun_hook_with_args (4, args);
3d1e2d9c 2069
4a181359
SM
2070 /* There was no error: unarm the reset_on_error. */
2071 XSETCDR (rvoe_arg, Qt);
e45fb8bf 2072 }
d07c0804 2073
4cb3e6b3 2074 if (buffer_has_overlays ())
3be11131
RS
2075 report_overlay_modification (make_number (charpos),
2076 make_number (charpos + lenins),
d07c0804 2077 1,
3be11131
RS
2078 make_number (charpos),
2079 make_number (charpos + lenins),
d07c0804 2080 make_number (lendel));
c5ca0786
RS
2081
2082 /* After an insertion, call the text properties
2083 insert-behind-hooks or insert-in-front-hooks. */
2084 if (lendel == 0)
d6b81c0f
AS
2085 report_interval_modification (make_number (charpos),
2086 make_number (charpos + lenins));
4a181359
SM
2087
2088 unbind_to (count, Qnil);
b45433b3 2089}
fb2e7d14 2090
85876d07 2091static Lisp_Object
971de7fb 2092Fcombine_after_change_execute_1 (Lisp_Object val)
fb2e7d14
RS
2093{
2094 Vcombine_after_change_calls = val;
2095 return val;
2096}
2097
2098DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
335c5470
PJ
2099 Scombine_after_change_execute, 0, 0, 0,
2100 doc: /* This function is for use internally in `combine-after-change-calls'. */)
5842a27b 2101 (void)
fb2e7d14 2102{
d311d28c
PE
2103 ptrdiff_t count = SPECPDL_INDEX ();
2104 ptrdiff_t beg, end, change;
2105 ptrdiff_t begpos, endpos;
fb2e7d14
RS
2106 Lisp_Object tail;
2107
101e446f
KH
2108 if (NILP (combine_after_change_list))
2109 return Qnil;
2110
c47a9ed1
CY
2111 /* It is rare for combine_after_change_buffer to be invalid, but
2112 possible. It can happen when combine-after-change-calls is
2113 non-nil, and insertion calls a file handler (e.g. through
2114 lock_file) which scribbles into a temp file -- cyd */
2115 if (!BUFFERP (combine_after_change_buffer)
4b4deea2 2116 || NILP (BVAR (XBUFFER (combine_after_change_buffer), name)))
c47a9ed1
CY
2117 {
2118 combine_after_change_list = Qnil;
2119 return Qnil;
2120 }
2121
fb2e7d14
RS
2122 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
2123
2124 Fset_buffer (combine_after_change_buffer);
2125
2126 /* # chars unchanged at beginning of buffer. */
2127 beg = Z - BEG;
2128 /* # chars unchanged at end of buffer. */
2129 end = beg;
2130 /* Total amount of insertion (negative for deletion). */
2131 change = 0;
2132
2133 /* Scan the various individual changes,
2134 accumulating the range info in BEG, END and CHANGE. */
2135 for (tail = combine_after_change_list; CONSP (tail);
03699b14 2136 tail = XCDR (tail))
fb2e7d14 2137 {
e688a080 2138 Lisp_Object elt;
d311d28c 2139 ptrdiff_t thisbeg, thisend, thischange;
fb2e7d14
RS
2140
2141 /* Extract the info from the next element. */
03699b14 2142 elt = XCAR (tail);
fb2e7d14
RS
2143 if (! CONSP (elt))
2144 continue;
03699b14 2145 thisbeg = XINT (XCAR (elt));
fb2e7d14 2146
03699b14 2147 elt = XCDR (elt);
fb2e7d14
RS
2148 if (! CONSP (elt))
2149 continue;
03699b14 2150 thisend = XINT (XCAR (elt));
fb2e7d14 2151
03699b14 2152 elt = XCDR (elt);
fb2e7d14
RS
2153 if (! CONSP (elt))
2154 continue;
03699b14 2155 thischange = XINT (XCAR (elt));
fb2e7d14
RS
2156
2157 /* Merge this range into the accumulated range. */
2158 change += thischange;
2159 if (thisbeg < beg)
2160 beg = thisbeg;
2161 if (thisend < end)
2162 end = thisend;
2163 }
2164
2165 /* Get the current start and end positions of the range
2166 that was changed. */
2167 begpos = BEG + beg;
2168 endpos = Z - end;
177c0ea7 2169
fb2e7d14
RS
2170 /* We are about to handle these, so discard them. */
2171 combine_after_change_list = Qnil;
2172
2173 /* Now run the after-change functions for real.
2174 Turn off the flag that defers them. */
2175 record_unwind_protect (Fcombine_after_change_execute_1,
2176 Vcombine_after_change_calls);
2177 signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
0ef71121 2178 update_compositions (begpos, endpos, CHECK_ALL);
fb2e7d14 2179
101e446f 2180 return unbind_to (count, Qnil);
fb2e7d14
RS
2181}
2182\f
dfcf069d 2183void
971de7fb 2184syms_of_insdel (void)
fb2e7d14
RS
2185{
2186 staticpro (&combine_after_change_list);
bf0bf758 2187 staticpro (&combine_after_change_buffer);
fb2e7d14 2188 combine_after_change_list = Qnil;
101e446f 2189 combine_after_change_buffer = Qnil;
fb2e7d14 2190
29208e82 2191 DEFVAR_LISP ("combine-after-change-calls", Vcombine_after_change_calls,
335c5470 2192 doc: /* Used internally by the `combine-after-change-calls' macro. */);
fb2e7d14
RS
2193 Vcombine_after_change_calls = Qnil;
2194
29208e82 2195 DEFVAR_BOOL ("inhibit-modification-hooks", inhibit_modification_hooks,
335c5470
PJ
2196 doc: /* Non-nil means don't run any of the hooks that respond to buffer changes.
2197This affects `before-change-functions' and `after-change-functions',
2198as well as hooks attached to text properties and overlays. */);
7baf49cf 2199 inhibit_modification_hooks = 0;
cd3520a4 2200 DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks");
7baf49cf 2201
fb2e7d14
RS
2202 defsubr (&Scombine_after_change_execute);
2203}