emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android 5f09df3d6a3: Improve undo behavior on Android


From: Po Lu
Subject: feature/android 5f09df3d6a3: Improve undo behavior on Android
Date: Mon, 5 Jun 2023 22:13:04 -0400 (EDT)

branch: feature/android
commit 5f09df3d6a359fabb2485c83549a0176a0ba2796
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Improve undo behavior on Android
    
    * lisp/simple.el (undo-auto-amalgamate): Update doc string to
    describe new amalgamating commands.
    (analyze-text-conversion): Make this an amalgamating command by
    default, unless a new line has been inserted.  Also, shorten the
    undo boundary timer.
    * src/textconv.c (really_commit_text)
    (really_set_composing_text): Correctly report ephemeral
    deletions.
    (syms_of_textconv): Fix doc strings.
---
 lisp/simple.el | 152 +++++++++++++++++++++++++++++++++++++--------------------
 src/textconv.c |  31 ++++++------
 2 files changed, 113 insertions(+), 70 deletions(-)

diff --git a/lisp/simple.el b/lisp/simple.el
index 5ae92c6e4a0..62489d8ffcb 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -4092,10 +4092,11 @@ default values.")
   "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)
@@ -10988,6 +10989,9 @@ If the buffer doesn't exist, create it first."
 (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.
@@ -11016,57 +11020,99 @@ For each insertion:
     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)))))))
 
 
 
diff --git a/src/textconv.c b/src/textconv.c
index 0716cf7edbb..0dcf5bdcea8 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -600,7 +600,7 @@ really_commit_text (struct frame *f, EMACS_INT position,
       /* 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.  */
 
@@ -786,7 +786,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t 
position,
        {
          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.  */
@@ -808,14 +808,14 @@ really_set_composing_text (struct frame *f, ptrdiff_t 
position,
       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)
@@ -1927,27 +1927,24 @@ syms_of_textconv (void)
     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.  */);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]