Skip to content

Commit

Permalink
Merge pull request #24 from pmonks/issue-23
Browse files Browse the repository at this point in the history
Address issue #23, and also switch to Markdown format for the docs
  • Loading branch information
pmonks authored Oct 3, 2024
2 parents d2d5e88 + c148980 commit 5344b2a
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 164 deletions.
4 changes: 3 additions & 1 deletion pbr.clj
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@
:licenses [:license {:name "Apache-2.0" :url "http://www.apache.org/licenses/LICENSE-2.0.html"}]
:developers [:developer {:id "pmonks" :name "Peter Monks" :email "pmonks+spinner@gmail.com"}]
:scm {:url "https://github.com/pmonks/spinner" :connection "scm:git:git://github.com/pmonks/spinner.git" :developer-connection "scm:git:ssh://git@github.com/pmonks/spinner.git"}
:issue-management {:system "github" :url "https://github.com/pmonks/spinner/issues"}}))
:issue-management {:system "github" :url "https://github.com/pmonks/spinner/issues"}}
:codox {:namespaces ['progress.determinate 'progress.indeterminate 'progress.util 'spinner.core]
:metadata {:doc/format :markdown}}))
17 changes: 17 additions & 0 deletions src/progress/3rd_party.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
;;; This namespace contains 3rd party code obtained from other sources.
;;; Copyright and license information is listed on a per-var basis.

(ns progress.3rd-party)

;; Copyright 2014 A. Webb (https://stackoverflow.com/users/1756702/a-webb)
;; SPDX-License-Identifier: CC-BY-SA-3.0
(defn swap*!
"Like [[clojure.core/swap!]] but returns a vector of `[old-value new-value]`.
Adapted from [this StackOverflow question](https://stackoverflow.com/a/22409846)."
[atom f & args]
(loop []
(let [ov @atom
nv (apply f ov args)]
(if (compare-and-set! atom ov nv)
[ov nv]
(recur)))))
20 changes: 13 additions & 7 deletions src/progress/ansi.clj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
;

(ns progress.ansi
"Handy ANSI related functionality. Note: requiring this namespace has the side effect of enabling JANSI."
"Handy ANSI related functionality. Note: requiring this namespace has the side
effect of enabling [JANSI](https://github.com/fusesource/jansi?tab=readme-ov-file#example-usage)."
(:require [clojure.string :as s]
[jansi-clj.core :as jansi]))

Expand All @@ -38,7 +39,8 @@
(flush))

(defn print-at
"Send text output to the specified screen locations (note: ANSI screen coordinates are 1-based). msgs may include jansi formatting."
"Send text output to the specified screen locations (note: ANSI screen
coordinates are 1-based). msgs may include jansi formatting."
[x y & msgs]
(save-cursor!)
(jansi/cursor! x y)
Expand All @@ -47,17 +49,20 @@
(restore-cursor!))

(defn debug-print-at
"Send debug output to the specified screen location (note: ANSI screen coordinates are 1-based)."
"Send debug output to the specified screen location (note: ANSI screen
coordinates are 1-based)."
[x y & args]
(print-at x y (jansi/a :bold (jansi/fg-bright :yellow (jansi/bg :red (str "DEBUG: " (s/join " " args)))))))

(defn debug-print
"Send debug output to the upper left corner of the screen, where (hopefully) it minimises interference with everything else."
"Send debug output to the upper left corner of the screen, where (hopefully)
it minimises interference with everything else."
[& args]
(apply debug-print-at 1 1 args))

(defn apply-colour
"Applies an 'enhanced' colour keyword (which may include the prefix 'bright-') to either the foreground or background of body."
"Applies an 'enhanced' colour keyword (which may include the prefix 'bright-')
to either the foreground or background of body."
[fg? colour-key s]
(let [bright? (s/starts-with? (name colour-key) "bright-")
colour-name (if bright? (keyword (subs (name colour-key) (count "bright-"))) colour-key)]
Expand All @@ -68,12 +73,13 @@
[false false] (jansi/bg colour-name s))))

(defn apply-attributes
"Applies all of provided attributes (a seq) to s (a string)."
"Applies all of provided attributes (a seq) to `s` (a `String`)."
[attributes s]
((apply comp (map #(partial jansi/a %) attributes)) s))

(defn apply-colours-and-attrs
"Applies the foreground colour, background colour, and attributes (a seq) to s (a string)."
"Applies the foreground colour, background colour, and attributes (a seq) to
`s` (a `String`)."
[fg-colour bg-colour attrs s]
(apply-attributes (if (seq attrs) attrs [:default])
(apply-colour false (if bg-colour bg-colour :default)
Expand Down
128 changes: 64 additions & 64 deletions src/progress/determinate.clj
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
;

(ns progress.determinate
"Determine progress indicator (aka a \"progress bar\"), for the case where the
progress of a long-running task can be determined."
"Determinate progress indicator (aka a \"progress bar\"), for the case where
the progress of a long-running task can be determined."
(:require [clojure.string :as s]
[jansi-clj.core :as jansi]
[wcwidth.api :as w]
Expand All @@ -28,14 +28,15 @@ progress of a long-running task can be determined."

(def default-style
"The default determinate progress indicator style used, if one isn't
specified. This is known to function on all platforms."
specified, as a `keyword` that has an associated entry in [styles]. This style
is known to function on all platforms."
:ascii-basic)

(def styles
"A selection of predefined styles of determinate progress indicators. Only
ASCII progress indicators are known to work reliably - other styles depend on
the operating system, terminal font & encoding, phase of the moon, and how long
since your dog last pooped."
"A selection of predefined styles of determinate progress indicators,
represented as a `map`. Only ASCII progress indicators are known to work
reliably - other styles depend on the operating system, terminal font &
encoding, phase of the moon, and how long since your dog last pooped."
{
; ASCII determinate progress indicators are reliable across platforms
:ascii-basic {:left "["
Expand Down Expand Up @@ -65,6 +66,7 @@ since your dog last pooped."
(max mn (min mx x)))

(defn- redraw-progress-indicator!
"Redraws the progress indicator."
[style style-widths label line width counter? total units _ _ _ new-value] ; Ignored args are required as this fn is also a watch
; Make sure this code is non re-entrant
(locking lock
Expand Down Expand Up @@ -138,7 +140,7 @@ since your dog last pooped."
(flush))))

(defn- valid-width
"Returns a valid width for s (throws on zero or non-printing)."
"Returns a valid width for `s` (throws on zero or non-printing)."
[s]
(when s
(let [width (w/display-width s)]
Expand All @@ -149,36 +151,36 @@ since your dog last pooped."

(defn animatef!
"Wraps execution of the given function in a determinate progress indicator,
monitoring atom `a` (a number between 0 and (:total opts), representing
progress). An optional options map (`opts`) may also be provided.
monitoring atom `a` (a number between `0` and `(:total opts)`, representing
progress).
Note that the `animate!` macro is preferred over this function.
**Note: the [[animate!]] macro is preferred over this function.**
opts is a map, optionally containing these keys:
:style - a map defining the style (characters, colours, and attributes)
to use when printing the progress indicator
Optional, default: (:ascii-basic styles)
:label - a String to display before the progress indicator - this could
be the filename for a lengthy file download, for example
Optional, default: nil
:line - the line number on the screen at which to display the progress
indicator (note: 1-based)
Optional, default: nil (display at current location)
:width - the (approximate) desired width of the progress indicator,
including any labels and counters. This is approximate because
emoji-based styles may not take up an even fraction of the
desired width
Optional, default: 72
:total - the final number that the atom will reach
Optional, default: 100 (i.e. the atom represents a %age)
:units - a unit label (String) to display after the counter
Optional, default: nil
:preserve? - flag indicating whether to preserve the progress indicator on
screen after it finishes (vs erasing it)
Optional, default: false (erase it)
:counter? - whether to display a counter to the right of the progress
indicator
Optional, default: true (display a counter)"
The optional `opts` map may have an/all of these keys:
* `:style` - a map defining the style (characters, colours, and
attributes) to use when printing the progress indicator.
Optional, default: `(:ascii-basic styles)`
* `:label` - a `String` to display before the progress indicator - this
could be the filename for a lengthy file download, for
example. Optional, default: `nil`
* `:line` - the line number on the screen at which to display the
progress indicator (note: 1-based). Optional, default: `nil`
(display at current location)
* `:width` - the (approximate) desired width of the progress indicator,
including any labels and counters. This is approximate
because emoji-based styles may not take up an even fraction
of the desired width. Optional, default: `72`
* `:total` - the final number that the atom will reach. Optional, default:
`100` (i.e. the atom represents a %age)
* `:units` - a unit label (`String`) to display after the counter - this
could be a file size unit (`\"KB\"`, `\"MB\"`, etc.), for
example. Optional, default: `nil`
* `:preserve?` - flag indicating whether to preserve the progress indicator on
screen after it finishes (vs erasing it). Optional, default:
`false` (erase it)
* `:counter?` - whether to display a counter to the right of the progress
indicator. Optional, default: `true` (display a counter)"
([a f] (animatef! a nil f))
([a opts f]
(when (and a f)
Expand Down Expand Up @@ -228,35 +230,33 @@ opts is a map, optionally containing these keys:

(defmacro animate!
"Wraps execution of the given forms in a determinate progress indicator,
monitoring atom `a` (a number between 0 and (:total opts), representing
progress). If the first form is the keyword `:opts`, the second form must be an
opts map.
monitoring atom `a` (a number between `0` and `(:total opts)`, representing
progress). If the first form is the keyword `:opts`, the second form _must_ be
a map, containing any/all of these keys:
The opts map (if present) may optionally contain these keys:
:style - a map defining the style (characters, colours, and attributes)
to use when printing the progress indicator
Optional, default: (:ascii-basic styles)
:label - a String to display before the progress indicator - this could
be the filename for a lengthy file download, for example
Optional, default: nil
:line - the line number on the screen at which to display the progress
indicator (note: 1-based)
Optional, default: nil (display at current location)
:width - the (approximate) desired width of the progress indicator,
including any labels and counters. This is approximate because
emoji-based styles may not take up an even fraction of the
desired width
Optional, default: 72
:total - the final number that the atom will reach
Optional, default: 100 (i.e. the atom represents a %age)
:units - a unit label (String) to display after the counter
Optional, default: nil
:preserve? - flag indicating whether to preserve the progress indicator on
screen after it finishes (vs erasing it)
Optional, default: false (erase it)
:counter? - whether to display a counter to the right of the progress
indicator
Optional, default: true (display a counter)"
* `:style` - a map defining the style (characters, colours, and
attributes) to use when printing the progress indicator.
Optional, default: `(:ascii-basic styles)`
* `:label` - a `String` to display before the progress indicator - this
could be the filename for a lengthy file download, for
example. Optional, default: `nil`
* `:line` - the line number on the screen at which to display the
progress indicator (note: 1-based). Optional, default: `nil`
(display at current location)
* `:width` - the (approximate) desired width of the progress indicator,
including any labels and counters. This is approximate
because emoji-based styles may not take up an even fraction
of the desired width. Optional, default: `72`
* `:total` - the final number that the atom will reach. Optional, default:
`100` (i.e. the atom represents a %age)
* `:units` - a unit label (`String`) to display after the counter - this
could be a file size unit (`\"KB\"`, `\"MB\"`, etc.), for
example. Optional, default: `nil`
* `:preserve?` - flag indicating whether to preserve the progress indicator on
screen after it finishes (vs erasing it). Optional, default:
`false` (erase it)
* `:counter?` - whether to display a counter to the right of the progress
indicator. Optional, default: `true` (display a counter)"
[a & body]
(if (= :opts (first body))
`(animatef! ~a ~(second body) (fn [] ~@(rest (rest body))))
Expand Down
Loading

0 comments on commit 5344b2a

Please sign in to comment.