* 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'.
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.
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).
+
\f
* New Modes and Packages in Emacs 28.1
;; 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)
(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)
;;;;; ************************* EMACS CONTROL ************************* ;;;;;
+\f
+;; 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
(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