branch: externals/cursor-undo commit 2264853e77872122a085fb2733b43c80337c44ca Author: Luke Lee <luke@gauss> Commit: Luke Lee <luke@gauss>
* cursor-undo.el: Fix cursor-undo for read-only buffers. (cundo-track-screen-start, cundo-track-prev-point): new functions to make `def-cursor-undo' more clean, also fix read-only buffer issues. (def-cursor-undo): use the above functions to fix the issue. * README.org: Newly added README file to provide more info. --- README.org | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cursor-undo.el | 18 +++++++--- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/README.org b/README.org new file mode 100644 index 0000000000..d192a5a4b4 --- /dev/null +++ b/README.org @@ -0,0 +1,112 @@ +# Cursor Undo Mode -*- mode: org; -*- + +* _About_ + +Cursor-undo allows you to undo cursor movement commands using the +Emacs standard `undo' command. + +For frequent cursor movements such as 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. + +* _A "Brief" History_ + +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 ELPA package [[https://elpa.gnu.org/packages/brief.html][BriefMode]] 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, I have prepared ready `def-cursor-undo' advice sets for +standard Emacs cursor movement commands, Brief Editor mode, Viper +mode, and EVIL mode. + +* _Usage_ + +This package is released as a standard [[https://elpa.gnu.org/packages/cursor-undo.html][ELPA package]]. Just go to Emacs +main menu -> "Options" -> "Manage Emacs Packages" and install it. +Once this package is installed, you only need to enable cursor-undo +mode by adding the following line into your Emacs init file .emacs or +init.el: + +#+begin_src + (cursor-undo 1) +#+end_src + +That's all. You can now move your cursor around and use stand Emacs +`undo' (C-_, C-/ or C-x u) to move it back. Even in read-only buffers +you can still undo cursor movements. This is convenient especially +when browsing a huge file. + +* _Notes for read-only buffers_ + +Notice that the original `undo' cannot be performed in read-only +buffers. Here, I also advised the undo operation in read-only buffers +as long as the pending undo list is still a cursor movement. + +This also enables a editing trick: If you are just editing a big file +and moving the cursor to browse other parts but forgot where you were, +you can undo cursor movements to go back to your last edited position +by long-holding <undo> until the last editing command. However, by +doing so you risk missing your last edited operation as it might flash +by so quickly that you don't even notice but keep undoing other cursor +commands you don't want to undo. + +In this case, you can switch the buffer to read-only mode (by setting +`buffer-read-only' to t), then long press <undo> until the undo +command warns you that you're trying to edit a read-only buffer. At +this point, you're exactly at the latest editing position you are +looking for. You can then safely set `buffer-read-only' flag back to +NIL and continue your editing. + +* _Notes for EVIL mode user_ + +If you choose to use the 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. + +* _Technical notes_ + +Why do I use advice functions instead of the more generalized pre/post +command hooks? I bet you wouldn't like all the cursor movements in +your debugging session being recorded as undo entries; when you start +`undo'ing you will find the cursor replaying your previous debugging +session in reverse order. Similar effects happens in `semantic' mode +and many others. If it record *every* movement you will soon find it +difficult to use in many situations. Therefore, only the chosen +editing key commands are advised with `def-cursor-undo'. If you find +something missing, just advise it with your own `def-cursor-undo'. + + +Luke Lee diff --git a/cursor-undo.el b/cursor-undo.el index aafa26957b..b16175d81b 100644 --- a/cursor-undo.el +++ b/cursor-undo.el @@ -5,7 +5,7 @@ ;; Author: Luke Lee <luke.yx....@gmail.com> ;; Maintainer: Luke Lee <luke.yx....@gmail.com> ;; Keywords: undo, cursor -;; Version: 1.1.2 +;; Version: 1.1.3 ;; 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 @@ -135,6 +135,17 @@ ;; "Warning: Unused lexical variable ‘prev-screen-start’". (defvar prev-screen-start) +(defun cundo-track-screen-start (prv-screen-start) + (let ((entry (list 'apply 'cundo-restore-win prv-screen-start))) + (if (eq buffer-undo-list 't) + (setq buffer-undo-list entry) + (push entry buffer-undo-list)))) + +(defun cundo-track-prev-point (prev-point) + (if (eq buffer-undo-list 't) + (setq buffer-undo-list (list prev-point)) + (push prev-point 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. @@ -227,10 +238,9 @@ relative screen position (screen-pos=NIL) nor `point' position (no-move=t).")) (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))) + '((cundo-track-screen-start prev-screen-start))) ,@(unless no-move - '((push prev-point buffer-undo-list))) + '((cundo-track-prev-point prev-point))) ;;(abbrevmsg (format "c=%S,%S b=%S" last-command this-command ;; buffer-undo-list) 128) ;; DBG (undo-boundary))