```{r, child = "setup.Rmd", include = FALSE} ``` Condition formatting is a set of operations applied to raw inputs for error messages that includes: - Transforming a character vector of lines to a width-wrapped list of error bullets. This makes it easy to write messages in a list format where each bullet conveys a single important point. ```{r, error = TRUE} abort(c( "The error header", "*" = "An error bullet", "i" = "An info bullet", "x" = "A cross bullet" )) ``` See the [tidyverse error style guide](https://style.tidyverse.org/error-messages.html) for more about this style of error messaging. - Applying style (emphasis, boldness, ...) and colours to message elements. While the rlang package embeds rudimentary formatting routines, the main formatting engine is implemented in the [cli package](https://cli.r-lib.org/). ## Formatting messages with cli By default, rlang uses an internal mechanism to format bullets. It is preferable to delegate formatting to the [cli package](https://cli.r-lib.org/) by using [cli::cli_abort()], [cli::cli_warn()], and [cli::cli_inform()] instead of the rlang versions. These wrappers enable cli formatting with sophisticated paragraph wrapping and bullet indenting that make long lines easier to read. In the following example, a long `!` bullet is broken with an indented newline: ```r rlang::global_entrace(class = "errorr") #> Error in `rlang::global_entrace()`: #> ! `class` must be one of "error", "warning", or "message", #> not "errorr". #> i Did you mean "error"? ``` The cli wrappers also add many features such as interpolation, semantic formatting of text elements, and pluralisation: ```r inform_marbles <- function(n_marbles) { cli::cli_inform(c( "i" = "I have {n_marbles} shiny marble{?s} in my bag.", "v" = "Way to go {.code cli::cli_inform()}!" )) } inform_marbles(1) #> i I have 1 shiny marble in my bag. #> v Way to go `cli::cli_inform()`! inform_marbles(2) #> i I have 2 shiny marbles in my bag. #> v Way to go `cli::cli_inform()`! ``` ## Transitioning from `abort()` to `cli_abort()` If you plan to mass-rename calls from `abort()` to `cli::cli_abort()`, be careful if you assemble error messages from user inputs. If these individual pieces contain cli or glue syntax, this will result in hard-to-debug errors and possibly [unexpected behaviour](https://xkcd.com/327/). ```{r, error = TRUE} user_input <- "{base::stop('Wrong message.', call. = FALSE)}" cli::cli_abort(sprintf("Can't handle input `%s`.", user_input)) ``` To avoid this, protect your error messages by using cli to assemble the pieces: ```{r, error = TRUE} user_input <- "{base::stop('Wrong message.', call. = FALSE)}" cli::cli_abort("Can't handle input {.code {user_input}}.") ``` ## Enabling cli formatting globally To enable cli formatting for all `abort()` calls in your namespace, call [local_use_cli()] in the `onLoad` hook of your package. Using [on_load()] (make sure to call [run_on_load()] in your hook): ```r on_load(local_use_cli()) ``` Enabling cli formatting in `abort()` is useful for: - Transitioning from `abort()` to `cli::cli_abort()` progressively. - Using `abort()` when you'd like to disable interpolation syntax. - Creating error conditions with `error_cnd()`. These condition messages will be automatically formatted with cli as well.