From d2d5e886dcf378fdc153a0c88d39518dbabed91d Mon Sep 17 00:00:00 2001 From: Peter Monks Date: Thu, 27 Jun 2024 16:07:51 -0700 Subject: [PATCH 1/3] :green_heart: Rename main branch to release --- .github/workflows/deploy.yml | 2 +- .github/workflows/docs.yml | 2 +- README.md | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e097736..3d348a6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,7 +2,7 @@ name: deploy on: push: branches: - - main + - release jobs: deploy: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8fe9c8e..919044b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -2,7 +2,7 @@ name: docs on: push: branches: - - main + - release jobs: docs: diff --git a/README.md b/README.md index eefd209..28d6572 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ | | | | |---:|:---:|:---:| -| [**main**](https://github.com/pmonks/spinner/tree/main) | [![CI](https://github.com/pmonks/spinner/workflows/CI/badge.svg?branch=main)](https://github.com/pmonks/spinner/actions?query=workflow%3ACI+branch%3Amain) | [![Dependencies](https://github.com/pmonks/spinner/workflows/dependencies/badge.svg?branch=main)](https://github.com/pmonks/spinner/actions?query=workflow%3Adependencies+branch%3Amain) | -| [**dev**](https://github.com/pmonks/spinner/tree/dev) | [![CI](https://github.com/pmonks/spinner/workflows/CI/badge.svg?branch=dev)](https://github.com/pmonks/spinner/actions?query=workflow%3ACI+branch%3Adev) | [![Dependencies](https://github.com/pmonks/spinner/workflows/dependencies/badge.svg?branch=dev)](https://github.com/pmonks/spinner/actions?query=workflow%3Adependencies+branch%3Adev) | +| [**release**](https://github.com/pmonks/spinner/tree/release) | [![CI](https://github.com/pmonks/spinner/actions/workflows/ci.yml/badge.svg?branch=release)](https://github.com/pmonks/spinner/actions?query=workflow%3ACI+branch%3Arelease) | [![Dependencies](https://github.com/pmonks/spinner/actions/workflows/dependencies.yml/badge.svg?branch=release)](https://github.com/pmonks/spinner/actions?query=workflow%3Adependencies+branch%3Arelease) | +| [**dev**](https://github.com/pmonks/spinner/tree/dev) | [![CI](https://github.com/pmonks/spinner/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/pmonks/spinner/actions?query=workflow%3ACI+branch%3Adev) | [![Dependencies](https://github.com/pmonks/spinner/actions/workflows/dependencies.yml/badge.svg?branch=dev)](https://github.com/pmonks/spinner/actions?query=workflow%3Adependencies+branch%3Adev) | -[![Latest Version](https://img.shields.io/clojars/v/com.github.pmonks/spinner)](https://clojars.org/com.github.pmonks/spinner/) [![Open Issues](https://img.shields.io/github/issues/pmonks/spinner.svg)](https://github.com/pmonks/spinner/issues) [![License](https://img.shields.io/github/license/pmonks/spinner.svg)](https://github.com/pmonks/spinner/blob/main/LICENSE) +[![Latest Version](https://img.shields.io/clojars/v/com.github.pmonks/spinner)](https://clojars.org/com.github.pmonks/spinner/) [![Open Issues](https://img.shields.io/github/issues/pmonks/spinner.svg)](https://github.com/pmonks/spinner/issues) [![License](https://img.shields.io/github/license/pmonks/spinner.svg)](https://github.com/pmonks/spinner/blob/release/LICENSE) # spinner @@ -15,7 +15,7 @@ To give the user of a command line app a visual progress indicator during long r Here it is in action (from the unit tests):

- Spinner example screenshot + Spinner example screenshot

Note that using Unicode characters in progress indicators may be unreliable, depending on your OS, terminal, font, encoding, phase of the moon, etc. @@ -96,21 +96,21 @@ Require them in your application: ### API Documentation -[API documentation is available here](https://pmonks.github.io/spinner/). The [unit](https://github.com/pmonks/spinner/blob/main/test/progress/indeterminate_test.clj) [tests](https://github.com/pmonks/spinner/blob/main/test/progress/determinate_test.clj) provide comprehensive usage examples (alternative animation sets, formatting, etc.). +[API documentation is available here](https://pmonks.github.io/spinner/). The [unit](https://github.com/pmonks/spinner/blob/release/test/progress/indeterminate_test.clj) [tests](https://github.com/pmonks/spinner/blob/release/test/progress/determinate_test.clj) provide comprehensive usage examples (alternative animation sets, formatting, etc.). ## Contributor Information -[Contributing Guidelines](https://github.com/pmonks/spinner/blob/main/.github/CONTRIBUTING.md) +[Contributing Guidelines](https://github.com/pmonks/spinner/blob/release/.github/CONTRIBUTING.md) [Bug Tracker](https://github.com/pmonks/spinner/issues) -[Code of Conduct](https://github.com/pmonks/spinner/blob/main/.github/CODE_OF_CONDUCT.md) +[Code of Conduct](https://github.com/pmonks/spinner/blob/release/.github/CODE_OF_CONDUCT.md) ### Developer Workflow -This project uses the [git-flow branching strategy](https://nvie.com/posts/a-successful-git-branching-model/), with the caveat that the permanent branches are called `main` and `dev`, and any changes to the `main` branch are considered a release and auto-deployed (JARs to Clojars, API docs to GitHub Pages, etc.). +This project uses the [git-flow branching strategy](https://nvie.com/posts/a-successful-git-branching-model/), and the permanent branches are called `release` and `dev`. Any changes to the `release` branch are considered a release and auto-deployed (JARs to Clojars, API docs to GitHub Pages, etc.). -For this reason, **all development must occur either in branch `dev`, or (preferably) in temporary branches off of `dev`.** All PRs from forked repos must also be submitted against `dev`; the `main` branch is **only** updated from `dev` via PRs created by the core development team. All other changes submitted to `main` will be rejected. +For this reason, **all development must occur either in branch `dev`, or (preferably) in temporary branches off of `dev`.** All PRs from forked repos must also be submitted against `dev`; the `release` branch is **only** updated from `dev` via PRs created by the core development team. All other changes submitted to `release` will be rejected. ### Build Tasks From c148980bd493f2faa29ac618f59fc57e03cc7846 Mon Sep 17 00:00:00 2001 From: Peter Monks Date: Thu, 3 Oct 2024 12:13:43 -0700 Subject: [PATCH 2/3] :ambulance: Address issue #23, and also switch to Markdown format for the docs --- pbr.clj | 4 +- src/progress/3rd_party.clj | 17 +++ src/progress/ansi.clj | 20 ++-- src/progress/determinate.clj | 128 ++++++++++----------- src/progress/indeterminate.clj | 160 +++++++++++++++++---------- src/progress/util.clj | 17 +-- src/spinner/core.clj | 36 +++--- test/progress/indeterminate_test.clj | 10 +- test/spinner/core_test.clj | 4 +- 9 files changed, 232 insertions(+), 164 deletions(-) create mode 100644 src/progress/3rd_party.clj diff --git a/pbr.clj b/pbr.clj index 2958c6e..2bcc544 100644 --- a/pbr.clj +++ b/pbr.clj @@ -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}})) diff --git a/src/progress/3rd_party.clj b/src/progress/3rd_party.clj new file mode 100644 index 0000000..79c68dd --- /dev/null +++ b/src/progress/3rd_party.clj @@ -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))))) diff --git a/src/progress/ansi.clj b/src/progress/ansi.clj index 7b0f365..8dceaf3 100644 --- a/src/progress/ansi.clj +++ b/src/progress/ansi.clj @@ -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])) @@ -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) @@ -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)] @@ -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) diff --git a/src/progress/determinate.clj b/src/progress/determinate.clj index 68082f6..f744e7d 100644 --- a/src/progress/determinate.clj +++ b/src/progress/determinate.clj @@ -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] @@ -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 "[" @@ -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 @@ -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)] @@ -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) @@ -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)))) diff --git a/src/progress/indeterminate.clj b/src/progress/indeterminate.clj index 9717fb8..8cb2fe9 100644 --- a/src/progress/indeterminate.clj +++ b/src/progress/indeterminate.clj @@ -17,11 +17,12 @@ ; (ns progress.indeterminate - "Indetermine progress indicator (aka a \"spinner\"), for the case where the progress of a long-running task cannot be determined." - (:require [clojure.string :as s] - [jansi-clj.core :as jansi] - [progress.ansi :as ansi] - [progress.util :as u]) + "Indeterminate progress indicator (aka a \"spinner\"), for the case where the + progress of a long-running task cannot be determined." + (:require [clojure.string :as s] + [jansi-clj.core :as jansi] + [progress.ansi :as ansi] + [progress.3rd-party :as tp]) (:refer-clojure :exclude [print])) (def ^:private fut (atom nil)) @@ -30,9 +31,10 @@ (defn state "What state is the indeterminate progress indicator currently in? One of: - * :inactive - * :active - * :shutting-down" + + * `:inactive` + * `:active` + * `:shutting-down`" [] @s) @@ -42,12 +44,18 @@ (= :active @s)) (defn print - "Schedules the given values for printing (ala clojure.core/print), since clojure.core/print (and similar output fns) interfere with an active indeterminate progress indicator. - -Notes: - * output is emitted in between 'frames' of the progress indicator, so may not appear immediately - * values are space delimited (as in clojure.core/print) - use clojure.core/str for finer control - * no newlines are inserted - if message(s) are to appear on new lines the caller needs to include \\newline in the value(s)" + "Schedules the given values for printing (via [clojure.core/print]), since + [clojure.core/print] and related output fns interfere with an active + indeterminate progress indicator. + + Notes: + + * output is emitted in between 'frames' of the progress indicator, so may not + appear immediately + * values are space delimited (as in [clojure.core/print]) - use + [clojure.core/str], [clojure.core/format], etc. for finer control + * no newlines are inserted - if message(s) are to appear on new lines the + caller needs to include `\\n` in the value(s)" [& more] (when (seq more) (let [msg (s/join " " more)] @@ -61,14 +69,15 @@ Notes: (defn- print-pending-messages "Prints all pending messages" [] - (when-let [messages (first (u/swap*! msgs (constantly nil)))] + (when-let [messages (first (tp/swap*! msgs (constantly nil)))] (clojure.core/print messages) (flush) (ansi/save-cursor!))) - (def default-style - "The default indeterminate progress indicator style used, if one isn't specified. This is known to function on all platforms." + "The default indeterminate progress indicator style used, if one isn't + specified, as a `keyword` that has an associated entry in [styles]. This style + is known to function on all platforms." :ascii-spinner) (def default-delay-ms @@ -76,9 +85,10 @@ Notes: 100) (def styles - "A selection of predefined styles of indeterminate 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 indeterminate progress indicators are reliable across platforms :ascii-spinner [\| \/ \- \\] @@ -111,7 +121,8 @@ long since your dog last pooped." }) (defn- indeterminate-progress-indicator - "Indeterminate progress indicator logic, for use in a future or Thread or wotnot" + "Indeterminate progress indicator logic, for use in a `future` or `Thread` or + wotnot." ([] (indeterminate-progress-indicator nil)) ([{:keys [delay-in-ms frames fg-colour bg-colour attributes] :or {delay-in-ms default-delay-ms @@ -119,52 +130,69 @@ long since your dog last pooped." fg-colour :default bg-colour :default attributes [:default]}}] - (ansi/save-cursor!) - (loop [i 0] - (clojure.core/print (str (ansi/apply-colours-and-attrs fg-colour bg-colour attributes (nth frames (mod i (count frames)))) - " ")) - (flush) - (Thread/sleep ^Long delay-in-ms) - (ansi/restore-cursor!) - (jansi/erase-line!) - (print-pending-messages) - (when (active?) - (recur (inc i)))) + (let [delay-in-ms (long (Math/round (double delay-in-ms)))] ; Coerce delay-in-ms to a long + (ansi/save-cursor!) + (loop [i 0] + (clojure.core/print (str (ansi/apply-colours-and-attrs fg-colour bg-colour attributes (nth frames (mod i (count frames)))) + " ")) + (flush) + (when (pos? delay-in-ms) (Thread/sleep delay-in-ms)) ; Thread/sleep throws on negative values, and sleeping for 0ms makes no sense + (ansi/restore-cursor!) + (jansi/erase-line!) + (print-pending-messages) + (when (active?) + (recur (inc i))))) nil)) -(defn start! - "Not intended for public use. Use animate! or animatef! instead." +(defn ^:no-doc start! + "Not intended for public use. Use [animate!] or [animatef!] instead." ([] (start! nil)) ([opts] (when-not (compare-and-set! s :inactive :active) (throw (java.lang.IllegalStateException. "Progress indicator is already active."))) - (flush) ; Flush any residual I/O to stdout before we start animating - (reset! fut (future (indeterminate-progress-indicator opts))) (reset! msgs nil) + (reset! fut (future (indeterminate-progress-indicator opts))) nil)) -(defn stop! - "Not intended for public use. Use animate! or animatef! instead." +(defn ^:no-doc stop! + "Not intended for public use. Use [animate!] or [animatef!] instead." [] (when (compare-and-set! s :active :shutting-down) - @@fut ; Wait for the future to stop (deref the atom AND the future) - (print-pending-messages) ; Flush any remaining messages - (reset! fut nil) - (reset! s :inactive)) + (try + @@fut ; Wait for the future to stop (deref the atom AND the future) + (print-pending-messages) ; Flush any remaining messages + (finally + (reset! fut nil) + (reset! s :inactive)))) nil) (defn animatef! - "Starts the indeterminate progress indicator, calls fn f (a function of zero parameters), then stops it. Returns the result of f. - - Note that the `animate!` macro is preferred over this function. - - opts is a map, optionally containing these keys: - :frames - the frames (a sequence of strings) to use for the indeterminate progress indicator (default is (:ascii-spinner styles)) - :delay - the delay (in ms) between frames (default is 100ms) - :fg-colour - the foregound colour of the indeterminate progress indicator (default is :default) - see https://github.com/xsc/jansi-clj#colors for allowed values, and prefix with bright- to get the bright equivalent - :bg-colour - the background colour of the indeterminate progress indicator (default is :default) - see https://github.com/xsc/jansi-clj#colors for allowed values, and prefix with bright- to get the bright equivalent - :attributes - the attributes of the indeterminate progress indicator (default is [:default]) - see https://github.com/xsc/jansi-clj#attributes for allowed values" + "Starts the indeterminate progress indicator, calls fn `f` (a function of zero + parameters), then stops the progress indicator. Returns the result of `f`. + + **Note: the [[animate!]] macro is preferred over this function.** + + The optional `opts` map may have an/all of these keys: + + * `:frames` - the frames (a sequence of `String`s) to use for the + indeterminate progress indicator (default is + `(:ascii-spinner styles)`) + * `:delay-in-ms` - the delay (in ms) between frames (default is `100`ms) + * `:fg-colour` - the foregound colour of the indeterminate progress + indicator (default is `:default`) - see [the `jansi-clj` + docs](https://github.com/xsc/jansi-clj#colors) for allowed + values, and prefix with `bright-` to get the bright + equivalent + * `:bg-colour` - the background colour of the indeterminate progress + indicator (default is `:default`) - see [the `jansi-clj` + docs](https://github.com/xsc/jansi-clj#colors) for allowed + values, and prefix with `bright-` to get the bright + equivalent + * `:attributes` - the attributes of the indeterminate progress indicator + (default is `[:default]`) - see [the `jansi-clj` + docs](https://github.com/xsc/jansi-clj#attributes) for + allowed values" ([f] (animatef! nil f)) ([opts f] (when f @@ -175,12 +203,28 @@ long since your dog last pooped." (stop!)))))) (defmacro animate! - "Wraps the given forms in the indeterminate progress indicator. If the first form is the keyword `:opts`, the second form must be a map, optionally containing these keys: - :frames - the frames (a sequence of strings) to use for the indeterminate progress indicator (default is (:ascii-spinner styles)) - :delay - the delay (in ms) between frames (default is 100ms) - :fg-colour - the foregound colour of the indeterminate progress indicator (default is :default) - see https://github.com/xsc/jansi-clj#colors for allowed values, and prefix with bright- to get the bright equivalent - :bg-colour - the background colour of the indeterminate progress indicator (default is :default) - see https://github.com/xsc/jansi-clj#colors for allowed values, and prefix with bright- to get the bright equivalent - :attributes - the attributes of the indeterminate progress indicator (default is [:default]) - see https://github.com/xsc/jansi-clj#attributes for allowed values" + "Wraps the given forms in an indeterminate progress indicator. If the first + form is the keyword `:opts`, the second form _must_ be a map, containing + any/all of these keys: + + * `:frames` - the frames (a sequence of `String`s) to use for the + indeterminate progress indicator (default is + `(:ascii-spinner styles)`) + * `:delay-in-ms` - the delay (in ms) between frames (default is `100`ms) + * `:fg-colour` - the foregound colour of the indeterminate progress + indicator (default is `:default`) - see [the `jansi-clj` + docs](https://github.com/xsc/jansi-clj#colors) for allowed + values, and prefix with `bright-` to get the bright + equivalent + * `:bg-colour` - the background colour of the indeterminate progress + indicator (default is `:default`) - see [the `jansi-clj` + docs](https://github.com/xsc/jansi-clj#colors) for allowed + values, and prefix with `bright-` to get the bright + equivalent + * `:attributes` - the attributes of the indeterminate progress indicator + (default is `[:default]`) - see [the `jansi-clj` + docs](https://github.com/xsc/jansi-clj#attributes) for + allowed values" [& body] (if (= :opts (first body)) `(animatef! ~(second body) (fn [] ~@(rest (rest body)))) diff --git a/src/progress/util.clj b/src/progress/util.clj index dcd2248..040b360 100644 --- a/src/progress/util.clj +++ b/src/progress/util.clj @@ -17,20 +17,9 @@ ; (ns progress.util - (:require [clojure.string :as s]) - (:refer-clojure :exclude [print])) + (:require [clojure.string :as s])) (def is-windows? - "Are we running on Windows? If so, best to stick with ASCII-only progress indicators... 😢" + "Are we running on Windows? If so, best to stick with ASCII-only progress + indicators... 😢" (s/starts-with? (s/lower-case (System/getProperty "os.name")) "windows")) - -(defn swap*! - "Like clojure.core/swap! but returns a vector of [old-value new-value]. - From http://stackoverflow.com/questions/22409638/remove-first-item-from-clojure-vector-atom-and-return-it" - [atom f & args] - (loop [] - (let [ov @atom - nv (apply f ov args)] - (if (compare-and-set! atom ov nv) - [ov nv] - (recur))))) diff --git a/src/spinner/core.clj b/src/spinner/core.clj index 30944ed..f077ebc 100644 --- a/src/spinner/core.clj +++ b/src/spinner/core.clj @@ -17,49 +17,59 @@ ; (ns ^:deprecated spinner.core + "Deprecated namespace from the original version of the library. Only retained + for backwards compatibility reasons. Superceded by the + [[progress.indeterminate]] namespace." (:require [progress.indeterminate :as pi] [progress.util :as u]) (:refer-clojure :exclude [print])) +(defn- fix-delay-opt + "'Fixes' the old `:delay` opt by replacing it with `:delay-in-ms`." + [m] + (when (and (contains? m :delay) + (not (contains? m :delay-in-ms))) + (assoc (dissoc m :delay) :delay-in-ms (:delay m)))) + (def ^:deprecated is-windows? - "See progress.util/is-windows?" + "See [[progress.util/is-windows?]]" u/is-windows?) (def ^:deprecated default-style - "See progress.indeterminate/default-style" + "See [[progress.indeterminate/default-style]]" pi/default-style) (def ^:deprecated default-delay-ms - "See progress.indeterminate/default-delay-ms" + "See [[progress.indeterminate/default-delay-ms]]" pi/default-delay-ms) (def ^:deprecated styles - "See progress.indeterminate/styles" + "See [[progress.indeterminate/styles]]" pi/styles) (defn ^:deprecated active? - "See progress.indeterminate/active?" + "See [[progress.indeterminate/active?]]" [] (pi/active?)) (defn ^:deprecated start! - "See progress.indeterminate/start!" + "See [[progress.indeterminate/start!]]" ([] (start! nil)) - ([options] - (pi/start! options))) + ([opts] + (pi/start! (fix-delay-opt opts)))) (defn ^:deprecated stop! - "See progress.indeterminate/stop!" + "See [[progress.indeterminate/stop!]]" [] (pi/stop!)) (defn ^:deprecated spin! - "See progress.indeterminate/animatef!" + "See [[progress.indeterminate/animatef!]]" ([f] (spin! f nil)) - ([f options] - (pi/animatef! options f))) + ([f opts] + (pi/animatef! (fix-delay-opt opts) f))) (defn ^:deprecated print - "See progress.indeterminate/print" + "See [[progress.indeterminate/print]]" [& more] (apply pi/print more)) diff --git a/test/progress/indeterminate_test.clj b/test/progress/indeterminate_test.clj index bd455f6..8e7da1b 100644 --- a/test/progress/indeterminate_test.clj +++ b/test/progress/indeterminate_test.clj @@ -77,11 +77,11 @@ (testing "Custom everything" (is (= nil (pi/animate! :opts {:frames (:box-fade pi/styles) - :delay (/ pi/default-delay-ms 2) - :fg-colour :bright-yellow - :bg-colour :bright-red - :attributes [:bold :blink-fast]} - (Thread/sleep 250))))) + :delay-in-ms (/ pi/default-delay-ms 4) ; Hyperspeed! + :fg-colour :bright-yellow + :bg-colour :bright-red + :attributes [:bold :blink-fast]} + (Thread/sleep 1000))))) (testing "All styles with leading message" (doall diff --git a/test/spinner/core_test.clj b/test/spinner/core_test.clj index e7aa6cf..cfea081 100644 --- a/test/spinner/core_test.clj +++ b/test/spinner/core_test.clj @@ -48,7 +48,7 @@ (deftest display (testing "Custom everything" (is (= (do (spin/start! {:frames (:box-fade spin/styles) - :delay (/ spin/default-delay-ms 2) + :delay (/ spin/default-delay-ms 2) ; Note: we use the old/deprecated :delay opt here, to ensure that fix-delay-opt is working correctly :fg-colour :bright-yellow :bg-colour :bright-red :attributes [:bold :blink-fast]}) @@ -71,7 +71,7 @@ (testing "Function" (is (= (spin/spin! (fn [] (Thread/sleep 250)) {:frames (:ascii-bouncing-ball spin/styles) - :delay (* spin/default-delay-ms 2) + :delay (* spin/default-delay-ms 2) ; Note: we use the old/deprecated :delay opt here, to ensure that fix-delay-opt is working correctly :fg-colour :red :bg-colour :bright-white}) nil)))) From 987bd9e05dc0268cc527ef9e01306664ab54df0f Mon Sep 17 00:00:00 2001 From: Peter Monks Date: Thu, 3 Oct 2024 12:29:51 -0700 Subject: [PATCH 3/3] :ambulance: Update build script to reflect recent branch name changes --- pbr.clj | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pbr.clj b/pbr.clj index 2bcc544..e9e1aff 100644 --- a/pbr.clj +++ b/pbr.clj @@ -16,16 +16,13 @@ ; SPDX-License-Identifier: Apache-2.0 ; -(def lib 'com.github.pmonks/spinner) - #_{:clj-kondo/ignore [:unresolved-namespace]} -(def version (format "2.0.%s" (b/git-count-revs nil))) - (defn set-opts [opts] (assoc opts - :lib lib - :version version + :lib 'com.github.pmonks/spinner + :version (pbr/calculate-version 2 0) + :prod-branch "release" :write-pom true :validate-pom true :pom {:description "Simple ANSI text progress indicators for command line Clojure apps."