X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/adde48587ad06ed296b5d71431fd5d361027d46e..9a5114ac7e384d28a13c99725380b6024abde5cf:/src/insdel.c diff --git a/src/insdel.c b/src/insdel.c index 449bfc58b3..12b7eedb58 100644 --- a/src/insdel.c +++ b/src/insdel.c @@ -5,7 +5,7 @@ This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) +the Free Software Foundation; either version 2, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, @@ -15,16 +15,20 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ #include #include "lisp.h" #include "intervals.h" #include "buffer.h" +#include "charset.h" #include "window.h" #include "blockinput.h" +#define min(x, y) ((x) < (y) ? (x) : (y)) + static void insert_from_string_1 (); static void insert_from_buffer_1 (); static void gap_left (); @@ -32,6 +36,27 @@ static void gap_right (); static void adjust_markers (); static void adjust_point (); +Lisp_Object Fcombine_after_change_execute (); + +/* Non-nil means don't call the after-change-functions right away, + just record an element in Vcombine_after_change_calls_list. */ +Lisp_Object Vcombine_after_change_calls; + +/* List of elements of the form (BEG-UNCHANGED END-UNCHANGED CHANGE-AMOUNT) + describing changes which happened while combine_after_change_calls + was nonzero. We use this to decide how to call them + once the deferral ends. + + In each element. + BEG-UNCHANGED is the number of chars before the changed range. + END-UNCHANGED is the number of chars after the changed range, + and CHANGE-AMOUNT is the number of characters inserted by the change + (negative for a deletion). */ +Lisp_Object combine_after_change_list; + +/* Buffer which combine_after_change_list is about. */ +Lisp_Object combine_after_change_buffer; + /* Move gap to position `pos'. Note that this can quit! */ @@ -61,7 +86,8 @@ gap_left (pos, newgap) if (!newgap) { - if (unchanged_modified == MODIFF) + if (unchanged_modified == MODIFF + && overlay_unchanged_modified == OVERLAY_MODIFF) { beg_unchanged = pos; end_unchanged = Z - pos - 1; @@ -128,6 +154,7 @@ gap_left (pos, newgap) or may be where a quit was detected. */ adjust_markers (pos + 1, GPT, GAP_SIZE); GPT = pos + 1; + if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */ QUIT; } @@ -141,7 +168,9 @@ gap_right (pos) pos--; - if (unchanged_modified == MODIFF) + if (unchanged_modified == MODIFF + && overlay_unchanged_modified == OVERLAY_MODIFF) + { beg_unchanged = pos; end_unchanged = Z - pos - 1; @@ -204,14 +233,20 @@ gap_right (pos) adjust_markers (GPT + GAP_SIZE, pos + 1 + GAP_SIZE, - GAP_SIZE); GPT = pos + 1; + if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */ QUIT; } -/* Add `amount' to the position of every marker in the current buffer - whose current position is between `from' (exclusive) and `to' (inclusive). +/* Add AMOUNT to the position of every marker in the current buffer + whose current position is between FROM (exclusive) and TO (inclusive). + Also, any markers past the outside of that interval, in the direction of adjustment, are first moved back to the near end of the interval - and then adjusted by `amount'. */ + and then adjusted by AMOUNT. + + When the latter adjustment is done, if AMOUNT is negative, + we record the adjustment for undo. (This case happens only for + deletion.) */ static void adjust_markers (from, to, amount) @@ -234,8 +269,30 @@ adjust_markers (from, to, amount) } else { + /* Here's the case where a marker is inside text being deleted. + AMOUNT can be negative for gap motion, too, + but then this range contains no markers. */ if (mpos > from + amount && mpos <= from) - mpos = from + amount; + { + int before = mpos; + int after = from + amount; + + mpos = after; + + /* Compute the before and after positions + as buffer positions. */ + if (before > GPT + GAP_SIZE) + before -= GAP_SIZE; + else if (before > GPT) + before = GPT; + + if (after > GPT + GAP_SIZE) + after -= GAP_SIZE; + else if (after > GPT) + after = GPT; + + record_marker_adjustment (marker, after - before); + } } if (mpos > from && mpos <= to) mpos += amount; @@ -244,15 +301,44 @@ adjust_markers (from, to, amount) } } +/* Adjust markers whose insertion-type is t + for an insertion of AMOUNT characters at POS. */ + +static void +adjust_markers_for_insert (pos, amount) + register int pos, amount; +{ + Lisp_Object marker; + int adjusted = 0; + + marker = BUF_MARKERS (current_buffer); + + while (!NILP (marker)) + { + register struct Lisp_Marker *m = XMARKER (marker); + if (m->insertion_type && m->bufpos == pos) + { + m->bufpos += amount; + adjusted = 1; + } + marker = m->chain; + } + if (adjusted) + /* Adjusting only markers whose insertion-type is t may result in + disordered overlays in the slot `overlays_before'. */ + fix_overlays_before (current_buffer, pos, pos + amount); +} + /* Add the specified amount to point. This is used only when the value of point changes due to an insert or delete; it does not represent a conceptual change in point as a marker. In particular, point is not crossing any interval boundaries, so there's no need to use the usual SET_PT macro. In fact it would be incorrect to do so, because - either the old or the new value of point is out of synch with the + either the old or the new value of point is out of sync with the current set of intervals. */ static void adjust_point (amount) + int amount; { BUF_PT (current_buffer) += amount; } @@ -271,8 +357,17 @@ make_gap (increment) /* If we have to get more space, get enough to last a while. */ increment += 2000; + /* Don't allow a buffer size that won't fit in an int + even if it will fit in a Lisp integer. + That won't work because so many places use `int'. */ + + if (Z - BEG + GAP_SIZE + increment + >= ((unsigned) 1 << (min (BITS_PER_INT, VALBITS) - 1))) + error ("Buffer exceeds maximum size"); + BLOCK_INPUT; - result = BUFFER_REALLOC (BEG_ADDR, (Z - BEG + GAP_SIZE + increment)); + /* We allocate extra 1-byte `\0' at the tail for anchoring a search. */ + result = BUFFER_REALLOC (BEG_ADDR, (Z - BEG + GAP_SIZE + increment + 1)); if (result == 0) { @@ -303,6 +398,9 @@ make_gap (increment) GAP_SIZE += old_gap_size; GPT = real_gap_loc; + /* Put an anchor. */ + *(Z_ADDR) = 0; + Vinhibit_quit = tem; } @@ -342,11 +440,6 @@ insert_1 (string, length, inherit, prepare) { register Lisp_Object temp; - /* Make sure point-max won't overflow after this insertion. */ - XSETINT (temp, length + Z); - if (length + Z != XINT (temp)) - error ("maximum buffer size exceeded"); - if (prepare) prepare_to_modify_buffer (PT, PT); @@ -370,7 +463,9 @@ insert_1 (string, length, inherit, prepare) GPT += length; ZV += length; Z += length; + if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */ adjust_overlays_for_insert (PT, length); + adjust_markers_for_insert (PT, length); adjust_point (length); #ifdef USE_TEXT_PROPERTIES @@ -437,7 +532,9 @@ insert_from_string_1 (string, pos, length, inherit) GPT += length; ZV += length; Z += length; + if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */ adjust_overlays_for_insert (PT, length); + adjust_markers_for_insert (PT, length); /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */ graft_intervals_into_buffer (XSTRING (string)->intervals, PT, length, @@ -512,7 +609,9 @@ insert_from_buffer_1 (buf, pos, length, inherit) GPT += length; ZV += length; Z += length; + if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */ adjust_overlays_for_insert (PT, length); + adjust_markers_for_insert (PT, length); adjust_point (length); /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */ @@ -525,9 +624,12 @@ insert_from_buffer_1 (buf, pos, length, inherit) void insert_char (c) - unsigned char c; + int c; { - insert (&c, 1); + unsigned char workbuf[4], *str; + int len = CHAR_STRING (c, workbuf, str); + + insert (str, len); } /* Insert the null-terminated string S before point */ @@ -625,6 +727,12 @@ del_range_1 (from, to, prepare) if (prepare) prepare_to_modify_buffer (from, to); + /* Relocate all markers pointing into the new, larger gap + to point at the end of the text before the gap. + This has to be done before recording the deletion, + so undo handles this after reinserting the text. */ + adjust_markers (to + GAP_SIZE, to + GAP_SIZE, - numdel - GAP_SIZE); + record_delete (from, numdel); MODIFF++; @@ -635,18 +743,15 @@ del_range_1 (from, to, prepare) /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */ offset_intervals (current_buffer, from, - numdel); - /* Relocate all markers pointing into the new, larger gap - to point at the end of the text before the gap. */ - adjust_markers (to + GAP_SIZE, to + GAP_SIZE, - numdel - GAP_SIZE); - /* Adjust the overlay center as needed. This must be done after - adjusting the markers that bound the overlays. */ + adjusting the markers that bound the overlays. */ adjust_overlays_for_delete (from, numdel); GAP_SIZE += numdel; ZV -= numdel; Z -= numdel; GPT = from; + *(GPT_ADDR) = 0; /* Put an anchor. */ if (GPT - BEG < beg_unchanged) beg_unchanged = GPT - BEG; @@ -673,10 +778,13 @@ modify_region (buffer, start, end) prepare_to_modify_buffer (start, end); - if (start - 1 < beg_unchanged || unchanged_modified == MODIFF) + if (start - 1 < beg_unchanged + || (unchanged_modified == MODIFF + && overlay_unchanged_modified == OVERLAY_MODIFF)) beg_unchanged = start - 1; if (Z - end < end_unchanged - || unchanged_modified == MODIFF) + || (unchanged_modified == MODIFF + && overlay_unchanged_modified == OVERLAY_MODIFF)) end_unchanged = Z - end; if (MODIFF <= SAVE_MODIFF) @@ -696,7 +804,7 @@ modify_region (buffer, start, end) void prepare_to_modify_buffer (start, end) - Lisp_Object start, end; + int start, end; { if (!NILP (current_buffer->read_only)) Fbarf_if_buffer_read_only (); @@ -706,9 +814,11 @@ prepare_to_modify_buffer (start, end) verify_interval_modification (current_buffer, start, end); #ifdef CLASH_DETECTION - if (!NILP (current_buffer->filename) + if (!NILP (current_buffer->file_truename) + /* Make binding buffer-file-name to nil effective. */ + && !NILP (current_buffer->filename) && SAVE_MODIFF >= MODIFF) - lock_file (current_buffer->filename); + lock_file (current_buffer->file_truename); #else /* At least warn if this file has changed on disk since it was visited. */ if (!NILP (current_buffer->filename) @@ -733,100 +843,57 @@ prepare_to_modify_buffer (start, end) Vdeactivate_mark = Qt; } -static Lisp_Object -before_change_function_restore (value) - Lisp_Object value; -{ - Vbefore_change_function = value; -} - -static Lisp_Object -after_change_function_restore (value) - Lisp_Object value; -{ - Vafter_change_function = value; -} - -static Lisp_Object -before_change_functions_restore (value) - Lisp_Object value; -{ - Vbefore_change_functions = value; -} - -static Lisp_Object -after_change_functions_restore (value) - Lisp_Object value; -{ - Vafter_change_functions = value; -} - /* Signal a change to the buffer immediately before it happens. - START and END are the bounds of the text to be changed, - as Lisp objects. */ + START_INT and END_INT are the bounds of the text to be changed. */ void -signal_before_change (start, end) - Lisp_Object start, end; +signal_before_change (start_int, end_int) + int start_int, end_int; { + Lisp_Object start, end; + + start = make_number (start_int); + end = make_number (end_int); + /* If buffer is unmodified, run a special hook for that case. */ if (SAVE_MODIFF >= MODIFF && !NILP (Vfirst_change_hook) && !NILP (Vrun_hooks)) call1 (Vrun_hooks, Qfirst_change_hook); - /* Now in any case run the before-change-function if any. */ + /* Run the before-change-function if any. + We don't bother "binding" this variable to nil + because it is obsolete anyway and new code should not use it. */ if (!NILP (Vbefore_change_function)) - { - int count = specpdl_ptr - specpdl; - Lisp_Object function; - - function = Vbefore_change_function; - - record_unwind_protect (after_change_function_restore, - Vafter_change_function); - record_unwind_protect (before_change_function_restore, - Vbefore_change_function); - record_unwind_protect (after_change_functions_restore, - Vafter_change_functions); - record_unwind_protect (before_change_functions_restore, - Vbefore_change_functions); - Vafter_change_function = Qnil; - Vbefore_change_function = Qnil; - Vafter_change_functions = Qnil; - Vbefore_change_functions = Qnil; - - call2 (function, start, end); - unbind_to (count, Qnil); - } + call2 (Vbefore_change_function, start, end); - /* Now in any case run the before-change-function if any. */ + /* Now run the before-change-functions if any. */ if (!NILP (Vbefore_change_functions)) { - int count = specpdl_ptr - specpdl; - Lisp_Object functions; - - functions = Vbefore_change_functions; - - record_unwind_protect (after_change_function_restore, - Vafter_change_function); - record_unwind_protect (before_change_function_restore, - Vbefore_change_function); - record_unwind_protect (after_change_functions_restore, - Vafter_change_functions); - record_unwind_protect (before_change_functions_restore, - Vbefore_change_functions); - Vafter_change_function = Qnil; - Vbefore_change_function = Qnil; - Vafter_change_functions = Qnil; + Lisp_Object args[3]; + Lisp_Object before_change_functions; + Lisp_Object after_change_functions; + struct gcpro gcpro1, gcpro2; + + /* "Bind" before-change-functions and after-change-functions + to nil--but in a way that errors don't know about. + That way, if there's an error in them, they will stay nil. */ + before_change_functions = Vbefore_change_functions; + after_change_functions = Vafter_change_functions; Vbefore_change_functions = Qnil; - - while (CONSP (functions)) - { - call2 (XCONS (functions)->car, start, end); - functions = XCONS (functions)->cdr; - } - unbind_to (count, Qnil); + Vafter_change_functions = Qnil; + GCPRO2 (before_change_functions, after_change_functions); + + /* Actually run the hook functions. */ + args[0] = Qbefore_change_functions; + args[1] = start; + args[2] = end; + run_hook_list_with_args (before_change_functions, 3, args); + + /* "Unbind" the variables we "bound" to nil. */ + Vbefore_change_functions = before_change_functions; + Vafter_change_functions = after_change_functions; + UNGCPRO; } if (!NILP (current_buffer->overlays_before) @@ -838,71 +905,179 @@ signal_before_change (start, end) POS is the address of the start of the changed text. LENDEL is the number of characters of the text before the change. (Not the whole buffer; just the part that was changed.) - LENINS is the number of characters in the changed text. - - (Hence POS + LENINS - LENDEL is the position after the changed text.) */ + LENINS is the number of characters in that part of the text + after the change. */ void signal_after_change (pos, lendel, lenins) int pos, lendel, lenins; { - if (!NILP (Vafter_change_function)) + /* If we are deferring calls to the after-change functions + and there are no before-change functions, + just record the args that we were going to use. */ + if (! NILP (Vcombine_after_change_calls) + && NILP (Vbefore_change_function) && NILP (Vbefore_change_functions) + && NILP (current_buffer->overlays_before) + && NILP (current_buffer->overlays_after)) { - int count = specpdl_ptr - specpdl; - Lisp_Object function; - function = Vafter_change_function; - - record_unwind_protect (after_change_function_restore, - Vafter_change_function); - record_unwind_protect (before_change_function_restore, - Vbefore_change_function); - record_unwind_protect (after_change_functions_restore, - Vafter_change_functions); - record_unwind_protect (before_change_functions_restore, - Vbefore_change_functions); - Vafter_change_function = Qnil; - Vbefore_change_function = Qnil; - Vafter_change_functions = Qnil; - Vbefore_change_functions = Qnil; + Lisp_Object elt; - call3 (function, make_number (pos), make_number (pos + lenins), - make_number (lendel)); - unbind_to (count, Qnil); + if (!NILP (combine_after_change_list) + && current_buffer != XBUFFER (combine_after_change_buffer)) + Fcombine_after_change_execute (); + + elt = Fcons (make_number (pos - BEG), + Fcons (make_number (Z - (pos - lendel + lenins)), + Fcons (make_number (lenins - lendel), Qnil))); + combine_after_change_list + = Fcons (elt, combine_after_change_list); + combine_after_change_buffer = Fcurrent_buffer (); + + return; } + + if (!NILP (combine_after_change_list)) + Fcombine_after_change_execute (); + + /* Run the after-change-function if any. + We don't bother "binding" this variable to nil + because it is obsolete anyway and new code should not use it. */ + if (!NILP (Vafter_change_function)) + call3 (Vafter_change_function, + make_number (pos), make_number (pos + lenins), + make_number (lendel)); + if (!NILP (Vafter_change_functions)) { - int count = specpdl_ptr - specpdl; - Lisp_Object functions; - functions = Vafter_change_functions; - - record_unwind_protect (after_change_function_restore, - Vafter_change_function); - record_unwind_protect (before_change_function_restore, - Vbefore_change_function); - record_unwind_protect (after_change_functions_restore, - Vafter_change_functions); - record_unwind_protect (before_change_functions_restore, - Vbefore_change_functions); - Vafter_change_function = Qnil; - Vbefore_change_function = Qnil; - Vafter_change_functions = Qnil; + Lisp_Object args[4]; + Lisp_Object before_change_functions; + Lisp_Object after_change_functions; + struct gcpro gcpro1, gcpro2; + + /* "Bind" before-change-functions and after-change-functions + to nil--but in a way that errors don't know about. + That way, if there's an error in them, they will stay nil. */ + before_change_functions = Vbefore_change_functions; + after_change_functions = Vafter_change_functions; Vbefore_change_functions = Qnil; - - while (CONSP (functions)) - { - call3 (XCONS (functions)->car, - make_number (pos), make_number (pos + lenins), - make_number (lendel)); - functions = XCONS (functions)->cdr; - } - unbind_to (count, Qnil); + Vafter_change_functions = Qnil; + GCPRO2 (before_change_functions, after_change_functions); + + /* Actually run the hook functions. */ + args[0] = Qafter_change_functions; + XSETFASTINT (args[1], pos); + XSETFASTINT (args[2], pos + lenins); + XSETFASTINT (args[3], lendel); + run_hook_list_with_args (after_change_functions, + 4, args); + + /* "Unbind" the variables we "bound" to nil. */ + Vbefore_change_functions = before_change_functions; + Vafter_change_functions = after_change_functions; + UNGCPRO; } if (!NILP (current_buffer->overlays_before) || !NILP (current_buffer->overlays_after)) report_overlay_modification (make_number (pos), - make_number (pos + lenins - lendel), + make_number (pos + lenins), 1, make_number (pos), make_number (pos + lenins), make_number (lendel)); + + /* After an insertion, call the text properties + insert-behind-hooks or insert-in-front-hooks. */ + if (lendel == 0) + report_interval_modification (pos, pos + lenins); +} + +Lisp_Object +Fcombine_after_change_execute_1 (val) + Lisp_Object val; +{ + Vcombine_after_change_calls = val; + return val; +} + +DEFUN ("combine-after-change-execute", Fcombine_after_change_execute, + Scombine_after_change_execute, 0, 0, 0, + "This function is for use internally in `combine-after-change-calls'.") + () +{ + register Lisp_Object val; + int count = specpdl_ptr - specpdl; + int beg, end, change; + int begpos, endpos; + Lisp_Object tail; + + record_unwind_protect (Fset_buffer, Fcurrent_buffer ()); + + Fset_buffer (combine_after_change_buffer); + + /* # chars unchanged at beginning of buffer. */ + beg = Z - BEG; + /* # chars unchanged at end of buffer. */ + end = beg; + /* Total amount of insertion (negative for deletion). */ + change = 0; + + /* Scan the various individual changes, + accumulating the range info in BEG, END and CHANGE. */ + for (tail = combine_after_change_list; CONSP (tail); + tail = XCONS (tail)->cdr) + { + Lisp_Object elt; + int thisbeg, thisend, thischange; + + /* Extract the info from the next element. */ + elt = XCONS (tail)->car; + if (! CONSP (elt)) + continue; + thisbeg = XINT (XCONS (elt)->car); + + elt = XCONS (elt)->cdr; + if (! CONSP (elt)) + continue; + thisend = XINT (XCONS (elt)->car); + + elt = XCONS (elt)->cdr; + if (! CONSP (elt)) + continue; + thischange = XINT (XCONS (elt)->car); + + /* Merge this range into the accumulated range. */ + change += thischange; + if (thisbeg < beg) + beg = thisbeg; + if (thisend < end) + end = thisend; + } + + /* Get the current start and end positions of the range + that was changed. */ + begpos = BEG + beg; + endpos = Z - end; + + /* We are about to handle these, so discard them. */ + combine_after_change_list = Qnil; + + /* Now run the after-change functions for real. + Turn off the flag that defers them. */ + record_unwind_protect (Fcombine_after_change_execute_1, + Vcombine_after_change_calls); + signal_after_change (begpos, endpos - begpos - change, endpos - begpos); + + return unbind_to (count, val); +} + +syms_of_insdel () +{ + staticpro (&combine_after_change_list); + combine_after_change_list = Qnil; + + DEFVAR_LISP ("combine-after-change-calls", &Vcombine_after_change_calls, + "Used internally by the `combine-after-change-calls' macro."); + Vcombine_after_change_calls = Qnil; + + defsubr (&Scombine_after_change_execute); }