help-gnu-emacs
[Top][All Lists]
Advanced

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

Re: Returning variable "references" under lexical binding


From: Stefan Monnier
Subject: Re: Returning variable "references" under lexical binding
Date: Wed, 22 May 2013 21:01:59 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3.50 (gnu/linux)

> (defun run-periodically-when-idle (seconds func)
>   (let (timer count)
>     (labels ((on-first-idle ()
>                (when timer
>                  (cancel-timer timer))
>                (setq count 1)
>                (run-and-schedule-next))
>              (run-and-schedule-next ()
>                (setq timer
>                  (and (funcall func count)
>                       (run-with-idle-timer
>                        (* seconds (incf count))
>                        nil
>                        #'run-and-schedule-next)))))
>       (list (run-with-idle-timer seconds t #'on-first-idle)
>             (lambda () timer)))))

BTW, for the curious, the byte-compiler will turn the above code into
something similar to:

    (defun run-periodically-when-idle (seconds func)
      (let ((timer (list nil))
            (count (list nil)))
        (letrec ((on-first-idle
                  `(lambda ()
                     (when (car ',timer)
                       (cancel-timer (car ',timer)))
                     (setcar ',count 1)
                     (funcall ',run-and-schedule-next)))
                 (run-and-schedule-next
                  `(lambda ()
                     (setcar ',timer
                             (and (funcall ',func (car ',count))
                                  (run-with-idle-timer
                                   (* ',seconds (incf (car ',count)))
                                   nil
                                   ',run-and-schedule-next))))))
          (list (run-with-idle-timer seconds t on-first-idle)
                `(lambda () (car ',timer))))))
   
So if I were you I'd be tempted to use something like

   (defun run-periodically-when-idle (seconds func)
     (let ((timers (list nil))
           count)
       (labels ((on-first-idle ()
                  (when (cdr timers)
                    (cancel-timer (cdr timers)))
                  (setq count 1)
                  (run-and-schedule-next))
                (run-and-schedule-next ()
                  (setcdr timers
                    (and (funcall func count)
                         (run-with-idle-timer
                          (* seconds (incf count))
                          nil
                          #'run-and-schedule-next)))))
         (setcar timers (run-with-idle-timer seconds t #'on-first-idle))
         timers)))
   
   (defun cancel-periodic-idle-timer (timers)
     (cancel-timer (car timers))
     (aif (cdr timers)
       (cancel-timer it)))

BTW, as you have discovered there is often the need to do what your code
does, and in the packages that need it, they often use
a run-with-timer (i.e. a non-idle timer) for the repetition (which they
usually cancel in something like a pre-command-hook).

In jit-lock, the repetition is done with an idle timer (like here), but
instead of creating a new timer each time, the old timer is re-armed for
a different idle time.

I'm not very satisfied with either of the solutions.  It would be good
to add the functionality directly in timer.el (even if using one of the
existing techniques: at least clients could be later upgraded to a better
technique by improving timer.el).  IOW, I'd welcome a patch to timer.el
providing this feature.


        Stefan




reply via email to

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