/* undo handling for GNU Emacs.
- Copyright (C) 1990, 1993, 1994, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1990, 1993, 1994, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
This file is part of GNU Emacs.
-GNU Emacs is free software; you can redistribute it and/or modify
+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 2, 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
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, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include "lisp.h"
#include "buffer.h"
#include "commands.h"
+#include "window.h"
/* Limits controlling how much undo information to keep. */
Lisp_Object Vundo_outer_limit_function;
/* Last buffer for which undo information was recorded. */
-Lisp_Object last_undo_buffer;
+/* BEWARE: This is not traced by the GC, so never dereference it! */
+struct buffer *last_undo_buffer;
+
+/* Position of point last time we inserted a boundary. */
+struct buffer *last_boundary_buffer;
+EMACS_INT last_boundary_position;
Lisp_Object Qinhibit_read_only;
an undo-boundary. */
Lisp_Object pending_boundary;
+/* Nonzero means do not record point in record_point. */
+
+int undo_inhibit_record_point;
+
/* Record point as it was at beginning of this command (if necessary)
- And prepare the undo info for recording a change.
+ and prepare the undo info for recording a change.
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. */
{
int 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 (!BUFFERP (last_undo_buffer)
- || current_buffer != XBUFFER (last_undo_buffer))
+ 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 ();
- XSETBUFFER (last_undo_buffer, current_buffer);
+ last_undo_buffer = current_buffer;
if (CONSP (current_buffer->undo_list))
{
/* If we are just after an undo boundary, and
point wasn't at start of deleted range, record where it was. */
if (at_boundary
- && last_point_position != pt
- /* If we're called from batch mode, this could be nil. */
- && BUFFERP (last_point_position_buffer)
- && current_buffer == XBUFFER (last_point_position_buffer))
+ && current_buffer == last_boundary_buffer
+ && last_boundary_position != pt)
current_buffer->undo_list
- = Fcons (make_number (last_point_position), current_buffer->undo_list);
+ = Fcons (make_number (last_boundary_position), current_buffer->undo_list);
}
/* Record an insertion that just happened or is about to happen,
if (NILP (pending_boundary))
pending_boundary = Fcons (Qnil, Qnil);
- if (!BUFFERP (last_undo_buffer)
- || current_buffer != XBUFFER (last_undo_buffer))
+ if (current_buffer != last_undo_buffer)
Fundo_boundary ();
- XSETBUFFER (last_undo_buffer, current_buffer);
+ last_undo_buffer = current_buffer;
current_buffer->undo_list
= Fcons (Fcons (marker, make_number (adjustment)),
if (EQ (current_buffer->undo_list, Qt))
return;
- if (!BUFFERP (last_undo_buffer)
- || current_buffer != XBUFFER (last_undo_buffer))
+ if (current_buffer != last_undo_buffer)
Fundo_boundary ();
- XSETBUFFER (last_undo_buffer, current_buffer);
+ last_undo_buffer = current_buffer;
if (base_buffer->base_buffer)
base_buffer = base_buffer->base_buffer;
Lisp_Object prop, value, buffer;
{
Lisp_Object lbeg, lend, entry;
- struct buffer *obuf = current_buffer;
+ struct buffer *obuf = current_buffer, *buf = XBUFFER (buffer);
int boundary = 0;
- if (EQ (XBUFFER (buffer)->undo_list, Qt))
+ if (EQ (buf->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 (!EQ (buffer, last_undo_buffer))
+ if (buf != last_undo_buffer)
boundary = 1;
- last_undo_buffer = buffer;
+ last_undo_buffer = buf;
/* Switch temporarily to the buffer that was changed. */
- current_buffer = XBUFFER (buffer);
+ current_buffer = buf;
if (boundary)
Fundo_boundary ();
else
current_buffer->undo_list = Fcons (Qnil, current_buffer->undo_list);
}
+ last_boundary_position = PT;
+ last_boundary_buffer = current_buffer;
return Qnil;
}
&& size_so_far > XINT (Vundo_outer_limit)
&& !NILP (Vundo_outer_limit_function))
{
- Lisp_Object temp = last_undo_buffer;
+ Lisp_Object tem;
+ struct buffer *temp = last_undo_buffer;
/* Normally the function this calls is undo-outer-limit-truncate. */
- if (! NILP (call1 (Vundo_outer_limit_function,
- make_number (size_so_far))))
+ tem = call1 (Vundo_outer_limit_function, make_number (size_so_far));
+ if (! NILP (tem))
{
/* The function is responsible for making
any desired changes in buffer-undo-list. */
Lisp_Object next;
int count = SPECPDL_INDEX ();
register int arg;
+ Lisp_Object oldlist;
+ int did_apply = 0;
#if 0 /* This is a good feature, but would make undo-start
unable to do what is expected. */
arg = XINT (n);
next = Qnil;
GCPRO2 (next, list);
+ /* I don't think we need to gcpro oldlist, as we use it only
+ to check for EQ. ++kfs */
/* In a writable buffer, enable undoing read-only text that is so
because of text properties. */
/* Don't let `intangible' properties interfere with undo. */
specbind (Qinhibit_point_motion_hooks, Qt);
+ oldlist = current_buffer->undo_list;
+
while (arg > 0)
{
while (CONSP (list))
beg = Fcar (cdr);
end = Fcdr (cdr);
+ if (XINT (beg) < BEGV || XINT (end) > ZV)
+ error ("Changes to be undone are outside visible portion of buffer");
Fput_text_property (beg, end, prop, val, Qnil);
}
else if (INTEGERP (car) && INTEGERP (cdr))
}
else if (EQ (car, Qapply))
{
- Lisp_Object oldlist = current_buffer->undo_list;
- /* Element (apply FUNNAME . ARGS) means call FUNNAME to undo. */
+ /* Element (apply FUN . ARGS) means call FUN to undo. */
+ struct buffer *save_buffer = current_buffer;
+
car = Fcar (cdr);
+ cdr = Fcdr (cdr);
if (INTEGERP (car))
{
- /* Long format: (apply DELTA START END FUNNAME . ARGS). */
- cdr = Fcdr (Fcdr (Fcdr (cdr)));
- car = Fcar (cdr);
+ /* Long format: (apply DELTA START END FUN . ARGS). */
+ Lisp_Object delta = car;
+ Lisp_Object start = Fcar (cdr);
+ Lisp_Object end = Fcar (Fcdr (cdr));
+ Lisp_Object start_mark = Fcopy_marker (start, Qnil);
+ Lisp_Object end_mark = Fcopy_marker (end, Qt);
+
+ cdr = Fcdr (Fcdr (cdr));
+ apply1 (Fcar (cdr), Fcdr (cdr));
+
+ /* Check that the function did what the entry said it
+ would do. */
+ if (!EQ (start, Fmarker_position (start_mark))
+ || (XINT (delta) + XINT (end)
+ != marker_position (end_mark)))
+ error ("Changes to be undone by function different than announced");
+ Fset_marker (start_mark, Qnil, Qnil);
+ Fset_marker (end_mark, Qnil, Qnil);
}
- cdr = Fcdr (cdr);
- apply1 (car, cdr);
-
- /* Make sure this produces at least one undo entry,
- so the test in `undo' for continuing an undo series
- will work right. */
- if (EQ (oldlist, current_buffer->undo_list))
- current_buffer->undo_list
- = Fcons (list3 (Qapply, Qcdr, Qnil), current_buffer->undo_list);
+ else
+ apply1 (car, cdr);
+
+ if (save_buffer != current_buffer)
+ error ("Undo function switched buffer");
+ did_apply = 1;
}
else if (STRINGP (car) && INTEGERP (cdr))
{
arg--;
}
+
+ /* Make sure an apply entry produces at least one undo entry,
+ so the test in `undo' for continuing an undo series
+ will work right. */
+ if (did_apply
+ && EQ (oldlist, current_buffer->undo_list))
+ current_buffer->undo_list
+ = Fcons (list3 (Qapply, Qcdr, Qnil), current_buffer->undo_list);
+
UNGCPRO;
return unbind_to (count, list);
}
pending_boundary = Qnil;
staticpro (&pending_boundary);
+ last_undo_buffer = NULL;
+ last_boundary_buffer = NULL;
+
defsubr (&Sprimitive_undo);
defsubr (&Sundo_boundary);
The size is counted as the number of bytes occupied,
which includes both saved text and other data. */);
- undo_limit = 20000;
+ undo_limit = 80000;
DEFVAR_INT ("undo-strong-limit", &undo_strong_limit,
doc: /* Don't keep more than this much size of undo information.
The size is counted as the number of bytes occupied,
which includes both saved text and other data. */);
- undo_strong_limit = 30000;
+ undo_strong_limit = 120000;
DEFVAR_LISP ("undo-outer-limit", &Vundo_outer_limit,
doc: /* Outer limit on size of undo information for one command.
`undo-outer-limit-function' with one argument, the size.
The text above describes the behavior of the function
that variable usually specifies. */);
- Vundo_outer_limit = make_number (3000000);
+ Vundo_outer_limit = make_number (12000000);
DEFVAR_LISP ("undo-outer-limit-function", &Vundo_outer_limit_function,
doc: /* Function to call when an undo list exceeds `undo-outer-limit'.
Garbage collection is inhibited around the call to this function,
so it must make sure not to do a lot of consing. */);
Vundo_outer_limit_function = Qnil;
+
+ 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;
}
/* arch-tag: d546ee01-4aed-4ffb-bb8b-eefaae50d38a