]> code.delx.au - gnu-emacs-elpa/blob - spinner.el
Add spinners
[gnu-emacs-elpa] / spinner.el
1 ;;; spinner.el --- Mode-line spinner for operations in progress -*- 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 ;; Keywords: processes mode-line
8
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
13
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23
24 ;; Run `(spinner-start)' to see the effect.
25
26 \f
27 ;;; Code:
28
29 (defconst spinner-types
30 '((3-line-clock . ["┤" "┘" "┴" "└" "├" "┌" "┬" "┐"])
31 (2-line-clock . ["┘" "└" "┌" "┐"])
32 (progress-bar . ["[ ]" "[= ]" "[== ]" "[=== ]" "[====]" "[ ===]" "[ ==]" "[ =]"])
33 (progress-bar-filled . ["| |" "|█ |" "|██ |" "|███ |" "|████|" "| ███|" "| ██|" "| █|"])
34 (vertical-breathing . ["▁" "▂" "▃" "▄" "▅" "▆" "▇" "█" "▇" "▆" "▅" "▄" "▃" "▂" "▁" " "])
35 (vertical-rising . ["▁" "▄" "█" "▀" "▔"])
36 (horizontal-breathing . [" " "▏" "▎" "▍" "▌" "▋" "▊" "▉" "▉" "▊" "▋" "▌" "▍" "▎" "▏"])
37 (horizontal-breathing-long
38 . [" " "▎ " "▌ " "▊ " "█ " "█▎" "█▌" "█▊" "██" "█▊" "█▌" "█▎" "█ " "▊ " "▋ " "▌ " "▍ " "▎ " "▏ "])
39 (horizontal-moving . [" " "▌ " "█ " "▐▌" " █" " ▐"])
40 (minibox . ["▖" "▘" "▝" "▗"])
41 (triangle . ["◢" "◣" "◤" "◥"])
42 (box-in-box . ["◰" "◳" "◲" "◱"])
43 (box-in-circle . ["◴" "◷" "◶" "◵"])
44 (half-circle . ["◐" "◓" "◑" "◒"])
45 (moon . ["🌑" "🌘" "🌖" "🌕" "🌔" "🌒"]))
46 "Predefined alist of spinners.
47 Each car is a symbol identifying the spinner, and each cdr is a
48 vector, the spinner itself.")
49
50 (defvar spinner-current nil
51 "Spinner curently being displayed on the mode-line.")
52 (make-variable-buffer-local 'spinner-current)
53
54 (defun spinner-stop ()
55 "Stop the current buffer's spinner."
56 (when (timerp spinner--timer)
57 (cancel-timer spinner--timer))
58 (setq spinner--timer nil
59 spinner-current nil)
60 (setq mode-line-format
61 (remove 'spinner--mode-line-construct mode-line-format)))
62
63 (defconst spinner--mode-line-construct
64 '((spinner-current
65 (" "
66 (:eval (elt spinner-current
67 (% (cadr (current-time))
68 (length spinner-current)))))
69 (spinner--timer
70 (:eval (spinner-stop)))))
71 "Construct used to display the spinner.")
72 (put 'spinner--mode-line-construct 'risky-local-variable t)
73
74 (defvar spinner--timer nil
75 "Holds the timer being used on the current buffer.")
76 (make-variable-buffer-local 'spinner--timer)
77
78 (defun spinner-start (&optional type)
79 "Start a mode-line spinner of given TYPE.
80 Spinners are buffer local. Call `spinner-stop' in the same buffer
81 to stop it.
82
83 If TYPE is nil, use the first element of `spinner-types'.
84 If TYPE is `random', use a random element of `spinner-types'.
85 If it is a symbol, it specifies an element of `spinner-types'.
86 If it is a vector, it used as the spinner.
87 If it is a list, it should be a list of symbols, and a random one
88 is chosen as the spinner type."
89 ;; Choose type.
90 (setq spinner-current
91 (cond
92 ((vectorp type) type)
93 ((not type) (cdr (car spinner-types)))
94 ((eq type 'random)
95 (cdr (elt spinner-types
96 (random (length spinner-types)))))
97 ((listp type)
98 (cdr (assq (elt type (random (length type)))
99 spinner-types)))
100 ((symbolp type) (cdr (assq type spinner-types)))
101 (t (error "Unknown spinner type: %s" type))))
102
103 ;; Maybe add to mode-line.
104 (unless (memq 'spinner--mode-line-construct mode-line-format)
105 (setq mode-line-format (copy-list mode-line-format))
106 (let ((cell (memq 'mode-line-buffer-identification mode-line-format)))
107 (if cell
108 (setcdr cell (cons 'spinner--mode-line-construct (cdr cell)))
109 (setcdr (last mode-line-format) '(spinner--mode-line-construct)))))
110
111 ;; Create timer.
112 (when (timerp spinner--timer)
113 (cancel-timer spinner--timer))
114 (let ((buffer (current-buffer))
115 ;; Create the timer as a lex variable so it can cancel itself.
116 (timer (run-at-time t 1 #'ignore)))
117 (timer-set-function
118 timer (lambda ()
119 (if (buffer-live-p buffer)
120 (with-current-buffer buffer
121 (force-mode-line-update))
122 (ignore-errors (cancel-timer timer)))))
123 (setq spinner--timer timer)))
124
125 (provide 'spinner)
126 ;;; spinner.el ends here