emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: question about `sit-for' and `C-g'


From: Drew Adams
Subject: RE: question about `sit-for' and `C-g'
Date: Sun, 13 May 2012 09:10:37 -0700

> > It seems that all I had to do was define a substitute for 
> > `sit-for' that does exactly the same thing but also binds
> > `inhibit-quit' to t around the call to `read-event'.
> 
> Or you can just do that binding around your call to `sit-for'.

Of course.

[But I already use a substitute for `sit-for' in Icicle mode, which checks
`overriding-terminal-local-map' against `icicle-universal-argument-map', in
addition to `universal-argument-map'.  (The commands in the former map echo the
prefix arg, which can be used separately for individual completion candidates.)

And I do want `C-g' to be handled by its current keybinding for each call to
`sit-for' in my code.  For both of these reasons it makes sense to bind
`inhibit-quit' in `icicle-sit-for'.]

> At top level, C-g is bound to `keyboard-quit'.  In the 
> minibuffer, it's typically bound to `abort-recursive-edit' 
> (since reading from the minibuffer is a special, unadvertised 
> kind of recursive edit).

Yes, but neither of those is involved here.  In fact, there is _no_ key binding
of `C-g' that is involved.

And searching Emacs Lisp and C source code for `ding', `beep', SIGQUIT, and
"Quit" showed that such occurrences (and there are a _lot_ of them) are not
involved either.

In sum, it was not easy to find what was handling the `C-g' in this situation.
I finally tracked it down to a call to `sit-for' and thus ultimately to C
function `read_char'.

That call to `sit-for' was so ancillary to the general user interaction in the
minibuffer that I did not even think of it at first.  It was only after
searching unsuccessfully for what code might be intercepting the `C-g' and just
printing `Quit', and a fair amount of debugging (including via
`read-bell-function), that I eventually discovered the `sit-for' and its `C-g'
gotcha.

> Much more important, though, is 
> that while not in a command loop (e.g., during `sit-for'), 
> `quit-char' is turned directly into a `quit' signal (not 
> SIGQUIT; that's an unrelated Unix concept).  That signal 
> unwinds the call stack until it hits a recursive editing 
> level (which catches all exceptions) and prints "Quit".

Yes, that's what I was trying to say wrt the code in `read_char'.

(I think you meant `quit_char', not `quit-char'.  See also thread "`quit_char',
`set-quit-char', and `set-input-mode'", which suggests that maybe we should have
a Lisp-level equivalent of the C variable `quit_char'.)

(SIGQUIT might be unrelated to a `quit' signal, but it is present quite a bit in
the Emacs C code and even a bit in the Lisp code.)

> From a conceptual if not a C-implementation point of view, 
> then, `read-char' and so forth do not handle C-g in any 
> special way.  That is to say, it does what it always does, 
> and aborts the currently running Lisp code.

"Aborts the currently running Lisp code" does not, in this case, mean that it
returns to top level.  It seems to mean that it aborts `sit-for' (?).

> As it is global and standard, they do not document that behavior;
> see (elisp)Quitting.

Yes, I read that, of course (see bug #11458).  Including this part:

 "Typing `C-g' while the command loop is waiting for
  keyboard input does not cause a quit; it acts as an
  ordinary input character."

It could be argued that `sit-for' matches that description, at least partly, in
its use of `read-event'.  But it does not match the behavior described.

Or rather, `read-event' & compagnie, while reading keyboard input, do not seem
to just treat `C-g' as an ordinary input character, but rather they handle it
directly (in addition to returning it).  They seem to "cause a quit", AFAICT.

Or maybe I'm not paying close enough attention to "command loop" here, and
focusing on "while ... waiting for keyboard input".  IOW, maybe I'm trying to
read more (or less) into this passage than was meant.

> > My next question is whether `sit-for' itself should do the 
> > same thing: bind `inhibit-quit' to t.
> 
> Surely not.  Aborting a command like yours in a normal 
> minibuffer would then abort the whole minibuffer (because 
> you'd get C-g in `unread-command-events' AND the `quit' 
> signal).

Binding it would, and does, simply cause `C-g' in the minibuffer to do what
`C-g' in the minibuffer does in general and is meant to do there:
`abort-recursive-minibuffer'.  TRT?

Not binding it causes `C-g' to just beep and print `Quit', which is not really
what a user wants, IMO.  In that case, the `C-g' does not really quit anything,
AFAICT, except perhaps the `sit-for' itself.

What a user expects when s?he uses `C-g' in the minibuffer is
`abort-recursive-edit', IMO.  S?he does not know or care whether a `sit-for'
might be in progress.

But I won't insist that `sit-for' should simply pass the `C-g' on to the calling
context so that its current binding (e.g. `abort-recursive-edit' in the
minibuffer) can take effect.  I only posed the question, for consideration by
those of you who understand this stuff better than I.

> And while it's easy to add a let-binding to get the 
> behavior you want, you couldn't ever go back were it the default.

I have to agree with that.  So part of the question I posed is whether one would
ever want/need to go back.  Dunno.  I don't see any reason for that, offhand,
but I agree that such a change to `sit-for' would remove the option (from
`sit-for').  Maybe it's as simple as asking whether `C-g' during `sit-for'
should quit only the `sit-for' (?), i.e., whether that is ever useful.

----

More importantly, let me just say that it took me a few _hours_ to figure out
what was going on.

Had _any_ of the functions I came across in my quest documented that it handled
`C-g' directly as a special case (preventing it from being handled in the
caller, via its key binding, etc.), then I would have found the answer
(understood) immediately.  (By "special case" I mean that it is treated there
differently from other events/chars that are read.)

That includes not only the doc strings of Lisp functions (e.g. `sit-for',
`read-event'), but even the code comments for the C functions (e.g.
`read_filtered_event', `read_char') - nada about `C-g'.

It wasn't until I finally got to the `read_char' code that I saw anything that
indicated that `C-g' was being handled.  (And yes, `read_char' is something very
different from `read-char'.)

Looking at the code for `sit-for', unless you happen to know that `read-event'
handles `C-g' directly, you (I) can easily get the impression that the `C-g'
char gets pushed onto `unread-command-events'.  (And that's the behavior I would
in fact expect/prefer.)

So yes, I disagree that this should not be documented - and yes, at the
individual function level.  It certainly does not hurt, IMO, to let users of a
function know that a `C-g' char is handled specially there, even if this is, as
you seem to say, something that people should know generally from reading (elisp
`Quitting').

Anyway, I do appreciate your reply/explanation, Davis.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]