3

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.

4
  • 1
    Add a buffer-local post-command-hook when you set up the minibuffer?
    – phils
    Dec 21, 2019 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, 2019 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, 2019 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, 2019 at 16:27

1 Answer 1

4

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.

3
  • This worked perfectly. I ended up going with after-change-functions only.
    – Lassi
    Jan 3, 2020 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, 2020 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, 2020 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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