On Emacs 28’ context menu and Unix mouse-usage in general

40
On Emacs 28’ context menu and Unix mouse-usage in general

It seems as though an irrational fear of the mouse dominates amongst Un*x dogmatists. Be it because their tools don’t integrate, or because it is a symbol or rebellion, a means to differentiate their prior ignorance from the enlightenment they have attained between the standard input and the standard output stream, the self-emancipation from the mouse is a rite of passage or a shameful burden on those who fail1 2.

As might be clear by my tone, I have little regard for this position. It certainly is worthwhile knowing that and how it is possible to avoid using the mouse, when keyboard shortcuts more convenient3, but there is no need for the artificial asceticism of ready-to-use technology, for the sake of purity and dogma.

The denunciation of the mouse usually involves invoking concepts such as the “home row”, or the cumbersome migration of the hand between keyboard and mouse. These might all be well and good, if I were a typist and as such all I did was to type. But this isn’t the case, I ponder and perceive, more than I write.

When debugging code or studying a program, it is quite comfortable to depend on only one hand, and have the other free to scribble or relax. The issue isn’t using a mouse or only using a keyboard, it is context switching.


And now for something mostly different: Speaking of “context”, here is something more concrete:

The more or less recent addition of context-menu-mode by the ever creative Juri Linkov to Emacs 28 is worth talking about.

The general idea is to provide a “context menu” to Emacs, that can be invoked by a simple right click. You might know these kinds of menus from your file manager or browser, where you get fewer or more options, depend on what you clicked. To enabled this feature in Emacs 28 or greater, one simply has to enable the aforementioned context-menu-mode minor mode.

It wouldn’t be Emacs if it ended here! It has to be configurable, extendable and improvable in accordance to the users needs and wants. That is where context-menu-functions comes into play. This is a list of functions used to populate the menu programmatically. In my case, I set it to:

(setq context-menu-functions
      '(context-menu-ffap
        occur-context-menu
        context-menu-region
        context-menu-undo
        context-menu-dictionary))

For example, the first function context-menu-ffap checks if there is a generalized file name wherever I clicked, and in that case it will insert a menu item for me to open whatever I clicked on:

An example of the context menu (keep in mind that the UI is specific to my Toolkit, to Emacs)

When first playing around with context-menu-mode, my experiments led me to contribute occur-context-menu that generates an occur buffer for the word or symbol at point. As mentioned, this is a nice instrument when debugging4.

I’d like to end with a few examples, demonstrating how to extend context-menu-functions with your own functions:

By default this functionality is bound to M-s h ., and I find it useful for debugging terse or complicated code. If we want to use the mouse instead, a ...-at-mouse command can be defined, together with a function placed in context-menu-functions that adds entries to the menu using define-key-after:

(defun highlight-symbol-at-mouse (e)
  "Highlight symbol at mouse click E."
  (interactive "e")
  (save-excursion
    (mouse-set-point e)
    (highlight-symbol-at-point)))

(defun context-menu-highlight-symbol (menu click)
  "Populate MENU with command to search online."
  (save-excursion
    (mouse-set-point click)
    (when (symbol-at-point)
      (define-key-after menu [highlight-search-separator] menu-bar-separator)
      (define-key-after menu [highlight-search-mouse]
        '(menu-item "Highlight Symbol" highlight-symbol-at-mouse
                    :help "Highlight symbol at point"))))
  menu)

(add-hook 'context-menu-functions #'context-menu-highlight-symbol)

Notice how context-menu-highlight-symbol only adds highlight-symbol-at-mouse if symbol-at-point indicates that there is a symbol that could be highlighted.

A popular feature from other text editors is to easily search for something online. The command mouse-online-search-at-point implements something like this. As before, the command could be bound directly to some mouse key5, but to add it to the context menu we define another hook function:

(require 'eww)

(defun mouse-online-search-at-point (e)
  "Search for word at point or selection."
  (interactive "e")
  (let ((query (if (region-active-p)
                   (buffer-substring (region-beginning)
                                     (region-end))
                 (save-excursion
                   (mouse-set-point e)
                   (thing-at-point 'symbol)))))
    (unless query
      (user-error "Nothing to search for"))
    (browse-url (concat
                 eww-search-prefix
                 (mapconcat #'url-hexify-string (split-string query) "+")))))

(defun context-menu-online-search (menu click)
  "Populate MENU with command to search online."
  (save-excursion
    (mouse-set-point click)
    (define-key-after menu [online-search-separator] menu-bar-separator)
    (define-key-after menu [online-search-at-mouse]
      '(menu-item "Online search" mouse-online-search-at-point
                  :help "Search for region or word online")))
  menu)

Modify eww-search-prefix to use a different search engine.

Another idea is to open man page, when clicking something like emacs(1). This uses man.el (but could just as well use woman.el) to display the requested page.

(defun man-at-mouse (e)
  "Open man manual at point."
  (interactive "e")
  (save-excursion
    (mouse-set-point e)
    (man (Man-default-man-entry))))

(defun man-context-menu (menu click)
  "Populate MENU with commands that open a man page at point."
  (save-excursion
    (mouse-set-point click)
    (when (save-excursion
            (skip-syntax-backward "^ ")
            (and (looking-at
                  "[[:space:]]*\([[:alnum:]_-]+([[:alnum:]]+)\)")
                  (match-string 1)))
      (define-key-after menu [man-separator] menu-bar-separator)
      (define-key-after menu [man-at-mouse]
    '(menu-item "Open man page" man-at-mouse
            :help "Open man page around mouse click"))))
  menu)

(add-hook 'context-menu-functions #'man-context-menu)

This could be extended to only offer displaying a man page, if it actually exists, and not just if something looks like a man page reference.


Emacs 28 has not yet been released, but pre-releases have been marked. If you want to try out this or other features, download the pre-release or clone the emacs-28 branch of development repository to try it out, and report bugs if you find any!

Join the pack! Join 8000+ others registered users, and get chat, make groups, post updates and make friends around the world!
www.knowasiak.com/register/

Vanic
WRITTEN BY

Vanic

“Simplicity, patience, compassion.
These three are your greatest treasures.
Simple in actions and thoughts, you return to the source of being.
Patient with both friends and enemies,
you accord with the way things are.
Compassionate toward yourself,
you reconcile all beings in the world.”
― Lao Tzu, Tao Te Ching