knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "",
  out.width = "100%",
  #asciicast_knitr_output = "html",
  asciicast_theme = "solarized-light", # tango, solarized-dark, solarized-light
  asciicast_cols = 72
)
asciicast::init_knitr_engine(
  #echo = TRUE,
  #echo_input = FALSE,
  startup = quote({
    library(usethis)
    library(glue)
    options(cli.num_colors = 256)
    ui_code_snippet <- usethis:::ui_code_snippet
    usethis_theme <- usethis:::usethis_theme
    ui_inform <- usethis:::ui_inform
    kv_line <- usethis:::kv_line
    ui_special <- usethis:::ui_special
    ui_abort <- usethis:::ui_abort
    ui_bullets <- usethis:::ui_bullets
    set.seed(1) })
)
library(usethis)
library(glue)

In a hidden chunk here, I'm "exporting" some unexported internal helpers, so that I can use them and talk about them. For similar reasons, I attach glue above, so that certain glue functions work here, without explicitly namespacing them.

#| include: false
ui_code_snippet <- usethis:::ui_code_snippet
usethis_theme <- usethis:::usethis_theme
ui_inform <- usethis:::ui_inform
kv_line <- usethis:::kv_line
ui_special <- usethis:::ui_special
ui_abort <- usethis:::ui_abort
ui_bullets <- usethis:::ui_bullets

Block styles

The block styles exist to produce bulleted output with a specific symbol, using a specific color.

#| collapse: false
f <- function() {
  ui_todo("ui_todo(): red bullet")
  ui_done("ui_done(): green check")
  ui_oops("ui_oops(): red x")
  ui_info("ui_info(): yellow i")
  ui_line("ui_line(): (no symbol)")
}
f()

Another important feature is that all of this output can be turned off package-wide via the usethis.quiet option.

withr::with_options(
  list(usethis.quiet = TRUE),
  ui_info("You won't see this message.")
)
withr::with_options(
  list(usethis.quiet = FALSE), # this is the default
  ui_info("But you will see this one.")
)

These styles are very close to what can be done with cli::cli_bullets() and the way it responds to the names of its input text.

cli::cli_bullets(c(
        "noindent",
  " " = "indent",
  "*" = "bullet",
  ">" = "arrow",
  "v" = "success",
  "x" = "danger",
  "!" = "warning",
  "i" = "info"
))

A direct translation would look something like this:

| Legacy ui_*() | cli_bullets() shortcode | tweaks needed | |-----------------|---------------------------|-------------------------------| | ui_todo() | * | blue (default) -> red | | ui_done() | v | perfect match | | ui_oops() | x | perfect match | | ui_info() | i | blue (default) -> yellow | | ui_line() | (unnamed) | sort of a perfect match? although sometimes ui_line() is used just to get a blank line |

The overall conversion plan is to switch to a new function, ui_bullets(), which is a wrapper around cli::cli_bullets(), that adds a few features:

ui_bullets(c(
  "v" = "A great success!",
  "_" = "Something you need to do.",
  "x" = "Bad news.",
  "i" = "The more you know.",
  " " = "I'm just here for the indentation.",
  "No indentation at all. Not used much in usethis."
))

Summary of what I've done for todo's:

In terms of the block styles, that just leaves ui_code_block(), which is pretty different. ui_code_block() is used to put some code on the screen and optionally place it on the clipboard. I have created a new function, ui_code_snippet() that is built around cli::code_block(). Main observations:

Utility functions

The block style functions all route through some unexported utility functions.

is_quiet() just consults the usethis.quiet option and implements the default of FALSE.

#| eval: false
is_quiet <- function() {
  isTRUE(getOption("usethis.quiet", default = FALSE))
}

ui_bullet() is an intermediate helper used by ui_todo(), ui_done(), ui_oops() and ui_info(). It does some hygiene related to indentation (using the indent() utility function), then calls ui_inform(). ui_line() and ui_code() both call ui_inform() directly.

ui_inform() is just a wrapper around rlang::inform() that is guarded by a call to is_quiet()

#| eval: false
ui_inform <- function(...) {
  if (!is_quiet()) {
    inform(paste0(...))
  }
  invisible()
}

Other than is_quiet(), which will continue to play the same role, I anticipate that we no longer need these utilities (indent(), ui_bullet(), ui_inform()). Updates from the future:

Let's cover ui_silence() while we're here, which is exported. It's just a withr::with_*() function for executing code with usethis.quiet = TRUE.

#| eval: false
ui_silence <- function(code) {
  withr::with_options(list(usethis.quiet = TRUE), code)
}

Inline styles

Legacy functions

usethis has its own inline styles (mostly) for use inside functions like ui_todo():

# why is this block truncated from the top in the rendered document?
new_val <- "oxnard"
x <- glue("{ui_field('name')} set to {ui_value(new_val)}")
dput(x)
ui_done(x)

The inline styles enact some combination of:

ui_path() is special because it potentially modifies the input before styling it. ui_path() first makes the path relative to a specific base (by default, the active project root) and, if the path is a directory, it also ensures there is a trailing /.

ui_unset() is a special purpose helper used when we need to report that something is unknown, not configured, nonexistent, etc.

# why is this block truncated from the top in the rendered document?
x <- glue("Your super secret password is {ui_unset()}.")
dput(x)
ui_info(x)

cli replacements

In general, we can move towards cli's native inline-markup: https://cli.r-lib.org/reference/inline-markup.html

Here's the general conversion plan:

Conditions

I'm moving from ui_stop():

#| eval: false
ui_stop <- function(x, .envir = parent.frame()) {
  x <- glue_collapse(x, "\n")
  x <- glue(x, .envir = .envir)

  cnd <- structure(
    class = c("usethis_error", "error", "condition"),
    list(message = x)
  )

  stop(cnd)
}

to ui_abort():

#| eval: false
ui_abort <- function(message, ..., class = NULL, .envir = parent.frame()) {
  cli::cli_div(theme = usethis_theme())
  # bullet naming gymnastics, see below
  cli::cli_abort(
    message,
    class = c(class, "usethis_error"),
    .envir = .envir,
    ...
  )
}

The main point of ui_abort() is to use to cli_abort() (and to continue applying the "usethis_error" class).

I also use ui_abort() to apply different default bullet naming/styling. Starting with "x" and then defaulting to "i" seems to fit best with usethis's existing errors.

#| error: true
# why is this block truncated from the top in the rendered document?
block_start = "# <<<"
block_end = "# >>>"
ui_abort(c(
  "Invalid block specification.",
  "Must start with {.code {block_start}} and end with {.code {block_end}}."
))

Any bullets that are explicitly given are honored.

#| error: true
ui_abort(c("v" = "It's weird to give a green check in an error, but whatever."))
ui_abort(c(
  "!" = "Things are not ideal.",
  ">" = "Look at me!"
))

rlang::abort() and cli::cli_abort() start with "!" by default, then use "*" and " ", respectively.

The legacy functions also include ui_warn(). It has very little usage and, instead of converting it, I've eliminated its use altogether in favor of a "!" bullet:

ui_bullets(c("!" = "The guy she told you not to worry about."))

Sidebar: Now that I'm looking at a lot of the new errors with ui_abort() I realize that usethis also needs to be passing the call argument along. I'm going to leave that for a future, separate effort.

Sitrep and format helpers

This is a small clump of functions that support sitrep-type output.

kv_line() stands for "key-value line". Here's what it used to be:

kv_line_legacy <- function(key, value, .envir = parent.frame()) {
  value <- if (is.null(value)) ui_unset() else ui_value(value)
  key <- glue(key, .envir = .envir)
  ui_inform(glue("{cli::symbol$bullet} {key}: {value}"))
}

url <- "https://github.com/r-lib/usethis.git"
remote <- "origin"
kv_line_legacy("URL for the {ui_value(remote)} remote", url)

host <- "github.com"
kv_line_legacy("Personal access token for {ui_value(host)}", NULL)

Key features:

I won't show the updated source for kv_line() but here is some usage to show what it's capable of:

# why is this block truncated from the top in the rendered document?
noun <- "thingy"
value <- "VALUE"
kv_line("Let's reveal {.field {noun}}", "whatever")

kv_line("URL for the {.val {remote}} remote", I("{.url {url}}"))

kv_line("Personal access token for {.val {host}}", NULL)

kv_line("Personal access token for {.val {host}}", ui_special("discovered"))

ui_special() is the successor to ui_unset().

Questions

There's currently no drop-in substitute for ui_yeah() and ui_nope() in cli. Related issues: https://github.com/r-lib/cli/issues/228, https://github.com/r-lib/cli/issues/488. Therefore, in the meantime, ui_yeah() and ui_nope() are not-quite-superseded for external users.

However, internally, I've switched to the unexported functions ui_yep() and ui_nah() that are lightly modified versions of ui_yeah() and ui_nope() that use cli for styling.

#| eval: false
if (ui_nope("
      Current branch ({ui_value(actual)}) is not repo's default \\
      branch ({ui_value(default_branch)}).{details}")) {
        ui_abort("Cancelling. Not on desired branch.")
    }

Miscellaneous notes



r-lib/usethis documentation built on March 20, 2024, 8:51 p.m.