]> code.delx.au - gnu-emacs-elpa/blobdiff - spinner.el
Require cl-lib
[gnu-emacs-elpa] / spinner.el
index 721f9c29f63813e2cfe38331276db4ced84971db..0132bf718fa5fe6a6ebfed4d0d9e37e026fdd1aa 100644 (file)
@@ -1,9 +1,11 @@
-;;; spinner.el --- Mode-line spinner for operations in progress  -*- lexical-binding: t; -*-
+;;; spinner.el --- Add spinners and progress-bars to the mode-line for ongoing operations -*- lexical-binding: t; -*-
 
 ;; Copyright (C) 2015  Artur Malabarba
 
 ;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
 ;; Version: 1.0
+;; Package-Requires: ((cl-lib "0.5"))
+;; URL: https://github.com/Bruce-Connor/spinner.el
 ;; Keywords: processes mode-line
 
 ;; This program is free software; you can redistribute it and/or modify
 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
-
-;; Run `(spinner-start)' to see the effect.
+;; 1 Usage
+;; ═══════
+;;
+;; 1. Add `(spinner "1.0")' to your package’s dependencies.
+;;
+;; 2. Call `(spinner-start)' and a spinner will be added to the
+;; mode-line.
+;;
+;; 3. Call `(spinner-stop)' on the same buffer when you want to remove
+;; it.
+;;
+;;
+;; 2 Behavior
+;; ══════════
+;;
+;; The default spinner is a line drawing that rotates. You can pass an
+;; argument to `spinner-start' to specify which spinner you want. All
+;; possibilities are listed in the `spinner-types' variable, but here are
+;; a few examples for you to try:
+;;
+;; • `(spinner-start 'vertical-breathing 10)'
+;; • `(spinner-start 'minibox)'
+;; • `(spinner-start 'moon)'
+;; • `(spinner-start 'triangle)'
 
 \f
 ;;; Code:
+(require 'cl-lib)
 
 (defconst spinner-types
   '((3-line-clock . ["┤" "┘" "┴" "└" "├" "┌" "┬" "┐"])
     (2-line-clock . ["┘" "└" "┌" "┐"])
+    (flipping-line . ["_" "\\" "|" "/"])
+    (rotating-line . ["-" "\\" "|" "/"])
     (progress-bar . ["[    ]" "[=   ]" "[==  ]" "[=== ]" "[====]" "[ ===]" "[  ==]" "[   =]"])
     (progress-bar-filled . ["|    |" "|█   |" "|██  |" "|███ |" "|████|" "| ███|" "|  ██|" "|   █|"])
     (vertical-breathing . ["▁" "▂" "▃" "▄" "▅" "▆" "▇" "█" "▇" "▆" "▅" "▄" "▃" "▂" "▁" " "])
@@ -51,20 +78,15 @@ vector, the spinner itself.")
   "Spinner curently being displayed on the mode-line.")
 (make-variable-buffer-local 'spinner-current)
 
-(defun spinner-stop ()
-  "Stop the current buffer's spinner."
-  (when (timerp spinner--timer)
-    (cancel-timer spinner--timer))
-  (setq spinner--timer nil
-        spinner-current nil)
-  (setq mode-line-format
-        (remove 'spinner--mode-line-construct mode-line-format)))
+(defvar spinner--counter 0
+  "Current frame of the spinner.")
+(make-variable-buffer-local 'spinner--counter)
 
 (defconst spinner--mode-line-construct
   '((spinner-current
      (" "
       (:eval (elt spinner-current
-                  (% (cadr (current-time))
+                  (% spinner--counter
                      (length spinner-current)))))
      (spinner--timer
       (:eval (spinner-stop)))))
@@ -75,10 +97,24 @@ vector, the spinner itself.")
   "Holds the timer being used on the current buffer.")
 (make-variable-buffer-local 'spinner--timer)
 
-(defun spinner-start (&optional type)
+(defvar spinner-frames-per-second 5
+  "Default speed at which spinners spin, in frames per second.
+Applications can override this value.")
+
+\f
+;;; The main function
+;;;###autoload
+(defun spinner-start (&optional type fps)
   "Start a mode-line spinner of given TYPE.
-Spinners are buffer local.  Call `spinner-stop' in the same buffer
-to stop it.
+Spinners are buffer local. It is added to the mode-line in the
+buffer where `spinner-start' is called.
+
+Return value is a function which can be called anywhere to stop
+this spinner.  You can also call `spinner-stop' in the same
+buffer where the spinner was created.
+
+FPS, if given, is the number of desired frames per second.
+Default is `spinner-frames-per-second'.
 
 If TYPE is nil, use the first element of `spinner-types'.
 If TYPE is `random', use a random element of `spinner-types'.
@@ -99,10 +135,11 @@ is chosen as the spinner type."
                      spinner-types)))
          ((symbolp type) (cdr (assq type spinner-types)))
          (t (error "Unknown spinner type: %s" type))))
-  
+  (setq spinner--counter 0)
+
   ;; Maybe add to mode-line.
   (unless (memq 'spinner--mode-line-construct mode-line-format)
-    (setq mode-line-format (copy-list mode-line-format))
+    (setq mode-line-format (cl-copy-list mode-line-format))
     (let ((cell (memq 'mode-line-buffer-identification mode-line-format)))
       (if cell
           (setcdr cell (cons 'spinner--mode-line-construct (cdr cell)))
@@ -113,14 +150,31 @@ is chosen as the spinner type."
     (cancel-timer spinner--timer))
   (let ((buffer (current-buffer))
         ;; Create the timer as a lex variable so it can cancel itself.
-        (timer (run-at-time t 1 #'ignore)))
+        (timer (run-at-time t
+                            (/ 1.0 (or fps spinner-frames-per-second))
+                            #'ignore)))
     (timer-set-function
      timer (lambda ()
              (if (buffer-live-p buffer)
                  (with-current-buffer buffer
+                   (setq spinner--counter (1+ spinner--counter))
                    (force-mode-line-update))
                (ignore-errors (cancel-timer timer)))))
-    (setq spinner--timer timer))) 
+    (setq spinner--timer timer)
+    ;; Return a stopping function.
+    (lambda () (when (buffer-live-p buffer)
+            (with-current-buffer buffer
+              (spinner-stop))))))
+
+(defun spinner-stop ()
+  "Stop the current buffer's spinner."
+  (when (timerp spinner--timer)
+    (cancel-timer spinner--timer))
+  (setq spinner--timer nil
+        spinner-current nil)
+  (setq mode-line-format
+        (remove 'spinner--mode-line-construct mode-line-format)))
 
 (provide 'spinner)
+
 ;;; spinner.el ends here