Sometimes I must use Emacs (in X mode) over a network connection (for example, working on a cloud VM). I am pretty comfortable with the interface responsiveness inside a frame, although X is notoriously sensitive to network delays. But when it comes to any type of creating a new X window, be it a menu, a tooltip or a new frame, I'm looking at a few seconds wait. Tolerably painful if I want a new frame--this is not something I do often--but entirely unusable for a menu.

Unfortunately, flyspell, that I often use, is hard-coded to invoke x-popup-menu¹. Fixing a typo manually is certainly faster than popping up the menu, but there is not a function in it to add the word to dictionary, and this is where the menu gets me.

But nothing in principle prevents one from fbinding their own override version of x-popup-menu to intercept calls to it. So my question is, essentially, two-part:

  1. Is there a more or less generic way (a package, presumably) that converts pop-up menus into (ideally) ido prompts, or other in-frame interface, e. g., the usual completion buffer? This would be a win for me!

  2. If such a thing does not exist, is there a flyspell-specific modification: a package that hooks into flyspell, or a fork that does not use menus, or just anything but the popups?

I am aware of certain alternatives. For example, I use tramp mode over SSH when feasible and edit remote files in a local, responsive Emacs. But it often happens that all I have on me is a Windows laptop, so a remote X client is the only practical option.

¹ Tangentially, and curiously, for the (late) XEmacs flyspell has a separate code path that invokes the more general, window-system-agnostic popup-menu function, which exists, and is supposedly compatible, in GNU Emacs. Even though the XEmacs project has been thoroughly dead for a decade, being one of its major developers, I can't stop wondering what was the reason for this...

  • Not an answer to your question, but I've been using flyspell for 5+ years and I had completely forgotten that there is even a pop-up available. If flyspell-mode is active, the default is just to wavy-underline a misspelled word without using popups. When I am finished with the document, I run ispell-buffer and am then presented with a buffer of various options to choose from. You may be interested in doing something similar and bypass the pop-up feature entirely. I would have to trace the functions to figure out how you are getting the popup to even happen, but popups are not mandatory.
    – lawlist
    Aug 17 '19 at 7:39
  • @lawlist, thank you. ispell-buffer is not what I am looking for, as it prompts for everything it considers mispellings, including even parts of identifiers. 99% of what I edit is code. Ideally, I want to hit a chord and select a replacement word or an option to save to dictionary the word at the point, and this single word only. I am not aware of any ispell function that allows this--do you know of one? I think I must dig deeper into it.
    – kkm
    Aug 18 '19 at 5:15
  • You might want to check out my frog-menu package.
    – clemera
    Aug 20 '19 at 20:06
  • @clemera, although your package is not something I can use directly (remember, anything that wants to pop up is a disaster over a higher-latency X link, posframe won't cut it), the package you are giving in an example is probably just what I need (gitlab.com/d12frosted/flyspell-correct/blob/master/…). So thanks for the pointer, I'll give that one a try!
    – kkm
    Aug 21 '19 at 21:04
  • @kkm You can use frog-menu-type set to avy-side-window to avoid posframe.
    – clemera
    Aug 21 '19 at 21:32

Not quite sure what you're asking, but this might be a partial answer, although I don't think it applies to (x-)popup-menu.

If some code explicitly invokes (x-)popup-menu then it has chosen to use a popup menu. As far as I know, you have no choice in that case; it's hard-coded.

You could of course copy the source code, change it to use keyboard input, and use your own, modified commands instead of those provided by the library. You could also request that the library maintainer offer users a choice in this.

Here's what I was going to mention, which is not about popup menus:

If you customize option use-dialog-box to non-nil then mouse-initiated commands use dialog boxes instead of the keyboard to query you.

If you also customize use-file-dialog to non-nil then the same happens for file selection: a file-selection dialog box is used. In this case the dialog box is used even if the command is initiated from the keyboard.

On MS Windows, if you customize option w32-use-w32-font-dialog to non-nil then font selection uses the standard Windows font-selection dialog.

  • “If[…] code[…] invokes (x-)popup-menu then it has chosen to use a popup menu”--this is true, but then it's up to the environment how to interpret the UI presentation of a popup menu. “you have no choice in that case”--see defadvice, a high-level alternative to directly fbinding built-in functions. An example package is ido-yes-or-no that defadvice's the yes-or-no-p to change its UI behavior. Thanks for other pointers; it happened so that I already have these (and more) customized. But I shall indeed upvote your answer, as it is generally useful--to others... :(
    – kkm
    Aug 18 '19 at 5:07

I forgot that I asked this question before, got frustrated by the slow link again, started googling, and this was the first page that came up in search. Not a good sign... So I did it. Hope someone would use it one day.

This relies on string keys in plist be eq to strings in choices: this is how plist-get works; the strings must not be copied. Not that I can think of a scenario where that would make sense; a reminder just in case.

If someone would want to make that into a package, please do. This can be generalized by attaching a concrete function (like this one which parses the menu and returns something that flyspell-correct-word-before-point expects to get from its x-popup-menu call) as a property of a command symbol ('flyspell-correct-word-before-point in this case).

(require 'ido)

;; This can be generalized by attaching a property to command symbol.
(defun x-popup-menu--advice-around (f &rest args)
  (if (or (not (eq 'flyspell-correct-word-before-point this-command))
          (ignore-errors (mouse-event-p (car args))))
      (apply f args) ; Not our guy, or called via a mouse event, run normally.

    ;; Called via keyboard, do ido-completing-read.
    (let* ((mdesc (cadadr args))
           (prompt (concat (car mdesc) ": ")) ; Menu title.
           (mbody (cdr mdesc)) ; Menu body: (("foo" "foo") ... "" ... ) ; Menu body.
           (plist (mapcan (lambda (p) (and p (listp p)
                                           (list (car p) (cdr p))))
                          mbody)) ; (display_name (value) ... )
           (choices (mapcan (lambda (p) (and p (listp p)
                                             (list (car p))))
                            mbody))) ; (display_name ...)
      ;; Every value in the plist is a 1-element list.
      (plist-get plist
                 (ido-completing-read prompt choices nil t)))))

(add-hook 'flyspell-mode-hook
          (lambda ()
            (if (not flyspell-mode)
                (advice-remove 'x-popup-menu #'x-popup-menu--advice-around)
              (advice-add 'x-popup-menu :around #'x-popup-menu--advice-around)
              (define-key flyspell-mode-map [(control ?\.)]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.