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