Josh Munn's Website

TIL: Emacs Repeat Maps

I learned about Emacs’ repeat-mode from the dape docs. It’s explained effectively in this blog post by Karthinks. It turns out repeat-mode has recently been added to Emacs core — I’m using 30.0.91, which has a bunch of my favourite recent packages included. It’s a very convenient package. I’m using it with dape, smerge, and flymake, and it really helps cut down on repeated modifiers.

I also discovered that use-package has a neat shorthand for creating repeat maps. Here are my definitions for flymake and smerge:

(use-package smerge-mode
  :elpaca nil
  :bind
  (:repeat-map
   josh/smerge-mode-repeat-map
   ("n" . smerge-next)
   ("p" . smerge-prev)
   ("u" . smerge-keep-upper)
   ("l" . smerge-keep-lower)
   ("a" . smerge-keep-all)))

(use-package flymake
  :elpaca nil
  :after project
  :config
  (setq josh/allowed-flymake-commands
	'(("ruff" "--quiet" "--stdin-filename=stdin" "-")))
  (put 'python-flymake-command 'safe-local-variable
       (lambda (command) (member command josh/allowed-flymake-commands)))
  (defun josh/window-bottom-left-side-window-p (buf act)
    (with-current-buffer buf (member major-mode '(flymake-diagnostics-buffer-mode))))

  (add-to-list 'display-buffer-alist
               '(josh/window-bottom-left-side-window-p
                 (display-buffer-in-side-window)
                 (window-height . 0.30)
                 (window-width . 0.55)
                 (dedicated . t)
                 (side . bottom)
                 (slot . 0)
                 (window-parameters . ((mode-line-format . 'none)))))

  (defun josh/hide-flymake-buffer-diagnostics ()
    (interactive)
    (dolist (buf (buffer-list))
      (when (string-prefix-p "*Flymake diagnostics" (buffer-name buf))
        (let ((window (get-buffer-window (buffer-name buf))))
          (when window
            (quit-window nil window))))))

  (bind-key "C-c ! l" #'flymake-show-buffer-diagnostics 'flymake-mode-map)
  (bind-key "C-c ! n" #'flymake-goto-next-error 'flymake-mode-map)
  (bind-key "C-c ! p" #'flymake-goto-prev-error 'flymake-mode-map)
  (bind-key "C-c ! q" #'josh/hide-flymake-buffer-diagnostics 'flymake-mode-map)

  :bind
  (:repeat-map
   josh/flymake-repeat-map
   ("n" . flymake-goto-next-error)
   ("p" . flymake-goto-prev-error)
   ("q" . josh/hide-flymake-buffer-diagnostics)
   ("l" . flymake-show-buffer-diagnostics)))

The :bind (:repeat-map) form is more concise than the vanilla way of creating a repeat map:

 (use-package smerge-mode
   :elpaca nil
-  :config
-  (defvar josh/smerge-mode-repeat-map
-    (let ((map (make-sparse-keymap)))
-      (define-key map (kbd "n") #'smerge-next)
-      (define-key map (kbd "p") #'smerge-prev)
-      (define-key map (kbd "u") #'smerge-keep-upper)
-      (define-key map (kbd "l") #'smerge-keep-lower)
-      (define-key map (kbd "a") #'smerge-keep-all)
-      map)
-    "repeat-mode map for smerge-mode")
+  :bind
+  (:repeat-map
+   josh/smerge-mode-repeat-map
+   ("n" . smerge-next)
+   ("p" . smerge-prev)
+   ("u" . smerge-keep-upper)
+   ("l" . smerge-keep-lower)

The smerge map makes zipping through a diff much more efficient. Instead of “C-c ^ n, C-c ^ u, C-c ^ n, …” I can use “C-c ^ n, u, n, u, l, …”.

See the use-package docs for repeat maps.

Tags: