this post was submitted on 20 Nov 2023
1 points (100.0% liked)

Emacs

313 readers
2 users here now

A community for the timeless and infinitely powerful editor. Want to see what Emacs is capable of?!

Get Emacs

Rules

  1. Posts should be emacs related
  2. Be kind please
  3. Yes, we already know: Google results for "emacs" and "vi" link to each other. We good.

Emacs Resources

Emacs Tutorials

Useful Emacs configuration files and distributions

Quick pain-saver tip

founded 1 year ago
MODERATORS
 

Hey, I was working on this one off and on for a few days after briefly trying out skeleton-mode, yasnippet, and some other stuff, and not really being too happy with them. I find that I have a lot of repetitive editing tasks where I need to do something to a small block of code a lot, but in the process change some names or values in a way that's just a little bit different each time. Normally this is where people would start to reach for yasnippet and auto-yasnippet, which is fine if that works for them, but personally that's just a bit more heavyweight and powerful than what I normally need. What I wanted was just a way to enhance a regular Emacs keyboard macro to support that sort of thing, so I wrote this. If it helps you too, wonderful!

To use, just press C-x Q (that's a capital Q, not a lowercase q) during keyboard macro recording, and press your normal enter/return/minibuffer-exit when you're done. I went through a lot of trouble figuring out how to make the minibuffer exit also exit the sub-macro recording!

;; Keyboard macro enhancement. If you call this, instead of
;; kbd-macro-query, it will prompt the user for a value. This value
;; will then be inserted into the buffer. Every time you call the
;; macro, you can provide a different value.
;;
;; Alternatively, you can call this with a prefix argument. If you do
;; this, you will be prompted for a symbol name. Instead of the value
;; being inserted into the buffer, it will be saved in the symbol
;; variable. You can then manipulate it or do whatever you want with
;; that symbol as part of the keyboard macro. Just, when you do this,
;; make sure you don't use minibuffer history at all when defining the
;; macro, or you can get some unexpected behavior if you save your
;; macro for later use and try it a few hours later!
(defun config:macro-query (symbol)
  (interactive
   (list (when current-prefix-arg
           (intern (read-from-minibuffer "symbol: ")))))
  (cl-flet ((internal-exit ()
              (interactive)
              (exit-recursive-edit)))
    (let ((making-macro defining-kbd-macro)  ;; Save value.
          (temp-map (make-sparse-keymap)))
      ;; Temporarily bind what is normally C-M-c (exit-recursive-edit)
      ;; to RET, so RET will work in the spawned minibuffer.
      (set-keymap-parent temp-map minibuffer-local-map)
      (substitute-key-definition 'exit-minibuffer #'internal-exit temp-map)
      (let ((exit-fn (set-transient-map temp-map (-const t))))
        (cl-flet ((also-quit-minibuffer ()
                    ;; When this is called (advice after
                    ;; recursive-edit), this-command should be
                    ;; whatever was just used to exit the recursive
                    ;; edit / minibuffer. Usually RET. Push that onto
                    ;; the unread commands, and it will immediately
                    ;; get picked up and executed. We also want to use
                    ;; this moment to turn off the transient map.
                    (funcall exit-fn)
                    (when making-macro
                      (setq unread-command-events
                            (nconc (listify-key-sequence (this-command-keys))
                                   unread-command-events)))))
          (advice-add 'recursive-edit :after #'also-quit-minibuffer)
          (unwind-protect
              (let ((input (minibuffer-with-setup-hook
                               (lambda ()
                                 (kbd-macro-query t))
                             (read-from-minibuffer "Value: "))))
                (if symbol
                    (set symbol input)
                  (insert input)))
            ;; Ensure that the advice and minibuffer map goes back to
            ;; normal.
            (advice-remove 'recursive-edit #'also-quit-minibuffer)
            (funcall exit-fn)))))))
(global-set-key (kbd "C-x Q") 'config:macro-query)
no comments (yet)
sorted by: hot top controversial new old
there doesn't seem to be anything here