[Top][All Lists]

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

Re: Signal `quit' in a `font-lock-fontify-region-function'

From: Paul Pogonyshev
Subject: Re: Signal `quit' in a `font-lock-fontify-region-function'
Date: Sun, 30 Jun 2019 01:49:46 +0200

On Sat, 29 Jun 2019 at 23:26, Stefan Monnier <address@hidden> wrote:
> > I'm not sure if this is flexible enough.  I don't like the idea of C-g
> > already invoking some code (that e.g. disables font-locking).
> [...] It definitely wouldn't disable font-locking right away.
> The disabling would only be in response to a "large" number of C-g,
> which presumably should only be needed in extreme circumstances
> (basically when currently the user ends up killing Emacs instead).

So, basically, as I understand, C-g handler in your proposal would
look like (I know it is in C code, but...), simplified:

    (defun handle-C-g (...)
      (when registered-many-C-g-callback  ; variable set by e.g.
font-locking code
        (setq num-C-g-presses (1+ num-C-g-presses))
        (when (> num-C-g-presses 3)
          (funcall registered-many-C-g-callback))))

and font-locking code would be roughly

    (let ((registered-many-C-g-callback (lambda () (setq font-lock-mode nil))))
      ; call fontification function, process its result etc.

That's what I meant: here `handle-C-g' already invokes some callback
that higher level (in this case font-locking) registers.

I propose that instead font-locking code simply says "I will process
multiple C-g presses somehow, please count them" by let-binding
`count-C-g-presses' to t.  The idea is to not invoke the "abort"
callback immediately when the 3rd C-g is pressed, but instead let the
iteration finish gracefully and _then_ let the higher-level code
(font-locking) abort or not based on all the information it has by
now.  Usually this information would be just result of
`(too-many-C-g-recently)', but could be anything in the future, so we
are more flexible.

> > So, I'd like to be able to _handle_ `quit' signal in the function
> > itself and stop extending the region and return immediately, so that I
> > don't throw away work on a considerable region of text that has been
> > done already.
> I don't understand why you say "don't throw away work on a considerable
> region of text that has been done already": if you're in the middle of
> extending the region, presumably no "work" has been done yet.

For example, my function decides to fontify 1000 lines of text and by
the time C-g is pressed (triggering the signal) it has already
fontified 500 of them (this is already more than what font-locking
code wanted, because I extended the requested region).  I'd like to be
able to notify font-locking code about the work that has been done,
even if I planned to do even more.  If I raise `quit' signal or let
C-g raise it by default, all this work is thrown away.

> Could `while-no-input` be usable in this case?

This macro uses `with-local-quit', which, as I understand will rethrow
signal `quit' after the body.  As a result, font-locking code will
still see my function terminate non-locally, deem the whole invocation
failed and set property `fontified' to nil.  So no, to achive what I
want I need to let my function return normally somehow.

> > I'm not sure if it is the best solution, but I'd propose C-g to be
> > counted not when `quit' signal is caught, but when it would be
> > emitted, even if `inhibit-quit' is t.  And font-locking code would
> > look something like this: [...]
> I think if C-g resulted in (signal 'quit ...), then presumably we exited
> this code, so the only really interesting case should be the one where
> we just have `quit-flag` set to t.

Yes, but the point is in that I want to avoid C-g resulting in the
signal by binding `inhibit-quit' inside my fontification function.
But still have C-g handler note that C-g is pressed and let
fontification code find if it is being pressed too often.

> Maybe we should change the C-g code so it sets `quit-flag` to the
> number of C-g that are pending.

This generally sounds like a good idea: I'm always in favour of having
more information.  However, in my case it is not enough.

I guess it is easier to explain by pseudocode of my fontification

    (defun my--fontify-region (region-start region-end _loudly)
      ;; Very fast, so always work on large regions to avoid setup overhead.
      (setq region-end (+ region-start 50000))
      ;; Here comes some internal setup.
      (let ((fontified-up-to region-start)
            (inhibit-quit    t))
        ;; While `quit' is inhibitted, the below loop will still be
aborted on C-g.
        (while (and (< fontified-up-to region-end) (null quit-flag))
          ...  ; do some work on the next small chunk of text; this is
very fast,
               ; but as the region is quite big, the loop is iterated many times
        ;; Always exit normally.  Font-locking code will find out if
we suppressed
        ;; a C-g through other means, i.e. using (too-many-C-g-recently).
        (setq quit-flag nil)
        `(jit-lock-bounds ,region-start . ,fontified-up-to)))

Note that the function doesn't suppress any other signals/errors.


reply via email to

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