├── air.toml ├── .github ├── .gitignore └── workflows │ ├── rhel.yaml │ └── pkgdown.yaml ├── vignettes ├── .gitignore ├── palettes.css └── pluralization.Rmd ├── LICENSE ├── R ├── sysdata.rda ├── cli-package.R ├── mocks.R ├── server.R ├── progress-ticking.R ├── ruler.R ├── enc-utils.R ├── utils.R ├── assertions.R ├── cli-errors.R ├── rematch2.R ├── timer.R ├── ansi-utils.R ├── progress-utils.R ├── docs.R ├── progress-c.R └── sitrep.R ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── utf8.md │ ├── progress-handler-logger.md │ ├── defer.md │ ├── progress-ticking.md │ ├── console-width.md │ ├── ansiex-2.md │ ├── new-r │ │ └── inline-2.md │ ├── old-r │ │ └── inline-2.md │ ├── non-breaking-space.md │ ├── app.md │ ├── verbatim.md │ ├── rules.md │ ├── substitution.md │ ├── code.md │ ├── ansi.md │ ├── spark.md │ ├── text.md │ ├── ansi-make.md │ ├── glue.md │ ├── box-styles.md │ ├── utf8 │ │ └── utf8-output.txt │ ├── progress-message.md │ ├── ansi-hyperlink.md │ ├── rlang-1.1.0 │ │ └── rlang-errors.md │ ├── rlang-1.1.4 │ │ └── rlang-errors.md │ ├── rlang-1.1.6 │ │ └── rlang-errors.md │ └── deep-lists.md │ ├── test-code.R │ ├── test-defer.R │ ├── test-cat.R │ ├── progresstestcpp │ ├── NAMESPACE │ ├── R │ │ ├── cpp11.R │ │ └── testcpp.R │ ├── DESCRIPTION │ └── src │ │ ├── testcpp.cpp │ │ └── cpp11.cpp │ ├── test-box-styles.R │ ├── setup.R │ ├── progresstest │ ├── NAMESPACE │ ├── DESCRIPTION │ ├── progresstest.Rproj │ ├── R │ │ └── test.R │ └── src │ │ └── cleancall.h │ ├── test-substitution.R │ ├── test-progress-handler-logger.R │ ├── test-app.R │ ├── test-non-breaking-space.R │ ├── test-spark.R │ ├── test-timer.R │ ├── progress-2.c │ ├── test-spinners.R │ ├── test-console-width.R │ ├── test-verbatim.R │ ├── test-custom-handler.R │ ├── test-sitrep.R │ ├── test-progress-ticking.R │ ├── test-suppress.R │ ├── test-text.R │ ├── test-headers.R │ ├── test-progress-handler-say.R │ ├── test-alerts.R │ ├── test-package.R │ ├── test-bullets.R │ ├── test-ansi-combine.R │ ├── test-ansi-palette.R │ ├── test-glue.R │ ├── test-progress-utils.R │ ├── test-deep-lists.R │ ├── test-progress-bar.R │ ├── test-cat-helpers.R │ ├── test-ansi-utils.R │ └── test-meta.R ├── .vscode ├── extensions.json └── settings.json ├── src ├── Makevars ├── winfiles.h ├── inst.c ├── win-utf8.c ├── utils.c ├── vtparse.h ├── tty.c ├── vtparse_table.h └── cleancall.h ├── Makefile ├── codecov.yml ├── man ├── ruler.Rd ├── pretty_print_code.Rd ├── is_utf8_output.Rd ├── list_spinners.Rd ├── ansi_simplify.Rd ├── ansi_nzchar.Rd ├── cli_output_connection.Rd ├── match_selector.Rd ├── ansi_regex.Rd ├── demo_spinners.Rd ├── symbol.Rd ├── cli_fmt.Rd ├── ansi_string.Rd ├── cli_list_themes.Rd ├── utf8_graphemes.Rd ├── roxygen │ └── meta.R ├── has_keypress_support.Rd ├── parse_selector.Rd ├── pluralization-helpers.Rd ├── spark_line.Rd ├── match_selector_node.Rd ├── cli_sitrep.Rd ├── ansi_has_any.Rd ├── ansi_strip.Rd ├── figures │ └── README │ │ ├── h3-dark.svg │ │ ├── h3.svg │ │ ├── alert-dark.svg │ │ ├── alert.svg │ │ ├── alert-success-dark.svg │ │ ├── alert-success.svg │ │ ├── h2-dark.svg │ │ ├── h2.svg │ │ ├── glue-dark.svg │ │ ├── glue.svg │ │ ├── h1.svg │ │ ├── h1-dark.svg │ │ ├── alert-danger-dark.svg │ │ ├── alert-danger.svg │ │ ├── alert-info.svg │ │ ├── alert-info-dark.svg │ │ ├── plurals.svg │ │ ├── plurals-dark.svg │ │ ├── alert-warning-dark.svg │ │ ├── alert-warning.svg │ │ ├── themes-dark.svg │ │ ├── themes.svg │ │ ├── lists.svg │ │ └── lists-dark.svg ├── utf8_substr.Rd ├── progress-utils.Rd ├── ansi_trimws.Rd ├── is_ansi_tty.Rd ├── code_highlight.Rd ├── cli-package.Rd ├── diff_str.Rd ├── ansi_strtrim.Rd ├── keypress.Rd ├── start_app.Rd ├── ansi_nchar.Rd ├── ansi_hide_cursor.Rd ├── ansi_html.Rd ├── unicode-width-workaround.Rd ├── get_spinner.Rd ├── combine_ansi_styles.Rd ├── cat_line.Rd ├── cli_debug_doc.Rd ├── style_hyperlink.Rd ├── vt_output.Rd ├── console_width.Rd ├── utf8_nchar.Rd ├── ansi_html_style.Rd ├── ansi_strwrap.Rd ├── hash_md5.Rd ├── hash_sha1.Rd ├── ansi_grep.Rd ├── hash_sha256.Rd ├── cli_bullets_raw.Rd └── cli_verbatim.Rd ├── cli.Rproj ├── LICENSE.note ├── .gitignore ├── .Rbuildignore ├── tools ├── ansi-iterm-palettes.txt ├── unicode.R ├── spinners.R └── ansi-palettes.txt ├── LICENSE.md ├── inst ├── shiny │ ├── along │ │ └── app.R │ ├── simple │ │ └── app.R │ ├── format │ │ └── app.R │ └── output │ │ └── app.R └── examples │ └── apps │ └── up.R ├── exec └── up.R └── DESCRIPTION /air.toml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2025 2 | COPYRIGHT HOLDER: cli authors 3 | -------------------------------------------------------------------------------- /R/sysdata.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-lib/cli/HEAD/R/sysdata.rda -------------------------------------------------------------------------------- /vignettes/palettes.css: -------------------------------------------------------------------------------- 1 | 2 | img { 3 | border: 1px solid #111; 4 | } 5 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(cli) 3 | 4 | test_check("cli") 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Posit.air-vscode" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | PKG_CPPFLAGS = -I../inst/include 2 | PKG_CFLAGS = $(C_VISIBILITY) 3 | PKG_LIBS = -lpthread 4 | -------------------------------------------------------------------------------- /src/winfiles.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef R_WINFILES_H 3 | #define R_WINFILES_H 4 | 5 | int open_file(const char *path, int oflag); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/utf8.md: -------------------------------------------------------------------------------- 1 | # errors 2 | 3 | ! Invalid output from UTF-8 R 4 | i Found 1 UTF-8 begin marker and 2 end markers. 5 | 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[r]": { 3 | "editor.formatOnSave": true, 4 | "editor.defaultFormatter": "Posit.air-vscode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /R/cli-package.R: -------------------------------------------------------------------------------- 1 | #' @aliases cli-package NULL 2 | #' @keywords internal 3 | "_PACKAGE" 4 | 5 | ## usethis namespace: start 6 | ## usethis namespace: end 7 | NULL 8 | -------------------------------------------------------------------------------- /tests/testthat/test-code.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that_cli("issue #154", { 5 | expect_snapshot({ 6 | cli_code("a\nb\nc") 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | README.md: README.Rmd 3 | Rscript -e "rmarkdown::render('$<')" && \ 4 | sed -i .bak 's|tools/figures|https://cdn.jsdelivr.net/gh/r-lib/cli@v1.1.0-pre/tools/figures|g' README.md 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/progress-handler-logger.md: -------------------------------------------------------------------------------- 1 | # loggerr_out 2 | 3 | Code 4 | logger_out(bar, "updated") 5 | Output 6 | 2021-06-10T13:51:05+00:00 id 13/113 updated 7 | 8 | -------------------------------------------------------------------------------- /tests/testthat/test-defer.R: -------------------------------------------------------------------------------- 1 | test_that("errors", { 2 | fun <- function() { 3 | defer(1 + "") 4 | } 5 | 6 | expect_snapshot( 7 | error = TRUE, 8 | fun() 9 | ) 10 | }) 11 | -------------------------------------------------------------------------------- /tests/testthat/test-cat.R: -------------------------------------------------------------------------------- 1 | test_that("cat_line appends to file", { 2 | tmp <- tempfile() 3 | cat_line("a", file = tmp) 4 | cat_line("b", file = tmp) 5 | expect_equal(readLines(tmp), c("a", "b")) 6 | }) 7 | -------------------------------------------------------------------------------- /tests/testthat/progresstestcpp/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(test_baseline) 4 | export(test_cli) 5 | export(test_template) 6 | useDynLib(progresstestcpp, .registration = TRUE) 7 | -------------------------------------------------------------------------------- /tests/testthat/test-box-styles.R: -------------------------------------------------------------------------------- 1 | test_that_cli(configs = c("plain", "unicode"), "list_border_styles", { 2 | expect_snapshot( 3 | for (st in list_border_styles()) print(boxx("", border_style = st)) 4 | ) 5 | }) 6 | -------------------------------------------------------------------------------- /tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | unlink(dir( 2 | file.path(dirname(dirname(normalizePath(test_path()))), "src"), 3 | pattern = "[.]gcda$", 4 | full.names = TRUE 5 | )) 6 | withr::defer(.Call(clic__gcov_flush), teardown_env()) 7 | -------------------------------------------------------------------------------- /R/mocks.R: -------------------------------------------------------------------------------- 1 | .Call <- NULL 2 | Sys.time <- NULL 3 | commandArgs <- NULL 4 | get <- NULL 5 | getRversion <- NULL 6 | isatty <- NULL 7 | l10n_info <- NULL 8 | loadedNamespaces <- NULL 9 | system <- NULL 10 | system2 <- NULL 11 | -------------------------------------------------------------------------------- /src/inst.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // This file only tests that `inst/cli/progress.h` can be compiled 5 | // without issues 6 | 7 | void test(void) { 8 | cli_progress_bar(0, R_NilValue); 9 | } 10 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/defer.md: -------------------------------------------------------------------------------- 1 | # errors 2 | 3 | Code 4 | fun() 5 | Condition 6 | Error: 7 | ! Error in a deferred `on.exit()` clause 8 | Caused by error: 9 | ! non-numeric argument to binary operator 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/progresstest/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(test_baseline) 4 | export(test_cli) 5 | export(test_cli_unroll) 6 | export(test_modulo) 7 | export(testx) 8 | useDynLib(progresstest, .registration = TRUE, .fixes = "c_") 9 | -------------------------------------------------------------------------------- /tests/testthat/test-substitution.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that("glue errors", { 5 | expect_snapshot(error = TRUE, { 6 | cli_h1("foo { asdfasdfasdf } bar") 7 | cli_text("foo {cmd {dsfsdf()}}") 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /tests/testthat/test-progress-handler-logger.R: -------------------------------------------------------------------------------- 1 | test_that("loggerr_out", { 2 | bar <- new.env(parent = emptyenv()) 3 | bar$id <- "id" 4 | bar$current <- 13 5 | bar$total <- 113 6 | local_mocked_bindings(Sys.time = function() .POSIXct(1623325865, tz = "CET")) 7 | expect_snapshot(logger_out(bar, "updated")) 8 | }) 9 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/progress-ticking.md: -------------------------------------------------------------------------------- 1 | # ticking 2 | 3 | Code 4 | out 5 | Output 6 | [1] "\r1/10\033[K\r" "\r2/10\033[K\r" "\r3/10\033[K\r" "\r4/10\033[K\r" 7 | [5] "\r5/10\033[K\r" "\r6/10\033[K\r" "\r7/10\033[K\r" "\r8/10\033[K\r" 8 | [9] "\r9/10\033[K\r" "\r\033[K" 9 | 10 | -------------------------------------------------------------------------------- /tests/testthat/test-app.R: -------------------------------------------------------------------------------- 1 | test_that("stop_app() errors", { 2 | expect_snapshot( 3 | error = TRUE, 4 | stop_app(1:10) 5 | ) 6 | }) 7 | 8 | test_that("warning if inactive app", { 9 | app <- start_app(.auto_close = FALSE) 10 | stop_app(app) 11 | expect_snapshot( 12 | stop_app(app) 13 | ) 14 | }) 15 | -------------------------------------------------------------------------------- /tests/testthat/test-non-breaking-space.R: -------------------------------------------------------------------------------- 1 | test_that("does not break", { 2 | testthat::local_reproducible_output(unicode = TRUE) 3 | expect_snapshot(local({ 4 | withr::local_options(cli.width = 40) 5 | str30 <- "123456789 123456789 1234567890" 6 | cli_text(c(str30, "this\u00a0is\u00a0not\u00a0breaking")) 7 | })) 8 | }) 9 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/console-width.md: -------------------------------------------------------------------------------- 1 | # errors 2 | 3 | ! `options("cli.width")` must be an integer scalar. 4 | i `options("cli.width")` is a character vector. 5 | 6 | --- 7 | 8 | ! `options("cli.width")` cannot be `NA`. 9 | 10 | --- 11 | 12 | ! `options("cli.width")` must be a positive integer and not -100. 13 | 14 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/ansiex-2.md: -------------------------------------------------------------------------------- 1 | # NA 2 | 3 | Code 4 | ansi_html(s) 5 | Output 6 | [1] "foo" 7 | [2] NA 8 | [3] "bar" 9 | [4] "foobar" 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/new-r/inline-2.md: -------------------------------------------------------------------------------- 1 | # various errors 2 | 3 | Code 4 | cli_text("xx {__cannot-parse-this__} yy") 5 | Condition 6 | Error: 7 | ! Could not parse cli `{}` expression: `__cannot-parse-th...`. 8 | Caused by error: 9 | ! :1:2: unexpected input 10 | 1: __ 11 | ^ 12 | 13 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/old-r/inline-2.md: -------------------------------------------------------------------------------- 1 | # various errors 2 | 3 | Code 4 | cli_text("xx {__cannot-parse-this__} yy") 5 | Condition 6 | Error: 7 | ! Could not parse cli `{}` expression: `__cannot-parse-th...`. 8 | Caused by error: 9 | ! :1:1: unexpected input 10 | 1: _ 11 | ^ 12 | 13 | -------------------------------------------------------------------------------- /tests/testthat/progresstestcpp/R/cpp11.R: -------------------------------------------------------------------------------- 1 | # Generated by cpp11: do not edit by hand 2 | 3 | test_baseline_ <- function() { 4 | .Call(`_progresstestcpp_test_baseline_`) 5 | } 6 | 7 | test_cli_ <- function() { 8 | .Call(`_progresstestcpp_test_cli_`) 9 | } 10 | 11 | test_template_ <- function() { 12 | .Call(`_progresstestcpp_test_template_`) 13 | } 14 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/non-breaking-space.md: -------------------------------------------------------------------------------- 1 | # does not break 2 | 3 | Code 4 | local({ 5 | withr::local_options(cli.width = 40) 6 | str30 <- "123456789 123456789 1234567890" 7 | cli_text(c(str30, "this is not breaking")) 8 | }) 9 | Message 10 | 123456789 123456789 11 | 1234567890this is not breaking 12 | 13 | -------------------------------------------------------------------------------- /tests/testthat/progresstestcpp/R/testcpp.R: -------------------------------------------------------------------------------- 1 | #' @useDynLib progresstestcpp, .registration = TRUE 2 | NULL 3 | 4 | #' @export 5 | 6 | test_baseline <- function() { 7 | test_baseline_() 8 | } 9 | 10 | #' @export 11 | 12 | test_cli <- function() { 13 | test_cli_() 14 | } 15 | 16 | #' @export 17 | 18 | test_template <- function() { 19 | test_template_() 20 | } 21 | -------------------------------------------------------------------------------- /tests/testthat/test-spark.R: -------------------------------------------------------------------------------- 1 | test_that_cli(configs = c("plain", "unicode"), "spark_bar", { 2 | expect_snapshot({ 3 | spark_bar(seq(0, 1, length.out = 8)) 4 | spark_bar(c(0, NA, 0.5, NA, 1)) 5 | }) 6 | }) 7 | 8 | test_that_cli(configs = c("plain", "unicode"), "spark_line", { 9 | expect_snapshot({ 10 | spark_line(seq(0, 1, length.out = 10)) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /R/server.R: -------------------------------------------------------------------------------- 1 | cli_server_default <- function(msg) { 2 | cli_server_default_safe(msg) 3 | } 4 | 5 | cli_server_default_safe <- function(msg) { 6 | type <- as.character(msg$type)[1] 7 | app <- default_app() %||% start_app(.auto_close = FALSE) 8 | do.call(app[[type]], msg$args) 9 | } 10 | 11 | cli_server_callr_handler <- function(msg) { 12 | cli_server_default(msg) 13 | } 14 | -------------------------------------------------------------------------------- /tests/testthat/test-timer.R: -------------------------------------------------------------------------------- 1 | test_that("ALTREP methods", { 2 | expect_equal(length(`__cli_update_due`), 1L) 3 | 4 | expect_silent({ 5 | tim <- `__cli_update_due` 6 | tim[1] <- FALSE 7 | }) 8 | 9 | expect_silent({ 10 | `__cli_update_due`[1] 11 | }) 12 | }) 13 | 14 | test_that("cli_tick_set", { 15 | skip_on_cran() 16 | expect_silent(cli_tick_set()) 17 | }) 18 | -------------------------------------------------------------------------------- /vignettes/pluralization.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "cli pluralization" 3 | author: "Gábor Csárdi" 4 | description: > 5 | Automatically pluralize cli messages using glue templating 6 | date: "`r Sys.Date()`" 7 | output: rmarkdown::html_document 8 | --- 9 | 10 | 11 | ```{r child= "../man/chunks/pluralization.Rmd"} 12 | ``` 13 | -------------------------------------------------------------------------------- /tests/testthat/progress-2.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | SEXP clitest__init_timer() { 6 | int before = CLI_SHOULD_TICK; 7 | cli_progress_init_timer(); 8 | int after = CLI_SHOULD_TICK; 9 | SEXP ret = PROTECT(Rf_allocVector(INTSXP, 2)); 10 | INTEGER(ret)[0] = before; 11 | INTEGER(ret)[1] = after; 12 | UNPROTECT(1); 13 | return ret; 14 | } 15 | -------------------------------------------------------------------------------- /tests/testthat/test-spinners.R: -------------------------------------------------------------------------------- 1 | test_that("get_spinner", { 2 | if (is_utf8_output()) { 3 | expect_equal(get_spinner()$name, "dots") 4 | } else { 5 | expect_equal(get_spinner()$name, "line") 6 | } 7 | }) 8 | 9 | test_that("list_spinners", { 10 | ls <- list_spinners() 11 | expect_true(is.character(ls)) 12 | expect_true("dots" %in% ls) 13 | expect_true("line" %in% ls) 14 | }) 15 | -------------------------------------------------------------------------------- /man/ruler.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ruler.R 3 | \name{ruler} 4 | \alias{ruler} 5 | \title{Print the helpful ruler to the screen} 6 | \usage{ 7 | ruler(width = console_width()) 8 | } 9 | \arguments{ 10 | \item{width}{Ruler width.} 11 | } 12 | \description{ 13 | Print the helpful ruler to the screen 14 | } 15 | \examples{ 16 | ruler() 17 | } 18 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/app.md: -------------------------------------------------------------------------------- 1 | # stop_app() errors 2 | 3 | Code 4 | stop_app(1:10) 5 | Condition 6 | Error: 7 | ! `app` must be a CLI app 8 | i `app` is an integer vector 9 | 10 | # warning if inactive app 11 | 12 | Code 13 | stop_app(app) 14 | Condition 15 | Warning in `stop_app()`: 16 | No app to end 17 | Output 18 | NULL 19 | 20 | -------------------------------------------------------------------------------- /tests/testthat/progresstest/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: progresstest 2 | Title: Testing Terminal Progress Bars 3 | Version: 1.0.0 4 | Authors@R: person("Gabor", "Csardi", , "csardi.gabor@gmail.com", role = c("aut", "cre")) 5 | Description: Test progress bars from the cli package. 6 | License: MIT + file LICENSE 7 | Imports: 8 | cli 9 | LinkingTo: 10 | cli 11 | RoxygenNote: 7.1.1.9001 12 | Encoding: UTF-8 13 | -------------------------------------------------------------------------------- /tests/testthat/test-console-width.R: -------------------------------------------------------------------------------- 1 | test_that("errors", { 2 | withr::local_options(cli.width = letters) 3 | expect_snapshot_error( 4 | console_width() 5 | ) 6 | 7 | withr::local_options(cli.width = NA_integer_) 8 | expect_snapshot_error( 9 | console_width() 10 | ) 11 | 12 | withr::local_options(cli.width = -100L) 13 | expect_snapshot_error( 14 | console_width() 15 | ) 16 | }) 17 | -------------------------------------------------------------------------------- /tests/testthat/test-verbatim.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that("verbatim text is correctly styled", { 5 | expect_snapshot(local({ 6 | theme <- list(.padded = list("margin-left" = 4)) 7 | cli_div(class = "padded", theme = theme) 8 | 9 | lines <- c("first", "second", "third") 10 | 11 | cli_verbatim(lines) 12 | cli_verbatim(paste0(lines, collapse = "\n")) 13 | })) 14 | }) 15 | -------------------------------------------------------------------------------- /R/progress-ticking.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | ticking <- function(cond, name = NULL, ..., .envir = parent.frame()) { 4 | val <- force(cond) 5 | 6 | new <- is.null(clienv$progress_ids[[format(.envir)]]) 7 | 8 | if (new && val) cli_progress_bar(name = name, ..., .envir = .envir) 9 | 10 | if (val) { 11 | cli_progress_update(.envir = .envir) 12 | } else { 13 | cli_progress_done(.envir = .envir) 14 | } 15 | 16 | val 17 | } 18 | -------------------------------------------------------------------------------- /tests/testthat/progresstestcpp/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: progresstestcpp 2 | Title: Testing Terminal Progress Bars 3 | Version: 1.0.0 4 | Authors@R: person("Gabor", "Csardi", , "csardi.gabor@gmail.com", role = c("aut", "cre")) 5 | Description: Test progress bars from the cli package, C++ version. 6 | License: MIT + file LICENSE 7 | Imports: 8 | cli 9 | LinkingTo: 10 | cli, 11 | cpp11 12 | RoxygenNote: 7.1.1.9001 13 | Encoding: UTF-8 14 | -------------------------------------------------------------------------------- /R/ruler.R: -------------------------------------------------------------------------------- 1 | #' Print the helpful ruler to the screen 2 | #' 3 | #' @export 4 | #' @param width Ruler width. 5 | #' @examples 6 | #' ruler() 7 | 8 | ruler <- function(width = console_width()) { 9 | x <- seq_len(width) 10 | y <- rep("-", length(x)) 11 | 12 | y[x %% 5 == 0] <- "+" 13 | y[x %% 10 == 0] <- style_bold(as.character((x[x %% 10 == 0] %/% 10) %% 10)) 14 | 15 | cat(y, "\n", sep = "") 16 | cat(x %% 10, "\n", sep = "") 17 | } 18 | -------------------------------------------------------------------------------- /tests/testthat/progresstest/progresstest.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /cli.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/verbatim.md: -------------------------------------------------------------------------------- 1 | # verbatim text is correctly styled 2 | 3 | Code 4 | local({ 5 | theme <- list(.padded = list(`margin-left` = 4)) 6 | cli_div(class = "padded", theme = theme) 7 | lines <- c("first", "second", "third") 8 | cli_verbatim(lines) 9 | cli_verbatim(paste0(lines, collapse = "\n")) 10 | }) 11 | Message 12 | first 13 | second 14 | third 15 | first 16 | second 17 | third 18 | 19 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/rules.md: -------------------------------------------------------------------------------- 1 | # centered label 2 | 3 | Code 4 | rule(left = "label", center = "label") 5 | Condition 6 | Error in `rule()`: 7 | ! 'center' cannot be specified with 'left' or 'right' 8 | Code 9 | rule(center = "label", right = "label") 10 | Condition 11 | Error in `rule()`: 12 | ! 'center' cannot be specified with 'left' or 'right' 13 | 14 | # print.cli_rule 15 | 16 | Code 17 | rule("foo") 18 | Output 19 | -- foo ------------- 20 | 21 | -------------------------------------------------------------------------------- /tests/testthat/test-custom-handler.R: -------------------------------------------------------------------------------- 1 | test_that("custom handler works", { 2 | conds <- list() 3 | withr::with_options( 4 | list(cli.default_handler = function(msg) conds <<- c(conds, list(msg))), 5 | { 6 | cli_h1("title") 7 | cli_h2("subtitle") 8 | cli_text("text") 9 | } 10 | ) 11 | expect_equal(length(conds), 3) 12 | lapply(conds, expect_s3_class, "cli_message") 13 | expect_equal(conds[[1]]$type, "h1") 14 | expect_equal(conds[[2]]$type, "h2") 15 | expect_equal(conds[[3]]$type, "text") 16 | }) 17 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/substitution.md: -------------------------------------------------------------------------------- 1 | # glue errors 2 | 3 | Code 4 | cli_h1("foo { asdfasdfasdf } bar") 5 | Condition 6 | Error: 7 | ! Could not evaluate cli `{}` expression: ` asdfasdfasdf `. 8 | Caused by error: 9 | ! object 'asdfasdfasdf' not found 10 | Code 11 | cli_text("foo {cmd {dsfsdf()}}") 12 | Condition 13 | Error: 14 | ! Could not parse cli `{}` expression: `cmd {dsfsdf()}`. 15 | Caused by error: 16 | ! :1:5: unexpected '{' 17 | 1: cmd { 18 | ^ 19 | 20 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/code.md: -------------------------------------------------------------------------------- 1 | # issue #154 [plain] 2 | 3 | Code 4 | cli_code("a\nb\nc") 5 | Message 6 | a 7 | b 8 | c 9 | 10 | # issue #154 [ansi] 11 | 12 | Code 13 | cli_code("a\nb\nc") 14 | Message 15 | a 16 | b 17 | c 18 | 19 | # issue #154 [unicode] 20 | 21 | Code 22 | cli_code("a\nb\nc") 23 | Message 24 | a 25 | b 26 | c 27 | 28 | # issue #154 [fancy] 29 | 30 | Code 31 | cli_code("a\nb\nc") 32 | Message 33 | a 34 | b 35 | c 36 | 37 | -------------------------------------------------------------------------------- /man/pretty_print_code.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/prettycode.R 3 | \name{pretty_print_code} 4 | \alias{pretty_print_code} 5 | \title{Turn on pretty-printing functions at the R console} 6 | \usage{ 7 | pretty_print_code() 8 | } 9 | \description{ 10 | Defines a print method for functions, in the current session, that supports 11 | syntax highlighting. 12 | } 13 | \details{ 14 | The new print method takes priority over the built-in one. Use 15 | \code{\link[base:message]{base::suppressMessages()}} to suppress the alert message. 16 | } 17 | -------------------------------------------------------------------------------- /tests/testthat/test-sitrep.R: -------------------------------------------------------------------------------- 1 | test_that("sitrep runs", { 2 | expect_true(is.list(cli_sitrep())) 3 | expect_true(is.character(format(cli_sitrep()))) 4 | out <- capture_output(print(cli_sitrep())) 5 | expect_true(all(grepl("^- ", out))) 6 | }) 7 | 8 | test_that("get_active_symbol_set", { 9 | withr::with_options(list(cli.unicode = TRUE), { 10 | expect_true(get_active_symbol_set() %in% c("UTF-8", "RStudio (UTF-8)")) 11 | }) 12 | withr::with_options(list(cli.unicode = FALSE), { 13 | set <- get_active_symbol_set() 14 | expect_equal(set, "ASCII (non UTF-8)") 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /tests/testthat/test-progress-ticking.R: -------------------------------------------------------------------------------- 1 | test_that("ticking", { 2 | withr::local_options( 3 | cli.ansi = TRUE, 4 | cli.dynamic = TRUE, 5 | cli.progress_show_after = 0, 6 | cli.progress_handlers_only = "cli" 7 | ) 8 | 9 | fun <- function() { 10 | i <- 0L 11 | while ( 12 | ticking( 13 | i < 10L, 14 | total = 10L, 15 | name = "ticking", 16 | format = "{cli::pb_current}/{cli::pb_total}" 17 | ) 18 | ) { 19 | i <- i + 1L 20 | } 21 | } 22 | 23 | out <- capture_cli_messages(cli_with_ticks(fun())) 24 | expect_snapshot(out) 25 | }) 26 | -------------------------------------------------------------------------------- /man/is_utf8_output.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utf8.R 3 | \name{is_utf8_output} 4 | \alias{is_utf8_output} 5 | \title{Whether cli is emitting UTF-8 characters} 6 | \usage{ 7 | is_utf8_output() 8 | } 9 | \value{ 10 | Flag, whether cli uses UTF-8 characters. 11 | } 12 | \description{ 13 | UTF-8 cli characters can be turned on by setting the \code{cli.unicode} 14 | option to \code{TRUE}. They can be turned off by setting if to \code{FALSE}. 15 | If this option is not set, then \code{\link[base:l10n_info]{base::l10n_info()}} is used to detect 16 | UTF-8 support. 17 | } 18 | -------------------------------------------------------------------------------- /man/list_spinners.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/spinner.R 3 | \name{list_spinners} 4 | \alias{list_spinners} 5 | \title{List all available spinners} 6 | \usage{ 7 | list_spinners() 8 | } 9 | \value{ 10 | Character vector of all available spinner names. 11 | } 12 | \description{ 13 | List all available spinners 14 | } 15 | \examples{ 16 | list_spinners() 17 | get_spinner(list_spinners()[1]) 18 | } 19 | \seealso{ 20 | Other spinners: 21 | \code{\link{demo_spinners}()}, 22 | \code{\link{get_spinner}()}, 23 | \code{\link{make_spinner}()} 24 | } 25 | \concept{spinners} 26 | -------------------------------------------------------------------------------- /man/ansi_simplify.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_simplify} 4 | \alias{ansi_simplify} 5 | \title{Simplify ANSI styling tags} 6 | \usage{ 7 | ansi_simplify(x, csi = c("keep", "drop")) 8 | } 9 | \arguments{ 10 | \item{x}{Input string} 11 | 12 | \item{csi}{What to do with non-SGR ANSI sequences, either \code{"keep"}, 13 | or \code{"drop"} them.} 14 | } 15 | \value{ 16 | Simplified \code{cli_ansi_string} vector. 17 | } 18 | \description{ 19 | It creates an equivalent, but possibly shorter ANSI styled string, by 20 | removing duplicate and empty tags. 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/test-suppress.R: -------------------------------------------------------------------------------- 1 | test_that("suppress output", { 2 | if (getRversion() >= "4.0.0") { 3 | cnd <- NULL 4 | tryCatch( 5 | suppressMessages(cli_text("foo"), "cliMessage"), 6 | message = function(cnd2) cnd <<- cnd2 7 | ) 8 | expect_null(cnd) 9 | } 10 | 11 | mysuppress <- function(expr) { 12 | withCallingHandlers( 13 | expr, 14 | cliMessage = function(msg) invokeRestart("muffleMessage") 15 | ) 16 | } 17 | 18 | cnd <- NULL 19 | tryCatch( 20 | mysuppress(cli_text("foo")), 21 | message = function(cnd2) cnd <<- cnd2 22 | ) 23 | expect_null(cnd) 24 | }) 25 | -------------------------------------------------------------------------------- /R/enc-utils.R: -------------------------------------------------------------------------------- 1 | # keep encoding, even if useBytes = TRUE 2 | 3 | sub_ <- function(pattern, replacement, x, ...) { 4 | enc <- Encoding(x) 5 | ret <- sub(pattern, replacement, x, ...) 6 | if (length(ret)) Encoding(ret) <- enc 7 | ret 8 | } 9 | 10 | gsub_ <- function(pattern, replacement, x, ...) { 11 | enc <- Encoding(x) 12 | ret <- gsub(pattern, replacement, x, ...) 13 | if (length(ret)) Encoding(ret) <- enc 14 | ret 15 | } 16 | 17 | strsplit_ <- function(x, ...) { 18 | enc <- Encoding(x) 19 | ret <- strsplit(x, ...) 20 | for (i in seq_along(ret)) { 21 | Encoding(ret[[i]]) <- enc[i] 22 | } 23 | ret 24 | } 25 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | is_yes <- function(x) { 2 | tolower(x) %in% c("true", "yes", "y", "t", "1") 3 | } 4 | 5 | format_iso_8601 <- function(p) { 6 | format(p, "%Y-%m-%dT%H:%M:%S+00:00") 7 | } 8 | 9 | has_packages <- function(pkgs) { 10 | all(vapply(pkgs, requireNamespace, logical(1), quietly = TRUE)) 11 | } 12 | 13 | cli_escape <- function(x) { 14 | x <- gsub("{", "{{", x, fixed = TRUE) 15 | x <- gsub("}", "}}", x, fixed = TRUE) 16 | x 17 | } 18 | 19 | # missing from older R 20 | 21 | isFALSE <- function(x) { 22 | is.logical(x) && length(x) == 1L && !is.na(x) && !x 23 | } 24 | 25 | get_ppid <- function() { 26 | .Call(clic_getppid) 27 | } 28 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/ansi.md: -------------------------------------------------------------------------------- 1 | # print.cli_ansi_style 2 | 3 | Code 4 | print(col_red) 5 | Output 6 | 7 | Example output 8 | 9 | # print.cli_ansi_string 10 | 11 | Code 12 | print(col_red("red")) 13 | Output 14 | 15 | [1] red 16 | 17 | # ansi-scale 18 | 19 | Code 20 | ansi_scale(c(0, 0, 0)) 21 | Output 22 | [1] 0 0 0 23 | Code 24 | ansi_scale(c(255, 100, 0)) 25 | Output 26 | [1] 5 2 0 27 | Code 28 | ansi_scale(c(255, 100, 0), round = FALSE) 29 | Output 30 | [1] 5.000000 1.960784 0.000000 31 | 32 | -------------------------------------------------------------------------------- /man/ansi_nzchar.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_nzchar} 4 | \alias{ansi_nzchar} 5 | \title{Like \code{\link[base:nchar]{base::nzchar()}}, but for ANSI strings} 6 | \usage{ 7 | ansi_nzchar(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{Character vector. Other objects are coerced using 11 | \code{\link[base:character]{base::as.character()}}.} 12 | 13 | \item{...}{Passed to \code{\link[base:nchar]{base::nzchar()}}.} 14 | } 15 | \description{ 16 | Like \code{\link[base:nchar]{base::nzchar()}}, but for ANSI strings 17 | } 18 | \examples{ 19 | ansi_nzchar("") 20 | ansi_nzchar(col_red("")) 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE.note: -------------------------------------------------------------------------------- 1 | 2 | ## utf8lite 3 | 4 | Some parts of cli are based on the utf8lite library 5 | (https://github.com/patperry/utf8lite), which is licensed 6 | under the Apache License 2.0. 7 | 8 | In particular, 9 | 10 | * `src/utf8.c` contains parts modified from utf8lite, 11 | * `src/cli.h` contains parts modified from utf8lite, 12 | * The `src/graphbreak.h` and `src/charwidth` files are generated 13 | with scripts from utf8lite. 14 | 15 | ## vtparse 16 | 17 | The files `src/vtparse.h`, `src/vtparse.c`, `src/vtparse_table.h`, 18 | `src/vtparse_tablre.c` are in the public domain, see 19 | https://github.com/haberman/vtparse/ 20 | Pull request #1 is also included. 21 | -------------------------------------------------------------------------------- /man/cli_output_connection.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tty.R 3 | \name{cli_output_connection} 4 | \alias{cli_output_connection} 5 | \title{The connection option that cli would use} 6 | \usage{ 7 | cli_output_connection() 8 | } 9 | \value{ 10 | Connection object. 11 | } 12 | \description{ 13 | Note that this only refers to the current R process. If the output 14 | is produced in another process, then it is not relevant. 15 | } 16 | \details{ 17 | In interactive sessions the standard output is chosen, otherwise the 18 | standard error is used. This is to avoid painting output messages red 19 | in the R GUIs. 20 | } 21 | -------------------------------------------------------------------------------- /man/match_selector.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/themes.R 3 | \name{match_selector} 4 | \alias{match_selector} 5 | \title{Match a selector to a container stack} 6 | \usage{ 7 | match_selector(sels, cnts) 8 | } 9 | \arguments{ 10 | \item{sels}{A list of selector nodes.} 11 | 12 | \item{cnts}{A list of container nodes. 13 | 14 | The last selector in the list must match the last container, so we 15 | do the matching from the back. This is because we use this function 16 | to calculate the style of newly encountered containers.} 17 | } 18 | \description{ 19 | Match a selector to a container stack 20 | } 21 | \keyword{internal} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | /README.html 5 | /README.md.bak 6 | /revdep 7 | /r-packages 8 | /README_cache 9 | /README_files 10 | /docs/ 11 | inst/doc 12 | doc 13 | Meta 14 | /vignettes/*_cache 15 | /vignettes/*_files 16 | /man/_cache 17 | /man/chunks/pluralization_cache 18 | /src/*.o 19 | /src/cli.so 20 | /src/cli.dll 21 | /tests/testthat/progresstest/src/*.o 22 | /tests/testthat/progresstest/src/*.so 23 | /tests/testthat/progresstest/src/*.dll 24 | /tests/testthat/progresstestcpp/src/*.o 25 | /tests/testthat/progresstestcpp/src/*.so 26 | /tests/testthat/progresstestcpp/src/*.dll 27 | /dev-lib 28 | /src/*.gc* 29 | /cache 30 | /man/chunks/FAQ_cache 31 | /tests/testthat/results.rds 32 | -------------------------------------------------------------------------------- /man/ansi_regex.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_regex} 4 | \alias{ansi_regex} 5 | \title{Perl compatible regular expression that matches ANSI escape 6 | sequences} 7 | \usage{ 8 | ansi_regex() 9 | } 10 | \value{ 11 | String scalar, the regular expression. 12 | } 13 | \description{ 14 | Don't forget to use \code{perl = TRUE} when using this with \code{\link[=grepl]{grepl()}} and 15 | friends. 16 | } 17 | \seealso{ 18 | Other low level ANSI functions: 19 | \code{\link{ansi_has_any}()}, 20 | \code{\link{ansi_hide_cursor}()}, 21 | \code{\link{ansi_string}()}, 22 | \code{\link{ansi_strip}()} 23 | } 24 | \concept{low level ANSI functions} 25 | -------------------------------------------------------------------------------- /tests/testthat/test-text.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that("text is wrapped", { 5 | withr::local_options(cli.width = 60) 6 | withr::local_rng_version("3.3.0") 7 | set.seed(42) 8 | expect_snapshot(local({ 9 | cli_div(class = "testcli", theme = test_style()) 10 | cli_h1("Header") 11 | cli_text(lorem_ipsum()) 12 | })) 13 | }) 14 | 15 | test_that("verbatim text is not wrapped", { 16 | cli_div(class = "testcli", theme = test_style()) 17 | withr::local_options(cli.width = 60) 18 | suppressMessages(cli_h1("Header")) 19 | txt <- strrep("1234567890 ", 20) 20 | out <- capt0(cli_verbatim(txt), strip_style = TRUE) 21 | expect_equal(out, paste0(txt, "\n")) 22 | }) 23 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/spark.md: -------------------------------------------------------------------------------- 1 | # spark_bar [plain] 2 | 3 | Code 4 | spark_bar(seq(0, 1, length.out = 8)) 5 | Output 6 | __,,**## 7 | Code 8 | spark_bar(c(0, NA, 0.5, NA, 1)) 9 | Output 10 | _ , # 11 | 12 | # spark_bar [unicode] 13 | 14 | Code 15 | spark_bar(seq(0, 1, length.out = 8)) 16 | Output 17 | ▁▂▃▄▅▆▇█ 18 | Code 19 | spark_bar(c(0, NA, 0.5, NA, 1)) 20 | Output 21 | ▁ ▄ █ 22 | 23 | # spark_line [plain] 24 | 25 | Code 26 | spark_line(seq(0, 1, length.out = 10)) 27 | Output 28 | _,,-^ 29 | 30 | # spark_line [unicode] 31 | 32 | Code 33 | spark_line(seq(0, 1, length.out = 10)) 34 | Output 35 | ⣀⡠⠔⠊⠉ 36 | 37 | -------------------------------------------------------------------------------- /tests/testthat/test-headers.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that_cli("headers", { 5 | expect_snapshot( 6 | local({ 7 | cli_div(class = "testcli", theme = test_style()) 8 | cli_h1("HEADER") 9 | cli_h2("Header") 10 | cli_h3("Header") 11 | x <- "foobar" 12 | xx <- 100 13 | cli_h2("{xx}. header: {x}") 14 | }), 15 | variant = if (packageVersion("testthat") <= "3.1.4") "old" else "new" 16 | ) 17 | }) 18 | 19 | test_that("issue #218", { 20 | expect_snapshot( 21 | { 22 | cli_h1("one {1} two {2} three {3}") 23 | cli_h2("one {1} two {2} three {3}") 24 | }, 25 | variant = if (packageVersion("testthat") <= "3.1.4") "old" else "new" 26 | ) 27 | }) 28 | -------------------------------------------------------------------------------- /man/demo_spinners.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/spinner.R 3 | \name{demo_spinners} 4 | \alias{demo_spinners} 5 | \title{Show a demo of some (by default all) spinners} 6 | \usage{ 7 | demo_spinners(which = NULL) 8 | } 9 | \arguments{ 10 | \item{which}{Character vector, which spinners to demo.} 11 | } 12 | \description{ 13 | Each spinner is shown for about 2-3 seconds. 14 | } 15 | \details{ 16 | \if{html}{\out{
}}\preformatted{demo_spinners("clock") 17 | }\if{html}{\out{
}} 18 | 19 | \if{html}{\figure{demo-spinners.svg}} 20 | } 21 | \seealso{ 22 | Other spinners: 23 | \code{\link{get_spinner}()}, 24 | \code{\link{list_spinners}()}, 25 | \code{\link{make_spinner}()} 26 | } 27 | \concept{spinners} 28 | -------------------------------------------------------------------------------- /man/symbol.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/symbol.R 3 | \name{symbol} 4 | \alias{symbol} 5 | \alias{list_symbols} 6 | \title{Various handy symbols to use in a command line UI} 7 | \format{ 8 | A named list, see \code{names(symbol)} for all sign names. 9 | } 10 | \usage{ 11 | symbol 12 | 13 | list_symbols() 14 | } 15 | \description{ 16 | Various handy symbols to use in a command line UI 17 | } 18 | \details{ 19 | On Windows they have a fallback to less fancy symbols. 20 | 21 | \code{list_symbols()} prints a table with all symbols to the screen. 22 | } 23 | \examples{ 24 | cat(symbol$tick, " SUCCESS\n", symbol$cross, " FAILURE\n", sep = "") 25 | 26 | ## All symbols 27 | cat(paste(format(names(symbol), width = 20), 28 | unlist(symbol)), sep = "\n") 29 | } 30 | -------------------------------------------------------------------------------- /man/cli_fmt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cli.R 3 | \name{cli_fmt} 4 | \alias{cli_fmt} 5 | \title{Capture the output of cli functions instead of printing it} 6 | \usage{ 7 | cli_fmt(expr, collapse = FALSE, strip_newline = FALSE) 8 | } 9 | \arguments{ 10 | \item{expr}{Expression to evaluate, containing \verb{cli_*()} calls, 11 | typically.} 12 | 13 | \item{collapse}{Whether to collapse the output into a single character 14 | scalar, or return a character vector with one element for each line.} 15 | 16 | \item{strip_newline}{Whether to strip the trailing newline.} 17 | } 18 | \description{ 19 | Capture the output of cli functions instead of printing it 20 | } 21 | \examples{ 22 | cli_fmt({ 23 | cli_alert_info("Loading data file") 24 | cli_alert_success("Loaded data file") 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /man/ansi_string.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_string} 4 | \alias{ansi_string} 5 | \title{Labels a character vector as containing ANSI control codes.} 6 | \usage{ 7 | ansi_string(x) 8 | } 9 | \arguments{ 10 | \item{x}{A character vector or something that can be 11 | coerced into one.} 12 | } 13 | \value{ 14 | A \code{cli_ansi_string} object, a subclass of 15 | \code{character}, with the same length and contents 16 | as \code{x}. 17 | } 18 | \description{ 19 | This function sets the class of its argument, activating 20 | ANSI-string-specific methods such as for printing. 21 | } 22 | \seealso{ 23 | Other low level ANSI functions: 24 | \code{\link{ansi_has_any}()}, 25 | \code{\link{ansi_hide_cursor}()}, 26 | \code{\link{ansi_regex}()}, 27 | \code{\link{ansi_strip}()} 28 | } 29 | \concept{low level ANSI functions} 30 | -------------------------------------------------------------------------------- /tests/testthat/progresstest/R/test.R: -------------------------------------------------------------------------------- 1 | #' @useDynLib progresstest, .registration = TRUE, .fixes = "c_" 2 | NULL 3 | 4 | #' @export 5 | 6 | test_baseline <- function() { 7 | .Call(c_test0) 8 | } 9 | 10 | #' @export 11 | 12 | test_modulo <- function(progress = FALSE) { 13 | .Call(c_test00, progress) 14 | } 15 | 16 | #' @export 17 | 18 | test_cli <- function() { 19 | .Call(c_test1) 20 | } 21 | 22 | #' @export 23 | 24 | test_cli_unroll <- function() { 25 | this <- "test2" 26 | .Call(c_test2) 27 | } 28 | 29 | #' @export 30 | 31 | testx <- function() { 32 | this <- "testx" 33 | .Call(c_testx) 34 | } 35 | 36 | test3 <- function() { 37 | this <- "test3" 38 | test2() 39 | } 40 | 41 | call_with_cleanup <- function(ptr, ...) { 42 | .Call(c_cleancall_call, pairlist(ptr, ...), parent.frame()) 43 | } 44 | 45 | testc <- function() { 46 | this <- "testc" 47 | call_with_cleanup(c_testc) 48 | } 49 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^Makefile$ 4 | ^README.Rmd$ 5 | ^README.md.bak$ 6 | ^README.html$ 7 | ^revdep$ 8 | ^\.Rprofile$ 9 | ^r-packages$ 10 | ^.*_cache$ 11 | ^.*_files$ 12 | ^_pkgdown\.yml$ 13 | ^docs$ 14 | ^pkgdown$ 15 | ^doc$ 16 | ^Meta$ 17 | ^vignettes/rules-boxes-trees.*$ 18 | ^vignettes/semantic-cli.*$ 19 | ^vignettes/usethis-ui.*$ 20 | ^vignettes$ 21 | ^\.github$ 22 | ^LICENSE\.md$ 23 | ^src/.*\.o$ 24 | ^src/cli\.so$ 25 | ^src/cli\.dll$ 26 | ^tests/testthat/progresstest/src/.*\.so$ 27 | ^tests/testthat/progresstest/src/.*\.dll$ 28 | ^tests/testthat/progresstest/src/.*\.o$ 29 | ^tests/testthat/progresstestcpp/src/.*\.so$ 30 | ^tests/testthat/progresstestcpp/src/.*\.dll$ 31 | ^tests/testthat/progresstestcpp/src/.*\.o$ 32 | ^dev-lib$ 33 | ^src/.*[.]gc.*$ 34 | ^cache$ 35 | ^man/chunks/FAQ_cache$ 36 | ^codecov\.yml$ 37 | ^tests/testthat/results.rds$ 38 | ^[\.]?air\.toml$ 39 | ^\.vscode$ 40 | -------------------------------------------------------------------------------- /tests/testthat/test-progress-handler-say.R: -------------------------------------------------------------------------------- 1 | test_that("say_out", { 2 | px <- asNamespace("processx")$get_tool("px") 3 | tmp <- tempfile("cli-test-") 4 | on.exit(unlink(tmp), add = TRUE) 5 | withr::local_options( 6 | cli.progress_say_command = px, 7 | cli.progress_say_args = c("writefile", tmp) 8 | ) 9 | p <- say_out(" 70%") 10 | expect_s3_class(p, "process") 11 | p$wait(3000) 12 | expect_false(p$is_alive()) 13 | p$kill() 14 | expect_equal(readLines(tmp, warn = FALSE), " 70%") 15 | }) 16 | 17 | test_that("say_update", { 18 | withr::local_options(cli.progress_say_frequency = 1e-9) 19 | local_mocked_bindings(say_out = function(text) text) 20 | bar <- new.env(parent = emptyenv()) 21 | bar$current <- 10 22 | bar$total <- NA 23 | say_update(bar) 24 | expect_equal(bar$say_proc, 10) 25 | 26 | bar$total <- 50 27 | say_update(bar) 28 | expect_equal(bar$say_proc, " 20%") 29 | }) 30 | -------------------------------------------------------------------------------- /man/cli_list_themes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/themes.R 3 | \name{cli_list_themes} 4 | \alias{cli_list_themes} 5 | \title{List the currently active themes} 6 | \usage{ 7 | cli_list_themes() 8 | } 9 | \value{ 10 | A list of data frames with the active themes. 11 | Each data frame row is a style that applies to selected CLI tree nodes. 12 | Each data frame has columns: 13 | \itemize{ 14 | \item \code{selector}: The original CSS-like selector string. See \link{themes}. 15 | \item \code{parsed}: The parsed selector, as used by cli for matching to nodes. 16 | \item \code{style}: The original style. 17 | \item \code{cnt}: The id of the container the style is currently applied to, or 18 | \code{NA} if the style is not used. 19 | } 20 | } 21 | \description{ 22 | If there is no active app, then it calls \code{\link[=start_app]{start_app()}}. 23 | } 24 | \seealso{ 25 | \link{themes} 26 | } 27 | -------------------------------------------------------------------------------- /src/win-utf8.c: -------------------------------------------------------------------------------- 1 | 2 | #include "cli.h" 3 | #include "errors.h" 4 | #include "cleancall.h" 5 | 6 | #include 7 | 8 | #ifdef WIN32 9 | extern Rboolean EmitEmbeddedUTF8; 10 | #endif 11 | 12 | SEXP clic_get_embedded_utf8(void) { 13 | #ifdef WIN32 14 | #if R_VERSION < R_Version(4, 0, 0) 15 | error("get_embedded_utf8() needs at least R 4.0.0"); 16 | #else 17 | return ScalarLogical(EmitEmbeddedUTF8); 18 | #endif 19 | #else 20 | error("get_embedded_utf8() only works on Windows"); 21 | return R_NilValue; 22 | #endif 23 | } 24 | 25 | SEXP clic_set_embedded_utf8(SEXP value) { 26 | #ifdef WIN32 27 | #if R_VERSION < R_Version(4, 0, 0) 28 | error("set_embedded_utf8() needs at least R 4.0.0"); 29 | #else 30 | Rboolean prev = EmitEmbeddedUTF8; 31 | EmitEmbeddedUTF8 = LOGICAL(value)[0]; 32 | return ScalarLogical(prev); 33 | #endif 34 | #else 35 | error("set_embedded_utf8() only works on Windows"); 36 | return R_NilValue; 37 | #endif 38 | } 39 | -------------------------------------------------------------------------------- /tests/testthat/test-alerts.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that("generic", { 5 | expect_snapshot(local({ 6 | cli_div(theme = list(".alert" = list(before = "GENERIC! "))) 7 | cli_alert("wow") 8 | })) 9 | }) 10 | 11 | test_that_cli("success", { 12 | expect_snapshot(local({ 13 | cli_alert_success("wow") 14 | })) 15 | }) 16 | 17 | test_that_cli("danger", { 18 | expect_snapshot(local({ 19 | cli_alert_danger("wow") 20 | })) 21 | }) 22 | 23 | test_that_cli("warning", { 24 | expect_snapshot(local({ 25 | cli_alert_warning("wow") 26 | })) 27 | }) 28 | 29 | test_that_cli("info", { 30 | expect_snapshot(local({ 31 | cli_alert_info("wow") 32 | })) 33 | }) 34 | 35 | test_that("before and after can have spaces", { 36 | expect_snapshot(local({ 37 | cli_div(theme = list(.alert = list(before = "x ", after = " x"))) 38 | cli_alert("continuing that first alert", wrap = TRUE) 39 | })) 40 | }) 41 | -------------------------------------------------------------------------------- /R/assertions.R: -------------------------------------------------------------------------------- 1 | is_string <- function(x) { 2 | is.character(x) && length(x) == 1 && !is.na(x) 3 | } 4 | 5 | is_flag <- function(x) { 6 | is.logical(x) && length(x) == 1 && !is.na(x) 7 | } 8 | 9 | is_border_style <- function(x) { 10 | is_string(x) && x %in% rownames(box_styles()) 11 | } 12 | 13 | is_padding_or_margin <- function(x) { 14 | is.numeric(x) && 15 | length(x) %in% c(1, 4) && 16 | !anyNA(x) && 17 | all(as.integer(x) == x) 18 | } 19 | 20 | is_col <- function(x) { 21 | is.null(x) || is_string(x) || is.function(x) 22 | } 23 | 24 | is_count <- function(x) { 25 | is.numeric(x) && length(x) == 1 && !is.na(x) && as.integer(x) == x && x >= 0 26 | } 27 | 28 | is_tree_style <- function(x) { 29 | is.list(x) && 30 | length(x) == 4 && 31 | !is.null(names(x)) && 32 | all(sort(names(x)) == sort(c("h", "v", "l", "j"))) && 33 | all(sapply(x, is_string)) 34 | } 35 | 36 | is_named <- function(x) { 37 | !is.null(names(x)) 38 | } 39 | -------------------------------------------------------------------------------- /R/cli-errors.R: -------------------------------------------------------------------------------- 1 | cli_error <- function( 2 | ..., 3 | .data = NULL, 4 | .class = NULL, 5 | .envir = parent.frame(), 6 | call. = TRUE 7 | ) { 8 | .hide_from_trace <- TRUE 9 | cnd <- new_error( 10 | call. = call., 11 | format_error( 12 | .envir = .envir, 13 | c( 14 | ... 15 | ) 16 | ) 17 | ) 18 | 19 | if (length(.data)) cnd[names(.data)] <- .data 20 | if (length(class)) class(cnd) <- c(.class, class(cnd)) 21 | 22 | cnd 23 | } 24 | 25 | stop_if_not <- function( 26 | message, 27 | ..., 28 | .envir = parent.frame(), 29 | call. = sys.call(-1) 30 | ) { 31 | conds <- list(...) 32 | for (cond in conds) { 33 | if (!cond) { 34 | throw( 35 | new_error(format_error(.envir = .envir, message), call. = call.), 36 | frame = .envir 37 | ) 38 | } 39 | } 40 | } 41 | 42 | `%??%` <- function(expr, err) { 43 | chain_error(expr, err, srcref = utils::getSrcref(sys.call())) 44 | } 45 | -------------------------------------------------------------------------------- /man/utf8_graphemes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utf8.R 3 | \name{utf8_graphemes} 4 | \alias{utf8_graphemes} 5 | \title{Break an UTF-8 character vector into grapheme clusters} 6 | \usage{ 7 | utf8_graphemes(x) 8 | } 9 | \arguments{ 10 | \item{x}{Character vector.} 11 | } 12 | \value{ 13 | List of characters vectors, the grapheme clusters of the input 14 | string. 15 | } 16 | \description{ 17 | Break an UTF-8 character vector into grapheme clusters 18 | } 19 | \examples{ 20 | # Five grapheme clusters 21 | str <- paste0( 22 | "\U0001f477\U0001f3ff\u200d\u2640\ufe0f", 23 | "\U0001f477\U0001f3ff", 24 | "\U0001f477\u200d\u2640\ufe0f", 25 | "\U0001f477\U0001f3fb", 26 | "\U0001f477\U0001f3ff") 27 | cat(str, "\n") 28 | chrs <- utf8_graphemes(str) 29 | } 30 | \seealso{ 31 | Other UTF-8 string manipulation: 32 | \code{\link{utf8_nchar}()}, 33 | \code{\link{utf8_substr}()} 34 | } 35 | \concept{UTF-8 string manipulation} 36 | -------------------------------------------------------------------------------- /man/roxygen/meta.R: -------------------------------------------------------------------------------- 1 | if (exists(".knitr_asciicast_process", envir = .GlobalEnv)) { 2 | rm(list = ".knitr_asciicast_process", envir = .GlobalEnv) 3 | } 4 | 5 | asciicast::init_knitr_engine( 6 | echo = TRUE, 7 | echo_input = FALSE, 8 | interactive = TRUE, 9 | timeout = as.integer(Sys.getenv("ASCIICAST_TIMEOUT", 10)), 10 | startup = quote({ 11 | options(cli.width = 70) 12 | options(cli.progress_show_after = 0) 13 | options(cli.progress_clear = FALSE) 14 | library(cli) 15 | set.seed(1) 16 | }) 17 | ) 18 | 19 | knitr::opts_chunk$set( 20 | asciicast_knitr_output = "html", 21 | asciicast_include_style = FALSE, 22 | asciicast_theme = "pkgdown" 23 | ) 24 | 25 | list( 26 | markdown = TRUE, 27 | knitr_chunk_options = list( 28 | cache = TRUE, 29 | cache_lazy = FALSE, 30 | cache.path = file.path(getwd(), "man/_cache/"), 31 | fig.path = file.path(getwd(), "man/figures"), 32 | error = TRUE 33 | ), 34 | restrict_image_formats = TRUE 35 | ) 36 | -------------------------------------------------------------------------------- /tools/ansi-iterm-palettes.txt: -------------------------------------------------------------------------------- 1 | black red green yellow blue magenta cyan white br_black br_red br_green br_yellow br_blue br_magenta br_cyan br_white 2 | iterm #000000 #c91b00 #00c200 #c7c400 #0225c7 #ca30c7 #00c5c7 #c7c7c7 #686868 #ff6e67 #5ffa68 #fffc67 #6871ff #ff77ff #60fdff #ffffff 3 | iterm-pastel #626262 #ff8373 #b4fb73 #fffdc3 #a5d5fe #ff90fe #d1d1fe #f1f1f1 #8f8f8f #ffc4be #d6fcba #fffed5 #c2e3ff #ffb2fe #e6e7fe #ffffff 4 | iterm-smoooooth #14191e #b43c2a #00c200 #c7c400 #2744c7 #c040be #00c5c7 #c7c7c7 #686868 #dd7975 #58e790 #ece100 #a7abf2 #e17ee1 #60fdff #ffffff 5 | iterm-snazzy #000000 #ff5c57 #5af78e #f3f99d #57c7ff #ff6ac1 #9aedfe #f1f1f0 #686868 #ff5c57 #5af78e #f3f99d #57c7ff #ff6ac1 #9aedfe #f1f1f0 6 | iterm-solarized #073642 #dc322f #859900 #b58900 #268bd2 #d33682 #2aa198 #eee8d5 #002b36 #cb4b16 #586e75 #657b83 #839496 #6c71c4 #93a1a1 #fdf6e3 7 | iterm-tango #000000 #d81e00 #5ea702 #cfae00 #427ab3 #89658e #00a7aa #dbded8 #686a66 #f54235 #99e343 #fdeb61 #84b0d8 #bc94b7 #37e6e8 #f1f1f0 8 | -------------------------------------------------------------------------------- /tools/unicode.R: -------------------------------------------------------------------------------- 1 | # To create the data: 2 | 3 | update_wide_unicode_data <- function() { 4 | tab <- read.delim( 5 | stringsAsFactors = FALSE, 6 | "https://unicode.org/Public/UNIDATA/EastAsianWidth.txt", 7 | comment.char = "#", 8 | sep = ";", 9 | strip.white = TRUE, 10 | header = FALSE 11 | ) 12 | 13 | # Keep wide ones 14 | wide <- tab$V1[tab$V2 == "W"] 15 | first <- sub("\\.\\..*$", "", wide) 16 | range <- sub("^([0-9A-F]+)\\.\\.([0-9A-F]+)$", "\\\\U\\1-\\\\U\\2", wide) 17 | range <- sub("^([0-9A-F]+)$", "\\\\U\\1", range) 18 | 19 | wide_chars <- data.frame( 20 | stringsAsFactors = FALSE, 21 | test = sapply(parse(text = paste0('"', "\\U", first, '"')), eval), 22 | regex = sapply(parse(text = paste0('"', range, '"')), eval) 23 | ) 24 | 25 | env <- new.env(parent = emptyenv()) 26 | load("R/sysdata.rda", envir = env) 27 | 28 | env$wide_chars <- wide_chars 29 | save(list = ls(env), file = "R/sysdata.rda", envir = env, version = 2) 30 | } 31 | -------------------------------------------------------------------------------- /tests/testthat/test-package.R: -------------------------------------------------------------------------------- 1 | test_that("No leftover SVG figures", { 2 | skip_on_cran() 3 | skip_on_covr() 4 | pkg_dir <- test_package_root() 5 | figs <- dir(file.path(pkg_dir, "man", "figures"), pattern = "[.]svg$") 6 | rd <- dir(file.path(pkg_dir, "man"), pattern = "[.]Rd$", full.names = TRUE) 7 | rd_figs <- unlist(lapply(rd, function(x) { 8 | grep("\\\\figure\\{", readLines(x), value = TRUE) 9 | })) 10 | rd_figs <- sub("^.*\\\\figure\\{(.*[.]svg).*$", "\\1", rd_figs) 11 | 12 | expect_equal( 13 | sort(figs), 14 | sort(unique(rd_figs)) 15 | ) 16 | 17 | figs2 <- dir( 18 | file.path(pkg_dir, "man", "figures", "README"), 19 | pattern = "[.]svg$" 20 | ) 21 | readme <- file.path(pkg_dir, "README.md") 22 | readme_figs <- grep("man/figures/", readLines(readme), value = TRUE) 23 | readme_figs <- sub("^.*man/figures/README/(.*[.]svg).*$", "\\1", readme_figs) 24 | 25 | expect_equal( 26 | sort(figs2), 27 | sort(unique(readme_figs)) 28 | ) 29 | }) 30 | -------------------------------------------------------------------------------- /man/has_keypress_support.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/keypress.R 3 | \name{has_keypress_support} 4 | \alias{has_keypress_support} 5 | \title{Check if the current platform/terminal supports reading 6 | single keys.} 7 | \usage{ 8 | has_keypress_support() 9 | } 10 | \value{ 11 | Whether there is support for waiting for individual 12 | keypressses. 13 | } 14 | \description{ 15 | Check if the current platform/terminal supports reading 16 | single keys. 17 | } 18 | \details{ 19 | Supported platforms: 20 | \itemize{ 21 | \item Terminals in Windows and Unix. 22 | \item RStudio terminal. 23 | } 24 | 25 | Not supported: 26 | \itemize{ 27 | \item RStudio (if not in the RStudio terminal). 28 | \item R.app on macOS. 29 | \item Rgui on Windows. 30 | \item Emacs ESS. 31 | \item Others. 32 | } 33 | } 34 | \examples{ 35 | has_keypress_support() 36 | } 37 | \seealso{ 38 | Other keypress function: 39 | \code{\link{keypress}()} 40 | } 41 | \concept{keypress function} 42 | -------------------------------------------------------------------------------- /man/parse_selector.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/themes.R 3 | \name{parse_selector} 4 | \alias{parse_selector} 5 | \title{Parse a CSS3-like selector} 6 | \usage{ 7 | parse_selector(x) 8 | } 9 | \arguments{ 10 | \item{x}{CSS3-like selector string.} 11 | } 12 | \description{ 13 | This is the rather small subset of CSS3 that is supported: 14 | } 15 | \details{ 16 | Selectors: 17 | \itemize{ 18 | \item Type selectors, e.g. \code{input} selects all \verb{} elements. 19 | \item Class selectors, e.g. \code{.index} selects any element that has a class 20 | of "index". 21 | \item ID selector. \verb{#toc} will match the element that has the ID \code{"toc"}. 22 | } 23 | 24 | Combinators: 25 | \itemize{ 26 | \item Descendant combinator, i.e. the space, that combinator selects nodes 27 | that are descendants of the first element. E.g. \verb{div span} will match 28 | all \verb{} elements that are inside a \verb{
} element. 29 | } 30 | } 31 | \keyword{internal} 32 | -------------------------------------------------------------------------------- /man/pluralization-helpers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/pluralize.R 3 | \name{no} 4 | \alias{no} 5 | \alias{qty} 6 | \title{Pluralization helper functions} 7 | \usage{ 8 | no(expr) 9 | 10 | qty(expr) 11 | } 12 | \arguments{ 13 | \item{expr}{For \code{no()} it is an expression that is printed as "no" in 14 | cli expressions, it is interpreted as a zero quantity. For \code{qty()} 15 | an expression that sets the pluralization quantity without printing 16 | anything. See examples below.} 17 | } 18 | \description{ 19 | Pluralization helper functions 20 | } 21 | \examples{ 22 | nfile <- 0; cli_text("Found {no(nfile)} file{?s}.") 23 | 24 | #> Found no files. 25 | 26 | nfile <- 1; cli_text("Found {no(nfile)} file{?s}.") 27 | 28 | #> Found 1 file. 29 | 30 | nfile <- 2; cli_text("Found {no(nfile)} file{?s}.") 31 | 32 | #> Found 2 files. 33 | 34 | } 35 | \seealso{ 36 | Other pluralization: 37 | \code{\link{pluralization}}, 38 | \code{\link{pluralize}()} 39 | } 40 | \concept{pluralization} 41 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/text.md: -------------------------------------------------------------------------------- 1 | # text is wrapped 2 | 3 | Code 4 | local({ 5 | cli_div(class = "testcli", theme = test_style()) 6 | cli_h1("Header") 7 | cli_text(lorem_ipsum()) 8 | }) 9 | Message 10 | 11 | Header 12 | 13 | Duis quis magna incididunt nulla commodo minim non 14 | exercitation nostrud ullamco dolor exercitation ut veniam. 15 | Fugiat irure tempor commodo voluptate ut. In et tempor 16 | excepteur quis. Qui et nisi ad quis ad cupidatat tempor 17 | laborum est excepteur aliqua veniam. Velit sunt magna 18 | veniam Lorem elit enim et pariatur. Occaecat mollit 19 | consequat dolore in. Veniam officia labore reprehenderit 20 | culpa dolore quis nisi do aliqua commodo deserunt. 21 | Cupidatat nostrud ad est in ad laboris consectetur esse 22 | minim. Irure do anim anim ea mollit ad cupidatat ullamco 23 | ullamco nulla elit in. Lorem Lorem deserunt deserunt et ut 24 | velit nulla nulla ipsum ad laborum quis. 25 | 26 | -------------------------------------------------------------------------------- /man/spark_line.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/spark.R 3 | \name{spark_line} 4 | \alias{spark_line} 5 | \title{Draw a sparkline line graph with Braille characters.} 6 | \usage{ 7 | spark_line(x) 8 | } 9 | \arguments{ 10 | \item{x}{A numeric vector between 0 and 1} 11 | } 12 | \description{ 13 | You might want to avoid sparklines on non-UTF-8 systems, because they 14 | do not look good. You can use \code{\link[=is_utf8_output]{is_utf8_output()}} to test for support 15 | for them. 16 | } 17 | \details{ 18 | \if{html}{\out{
}}\preformatted{x <- seq(0, 1, length = 10) 19 | spark_line(x) 20 | }\if{html}{\out{
}}\if{html}{\out{ 21 |
22 | #> ⣀⡠⠔⠊⠉                                                                           
23 | 
24 | }} 25 | } 26 | \seealso{ 27 | \code{\link[=spark_bar]{spark_bar()}} 28 | } 29 | -------------------------------------------------------------------------------- /tests/testthat/progresstestcpp/src/testcpp.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | [[cpp11::register]] 9 | int test_baseline_() { 10 | int res = 0; 11 | for (int i = 0; i < 2000000000; i++) { 12 | res += i % 2; 13 | } 14 | return res; 15 | } 16 | 17 | [[cpp11::register]] 18 | int test_cli_() { 19 | int res = 0; 20 | cpp11::sexp bar = cli_progress_bar(2000000000, NULL); 21 | for (int i = 0; i < 2000000000; i++) { 22 | if (CLI_SHOULD_TICK) cli_progress_set(bar, i); 23 | res += i % 2; 24 | } 25 | return res; 26 | } 27 | 28 | [[cpp11::register]] 29 | int test_template_() { 30 | int res = 0; 31 | cpp11::sexp bar = cli_progress_bar(2000000000, NULL); 32 | cli_progress_set_format( 33 | bar, 34 | "{%d} package{?s} {cli::pb_bar} | {cli::pb_elapsed}", 35 | 4 36 | ); 37 | for (int i = 0; i < 2000000000; i++) { 38 | if (CLI_SHOULD_TICK) cli_progress_set(bar, i); 39 | res += i % 2; 40 | } 41 | return res; 42 | } 43 | -------------------------------------------------------------------------------- /tests/testthat/test-bullets.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that_cli("bullets", { 5 | expect_snapshot(cli_bullets(c( 6 | "noindent", 7 | " " = "space", 8 | "v" = "success", 9 | "x" = "danger", 10 | "!" = "warning", 11 | "i" = "info", 12 | "*" = "bullet", 13 | ">" = "arrow" 14 | ))) 15 | }) 16 | 17 | test_that_cli("bullets glue", { 18 | expect_snapshot(cli_bullets(c( 19 | "noindent {.key {1:3}}", 20 | " " = "space {.key {1:3}}", 21 | "v" = "success {.key {1:3}}", 22 | "x" = "danger {.key {1:3}}", 23 | "!" = "warning {.key {1:3}}", 24 | "i" = "info {.key {1:3}}", 25 | "*" = "bullet {.key {1:3}}", 26 | ">" = "arrow {.key {1:3}}" 27 | ))) 28 | }) 29 | 30 | test_that_cli("bullets wrapping", { 31 | txt <- strrep("This is some text that is longer than the width. ", 3) 32 | expect_snapshot(cli_bullets(c( 33 | txt, 34 | " " = txt, 35 | "v" = txt, 36 | "x" = txt, 37 | "!" = txt, 38 | "i" = txt, 39 | "*" = txt, 40 | ">" = txt 41 | ))) 42 | }) 43 | -------------------------------------------------------------------------------- /R/rematch2.R: -------------------------------------------------------------------------------- 1 | re_match <- function(text, pattern, perl = TRUE, ...) { 2 | stopifnot(is.character(pattern), length(pattern) == 1, !is.na(pattern)) 3 | text <- as.character(text) 4 | 5 | match <- regexpr(pattern, text, perl = perl, ...) 6 | 7 | start <- as.vector(match) 8 | length <- attr(match, "match.length") 9 | end <- start + length - 1L 10 | 11 | matchstr <- substring(text, start, end) 12 | matchstr[start == -1] <- NA_character_ 13 | 14 | empty <- data.frame(stringsAsFactors = FALSE, .text = text)[, numeric()] 15 | res <- list(match = !is.na(matchstr), groups = empty) 16 | 17 | if (!is.null(attr(match, "capture.start"))) { 18 | gstart <- attr(match, "capture.start") 19 | glength <- attr(match, "capture.length") 20 | gend <- gstart + glength - 1L 21 | 22 | groupstr <- substring(text, gstart, gend) 23 | groupstr[gstart == -1] <- NA_character_ 24 | dim(groupstr) <- dim(gstart) 25 | 26 | res$groups <- cbind(groupstr, res$groups, stringsAsFactors = FALSE) 27 | names(res$groups) <- attr(match, "capture.names") 28 | } 29 | 30 | res$groups 31 | } 32 | -------------------------------------------------------------------------------- /man/match_selector_node.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/themes.R 3 | \name{match_selector_node} 4 | \alias{match_selector_node} 5 | \title{Match a selector node to a container} 6 | \usage{ 7 | match_selector_node(node, cnt) 8 | } 9 | \arguments{ 10 | \item{node}{Selector node, as parsed by \code{parse_selector_node()}.} 11 | 12 | \item{cnt}{Container node, has elements \code{tag}, \code{id}, \code{class}. 13 | 14 | The selector node matches the container, if all these hold: 15 | \itemize{ 16 | \item The id of the selector is missing or unique. 17 | \item The tag of the selector is missing or unique. 18 | \item The id of the container is missing or unique. 19 | \item The tag of the container is unique. 20 | \item If the selector specifies an id, it matches the id of the container. 21 | \item If the selector specifies a tag, it matches the tag of the container. 22 | \item If the selector specifies class names, the container has all these 23 | classes. 24 | }} 25 | } 26 | \description{ 27 | Match a selector node to a container 28 | } 29 | \keyword{internal} 30 | -------------------------------------------------------------------------------- /man/cli_sitrep.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sitrep.R 3 | \name{cli_sitrep} 4 | \alias{cli_sitrep} 5 | \title{cli situation report} 6 | \usage{ 7 | cli_sitrep() 8 | } 9 | \value{ 10 | Named list with entries listed above. It has a \code{cli_sitrep} 11 | class, with a \code{print()} and \code{format()} method. 12 | } 13 | \description{ 14 | Contains currently: 15 | \itemize{ 16 | \item \code{cli_unicode_option}: whether the \code{cli.unicode} option is set and its 17 | value. See \code{\link[=is_utf8_output]{is_utf8_output()}}. 18 | \item \code{symbol_charset}: the selected character set for \link{symbol}, UTF-8, 19 | Windows, or ASCII. 20 | \item \code{console_utf8}: whether the console supports UTF-8. See 21 | \code{\link[base:l10n_info]{base::l10n_info()}}. 22 | \item \code{latex_active}: whether we are inside knitr, creating a LaTeX 23 | document. 24 | \item \code{num_colors}: number of ANSI colors. See \code{\link[=num_ansi_colors]{num_ansi_colors()}}. 25 | \item \code{console_with}: detected console width. 26 | } 27 | } 28 | \examples{ 29 | cli_sitrep() 30 | } 31 | -------------------------------------------------------------------------------- /tools/spinners.R: -------------------------------------------------------------------------------- 1 | json <- "https://raw.githubusercontent.com/sindresorhus/cli-spinners/45cef9dff64ac5e36b46a194c68bccba448899ac/spinners.json" 2 | parsed <- jsonlite::fromJSON(json, simplifyVector = TRUE) 3 | pasis <- lapply(parsed, function(x) { 4 | x$frames <- I(x$frames) 5 | x 6 | }) 7 | pdt <- as.data.frame(do.call(rbind, pasis)) 8 | pdt$name <- rownames(pdt) 9 | rownames(pdt) <- NULL 10 | spinners <- pdt[, c("name", "interval", "frames")] 11 | usethis::use_data(spinners, internal = TRUE) 12 | spinners <- rbind( 13 | spinners, 14 | list( 15 | name = "growVeriticalDotsLR", 16 | interval = 80, 17 | frames = strsplit("⠀⡀⣀⣄⣤⣦⣶⣷⣿⣾⣶⣴⣤⣠⣀⢀", "", fixed = TRUE) 18 | ), 19 | list( 20 | name = "growVeriticalDotsRL", 21 | interval = 80, 22 | frames = strsplit("⠀⢀⣀⣠⣤⣴⣶⣾⣿⣷⣶⣦⣤⣄⣀⡀", "", fixed = TRUE) 23 | ), 24 | list( 25 | name = "growVeriticalDotsLL", 26 | interval = 80, 27 | frames = strsplit("⠀⡀⣀⣄⣤⣦⣶⣷⣿⣷⣶⣦⣤⣄⣀⡀", "", fixed = TRUE) 28 | ), 29 | list( 30 | name = "growVeriticalDotsRR", 31 | interval = 80, 32 | frames = strsplit("⠀⡀⣀⣠⣤⣴⣶⣾⣿⣾⣶⣴⣤⣠⣀⢀", "", fixed = TRUE) 33 | ) 34 | ) 35 | -------------------------------------------------------------------------------- /man/ansi_has_any.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_has_any} 4 | \alias{ansi_has_any} 5 | \title{Check if a string has some ANSI styling} 6 | \usage{ 7 | ansi_has_any(string, sgr = TRUE, csi = TRUE, link = TRUE) 8 | } 9 | \arguments{ 10 | \item{string}{The string to check. It can also be a character 11 | vector.} 12 | 13 | \item{sgr}{Whether to look for SGR (styling) control sequences.} 14 | 15 | \item{csi}{Whether to look for non-SGR control sequences.} 16 | 17 | \item{link}{Whether to look for ANSI hyperlinks.} 18 | } 19 | \value{ 20 | Logical vector, \code{TRUE} for the strings that have some 21 | ANSI styling. 22 | } 23 | \description{ 24 | Check if a string has some ANSI styling 25 | } 26 | \examples{ 27 | ## The second one has style if ANSI colors are supported 28 | ansi_has_any("foobar") 29 | ansi_has_any(col_red("foobar")) 30 | } 31 | \seealso{ 32 | Other low level ANSI functions: 33 | \code{\link{ansi_hide_cursor}()}, 34 | \code{\link{ansi_regex}()}, 35 | \code{\link{ansi_string}()}, 36 | \code{\link{ansi_strip}()} 37 | } 38 | \concept{low level ANSI functions} 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2025 cli authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /man/ansi_strip.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_strip} 4 | \alias{ansi_strip} 5 | \title{Remove ANSI escape sequences from a string} 6 | \usage{ 7 | ansi_strip(string, sgr = TRUE, csi = TRUE, link = TRUE) 8 | } 9 | \arguments{ 10 | \item{string}{The input string.} 11 | 12 | \item{sgr}{Whether to remove for SGR (styling) control sequences.} 13 | 14 | \item{csi}{Whether to remove for non-SGR control sequences.} 15 | 16 | \item{link}{Whether to remove ANSI hyperlinks.} 17 | } 18 | \value{ 19 | The cleaned up string. Note that \code{ansi_strip()} always drops 20 | the \code{cli_ansi_string} class, even if \code{sgr} and sci\code{are}FALSE`. 21 | } 22 | \description{ 23 | The input may be of class \code{cli_ansi_string} class, this is also dropped 24 | from the result. 25 | } 26 | \examples{ 27 | ansi_strip(col_red("foobar")) == "foobar" 28 | } 29 | \seealso{ 30 | Other low level ANSI functions: 31 | \code{\link{ansi_has_any}()}, 32 | \code{\link{ansi_hide_cursor}()}, 33 | \code{\link{ansi_regex}()}, 34 | \code{\link{ansi_string}()} 35 | } 36 | \concept{low level ANSI functions} 37 | -------------------------------------------------------------------------------- /man/figures/README/h3-dark.svg: -------------------------------------------------------------------------------- 1 | ──Heading3 -------------------------------------------------------------------------------- /man/figures/README/h3.svg: -------------------------------------------------------------------------------- 1 | ──Heading3 -------------------------------------------------------------------------------- /tests/testthat/_snaps/ansi-make.md: -------------------------------------------------------------------------------- 1 | # errors 2 | 3 | Code 4 | make_ansi_style(1:10) 5 | Condition 6 | Error: 7 | ! `style` must be an ANSI style 8 | i an ANSI style is a character scalar (cli style name, RGB or R color name), or a [3x1] or [4x1] numeric RGB matrix 9 | i `style` is an integer vector 10 | 11 | # make_ansi_style 12 | 13 | Code 14 | make_ansi_style(1:10) 15 | Condition 16 | Error: 17 | ! `style` must be an ANSI style 18 | i an ANSI style is a character scalar (cli style name, RGB or R color name), or a [3x1] or [4x1] numeric RGB matrix 19 | i `style` is an integer vector 20 | 21 | --- 22 | 23 | Code 24 | make_ansi_style("foobar") 25 | Condition 26 | Error: 27 | ! Unknown style specification: "style", it must be one of 28 | * a builtin cli style, e.g. "bold" or "red", 29 | * an R color name, see `?grDevices::colors()`. 30 | * a [3x1] or [4x1] numeric RGB matrix with, range 0-255. 31 | 32 | -------------------------------------------------------------------------------- /tests/testthat/test-ansi-combine.R: -------------------------------------------------------------------------------- 1 | test_that("one style", { 2 | testthat::skip_on_covr() # because we are comparing functions 3 | expect_equal( 4 | combine_ansi_styles(col_red), 5 | col_red, 6 | ignore_function_env = TRUE 7 | ) 8 | expect_equal( 9 | combine_ansi_styles(style_bold), 10 | style_bold, 11 | ignore_function_env = TRUE 12 | ) 13 | }) 14 | 15 | test_that_cli(configs = c("plain", "ansi"), "style objects", { 16 | expect_equal( 17 | combine_ansi_styles(col_red, style_bold)("blah"), 18 | col_red(style_bold("blah")) 19 | ) 20 | expect_equal( 21 | combine_ansi_styles(col_red, style_bold, style_underline)("foo"), 22 | col_red(style_bold(style_underline("foo"))) 23 | ) 24 | }) 25 | 26 | test_that_cli(configs = c("plain", "ansi"), "create styles on the fly", { 27 | expect_equal( 28 | combine_ansi_styles("darkolivegreen", style_bold)("blah"), 29 | make_ansi_style("darkolivegreen")((style_bold("blah"))) 30 | ) 31 | expect_equal( 32 | combine_ansi_styles(style_bold, "darkolivegreen", style_underline)("foo"), 33 | style_bold(make_ansi_style("darkolivegreen")(style_underline("foo"))) 34 | ) 35 | }) 36 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #ifdef _WIN32 5 | #include 6 | #include 7 | #endif 8 | 9 | #include 10 | 11 | #include "errors.h" 12 | 13 | #ifdef _WIN32 14 | 15 | SEXP clic_getppid(void) { 16 | DWORD pid = GetCurrentProcessId(); 17 | HANDLE handle = NULL; 18 | PROCESSENTRY32W pe = { 0 }; 19 | 20 | pe.dwSize = sizeof(PROCESSENTRY32W); 21 | handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 22 | if (handle == INVALID_HANDLE_VALUE) { 23 | R_THROW_SYSTEM_ERROR("Cannot query parent pid"); 24 | } 25 | 26 | if (Process32FirstW(handle, &pe)) { 27 | do { 28 | if (pe.th32ProcessID == pid) { 29 | DWORD ppid = pe.th32ParentProcessID; 30 | CloseHandle(handle); 31 | return ScalarInteger(ppid); 32 | } 33 | } while (Process32NextW(handle, &pe)); 34 | } 35 | 36 | /* Should not get here */ 37 | CloseHandle(handle); 38 | R_THROW_ERROR("Cannot find my own process, internal error"); 39 | return R_NilValue; 40 | } 41 | 42 | #else 43 | 44 | SEXP clic_getppid(void) { 45 | pid_t pid = getppid(); 46 | return Rf_ScalarInteger(pid); 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /man/figures/README/alert-dark.svg: -------------------------------------------------------------------------------- 1 | Agenericalert -------------------------------------------------------------------------------- /man/figures/README/alert.svg: -------------------------------------------------------------------------------- 1 | Agenericalert -------------------------------------------------------------------------------- /tests/testthat/test-ansi-palette.R: -------------------------------------------------------------------------------- 1 | test_that("ansi_palette_show", { 2 | local_clean_cli_context() 3 | expect_snapshot( 4 | ansi_palette_show(colors = truecolor) 5 | ) 6 | 7 | withr::local_options(cli.palette = "iterm-snazzy") 8 | expect_snapshot( 9 | ansi_palette_show(colors = truecolor) 10 | ) 11 | }) 12 | 13 | test_that("error", { 14 | expect_snapshot( 15 | error = TRUE, 16 | withr::with_options( 17 | list(cli.palette = "foobar12"), 18 | ansi_palette_show(colors = 256) 19 | ) 20 | ) 21 | }) 22 | 23 | test_that("custom palettes", { 24 | withr::local_options( 25 | cli.num_colors = 256, 26 | cli.palette = "iterm-snazzy" 27 | ) 28 | expect_snapshot({ 29 | col_black("black") 30 | col_red("red") 31 | col_green("green") 32 | col_yellow("yellow") 33 | col_blue("blue") 34 | col_magenta("magenta") 35 | col_cyan("cyan") 36 | col_white("white") 37 | 38 | col_br_black("br_black") 39 | col_br_red("br_red") 40 | col_br_green("br_green") 41 | col_br_yellow("br_yellow") 42 | col_br_blue("br_blue") 43 | col_br_magenta("br_magenta") 44 | col_br_cyan("br_cyan") 45 | col_br_white("br_white") 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/glue.md: -------------------------------------------------------------------------------- 1 | # glue quotes and comments 2 | 3 | Code 4 | cli_dl(c(test_1 = "all good", test_2 = "not #good")) 5 | Message 6 | test_1: all good 7 | test_2: not #good 8 | Code 9 | cli::cli_dl(c(test_3 = "no' good either")) 10 | Message 11 | test_3: no' good either 12 | Code 13 | cli::cli_dl(c(test_4 = "no\" good also")) 14 | Message 15 | test_4: no" good also 16 | Code 17 | cli::cli_text("{.url https://example.com/#section}") 18 | Message 19 | 20 | Code 21 | cli::cli_alert_success("Qapla'") 22 | Message 23 | v Qapla' 24 | 25 | # quotes, etc. within expressions are still OK 26 | 27 | Code 28 | cli::cli_text("{.url URL} {x <- 'foo'; nchar(x)}") 29 | Message 30 | 3 31 | Code 32 | cli::cli_text("{.url URL} {x <- \"foo\"; nchar(x)}") 33 | Message 34 | 3 35 | Code 36 | cli::cli_text("{.url URL} {1 + 1 # + 1} {1 + 1}") 37 | Message 38 | 2 2 39 | 40 | # { } is parsed with literal = FALSE 41 | 42 | Code 43 | format_message("{.emph {'{foo {}'}}") 44 | Output 45 | [1] "{foo {}" 46 | 47 | -------------------------------------------------------------------------------- /man/utf8_substr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utf8.R 3 | \name{utf8_substr} 4 | \alias{utf8_substr} 5 | \title{Substring of an UTF-8 string} 6 | \usage{ 7 | utf8_substr(x, start, stop) 8 | } 9 | \arguments{ 10 | \item{x}{Character vector.} 11 | 12 | \item{start}{Starting index or indices, recycled to match the length 13 | of \code{x}.} 14 | 15 | \item{stop}{Ending index or indices, recycled to match the length of 16 | \code{x}.} 17 | } 18 | \value{ 19 | Character vector of the same length as \code{x}, containing 20 | the requested substrings. 21 | } 22 | \description{ 23 | This function uses grapheme clusters instead of Unicode code points in 24 | UTF-8 strings. 25 | } 26 | \examples{ 27 | # Five grapheme clusters, select the middle three 28 | str <- paste0( 29 | "\U0001f477\U0001f3ff\u200d\u2640\ufe0f", 30 | "\U0001f477\U0001f3ff", 31 | "\U0001f477\u200d\u2640\ufe0f", 32 | "\U0001f477\U0001f3fb", 33 | "\U0001f477\U0001f3ff") 34 | cat(str) 35 | str24 <- utf8_substr(str, 2, 4) 36 | cat(str24) 37 | } 38 | \seealso{ 39 | Other UTF-8 string manipulation: 40 | \code{\link{utf8_graphemes}()}, 41 | \code{\link{utf8_nchar}()} 42 | } 43 | \concept{UTF-8 string manipulation} 44 | -------------------------------------------------------------------------------- /tests/testthat/test-glue.R: -------------------------------------------------------------------------------- 1 | # https://github.com/r-lib/cli/issues/370 2 | 3 | test_that("glue quotes and comments", { 4 | expect_snapshot({ 5 | cli_dl( 6 | c( 7 | "test_1" = "all good", 8 | "test_2" = "not #good" 9 | ) 10 | ) 11 | cli::cli_dl(c("test_3" = "no' good either")) 12 | cli::cli_dl(c("test_4" = "no\" good also")) 13 | cli::cli_text("{.url https://example.com/#section}") 14 | cli::cli_alert_success("Qapla'") 15 | }) 16 | }) 17 | 18 | test_that("quotes, etc. within expressions are still OK", { 19 | expect_snapshot({ 20 | cli::cli_text("{.url URL} {x <- 'foo'; nchar(x)}") 21 | cli::cli_text("{.url URL} {x <- \"foo\"; nchar(x)}") 22 | cli::cli_text("{.url URL} {1 + 1 # + 1} {1 + 1}") 23 | }) 24 | }) 25 | 26 | test_that("{. } is always a style", { 27 | cmd <- paste0("{.", basename(tempfile()), " }") 28 | expect_silent(glue_cmd(cmd, .envir = environment())) 29 | .foo <- 100 30 | vals <- glue_cmd("{.foo }", .envir = environment())$values 31 | expect_false(any(grepl("^v[0-9]+$", names(vals)))) 32 | }) 33 | 34 | test_that("{ } is parsed with literal = FALSE", { 35 | expect_snapshot( 36 | format_message("{.emph {'{foo {}'}}") 37 | ) 38 | }) 39 | -------------------------------------------------------------------------------- /R/timer.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | `__cli_update_due` <- FALSE 3 | 4 | #' @export 5 | 6 | cli_tick_reset <- function() { 7 | .Call(clic_tick_reset) 8 | } 9 | 10 | #' @export 11 | 12 | ccli_tick_reset <- NULL 13 | 14 | cli_tick_set <- function(tick_time = NULL, speed_time = NULL) { 15 | tick_time <- tick_time %||% clienv$tick_time 16 | speed_time <- speed_time %||% clienv$speed_time 17 | 18 | clienv$speed_time <- as.double(speed_time) 19 | clienv$tick_time <- as.integer(tick_time) 20 | .Call(clic_tick_set, clienv$tick_time, clienv$speed_time) 21 | invisible() 22 | } 23 | 24 | cli_tick_pause <- function(state = TRUE) { 25 | .Call(clic_tick_pause, state) 26 | } 27 | 28 | cli_tick_resume <- function(state = TRUE) { 29 | .Call(clic_tick_resume, state) 30 | } 31 | 32 | cli_with_ticks <- function(expr) { 33 | on.exit(cli_tick_resume(TRUE), add = TRUE) 34 | opts <- options(cli.progress_show_after = 0) 35 | on.exit(options(opts), add = TRUE) 36 | cli_tick_pause(TRUE) 37 | expr 38 | } 39 | 40 | cli_without_ticks <- function(expr) { 41 | on.exit(cli_tick_resume(TRUE), add = TRUE) 42 | opts <- options(cli.progress_show_after = 0) 43 | on.exit(options(opts), add = TRUE) 44 | cli_tick_pause(FALSE) 45 | expr 46 | } 47 | -------------------------------------------------------------------------------- /R/ansi-utils.R: -------------------------------------------------------------------------------- 1 | re_table <- function(...) { 2 | lapply(gregexpr(...), function(x) { 3 | res <- cbind( 4 | start = x, 5 | end = x + attr(x, "match.length") - 1, 6 | length = attr(x, "match.length") 7 | ) 8 | res <- res[res[, "start"] != -1, , drop = FALSE] 9 | }) 10 | } 11 | 12 | ## Create the non-matching table from the matching table 13 | 14 | non_matching <- function(table, str, empty = FALSE) { 15 | mapply(table, str, SIMPLIFY = FALSE, FUN = function(t, s) { 16 | if (!nrow(t)) { 17 | cbind(start = 1, end = base::nchar(s), length = base::nchar(s)) 18 | } else { 19 | start <- c(1, t[, "end"] + 1) 20 | end <- c(t[, "start"] - 1, base::nchar(s)) 21 | res <- cbind(start = start, end = end, length = end - start + 1) 22 | if (!empty) res[res[, "length"] != 0, , drop = FALSE] else res 23 | } 24 | }) 25 | } 26 | 27 | myseq <- function(from, to, by = 1) { 28 | stopifnot(by != 0) 29 | if (by > 0) { 30 | if (to < from) { 31 | integer() 32 | } else { 33 | seq(from, to, by = by) 34 | } 35 | } else { 36 | if (to > from) { 37 | integer() 38 | } else { 39 | seq(from, to, by = by) 40 | } 41 | } 42 | } 43 | 44 | `%:%` <- myseq 45 | -------------------------------------------------------------------------------- /tests/testthat/test-progress-utils.R: -------------------------------------------------------------------------------- 1 | test_that("cli_progress_num", { 2 | withr::local_options(cli.progress_handlers_only = "cli") 3 | fun <- function() { 4 | before <- cli_progress_num() 5 | cli_progress_bar() 6 | after <- cli_progress_num() 7 | expect_equal(before + 1, after) 8 | } 9 | 10 | capture_cli_messages(fun()) 11 | }) 12 | 13 | test_that("cli_progress_cleanup", { 14 | fun <- function() { 15 | num <- NULL 16 | cli::cli_progress_bar() 17 | fun2 <- function() { 18 | cli::cli_progress_bar() 19 | cli::cli_progress_cleanup() 20 | num <<- cli::cli_progress_num() 21 | } 22 | fun2() 23 | } 24 | 25 | out <- callr::r(fun) 26 | expect_equal(out, 0L) 27 | }) 28 | 29 | test_that("should_run_progress_examples", { 30 | withr::local_envvar(NOT_CRAN = "true") 31 | expect_true(should_run_progress_examples()) 32 | 33 | local_mocked_bindings(is_rcmd_check = function() TRUE) 34 | expect_false(should_run_progress_examples()) 35 | }) 36 | 37 | test_that("is_rcmd_check", { 38 | withr::local_envvar(NOT_CRAN = NA, "_R_CHECK_PACKAGE_NAME_" = NA) 39 | expect_false(is_rcmd_check()) 40 | withr::local_envvar(NOT_CRAN = NA, "_R_CHECK_PACKAGE_NAME_" = "cli") 41 | expect_true(is_rcmd_check()) 42 | }) 43 | -------------------------------------------------------------------------------- /man/figures/README/alert-success-dark.svg: -------------------------------------------------------------------------------- 1 | Downloaded3packages. -------------------------------------------------------------------------------- /man/figures/README/alert-success.svg: -------------------------------------------------------------------------------- 1 | Downloaded3packages. -------------------------------------------------------------------------------- /man/progress-utils.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/progress-utils.R 3 | \name{cli_progress_num} 4 | \alias{cli_progress_num} 5 | \alias{cli_progress_cleanup} 6 | \title{Progress bar utility functions.} 7 | \usage{ 8 | cli_progress_num() 9 | 10 | cli_progress_cleanup() 11 | } 12 | \value{ 13 | \code{cli_progress_num()} returns an integer scalar. 14 | 15 | `cli_progress_cleanup() does not return anything. 16 | } 17 | \description{ 18 | Progress bar utility functions. 19 | } 20 | \details{ 21 | \code{cli_progress_num()} returns the number of currently 22 | active progress bars. (These do not currently include the progress 23 | bars created in C/C++ code.) 24 | 25 | \code{cli_progress_cleanup()} terminates all active progress bars. 26 | (It currently ignores progress bars created in the C/C++ code.) 27 | } 28 | \seealso{ 29 | Other progress bar functions: 30 | \code{\link{cli_progress_along}()}, 31 | \code{\link{cli_progress_bar}()}, 32 | \code{\link{cli_progress_builtin_handlers}()}, 33 | \code{\link{cli_progress_message}()}, 34 | \code{\link{cli_progress_output}()}, 35 | \code{\link{cli_progress_step}()}, 36 | \code{\link{cli_progress_styles}()}, 37 | \code{\link{progress-variables}} 38 | } 39 | \concept{progress bar functions} 40 | -------------------------------------------------------------------------------- /man/ansi_trimws.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_trimws} 4 | \alias{ansi_trimws} 5 | \title{Remove leading and/or trailing whitespace from an ANSI string} 6 | \usage{ 7 | ansi_trimws(x, which = c("both", "left", "right")) 8 | } 9 | \arguments{ 10 | \item{x}{ANSI string vector.} 11 | 12 | \item{which}{Whether to remove leading or trailing whitespace or both.} 13 | } 14 | \value{ 15 | ANSI string, with the whitespace removed. 16 | } 17 | \description{ 18 | This function is similar to \code{\link[base:trimws]{base::trimws()}} but works on ANSI strings, 19 | and keeps color and other styling. 20 | } 21 | \examples{ 22 | trimws(paste0(" ", col_red("I am red"), " ")) 23 | ansi_trimws(paste0(" ", col_red("I am red"), " ")) 24 | trimws(col_red(" I am red ")) 25 | ansi_trimws(col_red(" I am red ")) 26 | } 27 | \seealso{ 28 | Other ANSI string operations: 29 | \code{\link{ansi_align}()}, 30 | \code{\link{ansi_columns}()}, 31 | \code{\link{ansi_nchar}()}, 32 | \code{\link{ansi_strsplit}()}, 33 | \code{\link{ansi_strtrim}()}, 34 | \code{\link{ansi_strwrap}()}, 35 | \code{\link{ansi_substr}()}, 36 | \code{\link{ansi_substring}()}, 37 | \code{\link{ansi_toupper}()} 38 | } 39 | \concept{ANSI string operations} 40 | -------------------------------------------------------------------------------- /man/figures/README/h2-dark.svg: -------------------------------------------------------------------------------- 1 | ──Heading2── -------------------------------------------------------------------------------- /man/figures/README/h2.svg: -------------------------------------------------------------------------------- 1 | ──Heading2── -------------------------------------------------------------------------------- /man/is_ansi_tty.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tty.R 3 | \name{is_ansi_tty} 4 | \alias{is_ansi_tty} 5 | \title{Detect if a stream support ANSI escape characters} 6 | \usage{ 7 | is_ansi_tty(stream = "auto") 8 | } 9 | \arguments{ 10 | \item{stream}{The stream to inspect or manipulate, an R connection 11 | object. It can also be a string, one of \code{"auto"}, \code{"message"}, 12 | \code{"stdout"}, \code{"stderr"}. \code{"auto"} will select \code{stdout()} if the session is 13 | interactive and there are no sinks, otherwise it will select \code{stderr()}.} 14 | } 15 | \value{ 16 | \code{TRUE} or \code{FALSE}. 17 | } 18 | \description{ 19 | We check that all of the following hold: 20 | \itemize{ 21 | \item The stream is a terminal. 22 | \item The platform is Unix. 23 | \item R is not running inside R.app (the macOS GUI). 24 | \item R is not running inside RStudio. 25 | \item R is not running inside Emacs. 26 | \item The terminal is not "dumb". 27 | \item \code{stream} is either the standard output or the standard error stream. 28 | } 29 | } 30 | \examples{ 31 | is_ansi_tty() 32 | } 33 | \seealso{ 34 | Other terminal capabilities: 35 | \code{\link{ansi_hide_cursor}()}, 36 | \code{\link{is_dynamic_tty}()} 37 | } 38 | \concept{terminal capabilities} 39 | -------------------------------------------------------------------------------- /R/progress-utils.R: -------------------------------------------------------------------------------- 1 | #' @title Progress bar utility functions. 2 | #' @details `cli_progress_num()` returns the number of currently 3 | #' active progress bars. (These do not currently include the progress 4 | #' bars created in C/C++ code.) 5 | #' @return `cli_progress_num()` returns an integer scalar. 6 | #' 7 | #' @rdname progress-utils 8 | #' @family progress bar functions 9 | #' @export 10 | 11 | cli_progress_num <- function() { 12 | length(clienv$progress) 13 | } 14 | 15 | #' @details `cli_progress_cleanup()` terminates all active progress bars. 16 | #' (It currently ignores progress bars created in the C/C++ code.) 17 | #' @return `cli_progress_cleanup() does not return anything. 18 | #' 19 | #' @rdname progress-utils 20 | #' @export 21 | 22 | cli_progress_cleanup <- function() { 23 | while ((n <- cli_progress_num()) > 0) { 24 | cli_progress_done(clienv$progress[[n]]$id) 25 | } 26 | ansi_show_cursor() 27 | invisible() 28 | } 29 | 30 | should_run_progress_examples <- function() { 31 | if (is_rcmd_check()) return(FALSE) 32 | tolower(Sys.getenv("R_PROGRESS_NO_EXAMPLES")) != "true" 33 | } 34 | 35 | is_rcmd_check <- function() { 36 | if (identical(Sys.getenv("NOT_CRAN"), "true")) { 37 | FALSE 38 | } else { 39 | Sys.getenv("_R_CHECK_PACKAGE_NAME_", "") != "" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /man/code_highlight.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/prettycode.R 3 | \name{code_highlight} 4 | \alias{code_highlight} 5 | \title{Syntax highlight R code} 6 | \usage{ 7 | code_highlight(code, code_theme = NULL, envir = NULL) 8 | } 9 | \arguments{ 10 | \item{code}{Character vector, each element is one line of code.} 11 | 12 | \item{code_theme}{Theme see \code{\link[=code_theme_list]{code_theme_list()}}.} 13 | 14 | \item{envir}{Environment to look up function calls for hyperlinks. 15 | If \code{NULL}, then the global search path is used.} 16 | } 17 | \value{ 18 | Character vector, the highlighted code. 19 | } 20 | \description{ 21 | Syntax highlight R code 22 | } 23 | \details{ 24 | See \code{\link[=code_theme_list]{code_theme_list()}} for the default syntax highlighting theme and 25 | how to change it. 26 | 27 | If \code{code} does not parse, then it is returned unchanged and a 28 | \code{cli_parse_failure} condition is thrown. Note that this is not an error, 29 | and the condition is ignored, unless explicitly caught. 30 | } 31 | \examples{ 32 | code_highlight(deparse(ls)) 33 | cat(code_highlight(deparse(ls)), sep = "\n") 34 | } 35 | \seealso{ 36 | Other syntax highlighting: 37 | \code{\link{code_theme_list}()} 38 | } 39 | \concept{syntax highlighting} 40 | -------------------------------------------------------------------------------- /man/cli-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cli-package.R 3 | \docType{package} 4 | \name{cli-package} 5 | \alias{cli-package} 6 | \title{cli: Helpers for Developing Command Line Interfaces} 7 | \description{ 8 | A suite of tools to build attractive command line interfaces ('CLIs'), from semantic elements: headings, lists, alerts, paragraphs, etc. Supports custom themes via a 'CSS'-like language. It also contains a number of lower level 'CLI' elements: rules, boxes, trees, and 'Unicode' symbols with 'ASCII' alternatives. It support ANSI colors and text styles as well. 9 | } 10 | \seealso{ 11 | Useful links: 12 | \itemize{ 13 | \item \url{https://cli.r-lib.org} 14 | \item \url{https://github.com/r-lib/cli} 15 | \item Report bugs at \url{https://github.com/r-lib/cli/issues} 16 | } 17 | 18 | } 19 | \author{ 20 | \strong{Maintainer}: Gábor Csárdi \email{gabor@posit.co} 21 | 22 | Other contributors: 23 | \itemize{ 24 | \item Hadley Wickham [contributor] 25 | \item Kirill Müller [contributor] 26 | \item Salim Brüggemann \email{salim-b@pm.me} (\href{https://orcid.org/0000-0002-5329-5987}{ORCID}) [contributor] 27 | \item Posit Software, PBC (\href{https://ror.org/03wc8by49}{ROR}) [copyright holder, funder] 28 | } 29 | 30 | } 31 | \keyword{internal} 32 | -------------------------------------------------------------------------------- /tests/testthat/test-deep-lists.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that("deep lists ul", { 5 | test_ul = function(n = 2) { 6 | for (i in seq_len(n)) { 7 | cli::cli_ul() 8 | cli::cli_li(paste0("Level ", i)) 9 | } 10 | } 11 | expect_snapshot( 12 | for (i in 1:4) test_ul(i) 13 | ) 14 | }) 15 | 16 | test_that("deep lists ol", { 17 | test_ol = function(n = 2) { 18 | for (i in seq_len(n)) { 19 | cli::cli_ol() 20 | cli::cli_li(paste0("Level ", i)) 21 | } 22 | } 23 | expect_snapshot( 24 | for (i in 1:4) test_ol(i) 25 | ) 26 | }) 27 | 28 | test_that("deep lists ol ul", { 29 | test_ol_ul = function(n = 2) { 30 | for (i in seq_len(n)) { 31 | cli::cli_ol() 32 | cli::cli_li(paste0("Level ", 2 * i - 1)) 33 | 34 | cli::cli_ul() 35 | cli::cli_li(paste0("Level ", 2 * i)) 36 | } 37 | } 38 | expect_snapshot( 39 | for (i in 1:4) test_ol_ul(i) 40 | ) 41 | }) 42 | 43 | test_that("deep lists ul ol", { 44 | test_ul_ol = function(n = 2) { 45 | for (i in seq_len(n)) { 46 | cli::cli_ul() 47 | cli::cli_li(paste0("Level ", 2 * i - 1)) 48 | 49 | cli::cli_ol() 50 | cli::cli_li(paste0("Level ", 2 * i)) 51 | } 52 | } 53 | expect_snapshot( 54 | for (i in 1:4) test_ul_ol(i) 55 | ) 56 | }) 57 | -------------------------------------------------------------------------------- /src/vtparse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * VTParse - an implementation of Paul Williams' DEC compatible state machine parser 3 | * 4 | * Author: Joshua Haberman 5 | * 6 | * This code is in the public domain. 7 | */ 8 | 9 | #ifndef VTPARSE_DOT_H 10 | #define VTPARSE_DOT_H 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include "vtparse_table.h" 17 | 18 | #define MAX_INTERMEDIATE_CHARS 2 19 | #define ACTION(state_change) (state_change & 0x0F) 20 | #define STATE(state_change) (state_change >> 4) 21 | 22 | struct vtparse; 23 | 24 | typedef void (*vtparse_callback_t)(struct vtparse*, vtparse_action_t, unsigned int); 25 | 26 | typedef struct vtparse { 27 | vtparse_state_t state; 28 | vtparse_callback_t cb; 29 | unsigned char intermediate_chars[MAX_INTERMEDIATE_CHARS+1]; 30 | int num_intermediate_chars; 31 | char ignore_flagged; 32 | int params[16]; 33 | int num_params; 34 | void* user_data; 35 | int characterBytes; 36 | unsigned int utf8Character; 37 | } vtparse_t; 38 | 39 | void vtparse_init(vtparse_t *parser, vtparse_callback_t cb); 40 | void vtparse(vtparse_t *parser, unsigned char *data, unsigned int len); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /man/diff_str.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/diff.R 3 | \name{diff_str} 4 | \alias{diff_str} 5 | \title{Compare two character strings, character by character} 6 | \usage{ 7 | diff_str(old, new, max_dist = Inf) 8 | } 9 | \arguments{ 10 | \item{old}{First string, must not be \code{NA}.} 11 | 12 | \item{new}{Second string, must not be \code{NA}.} 13 | 14 | \item{max_dist}{Maximum distance to consider, or \code{Inf} for no limit. 15 | If the LCS edit distance is larger than this, then the function 16 | throws an error with class \code{"cli_diff_max_dist"}. (If you specify 17 | \code{Inf} the real limit is \code{.Machine$integer.max} but to reach this the 18 | function would have to run a very long time.)} 19 | } 20 | \value{ 21 | A list that is a \code{cli_diff_str} object and also a 22 | \code{cli_diff_chr} object, see \link{diff_str} for the details about its 23 | structure. 24 | } 25 | \description{ 26 | Characters are defined by UTF-8 graphemes. 27 | } 28 | \examples{ 29 | str1 <- "abcdefghijklmnopqrstuvwxyz" 30 | str2 <- "PREabcdefgMIDDLEnopqrstuvwxyzPOST" 31 | diff_str(str1, str2) 32 | } 33 | \seealso{ 34 | The diffobj package for a much more comprehensive set of 35 | \code{diff}-like tools. 36 | 37 | Other diff functions in cli: 38 | \code{\link{diff_chr}()} 39 | } 40 | \concept{diff functions in cli} 41 | -------------------------------------------------------------------------------- /R/docs.R: -------------------------------------------------------------------------------- 1 | #' Frequently Asked Questions 2 | #' 3 | #' @name faq 4 | #' @includeRmd man/chunks/FAQ.Rmd 5 | NULL 6 | 7 | 8 | docs_progress_c_api <- function() { 9 | if (file.exists("inst/include/cli/progress.h")) { 10 | lines <- readLines("inst/include/cli/progress.h") 11 | } else { 12 | lines <- readLines("../inst/include/cli/progress.h") 13 | } 14 | 15 | ## Remove non-matching lines, but leave an empty line between blocks 16 | ptn <- "^//'[ ]?" 17 | mtch <- grepl(ptn, lines) 18 | lines[!mtch] <- "" 19 | prev <- c("", lines[-length(lines)]) 20 | lines <- lines[mtch | prev != lines] 21 | 22 | ## Remove doc pattern 23 | lines <- sub(ptn, "", lines) 24 | 25 | tmp <- tempfile(fileext = ".Rmd") 26 | cat(lines, sep = "\n", file = tmp) 27 | tmp 28 | } 29 | 30 | #' @title The cli progress C API 31 | #' @name progress-c 32 | #' @section The cli progress C API: 33 | #' 34 | #' ```{r include = FALSE, cache = FALSE, child = cli:::docs_progress_c_api()} 35 | #' ``` 36 | NULL 37 | 38 | #' @title cli environment variables and options 39 | #' @name cli-config 40 | #' 41 | #' @section User facing configuration: 42 | #' 43 | #' ```{r include = FALSE, child = "vignettes/cli-config-user.Rmd"} 44 | #' ``` 45 | #' 46 | #' @section Internal configuration: 47 | #' 48 | #' ```{r include = FALSE, child = "vignettes/cli-config-internal.Rmd"} 49 | #' ``` 50 | NULL 51 | -------------------------------------------------------------------------------- /man/figures/README/glue-dark.svg: -------------------------------------------------------------------------------- 1 | Downloaded123.14MBin1.3s -------------------------------------------------------------------------------- /man/figures/README/glue.svg: -------------------------------------------------------------------------------- 1 | Downloaded123.14MBin1.3s -------------------------------------------------------------------------------- /man/figures/README/h1.svg: -------------------------------------------------------------------------------- 1 | ──Heading1─────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README/h1-dark.svg: -------------------------------------------------------------------------------- 1 | ──Heading1─────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README/alert-danger-dark.svg: -------------------------------------------------------------------------------- 1 | Failedtoconnecttodatabase. -------------------------------------------------------------------------------- /man/figures/README/alert-danger.svg: -------------------------------------------------------------------------------- 1 | Failedtoconnecttodatabase. -------------------------------------------------------------------------------- /man/ansi_strtrim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_strtrim} 4 | \alias{ansi_strtrim} 5 | \title{Truncate an ANSI string} 6 | \usage{ 7 | ansi_strtrim(x, width = console_width(), ellipsis = symbol$ellipsis) 8 | } 9 | \arguments{ 10 | \item{x}{Character vector of ANSI strings.} 11 | 12 | \item{width}{The width to truncate to.} 13 | 14 | \item{ellipsis}{The string to append to truncated strings. Supply an 15 | empty string if you don't want a marker.} 16 | } 17 | \description{ 18 | This function is similar to \code{\link[base:strtrim]{base::strtrim()}}, but works correctly with 19 | ANSI styled strings. It also adds \code{...} (or the corresponding Unicode 20 | character if Unicode characters are allowed) to the end of truncated 21 | strings. 22 | } 23 | \details{ 24 | Note: \code{ansi_strtrim()} does not support NA values currently. 25 | } 26 | \examples{ 27 | text <- cli::col_red(cli:::lorem_ipsum()) 28 | ansi_strtrim(c(text, "foobar"), 40) 29 | } 30 | \seealso{ 31 | Other ANSI string operations: 32 | \code{\link{ansi_align}()}, 33 | \code{\link{ansi_columns}()}, 34 | \code{\link{ansi_nchar}()}, 35 | \code{\link{ansi_strsplit}()}, 36 | \code{\link{ansi_strwrap}()}, 37 | \code{\link{ansi_substr}()}, 38 | \code{\link{ansi_substring}()}, 39 | \code{\link{ansi_toupper}()}, 40 | \code{\link{ansi_trimws}()} 41 | } 42 | \concept{ANSI string operations} 43 | -------------------------------------------------------------------------------- /man/figures/README/alert-info.svg: -------------------------------------------------------------------------------- 1 | Reopeneddatabase<example.com:port>. -------------------------------------------------------------------------------- /src/tty.c: -------------------------------------------------------------------------------- 1 | 2 | #include "cli.h" 3 | #include "errors.h" 4 | #include "cleancall.h" 5 | 6 | #ifdef WIN32 7 | #include 8 | #else 9 | #include 10 | #include 11 | #include 12 | #include 13 | #endif 14 | 15 | SEXP clic_tty_size(void) { 16 | SEXP result = PROTECT(Rf_allocVector(INTSXP, 2)); 17 | 18 | #ifdef WIN32 19 | CONSOLE_SCREEN_BUFFER_INFO info; 20 | BOOL ok = GetConsoleScreenBufferInfo( 21 | GetStdHandle(STD_OUTPUT_HANDLE), 22 | &info 23 | ); 24 | if (!ok) R_THROW_SYSTEM_ERROR("Cannot determine terminal size"); 25 | INTEGER(result)[0] = info.srWindow.Right - info.srWindow.Left + 1; 26 | INTEGER(result)[1] = info.srWindow.Bottom - info.srWindow.Top + 1; 27 | 28 | #elif defined(TIOCGWINSZ) 29 | struct winsize w; 30 | int err = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 31 | if (err == -1) R_THROW_SYSTEM_ERROR("Cannot determine terminal size"); 32 | INTEGER(result)[0] = w.ws_col; 33 | INTEGER(result)[1] = w.ws_row; 34 | 35 | #elif defined(TIOCGSIZE) 36 | struct ttysize ts; 37 | int err = ioctl(STDOUT_FILENO, TIOCGSIZE, &ts); 38 | if (err == -1) R_THROW_SYSTEM_ERROR("Cannot determine terminal size"); 39 | INTEGER(result)[0] = ts.ts_cols; 40 | INTEGER(result)[1] = ts.ts_rows; 41 | 42 | #else 43 | R_THROW_ERROR("Don't know how to determine terminal size"); 44 | #endif 45 | 46 | UNPROTECT(1); 47 | return result; 48 | } 49 | -------------------------------------------------------------------------------- /tests/testthat/test-progress-bar.R: -------------------------------------------------------------------------------- 1 | test_that_cli("make_progress_bar", { 2 | withr::local_options( 3 | cli.progress_bar_style = NULL, 4 | cli.progress_bar_style_unicode = NULL, 5 | cli.progress_bar_style_ascii = NULL 6 | ) 7 | expect_snapshot(make_progress_bar(.5)) 8 | }) 9 | 10 | test_that_cli(configs = "fancy", "cli_progress_styles", { 11 | withr::local_options( 12 | cli.progress_bar_style_unicode = NULL, 13 | cli.progress_bar_style_ascii = NULL 14 | ) 15 | 16 | withr::local_options(cli.progress_bar_style = "classic") 17 | expect_snapshot(make_progress_bar(.5)) 18 | 19 | withr::local_options(cli.progress_bar_style = "squares") 20 | expect_snapshot(make_progress_bar(.5)) 21 | 22 | withr::local_options(cli.progress_bar_style = "dot") 23 | expect_snapshot(make_progress_bar(.5)) 24 | 25 | withr::local_options(cli.progress_bar_style = "fillsquares") 26 | expect_snapshot(make_progress_bar(.5)) 27 | 28 | withr::local_options(cli.progress_bar_style = "bar") 29 | expect_snapshot(make_progress_bar(.5)) 30 | }) 31 | 32 | test_that_cli(configs = c("plain", "unicode"), "custom style", { 33 | mybar <- list(complete = "X", incomplete = "O", current = ">") 34 | withr::local_options( 35 | cli.progress_bar_style = mybar, 36 | cli.progress_bar_style_unicode = NULL, 37 | cli.progress_bar_style_ascii = NULL 38 | ) 39 | expect_snapshot(make_progress_bar(.5)) 40 | }) 41 | -------------------------------------------------------------------------------- /man/figures/README/alert-info-dark.svg: -------------------------------------------------------------------------------- 1 | Reopeneddatabase<example.com:port>. -------------------------------------------------------------------------------- /man/figures/README/plurals.svg: -------------------------------------------------------------------------------- 1 | Found3filesand1directory. -------------------------------------------------------------------------------- /src/vtparse_table.h: -------------------------------------------------------------------------------- 1 | typedef enum { 2 | VTPARSE_STATE_CSI_ENTRY = 1, 3 | VTPARSE_STATE_CSI_IGNORE = 2, 4 | VTPARSE_STATE_CSI_INTERMEDIATE = 3, 5 | VTPARSE_STATE_CSI_PARAM = 4, 6 | VTPARSE_STATE_DCS_ENTRY = 5, 7 | VTPARSE_STATE_DCS_IGNORE = 6, 8 | VTPARSE_STATE_DCS_INTERMEDIATE = 7, 9 | VTPARSE_STATE_DCS_PARAM = 8, 10 | VTPARSE_STATE_DCS_PASSTHROUGH = 9, 11 | VTPARSE_STATE_ESCAPE = 10, 12 | VTPARSE_STATE_ESCAPE_INTERMEDIATE = 11, 13 | VTPARSE_STATE_GROUND = 12, 14 | VTPARSE_STATE_OSC_STRING = 13, 15 | VTPARSE_STATE_SOS_PM_APC_STRING = 14, 16 | } vtparse_state_t; 17 | 18 | typedef enum { 19 | VTPARSE_ACTION_CLEAR = 1, 20 | VTPARSE_ACTION_COLLECT = 2, 21 | VTPARSE_ACTION_CSI_DISPATCH = 3, 22 | VTPARSE_ACTION_ESC_DISPATCH = 4, 23 | VTPARSE_ACTION_EXECUTE = 5, 24 | VTPARSE_ACTION_HOOK = 6, 25 | VTPARSE_ACTION_IGNORE = 7, 26 | VTPARSE_ACTION_OSC_END = 8, 27 | VTPARSE_ACTION_OSC_PUT = 9, 28 | VTPARSE_ACTION_OSC_START = 10, 29 | VTPARSE_ACTION_PARAM = 11, 30 | VTPARSE_ACTION_PRINT = 12, 31 | VTPARSE_ACTION_PUT = 13, 32 | VTPARSE_ACTION_UNHOOK = 14, 33 | VTPARSE_ACTION_ERROR = 15, 34 | } vtparse_action_t; 35 | 36 | typedef unsigned char state_change_t; 37 | extern state_change_t STATE_TABLE[14][256]; 38 | extern vtparse_action_t ENTRY_ACTIONS[14]; 39 | extern vtparse_action_t EXIT_ACTIONS[14]; 40 | extern char *ACTION_NAMES[16]; 41 | extern char *STATE_NAMES[15]; 42 | 43 | -------------------------------------------------------------------------------- /tests/testthat/test-cat-helpers.R: -------------------------------------------------------------------------------- 1 | test_that("cat_line", { 2 | expect_snapshot( 3 | cat_line("This is ", "a ", "line of text.") 4 | ) 5 | 6 | tmp <- tempfile() 7 | on.exit(unlink(tmp), add = TRUE) 8 | cat_line("This is ", "a ", "line of text.", file = tmp) 9 | exp <- "This is a line of text." 10 | expect_equal(readLines(tmp, warn = FALSE), exp) 11 | 12 | local_reproducible_output(crayon = TRUE) 13 | 14 | expect_snapshot({ 15 | cat_line("This is ", "a ", "line of text.", col = "red") 16 | cat_line("This is ", "a ", "line of text.", background_col = "green") 17 | }) 18 | }) 19 | 20 | test_that_cli(configs = c("plain", "unicode"), "cat_bullet", { 21 | expect_snapshot({ 22 | cat_bullet(letters[1:5]) 23 | }) 24 | }) 25 | 26 | test_that_cli(configs = c("plain", "unicode"), "cat_boxx", { 27 | expect_snapshot({ 28 | cat_boxx("foo") 29 | }) 30 | }) 31 | 32 | test_that_cli(configs = c("plain", "unicode"), "cat_rule", { 33 | expect_snapshot(local({ 34 | withr::local_options(cli.width = 20) 35 | cat_rule("title") 36 | })) 37 | }) 38 | 39 | test_that_cli(configs = c("plain", "unicode"), "cat_print", { 40 | expect_snapshot({ 41 | cat_print(boxx("")) 42 | }) 43 | 44 | expect_snapshot(local({ 45 | tmp <- tempfile() 46 | on.exit(unlink(tmp), add = TRUE) 47 | expect_silent(cat_print(boxx(""), file = tmp)) 48 | cat(readLines(tmp, warn = FALSE), sep = "\n") 49 | })) 50 | }) 51 | -------------------------------------------------------------------------------- /man/figures/README/plurals-dark.svg: -------------------------------------------------------------------------------- 1 | Found3filesand1directory. -------------------------------------------------------------------------------- /man/keypress.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/keypress.R 3 | \name{keypress} 4 | \alias{keypress} 5 | \title{Read a single keypress at the terminal} 6 | \usage{ 7 | keypress(block = TRUE) 8 | } 9 | \arguments{ 10 | \item{block}{Whether to wait for a key press, if there is none 11 | available now.} 12 | } 13 | \value{ 14 | The key pressed, a character scalar. For non-blocking reads 15 | \code{NA} is returned if no keys are available. 16 | } 17 | \description{ 18 | It currently only works at Linux/Unix and OSX terminals, 19 | and at the Windows command line. see \code{\link{has_keypress_support}}. 20 | } 21 | \details{ 22 | The following special keys are supported: 23 | \itemize{ 24 | \item Arrow keys: 'up', 'down', 'right', 'left'. 25 | \item Function keys: from 'f1' to 'f12'. 26 | \item Others: 'home', 'end', 'insert', 'delete', 'pageup', 'pagedown', 27 | 'tab', 'enter', 'backspace' (same as 'delete' on OSX keyboards), 28 | 'escape'. 29 | \item Control with one of the following keys: 'a', 'b', 'c', 'd', 'e', 'f', 30 | 'h', 'k', 'l', 'n', 'p', 't', 'u', 'w'. 31 | } 32 | } 33 | \examples{ 34 | \dontshow{if (FALSE) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 35 | x <- keypress() 36 | cat("You pressed key", x, "\n") 37 | \dontshow{\}) # examplesIf} 38 | } 39 | \seealso{ 40 | Other keypress function: 41 | \code{\link{has_keypress_support}()} 42 | } 43 | \concept{keypress function} 44 | -------------------------------------------------------------------------------- /man/start_app.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/app.R 3 | \name{start_app} 4 | \alias{start_app} 5 | \alias{stop_app} 6 | \alias{default_app} 7 | \title{Start, stop, query the default cli application} 8 | \usage{ 9 | start_app( 10 | theme = getOption("cli.theme"), 11 | output = c("auto", "message", "stdout", "stderr"), 12 | .auto_close = TRUE, 13 | .envir = parent.frame() 14 | ) 15 | 16 | stop_app(app = NULL) 17 | 18 | default_app() 19 | } 20 | \arguments{ 21 | \item{theme}{Theme to use.} 22 | 23 | \item{output}{How to print the output.} 24 | 25 | \item{.auto_close}{Whether to stop the app, when the calling frame 26 | is destroyed.} 27 | 28 | \item{.envir}{The environment to use, instead of the calling frame, 29 | to trigger the stop of the app.} 30 | 31 | \item{app}{App to stop. If \code{NULL}, the current default app is stopped. 32 | Otherwise we find the supplied app in the app stack, and remote it, 33 | together with all the apps above it.} 34 | } 35 | \value{ 36 | \code{start_app} returns the new app, \code{default_app} returns the default app. 37 | \code{stop_app} does not return anything. 38 | } 39 | \description{ 40 | \code{start_app} creates an app, and places it on the top of the app stack. 41 | } 42 | \details{ 43 | \code{stop_app} removes the top app, or multiple apps from the app stack. 44 | 45 | \code{default_app} returns the default app, the one on the top of the stack. 46 | } 47 | -------------------------------------------------------------------------------- /man/ansi_nchar.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_nchar} 4 | \alias{ansi_nchar} 5 | \title{Count number of characters in an ANSI colored string} 6 | \usage{ 7 | ansi_nchar(x, type = c("chars", "bytes", "width", "graphemes", "codepoints")) 8 | } 9 | \arguments{ 10 | \item{x}{Character vector, potentially ANSI styled, or a vector to be 11 | coerced to character. If it converted to UTF-8.} 12 | 13 | \item{type}{Whether to count graphemes (characters), code points, 14 | bytes, or calculate the display width of the string.} 15 | } 16 | \value{ 17 | Numeric vector, the length of the strings in the character 18 | vector. 19 | } 20 | \description{ 21 | This is a color-aware counterpart of \code{\link[=utf8_nchar]{utf8_nchar()}}. By default it 22 | counts Unicode grapheme clusters, instead of code points. 23 | } 24 | \examples{ 25 | str <- paste( 26 | col_red("red"), 27 | "default", 28 | col_green("green") 29 | ) 30 | 31 | cat(str, "\n") 32 | nchar(str) 33 | ansi_nchar(str) 34 | nchar(ansi_strip(str)) 35 | } 36 | \seealso{ 37 | Other ANSI string operations: 38 | \code{\link{ansi_align}()}, 39 | \code{\link{ansi_columns}()}, 40 | \code{\link{ansi_strsplit}()}, 41 | \code{\link{ansi_strtrim}()}, 42 | \code{\link{ansi_strwrap}()}, 43 | \code{\link{ansi_substr}()}, 44 | \code{\link{ansi_substring}()}, 45 | \code{\link{ansi_toupper}()}, 46 | \code{\link{ansi_trimws}()} 47 | } 48 | \concept{ANSI string operations} 49 | -------------------------------------------------------------------------------- /tests/testthat/progresstestcpp/src/cpp11.cpp: -------------------------------------------------------------------------------- 1 | // Generated by cpp11: do not edit by hand 2 | // clang-format off 3 | 4 | 5 | #include "cpp11/declarations.hpp" 6 | 7 | // testcpp.cpp 8 | int test_baseline_(); 9 | extern "C" SEXP _progresstestcpp_test_baseline_() { 10 | BEGIN_CPP11 11 | return cpp11::as_sexp(test_baseline_()); 12 | END_CPP11 13 | } 14 | // testcpp.cpp 15 | int test_cli_(); 16 | extern "C" SEXP _progresstestcpp_test_cli_() { 17 | BEGIN_CPP11 18 | return cpp11::as_sexp(test_cli_()); 19 | END_CPP11 20 | } 21 | // testcpp.cpp 22 | int test_template_(); 23 | extern "C" SEXP _progresstestcpp_test_template_() { 24 | BEGIN_CPP11 25 | return cpp11::as_sexp(test_template_()); 26 | END_CPP11 27 | } 28 | 29 | extern "C" { 30 | /* .Call calls */ 31 | extern SEXP _progresstestcpp_test_baseline_(); 32 | extern SEXP _progresstestcpp_test_cli_(); 33 | extern SEXP _progresstestcpp_test_template_(); 34 | 35 | static const R_CallMethodDef CallEntries[] = { 36 | {"_progresstestcpp_test_baseline_", (DL_FUNC) &_progresstestcpp_test_baseline_, 0}, 37 | {"_progresstestcpp_test_cli_", (DL_FUNC) &_progresstestcpp_test_cli_, 0}, 38 | {"_progresstestcpp_test_template_", (DL_FUNC) &_progresstestcpp_test_template_, 0}, 39 | {NULL, NULL, 0} 40 | }; 41 | } 42 | 43 | extern "C" void R_init_progresstestcpp(DllInfo* dll){ 44 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 45 | R_useDynamicSymbols(dll, FALSE); 46 | R_forceSymbols(dll, TRUE); 47 | } 48 | -------------------------------------------------------------------------------- /inst/shiny/along/app.R: -------------------------------------------------------------------------------- 1 | # modified from https://shiny.rstudio.com/articles/progress.html 2 | 3 | # This example uses `cli_progress_along()`. 4 | 5 | library(cli) 6 | 7 | # !!! You don't need these in real code. 8 | # cli.progress_show_after makes sure that we see the progress bar from 9 | # the beginning, not only after a delay. 10 | options(cli.progress_show_after = 0) 11 | 12 | # !!! You don't need these in real code. 13 | # This also requests logging the progress bar to the standard output, 14 | # in the console. 15 | options(cli.progress_handlers_only = c("shiny", "logger")) 16 | 17 | server <- function(input, output) { 18 | output$plot <- renderPlot({ 19 | input$goPlot # Re-run when button is clicked 20 | 21 | # Create 0-row data frame which will be used to store data 22 | dat <- data.frame(x = numeric(0), y = numeric(0)) 23 | 24 | # Number of times we'll go through the loop 25 | n <- 10 26 | 27 | for (i in cli_progress_along(1:n, "Rendering", format = "At {i}")) { 28 | # Each time through the loop, add another row of data. This is 29 | # a stand-in for a long-running computation. 30 | dat <- rbind(dat, data.frame(x = rnorm(1), y = rnorm(1))) 31 | 32 | # Pause for 0.1 seconds to simulate a long computation. 33 | Sys.sleep(0.5) 34 | } 35 | 36 | plot(dat$x, dat$y) 37 | }) 38 | } 39 | 40 | ui <- shinyUI(basicPage( 41 | plotOutput('plot', width = "300px", height = "300px"), 42 | actionButton('goPlot', 'Go plot') 43 | )) 44 | 45 | shinyApp(ui = ui, server = server) 46 | -------------------------------------------------------------------------------- /man/ansi_hide_cursor.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tty.R 3 | \name{ansi_hide_cursor} 4 | \alias{ansi_hide_cursor} 5 | \alias{ansi_show_cursor} 6 | \alias{ansi_with_hidden_cursor} 7 | \title{Hide/show cursor in a terminal} 8 | \usage{ 9 | ansi_hide_cursor(stream = "auto") 10 | 11 | ansi_show_cursor(stream = "auto") 12 | 13 | ansi_with_hidden_cursor(expr, stream = "auto") 14 | } 15 | \arguments{ 16 | \item{stream}{The stream to inspect or manipulate, an R connection 17 | object. It can also be a string, one of \code{"auto"}, \code{"message"}, 18 | \code{"stdout"}, \code{"stderr"}. \code{"auto"} will select \code{stdout()} if the session is 19 | interactive and there are no sinks, otherwise it will select \code{stderr()}.} 20 | 21 | \item{expr}{R expression to evaluate.} 22 | } 23 | \description{ 24 | This only works in terminal emulators. In other environments, it 25 | does nothing. 26 | } 27 | \details{ 28 | \code{ansi_hide_cursor()} hides the cursor. 29 | 30 | \code{ansi_show_cursor()} shows the cursor. 31 | 32 | \code{ansi_with_hidden_cursor()} temporarily hides the cursor for 33 | evaluating an expression. 34 | } 35 | \seealso{ 36 | Other terminal capabilities: 37 | \code{\link{is_ansi_tty}()}, 38 | \code{\link{is_dynamic_tty}()} 39 | 40 | Other low level ANSI functions: 41 | \code{\link{ansi_has_any}()}, 42 | \code{\link{ansi_regex}()}, 43 | \code{\link{ansi_string}()}, 44 | \code{\link{ansi_strip}()} 45 | } 46 | \concept{low level ANSI functions} 47 | \concept{terminal capabilities} 48 | -------------------------------------------------------------------------------- /man/ansi_html.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_html} 4 | \alias{ansi_html} 5 | \title{Convert ANSI styled text to HTML} 6 | \usage{ 7 | ansi_html(x, escape_reserved = TRUE, csi = c("drop", "keep")) 8 | } 9 | \arguments{ 10 | \item{x}{Input character vector.} 11 | 12 | \item{escape_reserved}{Whether to escape characters that are reserved 13 | in HTML (\code{&}, \code{<} and \code{>}).} 14 | 15 | \item{csi}{What to do with non-SGR ANSI sequences, either \code{"keep"}, 16 | or \code{"drop"} them.} 17 | } 18 | \value{ 19 | Character vector of HTML. 20 | } 21 | \description{ 22 | Convert ANSI styled text to HTML 23 | } 24 | \examples{ 25 | \dontshow{if (cli:::has_packages(c("htmltools", "withr"))) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 26 | ## Syntax highlight the source code of an R function with ANSI tags, 27 | ## and export it to a HTML file. 28 | code <- withr::with_options( 29 | list(ansi.num_colors = 256), 30 | code_highlight(format(ansi_html)) 31 | ) 32 | hcode <- paste(ansi_html(code), collapse = "\n") 33 | css <- paste(format(ansi_html_style()), collapse= "\n") 34 | page <- htmltools::tagList( 35 | htmltools::tags$head(htmltools::tags$style(css)), 36 | htmltools::tags$pre(htmltools::HTML(hcode)) 37 | ) 38 | 39 | if (interactive()) htmltools::html_print(page) 40 | \dontshow{\}) # examplesIf} 41 | } 42 | \seealso{ 43 | Other ANSI to HTML conversion: 44 | \code{\link{ansi_html_style}()} 45 | } 46 | \concept{ANSI to HTML conversion} 47 | -------------------------------------------------------------------------------- /inst/shiny/simple/app.R: -------------------------------------------------------------------------------- 1 | # modified from https://shiny.rstudio.com/articles/progress.html 2 | 3 | library(cli) 4 | 5 | # !!! You don't need these in real code. 6 | # cli.progress_show_after makes sure that we see the progress bar from 7 | # the beginning, not only after a delay. 8 | options(cli.progress_show_after = 0) 9 | 10 | # !!! You don't need these in real code. 11 | # This also requests logging the progress bar to the standard output, 12 | # in the console. 13 | options(cli.progress_handlers_only = c("shiny", "logger")) 14 | 15 | server <- function(input, output) { 16 | output$plot <- renderPlot({ 17 | input$goPlot # Re-run when button is clicked 18 | 19 | # Create 0-row data frame which will be used to store data 20 | dat <- data.frame(x = numeric(0), y = numeric(0)) 21 | 22 | # Number of times we'll go through the loop 23 | n <- 10 24 | 25 | cli_progress_bar(total = n, "Rendering plot") 26 | for (i in 1:n) { 27 | # Each time through the loop, add another row of data. This is 28 | # a stand-in for a long-running computation. 29 | dat <- rbind(dat, data.frame(x = rnorm(1), y = rnorm(1))) 30 | 31 | cli_progress_update(status = paste("Doing part", i)) 32 | 33 | # Pause for 0.1 seconds to simulate a long computation. 34 | Sys.sleep(0.5) 35 | } 36 | 37 | plot(dat$x, dat$y) 38 | }) 39 | } 40 | 41 | ui <- shinyUI(basicPage( 42 | plotOutput('plot', width = "300px", height = "300px"), 43 | actionButton('goPlot', 'Go plot') 44 | )) 45 | 46 | shinyApp(ui = ui, server = server) 47 | -------------------------------------------------------------------------------- /R/progress-c.R: -------------------------------------------------------------------------------- 1 | progress_c_update <- function(pb, auto_done = TRUE) { 2 | cli_tick_reset() 3 | 4 | caller <- pb$caller %||% sys.frame(sys.nframe() - 1L) 5 | 6 | pb$tick <- pb$tick + 1L 7 | 8 | if (is.null(pb$format)) { 9 | pb$format <- pb__default_format(pb$type, pb$total) 10 | } 11 | 12 | if ( 13 | pb$auto_terminate && auto_done && !is.na(pb$total) && pb$current == pb$total 14 | ) { 15 | progress_c_done(pb, caller = caller) 16 | return(NULL) 17 | } 18 | 19 | opt <- options(cli__pb = pb) 20 | on.exit(options(opt), add = TRUE) 21 | 22 | handlers <- cli_progress_select_handlers(pb, caller) 23 | if (is.null(pb$added)) { 24 | pb$added <- TRUE 25 | for (h in handlers) { 26 | if ("add" %in% names(h)) h$add(pb, .envir = caller) 27 | } 28 | } 29 | 30 | for (h in handlers) { 31 | if ("set" %in% names(h)) h$set(pb, .envir = caller) 32 | } 33 | 34 | NULL 35 | } 36 | 37 | progress_c_done <- function(pb, caller = NULL) { 38 | if (isTRUE(pb$done)) return() 39 | 40 | caller <- caller %||% pb$caller %||% sys.frame(sys.nframe() - 1L) 41 | 42 | opt <- options(cli__pb = pb) 43 | on.exit(options(opt), add = TRUE) 44 | 45 | handlers <- cli_progress_select_handlers() 46 | for (h in handlers) { 47 | if ("complete" %in% names(h)) { 48 | h$complete(pb, .envir = caller, result = "done") 49 | } 50 | } 51 | 52 | if (!is.null(pb$id)) clienv$progress[[pb$id]] <- NULL 53 | if (!is.null(pb$envkey)) clienv$progress_ids[[pb$envkey]] <- NULL 54 | 55 | pb$done <- TRUE 56 | 57 | NULL 58 | } 59 | -------------------------------------------------------------------------------- /man/figures/README/alert-warning-dark.svg: -------------------------------------------------------------------------------- 1 | !CannotreachGitHub,usinglocaldatabasecache. -------------------------------------------------------------------------------- /man/figures/README/alert-warning.svg: -------------------------------------------------------------------------------- 1 | !CannotreachGitHub,usinglocaldatabasecache. -------------------------------------------------------------------------------- /man/unicode-width-workaround.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/unicode.R 3 | \name{unicode-width-workaround} 4 | \alias{unicode-width-workaround} 5 | \title{Working around the bad Unicode character widths} 6 | \description{ 7 | R 3.6.2 and also the coming 3.6.3 and 4.0.0 versions use the Unicode 8 8 | standard to calculate the display width of Unicode characters. 9 | Unfortunately the widths of most emojis are incorrect in this standard, 10 | and width 1 is reported instead of the correct 2 value. 11 | } 12 | \details{ 13 | cli implements a workaround for this. The package contains a table that 14 | contains all Unicode ranges that have wide characters (display width 2). 15 | 16 | On first use of one of the workaround wrappers (in \code{ansi_nchar()}, etc.) 17 | we check what the current version of R thinks about the width of these 18 | characters, and then create a regex that matches the ones that R 19 | is wrong about (\code{re_bad_char_width}). 20 | 21 | Then we use this regex to duplicate all of the problematic characters 22 | in the input string to the wrapper function, before calling the real 23 | string manipulation function (\code{nchar()}, \code{strwrap()}) etc. At end we 24 | undo the duplication before we return the result. 25 | 26 | This workaround is fine for \code{nchar()} and \code{strwrap()}, and consequently 27 | \code{ansi_align()} and \code{ansi_strtrim()} as well. 28 | 29 | The rest of the \verb{ansi_*()} functions work on characters, and do not 30 | deal with character width. 31 | } 32 | \keyword{internal} 33 | -------------------------------------------------------------------------------- /man/get_spinner.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/spinner.R 3 | \name{get_spinner} 4 | \alias{get_spinner} 5 | \title{Character vector to put a spinner on the screen} 6 | \usage{ 7 | get_spinner(which = NULL) 8 | } 9 | \arguments{ 10 | \item{which}{The name of the chosen spinner. If \code{NULL}, then the default 11 | is used, which can be customized via the \code{cli.spinner_unicode}, 12 | \code{cli.spinner_ascii} and \code{cli.spinner} options. (The latter applies to 13 | both Unicode and ASCII displays. These options can be set to the name 14 | of a built-in spinner, or to a list that has an entry called \code{frames}, 15 | a character vector of frames.} 16 | } 17 | \value{ 18 | A list with entries: \code{name}, \code{interval}: the suggested update 19 | interval in milliseconds and \code{frames}: the character vector of the 20 | spinner's frames. 21 | } 22 | \description{ 23 | \code{cli} contains many different spinners, you choose one according to your 24 | taste. 25 | } 26 | \details{ 27 | \if{html}{\out{
}}\preformatted{options(cli.spinner = "hearts") 28 | fun <- function() \{ 29 | cli_progress_bar("Spinning") 30 | for (i in 1:100) \{ 31 | Sys.sleep(4/100) 32 | cli_progress_update() 33 | \} 34 | \} 35 | fun() 36 | options(cli.spinner = NULL) 37 | }\if{html}{\out{
}} 38 | 39 | \if{html}{\figure{get-spinner.svg}} 40 | } 41 | \seealso{ 42 | Other spinners: 43 | \code{\link{demo_spinners}()}, 44 | \code{\link{list_spinners}()}, 45 | \code{\link{make_spinner}()} 46 | } 47 | \concept{spinners} 48 | -------------------------------------------------------------------------------- /man/combine_ansi_styles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansi.R 3 | \name{combine_ansi_styles} 4 | \alias{combine_ansi_styles} 5 | \title{Combine two or more ANSI styles} 6 | \usage{ 7 | combine_ansi_styles(...) 8 | } 9 | \arguments{ 10 | \item{...}{The styles to combine. For character strings, the 11 | \code{\link[=make_ansi_style]{make_ansi_style()}} function is used to create a style first. 12 | They will be applied from right to left.} 13 | } 14 | \value{ 15 | The combined style function. 16 | } 17 | \description{ 18 | Combine two or more styles or style functions into a new style function 19 | that can be called on strings to style them. 20 | } 21 | \details{ 22 | It does not usually make sense to combine two foreground 23 | colors (or two background colors), because only the first one 24 | applied will be used. 25 | 26 | It does make sense to combine different kind of styles, 27 | e.g. background color, foreground color, bold font. 28 | } 29 | \examples{ 30 | ## Use style names 31 | alert <- combine_ansi_styles("bold", "red4") 32 | cat(alert("Warning!"), "\n") 33 | 34 | ## Or style functions 35 | alert <- combine_ansi_styles(style_bold, col_red, bg_cyan) 36 | cat(alert("Warning!"), "\n") 37 | 38 | ## Combine a composite style 39 | alert <- combine_ansi_styles( 40 | "bold", 41 | combine_ansi_styles("red", bg_cyan)) 42 | cat(alert("Warning!"), "\n") 43 | } 44 | \seealso{ 45 | Other ANSI styling: 46 | \code{\link{ansi-styles}}, 47 | \code{\link{make_ansi_style}()}, 48 | \code{\link{num_ansi_colors}()} 49 | } 50 | \concept{ANSI styling} 51 | -------------------------------------------------------------------------------- /man/cat_line.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cat.R 3 | \name{cat_line} 4 | \alias{cat_line} 5 | \alias{cat_bullet} 6 | \alias{cat_boxx} 7 | \alias{cat_rule} 8 | \alias{cat_print} 9 | \title{\code{cat()} helpers} 10 | \usage{ 11 | cat_line(..., col = NULL, background_col = NULL, file = stdout()) 12 | 13 | cat_bullet( 14 | ..., 15 | col = NULL, 16 | background_col = NULL, 17 | bullet = "bullet", 18 | bullet_col = NULL, 19 | file = stdout() 20 | ) 21 | 22 | cat_boxx(..., file = stdout()) 23 | 24 | cat_rule(..., file = stdout()) 25 | 26 | cat_print(x, file = "") 27 | } 28 | \arguments{ 29 | \item{...}{For \code{cat_line()} and \code{cat_bullet()}, pasted together with 30 | \code{collapse = "\\n"}. For \code{cat_rule()} and \code{cat_boxx()} passed on to 31 | \code{\link[=rule]{rule()}} and \code{\link[=boxx]{boxx()}} respectively.} 32 | 33 | \item{col, background_col, bullet_col}{Colors for text, background, and 34 | bullets respectively.} 35 | 36 | \item{file}{Output destination. Defaults to standard output.} 37 | 38 | \item{bullet}{Name of bullet character. Indexes into \link{symbol}} 39 | 40 | \item{x}{An object to print.} 41 | } 42 | \description{ 43 | These helpers provide useful wrappers around \code{\link[=cat]{cat()}}: most importantly 44 | they all set \code{sep = ""}, and \code{cat_line()} automatically adds a newline. 45 | } 46 | \examples{ 47 | cat_line("This is ", "a ", "line of text.", col = "red") 48 | cat_bullet(letters[1:5]) 49 | cat_bullet(letters[1:5], bullet = "tick", bullet_col = "green") 50 | cat_rule() 51 | } 52 | -------------------------------------------------------------------------------- /man/cli_debug_doc.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/debug.R 3 | \name{cli_debug_doc} 4 | \alias{cli_debug_doc} 5 | \title{Debug cli internals} 6 | \usage{ 7 | cli_debug_doc(app = default_app() \%||\% start_app()) 8 | } 9 | \arguments{ 10 | \item{app}{The cli app to debug. Defaults to the current app. 11 | if there is no app, then it creates one by calling \code{\link[=start_app]{start_app()}}.} 12 | } 13 | \value{ 14 | Data frame with columns: \code{tag}, \code{id}, \code{class} (space separated), 15 | theme (id of the theme the element added), \code{styles} (computed styles 16 | for the element). 17 | } 18 | \description{ 19 | Return the current state of a cli app. It includes the currently 20 | open tags, their ids, classes and their computed styles. 21 | } 22 | \details{ 23 | The returned data frame has a print method, and if you want to create 24 | a plain data frame from it, index it with an empty bracket: 25 | \code{cli_debug_doc()[]}. 26 | 27 | To see all currently active themes, use \code{app$themes}, e.g. for the 28 | default app: \code{default_app()$themes}. 29 | } 30 | \examples{ 31 | \dontrun{ 32 | cli_debug_doc() 33 | 34 | olid <- cli_ol() 35 | cli_li() 36 | cli_debug_doc() 37 | cli_debug_doc()[] 38 | 39 | cli_end(olid) 40 | cli_debug_doc() 41 | } 42 | } 43 | \seealso{ 44 | \code{\link[=cli_sitrep]{cli_sitrep()}}. To debug containers, you can set the 45 | \code{CLI-DEBUG_BAD_END} environment variable to \code{true}, and then cli will 46 | warn when it cannot find the specified container to close (or any 47 | contained at all). 48 | } 49 | -------------------------------------------------------------------------------- /inst/shiny/format/app.R: -------------------------------------------------------------------------------- 1 | # modified from https://shiny.rstudio.com/articles/progress.html 2 | 3 | # This app has a custom format string 4 | 5 | library(cli) 6 | 7 | # !!! You don't need these in real code. 8 | # cli.progress_show_after makes sure that we see the progress bar from 9 | # the beginning, not only after a delay. 10 | options(cli.progress_show_after = 0) 11 | 12 | # !!! You don't need these in real code. 13 | # This also requests logging the progress bar to the standard output, 14 | # in the console. 15 | options(cli.progress_handlers_only = c("shiny", "logger")) 16 | 17 | server <- function(input, output) { 18 | output$plot <- renderPlot({ 19 | input$goPlot # Re-run when button is clicked 20 | 21 | # Create 0-row data frame which will be used to store data 22 | dat <- data.frame(x = numeric(0), y = numeric(0)) 23 | 24 | # Number of times we'll go through the loop 25 | n <- 10 26 | 27 | cli_progress_bar(total = n, "Rendering plot", format = "Starting part {i}") 28 | for (i in 1:n) { 29 | # Each time through the loop, add another row of data. This is 30 | # a stand-in for a long-running computation. 31 | dat <- rbind(dat, data.frame(x = rnorm(1), y = rnorm(1))) 32 | 33 | cli_progress_update() 34 | 35 | # Pause for 0.1 seconds to simulate a long computation. 36 | Sys.sleep(0.5) 37 | } 38 | 39 | plot(dat$x, dat$y) 40 | }) 41 | } 42 | 43 | ui <- shinyUI(basicPage( 44 | plotOutput('plot', width = "300px", height = "300px"), 45 | actionButton('goPlot', 'Go plot') 46 | )) 47 | 48 | shinyApp(ui = ui, server = server) 49 | -------------------------------------------------------------------------------- /man/style_hyperlink.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansi-hyperlink.R 3 | \name{style_hyperlink} 4 | \alias{style_hyperlink} 5 | \alias{ansi_has_hyperlink_support} 6 | \alias{ansi_hyperlink_types} 7 | \title{Terminal Hyperlinks} 8 | \usage{ 9 | style_hyperlink(text, url, params = NULL) 10 | 11 | ansi_has_hyperlink_support() 12 | 13 | ansi_hyperlink_types() 14 | } 15 | \arguments{ 16 | \item{text}{Text to show. \code{text} and \code{url} are recycled to match their 17 | length, via a \code{paste0()} call.} 18 | 19 | \item{url}{URL to link to.} 20 | 21 | \item{params}{A named character vector of additional parameters, or \code{NULL}.} 22 | } 23 | \value{ 24 | Styled \code{cli_ansi_string} for \code{style_hyperlink()}. 25 | Logical scalar for \code{ansi_has_hyperlink_support()}. 26 | } 27 | \description{ 28 | \code{ansi_hyperlink()} creates an ANSI hyperlink. 29 | } 30 | \details{ 31 | This function is currently experimental. In particular, many of the 32 | \verb{ansi_*()} functions do not support it properly. 33 | 34 | \code{ansi_has_hyperlink_support()} checks if the current \code{stdout()} 35 | supports hyperlinks. 36 | 37 | See also 38 | \url{https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda}. 39 | 40 | \code{ansi_hyperlink_types()} checks if current \code{stdout()} supports various 41 | types of hyperlinks. It returns a list with entries \code{href}, \code{run}, 42 | \code{help} and \code{vignettes}. 43 | } 44 | \examples{ 45 | cat("This is an", style_hyperlink("R", "https://r-project.org"), "link.\n") 46 | ansi_has_hyperlink_support() 47 | } 48 | -------------------------------------------------------------------------------- /inst/shiny/output/app.R: -------------------------------------------------------------------------------- 1 | # modified from https://shiny.rstudio.com/articles/progress.html 2 | 3 | library(cli) 4 | 5 | # !!! You don't need these in real code. 6 | # cli.progress_show_after makes sure that we see the progress bar from 7 | # the beginning, not only after a delay. 8 | options(cli.progress_show_after = 0) 9 | 10 | # !!! You don't need this in real code. 11 | options(cli.progress_handlers_only = "shiny") 12 | 13 | server <- function(input, output) { 14 | output$plot <- renderPlot({ 15 | input$goPlot # Re-run when button is clicked 16 | 17 | # Create 0-row data frame which will be used to store data 18 | dat <- data.frame(x = numeric(0), y = numeric(0)) 19 | 20 | # Number of times we'll go through the loop 21 | n <- 10 22 | 23 | cli_progress_bar(total = n, "Rendering plot") 24 | for (i in 1:n) { 25 | # Each time through the loop, add another row of data. This is 26 | # a stand-in for a long-running computation. 27 | dat <- rbind(dat, data.frame(x = rnorm(1), y = rnorm(1))) 28 | 29 | cli_progress_update(status = paste("Doing part", i)) 30 | 31 | # Pause for 0.5 seconds to simulate a long computation. 32 | # Produce some extra output 33 | Sys.sleep(0.5) 34 | msg <- paste(strwrap(cli:::lorem_ipsum(1, 1)), collapse = "\n") 35 | if (i != n) cli_progress_output(msg) 36 | Sys.sleep(0.5) 37 | } 38 | 39 | plot(dat$x, dat$y) 40 | }) 41 | } 42 | 43 | ui <- shinyUI(basicPage( 44 | plotOutput('plot', width = "300px", height = "300px"), 45 | actionButton('goPlot', 'Go plot') 46 | )) 47 | 48 | shinyApp(ui = ui, server = server) 49 | -------------------------------------------------------------------------------- /src/cleancall.h: -------------------------------------------------------------------------------- 1 | #ifndef CLEANCALL_H 2 | #define CLEANCALL_H 3 | 4 | #include 5 | #include 6 | 7 | // -------------------------------------------------------------------- 8 | // Internals 9 | // -------------------------------------------------------------------- 10 | 11 | typedef union {void* p; DL_FUNC fn;} fn_ptr; 12 | 13 | #if (defined(R_VERSION) && R_VERSION < R_Version(3, 4, 0)) 14 | SEXP R_MakeExternalPtrFn(DL_FUNC p, SEXP tag, SEXP prot); 15 | DL_FUNC R_ExternalPtrAddrFn(SEXP s); 16 | #endif 17 | 18 | // -------------------------------------------------------------------- 19 | // API for packages that embed cleancall 20 | // -------------------------------------------------------------------- 21 | 22 | // The R API does not have a setter for external function pointers 23 | SEXP cleancall_MakeExternalPtrFn(DL_FUNC p, SEXP tag, SEXP prot); 24 | void cleancall_SetExternalPtrAddrFn(SEXP s, DL_FUNC p); 25 | 26 | #define CLEANCALL_METHOD_RECORD \ 27 | {"cleancall_call", (DL_FUNC) &cleancall_call, 2} 28 | 29 | SEXP cleancall_call(SEXP args, SEXP env); 30 | extern SEXP cleancall_fns_dot_call; 31 | void cleancall_init(void); 32 | 33 | // -------------------------------------------------------------------- 34 | // Public API 35 | // -------------------------------------------------------------------- 36 | 37 | #define R_CLEANCALL_SUPPORT 1 38 | 39 | SEXP r_with_cleanup_context(SEXP (*fn)(void* data), void* data); 40 | void r_call_on_exit(void (*fn)(void* data), void* data); 41 | void r_call_on_early_exit(void (*fn)(void* data), void* data); 42 | int r_cleancall_is_active(void); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /man/vt_output.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vt.R 3 | \name{vt_output} 4 | \alias{vt_output} 5 | \title{Simulate (a subset of) a VT-5xx ANSI terminal} 6 | \usage{ 7 | vt_output(output, width = 80L, height = 25L) 8 | } 9 | \arguments{ 10 | \item{output}{Character vector or raw vector. Character vectors are 11 | collapsed (without a separator), and converted to a raw vector using 12 | \code{\link[base:rawConversion]{base::charToRaw()}}.} 13 | 14 | \item{width}{Terminal width.} 15 | 16 | \item{height}{Terminal height.} 17 | } 18 | \value{ 19 | Data frame with columns \code{lineno}, \code{segmentno}, \code{segment}, 20 | \code{attributes}. 21 | } 22 | \description{ 23 | This is utility function that calculates the state of a VT-5xx screen 24 | after a certain set of output. 25 | } 26 | \details{ 27 | Currently it supports: 28 | \itemize{ 29 | \item configurable terminal width and height 30 | \item ASCII printable characters. 31 | \item \verb{\\n}, \verb{\\r}. 32 | \item ANSI SGR colors, 8 color mode, 256 color mode and true color mode. 33 | \item Other ANSI SGR features: bold, italic, underline, strikethrough, 34 | blink, inverse. 35 | } 36 | 37 | It does \emph{not} currently supports other features, mode notably: 38 | \itemize{ 39 | \item Other ANSI control sequences and features. Other control sequences 40 | are silently ignored. 41 | \item Wide Unicode characters. Their width is not taken into account 42 | correctly. 43 | \item Unicode graphemes. 44 | } 45 | } 46 | \note{ 47 | This function is experimental, and the virtual terminal API will 48 | likely change in future versions of cli. 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/rhel.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | name: rhel.yaml 5 | 6 | permissions: read-all 7 | 8 | jobs: 9 | rhel: 10 | runs-on: ubuntu-latest 11 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | config: 16 | # cannot run the nodejs actions because RHEL7 cannot run node20 17 | # - { os: rhel7, r: release, key: REDHAT_ACTIVATION_KEY_RHEL7 } 18 | - { os: rhel8, r: release, key: REDHAT_ACTIVATION_KEY_RHEL8 } 19 | - { os: rhel9, r: release, key: REDHAT_ACTIVATION_KEY_RHEL9 } 20 | container: 21 | image: ghcr.io/r-hub/containers/${{ matrix.config.os }}:latest 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Register 27 | run: | 28 | subscription-manager register \ 29 | --org ${{ secrets.REDHAT_ORG }} \ 30 | --activationkey ${{ secrets[matrix.config.key] }} 31 | shell: bash 32 | 33 | - name: Install R 34 | if: ${{ matrix.config.r != 'release' }} 35 | run: | 36 | rig add ${{ matrix.config.r }} 37 | shell: bash 38 | 39 | - uses: r-lib/actions/setup-r-dependencies@v2 40 | with: 41 | extra-packages: any::rcmdcheck 42 | needs: check 43 | 44 | - uses: r-lib/actions/check-r-package@v2 45 | with: 46 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 47 | env: 48 | NOT_CRAN: true 49 | 50 | - name: Unregister 51 | if: always() 52 | run: | 53 | subscription-manager unregister || true 54 | -------------------------------------------------------------------------------- /man/console_width.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/width.R 3 | \name{console_width} 4 | \alias{console_width} 5 | \title{Determine the width of the console} 6 | \usage{ 7 | console_width() 8 | } 9 | \value{ 10 | Integer scalar, the console with, in number of characters. 11 | } 12 | \description{ 13 | It uses the \code{cli.width} option, if set. Otherwise it tries to 14 | determine the size of the terminal or console window. 15 | } 16 | \details{ 17 | These are the exact rules: 18 | \itemize{ 19 | \item If the \code{cli.width} option is set to a positive integer, it is used. 20 | \item If the \code{cli.width} option is set, but it is not a positive integer, 21 | and error is thrown. 22 | } 23 | 24 | Then we try to determine the size of the terminal or console window: 25 | \itemize{ 26 | \item If we are not in RStudio, or we are in an RStudio terminal, 27 | then we try to use the \code{tty_size()} function to query the 28 | terminal size. This might fail if R is not running in a terminal, 29 | but failures are ignored. 30 | \item If we are in the RStudio build pane, then the \code{RSTUDIO_CONSOLE_WIDTH} 31 | environment variable is used. If the build pane is resized, then this 32 | environment variable is not accurate any more, and the output might 33 | get garbled. 34 | \item We are \emph{not} using the \code{RSTUDIO_CONSOLE_WIDTH} environment variable 35 | if we are in the RStudio console. 36 | } 37 | 38 | If we cannot determine the size of the terminal or console window, then 39 | we use the \code{width} option. If the \code{width} option is not set, then 40 | we return 80L. 41 | } 42 | \examples{ 43 | console_width() 44 | } 45 | -------------------------------------------------------------------------------- /man/utf8_nchar.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utf8.R 3 | \name{utf8_nchar} 4 | \alias{utf8_nchar} 5 | \title{Count the number of characters in a character vector} 6 | \usage{ 7 | utf8_nchar(x, type = c("chars", "bytes", "width", "graphemes", "codepoints")) 8 | } 9 | \arguments{ 10 | \item{x}{Character vector, it is converted to UTF-8.} 11 | 12 | \item{type}{Whether to count graphemes (characters), code points, 13 | bytes, or calculate the display width of the string.} 14 | } 15 | \value{ 16 | Numeric vector, the length of the strings in the character 17 | vector. 18 | } 19 | \description{ 20 | By default it counts Unicode grapheme clusters, instead of code points. 21 | } 22 | \examples{ 23 | # Grapheme example, emoji with combining characters. This is a single 24 | # grapheme, consisting of five Unicode code points: 25 | # * `\U0001f477` is the construction worker emoji 26 | # * `\U0001f3fb` is emoji modifier that changes the skin color 27 | # * `\u200d` is the zero width joiner 28 | # * `\u2640` is the female sign 29 | # * `\ufe0f` is variation selector 16, requesting an emoji style glyph 30 | emo <- "\U0001f477\U0001f3fb\u200d\u2640\ufe0f" 31 | cat(emo) 32 | 33 | utf8_nchar(emo, "chars") # = graphemes 34 | utf8_nchar(emo, "bytes") 35 | utf8_nchar(emo, "width") 36 | utf8_nchar(emo, "codepoints") 37 | 38 | # For comparison, the output for width depends on the R version used: 39 | nchar(emo, "chars") 40 | nchar(emo, "bytes") 41 | nchar(emo, "width") 42 | } 43 | \seealso{ 44 | Other UTF-8 string manipulation: 45 | \code{\link{utf8_graphemes}()}, 46 | \code{\link{utf8_substr}()} 47 | } 48 | \concept{UTF-8 string manipulation} 49 | -------------------------------------------------------------------------------- /tools/ansi-palettes.txt: -------------------------------------------------------------------------------- 1 | black red green yellow blue magenta cyan white br_black br_red br_green br_yellow br_blue br_magenta br_cyan br_white 2 | dichro #000000 #882255 #117733 #ddcc77 #332288 #aa4499 #88ccee #e5e5e5 #000000 #cc6677 #999933 #ddcc77 #44aa99 #aa4499 #88ccee #ffffff 3 | vga #000000 #aa0000 #00aa00 #aa5500 #0000aa #aa00aa #00aaaa #aaaaaa #555555 #ff5555 #55ff55 #ffff55 #5555ff #ff55ff #55ffff #ffffff 4 | winxp #000000 #800000 #008000 #808000 #000080 #800080 #008080 #c0c0c0 #808080 #ff0000 #00ff00 #ffff00 #0000ff #ff00ff #00ffff #ffffff 5 | vscode #000000 #cd3131 #0dbc79 #e5e510 #2472c8 #bc3fbc #11a8cd #e5e5e5 #666666 #f14c4c #23d18b #f5f543 #3b8eea #d670d6 #29b8db #e5e5e5 6 | win10 #0c0c0c #c50f1f #13a10e #c19c00 #0037da #881798 #3a96dd #cccccc #767676 #e74856 #16c60c #f9f1a5 #3b78ff #b4009e #61d6d6 #f2f2f2 7 | macos #000000 #c23621 #25bc24 #adad27 #492ee1 #d338d3 #33bbc8 #cbcccd #818383 #fc391f #31e722 #eaec23 #5833ff #f935f8 #14f0f0 #e9ebeb 8 | putty #000000 #bb0000 #00bb00 #bbbb00 #0000bb #bb00bb #00bbbb #bbbbbb #555555 #ff5555 #55ff55 #ffff55 #5555ff #ff55ff #55ffff #ffffff 9 | mirc #000000 #7f0000 #009300 #fc7f00 #00007f #9c009c #009393 #d2d2d2 #7f7f7f #ff0000 #00fc00 #ffff00 #0000fc #ff00ff #00ffff #ffffff 10 | xterm #000000 #cd0000 #00cd00 #cdcd00 #0000ee #cd00cd #00cdcd #e5e5e5 #7f7f7f #ff0000 #00ff00 #ffff00 #5c5cff #ff00ff #00ffff #ffffff 11 | ubuntu #010101 #de382b #39b54a #ffc706 #006fb8 #762671 #2cb5e9 #cccccc #808080 #ff0000 #00ff00 #ffff00 #0000ff #ff00ff #00ffff #ffffff 12 | eclipse #000000 #cd0000 #00cd00 #cdcd00 #0000ee #cd00cd #00cdcd #e5e5e5 #000000 #ff0000 #00ff00 #ffff00 #5c5cff #ff00ff #00ffff #ffffff 13 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/box-styles.md: -------------------------------------------------------------------------------- 1 | # list_border_styles [plain] 2 | 3 | Code 4 | for (st in list_border_styles()) print(boxx("", border_style = st)) 5 | Output 6 | +------+ 7 | | | 8 | | | 9 | | | 10 | +------+ 11 | +------+ 12 | | | 13 | | | 14 | | | 15 | +------+ 16 | +------+ 17 | | | 18 | | | 19 | | | 20 | +------+ 21 | +------+ 22 | | | 23 | | | 24 | | | 25 | +------+ 26 | +------+ 27 | | | 28 | | | 29 | | | 30 | +------+ 31 | +------+ 32 | | | 33 | | | 34 | | | 35 | +------+ 36 | 37 | 38 | 39 | 40 | 41 | 42 | # list_border_styles [unicode] 43 | 44 | Code 45 | for (st in list_border_styles()) print(boxx("", border_style = st)) 46 | Output 47 | ┌──────┐ 48 | │ │ 49 | │ │ 50 | │ │ 51 | └──────┘ 52 | ╔══════╗ 53 | ║ ║ 54 | ║ ║ 55 | ║ ║ 56 | ╚══════╝ 57 | ╭──────╮ 58 | │ │ 59 | │ │ 60 | │ │ 61 | ╰──────╯ 62 | ╓──────╖ 63 | ║ ║ 64 | ║ ║ 65 | ║ ║ 66 | ╙──────╜ 67 | ╒══════╕ 68 | │ │ 69 | │ │ 70 | │ │ 71 | ╘══════╛ 72 | +------+ 73 | | | 74 | | | 75 | | | 76 | +------+ 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/utf8/utf8-output.txt: -------------------------------------------------------------------------------- 1 | 2 | ── Alerts ──────────────────────────────────────────────────────────── 3 | → マルチバイトのタイトル 4 | ✖ árvíztűrő tükörfúrógép 5 | ℹ マルチバイトのタイトル 6 | ✔ マルチバイトのタイトル 7 | ! マルチバイトのタイトル 8 | 9 | ── Block quote ─────────────────────────────────────────────────────── 10 | 11 | “マルチバイトのタイトル” 12 | — árvíztűrő tükörfúrógép 13 | 14 | ── Bullets ─────────────────────────────────────────────────────────── 15 | • マルチバイトのタイトル 16 | ! árvíztűrő tükörfúrógép 17 | 18 | ── Code ────────────────────────────────────────────────────────────── 19 | マルチバイトのタイトル 20 | árvíztűrő tükörfúrógép 21 | 22 | ── Lists ───────────────────────────────────────────────────────────── 23 | s1: マルチバイトのタイトル 24 | s2: árvíztűrő tükörfúrógép 25 | 1. マルチバイトのタイトル 26 | 2. árvíztűrő tükörfúrógép 27 | • マルチバイトのタイトル 28 | • árvíztűrő tükörfúrógép 29 | 30 | ── Headers ─────────────────────────────────────────────────────────── 31 | 32 | ── マルチバイトのタイトル ──────────────────────────────────────────── 33 | 34 | ── árvíztűrő tükörfúrógép ── 35 | 36 | ── マルチバイトのタイトル 37 | 38 | ── Progress bars ───────────────────────────────────────────────────── 39 | ℹ マルチバイトのタイトル 40 | ✔ マルチバイトのタイトル [1s] 41 | 42 | ℹ árvíztűrő tükörfúrógép 43 | ✔ árvíztűrő tükörfúrógép [1s] 44 | 45 | 46 | ── Text ────────────────────────────────────────────────────────────── 47 | マルチバイトのタイトル árvíztűrő tükörfúrógép マルチバイトのタイトル 48 | árvíztűrő tükörfúrógép マルチバイトのタイトル マルチバイトのタイトル 49 | マルチバイトのタイトル マルチバイトのタイトル árvíztűrő tükörfúrógép 50 | マルチバイトのタイトル árvíztűrő tükörfúrógép マルチバイトのタイトル 51 | マルチバイトのタイトル マルチバイトのタイトル マルチバイトのタイトル 52 | 53 | ── Verbatim ────────────────────────────────────────────────────────── 54 | マルチバイトのタイトル 55 | árvíztűrő tükörfúrógép 56 | -------------------------------------------------------------------------------- /man/ansi_html_style.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_html_style} 4 | \alias{ansi_html_style} 5 | \title{CSS styles for the output of \code{ansi_html()}} 6 | \usage{ 7 | ansi_html_style( 8 | colors = TRUE, 9 | palette = c("vscode", "dichro", "vga", "winxp", "win10", "macos", "putty", "mirc", 10 | "xterm", "ubuntu", "eclipse", "iterm", "iterm-pastel", "iterm-smoooooth", 11 | "iterm-snazzy", "iterm-solarized", "iterm-tango") 12 | ) 13 | } 14 | \arguments{ 15 | \item{colors}{Whether or not to include colors. \code{FALSE} will not include 16 | colors, \code{TRUE} or \code{8} will include eight colors (plus their bright 17 | variants), \code{256} will include 256 colors.} 18 | 19 | \item{palette}{Character scalar, palette to use for the first eight colors 20 | plus their bright variants. Terminals define these colors differently, 21 | and cli includes a couple of examples. Sources of palettes: 22 | \itemize{ 23 | \item https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit 24 | \item iTerm2 builtin palettes 25 | \item \url{https://github.com/sindresorhus/iterm2-snazzy} 26 | }} 27 | } 28 | \value{ 29 | Named list of CSS declaration blocks, where the names are 30 | CSS selectors. It has a \code{format()} and \code{print()} methods, which you 31 | can use to write the output to a CSS or HTML file. 32 | } 33 | \description{ 34 | CSS styles for the output of \code{ansi_html()} 35 | } 36 | \examples{ 37 | ansi_html_style(colors = FALSE) 38 | ansi_html_style(colors = 8, palette = "iterm-snazzy") 39 | } 40 | \seealso{ 41 | Other ANSI to HTML conversion: 42 | \code{\link{ansi_html}()} 43 | } 44 | \concept{ANSI to HTML conversion} 45 | -------------------------------------------------------------------------------- /man/figures/README/themes-dark.svg: -------------------------------------------------------------------------------- 1 | ThisisveryimportantBacktotheprevioustheme -------------------------------------------------------------------------------- /man/figures/README/themes.svg: -------------------------------------------------------------------------------- 1 | ThisisveryimportantBacktotheprevioustheme -------------------------------------------------------------------------------- /man/figures/README/lists.svg: -------------------------------------------------------------------------------- 1 | 1.Item1Subitem1Subitem22.Item2 -------------------------------------------------------------------------------- /tests/testthat/test-ansi-utils.R: -------------------------------------------------------------------------------- 1 | test_that("re_table", { 2 | withr::local_options( 3 | cli.num_colors = 256, 4 | cli.hyperlink = TRUE 5 | ) 6 | txt <- paste0( 7 | "this is some text ", 8 | col_red("red"), 9 | " some more text ", 10 | col_green("green"), 11 | " then some ", 12 | style_hyperlink("text", "https://example.com") 13 | ) 14 | tbl <- re_table(ansi_regex(), txt)[[1]] 15 | tbl2 <- cbind(tbl, c(text = substring(txt, tbl[, "start"], tbl[, "end"]))) 16 | expect_snapshot(tbl2) 17 | expect_snapshot(non_matching(list(tbl), txt)) 18 | }) 19 | 20 | test_that("re_table special cases", { 21 | withr::local_options( 22 | cli.num_colors = 256, 23 | cli.hyperlink = TRUE 24 | ) 25 | txt <- "foobar" 26 | tbl <- re_table(ansi_regex(), txt)[[1]] 27 | expect_snapshot(tbl) 28 | expect_snapshot(non_matching(list(tbl), txt)) 29 | expect_snapshot(non_matching(list(tbl), txt, empty = TRUE)) 30 | 31 | txt <- col_red("foobar") 32 | tbl <- re_table(ansi_regex(), txt)[[1]] 33 | expect_snapshot(tbl) 34 | expect_snapshot(non_matching(list(tbl), txt)) 35 | expect_snapshot(non_matching(list(tbl), txt, empty = TRUE)) 36 | 37 | txt <- paste0("foo ", col_red(""), " bar") 38 | tbl <- re_table(ansi_regex(), txt)[[1]] 39 | expect_snapshot(tbl) 40 | expect_snapshot(non_matching(list(tbl), txt)) 41 | expect_snapshot(non_matching(list(tbl), txt, empty = TRUE)) 42 | }) 43 | 44 | test_that("myseq", { 45 | expect_snapshot({ 46 | myseq(1, 5) 47 | myseq(1, 1) 48 | myseq(1, 0) 49 | myseq(1, 5, 2) 50 | myseq(1, 6, 2) 51 | myseq(1, 1, 2) 52 | myseq(1, 2, -1) 53 | myseq(10, 1, -1) 54 | myseq(10, 1, -2) 55 | myseq(1, 5, -2) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /man/figures/README/lists-dark.svg: -------------------------------------------------------------------------------- 1 | 1.Item1Subitem1Subitem22.Item2 -------------------------------------------------------------------------------- /tests/testthat/_snaps/progress-message.md: -------------------------------------------------------------------------------- 1 | # cli_progress_message 2 | 3 | Code 4 | capture_cli_messages(fun()) 5 | Output 6 | [1] "Simplest progress 'bar', `fn()` 2 twos\n" 7 | 8 | # cli_progress_message error 9 | 10 | Code 11 | callr::r(fun, stdout = outfile, stderr = outfile) 12 | Condition 13 | Error: 14 | ! in callr subprocess. 15 | Caused by error: 16 | ! oopsie 17 | 18 | --- 19 | 20 | Code 21 | readLines(outfile) 22 | Output 23 | [1] "Simplest progress 'bar', `fn()` 2 twos" 24 | [2] "Error in (function () : oopsie" 25 | 26 | --- 27 | 28 | Code 29 | callr::r(fun2, stdout = outfile, stderr = outfile) 30 | Condition 31 | Error: 32 | ! in callr subprocess. 33 | Caused by error: 34 | ! oopsie 35 | 36 | --- 37 | 38 | Code 39 | win2unix(out) 40 | Output 41 | [1] "\033[?25l\rSimplest progress 'bar', `fn()` 2 twos\033[K\r\r\033[K\033[?25hError in (function () : oopsie\n" 42 | 43 | # cli_progress_step 44 | 45 | Code 46 | msgs 47 | Output 48 | [1] "\ri First step\033[K\r" "\rv First step [1s]\033[K\r" 49 | [3] "\n" "\ri Second step\033[K\r" 50 | [5] "\rv Second step [1s]\033[K\r" "\n" 51 | 52 | # cli_progress_step error 53 | 54 | Code 55 | callr::r(fun, stdout = outfile, stderr = "2>&1") 56 | Condition 57 | Error: 58 | ! in callr subprocess. 59 | Caused by error: 60 | ! oopsie 61 | 62 | --- 63 | 64 | Code 65 | win2unix(out) 66 | Output 67 | [1] "i First step\nv First step [1s]\n\ni Second step\nx Second step [1s]\n\nError in (function () : oopsie\n" 68 | 69 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | 11 | name: pkgdown.yaml 12 | 13 | permissions: read-all 14 | 15 | jobs: 16 | pkgdown: 17 | runs-on: ubuntu-latest 18 | # Only restrict concurrency for non-PR jobs 19 | concurrency: 20 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 21 | env: 22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 23 | R_CLI_NUM_COLORS: 256 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: | 38 | any::pkgdown 39 | local::. 40 | local::./tests/testthat/progresstest 41 | needs: website 42 | 43 | - name: Build site 44 | run: | 45 | rmarkdown::render("README.Rmd") 46 | pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 47 | shell: Rscript {0} 48 | env: 49 | IN_PKGDOWN: true 50 | 51 | - name: Deploy to GitHub pages 🚀 52 | if: github.event_name != 'pull_request' 53 | uses: JamesIves/github-pages-deploy-action@v4.5.0 54 | with: 55 | clean: false 56 | branch: gh-pages 57 | folder: docs 58 | -------------------------------------------------------------------------------- /tests/testthat/progresstest/src/cleancall.h: -------------------------------------------------------------------------------- 1 | #ifndef CLEANCALL_H 2 | #define CLEANCALL_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | // -------------------------------------------------------------------- 12 | // Internals 13 | // -------------------------------------------------------------------- 14 | 15 | typedef union {void* p; DL_FUNC fn;} fn_ptr; 16 | 17 | #if (defined(R_VERSION) && R_VERSION < R_Version(3, 4, 0)) 18 | SEXP R_MakeExternalPtrFn(DL_FUNC p, SEXP tag, SEXP prot); 19 | DL_FUNC R_ExternalPtrAddrFn(SEXP s); 20 | #endif 21 | 22 | // -------------------------------------------------------------------- 23 | // API for packages that embed cleancall 24 | // -------------------------------------------------------------------- 25 | 26 | // The R API does not have a setter for external function pointers 27 | SEXP cleancall_MakeExternalPtrFn(DL_FUNC p, SEXP tag, SEXP prot); 28 | void cleancall_SetExternalPtrAddrFn(SEXP s, DL_FUNC p); 29 | 30 | #define CLEANCALL_METHOD_RECORD \ 31 | {"cleancall_call", (DL_FUNC) &cleancall_call, 2} 32 | 33 | SEXP cleancall_call(SEXP args, SEXP env); 34 | extern SEXP cleancall_fns_dot_call; 35 | void cleancall_init(); 36 | 37 | // -------------------------------------------------------------------- 38 | // Public API 39 | // -------------------------------------------------------------------- 40 | 41 | #define R_CLEANCALL_SUPPORT 1 42 | 43 | SEXP r_with_cleanup_context(SEXP (*fn)(void* data), void* data); 44 | void r_call_on_exit(void (*fn)(void* data), void* data); 45 | void r_call_on_early_exit(void (*fn)(void* data), void* data); 46 | int r_cleancall_is_active(); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/ansi-hyperlink.md: -------------------------------------------------------------------------------- 1 | # unknown hyperlink type 2 | 3 | Code 4 | make_link("this", "foobar") 5 | Condition 6 | Error in `match.arg()`: 7 | ! 'arg' should be one of "email", "file", "fun", "help", "href", "run", "topic", "url", "vignette" 8 | 9 | # iterm file links 10 | 11 | Code 12 | cli::cli_text("{.file /path/to/file:10}") 13 | Message 14 | ']8;;file:///path/to/file#10/path/to/file:10]8;;' 15 | Code 16 | cli::cli_text("{.file /path/to/file:10:20}") 17 | Message 18 | ']8;;file:///path/to/file#10:20/path/to/file:10:20]8;;' 19 | 20 | # rstudio links 21 | 22 | Code 23 | cli::cli_text("{.fun pkg::fun}") 24 | Message 25 | `]8;;ide:help:pkg::funpkg::fun]8;;()` 26 | 27 | --- 28 | 29 | Code 30 | cli::cli_text("{.help fun}") 31 | Message 32 | ]8;;ide:help:funfun]8;; 33 | 34 | --- 35 | 36 | Code 37 | cli::cli_text("{.run package::func()}") 38 | Message 39 | ]8;;ide:run:package::func()package::func()]8;; 40 | 41 | --- 42 | 43 | Code 44 | cli::cli_text("{.vignette package::title}") 45 | Message 46 | ]8;;ide:vignette:package::titlepackage::title]8;; 47 | 48 | --- 49 | 50 | Code 51 | cli::cli_text("{.topic pkg::topic}") 52 | Message 53 | ]8;;ide:help:pkg::topicpkg::topic]8;; 54 | 55 | # ST hyperlinks 56 | 57 | Code 58 | cat(style_hyperlink("text", "https://example.com")) 59 | Output 60 | ]8;;https://example.com\text]8;;\ 61 | 62 | # get_config_chr() errors if option is not NULL or string 63 | 64 | Code 65 | get_config_chr("something") 66 | Condition 67 | Error in `get_config_chr()`: 68 | ! is_string(opt) is not TRUE 69 | 70 | -------------------------------------------------------------------------------- /man/ansi_strwrap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_strwrap} 4 | \alias{ansi_strwrap} 5 | \title{Wrap an ANSI styled string to a certain width} 6 | \usage{ 7 | ansi_strwrap( 8 | x, 9 | width = console_width(), 10 | indent = 0, 11 | exdent = 0, 12 | simplify = TRUE 13 | ) 14 | } 15 | \arguments{ 16 | \item{x}{ANSI string.} 17 | 18 | \item{width}{Width to wrap to.} 19 | 20 | \item{indent}{Indentation of the first line of each paragraph.} 21 | 22 | \item{exdent}{Indentation of the subsequent lines of each paragraph.} 23 | 24 | \item{simplify}{Whether to return all wrapped strings in a single 25 | character vector, or wrap each element of \code{x} independently and return 26 | a list.} 27 | } 28 | \value{ 29 | If \code{simplify} is \code{FALSE}, then a list of character vectors, 30 | each an ANSI string. Otherwise a single ANSI string vector. 31 | } 32 | \description{ 33 | This function is similar to \code{\link[base:strwrap]{base::strwrap()}}, but works on ANSI 34 | styled strings, and leaves the styling intact. 35 | } 36 | \examples{ 37 | text <- cli:::lorem_ipsum() 38 | # Highlight some words, that start with 's' 39 | rexp <- gregexpr("\\\\b([sS][a-zA-Z]+)\\\\b", text) 40 | regmatches(text, rexp) <- lapply(regmatches(text, rexp), col_red) 41 | cat(text) 42 | 43 | wrp <- ansi_strwrap(text, width = 40) 44 | cat(wrp, sep = "\n") 45 | } 46 | \seealso{ 47 | Other ANSI string operations: 48 | \code{\link{ansi_align}()}, 49 | \code{\link{ansi_columns}()}, 50 | \code{\link{ansi_nchar}()}, 51 | \code{\link{ansi_strsplit}()}, 52 | \code{\link{ansi_strtrim}()}, 53 | \code{\link{ansi_substr}()}, 54 | \code{\link{ansi_substring}()}, 55 | \code{\link{ansi_toupper}()}, 56 | \code{\link{ansi_trimws}()} 57 | } 58 | \concept{ANSI string operations} 59 | -------------------------------------------------------------------------------- /R/sitrep.R: -------------------------------------------------------------------------------- 1 | #' cli situation report 2 | #' 3 | #' Contains currently: 4 | #' * `cli_unicode_option`: whether the `cli.unicode` option is set and its 5 | #' value. See [is_utf8_output()]. 6 | #' * `symbol_charset`: the selected character set for [symbol], UTF-8, 7 | #' Windows, or ASCII. 8 | #' * `console_utf8`: whether the console supports UTF-8. See 9 | #' [base::l10n_info()]. 10 | #' * `latex_active`: whether we are inside knitr, creating a LaTeX 11 | #' document. 12 | #' * `num_colors`: number of ANSI colors. See [num_ansi_colors()]. 13 | #' * `console_with`: detected console width. 14 | #' 15 | #' @return Named list with entries listed above. It has a `cli_sitrep` 16 | #' class, with a `print()` and `format()` method. 17 | #' 18 | #' @export 19 | #' @examples 20 | #' cli_sitrep() 21 | 22 | cli_sitrep <- function() { 23 | structure( 24 | list( 25 | cli_unicode_option = getOption("cli.unicode", NULL), 26 | symbol_charset = get_active_symbol_set(), 27 | console_utf8 = l10n_info()$`UTF-8`, 28 | latex_active = is_latex_output(), 29 | num_colors = num_ansi_colors(), 30 | console_width = console_width() 31 | ), 32 | class = "cli_sitrep" 33 | ) 34 | } 35 | 36 | #' @export 37 | 38 | print.cli_sitrep <- function(x, ...) { 39 | cat(format(x, ...), sep = "\n") 40 | invisible(x) 41 | } 42 | 43 | get_active_symbol_set <- function() { 44 | if (identical(symbol, symbol_utf8)) { 45 | "UTF-8" 46 | } else { 47 | "ASCII (non UTF-8)" 48 | } 49 | } 50 | 51 | #' @export 52 | 53 | format.cli_sitrep <- function(x, ...) { 54 | fmt_names <- format(names(x)) 55 | fmt_vals <- vapply(x, format, character(1)) 56 | paste0("- ", fmt_names, " : ", fmt_vals) 57 | } 58 | 59 | #' @export 60 | 61 | as.character.cli_sitrep <- function(x, ...) { 62 | "" 63 | } 64 | -------------------------------------------------------------------------------- /man/hash_md5.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hash.R 3 | \name{hash_md5} 4 | \alias{hash_md5} 5 | \alias{hash_raw_md5} 6 | \alias{hash_obj_md5} 7 | \alias{hash_file_md5} 8 | \title{MD5 hash} 9 | \usage{ 10 | hash_md5(x) 11 | 12 | hash_raw_md5(x) 13 | 14 | hash_obj_md5(x, serialize_version = 2) 15 | 16 | hash_file_md5(paths) 17 | } 18 | \arguments{ 19 | \item{x}{Character vector. If not a character vector, then 20 | \code{\link[=as.character]{as.character()}} is used to try to coerce it into one. \code{NA} entries 21 | will have an \code{NA} hash.} 22 | 23 | \item{serialize_version}{Workspace format version to use, see 24 | \code{\link[base:serialize]{base::serialize()}}.} 25 | 26 | \item{paths}{Character vector of file names.} 27 | } 28 | \value{ 29 | \code{hash_md5()} returns a character vector of hexadecimal MD5 30 | hashes. 31 | 32 | \code{hash_raw_md5()} returns a character scalar. 33 | 34 | \code{hash_obj_md5()} returns a character scalar. 35 | } 36 | \description{ 37 | Calculate the MD5 hash of each element of a character vector. 38 | } 39 | \details{ 40 | \code{hash_raw_md5()} calculates the MD5 hash of the bytes 41 | of a raw vector. 42 | 43 | \code{hash_obj_md5()} calculates the MD5 hash of an R 44 | object. The object is serialized into a binary vector first. 45 | 46 | \code{hash_file_md5()} calculates the MD5 hash of one or more 47 | files. 48 | } 49 | \examples{ 50 | hash_md5(c("foo", NA, "bar", "")) 51 | } 52 | \seealso{ 53 | \code{\link[tools:md5sum]{tools::md5sum()}} for a base R MD5 function that works on 54 | files. 55 | 56 | Other hash functions: 57 | \code{\link{hash_animal}()}, 58 | \code{\link{hash_emoji}()}, 59 | \code{\link{hash_sha1}()}, 60 | \code{\link{hash_sha256}()}, 61 | \code{\link{hash_xxhash}()} 62 | } 63 | \concept{hash functions} 64 | -------------------------------------------------------------------------------- /man/hash_sha1.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hash.R 3 | \name{hash_sha1} 4 | \alias{hash_sha1} 5 | \alias{hash_raw_sha1} 6 | \alias{hash_obj_sha1} 7 | \alias{hash_file_sha1} 8 | \title{SHA-1 hash} 9 | \usage{ 10 | hash_sha1(x) 11 | 12 | hash_raw_sha1(x) 13 | 14 | hash_obj_sha1(x, serialize_version = 2) 15 | 16 | hash_file_sha1(paths) 17 | } 18 | \arguments{ 19 | \item{x}{Character vector. If not a character vector, then 20 | \code{\link[=as.character]{as.character()}} is used to try to coerce it into one. \code{NA} entries 21 | will have an \code{NA} hash.} 22 | 23 | \item{serialize_version}{Workspace format version to use, see 24 | \code{\link[base:serialize]{base::serialize()}}.} 25 | 26 | \item{paths}{Character vector of file names.} 27 | } 28 | \value{ 29 | \code{hash_sha1()} returns a character vector of hexadecimal 30 | SHA-1 hashes. 31 | 32 | \code{hash_raw_sha1()} returns a character scalar. 33 | 34 | \code{hash_obj_sha1()} returns a character scalar. 35 | 36 | \code{hash_file_sha1()} returns a character vector of SHA-1 37 | hashes. 38 | } 39 | \description{ 40 | Calculate the SHA-1 hash of each element of a character vector. 41 | } 42 | \details{ 43 | \code{hash_raw_sha1()} calculates the SHA-1 hash of the bytes 44 | of a raw vector. 45 | 46 | \code{hash_obj_sha1()} calculates the SHA-1 hash of an R 47 | object. The object is serialized into a binary vector first. 48 | 49 | \code{hash_file_sha1()} calculates the SHA-1 hash of one or 50 | more files. 51 | } 52 | \examples{ 53 | hash_sha1(c("foo", NA, "bar", "")) 54 | } 55 | \seealso{ 56 | Other hash functions: 57 | \code{\link{hash_animal}()}, 58 | \code{\link{hash_emoji}()}, 59 | \code{\link{hash_md5}()}, 60 | \code{\link{hash_sha256}()}, 61 | \code{\link{hash_xxhash}()} 62 | } 63 | \concept{hash functions} 64 | -------------------------------------------------------------------------------- /man/ansi_grep.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ansiex.R 3 | \name{ansi_grep} 4 | \alias{ansi_grep} 5 | \alias{ansi_grepl} 6 | \title{Like \code{\link[base:grep]{base::grep()}} and \code{\link[base:grep]{base::grepl()}}, but for ANSI strings} 7 | \usage{ 8 | ansi_grep(pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE, ...) 9 | 10 | ansi_grepl(pattern, x, ...) 11 | } 12 | \arguments{ 13 | \item{pattern}{Character scalar, regular expression or fixed string 14 | (if \code{fixed = TRUE}), the pattern to search for. Other objects will be 15 | coerced using \code{\link[=as.character]{as.character()}}.} 16 | 17 | \item{x}{Character vector to search in. Other objects will be coerced 18 | using \code{\link[=as.character]{as.character()}}.} 19 | 20 | \item{ignore.case, perl, value}{Passed to \code{\link[base:grep]{base::grep()}}.} 21 | 22 | \item{...}{Extra arguments are passed to \code{\link[base:grep]{base::grep()}} or \code{\link[base:grep]{base::grepl()}}.} 23 | } 24 | \value{ 25 | The same as \code{\link[base:grep]{base::grep()}} and \code{\link[base:grep]{base::grepl()}}, respectively. 26 | } 27 | \description{ 28 | First ANSI sequences will be stripped with \code{\link[=ansi_strip]{ansi_strip()}}, both 29 | } 30 | \details{ 31 | Note that these functions work on code points (or bytes if 32 | \code{useBytes = TRUE}), and not graphemes. 33 | 34 | Unlike \code{\link[base:grep]{base::grep()}} and \code{\link[base:grep]{base::grepl()}} these functions do not special 35 | case factors. 36 | 37 | Both \code{pattern} and \code{x} are converted to UTF-8. 38 | } 39 | \examples{ 40 | red_needle <- col_red("needle") 41 | haystack <- c("foo", "needle", "foo") 42 | green_haystack <- col_green(haystack) 43 | ansi_grepl(red_needle, haystack) 44 | ansi_grepl(red_needle, green_haystack) 45 | } 46 | -------------------------------------------------------------------------------- /inst/examples/apps/up.R: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env Rscript 2 | 3 | ## To get the async package: 4 | ## source("https://install-github.me/r-lib/async") 5 | 6 | setup_app <- function() { 7 | theme <- list("url" = list(color = "blue")) 8 | app <- cliapp::start_app(theme = theme, output = "stdout") 9 | } 10 | 11 | load_packages <- function() { 12 | tryCatch( 13 | { 14 | library(cliapp) 15 | library(async) 16 | library(docopt) 17 | }, 18 | error = function(e) { 19 | cli_alert_danger( 20 | "The {.pkg async} and {.pkg docopt} packages are needed!" 21 | ) 22 | q(save = "no", status = 1) 23 | } 24 | ) 25 | } 26 | 27 | up <- function(urls, timeout = 5) { 28 | load_packages() 29 | setup_app() 30 | chk_url <- async(function(url, ...) { 31 | http_head(url, ...)$then(function(res) { 32 | if (res$status_code < 300) { 33 | cli_alert_success("{.url {url}} ({res$times[['total']]}s)") 34 | } else { 35 | cli_alert_danger("{.url {url}} (HTTP {res$status_code})") 36 | } 37 | })$catch(error = function(err) { 38 | e <- if (grepl("timed out", err$message)) "timed out" else "error" 39 | cli_alert_danger("{.url {url}} ({e})") 40 | }) 41 | }) 42 | 43 | invisible(synchronise( 44 | async_map(urls, chk_url, options = list(timeout = timeout)) 45 | )) 46 | } 47 | 48 | parse_arguments <- function() { 49 | "Usage: 50 | up.R [-t timeout] [URLS ...] 51 | up.R -h | --help 52 | 53 | Options: 54 | -t timeout Timeout for giving up on a site, in seconds [default: 5]. 55 | -h --help Print this help message 56 | 57 | Check if web sites are up. 58 | " -> doc 59 | 60 | docopt(doc) 61 | } 62 | 63 | if (is.null(sys.calls())) { 64 | load_packages() 65 | opts <- parse_arguments() 66 | up(opts$URLS, timeout = as.numeric(opts$t)) 67 | } 68 | -------------------------------------------------------------------------------- /exec/up.R: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env Rscript 2 | 3 | ## To get the async package: 4 | ## source("https://install-github.me/r-lib/async") 5 | 6 | setup_app <- function() { 7 | theme <- list("url" = list(color = "blue")) 8 | app <- cli::start_app(theme = theme, output = "stdout") 9 | } 10 | 11 | load_packages <- function() { 12 | tryCatch( 13 | { 14 | library(cli) 15 | library(async) 16 | library(docopt) 17 | }, 18 | error = function(e) { 19 | cli_alert_danger( 20 | "The {.pkg async} and {.pkg docopt} packages are needed!" 21 | ) 22 | q(save = "no", status = 1) 23 | } 24 | ) 25 | } 26 | 27 | up <- function(urls, timeout = 5) { 28 | load_packages() 29 | setup_app() 30 | chk_url <- async(function(url, ...) { 31 | http_head(url, ...)$then(function(res) { 32 | if (res$status_code < 300) { 33 | cli_alert_success("{.url {url}} ({res$times[['total']]}s)") 34 | } else { 35 | cli_alert_danger("{.url {url}} (HTTP {res$status_code})") 36 | } 37 | })$catch(error = function(err) { 38 | e <- if (grepl("timed out", err$message, fixed = TRUE)) "timed out" else 39 | "error" 40 | cli_alert_danger("{.url {url}} ({e})") 41 | }) 42 | }) 43 | 44 | invisible(synchronise( 45 | async_map(urls, chk_url, options = list(timeout = timeout)) 46 | )) 47 | } 48 | 49 | parse_arguments <- function() { 50 | "Usage: 51 | up.R [-t timeout] [URLS ...] 52 | up.R -h | --help 53 | 54 | Options: 55 | -t timeout Timeout for giving up on a site, in seconds [default: 5]. 56 | -h --help Print this help message 57 | 58 | Check if web sites are up. 59 | " -> doc 60 | 61 | docopt(doc) 62 | } 63 | 64 | if (is.null(sys.calls())) { 65 | load_packages() 66 | opts <- parse_arguments() 67 | up(opts$URLS, timeout = as.numeric(opts$t)) 68 | } 69 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/rlang-1.1.0/rlang-errors.md: -------------------------------------------------------------------------------- 1 | # cli_abort() captures correct call and backtrace 2 | 3 | Code 4 | print(expect_error(f())) 5 | Output 6 | 7 | Error in `h()`: 8 | ! foo 9 | --- 10 | Backtrace: 11 | x 12 | 1. +-base::print(expect_error(f())) 13 | 2. +-testthat::expect_error(f()) 14 | 3. | \-testthat:::expect_condition_matching(...) 15 | 4. | \-testthat:::quasi_capture(...) 16 | 5. | +-testthat (local) .capture(...) 17 | 6. | | \-base::withCallingHandlers(...) 18 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 19 | 8. \-cli (local) f() 20 | 9. \-cli (local) g() 21 | 10. \-cli (local) h() 22 | 23 | --- 24 | 25 | Code 26 | print(expect_error(f(list()))) 27 | Output 28 | 29 | Error in `h()`: 30 | ! `x` can't be empty. 31 | --- 32 | Backtrace: 33 | x 34 | 1. +-base::print(expect_error(f(list()))) 35 | 2. +-testthat::expect_error(f(list())) 36 | 3. | \-testthat:::expect_condition_matching(...) 37 | 4. | \-testthat:::quasi_capture(...) 38 | 5. | +-testthat (local) .capture(...) 39 | 6. | | \-base::withCallingHandlers(...) 40 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 41 | 8. \-cli (local) f(list()) 42 | 9. \-cli (local) g(x) 43 | 10. \-cli (local) h(x) 44 | 45 | # cli_abort(.internal = TRUE) reports the correct function (r-lib/rlang#1386) 46 | 47 | Code 48 | (expect_error(fn())) 49 | Output 50 | 51 | Error in `fn()`: 52 | ! Message. 53 | i This is an internal error that was detected in the base package. 54 | 55 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/rlang-1.1.4/rlang-errors.md: -------------------------------------------------------------------------------- 1 | # cli_abort() captures correct call and backtrace 2 | 3 | Code 4 | print(expect_error(f())) 5 | Output 6 | 7 | Error in `h()`: 8 | ! foo 9 | --- 10 | Backtrace: 11 | x 12 | 1. +-base::print(expect_error(f())) 13 | 2. +-testthat::expect_error(f()) 14 | 3. | \-testthat:::expect_condition_matching(...) 15 | 4. | \-testthat:::quasi_capture(...) 16 | 5. | +-testthat (local) .capture(...) 17 | 6. | | \-base::withCallingHandlers(...) 18 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 19 | 8. \-cli (local) f() 20 | 9. \-cli (local) g() 21 | 10. \-cli (local) h() 22 | 23 | --- 24 | 25 | Code 26 | print(expect_error(f(list()))) 27 | Output 28 | 29 | Error in `h()`: 30 | ! `x` can't be empty. 31 | --- 32 | Backtrace: 33 | x 34 | 1. +-base::print(expect_error(f(list()))) 35 | 2. +-testthat::expect_error(f(list())) 36 | 3. | \-testthat:::expect_condition_matching(...) 37 | 4. | \-testthat:::quasi_capture(...) 38 | 5. | +-testthat (local) .capture(...) 39 | 6. | | \-base::withCallingHandlers(...) 40 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 41 | 8. \-cli (local) f(list()) 42 | 9. \-cli (local) g(x) 43 | 10. \-cli (local) h(x) 44 | 45 | # cli_abort(.internal = TRUE) reports the correct function (r-lib/rlang#1386) 46 | 47 | Code 48 | (expect_error(fn())) 49 | Output 50 | 51 | Error in `fn()`: 52 | ! Message. 53 | i This is an internal error that was detected in the base package. 54 | 55 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/rlang-1.1.6/rlang-errors.md: -------------------------------------------------------------------------------- 1 | # cli_abort() captures correct call and backtrace 2 | 3 | Code 4 | print(expect_error(f())) 5 | Output 6 | 7 | Error in `h()`: 8 | ! foo 9 | --- 10 | Backtrace: 11 | x 12 | 1. +-base::print(expect_error(f())) 13 | 2. +-testthat::expect_error(f()) 14 | 3. | \-testthat:::expect_condition_matching(...) 15 | 4. | \-testthat:::quasi_capture(...) 16 | 5. | +-testthat (local) .capture(...) 17 | 6. | | \-base::withCallingHandlers(...) 18 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 19 | 8. \-cli (local) f() 20 | 9. \-cli (local) g() 21 | 10. \-cli (local) h() 22 | 23 | --- 24 | 25 | Code 26 | print(expect_error(f(list()))) 27 | Output 28 | 29 | Error in `h()`: 30 | ! `x` can't be empty. 31 | --- 32 | Backtrace: 33 | x 34 | 1. +-base::print(expect_error(f(list()))) 35 | 2. +-testthat::expect_error(f(list())) 36 | 3. | \-testthat:::expect_condition_matching(...) 37 | 4. | \-testthat:::quasi_capture(...) 38 | 5. | +-testthat (local) .capture(...) 39 | 6. | | \-base::withCallingHandlers(...) 40 | 7. | \-rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo)) 41 | 8. \-cli (local) f(list()) 42 | 9. \-cli (local) g(x) 43 | 10. \-cli (local) h(x) 44 | 45 | # cli_abort(.internal = TRUE) reports the correct function (r-lib/rlang#1386) 46 | 47 | Code 48 | (expect_error(fn())) 49 | Output 50 | 51 | Error in `fn()`: 52 | ! Message. 53 | i This is an internal error that was detected in the base package. 54 | 55 | -------------------------------------------------------------------------------- /man/hash_sha256.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hash.R 3 | \name{hash_sha256} 4 | \alias{hash_sha256} 5 | \alias{hash_raw_sha256} 6 | \alias{hash_obj_sha256} 7 | \alias{hash_file_sha256} 8 | \title{SHA-256 hash} 9 | \usage{ 10 | hash_sha256(x) 11 | 12 | hash_raw_sha256(x) 13 | 14 | hash_obj_sha256(x, serialize_version = 2) 15 | 16 | hash_file_sha256(paths) 17 | } 18 | \arguments{ 19 | \item{x}{Character vector. If not a character vector, then 20 | \code{\link[=as.character]{as.character()}} is used to try to coerce it into one. \code{NA} entries 21 | will have an \code{NA} hash.} 22 | 23 | \item{serialize_version}{Workspace format version to use, see 24 | \code{\link[base:serialize]{base::serialize()}}.} 25 | 26 | \item{paths}{Character vector of file names.} 27 | } 28 | \value{ 29 | \code{hash_sha256()} returns a character vector of hexadecimal 30 | SHA-256 hashes. 31 | 32 | \code{hash_raw_sha256()} returns a character scalar. 33 | 34 | \code{hash_obj_sha256()} returns a character scalar. 35 | 36 | \code{hash_file_sha256()} returns a character vector of SHA-256 37 | hashes. 38 | } 39 | \description{ 40 | Calculate the SHA-256 hash of each element of a character vector. 41 | } 42 | \details{ 43 | \code{hash_raw_sha256()} calculates the SHA-256 hash of the bytes 44 | of a raw vector. 45 | 46 | \code{hash_obj_sha256()} calculates the SHA-256 hash of an R 47 | object. The object is serialized into a binary vector first. 48 | 49 | \code{hash_file_sha256()} calculates the SHA-256 hash of one or 50 | more files. 51 | } 52 | \examples{ 53 | hash_sha256(c("foo", NA, "bar", "")) 54 | } 55 | \seealso{ 56 | Other hash functions: 57 | \code{\link{hash_animal}()}, 58 | \code{\link{hash_emoji}()}, 59 | \code{\link{hash_md5}()}, 60 | \code{\link{hash_sha1}()}, 61 | \code{\link{hash_xxhash}()} 62 | } 63 | \concept{hash functions} 64 | -------------------------------------------------------------------------------- /man/cli_bullets_raw.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bullets.R 3 | \name{cli_bullets_raw} 4 | \alias{cli_bullets_raw} 5 | \alias{format_bullets_raw} 6 | \title{List of verbatim items} 7 | \usage{ 8 | cli_bullets_raw(text, id = NULL, class = NULL) 9 | 10 | format_bullets_raw(text, id = NULL, class = NULL) 11 | } 12 | \arguments{ 13 | \item{text}{Character vector of items. See details below on how names 14 | are interpreted.} 15 | 16 | \item{id}{Optional id of the \code{div.bullets} element, can be used in themes.} 17 | 18 | \item{class}{Optional additional class(es) for the \code{div.bullets} element.} 19 | } 20 | \description{ 21 | \code{cli_format_bullets_raw()} is similar to \code{\link[=cli_bullets]{cli_bullets()}}, but it does 22 | not perform any inline styling or glue substitutions in the input. 23 | } 24 | \details{ 25 | \code{format_bullets_raw()} returns the output instead of printing it. 26 | } 27 | \seealso{ 28 | These functions support \link[=inline-markup]{inline markup}. 29 | 30 | See \code{\link[=cli_bullets]{cli_bullets()}} for examples. 31 | 32 | Other functions supporting inline markup: 33 | \code{\link{cli_abort}()}, 34 | \code{\link{cli_alert}()}, 35 | \code{\link{cli_blockquote}()}, 36 | \code{\link{cli_bullets}()}, 37 | \code{\link{cli_dl}()}, 38 | \code{\link{cli_h1}()}, 39 | \code{\link{cli_li}()}, 40 | \code{\link{cli_ol}()}, 41 | \code{\link{cli_process_start}()}, 42 | \code{\link{cli_progress_along}()}, 43 | \code{\link{cli_progress_bar}()}, 44 | \code{\link{cli_progress_message}()}, 45 | \code{\link{cli_progress_output}()}, 46 | \code{\link{cli_progress_step}()}, 47 | \code{\link{cli_rule}}, 48 | \code{\link{cli_status}()}, 49 | \code{\link{cli_status_update}()}, 50 | \code{\link{cli_text}()}, 51 | \code{\link{cli_ul}()}, 52 | \code{\link{format_error}()}, 53 | \code{\link{format_inline}()} 54 | } 55 | \concept{functions supporting inline markup} 56 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/deep-lists.md: -------------------------------------------------------------------------------- 1 | # deep lists ul 2 | 3 | Code 4 | for (i in 1:4) test_ul(i) 5 | Message 6 | * Level 1 7 | * Level 1 8 | * Level 2 9 | * Level 1 10 | * Level 2 11 | * Level 3 12 | * Level 1 13 | * Level 2 14 | * Level 3 15 | * Level 4 16 | 17 | # deep lists ol 18 | 19 | Code 20 | for (i in 1:4) test_ol(i) 21 | Message 22 | 1. Level 1 23 | 1. Level 1 24 | 1. Level 2 25 | 1. Level 1 26 | 1. Level 2 27 | 1. Level 3 28 | 1. Level 1 29 | 1. Level 2 30 | 1. Level 3 31 | 1. Level 4 32 | 33 | # deep lists ol ul 34 | 35 | Code 36 | for (i in 1:4) test_ol_ul(i) 37 | Message 38 | 1. Level 1 39 | * Level 2 40 | 1. Level 1 41 | * Level 2 42 | 1. Level 3 43 | * Level 4 44 | 1. Level 1 45 | * Level 2 46 | 1. Level 3 47 | * Level 4 48 | 1. Level 5 49 | * Level 6 50 | 1. Level 1 51 | * Level 2 52 | 1. Level 3 53 | * Level 4 54 | 1. Level 5 55 | * Level 6 56 | 1. Level 7 57 | * Level 8 58 | 59 | # deep lists ul ol 60 | 61 | Code 62 | for (i in 1:4) test_ul_ol(i) 63 | Message 64 | * Level 1 65 | 1. Level 2 66 | * Level 1 67 | 1. Level 2 68 | * Level 3 69 | 1. Level 4 70 | * Level 1 71 | 1. Level 2 72 | * Level 3 73 | 1. Level 4 74 | * Level 5 75 | 1. Level 6 76 | * Level 1 77 | 1. Level 2 78 | * Level 3 79 | 1. Level 4 80 | * Level 5 81 | 1. Level 6 82 | * Level 7 83 | 1. Level 8 84 | 85 | -------------------------------------------------------------------------------- /man/cli_verbatim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cli.R 3 | \name{cli_verbatim} 4 | \alias{cli_verbatim} 5 | \title{CLI verbatim text} 6 | \usage{ 7 | cli_verbatim(..., .envir = parent.frame()) 8 | } 9 | \arguments{ 10 | \item{...}{The text to show, in character vectors. Each element is 11 | printed on a new line.} 12 | 13 | \item{.envir}{Environment to evaluate the glue expressions in.} 14 | } 15 | \description{ 16 | It is not wrapped, but printed as is. Long lines will overflow. 17 | No glue substitution is performed on verbatim text. 18 | } 19 | \details{ 20 | \subsection{Line breaks}{ 21 | 22 | \if{html}{\out{
}}\preformatted{cli_verbatim("This has\\nthree\\nlines,") 23 | }\if{html}{\out{
}}\if{html}{\out{ 24 |
25 | #> This has                                                                        
26 | #> three                                                                           
27 | #> lines,                                                                          
28 | 
29 | }} 30 | 31 | } 32 | 33 | \subsection{Special characters}{ 34 | 35 | No glue substitution happens here. 36 | 37 | \if{html}{\out{
}}\preformatted{cli_verbatim("No string \{interpolation\} or \{.emph styling\} here") 38 | }\if{html}{\out{
}}\if{html}{\out{ 39 |
40 | #> No string \{interpolation\} or \{.emph styling\} here                               
41 | 
42 | }} 43 | 44 | } 45 | } 46 | \seealso{ 47 | \code{\link[=cli_code]{cli_code()}} for printing R or other source code. 48 | } 49 | -------------------------------------------------------------------------------- /tests/testthat/test-meta.R: -------------------------------------------------------------------------------- 1 | start_app() 2 | on.exit(stop_app(), add = TRUE) 3 | 4 | test_that_cli("meta basics", { 5 | expect_snapshot( 6 | cli::cli({ 7 | message("This is before") 8 | cli_alert_info("First message") 9 | message("This as well") 10 | cli_alert_success("Success!") 11 | }) 12 | ) 13 | }) 14 | 15 | test_that_cli("meta is single cli_message", { 16 | msgs <- list() 17 | withCallingHandlers( 18 | cli::cli({ 19 | cli_alert_info("First message") 20 | cli_alert_success("Success!") 21 | }), 22 | cli_message = function(msg) { 23 | msgs <<- c(msgs, list(msg)) 24 | invokeRestart("cli_message_handled") 25 | } 26 | ) 27 | 28 | expect_equal(length(msgs), 1L) 29 | expect_snapshot(cli_server_default(msgs[[1]])) 30 | }) 31 | 32 | test_that_cli("meta is single cliMessage", { 33 | msgs <- list() 34 | expect_snapshot( 35 | withCallingHandlers( 36 | cli::cli({ 37 | cli_alert_info("First message") 38 | cli_alert_success("Success!") 39 | }), 40 | cliMessage = function(msg) { 41 | msgs <<- c(msgs, list(msg)) 42 | } 43 | ) 44 | ) 45 | 46 | expect_equal(length(msgs), 1L) 47 | }) 48 | 49 | test_that_cli("substitution", { 50 | expect_snapshot(local({ 51 | x <- 1:3 52 | cli({ 53 | title <- "My title" 54 | cli_h1("Title: {.emph {title}}") 55 | cli_text("And {.emph some} more: {.val {x}}") 56 | }) 57 | })) 58 | }) 59 | 60 | test_that("return values are ok when recording (#496)", { 61 | expect_snapshot( 62 | cli::cli({ 63 | lid <- cli::cli_ul() 64 | cli::cli_li("a bullet") 65 | cli::cli_end(lid) 66 | }) 67 | ) 68 | }) 69 | 70 | test_that("nested cli() (#497)", { 71 | expect_snapshot( 72 | cli::cli({ 73 | cli::cli_h1("Header") 74 | cli::cli(cli::cli_text("Some text")) 75 | cli::cli_text("Some more text") 76 | }) 77 | ) 78 | }) 79 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: cli 2 | Title: Helpers for Developing Command Line Interfaces 3 | Version: 3.6.5.9000 4 | Authors@R: c( 5 | person("Gábor", "Csárdi", , "gabor@posit.co", role = c("aut", "cre")), 6 | person("Hadley", "Wickham", role = "ctb"), 7 | person("Kirill", "Müller", role = "ctb"), 8 | person("Salim", "Brüggemann", , "salim-b@pm.me", role = "ctb", 9 | comment = c(ORCID = "0000-0002-5329-5987")), 10 | person("Posit Software, PBC", role = c("cph", "fnd"), 11 | comment = c(ROR = "03wc8by49")) 12 | ) 13 | Description: A suite of tools to build attractive command line interfaces 14 | ('CLIs'), from semantic elements: headings, lists, alerts, paragraphs, 15 | etc. Supports custom themes via a 'CSS'-like language. It also 16 | contains a number of lower level 'CLI' elements: rules, boxes, trees, 17 | and 'Unicode' symbols with 'ASCII' alternatives. It support ANSI 18 | colors and text styles as well. 19 | License: MIT + file LICENSE 20 | URL: https://cli.r-lib.org, https://github.com/r-lib/cli 21 | BugReports: https://github.com/r-lib/cli/issues 22 | Depends: 23 | R (>= 3.4) 24 | Imports: 25 | utils 26 | Suggests: 27 | callr, 28 | covr, 29 | crayon, 30 | digest, 31 | glue (>= 1.6.0), 32 | grDevices, 33 | htmltools, 34 | htmlwidgets, 35 | knitr, 36 | methods, 37 | processx, 38 | ps (>= 1.3.4.9000), 39 | rlang (>= 1.0.2.9003), 40 | rmarkdown, 41 | rprojroot, 42 | rstudioapi, 43 | testthat (>= 3.2.0), 44 | tibble, 45 | whoami, 46 | withr 47 | Config/Needs/website: 48 | r-lib/asciicast, 49 | bench, 50 | brio, 51 | cpp11, 52 | decor, 53 | desc, 54 | fansi, 55 | prettyunits, 56 | sessioninfo, 57 | tidyverse/tidytemplate, 58 | usethis, 59 | vctrs 60 | Config/testthat/edition: 3 61 | Config/usethis/last-upkeep: 2025-04-25 62 | Encoding: UTF-8 63 | RoxygenNote: 7.3.2.9000 64 | --------------------------------------------------------------------------------