Mercurial > core / lisp/lib/cli/progress.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
7ce855f76e1d
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; lib/cli/progress.lisp --- Progress Bars 3 ;; from https://github.com/sirherrbatka/cl-progress-bar 6 (in-package :cli/progress) 8 (defclass progress-bar () 16 :initarg :progress-character 17 :accessor progress-character) 19 :initarg :character-count 20 :accessor character-count 21 :documentation "How many characters wide is the progress bar?") 23 :initarg :characters-so-far 24 :accessor characters-so-far) 26 :initarg :update-interval 27 :accessor update-interval 28 :documentation "Update the progress bar display after this many 29 internal-time units.") 31 :initarg :last-update-time 32 :accessor last-update-time 33 :documentation "The display was last updated at this time.") 37 :documentation "The total number of units tracked by this progress bar.") 41 :documentation "How far in the progress are we?") 44 :reader progress-mutex 45 :documentation "Just a mutex, allows progress bar to be thread safe.") 49 :documentation "How many raw units should be tracked in the next 52 :mutex (sb-thread:make-mutex) 53 :progress-character #\= 56 :update-interval (floor internal-time-units-per-second 4) 62 (defgeneric start-progress-display (progress-bar)) 63 (defgeneric update-progress (progress-bar unit-count)) 64 (defgeneric update-progress-display (progress-bar)) 65 (defgeneric finish-progress-display (progress-bar)) 66 (defgeneric elapsed-time (progress-bar)) 67 (defgeneric units-per-second (progress-bar)) 69 (defmethod start-progress-display (progress-bar) 70 (setf (last-update-time progress-bar) (get-internal-real-time)) 71 (setf (start-time progress-bar) (get-internal-real-time)) 75 (defmethod update-progress-display (progress-bar) 76 (incf (progress progress-bar) (pending progress-bar)) 77 (setf (pending progress-bar) 0) 78 (setf (last-update-time progress-bar) (get-internal-real-time)) 79 (unless (zerop (progress progress-bar)) 80 (let* ((showable (floor (character-count progress-bar) 81 (/ (total progress-bar) (progress progress-bar)))) 82 (needed (- showable (characters-so-far progress-bar)))) 83 (setf (characters-so-far progress-bar) showable) 85 (write-char (progress-character progress-bar))) 88 (defmethod update-progress (progress-bar unit-count) 89 (incf (pending progress-bar) unit-count) 90 (let ((now (get-internal-real-time))) 91 (when (< (update-interval progress-bar) 92 (- now (last-update-time progress-bar))) 93 (update-progress-display progress-bar)))) 95 (defconstant +seconds-in-one-hour+ 3600) 96 (defconstant +seconds-in-one-minute+ 60) 98 (defun time-in-seconds-minutes-hours (in-seconds) 99 (unless (zerop in-seconds) 100 (format t "Finished in") 101 (when (>= in-seconds +seconds-in-one-hour+) 102 (let* ((hours (floor (/ in-seconds +seconds-in-one-hour+)))) 103 (decf in-seconds (* hours +seconds-in-one-hour+)) 104 (format t " ~a hour~p" hours hours))) 105 (when (>= in-seconds +seconds-in-one-minute+) 106 (let* ((minutes (floor (/ in-seconds +seconds-in-one-minute+)))) 107 (decf in-seconds (* minutes +seconds-in-one-minute+)) 108 (format t " ~a minute~p" minutes minutes))) 109 (unless (zerop in-seconds) (format t " ~$ seconds" in-seconds))) 112 (defmethod finish-progress-display (progress-bar) 113 (update-progress-display progress-bar) 114 (setf (end-time progress-bar) (get-internal-real-time)) 116 (time-in-seconds-minutes-hours (elapsed-time progress-bar)) 118 (unless (= (progress progress-bar) (total progress-bar)) 119 (warn "Expected TOTAL is ~a but progress at the moment of finishing is ~a" 121 (progress progress-bar)))) 123 (defmethod elapsed-time (progress-bar) 124 (/ (- (end-time progress-bar) (start-time progress-bar)) 125 internal-time-units-per-second)) 127 (defmethod units-per-second (progress-bar) 128 (if (plusp (elapsed-time progress-bar)) 129 (/ (total progress-bar) (elapsed-time progress-bar)) 132 (defparameter *uncertain-progress-chars* "?") 134 (defclass uncertain-size-progress-bar (progress-bar) 135 ((progress-char-index 136 :initarg :progress-char-index 137 :accessor progress-char-index) 139 :initarg :units-per-char 140 :accessor units-per-char)) 143 :progress-char-index 0 144 :units-per-char (floor (expt 1024 2) 50))) 146 (defmethod update-progress :after ((progress-bar uncertain-size-progress-bar) 148 (incf (total progress-bar) unit-count)) 150 (defmethod progress-character ((progress-bar uncertain-size-progress-bar)) 151 (let ((index (progress-char-index progress-bar))) 153 (char *uncertain-progress-chars* index) 154 (setf (progress-char-index progress-bar) 155 (mod (1+ index) (length *uncertain-progress-chars*)))))) 157 (defmethod update-progress-display ((progress-bar uncertain-size-progress-bar)) 158 (setf (last-update-time progress-bar) (get-internal-real-time)) 159 (multiple-value-bind (chars pend) 160 (floor (pending progress-bar) (units-per-char progress-bar)) 161 (setf (pending progress-bar) pend) 163 (write-char (progress-character progress-bar)) 164 (incf (characters-so-far progress-bar)) 165 (when (<= (character-count progress-bar) 166 (characters-so-far progress-bar)) 168 (setf (characters-so-far progress-bar) 0) 172 (defvar *progress-bar* nil) 173 (defparameter *progress-bar-enabled* nil) 175 (declaim (inline update)) 176 (defun update (unit-count &optional (progress-bar *progress-bar*)) 177 (check-type unit-count (integer 1 *)) 178 (check-type progress-bar (or null progress-bar)) 179 (unless (null progress-bar) 180 (sb-thread:with-mutex ((progress-mutex progress-bar)) 181 (update-progress progress-bar unit-count)))) 183 (defun make-progress-bar (total) 184 (check-type total (or null (integer 0 *))) 185 (if (or (not total) (zerop total)) 186 (make-instance 'uncertain-size-progress-bar) 187 (make-instance 'progress-bar 190 (defmacro with-progress-bar ((steps-count description &rest desc-args) &body body) 191 (let ((!old-bar (gensym))) 192 `(let* ((,!old-bar *progress-bar*) 193 (*progress-bar* (or ,!old-bar 194 (when *progress-bar-enabled* 195 (make-progress-bar ,steps-count))))) 196 (unless (eq ,!old-bar *progress-bar*) 198 (format t ,description ,@desc-args) 199 (start-progress-display *progress-bar*)) 200 (prog1 (progn ,@body) 201 (unless (eq ,!old-bar *progress-bar*) 202 (finish-progress-display *progress-bar*)))))) 204 (defmacro with-progress-maybe (enabled (steps-count description &rest desc-args) &body body) 205 (declare (ignorable steps-count description desc-args)) 207 `(with-progress-bar (,steps-count ,description ,@desc-args) ,@body)