branch: externals/cursor-undo commit 4ebdbea075e4600469b858a298f4815809fde4fb Author: Luke Lee <luke.yx....@gmail.com> Commit: Luke Lee <luke.yx....@gmail.com>
Cursor-undo package initial import. --- README.txt | 63 ++++++ cursor-undo.el | 600 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 663 insertions(+) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000000..03c6ee3bb7 --- /dev/null +++ b/README.txt @@ -0,0 +1,63 @@ +Cursor-undo allows you to undo cursor movement commands using the Emacs +standard `undo' command. + +For frequent cursor movements such as like up/down/left/right, it +combines the movements of the same direction into a single undo entry. +This prevents the undo command from reversing each individual +character movement separately. For example, if you move the cursor 20 +characters to the right and then 10 lines up, the first undo will go +down 10 lines back, and the next undo move back 20 characters left. +On the other hand, for search commands that often jump across multiple +pages, each search command has its own undo entry, allowing you to +undo them one at a time rather than as a combined operation. + +This cursor-undo functionality has existed in my local Emacs init file +for over 11+ years, since version 0 on 2013-06-26. It was originally +intended to support my Brief Editor Mode only, but I later found it +would be more useful if implemented in a more generalized way. For +years I have hoped for an official implementation of this feature, +which is commonly seen among various editors. Considering my +implementation using advice functions a bit inelegant so I have always +hesitated to release it till recently. + +Until there is official support for the cursor undo feature, this +package serves most common daily needs. The core design is to align +with Emacs's native `undo' function by recording cursor positions +and screen-relative position undo entries in the `buffer-undo-list' +in accordance with its documentation. + +As this package primarily consists of advice functions to wrap cursor +movement commands, each cursor movement command needs to be manually +wrapped with `def-cursor-undo'. For interactive functions that +heavily invoke advised cursor movement commands, you may even need to +advise them with `disable-cursor-tracking' to prevent generating +numerous distinct cursor undo entries from a single command. For user +convenience, we have prepared ready `def-cursor-undo' advice sets for +standard Emacs cursor movement commands, Brief Editor mode, Viper +mode, and EVIL mode. + +Usage: + + In theory, once this package is installed, you should already have + cursor-undo autoloaded and enabled. If not, or if you downloaded this + package as source, put "cursor-undo.el" file in a `load-path' and add + the following line into your Emacs init file .emacs or init.el: + + (require 'cursor-undo) + +Notes for EVIL mode user: + + If you choose to use default Emacs `undo' system, you should be able + to use `evil-undo' to undo cursor movements. If your choice is + tree-undo or another undo system, you might need to use Emacs default + `undo' (C-_, C-/ or C-x u ...) to undo cursor movements. + +Notes for Viper mode user: + + The default `viper-undo' is advised to allow cursor-undo. If you + find the advised function not working properly, consider comment out + the following source code `(define-advice viper-undo ...' to restore + the original `viper-undo' function and use Emacs default `undo' + (C-_, C-/ or C-x u ...) to undo cursor movements. + +Luke Lee [2024-07-20] diff --git a/cursor-undo.el b/cursor-undo.el new file mode 100644 index 0000000000..43468c8fcd --- /dev/null +++ b/cursor-undo.el @@ -0,0 +1,600 @@ +;;; cursor-undo.el --- Undo Cursor Movement -*- lexical-binding: t -*- + +;; Copyright (C) 2024 Free Software Foundation, Inc. + +;; Author: Luke Lee <luke.yx....@gmail.com> +;; Maintainer: Luke Lee <luke.yx....@gmail.com> +;; Keywords: undo, cursor +;; Version: 1.0 +;; Package-Requires: ((cl-lib "0.5")) + +;; 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. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Cursor-undo allows you to undo cursor movement commands using the Emacs +;; standard `undo' command. +;; +;; For frequent cursor movements such as like up/down/left/right, it +;; combines the movements of the same direction into a single undo entry. +;; This prevents the undo command from reversing each individual +;; character movement separately. For example, if you move the cursor 20 +;; characters to the right and then 10 lines up, the first undo will go +;; down 10 lines back, and the next undo move back 20 characters left. +;; On the other hand, for search commands that often jump across multiple +;; pages, each search command has its own undo entry, allowing you to +;; undo them one at a time rather than as a combined operation. +;; +;; This cursor-undo functionality has existed in my local Emacs init file +;; for over 11+ years, since version 0 on 2013-06-26. It was originally +;; intended to support my Brief Editor Mode only, but I later found it +;; would be more useful if implemented in a more generalized way. For +;; years I have hoped for an official implementation of this feature, +;; which is commonly seen among various editors. Considering my +;; implementation using advice functions a bit inelegant so I have always +;; hesitated to release it till recently. +;; +;; Until there is official support for the cursor undo feature, this +;; package serves most common daily needs. The core design is to align +;; with Emacs's native `undo' function by recording cursor positions +;; and screen-relative position undo entries in the `buffer-undo-list' +;; in accordance with its documentation. +;; +;; As this package primarily consists of advice functions to wrap cursor +;; movement commands, each cursor movement command needs to be manually +;; wrapped with `def-cursor-undo'. For interactive functions that +;; heavily invoke advised cursor movement commands, you may even need to +;; advise them with `disable-cursor-tracking' to prevent generating +;; numerous distinct cursor undo entries from a single command. For user +;; convenience, we have prepared ready `def-cursor-undo' advice sets for +;; standard Emacs cursor movement commands, Brief Editor mode, Viper +;; mode, and EVIL mode. +;; +;; Usage: +;; +;; In theory, once this package is installed you should already have +;; cursor-undo autoloaded and enabled. If not, or if you download this +;; package as source, put "cursor-undo.el" file in a `load-path' and add +;; the following line into your Emacs init file .emacs or init.el: +;; +;; (require 'cursor-undo) +;; +;; Notes for EVIL mode user: +;; +;; If you choose to use default Emacs `undo' system, you should be able +;; to use `evil-undo' to undo cursor movements. If your choice is +;; tree-undo or another undo system, you might need to use Emacs default +;; `undo' (C-_, C-/ or C-x u ...) to undo cursor movements. +;; +;; Notes for Viper mode user: +;; +;; The default `viper-undo' is advised to allow cursor-undo. If you +;; find the advised function not working properly, consider comment out +;; the following source code `(define-advice viper-undo ...' to restore +;; the original `viper-undo' function and use Emacs default `undo' +;; (C-_, C-/ or C-x u ...) to undo cursor movements. +;; +;; Future TODO thoughts: a more desired implementation is to integrate it +;; into Emacs source by extending the `interactive' spec to add code +;; letters for various cursor undo information. +;; +;; Luke Lee [2024-07-19 Fri.] + +;;; Code: + +(defvar cundo-enable-cursor-tracking t) ; global enable flag +;; Local disable flag, use reverse logic as NIL is still a list and we can +;; pop it again and again +(defvar cundo-disable-local-cursor-tracking nil) +(make-variable-buffer-local 'cundo-disable-local-cursor-tracking) + +;; Clear duplicated `(apply cdr nil) nil' pairs in `buffer-undo-list' done by +;; `primitive-undo'. +(defvar cundo-clear-usless-undoinfo t) + +(defun cundo-restore-win (start) + (set-window-start nil start) + (if cundo-clear-usless-undoinfo + ;; This tries to remove as much garbage as possible but still some left + ;; in the `buffer-undo-list'. TODO: add a idle task to remove them + (while (and (null (first buffer-undo-list)) ;; nil + (equal (second buffer-undo-list) '(apply cdr nil)) + (null (third buffer-undo-list))) + ;; (nil (apply cdr nil) nil a b...) -> (nil a b...) + (setq buffer-undo-list (cddr buffer-undo-list))))) + +;;;###autoload +(defmacro def-cursor-undo (func-sym &optional no-combine screen-pos no-move) + "Define an advice for FUNC-SYM to track cursor movements in the undo buffer. +The NO-COMBINE flag track each movement without combining the same +commands into a single undo record. For example, multiple arrow key +movements like `right-chars' will be combined as a single undo operation +when NO-COMBINE is NIL. Nested/reentering cursor-undo are prevented +using `cursor-tracking' and `cundo-enable-cursor-tracking' dynamic +variables. + +Parameters: + NO-COMBINE: (default NIL) + Force adding an undo entry in undo buffer without combined with + the previous command, if the previous command (`last-command') + is the same as `this-command'. + + SCREEN-POS: (default NIL) + Record window cursor relative position in undo buffer entry so + that we can jump back undo editing and still having the cursor + at the original relative position to the window. + + NO-MOVE: (default NIL) + Add an undo entry even if cursor (`point') not moved. For + example, `recenter' won't move `point' but only the relative + position of the cursor to the current window." + + (if (and (not screen-pos) + no-move) + (error +"Error: No undo information will be stored if we're neither recording cursor +relative screen position (screen-pos=NIL) nor `point' position (no-move=t).")) + (let* ((func-sym-str (symbol-name func-sym)) + (advice-sym-str (concat func-sym-str "-cursor-undo")) + (advice-sym (make-symbol advice-sym-str)) + (def-advice-sym (intern-soft + (concat func-sym-str "@" advice-sym-str)))) + ;; prevent duplicate definition + (if def-advice-sym + (error (message + (format "Error: Redefining cursor undo advice for `%S'" + func-sym)))) + `(progn + (define-advice ,func-sym (:around (orig-func &rest args) ,advice-sym) + (let* ((cursor-tracking cundo-enable-cursor-tracking) + ;; prevent nested calls for complicated compound commands + (cundo-enable-cursor-tracking nil) + (prev-point (point)) + prev-screen-start) + ,@(when screen-pos + '((when cursor-tracking + (setq prev-screen-start (window-start))))) + (apply orig-func args) + ;; This is a helper for commands that might take long. eg. page-up/ + ;; page-down in big files, or line-up/down in big files when marking. + (unless + (or (not cursor-tracking) + ;;[2017-11-15 Wed] Still need to test + ;; `(called-interactively-p 'any)', why? Maybe it's because + ;; too many functions are invoked non-interactively and thus + ;; produce a lot of undo records in the undo + ;; buffer. Therefore after a search operation there are tons + ;; and tons of cursor undo information to redo. Therefore, + ;; testing `(called-interactively-p 'any)' will be safer. + ;; + ;;[2017-11-13 Mon] We've already prevent reentering so there + ;; is really no need to test if this call is called + ;; interactively or not. When a keyboard command calls + ;; another keyboard command using normal LISP function calls + ;; the (called-interactively-p 'any) will return nil unless + ;; they are called using `call-interactively'. Now we + ;; remove it to allow either case. + ;; + ;; A sample function is this: + ;; (def-cursor-undo line-bookmark-jump-nearest t t) + ;; ;;(def-cursor-undo line-bookmark-nearest-next t t) + ;; ;;(def-cursor-undo line-bookmark-nearest-prev t t) + ;; + ;; `line-bookmark-nearest-next'/`line-bookmark-nearest-prev' + ;; calls `line-bookmark-jump-nearest' (non-interactive call). + ;; By adding cursor-undo to the inner function + ;; `line-bookmark-jump-nearest' we don't need to add to both + ;; `line-bookmark-nearest-next'/`line-bookmark-nearest-prev'. + (not (called-interactively-p 'any)) + (car cundo-disable-local-cursor-tracking) + ,@(unless no-combine '((eq last-command this-command))) + ;; if NO-MOVE is specified, check if `point' moved + ,@(unless no-move '((= prev-point (point)))) + ;; Sometimes the buffer-undo-list is t + (and (listp buffer-undo-list) + (numberp (cadr buffer-undo-list)) + (= prev-point (cadr buffer-undo-list)))) + ,@(if screen-pos + '((push `(apply cundo-restore-win (,@prev-screen-start)) + buffer-undo-list))) + ,@(unless no-move + '((push prev-point buffer-undo-list))) + ;;(abbrevmsg (format "c=%S,%S b=%S" last-command this-command + ;; buffer-undo-list) 128) ;; DBG + (undo-boundary))))))) + +;; +;; Disable cursor tracking during miscellaneous operations that could cause +;; temporarily cursor jump +;; +(defmacro disable-cursor-tracking (func-sym) + (let ((advice-sym (make-symbol ;; gensym + (concat (symbol-name func-sym) "-disable-cursor-tracking")))) + `(progn + (define-advice ,func-sym (:around (orig-func &rest args) ,advice-sym) + (let ((cundo-enable-cursor-tracking nil)) + (apply orig-func args)))))) + +;; +;; Allow cursor undo in a read-only buffer +;; Notice that the original definition of undo is (interactive "*P") which +;; means it cannot be performed in a read-only buffer. Here we allow the undo +;; operation as long as the pending undo list is still a cursor movement. +;; +;; This also enables a keyboard trick: +;; If you just edited a big file and moved the cursor to browse the +;; other parts, but forgot where you were, you can undo cursor +;; movements to go back to your last edited position by long-holding +;; <undo> till the last editing command. However, you risk missing +;; your last edited operation as it might just flash by so quickly that +;; you don't even notice and keep undoing other cursor commands you +;; don't want to undo at all. In this case, you can switch the buffer +;; to read-only mode (by setting `buffer-read-only' to 't), then long +;; press <undo> utill the undo command warns that you that you're +;; trying to edit a read-only buffer. At this point you're exactly at +;; the latest editing position where you are looking for. Now you can +;; then safely set `buffer-read-only' back to NIL and continue your +;; editing. +;; +(define-advice undo (:around (orig-func &rest args) + undo-cursor-in-read-only-buffer) + (interactive "P") ;; Change the behavior from "*P" to "P" + (if (if (eq last-command 'undo) + ;; last-command is undo, check pending undo list if the first command + ;; is cursor movement or not + (and (listp pending-undo-list) + (numberp (car pending-undo-list))) + ;; not a continuous undo, check first command is cursor movement or not + (and (listp buffer-undo-list) + (null (car buffer-undo-list)) + (numberp (cadr buffer-undo-list)))) + (apply orig-func args) + (if buffer-read-only + (if (listp pending-undo-list) + (user-error "Buffer is read-only: cannot undo an editing command!") + (apply orig-func args)) + (apply orig-func args)))) + + +(provide 'cursor-undo) + +;;; +;;; Advice cursor movement commands +;;; + +;; ----------------------------------------------------------------------- +;; keyboard function no-combine screen-pos no-move +;; ----------------------------------------------------------------------- +;; General cursor movements +(def-cursor-undo previous-line nil nil nil) +(def-cursor-undo next-line nil nil nil) +(def-cursor-undo left-char nil nil nil) +(def-cursor-undo right-char nil nil nil) + +;; Mouse movement, scrolling +(def-cursor-undo mouse-set-point t) +(def-cursor-undo scroll-bar-toolkit-scroll nil) +(def-cursor-undo mwheel-scroll nil) + +;; Enabling `forward-secp' will cause semantic parsing to push a lot of cursor +;; undo entries into the buffer undo list. +(def-cursor-undo forward-sexp t) +(def-cursor-undo backward-sexp t) +(def-cursor-undo mouse-drag-region t) + +;; Others +(def-cursor-undo recenter nil t t) +(def-cursor-undo recenter-top-bottom nil t t) +(def-cursor-undo mark-whole-buffer t t) + +(disable-cursor-tracking save-buffer) +(disable-cursor-tracking write-file) +;; tabify +(disable-cursor-tracking tabify) +(disable-cursor-tracking untabify) + +;; +;; CUA rectangle, also used by Brief Editor mode +;; +(eval-after-load 'cua-rect + '(progn + (def-cursor-undo cua-resize-rectangle-up nil nil nil) + (def-cursor-undo cua-resize-rectangle-down nil nil nil) + (def-cursor-undo cua-resize-rectangle-left nil nil nil) + (def-cursor-undo cua-resize-rectangle-right nil nil nil) + (def-cursor-undo cua-resize-rectangle-page-up nil nil nil) + (def-cursor-undo cua-resize-rectangle-page-down nil nil nil) + (def-cursor-undo goto-line t t) + (def-cursor-undo move-to-window-line t))) + +;; +;; Brief Editor Mode cursor movements +;; +(eval-after-load 'brief + '(progn + ;; If we defined brief-left-char and brief-right-char, remember to check + ;; (brief-rectangle-active) and call cua-resize-rectangle-left/right + ;; accordingly + ;; ----------------------------------------------------------------------- + ;; keyboard function no-combine screen-pos no-move + ;; ----------------------------------------------------------------------- + (def-cursor-undo brief-previous-line nil nil nil) + (def-cursor-undo brief-next-line nil nil nil) + (def-cursor-undo brief-fixed-cursor-page-up nil t nil) + (def-cursor-undo brief-fixed-cursor-page-down nil t nil) + (def-cursor-undo brief-home t t) + (def-cursor-undo brief-end t t) + (def-cursor-undo brief-forward-word nil) + (def-cursor-undo brief-backward-word nil) + (def-cursor-undo brief-recenter-left-right nil t t) + (def-cursor-undo brief-move-to-window-line-0 nil t) + (def-cursor-undo brief-move-to-window-line-end nil t) + (def-cursor-undo brief-previous-physical-line nil nil) + (def-cursor-undo brief-next-physical-line nil nil) + (def-cursor-undo brief-beginning-of-file nil t) + (def-cursor-undo brief-end-of-file nil t) + (def-cursor-undo brief-mark-move-to-window-line-0 nil t) + (def-cursor-undo brief-mark-move-to-window-line-end nil t) + ;; Search + (def-cursor-undo brief-search-forward t t) + (def-cursor-undo brief-search-forward-currword t t) + (def-cursor-undo brief-search-backward t t) + (def-cursor-undo brief-search-backward-currword t t) + (def-cursor-undo brief-repeat-search t t) + (def-cursor-undo brief-repeat-search-backward t t) + ;; Bookmark related + (def-cursor-undo brief-bookmark-do-jump t t) + (def-cursor-undo brief-bookmark-jump-set-0 t t) + (def-cursor-undo brief-bookmark-jump-set-1 t t) + (def-cursor-undo brief-bookmark-jump-set-2 t t) + (def-cursor-undo brief-bookmark-jump-set-3 t t) + (def-cursor-undo brief-bookmark-jump-set-4 t t) + (def-cursor-undo brief-bookmark-jump-set-5 t t) + (def-cursor-undo brief-bookmark-jump-set-6 t t) + (def-cursor-undo brief-bookmark-jump-set-7 t t) + (def-cursor-undo brief-bookmark-jump-set-8 t t) + (def-cursor-undo brief-bookmark-jump-set-9 t t) + (def-cursor-undo brief-shift-tab t t) + + (define-advice brief-buffer-read-only-toggle + (:around (orig-func &rest args) + read-only-toggle-not-go-into-last-command) + ;; Let it not go into `last-command' so that it won't break our + ;; continuous undo operations. If our desire operation (using the above + ;; read-only+undo trick) is + ;; "<toggle R/O> - <undo>s till stop - <toggle R/O> - keep undoing", + ;; it will become + ;; "<toggle R/O> - <undo>s till stop - <toggle R/O> - REDOs !!!". + ;; We don't want it start redoing! That's mainly because <undo> detected + ;; the previous command is not an <undo> but a <toggle read-only>. By + ;; setting last-command back (technically, set `this-command' back since + ;; the caller will put this-command to last-command), we allow undos to + ;; keep going. + (let ((lastcmd last-command)) + (apply orig-func args) + (setf this-command lastcmd))))) + +;; Bookmark related +(eval-after-load 'bookmark+-1 ;; emacswiki bookmark extension + '(progn + (def-cursor-undo bookmark-jump t t) ;; C-x b g + (def-cursor-undo bookmark-jump-other-window t t) ;; C-x b j + (disable-cursor-tracking bookmark-save))) + +;; +;; Prevent cursor tracking during semantic parsing +;; +(eval-after-load 'semantic + '(progn + (add-hook 'semantic-before-idle-scheduler-reparse-hooks + #'(lambda () + (push 't cundo-disable-local-cursor-tracking))) + (add-hook 'semantic-after-idle-scheduler-reparse-hooks + #'(lambda () + (pop cundo-disable-local-cursor-tracking))) + (disable-cursor-tracking semantic-fetch-tags) + (disable-cursor-tracking senator-parse) + (disable-cursor-tracking senator-force-refresh) + (disable-cursor-tracking semantic-go-to-tag))) + +(eval-after-load 'smie + '(progn + ;; Need to disable the following, a sample test without disabling this is + ;; to open a shell-script and place cursor on 'if' or 'else' or 'fi', and + ;; try to move the cursor up/down. It will stuck at 'if','else', or 'fi'. + (disable-cursor-tracking smie-backward-sexp) + (disable-cursor-tracking smie-forward-sexp) + (disable-cursor-tracking smie-backward-sexp-command) + (disable-cursor-tracking smie-forward-sexp-command))) + +;; +;; Disable cursor tracking during ediff comparing [2013-06-28 15:16:06 +0800] +;; +(eval-after-load 'ediff + '(progn + (defvar undo-cursor-ediff-buffer-list nil) + (defun undo-cursor-ediff-prepare-buffer-hook () + (push (current-buffer) undo-cursor-ediff-buffer-list) + (push 't cundo-disable-local-cursor-tracking) + (message "Disable buffer %S cursor tracking" (current-buffer))) + + (defun undo-cursor-ediff-cleanup-hook () + (dolist (ediff-buf undo-cursor-ediff-buffer-list) + (with-current-buffer ediff-buf + (pop cundo-disable-local-cursor-tracking) + (message "Enable buffer %S cursor tracking" ediff-buf))) + (setf undo-cursor-ediff-buffer-list nil)) + + (add-hook 'ediff-prepare-buffer-hook + #'undo-cursor-ediff-prepare-buffer-hook) + ;; Set-up two cleanup hooks in case of any error + (add-hook 'ediff-cleanup-hook #'undo-cursor-ediff-cleanup-hook) + (add-hook 'ediff-quit-hook #'undo-cursor-ediff-cleanup-hook))) + +;; [2015-11-11 17:05:07 +0800] We don't need to track cursor when debugging. +;; This is for GUD functions which will call `recenter' from time to time (not +;; just my `gud-wrapper:display-line-recenter' function that calls `recenter', +;; some GUD functions seems to do so too). +(eval-after-load 'gud + '(progn + (disable-cursor-tracking gud-filter))) + +;; +;; EVIL mode cursor movements support +;; +(eval-after-load 'evil-commands + '(progn + (def-cursor-undo evil-previous-line nil nil nil) + (def-cursor-undo evil-next-line nil nil nil) + (def-cursor-undo evil-previous-visual-line nil nil nil) + (def-cursor-undo evil-next-visual-line nil nil nil) + (def-cursor-undo evil-forward-char nil nil nil) + (def-cursor-undo evil-backward-char nil nil nil) + (def-cursor-undo evil-beginning-of-line t t) + (def-cursor-undo evil-end-of-line t t) + (def-cursor-undo evil-beginning-of-visual-line t t) + (def-cursor-undo evil-end-of-visual-line t t) + (def-cursor-undo evil-line t t) + (def-cursor-undo evil-line-or-visual-line t t) + (def-cursor-undo evil-end-of-line-or-visual-line t t) + (def-cursor-undo evil-middle-of-visual-line t t) + (def-cursor-undo evil-percentage-of-line t t) + (def-cursor-undo evil-first-non-blank t t) + (def-cursor-undo evil-last-non-blank t t) + (def-cursor-undo evil-first-non-blank-of-visual-line t t) + (def-cursor-undo evil-next-line-first-non-blank t t) + (def-cursor-undo evil-next-line-1-first-non-blank t t) + (def-cursor-undo evil-previous-line-first-non-blank t t) + (def-cursor-undo evil-goto-line t t) + (def-cursor-undo evil-goto-first-line t t) + (def-cursor-undo evil-forward-word-begin nil nil) + (def-cursor-undo evil-forward-word-end nil nil) + (def-cursor-undo evil-backward-word-begin nil nil) + (def-cursor-undo evil-backward-word-end nil nil) + (def-cursor-undo evil-forward-WORD-begin nil nil) + (def-cursor-undo evil-forward-WORD-end nil nil) + (def-cursor-undo evil-backward-WORD-begin nil nil) + (def-cursor-undo evil-backward-WORD-end nil nil) + (def-cursor-undo evil-forward-section-begin nil t) + (def-cursor-undo evil-forward-section-end nil t) + (def-cursor-undo evil-backward-section-begin nil t) + (def-cursor-undo evil-backward-section-end nil t) + (def-cursor-undo evil-forward-sentence-begin nil t) + (def-cursor-undo evil-backward-sentence-begin nil t) + (def-cursor-undo evil-forward-paragraph nil t) + (def-cursor-undo evil-backward-paragraph nil t) + (def-cursor-undo evil-jump-item t t) + (def-cursor-undo evil-next-flyspell-error t t) + (def-cursor-undo evil-prev-flyspell-error t t) + (def-cursor-undo evil-previous-open-paren t t) + (def-cursor-undo evil-next-close-paren t t) + (def-cursor-undo evil-previous-open-brace t t) + (def-cursor-undo evil-next-close-brace t t) + (def-cursor-undo evil-next-mark t t) + (def-cursor-undo evil-next-mark-line t t) + (def-cursor-undo evil-previous-mark t t) + (def-cursor-undo evil-previous-mark-line t t) + (def-cursor-undo evil-find-char t t) + (def-cursor-undo evil-find-char-backward t t) + (def-cursor-undo evil-find-char-to t t) + (def-cursor-undo evil-find-char-to-backward t t) + (def-cursor-undo evil-repeat-find-char t t) + (def-cursor-undo evil-repeat-find-char-reverse t t) + (def-cursor-undo evil-goto-column t t) + (def-cursor-undo evil-jump-backward t t) + (def-cursor-undo evil-jump-forward t t) + (def-cursor-undo evil-jump-backward-swap t t) + (def-cursor-undo evil-jump-to-tag t t) + (def-cursor-undo evil-lookup t t) + (def-cursor-undo evil-ret t t) + (def-cursor-undo evil-ret-and-indent t t) + (def-cursor-undo evil-window-top nil t) + (def-cursor-undo evil-window-middle nil t) + (def-cursor-undo evil-window-bottom nil t) + (def-cursor-undo evil-visual-restore t t) + (def-cursor-undo evil-visual-exchange-corners t t) + (def-cursor-undo evil-search-forward t t) + (def-cursor-undo evil-search-backward t t) + (def-cursor-undo evil-search-next t t) + (def-cursor-undo evil-search-previous t t) + (def-cursor-undo evil-search-word-backward t t) + (def-cursor-undo evil-search-word-forward t t) + (def-cursor-undo evil-search-unbounded-word-backward t t) + (def-cursor-undo evil-search-unbounded-word-forward t t) + (def-cursor-undo evil-goto-definition t t) + (def-cursor-undo evil-ex-search-next t t) + (def-cursor-undo evil-ex-search-previous t t) + (def-cursor-undo evil-ex-search-forward t t) + (def-cursor-undo evil-ex-search-backward t t) + (def-cursor-undo evil-ex-search-word-forward t t) + (def-cursor-undo evil-ex-search-word-backward t t) + (def-cursor-undo evil-ex-search-unbounded-word-forward t t) + (def-cursor-undo evil-ex-search-unbounded-word-backward t t))) + +(eval-after-load 'viper-cmd + '(progn + (define-advice viper-undo (:around (orig-func &rest args) cundo-viper-undo) + (let ((bu1 (car buffer-undo-list)) + (bu2 (cadr buffer-undo-list))) + (if (not (or (and (null bu1) + (or (integerp bu2) + (and (eq (car bu2) #'apply) + (eq (cadr bu2) #'cundo-restore-win)))) + (and (integerp bu1) (null bu2)) + (and (eq (car bu1) #'apply) + (eq (cadr bu1) #'cundo-restore-win)))) + (apply orig-func args) + ;; We're at a cursor-undo entry, use Emacs native undo + (setq this-command 'undo) + (undo)))) + (def-cursor-undo viper-backward-Word) + (def-cursor-undo viper-end-of-Word) + (def-cursor-undo viper-find-char-backward) + (def-cursor-undo viper-goto-line t t) + (def-cursor-undo viper-window-top t t) + (def-cursor-undo viper-window-bottom t t) + (def-cursor-undo viper-window-middle t t) + (def-cursor-undo viper-search-Next t t) + (def-cursor-undo viper-goto-char-backward) + (def-cursor-undo viper-forward-Word) + (def-cursor-undo viper-brac-function) + (def-cursor-undo viper-ket-function) + (def-cursor-undo viper-bol-and-skip-white) + (def-cursor-undo viper-goto-mark t t) + (def-cursor-undo viper-backward-word) + (def-cursor-undo viper-end-of-word) + (def-cursor-undo viper-find-char-forward) + (def-cursor-undo viper-backward-char) + (def-cursor-undo viper-previous-line) + (def-cursor-undo viper-forward-char) + (def-cursor-undo viper-search-next t t) + (def-cursor-undo viper-goto-char-forward) + (def-cursor-undo viper-forward-word) + (def-cursor-undo viper-line-to-top t t t) + (def-cursor-undo viper-line-to-middle t t t) + (def-cursor-undo viper-line-to-bottom t t t) + (def-cursor-undo viper-backward-paragraph nil t) + (def-cursor-undo viper-goto-col) + (def-cursor-undo viper-forward-paragraph nil t) + (def-cursor-undo viper-goto-eol) + (def-cursor-undo viper-paren-match t t) + (def-cursor-undo viper-goto-mark-and-skip-white t t) + (def-cursor-undo viper-backward-sentence t t) + (def-cursor-undo viper-forward-sentence t t) + (def-cursor-undo viper-next-line-at-bol) + (def-cursor-undo viper-repeat-find-opposite t t) + (def-cursor-undo viper-previous-line-at-bol) + (def-cursor-undo viper-search-forward t t) + (def-cursor-undo viper-beginning-of-line) + (def-cursor-undo viper-repeat-find t t)))