* editfns.c (Fdelete_and_extract_region): New function.
[bpt/emacs.git] / src / insdel.c
index ab79279..60a9284 100644 (file)
@@ -1,5 +1,5 @@
 /* Buffer insertion/deletion and gap motion for GNU Emacs.
-   Copyright (C) 1985, 86, 93, 94, 95, 97, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1985, 86,93,94,95,97,98, 1999 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -33,6 +33,7 @@ Boston, MA 02111-1307, USA.  */
 #endif
 
 #define min(x, y) ((x) < (y) ? (x) : (y))
+#define max(x, y) ((x) > (y) ? (x) : (y))
 
 static void insert_from_string_1 P_ ((Lisp_Object, int, int, int, int, int, int));
 static void insert_from_buffer_1 ();
@@ -40,7 +41,7 @@ static void gap_left P_ ((int, int, int));
 static void gap_right P_ ((int, int));
 static void adjust_markers_gap_motion P_ ((int, int, int));
 static void adjust_markers_for_insert P_ ((int, int, int, int, int, int, int));
-static void adjust_markers_for_delete P_ ((int, int, int, int));
+void        adjust_markers_for_delete P_ ((int, int, int, int));
 static void adjust_markers_for_record_delete P_ ((int, int, int, int));
 static void adjust_point P_ ((int, int));
 
@@ -77,7 +78,7 @@ static int check_markers_debug_flag;
 void
 check_markers ()
 {
-  register Lisp_Object tail, prev, next;
+  register Lisp_Object tail;
   int multibyte = ! NILP (current_buffer->enable_multibyte_characters);
 
   tail = BUF_MARKERS (current_buffer);
@@ -135,21 +136,7 @@ gap_left (charpos, bytepos, newgap)
   int new_s1;
 
   if (!newgap)
-    {
-      if (unchanged_modified == MODIFF
-         && overlay_unchanged_modified == OVERLAY_MODIFF)
-       {
-         beg_unchanged = charpos - BEG;
-         end_unchanged = Z - charpos;
-       }
-      else
-       {
-         if (Z - GPT < end_unchanged)
-           end_unchanged = Z - GPT;
-         if (charpos < beg_unchanged)
-           beg_unchanged = charpos - BEG;
-       }
-    }
+    BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
 
   i = GPT_BYTE;
   to = GAP_END_ADDR;
@@ -224,19 +211,7 @@ gap_right (charpos, bytepos)
   register int i;
   int new_s1;
 
-  if (unchanged_modified == MODIFF
-      && overlay_unchanged_modified == OVERLAY_MODIFF)
-    {
-      beg_unchanged = charpos - BEG;
-      end_unchanged = Z - charpos;
-    }
-  else
-    {
-      if (Z - charpos - 1 < end_unchanged)
-       end_unchanged = Z - charpos;
-      if (GPT - BEG < beg_unchanged)
-       beg_unchanged = GPT - BEG;
-    }
+  BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
 
   i = GPT_BYTE;
   from = GAP_END_ADDR;
@@ -366,7 +341,7 @@ adjust_markers_gap_motion (from, to, amount)
    This function assumes that the gap is adjacent to
    or inside of the range being deleted.  */
 
-static void
+void
 adjust_markers_for_delete (from, from_byte, to, to_byte)
      register int from, from_byte, to, to_byte;
 {
@@ -578,7 +553,12 @@ adjust_markers_for_replace (from, from_byte, old_chars, old_bytes,
     {
       register struct Lisp_Marker *m = XMARKER (marker);
 
-      if (m->bytepos >= prev_to_byte)
+      if (m->bytepos >= prev_to_byte
+         && (old_bytes != 0
+             /* If this is an insertion (replacing 0 chars),
+                reject the case of a marker that is at the
+                insertion point and should stay before the insertion.  */
+             || m->bytepos > from_byte || m->insertion_type))
        {
          if (m->bytepos < prev_to_byte + combined_after_bytes)
            {
@@ -588,8 +568,9 @@ adjust_markers_for_replace (from, from_byte, old_chars, old_bytes,
            }
          else
            {
-             m->charpos += diff_chars;
-             m->bytepos += diff_bytes;
+             m->charpos = min (from + new_chars, m->charpos + diff_chars);
+             m->bytepos = min (from_byte + new_bytes,
+                               m->bytepos + diff_bytes);
            }
        }
       else if (m->bytepos >= from_byte)
@@ -700,7 +681,7 @@ copy_text (from_addr, to_addr, nbytes,
     {
       int nchars = 0;
       int bytes_left = nbytes;
-      Lisp_Object tbl = Qnil, temp;
+      Lisp_Object tbl = Qnil;
 
       /* We set the variable tbl to the reverse table of
          Vnonascii_translation_table in advance.  */
@@ -737,7 +718,9 @@ copy_text (from_addr, to_addr, nbytes,
          unsigned char workbuf[4], *str;
          int len;
 
-         if ((c >= 0240 || !NILP (Vnonascii_translation_table)) && c < 0400)
+         if (c < 0400
+             && (c >= 0240
+                 || (c >= 0200 && !NILP (Vnonascii_translation_table))))
            {
              c = unibyte_char_to_multibyte (c);
              len = CHAR_STRING (c, workbuf, str);
@@ -769,7 +752,7 @@ count_size_as_multibyte (ptr, nbytes)
     {
       unsigned int c = *ptr++;
 
-      if (c < 0240 && NILP (Vnonascii_translation_table))
+      if (c < 0200 || (c < 0240 && NILP (Vnonascii_translation_table)))
        outgoing_nbytes++;
       else
        {
@@ -894,6 +877,33 @@ insert_1 (string, nbytes, inherit, prepare, before_markers)
                 inherit, prepare, before_markers);
 }
 \f
+/* See if the byte sequence at STR1 of length LEN1 combine with the
+   byte sequence at STR2 of length LEN2 to form a single composite
+   character.  If so, return the number of bytes at the start of STR2
+   which combine in this way.  Otherwise, return 0.  If STR3 is not
+   NULL, it is a byte sequence of length LEN3 to be appended to STR1
+   before checking the combining.  */
+int
+count_combining_composition (str1, len1, str2, len2, str3, len3)
+     unsigned char *str1, *str2, *str3;
+     int len1, len2, len3;
+{
+  int len = len1 + len2 + len3;
+  unsigned char *buf = (unsigned char *) alloca (len + 1);
+  int bytes;
+
+  bcopy (str1, buf, len1);
+  if (str3)
+    {
+      bcopy (str3, buf + len1, len3);
+      len1 += len3;
+    }
+  bcopy (str2, buf + len1 , len2);
+  buf[len] = 0;
+  PARSE_MULTIBYTE_SEQ (buf, len, bytes);
+  return (bytes <= len1 ? 0 : bytes - len1);
+}
+
 /* See if the bytes before POS/POS_BYTE combine with bytes
    at the start of STRING to form a single character.
    If so, return the number of bytes at the start of STRING
@@ -905,30 +915,44 @@ count_combining_before (string, length, pos, pos_byte)
      int length;
      int pos, pos_byte;
 {
-  int opos = pos, opos_byte = pos_byte;
-  int c;
-  unsigned char *p = string;
+  int len, combining_bytes;
+  unsigned char *p;
 
   if (NILP (current_buffer->enable_multibyte_characters))
     return 0;
-  if (length == 0 || CHAR_HEAD_P (*string))
+
+  /* At first, we can exclude the following cases:
+       (1) STRING[0] can't be a following byte of multibyte sequence.
+       (2) POS is the start of the current buffer.
+       (3) A character before POS is not a multibyte character.  */
+  if (length == 0 || CHAR_HEAD_P (*string)) /* case (1) */
     return 0;
-  if (pos == BEG)
+  if (pos_byte == BEG_BYTE)    /* case (2) */
     return 0;
-  c = FETCH_BYTE (pos_byte - 1);
-  if (ASCII_BYTE_P (c))
+  len = 1;
+  p = BYTE_POS_ADDR (pos_byte - 1);
+  while (! CHAR_HEAD_P (*p)) p--, len++;
+  if (! BASE_LEADING_CODE_P (*p)) /* case (3) */
     return 0;
-  DEC_BOTH (pos, pos_byte);
-  c = FETCH_BYTE (pos_byte);
-  if (! BASE_LEADING_CODE_P (c))
+
+  /* A sequence of a composite character requires a special handling.  */
+  if (*p == LEADING_CODE_COMPOSITION)
+    return count_combining_composition (p, len, string, length, NULL, 0);
+
+  combining_bytes = BYTES_BY_CHAR_HEAD (*p) - len;
+  if (combining_bytes <= 0)
+    /* The character preceding POS is, complete and no room for
+       combining bytes (combining_bytes == 0), or an independent 8-bit
+       character (combining_bytes < 0).  */
     return 0;
 
-  /* We have a combination situation.
-     Count the bytes at STRING that will combine.  */
+  /* We have a combination situation.  Count the bytes at STRING that
+     may combine.  */
+  p = string + 1;
   while (!CHAR_HEAD_P (*p) && p < string + length)
     p++;
 
-  return p - string;
+  return (combining_bytes < p - string ? combining_bytes : p - string);
 }
 
 /* See if the bytes after POS/POS_BYTE combine with bytes
@@ -942,14 +966,27 @@ count_combining_after (string, length, pos, pos_byte)
      int length;
      int pos, pos_byte;
 {
-  int opos = pos, opos_byte = pos_byte;
+  int opos_byte = pos_byte;
   int i;
-  int c;
+  int bytes;
+  unsigned char *bufp;
 
   if (NILP (current_buffer->enable_multibyte_characters))
     return 0;
-  if (length > 0 && ASCII_BYTE_P (string[length - 1]))
+
+  /* At first, we can exclude the following cases:
+       (1) The last byte of STRING is an ASCII.
+       (2) POS is the last of the current buffer.
+       (3) A character at POS can't be a following byte of multibyte
+           character.  */
+  if (length > 0 && ASCII_BYTE_P (string[length - 1])) /* case (1) */
+    return 0;
+  if (pos_byte == Z_BYTE)      /* case (2) */
+    return 0;
+  bufp = BYTE_POS_ADDR (pos_byte);
+  if (CHAR_HEAD_P (*bufp))     /* case (3) */
     return 0;
+
   i = length - 1;
   while (i >= 0 && ! CHAR_HEAD_P (string[i]))
     {
@@ -957,33 +994,37 @@ count_combining_after (string, length, pos, pos_byte)
     }
   if (i < 0)
     {
-      /* All characters in `string' are not character head.
-        We must check also preceding bytes at POS.
-        We are sure that the gap is at POS.  */
-      string = BEG_ADDR;
+      /* All characters in STRING are not character head.  We must
+        check also preceding bytes at POS.  We are sure that the gap
+        is at POS.  */
+      unsigned char *p = BEG_ADDR;
       i = pos_byte - 2;
-      while (i >= 0 && ! CHAR_HEAD_P (string[i]))
+      while (i >= 0 && ! CHAR_HEAD_P (p[i]))
        i--;
-      if (i < 0 || !BASE_LEADING_CODE_P (string[i]))
+      if (i < 0 || !BASE_LEADING_CODE_P (p[i]))
        return 0;
+      /* A sequence of a composite character requires a special handling.  */
+      if (p[i] == LEADING_CODE_COMPOSITION)
+       return count_combining_composition (p + i, pos_byte - 1 - i,
+                                           bufp, Z_BYTE - pos_byte,
+                                           string, length);
+      bytes = BYTES_BY_CHAR_HEAD (p[i]);
+      return (bytes <= pos_byte - 1 - i + length
+             ? 0
+             : bytes - (pos_byte - 1 - i + length));
     }
-  else if (!BASE_LEADING_CODE_P (string[i]))
+  if (!BASE_LEADING_CODE_P (string[i]))
     return 0;
+  /* A sequence of a composite character requires a special handling.  */
+  if (string[i] == LEADING_CODE_COMPOSITION)
+    return count_combining_composition (string + i, length - i,
+                                       bufp, Z_BYTE - pos_byte, NULL, 0);
 
-  if (pos == Z)
-    return 0;
-  c = FETCH_BYTE (pos_byte);
-  if (CHAR_HEAD_P (c))
-    return 0;
-  while (pos_byte < Z_BYTE)
-    {
-      c = FETCH_BYTE (pos_byte);
-      if (CHAR_HEAD_P (c))
-       break;
-      pos_byte++;
-    }
+  bytes = BYTES_BY_CHAR_HEAD (string[i]) - (length - i);
+  bufp++, pos_byte++;
+  while (!CHAR_HEAD_P (*bufp)) bufp++, pos_byte++;
 
-  return pos_byte - opos_byte;
+  return (bytes <= pos_byte - opos_byte ? bytes : pos_byte - opos_byte);
 }
 
 /* Adjust the position TARGET/TARGET_BYTE for the combining of NBYTES
@@ -1025,7 +1066,6 @@ combine_bytes (pos, pos_byte, nbytes)
   ADJUST_CHAR_POS (ZV, ZV_BYTE);
 
   if (BUF_INTERVALS (current_buffer) != 0)
-    /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES.  */
     offset_intervals (current_buffer, pos, - nbytes);
 }
 
@@ -1039,8 +1079,8 @@ byte_combining_error ()
    region boundary, signal an error.  */
 #define CHECK_BYTE_COMBINING_FOR_INSERT(pos)                           \
   do {                                                                 \
-    if (combined_before_bytes && pos == BEGV                           \
-       || combined_after_bytes && pos == ZV)                           \
+    if ((combined_before_bytes && pos == BEGV)                         \
+       || (combined_after_bytes && pos == ZV))                         \
       byte_combining_error (); \
   } while (0)
 
@@ -1055,7 +1095,6 @@ insert_1_both (string, nchars, nbytes, inherit, prepare, before_markers)
      register int nchars, nbytes;
      int inherit, prepare, before_markers;
 {
-  register Lisp_Object temp;
   int combined_before_bytes, combined_after_bytes;
 
   if (NILP (current_buffer->enable_multibyte_characters))
@@ -1146,15 +1185,12 @@ insert_1_both (string, nchars, nbytes, inherit, prepare, before_markers)
                             combined_before_bytes, combined_after_bytes,
                             before_markers);
 
-#ifdef USE_TEXT_PROPERTIES
   if (BUF_INTERVALS (current_buffer) != 0)
-    /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES.  */
     offset_intervals (current_buffer, PT, nchars);
 
   if (!inherit && BUF_INTERVALS (current_buffer) != 0)
-    Fset_text_properties (make_number (PT), make_number (PT + nchars),
-                         Qnil, Qnil);
-#endif
+    set_text_properties (make_number (PT), make_number (PT + nchars),
+                        Qnil, Qnil, Qnil);
 
   {
     int pos = PT, pos_byte = PT_BYTE;
@@ -1218,11 +1254,9 @@ insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
      register int pos, pos_byte, nchars, nbytes;
      int inherit, before_markers;
 {
-  register Lisp_Object temp;
   struct gcpro gcpro1;
   int outgoing_nbytes = nbytes;
   int combined_before_bytes, combined_after_bytes;
-  int adjusted_nchars;
   INTERVAL intervals;
 
   /* Make OUTGOING_NBYTES describe the text
@@ -1243,7 +1277,7 @@ insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
 
   if (PT != GPT)
     move_gap_both (PT, PT_BYTE);
-  if (GAP_SIZE < nbytes)
+  if (GAP_SIZE < outgoing_nbytes)
     make_gap (outgoing_nbytes - GAP_SIZE);
   UNGCPRO;
 
@@ -1334,7 +1368,6 @@ insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
                             combined_before_bytes, combined_after_bytes,
                             before_markers);
 
-  /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */
   offset_intervals (current_buffer, PT, nchars);
 
   intervals = XSTRING (string)->intervals;
@@ -1388,13 +1421,12 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
      int inherit;
 {
   register Lisp_Object temp;
-  int chunk;
+  int chunk, chunk_expanded;
   int from_byte = buf_charpos_to_bytepos (buf, from);
   int to_byte = buf_charpos_to_bytepos (buf, from + nchars);
   int incoming_nbytes = to_byte - from_byte;
   int outgoing_nbytes = incoming_nbytes;
   int combined_before_bytes, combined_after_bytes;
-  int adjusted_nchars;
   INTERVAL intervals;
 
   /* Make OUTGOING_NBYTES describe the text
@@ -1403,10 +1435,31 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
   if (NILP (current_buffer->enable_multibyte_characters))
     outgoing_nbytes = nchars;
   else if (NILP (buf->enable_multibyte_characters))
-    outgoing_nbytes
-      = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, from_byte),
-                                incoming_nbytes);
+    {
+      int outgoing_before_gap = 0;
+      int outgoing_after_gap = 0;
+
+      if (from < BUF_GPT (buf))
+       {
+         chunk =  BUF_GPT_BYTE (buf) - from_byte;
+         if (chunk > incoming_nbytes)
+           chunk = incoming_nbytes;
+         outgoing_before_gap
+           = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, from_byte),
+                                      chunk);
+       }
+      else
+       chunk = 0;
 
+      if (chunk < incoming_nbytes)
+       outgoing_after_gap
+         = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, 
+                                                      from_byte + chunk),
+                                    incoming_nbytes - chunk);
+
+      outgoing_nbytes = outgoing_before_gap + outgoing_after_gap;
+    }
+  
   /* Make sure point-max won't overflow after this insertion.  */
   XSETINT (temp, outgoing_nbytes + Z);
   if (outgoing_nbytes + Z != XINT (temp))
@@ -1427,16 +1480,20 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
       chunk = BUF_GPT_BYTE (buf) - from_byte;
       if (chunk > incoming_nbytes)
        chunk = incoming_nbytes;
-      copy_text (BUF_BYTE_ADDRESS (buf, from_byte),
-                GPT_ADDR, chunk,
-                ! NILP (buf->enable_multibyte_characters),
-                ! NILP (current_buffer->enable_multibyte_characters));
+      /* Record number of output bytes, so we know where
+        to put the output from the second copy_text.  */
+      chunk_expanded
+       = copy_text (BUF_BYTE_ADDRESS (buf, from_byte),
+                    GPT_ADDR, chunk,
+                    ! NILP (buf->enable_multibyte_characters),
+                    ! NILP (current_buffer->enable_multibyte_characters));
     }
   else
-    chunk = 0;
+    chunk_expanded = chunk = 0;
+
   if (chunk < incoming_nbytes)
     copy_text (BUF_BYTE_ADDRESS (buf, from_byte + chunk),
-              GPT_ADDR + chunk, incoming_nbytes - chunk,
+              GPT_ADDR + chunk_expanded, incoming_nbytes - chunk,
               ! NILP (buf->enable_multibyte_characters),
               ! NILP (current_buffer->enable_multibyte_characters));
 
@@ -1519,10 +1576,8 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
                             PT_BYTE + outgoing_nbytes,
                             combined_before_bytes, combined_after_bytes, 0);
 
-#ifdef USE_TEXT_PROPERTIES
   if (BUF_INTERVALS (current_buffer) != 0)
     offset_intervals (current_buffer, PT, nchars);
-#endif
 
   /* Get the intervals for the part of the string we are inserting--
      not including the combined-before bytes.  */
@@ -1601,8 +1656,8 @@ adjust_after_replace (from, from_byte, prev_text, len, len_byte)
       nbytes_del = STRING_BYTES (XSTRING (prev_text));
     }
 
-  if (combine_before && from == BEGV
-      || combined_after_bytes && from == ZV)
+  if ((combine_before && from == BEGV)
+      || (combined_after_bytes && from == ZV))
     {
       /* We can't combine bytes nor signal an error here.  So, let's
         pretend that the new text is just a single space.  */
@@ -1631,7 +1686,7 @@ adjust_after_replace (from, from_byte, prev_text, len, len_byte)
     }
 
   if (combined_before_bytes
-      || len_byte == 0 && combined_after_bytes > 0)
+      || (len_byte == 0 && combined_after_bytes > 0))
     {
       Lisp_Object deletion;
       deletion = Qnil;
@@ -1674,16 +1729,12 @@ adjust_after_replace (from, from_byte, prev_text, len, len_byte)
     adjust_overlays_for_insert (from, len - nchars_del);
   else if (len < nchars_del)
     adjust_overlays_for_delete (from, nchars_del - len);
-#ifdef USE_TEXT_PROPERTIES
   if (BUF_INTERVALS (current_buffer) != 0)
     {
       offset_intervals (current_buffer, from, len - nchars_del);
     }
-#endif
 
   {
-    int pos = PT, pos_byte = PT_BYTE;
-
     if (from < PT)
       adjust_point (len - nchars_del, len_byte - nbytes_del);
 
@@ -1701,8 +1752,8 @@ adjust_after_replace (from, from_byte, prev_text, len, len_byte)
   }
 
   /* As byte combining will decrease Z, we must check this again. */
-  if (Z - GPT < end_unchanged)
-    end_unchanged = Z - GPT;
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
 
   CHECK_MARKERS ();
 
@@ -1755,7 +1806,6 @@ replace_range (from, to, new, prepare, inherit, markers)
   register Lisp_Object temp;
   struct gcpro gcpro1;
   int combined_before_bytes, combined_after_bytes;
-  int adjusted_inschars;
   INTERVAL intervals;
   int outgoing_insbytes = insbytes;
   Lisp_Object deletion;
@@ -1834,10 +1884,10 @@ replace_range (from, to, new, prepare, inherit, markers)
   if (GPT_BYTE < GPT)
     abort ();
 
-  if (GPT - BEG < beg_unchanged)
-    beg_unchanged = GPT - BEG;
-  if (Z - GPT < end_unchanged)
-    end_unchanged = Z - GPT;
+  if (GPT - BEG < BEG_UNCHANGED)
+    BEG_UNCHANGED = GPT - BEG;
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
 
   if (GAP_SIZE < insbytes)
     make_gap (insbytes - GAP_SIZE);
@@ -1859,8 +1909,8 @@ replace_range (from, to, new, prepare, inherit, markers)
   combined_after_bytes
     = count_combining_after (GPT_ADDR, outgoing_insbytes, from, from_byte);
 
-  if (combined_before_bytes && from == BEGV
-      || combined_after_bytes && from == ZV)
+  if ((combined_before_bytes && from == BEGV)
+      || (combined_after_bytes && from == ZV))
     {
       /* Bytes are being combined across the region boundary.  We
          should avoid it.  We recover the original contents before
@@ -1953,7 +2003,6 @@ replace_range (from, to, new, prepare, inherit, markers)
                               from + inschars, from_byte + outgoing_insbytes,
                               combined_before_bytes, combined_after_bytes, 0);
 
-#ifdef USE_TEXT_PROPERTIES
   offset_intervals (current_buffer, from, inschars - nchars_del);
 
   /* Get the intervals for the part of the string we are inserting--
@@ -1962,7 +2011,6 @@ replace_range (from, to, new, prepare, inherit, markers)
   /* Insert those intervals.  */
   graft_intervals_into_buffer (intervals, from, inschars,
                               current_buffer, inherit);
-#endif
 
   /* Relocate point as if it were a marker.  */
   if (from < PT)
@@ -1983,8 +2031,8 @@ replace_range (from, to, new, prepare, inherit, markers)
     combine_bytes (from, from_byte, combined_before_bytes);
 
   /* As byte combining will decrease Z, we must check this again. */
-  if (Z - GPT < end_unchanged)
-    end_unchanged = Z - GPT;
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
 
   if (outgoing_insbytes == 0)
     evaporate_overlays (from);
@@ -2005,16 +2053,19 @@ void
 del_range (from, to)
      register int from, to;
 {
-  del_range_1 (from, to, 1);
+  del_range_1 (from, to, 1, 0);
 }
 
-/* Like del_range; PREPARE says whether to call prepare_to_modify_buffer.  */
+/* Like del_range; PREPARE says whether to call prepare_to_modify_buffer.
+   RET_STRING says to return the deleted text. */
 
-void
-del_range_1 (from, to, prepare)
-     int from, to, prepare;
+Lisp_Object
+del_range_1 (from, to, prepare, ret_string)
+     int from, to, prepare, ret_string;
 {
   int from_byte, to_byte;
+  Lisp_Object deletion;
+  struct gcpro gcpro1;
 
   /* Make args be valid */
   if (from < BEGV)
@@ -2023,7 +2074,7 @@ del_range_1 (from, to, prepare)
     to = ZV;
 
   if (to <= from)
-    return;
+    return Qnil;
 
   if (prepare)
     {
@@ -2035,7 +2086,11 @@ del_range_1 (from, to, prepare)
   from_byte = CHAR_TO_BYTE (from);
   to_byte = CHAR_TO_BYTE (to);
 
-  del_range_2 (from, from_byte, to, to_byte);
+  deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
+  GCPRO1(deletion);
+  signal_after_change (from, to - from, 0);
+  UNGCPRO;
+  return deletion;
 }
 
 /* Like del_range_1 but args are byte positions, not char positions.  */
@@ -2071,7 +2126,8 @@ del_range_byte (from_byte, to_byte, prepare)
        to_byte = CHAR_TO_BYTE (to);
     }
 
-  del_range_2 (from, from_byte, to, to_byte);
+  del_range_2 (from, from_byte, to, to_byte, 0);
+  signal_after_change (from, to - from, 0);
 }
 
 /* Like del_range_1, but positions are specified both as charpos
@@ -2108,16 +2164,18 @@ del_range_both (from, from_byte, to, to_byte, prepare)
        to_byte = CHAR_TO_BYTE (to);
     }
 
-  del_range_2 (from, from_byte, to, to_byte);
+  del_range_2 (from, from_byte, to, to_byte, 0);
+  signal_after_change (from, to - from, 0);
 }
 
 /* Delete a range of text, specified both as character positions
    and byte positions.  FROM and TO are character positions,
-   while FROM_BYTE and TO_BYTE are byte positions.  */
+   while FROM_BYTE and TO_BYTE are byte positions.
+   If RET_STRING is true, the deleted area is returned as a string. */
 
-void
-del_range_2 (from, from_byte, to, to_byte)
-     int from, from_byte, to, to_byte;
+Lisp_Object
+del_range_2 (from, from_byte, to, to_byte, ret_string)
+     int from, from_byte, to, to_byte, ret_string;
 {
   register int nbytes_del, nchars_del;
   int combined_after_bytes;
@@ -2148,12 +2206,15 @@ del_range_2 (from, from_byte, to, to_byte)
   else
     from_byte_1 = from_byte;
 
-  if (! EQ (current_buffer->undo_list, Qt))
+  if (ret_string || ! EQ (current_buffer->undo_list, Qt))
     deletion
       = make_buffer_string_both (from - !!combined_after_bytes,
                                 from_byte_1,
                                 to + combined_after_bytes,
                                 to_byte + combined_after_bytes, 1);
+  else
+    deletion = Qnil;
+
   if (combined_after_bytes)
     /* COMBINED_AFTER_BYTES nonzero means that the above code moved
        the gap.  We must move the gap again to a proper place.  */
@@ -2189,7 +2250,6 @@ del_range_2 (from, from_byte, to, to_byte)
     adjust_point (from - (PT < to ? PT : to),
                  from_byte - (PT_BYTE < to_byte ? PT_BYTE : to_byte));
 
-  /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */
   offset_intervals (current_buffer, from, - nchars_del);
 
   /* Adjust the overlay center as needed.  This must be done after
@@ -2213,10 +2273,10 @@ del_range_2 (from, from_byte, to, to_byte)
   if (GPT_BYTE < GPT)
     abort ();
 
-  if (GPT - BEG < beg_unchanged)
-    beg_unchanged = GPT - BEG;
-  if (Z - GPT < end_unchanged)
-    end_unchanged = Z - GPT;
+  if (GPT - BEG < BEG_UNCHANGED)
+    BEG_UNCHANGED = GPT - BEG;
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
 
   if (combined_after_bytes)
     {
@@ -2229,14 +2289,15 @@ del_range_2 (from, from_byte, to, to_byte)
 
       record_insert (GPT - 1, 1);
 
-      if (Z - GPT < end_unchanged)
-       end_unchanged = Z - GPT;
+      if (Z - GPT < END_UNCHANGED)
+       END_UNCHANGED = Z - GPT;
     }
 
   CHECK_MARKERS ();
 
   evaporate_overlays (from);
-  signal_after_change (from, nchars_del, 0);
+
+  return deletion;
 }
 \f
 /* Call this if you're about to change the region of BUFFER from
@@ -2257,14 +2318,7 @@ modify_region (buffer, start, end)
 
   prepare_to_modify_buffer (start, end, NULL);
 
-  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
-         && overlay_unchanged_modified == OVERLAY_MODIFF))
-    end_unchanged = Z - end;
+  BUF_COMPUTE_UNCHANGED (buffer, start - 1, end);
 
   if (MODIFF <= SAVE_MODIFF)
     record_first_change ();
@@ -2294,7 +2348,11 @@ prepare_to_modify_buffer (start, end, preserve_ptr)
   if (!NILP (current_buffer->read_only))
     Fbarf_if_buffer_read_only ();
 
-  /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */
+  /* Let redisplay consider other windows than selected_window
+     if modifying another buffer.  */
+  if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer)
+    ++windows_or_buffers_changed;
+
   if (BUF_INTERVALS (current_buffer) != 0)
     {
       if (preserve_ptr)
@@ -2592,26 +2650,26 @@ DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
   /* 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)
+       tail = XCDR (tail))
     {
       Lisp_Object elt;
       int thisbeg, thisend, thischange;
 
       /* Extract the info from the next element.  */
-      elt = XCONS (tail)->car;
+      elt = XCAR (tail);
       if (! CONSP (elt))
        continue;
-      thisbeg = XINT (XCONS (elt)->car);
+      thisbeg = XINT (XCAR (elt));
 
-      elt = XCONS (elt)->cdr;
+      elt = XCDR (elt);
       if (! CONSP (elt))
        continue;
-      thisend = XINT (XCONS (elt)->car);
+      thisend = XINT (XCAR (elt));
 
-      elt = XCONS (elt)->cdr;
+      elt = XCDR (elt);
       if (! CONSP (elt))
        continue;
-      thischange = XINT (XCONS (elt)->car);
+      thischange = XINT (XCAR (elt));
 
       /* Merge this range into the accumulated range.  */
       change += thischange;
@@ -2652,5 +2710,11 @@ syms_of_insdel ()
     "Used internally by the `combine-after-change-calls' macro.");
   Vcombine_after_change_calls = Qnil;
 
+  DEFVAR_BOOL ("inhibit-modification-hooks", &inhibit_modification_hooks,
+    "Non-nil means don't run any of the hooks that respond to buffer changes.\n\
+This affects `before-change-functions' and `after-change-functions',\n\
+as well as hooks attached to text properties and overlays.");
+  inhibit_modification_hooks = 0;
+
   defsubr (&Scombine_after_change_execute);
 }