r-lib / cli

Tools for making beautiful & useful command line interfaces
https://cli.r-lib.org/
Other
644 stars 70 forks source link

Add option to customize last separator when auto-collapsing character vectors #639

Closed Teebusch closed 11 months ago

Teebusch commented 11 months ago

cli_text() and pluralize() auto-collapse character vectors, but there is no option to configure the final separator "and":

pkgs <- c("pkg1", "pkg2", "pkg3")
cli_text("Will remove the {.pkg {pkgs}} package{?s}.")
#> Will remove the pkg1, pkg2, and pkg3 packages.

This is hardcoded as last in cli:::inline_collapse():

function (x, style = list()) 
{
  sep <- style[["vec-sep"]] %||% style[["vec_sep"]] %||% ", "
  if (length(x) >= 3) {
    last <- style[["vec-last"]] %||% style[["vec_last"]] %||% 
      ", and "
  }
  else {
    last <- style[["vec-sep2"]] %||% style[["vec_sep2"]] %||% 
      style[["vec-last"]] %||% style[["vec_last"]] %||% 
      " and "
  }
  trunc <- style[["vec-trunc"]] %||% style[["vec_trunc"]] %||% 
    20L
  col_style <- style[["vec-trunc-style"]] %||% "both-ends"
  ansi_collapse(x, sep = sep, last = last, trunc = trunc, 
    style = col_style)
}

It would be useful to expose a last argument to the user so they can override it with a language-specific separator or just a regular ','.

A usable workaround is to collapse the character vector outside of the function, e.g. with glue::collapse(), which does have a last argument.

gaborcsardi commented 11 months ago

There are several ways to do this, here are two:

❯ cli_text("foo {.or {letters[1:5]}} bar")
foo a, b, c, d, or e bar
❯ v <- cli_vec(letters[1:5], style = list("vec-last" =  ", or "))
❯ cli_text("foo {v} bar")
foo a, b, c, d, or e bar
Teebusch commented 11 months ago

Awesome! Thanks for the quick response! It might be worth adding to the relevant documentation article https://cli.r-lib.org/articles/pluralization.html#use-the-length-of-character-vectors

Also worth mentioning that the first solution you suggest only is a solution if you want to replace "and" with "or"

cli::cli_text("foo {.or {letters[1:5]}} bar")
cli::cli_text("foo {.und {letters[1:5]}} bar") # won't work

And the second solution means you won't have the convenience of the automatic character vector collapsing.

gaborcsardi commented 11 months ago

Also worth mentioning that the first solution you suggest only is a solution if you want to replace "and" with "or"

You would need define {.und}.

And the second solution means you won't have the convenience of the automatic character vector collapsing.

I am not sure what you mean, see my second example. In general, you either need to define your own class with a theme, or add the style to the vector that you want to collapse.

Teebusch commented 1 month ago

I ran into this issue again today and it took me a while to remember how to define a custom {.und} inline markup again. For future me and anyone else who might need it -- Here are two ways to do it:

x <- c("a", "b", "c")
text <- "{.und {x}}"
my_theme <- list(span.und = list("vec-sep2" = " und ", "vec-last" = ", und "))

# -- option 1: modify theme temporarily for a single div

cli::cli_div(theme = my_theme)
cli::cli_text(text)
cli::cli_end()

# > a, b, und c

# -- option 2:modify theme more permanently via user theme

options("cli.user_theme" = my_theme)
cli::cli_text(text)

# > a, b, und c

Relevant sources

https://cli.r-lib.org/reference/inline-markup.html#collapsing-inline-vectors https://github.com/r-lib/cli/blob/2ddcc1af364cf6737a06cec8a344a52b26dd9afa/R/themes.R#L254 https://cli.r-lib.org/reference/themes.html https://cli.r-lib.org/reference/cli_div.html https://cli.r-lib.org/reference/inline-markup.html