5.2 Context Menu Customization

In Emacs, the implementation of a context menu is a list of functions placed in the customizable hook variable context-menu-functions. When the context menu is raised, typically via right-click (mouse-3), each function in context-menu-functions is run in order from first to last to populate the menu. Within each function is predicate logic to test if the context for adding a menu item is appropriate.

A context menu function has two arguments:

The click event can be translated to a cursor position (point).

The predicate logic implementation can take place at different levels:

  1. At the time of menu item instantiation.

    If the menu item is instantiated using the macro easy-menu-define, then the :visible and :active keywords can control how the menu item is displayed ((elisp)Easy Menu).

  2. At a level above menu item instantiation.

The context is determined by a predicate which can take into account different conditions, among them:

Separation of concerns between context menu functions are handled by convention. It is the responsibility of the implementer to manage any side-effect dependencies between different context menu functions, if any.

Shown below is a context menu function that demonstrates an example implementation of (2) above. Here it tests for major mode (text-mode) and to not apply when the current point is in an Org table nor a rectangle selection. Note that _click is ignored using the underscore notation, so the position of the context menu is unused.

(defun anju-context-menu-wordcount (menu _click)
  "Context menu hook function for word-count commands.

- MENU: menu
- CLICK: event

This hook is intended for `context-menu-functions'."
  (when (and (derived-mode-p 'text-mode)
             (not (anju-at-org-table-p))
             (not (anju-rectangle-selected-p)))
    (anju-context-menu-item-separator menu count-words-separator)
    (easy-menu-add-item menu nil [count-words count-words
                                  :label "Count Words"
                                  :help "Count words"]))
  menu)

It is up to the implementer to determine where to best define and apply predicates for a context.

Context Menu Current Point Handling

A design consideration for a context menu function with commands that act on a point is, which point: the current point or the point at which the context menu is raised?

A context menu function that does not change the point can unpleasantly surprise the user. For example, in an Org file window, while the current point is in body text, a user could raise the context menu over a heading. Should the context menu be populated with commands for body text or for a heading?

To clarify what the answer to that question could (or should) be, we will enumerate the following implementations of a menu item (command) that acts on a point:

  1. Use the current point p, ignoring the computed point p’ of the context menu position.
  2. Use the computed point p’, leaving the current point p unchanged.
  3. Move the current point to p’, displacing p.

Option (1) allows for context-menu-functions to be arbitrarily ordered at the cost of surprising the user when p and p’ are far from each other. The user must always adjust the current point to get the desired context menu.

Option (2) also allows for context-menu-functions to be arbitrarily ordered, however it adds implementation complexity as the context menu function and the commands within it must checkpoint and restore the current point p via save-excursion and mouse-set-point. The current point is unchanged, however more subtle behavior is introduced as menu items work on text where the context menu is raised, not where the current point is. This path could also result in a context menu with some menu items working on p’ and other menu items working on p, which might also lead to surprise.

Option (3) offers the least user surprise as it will move the current point to the position where the context menu is raised. This is a common approach taken by many other GUI applications. Option (3) makes the ordering of context-menu-functions significant, opening the possibility for unforeseen side-effects. One would be changing the insertion point for the context menu for paste operations.

Anju aspires to provide the least surprise to the user. As such all context menus which hold a command that depends on the current point will move it to where the context menu is raised. Anju makes the opinionated decision to take option (3).

The design guidelines taken by Anju for moving the current point will require the following conditions to be met: