branch: externals/timeout commit 441734b52029707032bb0fac184ab687b687bcc8 Author: Karthik Chikmagalur <karthikchikmaga...@gmail.com> Commit: Karthik Chikmagalur <karthikchikmaga...@gmail.com>
timeout: Add functions for throttling and debouncing * timeout.el (timeout-throttle, timeout-debounce): Add functions to generate new throttled and debounced versions of their argument functions, as opposed to advising them. Try to maintain the documentation and interactive specs of the argument functions. --- timeout.el | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/timeout.el b/timeout.el index 2bf41b833d..161955e5e6 100644 --- a/timeout.el +++ b/timeout.el @@ -34,6 +34,19 @@ ;; ;; To debounce a function FUNC to run after a delay of 0.3 seconds, run ;; (timeout-debounce! func 0.3) +;; +;; To create a new throttled or debounced version of FUNC instead, run +;; +;; (timeout-throttle func 2.0) +;; (timeout-debounce func 0.3) +;; +;; You can bind this via fset or defalias: +;; +;; (defalias 'throttled-func (timeout-throttle func 2.0)) +;; (fset 'throttled-func (timeout-throttle func 2.0)) +;; +;; The interactive spec and documentation of FUNC is carried over to the new +;; function. ;;; Code: (require 'nadvice) @@ -118,5 +131,98 @@ function." '((name . throttle) (depth . -98))))) +(defun timeout-throttle (func &optional throttle default) + "Return a throttled version of function FUNC. + +THROTTLE defaults to 1 second. DEFAULT is the immediate return +value of the function when called." + (let ((throttle-timer nil) + (throttle (or throttle 1)) + (result default)) + (if (commandp func) + ;; INTERACTIVE version + (lambda (&rest args) + (:documentation + (concat + (documentation func) + (format "\n\nThrottle calls to this function by %f seconds" throttle))) + (interactive (advice-eval-interactive-spec + (cadr (interactive-form func)))) + (if (and throttle-timer (timerp throttle-timer)) + result + (setq result (apply func args)) + (setq throttle-timer + (run-with-timer + throttle nil + (lambda () + (cancel-timer throttle-timer) + (setq throttle-timer nil)))))) + ;; NON-INTERACTIVE version + (lambda (&rest args) + (:documentation + (concat + (documentation func) + (format "\n\nThrottle calls to this function by %f seconds" throttle))) + (if (and throttle-timer (timerp throttle-timer)) + result + (setq result (apply func args)) + (setq throttle-timer + (run-with-timer + throttle nil + (lambda () + (cancel-timer throttle-timer) + (setq throttle-timer nil))))))))) + +(defun timeout-debounce (func &optional delay default) + "Return a debounced version of function FUNC. + +DELAY defaults to 0.5 seconds. DEFAULT is the immediate return +value of the function when called." + (let ((debounce-timer nil) + (delay (or delay 0.50))) + (if (commandp func) + ;; INTERACTIVE version + (lambda (&rest args) + (:documentation + (concat + (documentation func) + (format "\n\nDebounce calls to this function by %f seconds" delay))) + (interactive (advice-eval-interactive-spec + (cadr (interactive-form func)))) + (if (timerp debounce-timer) + (timer-set-idle-time debounce-timer delay) + (prog1 default + (setq debounce-timer + (run-with-idle-timer + delay nil + (lambda (buf) + (cancel-timer debounce-timer) + (setq debounce-timer nil) + (if (buffer-live-p buf) + (with-current-buffer buf + (apply func args)) + (apply func args))) + (current-buffer)))))) + ;; NON-INTERACTIVE version + (lambda (&rest args) + (:documentation + (concat + (documentation func) + (format "\n\nDebounce calls to this function by %f seconds" delay))) + (if (timerp debounce-timer) + (timer-set-idle-time debounce-timer delay) + (prog1 default + (setq debounce-timer + (run-with-idle-timer + delay nil + (lambda (buf) + (cancel-timer debounce-timer) + (setq debounce-timer nil) + (if (buffer-live-p buf) + (with-current-buffer buf + (apply func args)) + (apply func args))) + (current-buffer))))))))) + (provide 'timeout) ;;; timeout.el ends here