"Amalgamate undo if necessary.
This function can be called before an amalgamating command. It
removes the previous `undo-boundary' if a series of such calls
-have been made. By default `self-insert-command' and
-`delete-char' are the only amalgamating commands, although this
-function could be called by any command wishing to have this
-behavior."
+have been made. `self-insert-command' and `delete-char' are the
+most common amalgamating commands, although this function can be
+called by any command which desires this behavior.
+`analyze-text-conversion' (which see) is also an amalgamating
+command in most circumstances."
(let ((last-amalgamating-count
(undo-auto--last-boundary-amalgamating-number)))
(setq undo-auto--this-command-amalgamating t)
(defvar electric-pair-preserve-balance)
(declare-function electric-pair-analyze-conversion "elec-pair.el")
+;; Actually in emacs-lisp/timer.el.
+(declare-function timer-set-time "emacs-lisp/timer.el")
+
(defvar-local post-text-conversion-hook nil
"Hook run after text is inserted by an input method.
Each function in this list is run until one returns non-nil.
can work.
- Run `post-text-conversion-hook' with `last-command-event' set
- to the last character of any inserted text to finish up."
+ to the last character of any inserted text to finish up.
+
+Finally, amalgamate recent changes to the undo list with previous
+ones, unless a new line has been inserted or auto-fill has taken
+place. If undo information is being recorded, make sure
+`undo-auto-current-boundary-timer' will run within the next 5
+seconds."
(interactive)
- ;; The list must be processed in reverse.
- (dolist (edit (reverse text-conversion-edits))
- ;; Filter out ephemeral edits and deletions.
- (when (and (stringp (nth 3 edit)))
- (with-current-buffer (car edit)
- (if (not (eq (nth 1 edit) (nth 2 edit)))
- ;; Process this insertion. (nth 3 edit) is the text which
- ;; was inserted.
- (let* ((inserted (nth 3 edit))
- ;; Get the first and last characters.
- (start (aref inserted 0))
- (end (aref inserted (1- (length inserted))))
- ;; Figure out whether or not to auto-fill.
- (auto-fill-p (or (aref auto-fill-chars start)
- (aref auto-fill-chars end)))
- ;; Figure out whether or not a newline was inserted.
- (newline-p (string-search "\n" inserted))
- ;; FIXME: this leads to an error in
- ;; `atomic-change-group', seemingly because
- ;; buffer-undo-list is being modified or
- ;; prematurely truncated. Turn it off for now.
- (electric-pair-preserve-balance nil))
+ (let ((any-nonephemeral nil))
+ ;; The list must be processed in reverse.
+ (dolist (edit (reverse text-conversion-edits))
+ ;; Filter out ephemeral edits and deletions after point. Here, we
+ ;; are only interested in insertions or deletions whose contents
+ ;; can be identified.
+ (when (stringp (nth 3 edit))
+ (with-current-buffer (car edit)
+ (if (not (eq (nth 1 edit) (nth 2 edit)))
+ ;; Process this insertion. (nth 3 edit) is the text which
+ ;; was inserted.
+ (let* ((inserted (nth 3 edit))
+ ;; Get the first and last characters.
+ (start (aref inserted 0))
+ (end (aref inserted (1- (length inserted))))
+ ;; Figure out whether or not to auto-fill.
+ (auto-fill-p (or (aref auto-fill-chars start)
+ (aref auto-fill-chars end)))
+ ;; Figure out whether or not a newline was inserted.
+ (newline-p (string-search "\n" inserted))
+ ;; Save the current undo list to figure out
+ ;; whether or not auto-fill has actually taken
+ ;; place.
+ (old-undo-list buffer-undo-list)
+ ;; FIXME: this leads to an error in
+ ;; `atomic-change-group', seemingly because
+ ;; buffer-undo-list is being modified or
+ ;; prematurely truncated. Turn it off for now.
+ (electric-pair-preserve-balance nil))
+ (save-excursion
+ (if (and auto-fill-function newline-p)
+ (progn (goto-char (nth 2 edit))
+ (previous-logical-line)
+ (funcall auto-fill-function))
+ (when (and auto-fill-function auto-fill-p)
+ (progn (goto-char (nth 2 edit))
+ (funcall auto-fill-function))))
+ ;; Record whether or not this edit should result in
+ ;; an undo boundary being added.
+ (setq any-nonephemeral
+ (or any-nonephemeral newline-p
+ ;; See if auto-fill has taken place by
+ ;; comparing the current undo list with
+ ;; the saved head.
+ (not (eq old-undo-list
+ buffer-undo-list)))))
+ (goto-char (nth 2 edit))
+ (let ((last-command-event end))
+ (unless (run-hook-with-args-until-success
+ 'post-text-conversion-hook)
+ (run-hooks 'post-self-insert-hook))))
+ ;; Process this deletion before point. (nth 2 edit) is the
+ ;; text which was deleted. Input methods typically prefer
+ ;; to edit words instead of deleting characters off their
+ ;; ends, but they seem to always send proper requests for
+ ;; deletion for punctuation.
+ (when (and (boundp 'electric-pair-delete-adjacent-pairs)
+ (symbol-value 'electric-pair-delete-adjacent-pairs)
+ ;; Make sure elec-pair is loaded.
+ (fboundp 'electric-pair-analyze-conversion)
+ ;; Only do this if only a single edit happened.
+ text-conversion-edits)
(save-excursion
- (if (and auto-fill-function newline-p)
- (progn (goto-char (nth 2 edit))
- (previous-logical-line)
- (funcall auto-fill-function))
- (when (and auto-fill-function auto-fill-p)
- (progn (goto-char (nth 2 edit))
- (funcall auto-fill-function)))))
- (goto-char (nth 2 edit))
- (let ((last-command-event end))
- (unless (run-hook-with-args-until-success
- 'post-text-conversion-hook)
- (run-hooks 'post-self-insert-hook))))
- ;; Process this deletion before point. (nth 2 edit) is the
- ;; text which was deleted. Input methods typically prefer
- ;; to edit words instead of deleting characters off their
- ;; ends, but they seem to always send proper requests for
- ;; deletion for punctuation.
- (when (and (boundp 'electric-pair-delete-adjacent-pairs)
- (symbol-value 'electric-pair-delete-adjacent-pairs)
- ;; Make sure elec-pair is loaded.
- (fboundp 'electric-pair-analyze-conversion)
- ;; Only do this if only a single edit happened.
- text-conversion-edits)
- (save-excursion
- (goto-char (nth 2 edit))
- (electric-pair-analyze-conversion (nth 3 edit)))))))))
+ (goto-char (nth 2 edit))
+ (electric-pair-analyze-conversion (nth 3 edit))))))))
+ ;; If all edits were ephemeral, make this an amalgamating command.
+ ;; Then, make sure that an undo boundary is placed within the next
+ ;; five seconds.
+ (unless any-nonephemeral
+ (undo-auto-amalgamate)
+ (let ((timer undo-auto-current-boundary-timer))
+ (if timer
+ ;; The timer is already running. See if it's due to expire
+ ;; within the next five seconds.
+ (let ((time (list (aref timer 1) (aref timer 2)
+ (aref timer 3))))
+ (unless (<= (time-convert (time-subtract time nil)
+ 'integer)
+ 5)
+ ;; It's not, so make it run in 5 seconds.
+ (timer-set-time undo-auto-current-boundary-timer
+ (time-add nil 5))))
+ ;; Otherwise, start it for five seconds from now.
+ (setq undo-auto-current-boundary-timer
+ (run-at-time 5 nil #'undo-auto--boundary-timer)))))))
\f
/* Now delete whatever needs to go. */
del_range (start, end);
- record_buffer_change (start, start, Qnil);
+ record_buffer_change (start, start, Qt);
/* Don't record changes if TEXT is empty. */
{
del_range (start, end);
set_point (start);
- record_buffer_change (start, start, Qnil);
+ record_buffer_change (start, start, Qt);
}
/* Now set the markers which denote the composition region. */
set_point (start);
if (start != end)
- record_buffer_change (start, start, Qnil);
+ record_buffer_change (start, start, Qt);
}
/* Insert the new text. */
Finsert (1, &text);
if (start != PT)
- record_buffer_change (start, PT, Qnil);
+ record_buffer_change (start, PT, Qt);
/* Now move point to an appropriate location. */
if (position <= 0)
doc: /* List of buffers that were last edited as a result of text conversion.
This list can be used while handling a `text-conversion' event to
-determine the changes which have taken place.
+determine which changes have taken place.
Each element of the list describes a single edit in a buffer, of the
form:
(BUFFER BEG END EPHEMERAL)
-If an insertion or a change occured, then BEG and END are markers
-which denote the bounds of the text that was changed or inserted.
-
-If EPHEMERAL is t, then the input method will shortly make more
-changes to the text, so any actions that would otherwise be taken
-(such as indenting or automatically filling text) should not take
-place; otherwise, it is a string describing the text which was
-inserted.
+If an insertion or an edit to the buffer text is described, then BEG
+and END are markers which denote the bounds of the text that was
+changed or inserted. If a deletion is described, then BEG and END are
+the same object.
-If a deletion occured before point, then BEG and END are the same
-object, and EPHEMERAL is the text which was deleted.
+If EPHEMERAL is t, then the input method is preparing to make further
+edits to the text, so any actions that would otherwise be taken, such
+as indenting or automatically filling text, should not take place.
-If a deletion occured after point, then BEG and END are also the same
-object, but EPHEMERAL is nil.
+Otherwise, it is either a string containing text that was inserted,
+text deleted before point, or nil if text was deleted after point.
The list contents are ordered later edits first, so you must iterate
through the list in reverse. */);