]> code.delx.au - gnu-emacs-elpa/blob - packages/spinner/spinner.el
Add 'packages/spinner/' from commit '9477ee899d62259d4b946f243cdcdd9cdeb1e910'
[gnu-emacs-elpa] / packages / spinner / spinner.el
1 ;;; spinner.el --- Add spinners and progress-bars to the mode-line for ongoing operations -*- lexical-binding: t; -*-
2
3 ;; Copyright (C) 2015 Artur Malabarba
4
5 ;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
6 ;; Version: 1.0
7 ;; URL: https://github.com/Bruce-Connor/spinner.el
8 ;; Keywords: processes mode-line
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24 ;; 1 Usage
25 ;; ═══════
26 ;;
27 ;; 1. Add `(spinner "1.0")' to your package’s dependencies.
28 ;;
29 ;; 2. Call `(spinner-start)' and a spinner will be added to the
30 ;; mode-line.
31 ;;
32 ;; 3. Call `(spinner-stop)' on the same buffer when you want to remove
33 ;; it.
34 ;;
35 ;;
36 ;; 2 Behavior
37 ;; ══════════
38 ;;
39 ;; The default spinner is a line drawing that rotates. You can pass an
40 ;; argument to `spinner-start' to specify which spinner you want. All
41 ;; possibilities are listed in the `spinner-types' variable, but here are
42 ;; a few examples for you to try:
43 ;;
44 ;; • `(spinner-start 'vertical-breathing 10)'
45 ;; • `(spinner-start 'minibox)'
46 ;; • `(spinner-start 'moon)'
47 ;; • `(spinner-start 'triangle)'
48
49 \f
50 ;;; Code:
51
52 (defconst spinner-types
53 '((3-line-clock . ["┤" "┘" "┴" "└" "├" "┌" "┬" "┐"])
54 (2-line-clock . ["┘" "└" "┌" "┐"])
55 (flipping-line . ["_" "\\" "|" "/"])
56 (rotating-line . ["-" "\\" "|" "/"])
57 (progress-bar . ["[ ]" "[= ]" "[== ]" "[=== ]" "[====]" "[ ===]" "[ ==]" "[ =]"])
58 (progress-bar-filled . ["| |" "|█ |" "|██ |" "|███ |" "|████|" "| ███|" "| ██|" "| █|"])
59 (vertical-breathing . ["▁" "▂" "▃" "▄" "▅" "▆" "▇" "█" "▇" "▆" "▅" "▄" "▃" "▂" "▁" " "])
60 (vertical-rising . ["▁" "▄" "█" "▀" "▔"])
61 (horizontal-breathing . [" " "▏" "▎" "▍" "▌" "▋" "▊" "▉" "▉" "▊" "▋" "▌" "▍" "▎" "▏"])
62 (horizontal-breathing-long
63 . [" " "▎ " "▌ " "▊ " "█ " "█▎" "█▌" "█▊" "██" "█▊" "█▌" "█▎" "█ " "▊ " "▋ " "▌ " "▍ " "▎ " "▏ "])
64 (horizontal-moving . [" " "▌ " "█ " "▐▌" " █" " ▐"])
65 (minibox . ["▖" "▘" "▝" "▗"])
66 (triangle . ["◢" "◣" "◤" "◥"])
67 (box-in-box . ["◰" "◳" "◲" "◱"])
68 (box-in-circle . ["◴" "◷" "◶" "◵"])
69 (half-circle . ["◐" "◓" "◑" "◒"])
70 (moon . ["🌑" "🌘" "🌖" "🌕" "🌔" "🌒"]))
71 "Predefined alist of spinners.
72 Each car is a symbol identifying the spinner, and each cdr is a
73 vector, the spinner itself.")
74
75 (defvar spinner-current nil
76 "Spinner curently being displayed on the mode-line.")
77 (make-variable-buffer-local 'spinner-current)
78
79 (defvar spinner--counter 0
80 "Current frame of the spinner.")
81 (make-variable-buffer-local 'spinner--counter)
82
83 (defconst spinner--mode-line-construct
84 '((spinner-current
85 (" "
86 (:eval (elt spinner-current
87 (% spinner--counter
88 (length spinner-current)))))
89 (spinner--timer
90 (:eval (spinner-stop)))))
91 "Construct used to display the spinner.")
92 (put 'spinner--mode-line-construct 'risky-local-variable t)
93
94 (defvar spinner--timer nil
95 "Holds the timer being used on the current buffer.")
96 (make-variable-buffer-local 'spinner--timer)
97
98 (defvar spinner-frames-per-second 5
99 "Default speed at which spinners spin, in frames per second.
100 Applications can override this value.")
101
102 \f
103 ;;; The main function
104 ;;;###autoload
105 (defun spinner-start (&optional type fps)
106 "Start a mode-line spinner of given TYPE.
107 Spinners are buffer local. It is added to the mode-line in the
108 buffer where `spinner-start' is called.
109
110 Return value is a function which can be called anywhere to stop
111 this spinner. You can also call `spinner-stop' in the same
112 buffer where the spinner was created.
113
114 FPS, if given, is the number of desired frames per second.
115 Default is `spinner-frames-per-second'.
116
117 If TYPE is nil, use the first element of `spinner-types'.
118 If TYPE is `random', use a random element of `spinner-types'.
119 If it is a symbol, it specifies an element of `spinner-types'.
120 If it is a vector, it used as the spinner.
121 If it is a list, it should be a list of symbols, and a random one
122 is chosen as the spinner type."
123 ;; Choose type.
124 (setq spinner-current
125 (cond
126 ((vectorp type) type)
127 ((not type) (cdr (car spinner-types)))
128 ((eq type 'random)
129 (cdr (elt spinner-types
130 (random (length spinner-types)))))
131 ((listp type)
132 (cdr (assq (elt type (random (length type)))
133 spinner-types)))
134 ((symbolp type) (cdr (assq type spinner-types)))
135 (t (error "Unknown spinner type: %s" type))))
136 (setq spinner--counter 0)
137
138 ;; Maybe add to mode-line.
139 (unless (memq 'spinner--mode-line-construct mode-line-format)
140 (setq mode-line-format (copy-list mode-line-format))
141 (let ((cell (memq 'mode-line-buffer-identification mode-line-format)))
142 (if cell
143 (setcdr cell (cons 'spinner--mode-line-construct (cdr cell)))
144 (setcdr (last mode-line-format) '(spinner--mode-line-construct)))))
145
146 ;; Create timer.
147 (when (timerp spinner--timer)
148 (cancel-timer spinner--timer))
149 (let ((buffer (current-buffer))
150 ;; Create the timer as a lex variable so it can cancel itself.
151 (timer (run-at-time t
152 (/ 1.0 (or fps spinner-frames-per-second))
153 #'ignore)))
154 (timer-set-function
155 timer (lambda ()
156 (if (buffer-live-p buffer)
157 (with-current-buffer buffer
158 (setq spinner--counter (1+ spinner--counter))
159 (force-mode-line-update))
160 (ignore-errors (cancel-timer timer)))))
161 (setq spinner--timer timer)
162 ;; Return a stopping function.
163 (lambda () (when (buffer-live-p buffer)
164 (with-current-buffer buffer
165 (spinner-stop))))))
166
167 (defun spinner-stop ()
168 "Stop the current buffer's spinner."
169 (when (timerp spinner--timer)
170 (cancel-timer spinner--timer))
171 (setq spinner--timer nil
172 spinner-current nil)
173 (setq mode-line-format
174 (remove 'spinner--mode-line-construct mode-line-format)))
175
176 (provide 'spinner)
177
178 ;;; spinner.el ends here