knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  out.width = "100%",
  cache = TRUE,
  asciicast_at = "all",
  asciicast_cpp11_linkingto = "[[cpp11::linking_to(\"cli\")]]"
)
asciicast::init_knitr_engine(
  echo = TRUE,
  echo_input = FALSE
)

Introduction

```{asciicast, asciicast-tick-time, include = FALSE, cache = FALSE} set.seed(1) Sys.setenv(CLI_TICK_TIME = "100")

```{asciicast, progress-setup setup, cache = FALSE, include = FALSE}
library(cli)
options(cli.progress_show_after = 0)
options(cli.progress_clear = FALSE)
options(cli.progress_format_iterator = NULL)
options(cli.progress_format_iterator_nototal = NULL)
options(cli.progress_format_tasks = NULL)
options(cli.progress_format_tasks_nototal = NULL)
options(cli.progress_format_download = NULL)
options(cli.progress_format_download_nototal = NULL)
library(cli)
options(cli.progress_show_after = 0)
options(cli.progress_clear = FALSE)

This document discusses the structure and simplest uses of the cli progress bar API. For more advanced usage and the C progress bar API, see the 'Advanced cli progress bars' article and the manual pages.

From version 3.0.0 cli provides a set of functions to create progress bars. The main goals of the progress bar API are:

The traditional progress bar API

Add a progress bar in three steps:

  1. Call cli_progress_bar() to add create a progress bar.
  2. Call cli_progress_update() to update it.
  3. Call cli_progress_done() to terminate it.

For example:

```{asciicast classic-example} clean <- function() { cli_progress_bar("Cleaning data", total = 100) for (i in 1:100) { Sys.sleep(5/100) cli_progress_update() } cli_progress_done() } clean()

The traditional API provides full control w.r.t when to create, update and
terminate a progress bar.

# The current progress bar

For conciseness, the progress bar functions refer to the _current progress
bar_ by default. Every function has at most one current progress bar at
any time. The current progress bar of a function is terminated when the
function creates another progress bar or when the function returns, errors
or is interrupted.

The current progress bar lets us omit the `cli_progress_done()` call:

```{asciicast current}
clean <- function() {
  cli_progress_bar("Cleaning data #1", total = 100)
  for (i in 1:100) {
    Sys.sleep(3/100)
    cli_progress_update()
  }

  cli_progress_bar("Cleaning data #2", total = 100)
  for (i in 1:100) {
    Sys.sleep(3/100)
    cli_progress_update()
  }
}
clean()

Unknown total number of units

In some cases the total number of progress units is unknown, so simply omit them from cli_progress_bar() (or set them to NA). cli uses a different display when total is unknown:

```{asciicast unknown-total-seed, include = FALSE, cache = FALSE} set.seed(1)

```{asciicast unknown-total}
walk_dirs <- function() {
  cli_progress_bar("Walking directories")
  while (TRUE) {
    if (runif(1) < 0.01) break
    Sys.sleep(0.01)
    cli_progress_update()
  }
  cli_progress_update(force = TRUE)
}
walk_dirs()

Quick loops

By default, cli does not show progress bars that are terminated within two seconds after their creation. The end user can configure this limit with the cli.progress_show_after global option.

For example, in this document we set the limit to zero seconds, so progress bars are shown at their first update.

Progress bars for mapping functions: cli_progress_along()

cli_progress_along() is currently experimental.

To add a progress bar to a call to lapply() or another mapping function, wrap the input sequence into cli_progress_along():

lapply(cli_progress_along(X), fun)

cli_progress_along() works similarly to seq_along(), it returns an index vector. If you use cli_progress_along(), then lapply() will pass the indices of the elements in X to fun, instead of the elements themselves.

cli_progress_along() expects that the index vector will be used only once, from beginning to end. It is best to never assign the return value of cli_progress_along() to a variable.

An example:

```{asciicast, tickalong} f <- function() { rawabc <- lapply( cli_progress_along(letters), function(i) { charToRaw(letters[i]) Sys.sleep(0.5) } ) } f()

`cli_progress_along()` uses ALTREP, so it only works from R 3.5.0 and later.
On older R versions it is equivalent to `seq_along()` and it does not
create a progress bar.

## `for` loops

You can also use `cli_progress_along()` in `for` loops, with the additional
complication that if you use `break`, then you might need to terminate the
progress bar explicitly:

```r
for (i in cli_progress_along(seq)) {
  ...
  if (cond) cli_progress_done() && break
  ...
}

cli_progress_done() always returns TRUE to allow this form.

Alternatively, you can terminate the progress bar right after loop:

for (i in cli_progress_along(seq)) {
  ...
  if (cond) break
  ...
}
cli_progress_done()

If the function containing the for loop returns after the loop, or you create another progress bar with cli_progress_along() or cli_progress_bar(), then no explicit cli_progress_done() is needed.

Simplified API

Often you don't need the full power of the progress bar API, and only want to show a status message. The cli_progress_message() and cli_progress_step() functions are tailored for this.

cli_progress_message() shows a (potentially templated) message in the status bar. For convenience, the progress bar rules still apply here by default:

```{asciicast cli_progress_message} f <- function() { cli_progress_message("Task one is running...") Sys.sleep(2)

cli_progress_message("Task two is running...") Sys.sleep(2)

step <- 1L cli_progress_message("Task three is underway: step {step}") for (step in 1:5) { Sys.sleep(0.5) cli_progress_update() } } f()

Status messages may use glue interpolation, cli styling and pluralization,
as usual. You can call `cli_progress_update()` to update a status message.

`cli_progress_step()` is slightly different from
`cli_progress_message()`:
* it adds cli's alert themes to the status messages (info, success or
  danger),
* prints the duration of each step (by default), and
* it keeps the messages on the screen after they are terminated.

```{asciicast cli_progress_step_simple}
f <- function() {
  cli_progress_step("Downloading data")
  Sys.sleep(2)

  cli_progress_step("Importing data")
  Sys.sleep(1)

  cli_progress_step("Cleaning data")
  Sys.sleep(2)

  cli_progress_step("Fitting model")
  Sys.sleep(3)
}
f()

As usual, you can use cli_progress_step() to update an existing status message.

```{asciicast cli_progress_step} f <- function(n = 10) { cli_alert_info("About to start downloads of {n} file{?s}") i <- 0 cli_progress_step("Got {i}/{n} {qty(i)}file{?s}.") for (i in seq_len(n)) { Sys.sleep(0.5) if (i == 5) cli_alert_info("Already half way!") cli_progress_update() } } f()

If you can update the status message frequently enough, then you can also
add a spinner to it:

```{asciicast cli_progress_step_spinner}
f <- function() {
  cli_progress_step("Downloading data", spinner = TRUE)
  for (i in 1:100) { cli_progress_update(); Sys.sleep(2/100) }
  cli_progress_step("Importing data", spinner = TRUE)
  for (i in 1:100) { cli_progress_update(); Sys.sleep(1/100) }
  cli_progress_step("Cleaning data", spinner = TRUE)
  for (i in 1:100) { cli_progress_update(); Sys.sleep(2/100) }
  cli_progress_step("Fitting model", spinner = TRUE)
  for (i in 1:100) { cli_progress_update(); Sys.sleep(3/100) }
}
f()

cli_progress_step() automatically handles errors, and styles the status message accordingly:

```{asciicast step-error}

| asciicast_rows = 3

f <- function() { cli_progress_step("First step, this will succeed") Sys.sleep(1) cli_progress_step("Second step, this will fail") Sys.sleep(1) stop("Something is wrong here") } f() ```



r-pkgs/boxes documentation built on April 27, 2024, 11:08 a.m.