Karthik's notes on Emacs Chat 24: Omar Antolin Camarena

| emacs

Here's a guest post from Karthik Chikmagalur in response to Emacs Chat 24: Omar Antolin Camarena.

  • 16:00 - Omar's embark-on-last-message is gold! I implemented it and have already used it a dozen times in an hour.
  • 17:00 - Omar mentions his tmp package for creating throwaway buffers. I use the scratch package for this. M-x scratch will open up a scratch buffer. If you had a region selected, that will be copied to the scratch buffer. By default, it will use the same major mode as the buffer you calling it from. Calling M-x scratch with a prefix arg will let you pick the major mode you want.

    I have some additional customizations to try to automagically pick a major mode based on what I have selected: https://github.com/karthink/.emacs.d/blob/3deed38c0e02e95fdfab6812c494b1736b945a1e/lisp/utilities.el#L36

    Also related is the edit-indirect package, which I'm sure you're aware of. I think of it as scratch's dual: scratch is for when I want to edit something without regard to its provenance, edit-indirect is for editing the source (exactly like org-edit-special).

    I also try to automagically guess which major mode a piece of text should be edited in. edit-indirect edits something that looks like a lisp form in lisp-interaction-mode, even if the origin is (say) this email composition buffer: https://github.com/karthink/.emacs.d/blob/3deed38c0e02e95fdfab6812c494b1736b945a1e/lisp/utilities.el#L67

  • 21:20 - You mention that you sometimes want to insert something into the minibuffer when you're in the minibuffer, but you end up inserting into the main buffer instead. Omar agreed that there is no easy fix for this.

    Omar, Daniel Mendler and I actually discussed this years ago and came up with a separate command to do this:

    (defun minibuffer-replace-input (&optional arg)
      "Replace original minibuffer input.
    
    When a recursive minibuffer is active, insert the current string
    into the original minibuffer input.  With prefix ARG, replace it
    instead."
      (interactive "P")
      (when (and (minibufferp) (> (minibuffer-depth) 1))
        (let* ((replacement (minibuffer-contents)))
          (unwind-protect (minibuffer-quit-recursive-edit)
            (run-at-time 0 nil
                         (lambda (rep)
                           (when arg (delete-minibuffer-contents))
                           (insert rep)
                           (pulse-momentary-highlight-one-line))
                         replacement)))))
    

    I don't need it every day, but when I do it's very handy.

  • 29:40 - Omar mentions that he prefers to have lots of commands to mark specific text objects instead of hammering expand-region (or expreg-expand). There is a (now) old package called easy-kill which does this, allowing you to define marking commands for different objects at point (e.g. s for sexp, w for word, l for line, d for defun etc). It's easy to add support for more objects because I think it integrates with thing-at-point. The marking command provided by this package is actually called easy-mark.

    But easy-kill / easy-mark is actually the best of both marking styles, because you can use SPC to cycle between marking all the different text objects at point. I've further integrated this with expand-region so that at any point in the easy-kill mark process I can expand the region as well: https://github.com/karthink/.emacs.d/blob/3deed38c0e02e95fdfab6812c494b1736b945a1e/lisp/setup-editing-extra.el#L250

  • 36:00 - Didn't know Omar is the reason vertico-grid-mode exists. That's fortunate, I use it all the time!
  • 44:00 - Omar's point about improving ffap to improve Embark's default action on files is great, really speaks to my sensibilities about composing features in Emacs in a way that provides multiplicative benefits.
  • 48:00 - NOPE! I use CANCELLED as a TODO kwd in Org, but the fact that it's not 4 letters long has bothered me forever. NOPE is much better.
  • 58:30 - Re: Omar's toggle map: this is something I think many users end up writing? Mine is a transient map: toggle-modes-transient.png that grows extra columns in specific major-modes: toggle-modes-full-transient.png

    But I appreciate that Omar uses a regular keymap instead of a visual menu, that's the Embark way. Transient menus are frustratingly non-composable with other Emacs features.

  • 1:00:00 - isearch-delete-wrong is actually built-into ISearch? Pressing C-g once should delete the non-matching part. It's possible he's customized C-g to quit Isearch right away.
  • 1:07:30 - I didn't understand Omar's practice of using embark-dwim to preview the result of any minibuffer command, like org-ql-find. Is this something you were able to reproduce?
    • I've been using dot-mode for almost as long as Emacs, to the point where I've often made the mistake of assuming it was an included feature. It uses simple heuristics, but works surprisingly well at capturing your intent on what the "bounds of an edit" should be in Emacs.
    • Omar mentioned that he stopped using multiple-cursors because the immediate feedback from all cursors inspired false confidence, as off-screen cursors could do something unexpected. I use a personal fork of a package called macrursors that's somewhere in between multiple-cursors and keyboard macros:

      https://github.com/corytertel/macrursors Fork: https://github.com/karthink/macrursors

      It's inspired by both multiple-cursors and meow's beacon-mode. It places cursors at the locations where the keyboard macro will be executed, but executes the full keyboard macro at each location at once, without immediate updates. This addresses the "false confidence" issue, but it does three other things that are very handy:

      • You can bound the region inside which cursors should be placed. The scope can be the paragraph, like in Omar's example, but also any other text object (defun, line etc), and you can cycle between the scopes or expand it with expand-region.
      • You can place cursors from most common actions, like at ISearch or Avy candidates (selectively or all at once), or at all text objects of a certain type inside the bounds.
      • You can "narrow" the buffer to only the cursor locations, fitting and verifying more of them on screen. When the macro runs, the selective display in the buffer persists for a second so you can scan the results:

        https://share.karthinks.com/macrursors-isearch-hideshow-demo.mp4

        Steps:

        1. Start ISearch and search for cl-defmethod
        2. Create cursors from all ISearch matches
        3. Selectively display only the cursors
        4. Show more context around the cursors
        5. Make a change (involving a kmacro counter)
        6. Finish. (The selective display persists for a second.)
        7. Examine the changes.

      It uses undo amalgamation by default so you can undo all the cursor changes except the original one in one step. Of course, your changes are stored in the kmacro ring so you can now apply them as regular keyboard macros too.

      Most of these features are probably present in multiple-cursors at this point, although I'm not sure about bounding cursor ranges with macrursors-select. But this has replaced the usual keyboard macro workflow for me, and not many people are aware of macrursors so I thought I'd mention it.

Thanks to Karthik for his notes! If you have any comments, please feel free to email him.

View Org source for this post