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);
+    }
+}