We’re very chuffed to announce the release of cli 3.1.0. cli helps you create a consistent and convenient command line interface.
You can install it from CRAN with:
install.packages("cli")
This release of cli comes with an important feature for end users: the ability to select or define their preferred palettes. The selected palette is respected by every package that relies on either cli or the crayon package. We also show some other improvements in this post, these are mainly aimed at developers.
You can see a full list of changes in the release notes.
Color palettes
Built-in palettes
This release of cli adds support for ANSI color customization. Now the 16 foreground colors, created via the col_*()
functions, and the 16 background colors, created via the bg_*()
functions, can be customized with the cli.palette
option.
You can set cli.palette
to one of the built-in cli palettes, or you can create your own palette. See all built-in palettes at the
cli homepage. You can also look at the ansi_palettes
object, which is a data frame of RGB colors, with one row for each palette. To look at a single palette, run
ansi_palette_show()
. It shows the current palette by default:
To use a built-in palette, set cli.palette
to the palette name. To make this permanent, put this setting into your .Rprofile
:
options(cli.palette = "dichro")
To set the default palette again, set cli.palette
to NULL
:
options(cli.palette = NULL)
Custom palettes
To create a custom palette, set the cli.palette
option to a named list where the names are the same as the column names in ansi_palettes
. Colors can be specified with RGB color strings of the #rrggbb
form or R color names (see the output of
grDevices::colors()
). For example:
options(cli.palette = list(
black = "#010101", red = "#de382b",
green = "#39b54a", yellow = "#ffc706",
blue = "#006fb8", magenta = "#762671",
cyan = "#2cb5e9", white = "#cccccc",
br_black = "#808080", br_red = "#ff0000",
br_green = "#00ff00", br_yellow = "#ffff00",
br_blue = "#0000ff", br_magenta = "#ff00ff",
br_cyan = "#00ffff", br_white = "#ffffff"
))
Color interpolation
For color palettes your terminal or IDE needs to support at least 256 ANSI colors. On terminals with true color ANSI support cli will use the exact colors, as specified in the cli.palette
option. On consoles with 256 ANSI colors, e.g. the RStudio console, cli will interpolate the specified colors to the closest ANSI-256 color. This means that the actual output will probably look slightly different from the specified RGB colors on these displays.
What about the crayon package?
crayon is an older package than cli, with a smaller scope: adding ANSI colors to your display. More than 300 packages use crayon, so to make sure that cli palettes are respected in these packages as well, we added palette support to the latest release of crayon. Specifying the cli.palette
option changes the colors in cli and in crayon as well, the same way.
This said, cli does have some additional features compared to crayon, e.g. the support of bright colors. Our focus will be on improving the cli package in the future, and crayon will only receive important bug fixes. If you already use both cli and crayon, then it might make sense to completely switch to cli.
Palettes in terminals
Many modern terminal emulators, e.g. iTerm on macOS, already allow the customization of ANSI colors, and some also support themes with custom ANSI palettes. If you already use this method to customize ANSI colors, then you don’t need to set the cli.palette
option. If you use both terminals and RStudio then you can set it only in RStudio:
if (Sys.getenv("RSTUDIO")=="1") options(cli.palette = "dichro")
Other improvements
Bright ANSI colors
cli now has a new set of functions to create the bright version of the 8 base ANSI colors. The col_br_*()
functions set the foreground and the bg_br_*()
functions set the background colors of strings:
cli::col_blue("This is blue.")
cli::col_br_blue("This is bright blue.")
True color ANSI
cli now supports true color ANSI consoles better. Now custom styles made with
make_ansi_style()
will not interpolate the specified color on these displays:
orange <- make_ansi_style("#eb6123")
orange("This will be halloween orange.")
Unicode graphemes
cli’s ansi_*()
functions and the new utf8_*()
functions now handle Unicode graphemes properly. For example
ansi_nchar()
and
utf8_nchar()
count graphemes by default, and
ansi_substr()
and
utf8_substr()
will break the input strings at grapheme boundaries.
Consider this Unicode grapheme: 👷🏽♀️ (female construction worker, medium skin tone). It consists of five Unicode code points:
\U{1f477}
, construction worker,\U{1f3fd}
, emoji modifier Fitzpatrick type-4, for the skin tone,\u200d
, zero width joiner,\u2640
, female sign,\ufe0f
, variation selector-16, to specify that the preceding character should be displayed as an emoji.
cli functions handle this grapheme properly:
wrk <- "👷🏽♀️"
as.hexmode(utf8ToInt(wrk))
#> [1] "1f477" "1f3fd" "0200d" "02640" "0fe0f"
# graphemes by default
utf8_nchar(wrk)
#> [1] 1
# code points
utf8_nchar(wrk, type = "codepoints")
#> [1] 5
# correct display width
utf8_nchar(wrk, type = "width")
#> [1] 2
Syntax highlight R code
The new
code_highlight()
function parses and syntax highlights R code using ANSI colors and styles. You can use
deparse()
to highlight the code of an existing function:
writeLines(code_highlight(deparse(cli::hash_emoji)))
Human readable hash functions
Sometimes it is convenient to create a short hash of a string, that is easy to compare to other hashes. The new
hash_emoji()
function creates a very short emoji hash of a string. The new
hash_animal()
function uses a short expression with one or more adjectives and an animal name:
txt <- "Hash this string please!"
hash_emoji(txt)$hash
#> [1] "👨👩👦👦🍘😽"
hash_emoji(txt)$text
#> [1] "family: man, woman, boy, boy, rice cracker, kissing cat"
hash_animal(txt)$hash
#> [1] "deadsmooth anaemic bighorn"
If you are using the new version of the
sessioninfo package, then you already see an emoji hash on top of the
sessioninfo::session_info()
output. This makes trivial to decide if session_info()
outputs are the same or not, without comparing them line by line.
Acknowledgements
A big thanks to all 76 contributors who filed issues and contributed code to this and past cli releases:
@aedobbyn, @AkhilGNair, @AlbertRapp, @assignUser, @batpigandme, @brodieG, @bwiernik, @cderv, @cfhammill, @cjyetman, @ColinFay, @combiz, @cpsievert, @danielvartan, @datafj, @DavisVaughan, @dchiu911, @dfalbel, @dgkf, @elinw, @flying-sheep, @fmichonneau, @fmmattioni, @gaborcsardi, @gavinsimpson, @GjjvdBurg, @gregleleu, @GregorDeCillia, @gwd999, @hadley, @IndrajeetPatil, @jennybc, @jimhester, @jonkeane, @jonocarroll, @juniperlsimonis, @krlmlr, @lazappi, @leeper, @lionel-, @llrs, @lorenzwalthert, @MarkEdmondson1234, @markwsac, @mattfidler, @matthiaskaeding, @mgirlich, @MilesMcBain, @MislavSag, @mjsteinbaugh, @MLopez-Ibanez, @mrcaseb, @ms609, @nfancy, @nick-komick, @overmar, @pat-s, @paul-sheridan, @QuLogic, @ramiromagno, @rrodrigueznt, @rundel, @salim-b, @sgibb, @ShixiangWang, @sthibaul, @tentacles-from-outer-space, @thothal, @topepo, @torfason, @trestletech, @tzakharko, @wngrtn, @x1o, @yutannihilation, and @zachary-foster.