-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathnamed-timer.el
166 lines (137 loc) · 6 KB
/
named-timer.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
;;; named-timer.el --- Simplified timer management for Emacs Lisp -*- lexical-binding: t -*-
;; Copyright (C) 2018 Ryan C. Thompson
;; Filename: named-timer.el
;; Author: Ryan C. Thompson
;; Created: Sat Nov 10 16:52:30 2018 (-0800)
;; Version: 0.1
;; Package-Requires: ((emacs "24.4"))
;; URL: https://github.com/DarwinAwardWinner/emacs-named-timer
;; Keywords: tools
;; This file is NOT part of GNU Emacs.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;; This library provides functions for managing named timers. The
;; usual pattern for timer management in Emacs Lisp involves declaring
;; a timer variable, checking it for an existing timer, cancelling
;; that timer if it exists, setting the varaible to nil to indicate that
;; the timer is no longer active, and finally starting a new timer and
;; setting the variable to that value. For example:
;; (require 'timer)
;; (defvar my-timer nil)
;; (defun activate-my-timer ()
;; (when my-timer
;; (cancel-timer my-timer)
;; (setq my-timer nil))
;; (setq my-timer
;; (run-with-timer 5 nil #'message "My timer ran!")))
;; With named timers, this simplifies to a single line:
;; (require 'named-timer)
;; (defun activate-my-timer ()
;; (named-timer-run :my-timer 5 nil #'message "My timer ran!"))
;; In addition to being shorter, this code is less error prone: since
;; running a named timer automatically cancels any existing timer with
;; the same name, there is no chance of accidentally leaving multiple
;; timers by forgetting to cancel old timers. In short, all the
;; functions in this library are idempotent, which makes them much
;; easier to reason about.
;; The basic functions for managing named timers `named-timer-run',
;; `named-timer-idle-run', and `named-timer-cancel', which are,
;; respectively, analogues of `run-with-timer', `run-with-idle-timer',
;; and `cancel-timer'.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:
(require 'timer)
(require 'cl-lib)
(require 'subr-x)
(defvar named-timer-table (make-hash-table)
"A hash table mapping symbols to timers.")
(defsubst named-timer-names ()
"Return a list of all timer names in `named-timer-table'."
(hash-table-keys named-timer-table))
(defsubst named-timer-timers ()
"Return a list of all named timers in `named-timer-table'.
Note that this may include both regular and idle timers."
(hash-table-values named-timer-table))
(defsubst named-timer-get (name)
"Return the timer named NAME, if any.
If no timer with the specified name exists, returns nil. See
`named-timer-table'."
(cl-assert (symbolp name))
(gethash name named-timer-table))
(defun named-timer-cancel (name &optional _noremove)
"Cancel the timer with name NAME if it exists.
If NAME is the name of an active named timer, it is cancelled and
removed from `named-timer-table'. If no timer with that name
exists, nothing happens.
The second argument, _NOREMOVE, is intended for internal use
only. If non-nil, the entry for the cancelled timer in
`named-timer-table' is not removed. This is used for efficiency
in cases where that hash table entry is about to be replaced
anyway, such that deleting it is pointless."
(let ((timer (named-timer-get name)))
(when timer
(timer--check timer)
(cancel-timer timer)
(unless _noremove
(remhash name named-timer-table)))))
(defun named-timer-run (name &rest args)
"Set up a named timer.
This creates a new timer by calling `run-with-timer' on ARGS,
then adds that timer to `named-timer-table' with NAME as the key.
Like `run-with-timer', this returns the created timer.
Only one timer with a given name can be active. Calling this
function on a name that is already associated with an existing
timer will cancel the existing timer and replace it with the new
one in `named-timer-table'."
(declare (indent 1))
(cl-assert (symbolp name))
(named-timer-cancel name t)
(let ((timer (apply #'run-with-timer args)))
(timer--check timer)
(puthash name timer named-timer-table)))
(defun named-timer-idle-run (name &rest args)
"Set up a named idle timer.
This works like `named-timer-run', but passes ARGS to
`run-with-idle-timer' instead. The timer is added to
`named-timer-table' with NAME as the key."
(declare (indent 1))
(cl-assert (symbolp name))
(named-timer-cancel name t)
(let ((timer (apply #'run-with-idle-timer args)))
(timer--check timer)
(puthash name timer named-timer-table)))
;; Non-prefixed aliases that match the corresponding function names in
;; `timer.el'. Not sure if they should be kept or removed.
(defalias 'run-with-named-timer 'named-timer-run)
(defalias 'run-with-named-idle-timer 'named-timer-idle-run)
(defalias 'cancel-named-timer 'named-timer-cancel)
;; (defun cancel-timer@named-timer-cancel (orig-fun &rest args)
;; "Allow cancelling named timers by name.
;; This advice allows `cancel-timer' to accept a symbol argument
;; instead of a timer object. A symbol argument will be passed
;; instead to `named-timer-cancel' to cancel a timer with that
;; name."
;; (let ((timer-or-name (car args)))
;; (if (symbolp timer-or-name)
;; (named-timer-cancel timer-or-name)
;; (apply orig-fun args))))
;; (advice-add 'cancel-timer :around #'cancel-timer@named-timer-cancel)
(provide 'named-timer)
;;; named-timer.el ends here