157
|
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
|
|
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
|
|
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
|
251
|
317 /* Function to insert part of the text of a string (STRING) consisting
|
|
318 of LENGTH characters at position POS.
|
|
319 It does not work to use `insert' for this, becase a GC could happen
|
|
320 before we bcopy the stuff into the buffer, and relocate the string
|
|
321 without insert noticing. */
|
157
|
322 insert_from_string (string, pos, length)
|
|
323 Lisp_Object string;
|
|
324 register int pos, length;
|
|
325 {
|
|
326 register Lisp_Object temp;
|
|
327 struct gcpro gcpro1;
|
|
328
|
|
329 if (length < 1)
|
|
330 return;
|
|
331
|
|
332 /* Make sure point-max won't overflow after this insertion. */
|
|
333 XSET (temp, Lisp_Int, length + Z);
|
|
334 if (length + Z != XINT (temp))
|
|
335 error ("maximum buffer size exceeded");
|
|
336
|
|
337 GCPRO1 (string);
|
|
338 prepare_to_modify_buffer (point, point);
|
|
339
|
|
340 if (point != GPT)
|
|
341 move_gap (point);
|
|
342 if (GAP_SIZE < length)
|
|
343 make_gap (length - GAP_SIZE);
|
|
344
|
|
345 record_insert (point, length);
|
|
346 MODIFF++;
|
|
347 UNGCPRO;
|
|
348
|
|
349 bcopy (XSTRING (string)->data, GPT_ADDR, length);
|
|
350
|
|
351 GAP_SIZE -= length;
|
|
352 GPT += length;
|
|
353 ZV += length;
|
|
354 Z += length;
|
|
355 point += length;
|
|
356
|
|
357 signal_after_change (point-length, 0, length);
|
|
358 }
|
|
359
|
|
360 /* Insert the character C before point */
|
|
361
|
|
362 void
|
|
363 insert_char (c)
|
|
364 unsigned char c;
|
|
365 {
|
|
366 insert (&c, 1);
|
|
367 }
|
|
368
|
|
369 /* Insert the null-terminated string S before point */
|
|
370
|
|
371 void
|
|
372 insert_string (s)
|
|
373 char *s;
|
|
374 {
|
|
375 insert (s, strlen (s));
|
|
376 }
|
|
377
|
|
378 /* Like `insert' except that all markers pointing at the place where
|
|
379 the insertion happens are adjusted to point after it.
|
|
380 Don't use this function to insert part of a Lisp string,
|
|
381 since gc could happen and relocate it. */
|
|
382
|
|
383 insert_before_markers (string, length)
|
|
384 unsigned char *string;
|
|
385 register int length;
|
|
386 {
|
|
387 register int opoint = point;
|
|
388 insert (string, length);
|
|
389 adjust_markers (opoint - 1, opoint, length);
|
|
390 }
|
|
391
|
|
392 /* Insert part of a Lisp string, relocating markers after. */
|
|
393
|
|
394 insert_from_string_before_markers (string, pos, length)
|
|
395 Lisp_Object string;
|
|
396 register int pos, length;
|
|
397 {
|
|
398 register int opoint = point;
|
|
399 insert_from_string (string, pos, length);
|
|
400 adjust_markers (opoint - 1, opoint, length);
|
|
401 }
|
|
402
|
|
403 /* Delete characters in current buffer
|
|
404 from FROM up to (but not including) TO. */
|
|
405
|
|
406 del_range (from, to)
|
|
407 register int from, to;
|
|
408 {
|
|
409 register int numdel;
|
|
410
|
|
411 /* Make args be valid */
|
|
412 if (from < BEGV)
|
|
413 from = BEGV;
|
|
414 if (to > ZV)
|
|
415 to = ZV;
|
|
416
|
|
417 if ((numdel = to - from) <= 0)
|
|
418 return;
|
|
419
|
|
420 /* Make sure the gap is somewhere in or next to what we are deleting. */
|
|
421 if (from > GPT)
|
|
422 gap_right (from);
|
|
423 if (to < GPT)
|
|
424 gap_left (to, 0);
|
|
425
|
|
426 prepare_to_modify_buffer (from, to);
|
|
427
|
|
428 /* Relocate point as if it were a marker. */
|
|
429 if (from < point)
|
|
430 {
|
|
431 if (point < to)
|
|
432 SET_PT (from);
|
|
433 else
|
|
434 SET_PT (point - numdel);
|
|
435 }
|
|
436
|
|
437 record_delete (from, numdel);
|
|
438 MODIFF++;
|
|
439
|
|
440 /* Relocate all markers pointing into the new, larger gap
|
|
441 to point at the end of the text before the gap. */
|
|
442 adjust_markers (to + GAP_SIZE, to + GAP_SIZE, - numdel - GAP_SIZE);
|
|
443
|
|
444 GAP_SIZE += numdel;
|
|
445 ZV -= numdel;
|
|
446 Z -= numdel;
|
|
447 GPT = from;
|
|
448
|
|
449 if (GPT - BEG < beg_unchanged)
|
|
450 beg_unchanged = GPT - BEG;
|
|
451 if (Z - GPT < end_unchanged)
|
|
452 end_unchanged = Z - GPT;
|
|
453
|
|
454 signal_after_change (from, numdel, 0);
|
|
455 }
|
|
456
|
|
457 modify_region (start, end)
|
|
458 int start, end;
|
|
459 {
|
|
460 prepare_to_modify_buffer (start, end);
|
|
461
|
|
462 if (start - 1 < beg_unchanged || unchanged_modified == MODIFF)
|
|
463 beg_unchanged = start - 1;
|
|
464 if (Z - end < end_unchanged
|
|
465 || unchanged_modified == MODIFF)
|
|
466 end_unchanged = Z - end;
|
|
467 MODIFF++;
|
|
468 }
|
|
469
|
|
470 /* Check that it is okay to modify the buffer between START and END.
|
|
471 Run the before-change-function, if any. */
|
|
472
|
|
473 prepare_to_modify_buffer (start, end)
|
|
474 Lisp_Object start, end;
|
|
475 {
|
|
476 if (!NULL (current_buffer->read_only))
|
|
477 Fbarf_if_buffer_read_only ();
|
|
478
|
|
479 if (check_protected_fields)
|
|
480 Fregion_fields (start, end, Qnil, Qt);
|
|
481
|
|
482 #ifdef CLASH_DETECTION
|
|
483 if (!NULL (current_buffer->filename)
|
|
484 && current_buffer->save_modified >= MODIFF)
|
|
485 lock_file (current_buffer->filename);
|
|
486 #else
|
|
487 /* At least warn if this file has changed on disk since it was visited. */
|
|
488 if (!NULL (current_buffer->filename)
|
|
489 && current_buffer->save_modified >= MODIFF
|
|
490 && NULL (Fverify_visited_file_modtime (Fcurrent_buffer ()))
|
|
491 && !NULL (Ffile_exists_p (current_buffer->filename)))
|
|
492 call1 (intern ("ask-user-about-supersession-threat"),
|
|
493 current_buffer->filename);
|
|
494 #endif /* not CLASH_DETECTION */
|
|
495
|
|
496 signal_before_change (start, end);
|
|
497 }
|
|
498
|
|
499 static Lisp_Object
|
|
500 before_change_function_restore (value)
|
|
501 Lisp_Object value;
|
|
502 {
|
|
503 Vbefore_change_function = value;
|
|
504 }
|
|
505
|
|
506 static Lisp_Object
|
|
507 after_change_function_restore (value)
|
|
508 Lisp_Object value;
|
|
509 {
|
|
510 Vafter_change_function = value;
|
|
511 }
|
|
512
|
|
513 /* Signal a change to the buffer immediatly before it happens.
|
|
514 START and END are the bounds of the text to be changed,
|
|
515 as Lisp objects. */
|
|
516
|
|
517 signal_before_change (start, end)
|
|
518 Lisp_Object start, end;
|
|
519 {
|
|
520 /* If buffer is unmodified, run a special hook for that case. */
|
|
521 if (current_buffer->save_modified >= MODIFF
|
|
522 && !NULL (Vfirst_change_function))
|
|
523 {
|
|
524 call0 (Vfirst_change_function);
|
|
525 }
|
|
526 /* Now in any case run the before-change-function if any. */
|
|
527 if (!NULL (Vbefore_change_function))
|
|
528 {
|
|
529 int count = specpdl_ptr - specpdl;
|
|
530 Lisp_Object function;
|
|
531
|
|
532 function = Vbefore_change_function;
|
|
533 record_unwind_protect (after_change_function_restore,
|
|
534 Vafter_change_function);
|
|
535 record_unwind_protect (before_change_function_restore,
|
|
536 Vbefore_change_function);
|
|
537 Vafter_change_function = Qnil;
|
|
538 Vbefore_change_function = Qnil;
|
|
539
|
|
540 call2 (function, start, end);
|
|
541 unbind_to (count, Qnil);
|
|
542 }
|
|
543 }
|
|
544
|
|
545 /* Signal a change immediatly after it happens.
|
|
546 POS is the address of the start of the changed text.
|
|
547 LENDEL is the number of characters of the text before the change.
|
|
548 (Not the whole buffer; just the part that was changed.)
|
|
549 LENINS is the number of characters in the changed text. */
|
|
550
|
|
551 signal_after_change (pos, lendel, lenins)
|
|
552 int pos, lendel, lenins;
|
|
553 {
|
|
554 if (!NULL (Vafter_change_function))
|
|
555 {
|
|
556 int count = specpdl_ptr - specpdl;
|
|
557 Lisp_Object function;
|
|
558 function = Vafter_change_function;
|
|
559
|
|
560 record_unwind_protect (after_change_function_restore,
|
|
561 Vafter_change_function);
|
|
562 record_unwind_protect (before_change_function_restore,
|
|
563 Vbefore_change_function);
|
|
564 Vafter_change_function = Qnil;
|
|
565 Vbefore_change_function = Qnil;
|
|
566
|
|
567 call3 (function, make_number (pos), make_number (pos + lenins),
|
|
568 make_number (lendel));
|
|
569 unbind_to (count, Qnil);
|
|
570 }
|
|
571 }
|