Mercurial > emacs
changeset 157:01ad7ec29c98
Initial revision
author | Jim Blandy <jimb@redhat.com> |
---|---|
date | Sat, 05 Jan 1991 15:12:15 +0000 |
parents | 29a528f78681 |
children | a4e766535a97 |
files | src/insdel.c |
diffstat | 1 files changed, 570 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/insdel.c Sat Jan 05 15:12:15 1991 +0000 @@ -0,0 +1,570 @@ +/* Buffer insertion/deletion and gap motion for GNU Emacs. + Copyright (C) 1985, 1986 Free Software Foundation, Inc. + +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) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +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. */ + + +#include "config.h" +#include "lisp.h" +#include "buffer.h" +#include "window.h" + +/* Nonzero means don't allow protected fields to be modified. */ + +extern int check_protected_fields; + +/* Move gap to position `pos'. + Note that this can quit! */ + +move_gap (pos) + int pos; +{ + if (pos < GPT) + gap_left (pos, 0); + else if (pos > GPT) + gap_right (pos); +} + +/* Move the gap to POS, which is less than the current GPT. + If NEWGAP is nonzero, then don't update beg_unchanged and end_unchanged. */ + +gap_left (pos, newgap) + register int pos; + int newgap; +{ + register unsigned char *to, *from; + register int i; + int new_s1; + + pos--; + + if (!newgap) + { + if (unchanged_modified == MODIFF) + { + beg_unchanged = pos; + end_unchanged = Z - pos - 1; + } + else + { + if (Z - GPT < end_unchanged) + end_unchanged = Z - GPT; + if (pos < beg_unchanged) + beg_unchanged = pos; + } + } + + i = GPT; + to = GAP_END_ADDR; + from = GPT_ADDR; + new_s1 = GPT - BEG; + + /* Now copy the characters. To move the gap down, + copy characters up. */ + + while (1) + { + /* I gets number of characters left to copy. */ + i = new_s1 - pos; + if (i == 0) + break; + /* If a quit is requested, stop copying now. + Change POS to be where we have actually moved the gap to. */ + if (QUITP) + { + pos = new_s1; + break; + } + /* Move at most 32000 chars before checking again for a quit. */ + if (i > 32000) + i = 32000; +#ifdef GAP_USE_BCOPY + if (i >= 128 + /* bcopy is safe if the two areas of memory do not overlap + or on systems where bcopy is always safe for moving upward. */ + && (BCOPY_UPWARD_SAFE + || to - from >= 128)) + { + /* If overlap is not safe, avoid it by not moving too many + characters at once. */ + if (!BCOPY_UPWARD_SAFE && i > to - from) + i = to - from; + new_s1 -= i; + from -= i, to -= i; + bcopy (from, to, i); + } + else +#endif + { + new_s1 -= i; + while (--i >= 0) + *--to = *--from; + } + } + + /* Adjust markers, and buffer data structure, to put the gap at POS. + POS is where the loop above stopped, which may be what was specified + or may be where a quit was detected. */ + adjust_markers (pos + 1, GPT, GAP_SIZE); + GPT = pos + 1; + QUIT; +} + +gap_right (pos) + register int pos; +{ + register unsigned char *to, *from; + register int i; + int new_s1; + + pos--; + + if (unchanged_modified == MODIFF) + { + beg_unchanged = pos; + end_unchanged = Z - pos - 1; + } + else + { + if (Z - pos - 1 < end_unchanged) + end_unchanged = Z - pos - 1; + if (GPT - BEG < beg_unchanged) + beg_unchanged = GPT - BEG; + } + + i = GPT; + from = GAP_END_ADDR; + to = GPT_ADDR; + new_s1 = GPT - 1; + + /* Now copy the characters. To move the gap up, + copy characters down. */ + + while (1) + { + /* I gets number of characters left to copy. */ + i = pos - new_s1; + if (i == 0) + break; + /* If a quit is requested, stop copying now. + Change POS to be where we have actually moved the gap to. */ + if (QUITP) + { + pos = new_s1; + break; + } + /* Move at most 32000 chars before checking again for a quit. */ + if (i > 32000) + i = 32000; +#ifdef GAP_USE_BCOPY + if (i >= 128 + /* bcopy is safe if the two areas of memory do not overlap + or on systems where bcopy is always safe for moving downward. */ + && (BCOPY_DOWNWARD_SAFE + || from - to >= 128)) + { + /* If overlap is not safe, avoid it by not moving too many + characters at once. */ + if (!BCOPY_DOWNWARD_SAFE && i > from - to) + i = from - to; + new_s1 += i; + bcopy (from, to, i); + from += i, to += i; + } + else +#endif + { + new_s1 += i; + while (--i >= 0) + *to++ = *from++; + } + } + + adjust_markers (GPT + GAP_SIZE, pos + 1 + GAP_SIZE, - GAP_SIZE); + GPT = pos + 1; + QUIT; +} + +/* 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'. */ + +adjust_markers (from, to, amount) + register int from, to, amount; +{ + Lisp_Object marker; + register struct Lisp_Marker *m; + register int mpos; + + marker = current_buffer->markers; + + while (!NULL (marker)) + { + m = XMARKER (marker); + mpos = m->bufpos; + if (amount > 0) + { + if (mpos > to && mpos < to + amount) + mpos = to + amount; + } + else + { + if (mpos > from + amount && mpos <= from) + mpos = from + amount; + } + if (mpos > from && mpos <= to) + mpos += amount; + m->bufpos = mpos; + marker = m->chain; + } +} + +/* Make the gap INCREMENT characters longer. */ + +make_gap (increment) + int increment; +{ + unsigned char *result; + Lisp_Object tem; + int real_gap_loc; + int old_gap_size; + + /* If we have to get more space, get enough to last a while. */ + increment += 2000; + + result = BUFFER_REALLOC (BEG_ADDR, (Z - BEG + GAP_SIZE + increment)); + if (result == 0) + memory_full (); + BEG_ADDR = result; + + /* Prevent quitting in move_gap. */ + tem = Vinhibit_quit; + Vinhibit_quit = Qt; + + real_gap_loc = GPT; + old_gap_size = GAP_SIZE; + + /* Call the newly allocated space a gap at the end of the whole space. */ + GPT = Z + GAP_SIZE; + GAP_SIZE = increment; + + /* Move the new gap down to be consecutive with the end of the old one. + This adjusts the markers properly too. */ + gap_left (real_gap_loc + old_gap_size, 1); + + /* Now combine the two into one large gap. */ + GAP_SIZE += old_gap_size; + GPT = real_gap_loc; + + Vinhibit_quit = tem; +} + +/* Insert a string of specified length before point. + DO NOT use this for the contents of a Lisp string! + prepare_to_modify_buffer could relocate the string. */ + +insert (string, length) + register unsigned char *string; + register length; +{ + register Lisp_Object temp; + + if (length < 1) + return; + + /* Make sure point-max won't overflow after this insertion. */ + XSET (temp, Lisp_Int, length + Z); + if (length + Z != XINT (temp)) + error ("maximum buffer size exceeded"); + + prepare_to_modify_buffer (point, point); + + if (point != GPT) + move_gap (point); + if (GAP_SIZE < length) + make_gap (length - GAP_SIZE); + + record_insert (point, length); + MODIFF++; + + bcopy (string, GPT_ADDR, length); + + GAP_SIZE -= length; + GPT += length; + ZV += length; + Z += length; + SET_PT (point + length); + + signal_after_change (point-length, 0, length); +} + +/* Function to insert part of the text of a string (STRING) + consisting of LENGTH characters at position POS. + It does not work to use `insert' for this. */ + +insert_from_string (string, pos, length) + Lisp_Object string; + register int pos, length; +{ + register Lisp_Object temp; + struct gcpro gcpro1; + + if (length < 1) + return; + + /* Make sure point-max won't overflow after this insertion. */ + XSET (temp, Lisp_Int, length + Z); + if (length + Z != XINT (temp)) + error ("maximum buffer size exceeded"); + + GCPRO1 (string); + prepare_to_modify_buffer (point, point); + + if (point != GPT) + move_gap (point); + if (GAP_SIZE < length) + make_gap (length - GAP_SIZE); + + record_insert (point, length); + MODIFF++; + UNGCPRO; + + bcopy (XSTRING (string)->data, GPT_ADDR, length); + + GAP_SIZE -= length; + GPT += length; + ZV += length; + Z += length; + point += length; + + signal_after_change (point-length, 0, length); +} + +/* Insert the character C before point */ + +void +insert_char (c) + unsigned char c; +{ + insert (&c, 1); +} + +/* Insert the null-terminated string S before point */ + +void +insert_string (s) + char *s; +{ + insert (s, strlen (s)); +} + +/* Like `insert' except that all markers pointing at the place where + the insertion happens are adjusted to point after it. + Don't use this function to insert part of a Lisp string, + since gc could happen and relocate it. */ + +insert_before_markers (string, length) + unsigned char *string; + register int length; +{ + register int opoint = point; + insert (string, length); + adjust_markers (opoint - 1, opoint, length); +} + +/* Insert part of a Lisp string, relocating markers after. */ + +insert_from_string_before_markers (string, pos, length) + Lisp_Object string; + register int pos, length; +{ + register int opoint = point; + insert_from_string (string, pos, length); + adjust_markers (opoint - 1, opoint, length); +} + +/* Delete characters in current buffer + from FROM up to (but not including) TO. */ + +del_range (from, to) + register int from, to; +{ + register int numdel; + + /* Make args be valid */ + if (from < BEGV) + from = BEGV; + if (to > ZV) + to = ZV; + + if ((numdel = to - from) <= 0) + return; + + /* Make sure the gap is somewhere in or next to what we are deleting. */ + if (from > GPT) + gap_right (from); + if (to < GPT) + gap_left (to, 0); + + prepare_to_modify_buffer (from, to); + + /* Relocate point as if it were a marker. */ + if (from < point) + { + if (point < to) + SET_PT (from); + else + SET_PT (point - numdel); + } + + record_delete (from, numdel); + MODIFF++; + + /* 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); + + GAP_SIZE += numdel; + ZV -= numdel; + Z -= numdel; + GPT = from; + + if (GPT - BEG < beg_unchanged) + beg_unchanged = GPT - BEG; + if (Z - GPT < end_unchanged) + end_unchanged = Z - GPT; + + signal_after_change (from, numdel, 0); +} + +modify_region (start, end) + int start, end; +{ + prepare_to_modify_buffer (start, end); + + if (start - 1 < beg_unchanged || unchanged_modified == MODIFF) + beg_unchanged = start - 1; + if (Z - end < end_unchanged + || unchanged_modified == MODIFF) + end_unchanged = Z - end; + MODIFF++; +} + +/* Check that it is okay to modify the buffer between START and END. + Run the before-change-function, if any. */ + +prepare_to_modify_buffer (start, end) + Lisp_Object start, end; +{ + if (!NULL (current_buffer->read_only)) + Fbarf_if_buffer_read_only (); + + if (check_protected_fields) + Fregion_fields (start, end, Qnil, Qt); + +#ifdef CLASH_DETECTION + if (!NULL (current_buffer->filename) + && current_buffer->save_modified >= MODIFF) + lock_file (current_buffer->filename); +#else + /* At least warn if this file has changed on disk since it was visited. */ + if (!NULL (current_buffer->filename) + && current_buffer->save_modified >= MODIFF + && NULL (Fverify_visited_file_modtime (Fcurrent_buffer ())) + && !NULL (Ffile_exists_p (current_buffer->filename))) + call1 (intern ("ask-user-about-supersession-threat"), + current_buffer->filename); +#endif /* not CLASH_DETECTION */ + + signal_before_change (start, end); +} + +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; +} + +/* Signal a change to the buffer immediatly before it happens. + START and END are the bounds of the text to be changed, + as Lisp objects. */ + +signal_before_change (start, end) + Lisp_Object start, end; +{ + /* If buffer is unmodified, run a special hook for that case. */ + if (current_buffer->save_modified >= MODIFF + && !NULL (Vfirst_change_function)) + { + call0 (Vfirst_change_function); + } + /* Now in any case run the before-change-function if any. */ + if (!NULL (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); + Vafter_change_function = Qnil; + Vbefore_change_function = Qnil; + + call2 (function, start, end); + unbind_to (count, Qnil); + } +} + +/* Signal a change immediatly after it happens. + 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. */ + +signal_after_change (pos, lendel, lenins) + int pos, lendel, lenins; +{ + if (!NULL (Vafter_change_function)) + { + 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); + Vafter_change_function = Qnil; + Vbefore_change_function = Qnil; + + call3 (function, make_number (pos), make_number (pos + lenins), + make_number (lendel)); + unbind_to (count, Qnil); + } +}