aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhillip Lord2015-08-06 21:33:58 +0100
committerPhillip Lord2015-11-12 21:06:05 +0000
commit44dfa86b7d382b84564d68472da1448d08f48129 (patch)
tree778e2228ec90a4401b2be6cd7b258ee44a212b26
parent0aec2aaccd8b745fa7214f3edd453c04a04bfba4 (diff)
downloademacs-44dfa86b7d382b84564d68472da1448d08f48129.tar.gz
emacs-44dfa86b7d382b84564d68472da1448d08f48129.zip
The heuristic that Emacs uses to add an `undo-boundary' has been
reworked, as it interacts poorly with functions on `post-command-hook' or `after-change-functions'. * lisp/simple.el: New section added. * src/cmds.c (remove_excessive_undo_boundaries): Now in lisp. (self_insert_command): Calls simple.el to amalgamate. (delete_char): Calls simple.el to amalgamate. * src/keyboard.c (last_undo_boundary): Removed. * src/undo.c (run_undoable_change): New function.
-rw-r--r--lisp/simple.el137
-rw-r--r--src/cmds.c41
-rw-r--r--src/keyboard.c17
-rw-r--r--src/lisp.h1
-rw-r--r--src/undo.c51
5 files changed, 167 insertions, 80 deletions
diff --git a/lisp/simple.el b/lisp/simple.el
index 00c25db07d7..821c7665c6c 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2754,6 +2754,143 @@ with < or <= based on USE-<."
2754 '(0 . 0))) 2754 '(0 . 0)))
2755 '(0 . 0))) 2755 '(0 . 0)))
2756 2756
2757;;; Default undo-boundary addition
2758;;
2759;; This section adds a new undo-boundary at either after a command is
2760;; called or in some cases on a timer called after a change is made in
2761;; any buffer.
2762(defvar-local undo-auto--last-boundary-cause nil
2763 "Describe the cause of the last undo-boundary.
2764
2765If `explicit', the last boundary was caused by an explicit call to
2766`undo-boundary', that is one not called by the code in this
2767section.
2768
2769If it is equal to `timer', then the last boundary was inserted
2770by `undo-auto--boundary-timer'.
2771
2772If it is equal to `command', then the last boundary was inserted
2773automatically after a command, that is by the code defined in
2774this section.
2775
2776If it is equal to a list, then the last boundary was inserted by
2777an amalgamating command. The car of the list is the number of
2778times an amalgamating command has been called, and the cdr are the
2779buffers that were changed during the last command.")
2780
2781(defvar undo-auto--current-boundary-timer nil
2782 "Current timer which will run `undo-auto--boundary-timer' or nil.
2783
2784If set to non-nil, this will effectively disable the timer.")
2785
2786(defvar undo-auto--this-command-amalgamating nil
2787 "Non-nil if `this-command' should be amalgamated.
2788This variable is set to nil by `undo-auto--boundaries' and is set
2789by `undo-auto--amalgamate'." )
2790
2791(defun undo-auto--needs-boundary-p ()
2792 "Return non-nil if `buffer-undo-list' needs a boundary at the start."
2793 (car-safe buffer-undo-list))
2794
2795(defun undo-auto--last-boundary-amalgamating-number ()
2796 "Return the number of amalgamating last commands or nil.
2797Amalgamating commands are, by default, either
2798`self-insert-command' and `delete-char', but can be any command
2799that calls `undo-auto--amalgamate'."
2800 (car-safe undo-auto--last-boundary-cause))
2801
2802(defun undo-auto--ensure-boundary (cause)
2803 "Add an `undo-boundary' to the current buffer if needed.
2804REASON describes the reason that the boundary is being added; see
2805`undo-auto--last-boundary' for more information."
2806 (when (and
2807 (undo-auto--needs-boundary-p))
2808 (let ((last-amalgamating
2809 (undo-auto--last-boundary-amalgamating-number)))
2810 (undo-boundary)
2811 (setq undo-auto--last-boundary-cause
2812 (if (eq 'amalgamate cause)
2813 (cons
2814 (if last-amalgamating (1+ last-amalgamating) 0)
2815 undo-auto--undoably-changed-buffers)
2816 cause)))))
2817
2818(defun undo-auto--boundaries (cause)
2819 "Check recently changed buffers and add a boundary if necessary.
2820REASON describes the reason that the boundary is being added; see
2821`undo-last-boundary' for more information."
2822 (dolist (b undo-auto--undoably-changed-buffers)
2823 (when (buffer-live-p b)
2824 (with-current-buffer b
2825 (undo-auto--ensure-boundary cause))))
2826 (setq undo-auto--undoably-changed-buffers nil))
2827
2828(defun undo-auto--boundary-timer ()
2829 "Timer which will run `undo--auto-boundary-timer'."
2830 (setq undo-auto--current-boundary-timer nil)
2831 (undo-auto--boundaries 'timer))
2832
2833(defun undo-auto--boundary-ensure-timer ()
2834 "Ensure that the `undo-auto-boundary-timer' is set."
2835 (unless undo-auto--current-boundary-timer
2836 (setq undo-auto--current-boundary-timer
2837 (run-at-time 10 nil #'undo-auto--boundary-timer))))
2838
2839(defvar undo-auto--undoably-changed-buffers nil
2840 "List of buffers that have changed recently.
2841
2842This list is maintained by `undo-auto--undoable-change' and
2843`undo-auto--boundaries' and can be affected by changes to their
2844default values.
2845
2846See also `undo-auto--buffer-undoably-changed'.")
2847
2848(defun undo-auto--add-boundary ()
2849 "Add an `undo-boundary' in appropriate buffers."
2850 (undo-auto--boundaries
2851 (if undo-auto--this-command-amalgamating
2852 'amalgamate
2853 'command))
2854 (setq undo-auto--this-command-amalgamating nil))
2855
2856(defun undo-auto--amalgamate ()
2857 "Amalgamate undo if necessary.
2858This function can be called after an amalgamating command. It
2859removes the previous `undo-boundary' if a series of such calls
2860have been made. By default `self-insert-command' and
2861`delete-char' are the only amalgamating commands, although this
2862function could be called by any command wishing to have this
2863behaviour."
2864 (let ((last-amalgamating-count
2865 (undo-auto--last-boundary-amalgamating-number)))
2866 (setq undo-auto--this-command-amalgamating t)
2867 (when
2868 last-amalgamating-count
2869 (if
2870 (and
2871 (< last-amalgamating-count 20)
2872 (eq this-command last-command))
2873 ;; Amalgamate all buffers that have changed.
2874 (dolist (b (cdr undo-auto--last-boundary-cause))
2875 (when (buffer-live-p b)
2876 (with-current-buffer
2877 b
2878 (when
2879 ;; The head of `buffer-undo-list' is nil.
2880 ;; `car-safe' doesn't work because
2881 ;; `buffer-undo-list' need not be a list!
2882 (and (listp buffer-undo-list)
2883 (not (car buffer-undo-list)))
2884 (setq buffer-undo-list
2885 (cdr buffer-undo-list))))))
2886 (setq undo-auto--last-boundary-cause 0)))))
2887
2888(defun undo-auto--undoable-change ()
2889 "Called after every undoable buffer change."
2890 (add-to-list 'undo-auto--undoably-changed-buffers (current-buffer))
2891 (undo-auto--boundary-ensure-timer))
2892;; End auto-boundary section
2893
2757(defcustom undo-ask-before-discard nil 2894(defcustom undo-ask-before-discard nil
2758 "If non-nil ask about discarding undo info for the current command. 2895 "If non-nil ask about discarding undo info for the current command.
2759Normally, Emacs discards the undo info for the current command if 2896Normally, Emacs discards the undo info for the current command if
diff --git a/src/cmds.c b/src/cmds.c
index a975a8ed4e0..6f19a046893 100644
--- a/src/cmds.c
+++ b/src/cmds.c
@@ -220,36 +220,6 @@ to t. */)
220 return Qnil; 220 return Qnil;
221} 221}
222 222
223static int nonundocount;
224
225static void
226remove_excessive_undo_boundaries (void)
227{
228 bool remove_boundary = true;
229
230 if (!EQ (Vthis_command, KVAR (current_kboard, Vlast_command)))
231 nonundocount = 0;
232
233 if (NILP (Vexecuting_kbd_macro))
234 {
235 if (nonundocount <= 0 || nonundocount >= 20)
236 {
237 remove_boundary = false;
238 nonundocount = 0;
239 }
240 nonundocount++;
241 }
242
243 if (remove_boundary
244 && CONSP (BVAR (current_buffer, undo_list))
245 && NILP (XCAR (BVAR (current_buffer, undo_list)))
246 /* Only remove auto-added boundaries, not boundaries
247 added by explicit calls to undo-boundary. */
248 && EQ (BVAR (current_buffer, undo_list), last_undo_boundary))
249 /* Remove the undo_boundary that was just pushed. */
250 bset_undo_list (current_buffer, XCDR (BVAR (current_buffer, undo_list)));
251}
252
253DEFUN ("delete-char", Fdelete_char, Sdelete_char, 1, 2, "p\nP", 223DEFUN ("delete-char", Fdelete_char, Sdelete_char, 1, 2, "p\nP",
254 doc: /* Delete the following N characters (previous if N is negative). 224 doc: /* Delete the following N characters (previous if N is negative).
255Optional second arg KILLFLAG non-nil means kill instead (save in kill ring). 225Optional second arg KILLFLAG non-nil means kill instead (save in kill ring).
@@ -265,7 +235,7 @@ because it respects values of `delete-active-region' and `overwrite-mode'. */)
265 CHECK_NUMBER (n); 235 CHECK_NUMBER (n);
266 236
267 if (abs (XINT (n)) < 2) 237 if (abs (XINT (n)) < 2)
268 remove_excessive_undo_boundaries (); 238 call0 (Qundo_auto__amalgamate);
269 239
270 pos = PT + XINT (n); 240 pos = PT + XINT (n);
271 if (NILP (killflag)) 241 if (NILP (killflag))
@@ -311,7 +281,7 @@ At the end, it runs `post-self-insert-hook'. */)
311 error ("Negative repetition argument %"pI"d", XFASTINT (n)); 281 error ("Negative repetition argument %"pI"d", XFASTINT (n));
312 282
313 if (XFASTINT (n) < 2) 283 if (XFASTINT (n) < 2)
314 remove_excessive_undo_boundaries (); 284 call0 (Qundo_auto__amalgamate);
315 285
316 /* Barf if the key that invoked this was not a character. */ 286 /* Barf if the key that invoked this was not a character. */
317 if (!CHARACTERP (last_command_event)) 287 if (!CHARACTERP (last_command_event))
@@ -321,7 +291,7 @@ At the end, it runs `post-self-insert-hook'. */)
321 XINT (last_command_event)); 291 XINT (last_command_event));
322 int val = internal_self_insert (character, XFASTINT (n)); 292 int val = internal_self_insert (character, XFASTINT (n));
323 if (val == 2) 293 if (val == 2)
324 nonundocount = 0; 294 Fset (Qundo_auto__this_command_amalgamating, Qnil);
325 frame_make_pointer_invisible (SELECTED_FRAME ()); 295 frame_make_pointer_invisible (SELECTED_FRAME ());
326 } 296 }
327 297
@@ -526,6 +496,10 @@ internal_self_insert (int c, EMACS_INT n)
526void 496void
527syms_of_cmds (void) 497syms_of_cmds (void)
528{ 498{
499 DEFSYM (Qundo_auto__amalgamate, "undo-auto--amalgamate");
500 DEFSYM (Qundo_auto__this_command_amalgamating,
501 "undo-auto--this-command-amalgamating");
502
529 DEFSYM (Qkill_forward_chars, "kill-forward-chars"); 503 DEFSYM (Qkill_forward_chars, "kill-forward-chars");
530 504
531 /* A possible value for a buffer's overwrite-mode variable. */ 505 /* A possible value for a buffer's overwrite-mode variable. */
@@ -555,7 +529,6 @@ keys_of_cmds (void)
555{ 529{
556 int n; 530 int n;
557 531
558 nonundocount = 0;
559 initial_define_key (global_map, Ctl ('I'), "self-insert-command"); 532 initial_define_key (global_map, Ctl ('I'), "self-insert-command");
560 for (n = 040; n < 0177; n++) 533 for (n = 040; n < 0177; n++)
561 initial_define_key (global_map, n, "self-insert-command"); 534 initial_define_key (global_map, n, "self-insert-command");
diff --git a/src/keyboard.c b/src/keyboard.c
index 5f8667586c4..1f08e1f23ed 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1278,9 +1278,6 @@ static int read_key_sequence (Lisp_Object *, int, Lisp_Object,
1278 bool, bool, bool, bool); 1278 bool, bool, bool, bool);
1279static void adjust_point_for_property (ptrdiff_t, bool); 1279static void adjust_point_for_property (ptrdiff_t, bool);
1280 1280
1281/* The last boundary auto-added to buffer-undo-list. */
1282Lisp_Object last_undo_boundary;
1283
1284/* FIXME: This is wrong rather than test window-system, we should call 1281/* FIXME: This is wrong rather than test window-system, we should call
1285 a new set-selection, which will then dispatch to x-set-selection, or 1282 a new set-selection, which will then dispatch to x-set-selection, or
1286 tty-set-selection, or w32-set-selection, ... */ 1283 tty-set-selection, or w32-set-selection, ... */
@@ -1505,14 +1502,10 @@ command_loop_1 (void)
1505 } 1502 }
1506#endif 1503#endif
1507 1504
1508 if (NILP (KVAR (current_kboard, Vprefix_arg))) /* FIXME: Why? --Stef */ 1505 /* Ensure that we have added appropriate undo-boundaries as a
1509 { 1506 result of changes from the last command. */
1510 Lisp_Object undo = BVAR (current_buffer, undo_list); 1507 call0 (Qundo_auto__add_boundary);
1511 Fundo_boundary (); 1508
1512 last_undo_boundary
1513 = (EQ (undo, BVAR (current_buffer, undo_list))
1514 ? Qnil : BVAR (current_buffer, undo_list));
1515 }
1516 call1 (Qcommand_execute, Vthis_command); 1509 call1 (Qcommand_execute, Vthis_command);
1517 1510
1518#ifdef HAVE_WINDOW_SYSTEM 1511#ifdef HAVE_WINDOW_SYSTEM
@@ -11095,6 +11088,8 @@ syms_of_keyboard (void)
11095 DEFSYM (Qpre_command_hook, "pre-command-hook"); 11088 DEFSYM (Qpre_command_hook, "pre-command-hook");
11096 DEFSYM (Qpost_command_hook, "post-command-hook"); 11089 DEFSYM (Qpost_command_hook, "post-command-hook");
11097 11090
11091 DEFSYM (Qundo_auto__add_boundary, "undo-auto--add-boundary");
11092
11098 DEFSYM (Qdeferred_action_function, "deferred-action-function"); 11093 DEFSYM (Qdeferred_action_function, "deferred-action-function");
11099 DEFSYM (Qdelayed_warnings_hook, "delayed-warnings-hook"); 11094 DEFSYM (Qdelayed_warnings_hook, "delayed-warnings-hook");
11100 DEFSYM (Qfunction_key, "function-key"); 11095 DEFSYM (Qfunction_key, "function-key");
diff --git a/src/lisp.h b/src/lisp.h
index 02109d72174..aaf52bdd1be 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4174,7 +4174,6 @@ extern void syms_of_casetab (void);
4174extern Lisp_Object echo_message_buffer; 4174extern Lisp_Object echo_message_buffer;
4175extern struct kboard *echo_kboard; 4175extern struct kboard *echo_kboard;
4176extern void cancel_echoing (void); 4176extern void cancel_echoing (void);
4177extern Lisp_Object last_undo_boundary;
4178extern bool input_pending; 4177extern bool input_pending;
4179#ifdef HAVE_STACK_OVERFLOW_HANDLING 4178#ifdef HAVE_STACK_OVERFLOW_HANDLING
4180extern sigjmp_buf return_to_command_loop; 4179extern sigjmp_buf return_to_command_loop;
diff --git a/src/undo.c b/src/undo.c
index 750bc8afff2..364b37eeeb4 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -26,10 +26,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
26#include "commands.h" 26#include "commands.h"
27#include "window.h" 27#include "window.h"
28 28
29/* Last buffer for which undo information was recorded. */
30/* BEWARE: This is not traced by the GC, so never dereference it! */
31static struct buffer *last_undo_buffer;
32
33/* Position of point last time we inserted a boundary. */ 29/* Position of point last time we inserted a boundary. */
34static struct buffer *last_boundary_buffer; 30static struct buffer *last_boundary_buffer;
35static ptrdiff_t last_boundary_position; 31static ptrdiff_t last_boundary_position;
@@ -41,6 +37,12 @@ static ptrdiff_t last_boundary_position;
41 an undo-boundary. */ 37 an undo-boundary. */
42static Lisp_Object pending_boundary; 38static Lisp_Object pending_boundary;
43 39
40void
41run_undoable_change ()
42{
43 call0 (Qundo_auto__undoable_change);
44}
45
44/* Record point as it was at beginning of this command (if necessary) 46/* Record point as it was at beginning of this command (if necessary)
45 and prepare the undo info for recording a change. 47 and prepare the undo info for recording a change.
46 PT is the position of point that will naturally occur as a result of the 48 PT is the position of point that will naturally occur as a result of the
@@ -59,15 +61,7 @@ record_point (ptrdiff_t pt)
59 if (NILP (pending_boundary)) 61 if (NILP (pending_boundary))
60 pending_boundary = Fcons (Qnil, Qnil); 62 pending_boundary = Fcons (Qnil, Qnil);
61 63
62 if ((current_buffer != last_undo_buffer) 64 run_undoable_change ();
63 /* Don't call Fundo_boundary for the first change. Otherwise we
64 risk overwriting last_boundary_position in Fundo_boundary with
65 PT of the current buffer and as a consequence not insert an
66 undo boundary because last_boundary_position will equal pt in
67 the test at the end of the present function (Bug#731). */
68 && (MODIFF > SAVE_MODIFF))
69 Fundo_boundary ();
70 last_undo_buffer = current_buffer;
71 65
72 at_boundary = ! CONSP (BVAR (current_buffer, undo_list)) 66 at_boundary = ! CONSP (BVAR (current_buffer, undo_list))
73 || NILP (XCAR (BVAR (current_buffer, undo_list))); 67 || NILP (XCAR (BVAR (current_buffer, undo_list)));
@@ -139,9 +133,7 @@ record_marker_adjustments (ptrdiff_t from, ptrdiff_t to)
139 if (NILP (pending_boundary)) 133 if (NILP (pending_boundary))
140 pending_boundary = Fcons (Qnil, Qnil); 134 pending_boundary = Fcons (Qnil, Qnil);
141 135
142 if (current_buffer != last_undo_buffer) 136 run_undoable_change ();
143 Fundo_boundary ();
144 last_undo_buffer = current_buffer;
145 137
146 for (m = BUF_MARKERS (current_buffer); m; m = m->next) 138 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
147 { 139 {
@@ -228,10 +220,6 @@ record_first_change (void)
228 if (EQ (BVAR (current_buffer, undo_list), Qt)) 220 if (EQ (BVAR (current_buffer, undo_list), Qt))
229 return; 221 return;
230 222
231 if (current_buffer != last_undo_buffer)
232 Fundo_boundary ();
233 last_undo_buffer = current_buffer;
234
235 if (base_buffer->base_buffer) 223 if (base_buffer->base_buffer)
236 base_buffer = base_buffer->base_buffer; 224 base_buffer = base_buffer->base_buffer;
237 225
@@ -259,15 +247,10 @@ record_property_change (ptrdiff_t beg, ptrdiff_t length,
259 if (NILP (pending_boundary)) 247 if (NILP (pending_boundary))
260 pending_boundary = Fcons (Qnil, Qnil); 248 pending_boundary = Fcons (Qnil, Qnil);
261 249
262 if (buf != last_undo_buffer)
263 boundary = true;
264 last_undo_buffer = buf;
265
266 /* Switch temporarily to the buffer that was changed. */ 250 /* Switch temporarily to the buffer that was changed. */
267 current_buffer = buf; 251 set_buffer_internal (buf);
268 252
269 if (boundary) 253 run_undoable_change ();
270 Fundo_boundary ();
271 254
272 if (MODIFF <= SAVE_MODIFF) 255 if (MODIFF <= SAVE_MODIFF)
273 record_first_change (); 256 record_first_change ();
@@ -278,7 +261,8 @@ record_property_change (ptrdiff_t beg, ptrdiff_t length,
278 bset_undo_list (current_buffer, 261 bset_undo_list (current_buffer,
279 Fcons (entry, BVAR (current_buffer, undo_list))); 262 Fcons (entry, BVAR (current_buffer, undo_list)));
280 263
281 current_buffer = obuf; 264 /* Reset the buffer */
265 set_buffer_internal (obuf);
282} 266}
283 267
284DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0, 268DEFUN ("undo-boundary", Fundo_boundary, Sundo_boundary, 0, 0, 0,
@@ -308,6 +292,8 @@ but another undo command will undo to the previous boundary. */)
308 } 292 }
309 last_boundary_position = PT; 293 last_boundary_position = PT;
310 last_boundary_buffer = current_buffer; 294 last_boundary_buffer = current_buffer;
295
296 Fset (Qundo_auto__last_boundary_cause, Qexplicit);
311 return Qnil; 297 return Qnil;
312} 298}
313 299
@@ -383,7 +369,6 @@ truncate_undo_list (struct buffer *b)
383 && !NILP (Vundo_outer_limit_function)) 369 && !NILP (Vundo_outer_limit_function))
384 { 370 {
385 Lisp_Object tem; 371 Lisp_Object tem;
386 struct buffer *temp = last_undo_buffer;
387 372
388 /* Normally the function this calls is undo-outer-limit-truncate. */ 373 /* Normally the function this calls is undo-outer-limit-truncate. */
389 tem = call1 (Vundo_outer_limit_function, make_number (size_so_far)); 374 tem = call1 (Vundo_outer_limit_function, make_number (size_so_far));
@@ -394,10 +379,6 @@ truncate_undo_list (struct buffer *b)
394 unbind_to (count, Qnil); 379 unbind_to (count, Qnil);
395 return; 380 return;
396 } 381 }
397 /* That function probably used the minibuffer, and if so, that
398 changed last_undo_buffer. Change it back so that we don't
399 force next change to make an undo boundary here. */
400 last_undo_buffer = temp;
401 } 382 }
402 383
403 if (CONSP (next)) 384 if (CONSP (next))
@@ -455,6 +436,9 @@ void
455syms_of_undo (void) 436syms_of_undo (void)
456{ 437{
457 DEFSYM (Qinhibit_read_only, "inhibit-read-only"); 438 DEFSYM (Qinhibit_read_only, "inhibit-read-only");
439 DEFSYM (Qundo_auto__undoable_change, "undo-auto--undoable-change");
440 DEFSYM (Qundo_auto__last_boundary_cause, "undo-auto--last-boundary-cause");
441 DEFSYM (Qexplicit, "explicit");
458 442
459 /* Marker for function call undo list elements. */ 443 /* Marker for function call undo list elements. */
460 DEFSYM (Qapply, "apply"); 444 DEFSYM (Qapply, "apply");
@@ -462,7 +446,6 @@ syms_of_undo (void)
462 pending_boundary = Qnil; 446 pending_boundary = Qnil;
463 staticpro (&pending_boundary); 447 staticpro (&pending_boundary);
464 448
465 last_undo_buffer = NULL;
466 last_boundary_buffer = NULL; 449 last_boundary_buffer = NULL;
467 450
468 defsubr (&Sundo_boundary); 451 defsubr (&Sundo_boundary);