Repository navigation
Expose or repackage useful formatters (e.g. pluralization) #152
Description
Activity
Yeah, I agree it would be useful. How about a
pluralize()function?(Note to self: cli cannot use this function internally, because of the two-phase substitution, etc. but
pluralize()could just call the relevant parts of cli w/o theming and emitting anything.)Also, don't use
capture.outputfor this, because it'll not work in non-interactive processes:❯ R -q -e 'capture.output(cli::cli_text("time: {100} year{?s}"))' > capture.output(cli::cli_text("time: {100} year{?s}")) time: 100 years character(0) >
The message goes to stderr, and nothing is captured on stdout. This is a bug: #153.
I was thinking more generally of exposing the (very cool) machinery you've built. For example, instead of triggering a message on the CLI the result could be inlined to a string, taking advantage of all the substitutions you support
library(cli) cli_string <- function (..., .envir = parent.frame()) { string_inline(list(text = cli:::glue_cmd(..., .envir = .envir))) } string_inline <- function(texts) { out <- lapply(texts, function(t) { glue::glue(t$str, .envir = t$values, .open = paste0("{", t$values$marker), .close = paste0(t$values$marker, "}")) }) paste(out, collapse = "") } cli_string("time: {1} year{?s}") #> [1] "time: 1 year" cli_string("time: {2} year{?s}") #> [1] "time: 2 years" cli_string("time: {3} year{?s}") #> [1] "time: 3 years" ndir <- 1; cli_string("Found {ndir} director{?y/ies}.") #> [1] "Found 1 directory." ndir <- 5; cli_string("Found {ndir} director{?y/ies}.") #> [1] "Found 5 directories." nfile <- 0; cli_string("Found {no(nfile)} file{?s}.") #> [1] "Found no files." nfile <- 2; cli_string("Found {no(nfile)} file{?s}.") #> [1] "Found 2 files." ## since I've removed the inline_transformer, this fails pkgs <- c("pkg1", "pkg2", "pkg3") cli_string("Will remove {?no/the/the} {.pkg {pkgs}} package{?s}.") #> Error in parse(text = text, keep.source = FALSE): <text>:1:6: unexpected '{' #> 1: .pkg { #> ^ nfiles <- 3; ndirs <- 1 cli_string("Found {nfiles} file{?s} and {ndirs} director{?y/ies}") #> [1] "Found 3 files and 1 directory"
Created on 2020-05-21 by the reprex package (v0.3.0)
I appreciate (now that I've dug through enough of the source) that you're doing a lot to the text if it's headed for the terminal so this can't just be processed as a string then sent, but maybe the substitution mechanism can still be extracted to be used by both. It would be strange to have {cli} as a dependency for another package to build on top of this very general machinery (as above), but that's another possibility.
@jimhester is this (substitution markers) something that could find a home in {glue}?
If you want a way to get a fully formatted string, you can do this:
pkgs <- c("pkg1", "pkg2", "pkg3") cli_format_method(cli_text("Will remove {?no/the/the} {.pkg {pkgs}} package{?s}.")) #> [1] "Will remove the \033[34m\033[34mpkg1\033[34m\033[39m, \033[34m\033[34mpkg2\033[34m\033[39m, and \033[34m\033[34mpkg3\033[34m\033[39m packages."
This does not use the current theme, for good or bad.
I don't know how you could move the "substitution markers" to another package, if their visual appearance is computed in cli. What would
{.pkg }do in another package?Ah, that's exactly what I wanted, cheers! That's essentially a
sink()around the cli output, right? My version above is purely external to thecliapp()so all of that code could be used externally with just a few extracted functions, starting withcli:::glue_cmd, but I don't think any of them touchcliapp().For the 'substitution markers' I only imagined the
{?s}and{?none/one/several}possibilities outside of cli - the cli-specific styling has no meaning for strings so I don't expect e.g. {.pkg } to be useful outside of cli. The n-based replacements are a unique and valuable mechanism, and I'd love to see those more generally available.That's essentially a sink() around the cli output, right?
Well, not really. cli has a client-server model, where the client is throwing conditions and the server can catch these. If there is no server configured, then yes, cli will print the output on stdout (in interactive sessions) or stderr (in non-interactive ones). But as soon as there is a user or IDE defined server, a simple
sink()will potentially not get any of the output. E.g. in a callr subprocess callr defines a server, catches these conditions and copies them over to the main process, so nothing is printed in the subprocess.For the 'substitution markers' I only imagined the {?s} and {?none/one/several} possibilities outside of cli - the cli-specific styling has no meaning for strings so I don't expect e.g. {.pkg } to be useful outside of cli. The n-based replacements are a unique and valuable mechanism, and I'd love to see those more generally available.
Yes, this is what I suggested to have a
pluralize()function for. Since the pluralization is my brain child, I wouldn't really want to move it to another package. :)- addedfeaturea feature request or enhancementa feature request or enhancement
on Jun 10, 2020
I didn't see this in an existing issue, but apologies if it's been discussed already.
is an extremely useful piece of code but it's limited to console output (unless I'm mistaken). It would be more widely beneficial to either export a function which produces the text as a string before going to
cat(), PR such a helper to{glue}, or create a helper package which{cli}could import.It seems odd if one needs to use
capture.output()on this to make use of it outside of a console.