Initial revision
[bpt/emacs.git] / src / insdel.c
1 /* Buffer insertion/deletion and gap motion for GNU Emacs.
2 Copyright (C) 1985, 1986 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21 #include "config.h"
22 #include "lisp.h"
23 #include "buffer.h"
24 #include "window.h"
25
26 /* Nonzero means don't allow protected fields to be modified. */
27
28 extern int check_protected_fields;
29
30 /* Move gap to position `pos'.
31 Note that this can quit! */
32
33 move_gap (pos)
34 int pos;
35 {
36 if (pos < GPT)
37 gap_left (pos, 0);
38 else if (pos > GPT)
39 gap_right (pos);
40 }
41
42 /* Move the gap to POS, which is less than the current GPT.
43 If NEWGAP is nonzero, then don't update beg_unchanged and end_unchanged. */
44
45 gap_left (pos, newgap)
46 register int pos;
47 int newgap;
48 {
49 register unsigned char *to, *from;
50 register int i;
51 int new_s1;
52
53 pos--;
54
55 if (!newgap)
56 {
57 if (unchanged_modified == MODIFF)
58 {
59 beg_unchanged = pos;
60 end_unchanged = Z - pos - 1;
61 }
62 else
63 {
64 if (Z - GPT < end_unchanged)
65 end_unchanged = Z - GPT;
66 if (pos < beg_unchanged)
67 beg_unchanged = pos;
68 }
69 }
70
71 i = GPT;
72 to = GAP_END_ADDR;
73 from = GPT_ADDR;
74 new_s1 = GPT - BEG;
75
76 /* Now copy the characters. To move the gap down,
77 copy characters up. */
78
79 while (1)
80 {
81 /* I gets number of characters left to copy. */
82 i = new_s1 - pos;
83 if (i == 0)
84 break;
85 /* If a quit is requested, stop copying now.
86 Change POS to be where we have actually moved the gap to. */
87 if (QUITP)
88 {
89 pos = new_s1;
90 break;
91 }
92 /* Move at most 32000 chars before checking again for a quit. */
93 if (i > 32000)
94 i = 32000;
95 #ifdef GAP_USE_BCOPY
96 if (i >= 128
97 /* bcopy is safe if the two areas of memory do not overlap
98 or on systems where bcopy is always safe for moving upward. */
99 && (BCOPY_UPWARD_SAFE
100 || to - from >= 128))
101 {
102 /* If overlap is not safe, avoid it by not moving too many
103 characters at once. */
104 if (!BCOPY_UPWARD_SAFE && i > to - from)
105 i = to - from;
106 new_s1 -= i;
107 from -= i, to -= i;
108 bcopy (from, to, i);
109 }
110 else
111 #endif
112 {
113 new_s1 -= i;
114 while (--i >= 0)
115 *--to = *--from;
116 }
117 }
118
119 /* Adjust markers, and buffer data structure, to put the gap at POS.
120 POS is where the loop above stopped, which may be what was specified
121 or may be where a quit was detected. */
122 adjust_markers (pos + 1, GPT, GAP_SIZE);
123 GPT = pos + 1;
124 QUIT;
125 }
126
127 gap_right (pos)
128 register int pos;
129 {
130 register unsigned char *to, *from;
131 register int i;
132 int new_s1;
133
134 pos--;
135
136 if (unchanged_modified == MODIFF)
137 {
138 beg_unchanged = pos;
139 end_unchanged = Z - pos - 1;
140 }
141 else
142 {
143 if (Z - pos - 1 < end_unchanged)
144 end_unchanged = Z - pos - 1;
145 if (GPT - BEG < beg_unchanged)
146 beg_unchanged = GPT - BEG;
147 }
148
149 i = GPT;
150 from = GAP_END_ADDR;
151 to = GPT_ADDR;
152 new_s1 = GPT - 1;
153
154 /* Now copy the characters. To move the gap up,
155 copy characters down. */
156
157 while (1)
158 {
159 /* I gets number of characters left to copy. */
160 i = pos - new_s1;
161 if (i == 0)
162 break;
163 /* If a quit is requested, stop copying now.
164 Change POS to be where we have actually moved the gap to. */
165 if (QUITP)
166 {
167 pos = new_s1;
168 break;
169 }
170 /* Move at most 32000 chars before checking again for a quit. */
171 if (i > 32000)
172 i = 32000;
173 #ifdef GAP_USE_BCOPY
174 if (i >= 128
175 /* bcopy is safe if the two areas of memory do not overlap
176 or on systems where bcopy is always safe for moving downward. */
177 && (BCOPY_DOWNWARD_SAFE
178 || from - to >= 128))
179 {
180 /* If overlap is not safe, avoid it by not moving too many
181 characters at once. */
182 if (!BCOPY_DOWNWARD_SAFE && i > from - to)
183 i = from - to;
184 new_s1 += i;
185 bcopy (from, to, i);
186 from += i, to += i;
187 }
188 else
189 #endif
190 {
191 new_s1 += i;
192 while (--i >= 0)
193 *to++ = *from++;
194 }
195 }
196
197 adjust_markers (GPT + GAP_SIZE, pos + 1 + GAP_SIZE, - GAP_SIZE);
198 GPT = pos + 1;
199 QUIT;
200 }
201
202 /* Add `amount' to the position of every marker in the current buffer
203 whose current position is between `from' (exclusive) and `to' (inclusive).
204 Also, any markers past the outside of that interval, in the direction
205 of adjustment, are first moved back to the near end of the interval
206 and then adjusted by `amount'. */
207
208 adjust_markers (from, to, amount)
209 register int from, to, amount;
210 {
211 Lisp_Object marker;
212 register struct Lisp_Marker *m;
213 register int mpos;
214
215 marker = current_buffer->markers;
216
217 while (!NULL (marker))
218 {
219 m = XMARKER (marker);
220 mpos = m->bufpos;
221 if (amount > 0)
222 {
223 if (mpos > to && mpos < to + amount)
224 mpos = to + amount;
225 }
226 else
227 {
228 if (mpos > from + amount && mpos <= from)
229 mpos = from + amount;
230 }
231 if (mpos > from && mpos <= to)
232 mpos += amount;
233 m->bufpos = mpos;
234 marker = m->chain;
235 }
236 }
237 \f
238 /* Make the gap INCREMENT characters longer. */
239
240 make_gap (increment)
241 int increment;
242 {
243 unsigned char *result;
244 Lisp_Object tem;
245 int real_gap_loc;
246 int old_gap_size;
247
248 /* If we have to get more space, get enough to last a while. */
249 increment += 2000;
250
251 result = BUFFER_REALLOC (BEG_ADDR, (Z - BEG + GAP_SIZE + increment));
252 if (result == 0)
253 memory_full ();
254 BEG_ADDR = result;
255
256 /* Prevent quitting in move_gap. */
257 tem = Vinhibit_quit;
258 Vinhibit_quit = Qt;
259
260 real_gap_loc = GPT;
261 old_gap_size = GAP_SIZE;
262
263 /* Call the newly allocated space a gap at the end of the whole space. */
264 GPT = Z + GAP_SIZE;
265 GAP_SIZE = increment;
266
267 /* Move the new gap down to be consecutive with the end of the old one.
268 This adjusts the markers properly too. */
269 gap_left (real_gap_loc + old_gap_size, 1);
270
271 /* Now combine the two into one large gap. */
272 GAP_SIZE += old_gap_size;
273 GPT = real_gap_loc;
274
275 Vinhibit_quit = tem;
276 }
277 \f
278 /* Insert a string of specified length before point.
279 DO NOT use this for the contents of a Lisp string!
280 prepare_to_modify_buffer could relocate the string. */
281
282 insert (string, length)
283 register unsigned char *string;
284 register length;
285 {
286 register Lisp_Object temp;
287
288 if (length < 1)
289 return;
290
291 /* Make sure point-max won't overflow after this insertion. */
292 XSET (temp, Lisp_Int, length + Z);
293 if (length + Z != XINT (temp))
294 error ("maximum buffer size exceeded");
295
296 prepare_to_modify_buffer (point, point);
297
298 if (point != GPT)
299 move_gap (point);
300 if (GAP_SIZE < length)
301 make_gap (length - GAP_SIZE);
302
303 record_insert (point, length);
304 MODIFF++;
305
306 bcopy (string, GPT_ADDR, length);
307
308 GAP_SIZE -= length;
309 GPT += length;
310 ZV += length;
311 Z += length;
312 SET_PT (point + length);
313
314 signal_after_change (point-length, 0, length);
315 }
316
317 /* Function to insert part of the text of a string (STRING)
318 consisting of LENGTH characters at position POS.
319 It does not work to use `insert' for this. */
320
321 insert_from_string (string, pos, length)
322 Lisp_Object string;
323 register int pos, length;
324 {
325 register Lisp_Object temp;
326 struct gcpro gcpro1;
327
328 if (length < 1)
329 return;
330
331 /* Make sure point-max won't overflow after this insertion. */
332 XSET (temp, Lisp_Int, length + Z);
333 if (length + Z != XINT (temp))
334 error ("maximum buffer size exceeded");
335
336 GCPRO1 (string);
337 prepare_to_modify_buffer (point, point);
338
339 if (point != GPT)
340 move_gap (point);
341 if (GAP_SIZE < length)
342 make_gap (length - GAP_SIZE);
343
344 record_insert (point, length);
345 MODIFF++;
346 UNGCPRO;
347
348 bcopy (XSTRING (string)->data, GPT_ADDR, length);
349
350 GAP_SIZE -= length;
351 GPT += length;
352 ZV += length;
353 Z += length;
354 point += length;
355
356 signal_after_change (point-length, 0, length);
357 }
358
359 /* Insert the character C before point */
360
361 void
362 insert_char (c)
363 unsigned char c;
364 {
365 insert (&c, 1);
366 }
367
368 /* Insert the null-terminated string S before point */
369
370 void
371 insert_string (s)
372 char *s;
373 {
374 insert (s, strlen (s));
375 }
376
377 /* Like `insert' except that all markers pointing at the place where
378 the insertion happens are adjusted to point after it.
379 Don't use this function to insert part of a Lisp string,
380 since gc could happen and relocate it. */
381
382 insert_before_markers (string, length)
383 unsigned char *string;
384 register int length;
385 {
386 register int opoint = point;
387 insert (string, length);
388 adjust_markers (opoint - 1, opoint, length);
389 }
390
391 /* Insert part of a Lisp string, relocating markers after. */
392
393 insert_from_string_before_markers (string, pos, length)
394 Lisp_Object string;
395 register int pos, length;
396 {
397 register int opoint = point;
398 insert_from_string (string, pos, length);
399 adjust_markers (opoint - 1, opoint, length);
400 }
401 \f
402 /* Delete characters in current buffer
403 from FROM up to (but not including) TO. */
404
405 del_range (from, to)
406 register int from, to;
407 {
408 register int numdel;
409
410 /* Make args be valid */
411 if (from < BEGV)
412 from = BEGV;
413 if (to > ZV)
414 to = ZV;
415
416 if ((numdel = to - from) <= 0)
417 return;
418
419 /* Make sure the gap is somewhere in or next to what we are deleting. */
420 if (from > GPT)
421 gap_right (from);
422 if (to < GPT)
423 gap_left (to, 0);
424
425 prepare_to_modify_buffer (from, to);
426
427 /* Relocate point as if it were a marker. */
428 if (from < point)
429 {
430 if (point < to)
431 SET_PT (from);
432 else
433 SET_PT (point - numdel);
434 }
435
436 record_delete (from, numdel);
437 MODIFF++;
438
439 /* Relocate all markers pointing into the new, larger gap
440 to point at the end of the text before the gap. */
441 adjust_markers (to + GAP_SIZE, to + GAP_SIZE, - numdel - GAP_SIZE);
442
443 GAP_SIZE += numdel;
444 ZV -= numdel;
445 Z -= numdel;
446 GPT = from;
447
448 if (GPT - BEG < beg_unchanged)
449 beg_unchanged = GPT - BEG;
450 if (Z - GPT < end_unchanged)
451 end_unchanged = Z - GPT;
452
453 signal_after_change (from, numdel, 0);
454 }
455 \f
456 modify_region (start, end)
457 int start, end;
458 {
459 prepare_to_modify_buffer (start, end);
460
461 if (start - 1 < beg_unchanged || unchanged_modified == MODIFF)
462 beg_unchanged = start - 1;
463 if (Z - end < end_unchanged
464 || unchanged_modified == MODIFF)
465 end_unchanged = Z - end;
466 MODIFF++;
467 }
468
469 /* Check that it is okay to modify the buffer between START and END.
470 Run the before-change-function, if any. */
471
472 prepare_to_modify_buffer (start, end)
473 Lisp_Object start, end;
474 {
475 if (!NULL (current_buffer->read_only))
476 Fbarf_if_buffer_read_only ();
477
478 if (check_protected_fields)
479 Fregion_fields (start, end, Qnil, Qt);
480
481 #ifdef CLASH_DETECTION
482 if (!NULL (current_buffer->filename)
483 && current_buffer->save_modified >= MODIFF)
484 lock_file (current_buffer->filename);
485 #else
486 /* At least warn if this file has changed on disk since it was visited. */
487 if (!NULL (current_buffer->filename)
488 && current_buffer->save_modified >= MODIFF
489 && NULL (Fverify_visited_file_modtime (Fcurrent_buffer ()))
490 && !NULL (Ffile_exists_p (current_buffer->filename)))
491 call1 (intern ("ask-user-about-supersession-threat"),
492 current_buffer->filename);
493 #endif /* not CLASH_DETECTION */
494
495 signal_before_change (start, end);
496 }
497 \f
498 static Lisp_Object
499 before_change_function_restore (value)
500 Lisp_Object value;
501 {
502 Vbefore_change_function = value;
503 }
504
505 static Lisp_Object
506 after_change_function_restore (value)
507 Lisp_Object value;
508 {
509 Vafter_change_function = value;
510 }
511
512 /* Signal a change to the buffer immediatly before it happens.
513 START and END are the bounds of the text to be changed,
514 as Lisp objects. */
515
516 signal_before_change (start, end)
517 Lisp_Object start, end;
518 {
519 /* If buffer is unmodified, run a special hook for that case. */
520 if (current_buffer->save_modified >= MODIFF
521 && !NULL (Vfirst_change_function))
522 {
523 call0 (Vfirst_change_function);
524 }
525 /* Now in any case run the before-change-function if any. */
526 if (!NULL (Vbefore_change_function))
527 {
528 int count = specpdl_ptr - specpdl;
529 Lisp_Object function;
530
531 function = Vbefore_change_function;
532 record_unwind_protect (after_change_function_restore,
533 Vafter_change_function);
534 record_unwind_protect (before_change_function_restore,
535 Vbefore_change_function);
536 Vafter_change_function = Qnil;
537 Vbefore_change_function = Qnil;
538
539 call2 (function, start, end);
540 unbind_to (count, Qnil);
541 }
542 }
543
544 /* Signal a change immediatly after it happens.
545 POS is the address of the start of the changed text.
546 LENDEL is the number of characters of the text before the change.
547 (Not the whole buffer; just the part that was changed.)
548 LENINS is the number of characters in the changed text. */
549
550 signal_after_change (pos, lendel, lenins)
551 int pos, lendel, lenins;
552 {
553 if (!NULL (Vafter_change_function))
554 {
555 int count = specpdl_ptr - specpdl;
556 Lisp_Object function;
557 function = Vafter_change_function;
558
559 record_unwind_protect (after_change_function_restore,
560 Vafter_change_function);
561 record_unwind_protect (before_change_function_restore,
562 Vbefore_change_function);
563 Vafter_change_function = Qnil;
564 Vbefore_change_function = Qnil;
565
566 call3 (function, make_number (pos), make_number (pos + lenins),
567 make_number (lendel));
568 unbind_to (count, Qnil);
569 }
570 }