use dynwind_begin and dynwind_end
[bpt/emacs.git] / src / insdel.c
index ed68426..c6d89da 100644 (file)
@@ -1,6 +1,6 @@
 /* Buffer insertion/deletion and gap motion for GNU Emacs.
-   Copyright (C) 1985-1986, 1993-1995, 1997-2013 Free Software
-   Foundation, Inc.
+
+Copyright (C) 1985-1986, 1993-1995, 1997-2014 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -214,9 +214,8 @@ void
 adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
                           ptrdiff_t to, ptrdiff_t to_byte)
 {
-  Lisp_Object marker;
-  register struct Lisp_Marker *m;
-  register ptrdiff_t charpos;
+  struct Lisp_Marker *m;
+  ptrdiff_t charpos;
 
   for (m = BUF_MARKERS (current_buffer); m; m = m->next)
     {
@@ -233,34 +232,9 @@ adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
       /* Here's the case where a marker is inside text being deleted.  */
       else if (charpos > from)
        {
-         if (! m->insertion_type)
-           { /* Normal markers will end up at the beginning of the
-              re-inserted text after undoing a deletion, and must be
-              adjusted to move them to the correct place.  */
-             XSETMISC (marker, m);
-             record_marker_adjustment (marker, from - charpos);
-           }
-         else if (charpos < to)
-           { /* Before-insertion markers will automatically move forward
-              upon re-inserting the deleted text, so we have to arrange
-              for them to move backward to the correct position.  */
-             XSETMISC (marker, m);
-             record_marker_adjustment (marker, to - charpos);
-           }
          m->charpos = from;
          m->bytepos = from_byte;
        }
-      /* Here's the case where a before-insertion marker is immediately
-        before the deleted region.  */
-      else if (charpos == from && m->insertion_type)
-       {
-         /* Undoing the change uses normal insertion, which will
-            incorrectly make MARKER move forward, so we arrange for it
-            to then move backward to the correct place at the beginning
-            of the deleted region.  */
-         XSETMISC (marker, m);
-         record_marker_adjustment (marker, to - from);
-       }
     }
 }
 
@@ -727,7 +701,7 @@ count_combining_after (const unsigned char *string,
        (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) */
+  if (length > 0 && ASCII_CHAR_P (string[length - 1])) /* case (1) */
     return 0;
   if (pos_byte == Z_BYTE)      /* case (2) */
     return 0;
@@ -827,7 +801,7 @@ insert_1_both (const char *string,
 
   eassert (GPT <= GPT_BYTE);
 
-  /* The insert may have been in the unchanged region, so check again. */
+  /* The insert may have been in the unchanged region, so check again.  */
   if (Z - GPT < END_UNCHANGED)
     END_UNCHANGED = Z - GPT;
 
@@ -956,7 +930,7 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
 
   eassert (GPT <= GPT_BYTE);
 
-  /* The insert may have been in the unchanged region, so check again. */
+  /* The insert may have been in the unchanged region, so check again.  */
   if (Z - GPT < END_UNCHANGED)
     END_UNCHANGED = Z - GPT;
 
@@ -988,12 +962,16 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
 void
 insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail)
 {
-  int ins_charpos = GPT;
-  int ins_bytepos = GPT_BYTE;
+  ptrdiff_t ins_charpos = GPT, ins_bytepos = GPT_BYTE;
 
   if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
     nchars = nbytes;
 
+  /* No need to call prepare_to_modify_buffer, since this is called
+     from places that replace some region with a different text, so
+     prepare_to_modify_buffer was already called by the deletion part
+     of this dance.  */
+  invalidate_buffer_caches (current_buffer, GPT, GPT);
   record_insert (GPT, nchars);
   MODIFF++;
 
@@ -1057,6 +1035,9 @@ insert_from_buffer_1 (struct buffer *buf,
   ptrdiff_t outgoing_nbytes = incoming_nbytes;
   INTERVAL intervals;
 
+  if (nchars == 0)
+    return;
+
   /* Make OUTGOING_NBYTES describe the text
      as it will be inserted in this buffer.  */
 
@@ -1146,7 +1127,7 @@ insert_from_buffer_1 (struct buffer *buf,
 
   eassert (GPT <= GPT_BYTE);
 
-  /* The insert may have been in the unchanged region, so check again. */
+  /* The insert may have been in the unchanged region, so check again.  */
   if (Z - GPT < END_UNCHANGED)
     END_UNCHANGED = Z - GPT;
 
@@ -1211,12 +1192,9 @@ adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte,
     adjust_markers_for_insert (from, from_byte,
                               from + len, from_byte + len_byte, 0);
 
-  if (! EQ (BVAR (current_buffer, undo_list), Qt))
-    {
-      if (nchars_del > 0)
-       record_delete (from, prev_text);
-      record_insert (from, len);
-    }
+  if (nchars_del > 0)
+    record_delete (from, prev_text, false);
+  record_insert (from, len);
 
   if (len > nchars_del)
     adjust_overlays_for_insert (from, len - nchars_del);
@@ -1373,14 +1351,14 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
     emacs_abort ();
 #endif
 
-  if (! EQ (BVAR (current_buffer, undo_list), Qt))
+  /* Record the insertion first, so that when we undo,
+     the deletion will be undone first.  Thus, undo
+     will insert before deleting, and thus will keep
+     the markers before and after this text separate.  */
+  if (!NILP (deletion))
     {
-      /* Record the insertion first, so that when we undo,
-        the deletion will be undone first.  Thus, undo
-        will insert before deleting, and thus will keep
-        the markers before and after this text separate.  */
       record_insert (from + SCHARS (deletion), inschars);
-      record_delete (from, deletion);
+      record_delete (from, deletion, false);
     }
 
   GAP_SIZE -= outgoing_insbytes;
@@ -1712,14 +1690,14 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
   else
     deletion = Qnil;
 
-  /* Relocate all markers pointing into the new, larger gap
-     to point at the end of the text before the gap.
-     Do this before recording the deletion,
-     so that undo handles this after reinserting the text.  */
+  /* Record marker adjustments, and text deletion into undo
+     history.  */
+  record_delete (from, deletion, true);
+
+  /* Relocate all markers pointing into the new, larger gap to point
+     at the end of the text before the gap.  */
   adjust_markers_for_delete (from, from_byte, to, to_byte);
 
-  if (! EQ (BVAR (current_buffer, undo_list), Qt))
-    record_delete (from, deletion);
   MODIFF++;
   CHARS_MODIFF = MODIFF;
 
@@ -1760,31 +1738,28 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
   return deletion;
 }
 
-/* Call this if you're about to change the region of current buffer
+/* Call this if you're about to change the text of current buffer
    from character positions START to END.  This checks the read-only
    properties of the region, calls the necessary modification hooks,
    and warns the next redisplay that it should pay attention to that
-   area.
-
-   If PRESERVE_CHARS_MODIFF, do not update CHARS_MODIFF.
-   Otherwise set CHARS_MODIFF to the new value of MODIFF.  */
+   area.  */
 
 void
-modify_region_1 (ptrdiff_t start, ptrdiff_t end, bool preserve_chars_modiff)
+modify_text (ptrdiff_t start, ptrdiff_t end)
 {
   prepare_to_modify_buffer (start, end, NULL);
 
   BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end);
-
   if (MODIFF <= SAVE_MODIFF)
     record_first_change ();
   MODIFF++;
-  if (! preserve_chars_modiff)
-    CHARS_MODIFF = MODIFF;
+  CHARS_MODIFF = MODIFF;
 
   bset_point_before_scroll (current_buffer, Qnil);
 }
 
+Lisp_Object Qregion_extract_function;
+
 /* Check that it is okay to modify the buffer between START and END,
    which are char positions.
 
@@ -1796,19 +1771,15 @@ modify_region_1 (ptrdiff_t start, ptrdiff_t end, bool preserve_chars_modiff)
    by holding its value temporarily in a marker.  */
 
 void
-prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
-                         ptrdiff_t *preserve_ptr)
+prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
+                           ptrdiff_t *preserve_ptr)
 {
   struct buffer *base_buffer;
 
   if (!NILP (BVAR (current_buffer, read_only)))
     Fbarf_if_buffer_read_only ();
 
-  /* If we're modifying the buffer other than shown in a selected window,
-     let redisplay consider other windows if this buffer is visible.  */
-  if (XBUFFER (XWINDOW (selected_window)->contents) != current_buffer
-      && buffer_window_count (current_buffer))
-    ++windows_or_buffers_changed;
+  bset_redisplay (current_buffer);
 
   if (buffer_intervals (current_buffer))
     {
@@ -1833,54 +1804,93 @@ prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
   else
     base_buffer = current_buffer;
 
-#ifdef CLASH_DETECTION
+  if (inhibit_modification_hooks)
+    return;
+
   if (!NILP (BVAR (base_buffer, file_truename))
       /* Make binding buffer-file-name to nil effective.  */
       && !NILP (BVAR (base_buffer, filename))
       && SAVE_MODIFF >= MODIFF)
     lock_file (BVAR (base_buffer, file_truename));
-#else
-  /* At least warn if this file has changed on disk since it was visited.  */
-  if (!NILP (BVAR (base_buffer, filename))
-      && SAVE_MODIFF >= MODIFF
-      && NILP (Fverify_visited_file_modtime (Fcurrent_buffer ()))
-      && !NILP (Ffile_exists_p (BVAR (base_buffer, filename))))
-    call1 (intern ("ask-user-about-supersession-threat"),
-          BVAR (base_buffer,filename));
-#endif /* not CLASH_DETECTION */
 
   /* If `select-active-regions' is non-nil, save the region text.  */
+  /* FIXME: Move this to Elisp (via before-change-functions).  */
   if (!NILP (BVAR (current_buffer, mark_active))
-      && !inhibit_modification_hooks
       && XMARKER (BVAR (current_buffer, mark))->buffer
       && NILP (Vsaved_region_selection)
       && (EQ (Vselect_active_regions, Qonly)
          ? EQ (CAR_SAFE (Vtransient_mark_mode), Qonly)
          : (!NILP (Vselect_active_regions)
             && !NILP (Vtransient_mark_mode))))
-    {
-      ptrdiff_t b = marker_position (BVAR (current_buffer, mark));
-      ptrdiff_t e = PT;
-      if (b < e)
-       Vsaved_region_selection = make_buffer_string (b, e, 0);
-      else if (b > e)
-       Vsaved_region_selection = make_buffer_string (e, b, 0);
-    }
+    Vsaved_region_selection
+      = call1 (Fsymbol_value (Qregion_extract_function), Qnil);
 
   signal_before_change (start, end, preserve_ptr);
+  Vdeactivate_mark = Qt;
+}
 
-  if (current_buffer->newline_cache)
-    invalidate_region_cache (current_buffer,
-                             current_buffer->newline_cache,
-                             start - BEG, Z - end);
-  if (current_buffer->width_run_cache)
-    invalidate_region_cache (current_buffer,
-                             current_buffer->width_run_cache,
-                             start - BEG, Z - end);
+/* Like above, but called when we know that the buffer text
+   will be modified and region caches should be invalidated.  */
 
-  Vdeactivate_mark = Qt;
+void
+prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
+                         ptrdiff_t *preserve_ptr)
+{
+  prepare_to_modify_buffer_1 (start, end, preserve_ptr);
+  invalidate_buffer_caches (current_buffer, start, end);
 }
-\f
+
+/* Invalidate the caches maintained by the buffer BUF, if any, for the
+   region between buffer positions START and END.  */
+void
+invalidate_buffer_caches (struct buffer *buf, ptrdiff_t start, ptrdiff_t end)
+{
+  /* Indirect buffers usually have their caches set to NULL, but we
+     need to consider the caches of their base buffer.  */
+  if (buf->base_buffer)
+    buf = buf->base_buffer;
+  /* The bidi_paragraph_cache must be invalidated first, because doing
+     so might need to use the newline_cache (via find_newline_no_quit,
+     see below).  */
+  if (buf->bidi_paragraph_cache)
+    {
+      if (start != end
+         && start > BUF_BEG (buf))
+       {
+         /* If we are deleting or replacing characters, we could
+            create a paragraph start, because all of the characters
+            from START to the beginning of START's line are
+            whitespace.  Therefore, we must extend the region to be
+            invalidated up to the newline before START.  */
+         ptrdiff_t line_beg = start;
+         ptrdiff_t start_byte = buf_charpos_to_bytepos (buf, start);
+
+         if (BUF_FETCH_BYTE (buf, start_byte - 1) != '\n')
+           {
+             struct buffer *old = current_buffer;
+
+             set_buffer_internal (buf);
+
+             line_beg = find_newline_no_quit (start, start_byte, -1,
+                                              &start_byte);
+             set_buffer_internal (old);
+           }
+         start = line_beg - (line_beg > BUF_BEG (buf));
+       }
+      invalidate_region_cache (buf,
+                              buf->bidi_paragraph_cache,
+                              start - BUF_BEG (buf), BUF_Z (buf) - end);
+    }
+  if (buf->newline_cache)
+    invalidate_region_cache (buf,
+                             buf->newline_cache,
+                             start - BUF_BEG (buf), BUF_Z (buf) - end);
+  if (buf->width_run_cache)
+    invalidate_region_cache (buf,
+                             buf->width_run_cache,
+                             start - BUF_BEG (buf), BUF_Z (buf) - end);
+}
+
 /* These macros work with an argument named `preserve_ptr'
    and a local variable named `preserve_marker'.  */
 
@@ -1913,12 +1923,18 @@ prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
    VARIABLE is the variable to maybe set to nil.
    NO-ERROR-FLAG is nil if there was an error,
    anything else meaning no error (so this function does nothing).  */
-static Lisp_Object
-reset_var_on_error (Lisp_Object val)
+struct rvoe_arg
 {
-  if (NILP (XCDR (val)))
-    Fset (XCAR (val), Qnil);
-  return Qnil;
+  Lisp_Object *location;
+  bool errorp;
+};
+
+static void
+reset_var_on_error (void *ptr)
+{
+  struct rvoe_arg *p = ptr;
+  if (p->errorp)
+    *p->location = Qnil;
 }
 
 /* Signal a change to the buffer immediately before it happens.
@@ -1935,10 +1951,8 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
   Lisp_Object start_marker, end_marker;
   Lisp_Object preserve_marker;
   struct gcpro gcpro1, gcpro2, gcpro3;
-  ptrdiff_t count = SPECPDL_INDEX ();
-
-  if (inhibit_modification_hooks)
-    return;
+  dynwind_begin ();
+  struct rvoe_arg rvoe_arg;
 
   start = make_number (start_int);
   end = make_number (end_int);
@@ -1950,7 +1964,7 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
   specbind (Qinhibit_modification_hooks, Qt);
 
   /* If buffer is unmodified, run a special hook for that case.  The
-   check for Vfirst_change_hook is just a minor optimization. */
+   check for Vfirst_change_hook is just a minor optimization.  */
   if (SAVE_MODIFF >= MODIFF
       && !NILP (Vfirst_change_hook))
     {
@@ -1963,13 +1977,14 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
   if (!NILP (Vbefore_change_functions))
     {
       Lisp_Object args[3];
-      Lisp_Object rvoe_arg = Fcons (Qbefore_change_functions, Qnil);
+      rvoe_arg.location = &Vbefore_change_functions;
+      rvoe_arg.errorp = 1;
 
       PRESERVE_VALUE;
       PRESERVE_START_END;
 
       /* Mark before-change-functions to be reset to nil in case of error.  */
-      record_unwind_protect (reset_var_on_error, rvoe_arg);
+      record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
 
       /* Actually run the hook functions.  */
       args[0] = Qbefore_change_functions;
@@ -1978,7 +1993,7 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
       Frun_hook_with_args (3, args);
 
       /* There was no error: unarm the reset_on_error.  */
-      XSETCDR (rvoe_arg, Qt);
+      rvoe_arg.errorp = 0;
     }
 
   if (buffer_has_overlays ())
@@ -1988,14 +2003,10 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
                                   FETCH_START, FETCH_END, Qnil);
     }
 
-  if (! NILP (start_marker))
-    free_marker (start_marker);
-  if (! NILP (end_marker))
-    free_marker (end_marker);
   RESTORE_VALUE;
   UNGCPRO;
 
-  unbind_to (count, Qnil);
+  dynwind_end ();
 }
 
 /* Signal a change immediately after it happens.
@@ -2008,9 +2019,13 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
 void
 signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
 {
-  ptrdiff_t count = SPECPDL_INDEX ();
-  if (inhibit_modification_hooks)
+  dynwind_begin ();
+  struct rvoe_arg rvoe_arg;
+
+  if (inhibit_modification_hooks) {
+    dynwind_end ();
     return;
+  }
 
   /* If we are deferring calls to the after-change functions
      and there are no before-change functions,
@@ -2031,6 +2046,7 @@ signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
        = Fcons (elt, combine_after_change_list);
       combine_after_change_buffer = Fcurrent_buffer ();
 
+      dynwind_end ();
       return;
     }
 
@@ -2042,10 +2058,11 @@ signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
   if (!NILP (Vafter_change_functions))
     {
       Lisp_Object args[4];
-      Lisp_Object rvoe_arg = Fcons (Qafter_change_functions, Qnil);
+      rvoe_arg.location = &Vafter_change_functions;
+      rvoe_arg.errorp = 1;
 
       /* Mark after-change-functions to be reset to nil in case of error.  */
-      record_unwind_protect (reset_var_on_error, rvoe_arg);
+      record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
 
       /* Actually run the hook functions.  */
       args[0] = Qafter_change_functions;
@@ -2055,7 +2072,7 @@ signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
       Frun_hook_with_args (4, args);
 
       /* There was no error: unarm the reset_on_error.  */
-      XSETCDR (rvoe_arg, Qt);
+      rvoe_arg.errorp = 0;
     }
 
   if (buffer_has_overlays ())
@@ -2072,14 +2089,13 @@ signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
     report_interval_modification (make_number (charpos),
                                  make_number (charpos + lenins));
 
-  unbind_to (count, Qnil);
+  dynwind_end ();
 }
 
-static Lisp_Object
+static void
 Fcombine_after_change_execute_1 (Lisp_Object val)
 {
   Vcombine_after_change_calls = val;
-  return val;
 }
 
 DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
@@ -2087,13 +2103,15 @@ DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
        doc: /* This function is for use internally in the function `combine-after-change-calls'.  */)
   (void)
 {
-  ptrdiff_t count = SPECPDL_INDEX ();
+  dynwind_begin ();
   ptrdiff_t beg, end, change;
   ptrdiff_t begpos, endpos;
   Lisp_Object tail;
 
-  if (NILP (combine_after_change_list))
+  if (NILP (combine_after_change_list)) {
+    dynwind_end ();
     return Qnil;
+  }
 
   /* It is rare for combine_after_change_buffer to be invalid, but
      possible.  It can happen when combine-after-change-calls is
@@ -2103,6 +2121,7 @@ DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
       || !BUFFER_LIVE_P (XBUFFER (combine_after_change_buffer)))
     {
       combine_after_change_list = Qnil;
+      dynwind_end ();
       return Qnil;
     }
 
@@ -2164,12 +2183,15 @@ DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
   signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
   update_compositions (begpos, endpos, CHECK_ALL);
 
-  return unbind_to (count, Qnil);
+  dynwind_end ();
+  return Qnil;
 }
 \f
 void
 syms_of_insdel (void)
 {
+#include "insdel.x"
+
   staticpro (&combine_after_change_list);
   staticpro (&combine_after_change_buffer);
   combine_after_change_list = Qnil;
@@ -2186,5 +2208,5 @@ as well as hooks attached to text properties and overlays.  */);
   inhibit_modification_hooks = 0;
   DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks");
 
-  defsubr (&Scombine_after_change_execute);
+  DEFSYM (Qregion_extract_function, "region-extract-function");
 }