2

What would be the best way to implement an Emacs Lisp command that pops up the minibuffer, lets the user edit a search string there, and runs code to update the search results after each keystroke (or other command) that changes the minibuffer contents? To be specific, I'm asking about how to run a Lisp function in response to text changes; running a Lisp function in response to each keystroke may be close enough.

I'm talking about something that resembles the well-known incremental search (I-search) commands built into Emacs. isearch.el shows that I-search itself sets up isearch-mode-map such that each ASCII/multibyte character is manually mapped onto the isearch-printing-char command. (It loops over all the numerical character codepoints and assigns the same command to each codepoint).

If possible, it would probably be easier to have normal minibuffer editing, and simply have a hook function that gets called after every keystroke (or other command) that changes the minibuffer contents.

  • 1
    Add a buffer-local post-command-hook when you set up the minibuffer? – phils Dec 21 '19 at 10:37
  • The question is far too broad. What is it, specifically, that you are really trying to do. Saying "why" might make clearer what you're looking for, what problem you're trying to solve or what missing feature you want to implement. – Drew Dec 21 '19 at 15:19
  • 1
    [FYI: Emacs Isearch doesn't use the minibuffer (except if you use M-e to edit the search string). It uses the echo area, but with the cursor in that area. It can seem like it's using the minibuffer, but it's not.] – Drew Dec 21 '19 at 15:21
  • Thanks. I reworded the question to emphasize that I'm looking for a way (or ways) to run code in response to text changes in the input area. – Lassi Dec 21 '19 at 16:27
3

The minibuffer is a normal buffer, so you can use post-command-hook or after-change-functions (or both) to react to edits. You can use minibuffer-with-setup-hook to set them up:

(minibuffer-with-setup-hook
    (lambda ()
      (add-hook 'post-command-hook #'my-minibuf-after-cmd nil 'local)
      (add-hook 'after-change-functions #'my-minibuf-after-edit nil 'local))
  (read-string ...))

Using both can be useful: it's often a good idea to limit the amount of code run in after-change-functions either for performance reasons (a single command (such as filling a paragraph) can sometimes cause thousands of individual changes) or to avoid undesired interactions (after-change-functions is run right when the change takes place, before the command is finished, so it might run in an unusual context). In constrast post-command-hook is only run once per user interaction and "at top-level", so it is customary to limit after-change-functions to record the change in some global variable, and then use that var in post-command-hook.

| improve this answer | |
  • This worked perfectly. I ended up going with after-change-functions only. – Lassi Jan 3 at 16:11
  • Tip to anyone implementing incremental narrowing: use a custom invisibility spec. See the Invisible Text section in the Emacs Lisp Reference Manual for details. – Lassi Jan 3 at 16:12
  • Also (with-selected-window ...) and (recenter 0) can reset the display to the top of the buffer after each update to the search results. – Lassi Jan 3 at 16:14

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.