/* undo handling for GNU Emacs.
- Copyright (C) 1990, 1993-1994, 2000-2014 Free Software Foundation,
+ Copyright (C) 1990, 1993-1994, 2000-2016 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 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, 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
#include <config.h>
#include "lisp.h"
-#include "character.h"
#include "buffer.h"
-#include "commands.h"
-#include "window.h"
-
-/* Last buffer for which undo information was recorded. */
-/* BEWARE: This is not traced by the GC, so never dereference it! */
-static struct buffer *last_undo_buffer;
-
-/* Position of point last time we inserted a boundary. */
-static struct buffer *last_boundary_buffer;
-static ptrdiff_t last_boundary_position;
-
-Lisp_Object Qinhibit_read_only;
-
-/* Marker for function call undo list elements. */
-
-Lisp_Object Qapply;
+#include "keyboard.h"
/* The first time a command records something for undo.
it also allocates the undo-boundary object
/* Record point as it was at beginning of this command (if necessary)
and prepare the undo info for recording a change.
+ Prepare the undo info for recording a change. */
+static void
+prepare_record (void)
+{
+ /* Allocate a cons cell to be the undo boundary after this command. */
+ if (NILP (pending_boundary))
+ pending_boundary = Fcons (Qnil, Qnil);
+
+ if (MODIFF <= SAVE_MODIFF)
+ record_first_change ();
+}
+
+/* Record point as it was at beginning of this command.
PT is the position of point that will naturally occur as a result of the
undo record that will be added just after this command terminates. */
-
static void
record_point (ptrdiff_t pt)
{
- bool at_boundary;
-
/* Don't record position of pt when undo_inhibit_record_point holds. */
if (undo_inhibit_record_point)
return;
- /* Allocate a cons cell to be the undo boundary after this command. */
- if (NILP (pending_boundary))
- pending_boundary = Fcons (Qnil, Qnil);
-
- if ((current_buffer != last_undo_buffer)
- /* Don't call Fundo_boundary for the first change. Otherwise we
- risk overwriting last_boundary_position in Fundo_boundary with
- PT of the current buffer and as a consequence not insert an
- undo boundary because last_boundary_position will equal pt in
- the test at the end of the present function (Bug#731). */
- && (MODIFF > SAVE_MODIFF))
- Fundo_boundary ();
- last_undo_buffer = current_buffer;
-
- if (CONSP (BVAR (current_buffer, undo_list)))
- {
- /* Set AT_BOUNDARY only when we have nothing other than
- marker adjustment before undo boundary. */
-
- Lisp_Object tail = BVAR (current_buffer, undo_list), elt;
+ bool at_boundary;
- while (1)
- {
- if (NILP (tail))
- elt = Qnil;
- else
- elt = XCAR (tail);
- if (NILP (elt) || ! (CONSP (elt) && MARKERP (XCAR (elt))))
- break;
- tail = XCDR (tail);
- }
- at_boundary = NILP (elt);
- }
- else
- at_boundary = 1;
+ at_boundary = ! CONSP (BVAR (current_buffer, undo_list))
+ || NILP (XCAR (BVAR (current_buffer, undo_list)));
- if (MODIFF <= SAVE_MODIFF)
- record_first_change ();
+ prepare_record ();
/* If we are just after an undo boundary, and
point wasn't at start of deleted range, record where it was. */
- if (at_boundary
- && current_buffer == last_boundary_buffer
- && last_boundary_position != pt)
+ if (at_boundary)
bset_undo_list (current_buffer,
- Fcons (make_number (last_boundary_position),
+ Fcons (make_number (pt),
BVAR (current_buffer, undo_list)));
}
if (EQ (BVAR (current_buffer, undo_list), Qt))
return;
- record_point (beg);
+ prepare_record ();
/* If this is following another insertion and consecutive with it
in the buffer, combine the two. */
Fcons (Fcons (lbeg, lend), BVAR (current_buffer, undo_list)));
}
-/* Record that a deletion is about to take place,
- of the characters in STRING, at location BEG. */
+/* Record the fact that markers in the region of FROM, TO are about to
+ be adjusted. This is done only when a marker points within text
+ being deleted, because that's the only case where an automatic
+ marker adjustment won't be inverted automatically by undoing the
+ buffer modification. */
+static void
+record_marker_adjustments (ptrdiff_t from, ptrdiff_t to)
+{
+ Lisp_Object marker;
+ register struct Lisp_Marker *m;
+ register ptrdiff_t charpos, adjustment;
+
+ /* Allocate a cons cell to be the undo boundary after this command. */
+ if (NILP (pending_boundary))
+ pending_boundary = Fcons (Qnil, Qnil);
+
+ for (m = BUF_MARKERS (current_buffer); m; m = m->next)
+ {
+ charpos = m->charpos;
+ eassert (charpos <= Z);
+
+ if (from <= charpos && charpos <= to)
+ {
+ /* insertion_type nil 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.
+
+ insertion_type t 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. */
+ adjustment = (m->insertion_type ? to : from) - charpos;
+
+ if (adjustment)
+ {
+ XSETMISC (marker, m);
+ bset_undo_list
+ (current_buffer,
+ Fcons (Fcons (marker, make_number (adjustment)),
+ BVAR (current_buffer, undo_list)));
+ }
+ }
+ }
+}
+
+/* Record that a deletion is about to take place, of the characters in
+ STRING, at location BEG. Optionally record adjustments for markers
+ in the region STRING occupies in the current buffer. */
void
-record_delete (ptrdiff_t beg, Lisp_Object string)
+record_delete (ptrdiff_t beg, Lisp_Object string, bool record_markers)
{
Lisp_Object sbeg;
if (EQ (BVAR (current_buffer, undo_list), Qt))
return;
+ if (point_before_last_command_or_undo != beg
+ && buffer_before_last_command_or_undo == current_buffer)
+ record_point (point_before_last_command_or_undo);
+
if (PT == beg + SCHARS (string))
{
XSETINT (sbeg, -beg);
- record_point (PT);
+ prepare_record ();
}
else
{
XSETFASTINT (sbeg, beg);
- record_point (beg);
+ prepare_record ();
}
- bset_undo_list
- (current_buffer,
- Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list)));
-}
-
-/* Record the fact that MARKER is about to be adjusted by ADJUSTMENT.
- This is done only when a marker points within text being deleted,
- because that's the only case where an automatic marker adjustment
- won't be inverted automatically by undoing the buffer modification. */
-
-void
-record_marker_adjustment (Lisp_Object marker, ptrdiff_t adjustment)
-{
- if (EQ (BVAR (current_buffer, undo_list), Qt))
- return;
-
- /* Allocate a cons cell to be the undo boundary after this command. */
- if (NILP (pending_boundary))
- pending_boundary = Fcons (Qnil, Qnil);
-
- if (current_buffer != last_undo_buffer)
- Fundo_boundary ();
- last_undo_buffer = current_buffer;
+ /* primitive-undo assumes marker adjustments are recorded
+ immediately before the deletion is recorded. See bug 16818
+ discussion. */
+ if (record_markers)
+ record_marker_adjustments (beg, beg + SCHARS (string));
bset_undo_list
(current_buffer,
- Fcons (Fcons (marker, make_number (adjustment)),
- BVAR (current_buffer, undo_list)));
+ Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list)));
}
/* Record that a replacement is about to take place,
void
record_change (ptrdiff_t beg, ptrdiff_t length)
{
- record_delete (beg, make_buffer_string (beg, beg + length, 1));
+ record_delete (beg, make_buffer_string (beg, beg + length, true), false);
record_insert (beg, length);
}
\f
if (EQ (BVAR (current_buffer, undo_list), Qt))
return;
- if (current_buffer != last_undo_buffer)
- Fundo_boundary ();
- last_undo_buffer = current_buffer;
-
if (base_buffer->base_buffer)
base_buffer = base_buffer->base_buffer;
Lisp_Object buffer)
{
Lisp_Object lbeg, lend, entry;
- struct buffer *obuf = current_buffer, *buf = XBUFFER (buffer);
- bool boundary = 0;
+ struct buffer *buf = XBUFFER (buffer);
if (EQ (BVAR (buf, undo_list), Qt))
return;
if (NILP (pending_boundary))
pending_boundary = Fcons (Qnil, Qnil);
- if (buf != last_undo_buffer)
- boundary = 1;
- last_undo_buffer = buf;
-
- /* Switch temporarily to the buffer that was changed. */
- current_buffer = buf;
-
- if (boundary)
- Fundo_boundary ();
-
if (MODIFF <= SAVE_MODIFF)
record_first_change ();
entry = Fcons (Qnil, Fcons (prop, Fcons (value, Fcons (lbeg, lend))));
bset_undo_list (current_buffer,
Fcons (entry, BVAR (current_buffer, undo_list)));
-
- current_buffer = obuf;
}
DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0,
bset_undo_list (current_buffer,
Fcons (Qnil, BVAR (current_buffer, undo_list)));
}
- last_boundary_position = PT;
- last_boundary_buffer = current_buffer;
+
+ Fset (Qundo_auto__last_boundary_cause, Qexplicit);
+ point_before_last_command_or_undo = PT;
+ buffer_before_last_command_or_undo = current_buffer;
+
return Qnil;
}
&& !NILP (Vundo_outer_limit_function))
{
Lisp_Object tem;
- struct buffer *temp = last_undo_buffer;
/* Normally the function this calls is undo-outer-limit-truncate. */
tem = call1 (Vundo_outer_limit_function, make_number (size_so_far));
unbind_to (count, Qnil);
return;
}
- /* That function probably used the minibuffer, and if so, that
- changed last_undo_buffer. Change it back so that we don't
- force next change to make an undo boundary here. */
- last_undo_buffer = temp;
}
if (CONSP (next))
syms_of_undo (void)
{
DEFSYM (Qinhibit_read_only, "inhibit-read-only");
+ DEFSYM (Qundo_auto__last_boundary_cause, "undo-auto--last-boundary-cause");
+ DEFSYM (Qexplicit, "explicit");
+
+ /* Marker for function call undo list elements. */
DEFSYM (Qapply, "apply");
pending_boundary = Qnil;
staticpro (&pending_boundary);
- last_undo_buffer = NULL;
- last_boundary_buffer = NULL;
-
defsubr (&Sundo_boundary);
DEFVAR_INT ("undo-limit", undo_limit,
DEFVAR_BOOL ("undo-inhibit-record-point", undo_inhibit_record_point,
doc: /* Non-nil means do not record `point' in `buffer-undo-list'. */);
- undo_inhibit_record_point = 0;
+ undo_inhibit_record_point = false;
}