emacs-diffs
[Top][All Lists]
Advanced

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

master 12409c9: New transient mode 'repeat-mode' to allow shorter key se


From: Juri Linkov
Subject: master 12409c9: New transient mode 'repeat-mode' to allow shorter key sequences (bug#46515)
Date: Wed, 17 Feb 2021 13:05:07 -0500 (EST)

branch: master
commit 12409c9064c386a496dcbdca76b790108f6c1cad
Author: Juri Linkov <juri@linkov.net>
Commit: Juri Linkov <juri@linkov.net>

    New transient mode 'repeat-mode' to allow shorter key sequences (bug#46515)
    
    * doc/emacs/basic.texi (Repeating): Document repeat-mode.
    
    * lisp/repeat.el (repeat-exit-key): New defcustom.
    (repeat-mode): New global minor mode.
    (repeat-post-hook): New function.
    
    * lisp/bindings.el (undo-repeat-map): New variable.
    (undo): Put 'repeat-map' property with
    'undo-repeat-map'.
    (next-error-repeat-map): New variable.
    (next-error, previous-error): Put 'repeat-map' property with
    'next-error-repeat-map'.
    
    * lisp/window.el (other-window-repeat-map): New variable.
    (other-window): Put 'repeat-map' property with
    'other-window-repeat-map'.
    (resize-window-repeat-map): New variable.
    (enlarge-window, enlarge-window-horizontally)
    (shrink-window-horizontally, shrink-window): Put 'repeat-map'
    property with 'resize-window-repeat-map'.
---
 doc/emacs/basic.texi | 11 ++++++++
 etc/NEWS             | 11 ++++++++
 lisp/bindings.el     | 17 +++++++++++++
 lisp/repeat.el       | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 lisp/window.el       | 22 ++++++++++++++++
 5 files changed, 132 insertions(+)

diff --git a/doc/emacs/basic.texi b/doc/emacs/basic.texi
index 444b28f..8bf52d5 100644
--- a/doc/emacs/basic.texi
+++ b/doc/emacs/basic.texi
@@ -880,3 +880,14 @@ characters.  You can repeat that command (including its 
argument) three
 additional times, to delete a total of 80 characters, by typing @kbd{C-x
 z z z}.  The first @kbd{C-x z} repeats the command once, and each
 subsequent @kbd{z} repeats it once again.
+
+@findex repeat-mode
+  Also you can activate @code{repeat-mode} that temporarily enables
+a transient mode with short keys after a limited number of commands.
+Currently supported shorter key sequences are @kbd{C-x u u} instead of
+@kbd{C-x u C-x u} to undo many changes, @kbd{C-x o o} instead of
+@kbd{C-x o C-x o} to switch several windows, @kbd{C-x @{ @{ @} @} ^ ^
+v v} to resize the selected window interactively, @kbd{M-g n n p p} to
+navigate @code{next-error} matches.  Any other key exits transient mode
+and then is executed normally.  The user option @code{repeat-exit-key}
+defines an additional key to exit this transient mode.
diff --git a/etc/NEWS b/etc/NEWS
index 5c7acfd..b96bcd9 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2068,6 +2068,17 @@ the behavior introduced in Octave 3.8 of using a 
backslash as a line
 continuation marker within double-quoted strings, and an ellipsis
 everywhere else.
 
+** Repeat
+
++++
+*** New transient mode 'repeat-mode' to allow shorter key sequences.
+You can type 'C-x u u' instead of 'C-x u C-x u' to undo many changes,
+'C-x o o' instead of 'C-x o C-x o' to switch several windows,
+'C-x { { } } ^ ^ v v' to resize the selected window interactively,
+'M-g n n p p' to navigate next-error matches.  Any other key exits
+transient mode and then is executed normally.  'repeat-exit-key'
+defines an additional key to exit mode like 'isearch-exit' (RET).
+
 
 * New Modes and Packages in Emacs 28.1
 
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 2f4bab1..7111ae6 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -950,6 +950,12 @@ if `inhibit-field-text-motion' is non-nil."
 ;; Richard said that we should not use C-x <uppercase letter> and I have
 ;; no idea whereas to bind it.  Any suggestion welcome.  -stef
 ;; (define-key ctl-x-map "U" 'undo-only)
+(defvar undo-repeat-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "u" 'undo)
+    map)
+  "Keymap to repeat undo key sequences `C-x u u'.  Used in `repeat-mode'.")
+(put 'undo 'repeat-map 'undo-repeat-map)
 
 (define-key esc-map "!" 'shell-command)
 (define-key esc-map "|" 'shell-command-on-region)
@@ -1036,6 +1042,17 @@ if `inhibit-field-text-motion' is non-nil."
 
 (define-key ctl-x-map "`" 'next-error)
 
+(defvar next-error-repeat-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map    "n" 'next-error)
+    (define-key map "\M-n" 'next-error)
+    (define-key map    "p" 'previous-error)
+    (define-key map "\M-p" 'previous-error)
+    map)
+  "Keymap to repeat next-error key sequences.  Used in `repeat-mode'.")
+(put 'next-error 'repeat-map 'next-error-repeat-map)
+(put 'previous-error 'repeat-map 'next-error-repeat-map)
+
 (defvar goto-map (make-sparse-keymap)
   "Keymap for navigation commands.")
 (define-key esc-map "g" goto-map)
diff --git a/lisp/repeat.el b/lisp/repeat.el
index 795577c..84a613d 100644
--- a/lisp/repeat.el
+++ b/lisp/repeat.el
@@ -329,6 +329,77 @@ recently executed command not bound to an input event\"."
 
 ;;;;; ************************* EMACS CONTROL ************************* ;;;;;
 
+
+;; And now for something completely different.
+
+;;; repeat-mode
+
+(defcustom repeat-exit-key nil
+  "Key that stops the modal repeating of keys in sequence.
+For example, you can set it to <return> like `isearch-exit'."
+  :type '(choice (const :tag "No special key to exit repeating sequence" nil)
+                (key-sequence :tag "Key that exits repeating sequence"))
+  :group 'convenience
+  :version "28.1")
+
+;;;###autoload
+(define-minor-mode repeat-mode
+  "Toggle Repeat mode.
+When Repeat mode is enabled, and the command symbol has the property named
+`repeat-map', this map is activated temporarily for the next command."
+  :global t :group 'convenience
+  (if (not repeat-mode)
+      (remove-hook 'post-command-hook 'repeat-post-hook)
+    (add-hook 'post-command-hook 'repeat-post-hook)
+    (let* ((keymaps nil)
+           (commands (all-completions
+                      "" obarray (lambda (s)
+                                   (and (commandp s)
+                                        (get s 'repeat-map)
+                                        (push (get s 'repeat-map) keymaps))))))
+      (message "Repeat mode is enabled for %d commands and %d keymaps"
+               (length commands)
+               (length (delete-dups keymaps))))))
+
+(defun repeat-post-hook ()
+  "Function run after commands to set transient keymap for repeatable keys."
+  (when repeat-mode
+    (let ((repeat-map (and (symbolp this-command)
+                           (get this-command 'repeat-map))))
+      (when repeat-map
+        (when (boundp repeat-map)
+          (setq repeat-map (symbol-value repeat-map)))
+        (let ((map (copy-keymap repeat-map))
+              keys mess)
+          (map-keymap (lambda (key _) (push key keys)) map)
+
+          ;; Exit when the last char is not among repeatable keys,
+          ;; so e.g. `C-x u u' repeats undo, whereas `C-/ u' doesn't.
+          (when (or (memq last-command-event keys)
+                    (memq this-original-command '(universal-argument
+                                                  universal-argument-more
+                                                 digit-argument
+                                                  negative-argument)))
+            ;; Messaging
+            (setq mess (format-message
+                        "Repeat with %s%s"
+                        (mapconcat (lambda (key)
+                                     (key-description (vector key)))
+                                   keys ", ")
+                        (if repeat-exit-key
+                            (format ", or exit with %s"
+                                    (key-description repeat-exit-key))
+                          "")))
+            (if (current-message)
+                (message "%s [%s]" (current-message) mess)
+              (message mess))
+
+            ;; Adding an exit key
+            (when repeat-exit-key
+              (define-key map repeat-exit-key 'ignore))
+
+            (set-transient-map map)))))))
+
 (provide 'repeat)
 
 ;;; repeat.el ends here
diff --git a/lisp/window.el b/lisp/window.el
index 2d0a73b..cfd9876 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -10252,6 +10252,28 @@ displaying that processes's buffer."
 (define-key ctl-x-4-map "1" 'same-window-prefix)
 (define-key ctl-x-4-map "4" 'other-window-prefix)
 
+(defvar other-window-repeat-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "o" 'other-window)
+    map)
+  "Keymap to repeat other-window key sequences.  Used in `repeat-mode'.")
+(put 'other-window 'repeat-map 'other-window-repeat-map)
+
+(defvar resize-window-repeat-map
+  (let ((map (make-sparse-keymap)))
+    ;; Standard keys:
+    (define-key map "^" 'enlarge-window)
+    (define-key map "}" 'enlarge-window-horizontally)
+    (define-key map "{" 'shrink-window-horizontally)
+    ;; Additional keys:
+    (define-key map "v" 'shrink-window)
+    map)
+  "Keymap to repeat window resizing commands.  Used in `repeat-mode'.")
+(put 'enlarge-window 'repeat-map 'resize-window-repeat-map)
+(put 'enlarge-window-horizontally 'repeat-map 'resize-window-repeat-map)
+(put 'shrink-window-horizontally 'repeat-map 'resize-window-repeat-map)
+(put 'shrink-window 'repeat-map 'resize-window-repeat-map)
+
 (provide 'window)
 
 ;;; window.el ends here



reply via email to

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