8

I have a function that automatically runs after the user does some action. However, the function takes a long time to complete. This means Emacs becomes unresponsive for a short time after every input. To fix this, I'd like to check whether the user has entered a new input and if so, cancel execution of this function and proceed with the user's input.

Is there a variable that holds queued keyboard inputs? If not, is there another way to check whether the user has pressed a button since a function started running?

  • 2
    You might want to look at while-no-input macro. It seems to do what you want: i.e. it cancels the execution of its body when new input appears. – wvxvw Nov 4 '16 at 20:12
  • @wvxvw Your comment looks to me like a good answer. You should post it as such! – Phil Hudson Nov 8 '16 at 23:48
5

OK, I'll try an expanded answer. The thing is, Emacs Lisp is single-threaded. You can spin more processes, but not inside the Emacs Lisp interpreter. However, the keyboard input that you need to read from user to interrupt your function needs to be processed by the same Emacs Lisp interpreter. This means that if your interpreter is stuck interpreting some Lisp code there may not be a way for you to interrupt it from within. Here's a place in the manual that describes this situation:

At the level of C code, quitting cannot happen just anywhere; only at the special places that check quit-flag. The reason for this is that quitting at other places might leave an inconsistency in Emacs’s internal state. Because quitting is delayed until a safe place, quitting cannot make Emacs crash.

https://www.gnu.org/software/emacs/manual/html_node/elisp/Quitting.html

So, how Emacs still has the C-g / canceling functionality?--There are timers and functions which do I/O (wait for events in command loop). Timers can arrange for your computations to be done in chunks, so that you would have points in your computations where you can look around and, if necessary, prevent all further computations. I/O functions can send quit signal. The macro with-keyboard-quit waits for this signal and once received will exit gracefully. However, your function needs to know to send this signal. This means that if your function prevents the user from sending keyboard input, then you will not be able to benefit from keyboard interrupts.

Bottom line: try wrapping your function's code into while-no-input. If this doesn't work, try rewriting your function in a way that it will let Emacs process keyboard events.

  • C-g can always interrupt lisp code, while-no-input just extends this to other keyboard input. – npostavs Nov 9 '16 at 13:35
  • @npostavs not really. Try interrupting something like (while t). (But before you do, ensure you've saved all your changes). – wvxvw Nov 9 '16 at 15:12
  • I typed (while t) into *scratch* and hit C-x C-e then C-g; I got the Quit message, Emacs didn't get stuck. – npostavs Nov 9 '16 at 15:16
  • @npostavs well, I'll sure find a combination that isn't possible to interrupt, but don't have time to look for it right now. I'm not sure how much *scratch* can be trusted though because every evaluation you make there is wrapped in a helper function. – wvxvw Nov 9 '16 at 15:27
  • I did manage to get stuck with (while t (condition-case _ (while t) (quit nil))), I think in theory that is interruptible if you could just hit C-g at the right time, but in practice you can't. – npostavs Nov 9 '16 at 15:52
3

I would go with input-pending-t. From the docs:

(input-pending-p &optional CHECK-TIMERS)

Return t if command input is currently available with no wait. Actually, the value is nil only if we can be sure that no input is available; if there is a doubt, the value is t.

A quick example:

(progn
  ;; I put this into a progn call so you can call it direcly from Emacs
  ;; if you are using the sx package
  (message "Try pressing a key in the next 2 seconds!")
  (sleep-for 2)
  (message (if (input-pending-p)
               "There is input waiting!"
             "No input, let’s move on!"))
  (sleep-for 2))
  • Perfect! Both answers are great, shame I could only accept one. Thanks! – jcaw Nov 9 '16 at 22:37

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.