]> code.delx.au - gnu-emacs/blobdiff - src/insdel.c
(single_submenu): Set up help string.
[gnu-emacs] / src / insdel.c
index 87f0a7898dc88401a2198f004cea98b1542aa4d4..3cab83711baa007a0b3d32551b2f4fd8799bfbc3 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.  */
@@ -734,17 +715,13 @@ copy_text (from_addr, to_addr, nbytes,
       while (nbytes > 0)
        {
          int c = *from_addr++;
-         unsigned char workbuf[4], *str;
-         int len;
 
          if (c < 0400
              && (c >= 0240
                  || (c >= 0200 && !NILP (Vnonascii_translation_table))))
            {
              c = unibyte_char_to_multibyte (c);
-             len = CHAR_STRING (c, workbuf, str);
-             bcopy (str, to_addr, len);
-             to_addr += len;
+             to_addr += CHAR_STRING (c, to_addr);
              nbytes--;
            }
          else
@@ -801,6 +778,7 @@ insert (string, nbytes)
       int opoint = PT;
       insert_1 (string, nbytes, 0, 1, 0);
       signal_after_change (opoint, 0, PT - opoint);
+      update_compositions (opoint, PT, CHECK_BORDER);
     }
 }
 
@@ -816,6 +794,7 @@ insert_and_inherit (string, nbytes)
       int opoint = PT;
       insert_1 (string, nbytes, 1, 1, 0);
       signal_after_change (opoint, 0, PT - opoint);
+      update_compositions (opoint, PT, CHECK_BORDER);
     }
 }
 
@@ -825,16 +804,15 @@ void
 insert_char (c)
      int c;
 {
-  unsigned char workbuf[4], *str;
+  unsigned char str[MAX_MULTIBYTE_LENGTH];
   int len;
 
   if (! NILP (current_buffer->enable_multibyte_characters))
-    len = CHAR_STRING (c, workbuf, str);
+    len = CHAR_STRING (c, str);
   else
     {
       len = 1;
-      workbuf[0] = c;
-      str = workbuf;
+      str[0] = c;
     }
 
   insert (str, len);
@@ -865,6 +843,7 @@ insert_before_markers (string, nbytes)
 
       insert_1 (string, nbytes, 0, 1, 1);
       signal_after_change (opoint, 0, PT - opoint);
+      update_compositions (opoint, PT, CHECK_BORDER);
     }
 }
 
@@ -881,6 +860,7 @@ insert_before_markers_and_inherit (string, nbytes)
 
       insert_1 (string, nbytes, 1, 1, 1);
       signal_after_change (opoint, 0, PT - opoint);
+      update_compositions (opoint, PT, CHECK_BORDER);
     }
 }
 
@@ -907,30 +887,40 @@ 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))
+
+  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
@@ -944,14 +934,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]))
     {
@@ -959,33 +962,29 @@ 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;
+
+      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;
 
-  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
@@ -1027,7 +1026,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);
 }
 
@@ -1041,8 +1039,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)
 
@@ -1057,7 +1055,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))
@@ -1148,15 +1145,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;
@@ -1193,6 +1187,7 @@ insert_from_string (string, pos, pos_byte, length, length_byte, inherit)
   insert_from_string_1 (string, pos, pos_byte, length, length_byte,
                        inherit, 0);
   signal_after_change (opoint, 0, PT - opoint);
+  update_compositions (opoint, PT, CHECK_BORDER);
 }
 
 /* Like `insert_from_string' except that all markers pointing
@@ -1209,6 +1204,7 @@ insert_from_string_before_markers (string, pos, pos_byte,
   insert_from_string_1 (string, pos, pos_byte, length, length_byte,
                        inherit, 1);
   signal_after_change (opoint, 0, PT - opoint);
+  update_compositions (opoint, PT, CHECK_BORDER);
 }
 
 /* Subroutine of the insertion functions above.  */
@@ -1220,11 +1216,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
@@ -1245,7 +1239,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;
 
@@ -1336,7 +1330,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;
@@ -1381,6 +1374,7 @@ insert_from_buffer (buf, charpos, nchars, inherit)
 
   insert_from_buffer_1 (buf, charpos, nchars, inherit);
   signal_after_change (opoint, 0, PT - opoint);
+  update_compositions (opoint, PT, CHECK_BORDER);
 }
 
 static void
@@ -1396,7 +1390,6 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
   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
@@ -1546,10 +1539,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.  */
@@ -1628,8 +1619,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.  */
@@ -1658,7 +1649,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;
@@ -1701,16 +1692,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);
 
@@ -1728,8 +1715,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 ();
 
@@ -1782,7 +1769,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;
@@ -1861,10 +1847,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);
@@ -1886,8 +1872,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
@@ -1980,7 +1966,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--
@@ -1989,7 +1974,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)
@@ -2010,8 +1994,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);
@@ -2022,6 +2006,7 @@ replace_range (from, to, new, prepare, inherit, markers)
   UNGCPRO;
 
   signal_after_change (from, nchars_del, GPT - from);
+  update_compositions (from, GPT, CHECK_BORDER);
 }
 \f
 /* Delete characters in current buffer
@@ -2032,16 +2017,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)
@@ -2050,7 +2038,7 @@ del_range_1 (from, to, prepare)
     to = ZV;
 
   if (to <= from)
-    return;
+    return Qnil;
 
   if (prepare)
     {
@@ -2062,8 +2050,12 @@ 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);
+  update_compositions (from, from, CHECK_HEAD);
+  UNGCPRO;
+  return deletion;
 }
 
 /* Like del_range_1 but args are byte positions, not char positions.  */
@@ -2099,8 +2091,9 @@ 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);
+  update_compositions (from, from, CHECK_HEAD);
 }
 
 /* Like del_range_1, but positions are specified both as charpos
@@ -2137,17 +2130,19 @@ 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);
+  update_compositions (from, from, CHECK_HEAD);
 }
 
 /* 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;
@@ -2178,12 +2173,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.  */
@@ -2219,7 +2217,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
@@ -2243,10 +2240,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)
     {
@@ -2259,13 +2256,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);
+
+  return deletion;
 }
 \f
 /* Call this if you're about to change the region of BUFFER from
@@ -2286,14 +2285,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 ();
@@ -2323,7 +2315,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)
@@ -2621,26 +2617,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;
@@ -2663,6 +2659,7 @@ DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
   record_unwind_protect (Fcombine_after_change_execute_1,
                         Vcombine_after_change_calls);
   signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
+  update_compositions (begpos, endpos, CHECK_ALL);
 
   return unbind_to (count, Qnil);
 }
@@ -2681,5 +2678,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);
 }