Wednesday, September 16, 2020

Emacs: Paired Commands --- Efficient Keyboard Interaction Using Hydra

Emacs: paired Commands — Efficient Keyboard Interaction Using Hydra

1 Executive Summary

Emacs commands such as yank and yank-pop or undo-only and undo-redo come in logical pairs. Such logical pairs (or command tuples in general) are characterized by the fact that one often invokes a sequence of these commands repeatedly until the desired result is attained. Package Hydra defines an elegant means of combining such command pairs into a single higher-level command that can be invoked more efficiently from the keyboard. To my knowledge, the only built-in Emacs command that exhibits a similar behavior is Emacs' text-scale-adjust.

1.1 Example: undo-oanly And undo-redo

Command undo-only allows one to move back through a sequence of changes.Command undo-redo in contrast undoes previous undo commands. In practice, one often moves back through a sequence of operations, with a few undo-redo invocations mixed in if one moves back too far.

Emacs convention would suggest that one bind these commands to separate keys, but this has a few disadvantages:

  • Binding separate keys to the two commands feels wasteful, especially as unbound keys in Emacs become increasingly precious.
  • It's not ergonomic to jump between the two commands in this pair unless one chooses the key-bindings carefully — as an example, I used C-x u for undo-only and _C-x C-u for undo-redo for many years. But now, a continuous sequence of undo operations requires too much chording.

1.2 A Hydra Inspired Solution

  1. Combine the two commands into a higher-level command.
  2. Bind that command to a single key, in my case C-/ (C-_ on a tty).
  3. When invoked, the Hydra-defined command does one of the following:
    1. Invokes undo-only on being called, then continues to read keys.
    2. Subsequent presses of / invoke command undo-only.
    3. Pressing \ invokes undo-redo.
    4. Any other key quits the Hydra-generated higher-level command.

This is defined as part of Emacspeak's collection of Muggles, as is evident, this is much nicer to your wrists.

 (kbd "C-/") 
 (defhydra emacspeak-muggles-undo-only/undo-redo
   (:body-pre (emacspeak-hydra-body-pre "Undo Smartly")
                (when hydra-is-helpful (emacspeak-hydra-toggle-talkative))
              :post emacspeak-hydra-post)
   ("?" (emacspeak-hydra-self-help "emacspeak-muggles-undo-only/undo-redo"))
   ("/" undo-only nil)
   ("\\" undo-redo nil)))

Note that the above can be simplified by dropping the emacspeak-specific clauses when not using Emacspeak.

1.3 Repeatable Yank/Pop From The Hydra Wiki

I originally discovered the metaphor of repeatable paired commands a few years ago from the Hydra Wiki which demonstrates a repeatable yank/pop. To summarize its effects:

  1. C-y invokes the hydra-generated higher-level command.
  2. Subsequent presses of y is the same of yank-pop.
  3. Pressing Y invokes yank-pop with a negative argument.
  4. Consequence of above, you can grab the last entry on the kill-ring by pressing Y after invoking the command — would take a lot more keystrokes in vanilla Emacs.
  5. You can also add smart affordances such as searching the kill-ring via interfaces such as ido.

1.4 Conclusion

As Emacs gains in functionality, grouping commands into higher-level abstractions, generating a single higher-level command that is bound to a key, and using that invocation context to implement the result of subsequent keypresses is an abstraction that generalizes well.

Tuesday, September 08, 2020

Searching GMail From GNUS

Using GMail Search Operators In GNUS

1 Executive Summary

I have been using the following to search GMail from GNUS for over 8 years now. The recent announcement of the nnselect back-end reminded me that I had never gotten to writing this up formally, So here goes. With the described solution in place, you can search your GMail from within GNUS using the same GMail Search operators that you're familiar with from within your Web Browser, e.g. searches of the form from: foo, subject:bar, after:date and combinations of the above.

2 Background

You can read email with GNUS, and many people read GMail with GNUS, however, the details of GMail setup with GNUS wont fit this margin. For my own GMail setup using Ceasar's excellent auth-xoauth2 package, see file gnus-gmail-prepare.el.

3 Leveraging GNUS Back-end NNIR To Search GMail

GMail is accessed from GNUS using the imap protocol. The imap specification defines a set of standard search operators; GMail itself defines a slightly different and arguably easier to use set of search operators. Module gm-nnir himplements both the standard IMap search operator as well as GMail's search operators; in practice, I have mostly only used the GMail Search operators in the last 8 years since implementing this module.

Without further ado, here is the code to enable GMail Search:

(defun gm-nnir-group-make-gmail-group (query)
  "Use GMail search syntax.
See for syntax. "
  (interactive "sGMail Query: ")  
  (let ((nnir-imap-default-search-key "imap")
        (q (format "X-GM-RAW \"%s\"" query)))
     ((gnus-group-group-name)           ; Search current group
       nil                              ; no extra params needed
       `(nnir-specs (nnir-query-spec (query ,q)))))
     (t (error "Not on a group.")))))

I bind the above to / by using

(define-key gnus-group-mode-map "/" 'gm-nnir-group-make-gmail-group)

4 Example Of Use

I am subscribed to list and email sent to that list gets GMail Label emacs-devel. In Gnus, I open that label as group Typing / on the group line and entering

from:rms after:2020/09/01

in the minibuffer results in the following:

1.1 Re: Good first issues to contribute Richard Stallman 05-Sep [5.3k]
2.1 Lars Ingebrigtsen is now one of the Emacs maintainers Richard Stallman 06-Sep [4.9k]
3.1 Re: A new user perspective about "Changes for emacs 28" Richard Stallman 07-Sep [5.5k]
4.5 Re: Changes for emacs 28 Richard Stallman 07-Sep [5.3k]
5.1  Richard Stallman 07-Sep <6.2k>
6.1  Richard Stallman 07-Sep <6.0k>
7.1  Richard Stallman 07-Sep <5.5k>
8.1  Richard Stallman 07-Sep <5.7k>

The search above creates an ephemeral group with matching messages appearing as shown above, you can read messages, reply to them and do anything else that you might ordinarily do within the GNUS interface.