[med-svn] [r-cran-crayon] 07/09: New upstream version 1.3.2
Andreas Tille
tille at debian.org
Mon Oct 9 09:50:54 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository r-cran-crayon.
commit 07dc9fab0288f6e1a95edb48bee369e6034ec7ac
Author: Andreas Tille <tille at debian.org>
Date: Mon Oct 9 11:48:04 2017 +0200
New upstream version 1.3.2
---
DESCRIPTION | 34 ++++
LICENSE | 2 +
MD5 | 52 ++++++
NAMESPACE | 57 +++++++
R/ansi-256.r | 48 ++++++
R/combine.r | 55 +++++++
R/crayon-package.r | 16 ++
R/disposable.r | 98 +++++++++++
R/has_ansi.r | 34 ++++
R/has_color.r | 129 +++++++++++++++
R/machinery.r | 342 +++++++++++++++++++++++++++++++++++++++
R/parts.r | 58 +++++++
R/print.r | 14 ++
R/show.r | 50 ++++++
R/string.r | 57 +++++++
R/string_operations.r | 255 +++++++++++++++++++++++++++++
R/style-var.r | 45 ++++++
R/styles.r | 72 +++++++++
R/utils.r | 129 +++++++++++++++
debian/README.test | 8 -
debian/changelog | 19 ---
debian/compat | 1 -
debian/control | 28 ----
debian/copyright | 35 ----
debian/docs | 3 -
debian/rules | 21 ---
debian/source/format | 1 -
debian/tests/control | 3 -
debian/tests/run-unit-test | 13 --
debian/watch | 2 -
inst/ANSI-256-OSX.png | Bin 0 -> 467885 bytes
inst/ANSI-8-OSX.png | Bin 0 -> 70267 bytes
inst/NEWS.md | 70 ++++++++
inst/README.markdown | 140 ++++++++++++++++
inst/logo.png | Bin 0 -> 37280 bytes
inst/logo.svg.gz | Bin 0 -> 59649 bytes
man/chr.Rd | 21 +++
man/col_nchar.Rd | 41 +++++
man/col_strsplit.Rd | 49 ++++++
man/col_substr.Rd | 57 +++++++
man/col_substring.Rd | 58 +++++++
man/combine_styles.Rd | 56 +++++++
man/concat.Rd | 35 ++++
man/crayon.Rd | 158 ++++++++++++++++++
man/drop_style.Rd | 28 ++++
man/has_color.Rd | 39 +++++
man/has_style.Rd | 25 +++
man/make_style.Rd | 85 ++++++++++
man/num_colors.Rd | 43 +++++
man/show_ansi_colors.Rd | 20 +++
man/start.crayon.Rd | 40 +++++
man/strip_style.Rd | 21 +++
man/style.Rd | 30 ++++
man/styles.Rd | 25 +++
tests/testthat.R | 6 +
tests/testthat/test-color.r | 48 ++++++
tests/testthat/test-has-color.r | 76 +++++++++
tests/testthat/test-has-style.r | 28 ++++
tests/testthat/test-make-style.r | 58 +++++++
tests/testthat/test-operations.R | 192 ++++++++++++++++++++++
tests/testthat/test-style-var.r | 16 ++
tests/testthat/test-styles.r | 51 ++++++
tests/testthat/test-utils.R | 9 ++
tests/testthat/test-vectors.r | 64 ++++++++
64 files changed, 3136 insertions(+), 134 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..26e6dd5
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,34 @@
+Package: crayon
+Title: Colored Terminal Output
+Version: 1.3.2
+Authors at R: c(
+ person("Gábor", "Csárdi", , "csardi.gabor at gmail.com",
+ role = c("aut", "cre")),
+ person(
+ "Brodie", "Gaslam", email="brodie.gaslam at yahoo.com",
+ role=c("ctb"))
+ )
+Description: Colored terminal output on terminals that support 'ANSI'
+ color and highlight codes. It also works in 'Emacs' 'ESS'. 'ANSI'
+ color support is automatically detected. Colors and highlighting can
+ be combined and nested. New styles can also be created easily.
+ This package was inspired by the 'chalk' 'JavaScript' project.
+License: MIT + file LICENSE
+LazyData: true
+URL: https://github.com/gaborcsardi/crayon
+BugReports: https://github.com/gaborcsardi/crayon/issues
+Collate: 'ansi-256.r' 'combine.r' 'string.r' 'utils.r'
+ 'crayon-package.r' 'disposable.r' 'has_ansi.r' 'has_color.r'
+ 'styles.r' 'machinery.r' 'parts.r' 'print.r' 'style-var.r'
+ 'show.r' 'string_operations.r'
+Imports: grDevices, methods, utils
+Suggests: testthat
+RoxygenNote: 5.0.1
+Encoding: UTF-8
+NeedsCompilation: no
+Packaged: 2016-06-28 17:18:56 UTC; gaborcsardi
+Author: Gábor Csárdi [aut, cre],
+ Brodie Gaslam [ctb]
+Maintainer: Gábor Csárdi <csardi.gabor at gmail.com>
+Repository: CRAN
+Date/Publication: 2016-06-28 20:16:37
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6fe97ec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+YEAR: 2014-2016
+COPYRIGHT HOLDER: Gabor Csardi
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..a467e18
--- /dev/null
+++ b/MD5
@@ -0,0 +1,52 @@
+33b0d86445b3fae78955de3d4b58e397 *DESCRIPTION
+71e081e70f89b75ba2781efdce97f3b4 *LICENSE
+f163aae74ce504ed0d69967904db6ec6 *NAMESPACE
+4463bc9d9bd9c2d4423a17a8d795787b *R/ansi-256.r
+d91d95b5a9db91ec97b5b1c03a2eb01d *R/combine.r
+722a272258c2dcc30ffe7ae958c7b7b7 *R/crayon-package.r
+81b120d882c1da9d328216fa75127273 *R/disposable.r
+b501573c5d249cd7971bc2815deab815 *R/has_ansi.r
+0147fb0c5b7a2df1b80cb0b5f7b336d2 *R/has_color.r
+dce4accf89c6862ce2e0886120e2edb1 *R/machinery.r
+b85d1e01ab403b8a09ae2346d325bcc1 *R/parts.r
+d60aa06633288dddff27c30abbd93ae5 *R/print.r
+34e9b27d9d071a9b910510ff544bcfb1 *R/show.r
+cb822b77522c45543f177e7985f39d82 *R/string.r
+8fcb72e1fdff885fba887497e26b2655 *R/string_operations.r
+492e6593dd409f4abd4d36e358c6f2b3 *R/style-var.r
+7117857a16121744d8ab7e5bddfec9f9 *R/styles.r
+605e414985e272917c23aedeb28e90c6 *R/utils.r
+175d390058b8a9b12995525725218cf4 *inst/ANSI-256-OSX.png
+5a18e3dc240c1f6cfffd6059c8de5345 *inst/ANSI-8-OSX.png
+9d77d6031aa5556b770c65ce4f15ab5e *inst/NEWS.md
+53d5ddc63652f7c3a613f5a2cf66bf52 *inst/README.markdown
+475e112815d6b888898bd6f73284bd5a *inst/logo.png
+a3a5cfbd0780e6686c174aa238d41a61 *inst/logo.svg.gz
+0ca0bc9b4d741f5c45f170fd4ba79246 *man/chr.Rd
+b30b7a3002f3cff80216f297aaba4ad7 *man/col_nchar.Rd
+53596793a64851641d028dea15f5c719 *man/col_strsplit.Rd
+db27a893357469fbbda3e398e5de94b4 *man/col_substr.Rd
+78ecbe6f26d392d799428314cae10eb5 *man/col_substring.Rd
+18532409b427c61e93bd64325f73710a *man/combine_styles.Rd
+538ac6c5745e1c333dbe8b510f3ac638 *man/concat.Rd
+9f28fbc0c2f7d0328b5a5dcb94529293 *man/crayon.Rd
+3bfdc606313c1f4e1ae0efd9fa40fd34 *man/drop_style.Rd
+ae2804fd927bae6ea990119d7b4a53bd *man/has_color.Rd
+5061a5e75fe4eecf83f0bda69aa465dc *man/has_style.Rd
+d55b5926e903aa96ff70eae3a920f165 *man/make_style.Rd
+0d8b2fcc5010f52a1ba5680f543f53ba *man/num_colors.Rd
+e7da13f54636670d6e2a850c5393e28b *man/show_ansi_colors.Rd
+10584da85081d6d9ade9ea329e10a081 *man/start.crayon.Rd
+5838acb9e0fb0c536fed59f1223fdf2d *man/strip_style.Rd
+6023daa6bf279249af6602013197cbc6 *man/style.Rd
+2f0b80a2523df022da871a5a2c108f23 *man/styles.Rd
+c9d57cf01e1f808cc3039b0f9149d380 *tests/testthat.R
+541b9fb498b4a115f9f740c71cc3c98f *tests/testthat/test-color.r
+e4702f5e1469b8fc41014478dcbdcee0 *tests/testthat/test-has-color.r
+2cc3040d1a027f51e3d5f987ace997f5 *tests/testthat/test-has-style.r
+ae16fd97b1bbf86a6680678c8ae845e7 *tests/testthat/test-make-style.r
+1235e578364243d9e71a25f5284812a2 *tests/testthat/test-operations.R
+ce7bc77df0975d3f9aef7064c5dca438 *tests/testthat/test-style-var.r
+9d653af368745efcbb41342e5b369920 *tests/testthat/test-styles.r
+8e8cbd732867bd818e324c5b15e0a224 *tests/testthat/test-utils.R
+44e4500870306c397760a488530f1126 *tests/testthat/test-vectors.r
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..0cc0b22
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,57 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method("$",crayon)
+S3method(finish,crayon)
+S3method(print,crayon)
+S3method(start,crayon)
+export("%+%")
+export(bgBlack)
+export(bgBlue)
+export(bgCyan)
+export(bgGreen)
+export(bgMagenta)
+export(bgRed)
+export(bgWhite)
+export(bgYellow)
+export(black)
+export(blue)
+export(blurred)
+export(bold)
+export(chr)
+export(col_nchar)
+export(col_strsplit)
+export(col_substr)
+export(col_substring)
+export(combine_styles)
+export(cyan)
+export(drop_style)
+export(finish)
+export(green)
+export(has_color)
+export(has_style)
+export(hidden)
+export(inverse)
+export(italic)
+export(magenta)
+export(make_style)
+export(num_colors)
+export(red)
+export(reset)
+export(show_ansi_colors)
+export(silver)
+export(strikethrough)
+export(strip_style)
+export(style)
+export(styles)
+export(underline)
+export(white)
+export(yellow)
+importFrom(grDevices,col2rgb)
+importFrom(grDevices,colors)
+importFrom(methods,is)
+importFrom(stats,start)
+importFrom(utils,head)
+importFrom(utils,install.packages)
+importFrom(utils,package.skeleton)
+importFrom(utils,tail)
+importFrom(utils,tar)
diff --git a/R/ansi-256.r b/R/ansi-256.r
new file mode 100644
index 0000000..b766df3
--- /dev/null
+++ b/R/ansi-256.r
@@ -0,0 +1,48 @@
+
+fgcodes <- c(paste0('\x1b[38;5;', 0:255, 'm'), '\x1b[39m')
+bgcodes <- c(paste0('\x1b[48;5;', 0:255, 'm'), '\x1b[49m')
+
+rgb_index <- 17:232
+gray_index <- 233:256
+reset_index <- 257
+
+ansi256 <- function(rgb, bg = FALSE, grey = FALSE) {
+ codes <- if (bg) bgcodes else fgcodes
+ if (grey) {
+ ## Gray
+ list(
+ open = codes[gray_index][scale(rgb[1], to = c(0, 23)) + 1],
+ close = codes[reset_index]
+ )
+
+ } else {
+ ## Not gray
+ list(
+ open = codes[ansi256_rgb_index(rgb[1L], rgb[2L], rgb[3L])],
+ close = codes[reset_index]
+ )
+ }
+}
+
+## This is based off the algorithm in the ruby "paint" gem, as
+## implemented in rainbowrite.
+ansi256_rgb_index <- function(red, green, blue) {
+ gray_possible <- TRUE
+ sep <- 42.5
+ while (gray_possible) {
+ if (red < sep || green < sep || blue < sep) {
+ gray <- red < sep && green < sep && blue < sep
+ gray_possible <- FALSE
+ }
+ sep <- sep + 42.5
+ }
+
+ ## NOTE: The +1 here translates from base0 to base1 for the index
+ ## that does the same. Not ideal, but that does get the escape
+ ## characters in nicely.
+ if (gray) {
+ 232 + round((red + green + blue) / 33) + 1
+ } else {
+ 16 + sum(floor(6 * c(red, green, blue) / 256) * c(36, 6, 1)) + 1
+ }
+}
diff --git a/R/combine.r b/R/combine.r
new file mode 100644
index 0000000..a53f248
--- /dev/null
+++ b/R/combine.r
@@ -0,0 +1,55 @@
+
+#' Combine two or more ANSI styles
+#'
+#' Combine two or more styles or style functions into a new style function
+#' that can be called on strings to style them.
+#'
+#' It does not usually make sense to combine two foreground
+#' colors (or two background colors), because only the first one
+#' applied will be used.
+#'
+#' It does make sense to combine different kind of styles,
+#' e.g. background color, foreground color, bold font.
+#'
+#' The \code{$} operator can also be used to combine styles.
+#' Not that the left hand side of \code{$} is a style function,
+#' and the right hand side is the name of a style in \code{styles()}.
+#'
+#' @param ... The styles to combine. They will be applied from
+#' right to left.
+#' @return The combined style function.
+#'
+#' @export
+#' @examples
+#' ## Use style names
+#' alert <- combine_styles("bold", "red4", "bgCyan")
+#' cat(alert("Warning!"), "\n")
+#'
+#' ## Or style functions
+#' alert <- combine_styles(bold, red, bgCyan)
+#' cat(alert("Warning!"), "\n")
+#'
+#' ## Combine a composite style
+#' alert <- combine_styles(bold, combine_styles(red, bgCyan))
+#' cat(alert("Warning!"), "\n")
+#'
+#' ## Shorter notation
+#' alert <- bold $ red $ bgCyan
+#' cat(alert("Warning!"), "\n")
+
+combine_styles <- function(...) {
+ styles <- lapply(list(...), use_or_make_style)
+ all_ansi <- unlist(lapply(styles, attr, "_styles"), recursive = FALSE)
+ make_crayon(all_ansi)
+}
+
+#' @rdname combine_styles
+#' @param crayon A style function.
+#' @param style A style name that is included in \code{names(styles())}.
+#' @export
+#' @method $ crayon
+
+`$.crayon` <- function(crayon, style) {
+ attr(crayon, "_styles") <- c(attr(crayon, "_styles"), my_styles[style])
+ crayon
+}
diff --git a/R/crayon-package.r b/R/crayon-package.r
new file mode 100644
index 0000000..7d7b058
--- /dev/null
+++ b/R/crayon-package.r
@@ -0,0 +1,16 @@
+
+## ----------------------------------------------------------------------
+
+#' Colored terminal output
+#'
+#' With crayon it is easy to add color to terminal output, create styles
+#' for notes, warnings, errors; and combine styles.
+#'
+#' ANSI color support is automatically detected and used. Crayon was largely
+#' inspired by chalk \url{https://github.com/sindresorhus/chalk}.
+#'
+#' @include utils.r
+#' @include string.r
+#' @docType package
+#' @name crayon
+NULL
diff --git a/R/disposable.r b/R/disposable.r
new file mode 100644
index 0000000..3e5c312
--- /dev/null
+++ b/R/disposable.r
@@ -0,0 +1,98 @@
+
+install_quietly <- TRUE
+
+with_wd <- function(dir, expr) {
+ wd <- getwd()
+ on.exit(setwd(wd))
+ setwd(dir)
+ eval(substitute(expr), envir = parent.frame())
+}
+
+#' @importFrom utils tar
+
+build_pkg <- function(path, pkg_file = NULL) {
+ if (!file.exists(path)) stop("path does not exist")
+ pkg_name <- basename(path)
+ if (is.null(pkg_file)) {
+ pkg_file <- file.path(dirname(path), paste0(pkg_name, "_1.0.tar.gz"))
+ }
+ with_wd(dirname(path),
+ tar(basename(pkg_file), pkg_name, compression = "gzip"))
+ pkg_file
+}
+
+#' @importFrom utils package.skeleton install.packages
+
+install_tmp_pkg <- function(..., pkg_name, lib_dir, imports = character()) {
+ if (!file.exists(lib_dir)) stop("lib_dir does not exist")
+ if (!is.character(pkg_name) || length(pkg_name) != 1) {
+ stop("pkg_name is not a string")
+ }
+
+ ## Create a directory that will contain the source package
+ src_dir <- tempfile()
+ on.exit(try(unlink(src_dir, recursive = TRUE), silent = TRUE), add = TRUE)
+ dir.create(src_dir)
+
+ ## Create source package, need a non-empty environment,
+ ## otherwise package.skeleton fails
+ tmp_env <- new.env()
+ assign("f", function(x) x, envir = tmp_env)
+ suppressMessages(package.skeleton(pkg_name, path = src_dir,
+ environment = tmp_env))
+ pkg_dir <- file.path(src_dir, pkg_name)
+
+ ## Make it installable: remove man, add imports
+ unlink(file.path(pkg_dir, "man"), recursive = TRUE)
+ if (length(imports) != 0) {
+ cat("Imports: ", paste(imports, collapse = ", "), "\n",
+ file = file.path(pkg_dir, "DESCRIPTION"), append = TRUE)
+ cat(paste0("import(", imports, ")"), sep="\n",
+ file = file.path(pkg_dir, "NAMESPACE"), append = TRUE)
+ }
+
+ ## Put the code in it, dput is noisy, so we need to redirect it to
+ ## temporary file
+ exprs <- list(...)
+ unlink(file.path(pkg_dir, "R"), recursive = TRUE)
+ dir.create(file.path(pkg_dir, "R"))
+ code_file <- file.path(pkg_dir, "R", "code.R")
+ tmp_file <- tempfile()
+ on.exit(try(unlink(tmp_file), silent = TRUE), add = TRUE)
+ sapply(exprs, function(x)
+ cat(deparse(dput(x, file = tmp_file)),
+ file = code_file, append = TRUE, "\n", sep="\n"))
+
+ ## Build it
+ pkg_file <- build_pkg(pkg_dir)
+
+ ## Install it into the supplied lib_dir
+ install.packages(pkg_file, lib = lib_dir, repos = NULL, type = "source",
+ quiet = install_quietly)
+}
+
+with_libpath <- function(lib_path, ...) {
+ cur_lib_path <- .libPaths()
+ on.exit(.libPaths(cur_lib_path), add = TRUE)
+ .libPaths(c(lib_path, cur_lib_path))
+ exprs <- c(as.list(match.call(expand.dots = FALSE)$...))
+ sapply(exprs, eval, envir = parent.frame())
+}
+
+make_packages <- function(..., lib_dir = tempfile(),
+ imports = character()) {
+ if (!file.exists(lib_dir)) dir.create(lib_dir)
+ exprs <- c(as.list(match.call(expand.dots = FALSE)$...))
+ for (i in seq_along(exprs)) {
+ expr <- exprs[[i]]
+ name <- names(exprs)[i]
+ install_tmp_pkg(expr, pkg_name = name,
+ lib_dir = lib_dir, imports = imports)
+ ## Unload everything if an error happens
+ on.exit(try(unloadNamespace(name), silent = TRUE), add = TRUE)
+ with_libpath(lib_dir, suppressMessages(library(name, quietly = TRUE,
+ character.only = TRUE)))
+ on.exit()
+ }
+ invisible(lib_dir)
+}
diff --git a/R/has_ansi.r b/R/has_ansi.r
new file mode 100644
index 0000000..2aebc5e
--- /dev/null
+++ b/R/has_ansi.r
@@ -0,0 +1,34 @@
+
+ansi_regex <- paste0("(?:(?:\\x{001b}\\[)|\\x{009b})",
+ "(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])",
+ "|\\x{001b}[A-M]")
+
+#' Check if a sting has some ANSI styling
+#'
+#' @param string The string to check. It can also be a character
+#' vector.
+#' @return Logical vector, \code{TRUE} for the strings that have some
+#' ANSI styling.
+#'
+#' @export
+#' @examples
+#' ## The second one has style if crayon is enabled
+#' has_style("foobar")
+#' has_style(red("foobar"))
+
+has_style <- function(string) {
+ grepl(ansi_regex, string, perl = TRUE)
+}
+
+#' Remove ANSI escape sequences from a string
+#'
+#' @param string The input string.
+#' @return The cleaned up string.
+#'
+#' @export
+#' @examples
+#' strip_style(red("foobar")) == "foobar"
+
+strip_style <- function(string) {
+ gsub(ansi_regex, "", string, perl = TRUE)
+}
diff --git a/R/has_color.r b/R/has_color.r
new file mode 100644
index 0000000..fcc2f7e
--- /dev/null
+++ b/R/has_color.r
@@ -0,0 +1,129 @@
+
+## ----------------------------------------------------------------------
+
+#' Does the current R session support ANSI colors?
+#'
+#' @details
+#' The following algorithm is used to detect ANSI support: \itemize{
+#' \item If the \code{crayon.enabled} option is set to \code{TRUE}
+#' with \code{options()}, then \code{TRUE} is returned. If it is
+#' set to something else than \code{TRUE} (typically \code{FALSE}),
+#' then \code{FALSE} is returned.
+#' \item Otherwise, if the standard output is not a terminal, then
+#' \code{FALSE} is returned.
+#' \item Otherwise, if the platform is Windows, \code{TRUE} is returned
+#' if running in ConEmu (\url{https://conemu.github.io/}) or
+#' cmder (\url{http://cmder.net}) with ANSI color support.
+#' Otherwise \code{FALSE} is returned.
+#' \item Otherwise, if the \code{COLORTERM} environment variable is
+#' set, \code{TRUE} is returned.
+#' \item Otherwise, if the \code{TERM} environment variable starts
+#' with \code{screen}, \code{xterm} or \code{vt100}, or matches
+#' \code{color}, \code{ansi}, \code{cygwin} or \code{linux}
+#' (with case insentive matching), then \code{TRUE} is returned.
+#' \item Otherwise \code{FALSE} is returned.
+#' }
+#'
+#' @return \code{TRUE} if the current R session supports color.
+#'
+#' @export
+#' @examples
+#' has_color()
+
+has_color <- function() {
+
+ ## Colors forced?
+ enabled <- getOption("crayon.enabled")
+ if (!is.null(enabled)) { return(isTRUE(enabled)) }
+
+ ## Are we in a terminal? No?
+ if (!isatty(stdout())) { return(FALSE) }
+
+ ## Are we in a windows terminal with color support?
+ if (.Platform$OS.type == "windows") {
+ if (Sys.getenv("ConEmuANSI") == "ON") { return(TRUE) }
+ if (Sys.getenv("CMDER_ROOT") != "") { return(TRUE) }
+
+ ## Are we in another windows terminal or GUI? :(
+ return(FALSE)
+ }
+
+ ## Running in a recent Emacs?
+ if (inside_emacs() && emacs_version()[1] >= 23) { return(TRUE) }
+
+ ## COLORTERM set?
+ if ("COLORTERM" %in% names(Sys.getenv())) { return(TRUE) }
+
+ ## dumb terminal is not good
+ if (Sys.getenv("TERM") == "dumb") { return(FALSE) }
+
+ ## Otherwise try to guess based on TERM
+ grepl("^screen|^xterm|^vt100|color|ansi|cygwin|linux",
+ Sys.getenv("TERM"), ignore.case = TRUE, perl = TRUE)
+}
+
+#' Number of colors the terminal supports
+#'
+#' @details
+#' If the \code{crayon.colors} option is set, then we
+#' just use that. It should be an integer number. You can use this
+#' option as a workaround if crayon does not detect the number of
+#' colors accurately.
+#'
+#' In Emacs, we report eight colors.
+#'
+#' Otherwise, we use the \code{tput} shell command to detect the
+#' number of colors. If \code{tput} is not available,
+#' but we think that the terminal supports colors, then
+#' eigth colors are assumed.
+#'
+#' If tput returns 8, but TERM is xterm, we return 256, as most xterm
+#' compatible terminals in fact do support 256 colors.
+#' There is some discussion about this here:
+#' \url{https://github.com/gaborcsardi/crayon/issues/17}
+#'
+#' For efficiency, \code{num_colors} caches its result. To
+#' re-check the number of colors, set the \code{forget} argument to
+#' \code{TRUE}.
+#'
+#' @param forget Whether to forget the cached result of the color check.
+#' @return Numeric scalar, the number of colors the terminal supports.
+#' @export
+#' @examples
+#' num_colors()
+
+num_colors <- (function() {
+ # closure env to store state
+
+ cache <- NULL
+
+ function(forget=FALSE) {
+ if (forget || is.null(cache)) cache <<- i_num_colors()
+ cache
+ }
+})()
+
+i_num_colors <- function() {
+
+ ## Number of colors forced
+ cols <- getOption("crayon.colors")
+ if (!is.null(cols)) { return(as.integer(cols)) }
+
+ ## Otherwise try to detect. If no color support, then 1
+ if (!has_color()) { return(1) }
+
+ ## Emacs
+ if (inside_emacs()) { return(8) }
+
+ ## Try to run tput colors. If it did not run, but has_colors() is TRUE,
+ ## then we just report 8 colors
+ cols <- suppressWarnings(try(silent = TRUE,
+ as.numeric(system("tput colors", intern = TRUE))))
+ if (inherits(cols, "try-error") || !length(cols) || is.na(cols)) { return(8) }
+ if (cols %in% c(-1, 0, 1)) { return(1) }
+
+ ## See comment above in docs
+ if (cols == 8 && identical(Sys.getenv("TERM"), "xterm")) cols <- 256
+
+ cols
+}
diff --git a/R/machinery.r b/R/machinery.r
new file mode 100644
index 0000000..7bcdb0a
--- /dev/null
+++ b/R/machinery.r
@@ -0,0 +1,342 @@
+
+## ----------------------------------------------------------------------
+
+crayon_template <- function(...) {
+ my_styles <- attr(sys.function(), "_styles")
+ text <- mypaste(...)
+ if (has_color()) {
+ for (st in rev(my_styles)) {
+ text <- st$open %+%
+ gsub(st$close, st$open, text, fixed = TRUE) %+%
+ st$close
+ }
+ }
+ text
+}
+
+hash_color_regex <- "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$"
+
+is_builtin_style <- function(x) {
+ x %in% names(builtin_styles)
+}
+
+#' @importFrom grDevices colors
+
+is_r_color <- function(x) {
+ x %in% colors() || grepl(hash_color_regex, x)
+}
+
+is_rgb_matrix <- function(x) {
+ is.matrix(x) && is.numeric(x) && (nrow(x) == 3 || nrow(x) == 4)
+}
+
+#' @importFrom grDevices col2rgb
+
+style_from_r_color <- function(color, bg, num_colors, grey) {
+ style_from_rgb(col2rgb(color), bg, num_colors, grey)
+}
+
+style_8_from_rgb <- function(rgb, bg) {
+ ansi_cols <- if (bg) ansi_bg_rgb else ansi_fg_rgb
+ dist <- colSums((ansi_cols - as.vector(rgb)) ^ 2 )
+ builtin_name <- names(which.min(dist))[1]
+ builtin_styles[[builtin_name]]
+}
+
+style_from_rgb <- function(rgb, bg, num_colors, grey) {
+ if (num_colors < 256) { return(style_8_from_rgb(rgb, bg)) }
+ ansi256(rgb, bg, grey)
+}
+
+#' Create an ANSI color style
+#'
+#' Create a style, or a style function, or both. This function
+#' is intended for those who wish to use 256 ANSI colors,
+#' instead of the more widely supported eight colors.
+#'
+#' @details
+#' The crayon package comes with predefined styles (see
+#' \code{\link{styles}} for a list) and functions for the basic eight-color
+#' ANSI standard (\code{red}, \code{blue}, etc., see \link{crayon}).
+#'
+#' There are no predefined styles or style functions for the 256 color
+#' ANSI mode, however, because we simply did not want to create that
+#' many styles and functions. Instead, \code{make_style} can be
+#' used to create a style (or a style function, or both).
+#'
+#' There are two ways to use this function: \enumerate{
+#' \item If its first argument is not named, then it returns a function
+#' that can be used to color strings.
+#' \item If its first argument is named, then it also creates a
+#' style with the given name. This style can be used in
+#' \code{\link{style}}. One can still use the return value
+#' of the function, to create a style function.
+#' }
+#'
+#' The style (the code{...} argument) can be anything of the
+#' following: \itemize{
+#' \item An R color name, see \code{colors()}.
+#' \item A 6- or 8-digit hexa color string, e.g. \code{#ff0000} means
+#' red. Transparency (alpha channel) values are ignored.
+#' \item A one-column matrix with three rows for the red, green
+#' and blue channels, as returned by \code{col2rgb} (in the base
+#' grDevices package).
+#' }
+#'
+#' \code{make_style} detects the number of colors to use
+#' automatically (this can be overridden using the \code{colors}
+#' argument). If the number of colors is less than 256 (detected or given),
+#' then it falls back to the color in the ANSI eight color mode that
+#' is closest to the specified (RGB or R) color.
+#'
+#' See the examples below.
+#'
+#' @param ... The style to create. See details and examples below.
+#' @param bg Whether the color applies to the background.
+#' @param grey Whether to specifically create a grey color.
+#' This flag is included, because ANSI 256 has a finer color scale
+#' for greys, then the usual 0:5 scale for R, G and B components.
+#' It is only used for RGB color specifications (either numerically
+#' or via a hexa string), and it is ignored on eigth color ANSI
+#' terminals.
+#' @param colors Number of colors, detected automatically
+#' by default.
+#' @return A function that can be used to color strings.
+#'
+#' @family styles
+#' @export
+#' @examples
+#' ## Create a style function without creating a style
+#' pink <- make_style("pink")
+#' bgMaroon <- make_style(rgb(0.93, 0.19, 0.65), bg = TRUE)
+#' cat(bgMaroon(pink("I am pink if your terminal wants it, too.\n")))
+#'
+#' ## Create a new style for pink and maroon background
+#' make_style(pink = "pink")
+#' make_style(bgMaroon = rgb(0.93, 0.19, 0.65), bg = TRUE)
+#' "pink" %in% names(styles())
+#' "bgMaroon" %in% names(styles())
+#' cat(style("I am pink, too!\n", "pink", bg = "bgMaroon"))
+
+make_style <- function(..., bg = FALSE, grey = FALSE,
+ colors = num_colors()) {
+
+ args <- list(...)
+ stopifnot(length(args) == 1)
+ style <- args[[1]]
+ orig_style_name <- style_name <- names(args)[1]
+
+ stopifnot(is.character(style) && length(style) == 1 ||
+ is_rgb_matrix(style) && ncol(style) == 1,
+ is.logical(bg) && length(bg) == 1,
+ is.numeric(colors) && length(colors) == 1)
+
+ ansi_seqs <- if (is_builtin_style(style)) {
+ if (bg && substr(style, 1, 2) != "bg") {
+ style <- "bg" %+% capitalize(style)
+ }
+ if (is.null(style_name)) style_name <- style
+ builtin_styles[[style]]
+
+ } else if (is_r_color(style)) {
+ if (is.null(style_name)) style_name <- style
+ style_from_r_color(style, bg, colors, grey)
+
+ } else if (is_rgb_matrix(style)) {
+ style_from_rgb(style, bg, colors, grey)
+
+ } else {
+ stop("Unknown style specification: ", style)
+ }
+
+ if (!is.null(orig_style_name)) define_style(orig_style_name, ansi_seqs)
+
+ make_crayon(structure(list(ansi_seqs), names = style_name))
+}
+
+make_crayon <- function(ansi_seq) {
+ crayon <- crayon_template
+ attr(crayon, "_styles") <- ansi_seq
+ class(crayon) <- "crayon"
+ crayon
+}
+
+#' @include styles.r
+#'
+#' @usage
+#' ## Simple styles
+#' red(...)
+#' bold(...)
+#' ...
+#'
+#' ## See more styling below
+#'
+#' @param ... Strings to style.
+#' @name crayon
+#
+#' @details
+#'
+#' Crayon defines several styles, that can be combined. Each style in the list
+#' has a corresponding function with the same name.
+#'
+#' @section Genaral styles:
+#'
+#' \itemize{
+#' \item reset
+#' \item bold
+#' \item blurred (usually called \sQuote{dim}, renamed to avoid name clash)
+#' \item italic (not widely supported)
+#' \item underline
+#' \item inverse
+#' \item hidden
+#' \item strikethrough (not widely supported)
+#' }
+#'
+#' @section Text colors:
+#'
+#' \itemize{
+#' \item black
+#' \item red
+#' \item green
+#' \item yellow
+#' \item blue
+#' \item magenta
+#' \item cyan
+#' \item white
+#' \item silver (usually called \sQuote{gray}, renamed to avoid name clash)
+#' }
+#'
+#' @section Background colors:
+#'
+#' \itemize{
+#' \item bgBlack
+#' \item bgRed
+#' \item bgGreen
+#' \item bgYellow
+#' \item bgBlue
+#' \item bgMagenta
+#' \item bgCyan
+#' \item bgWhite
+#' }
+#'
+#' @section Styling:
+#'
+#' The styling functions take any number of character vectors as arguments,
+#' and they concatenate and style them: \preformatted{ library(crayon)
+#' cat(blue("Hello", "world!\n"))
+#' }
+#'
+#' Crayon defines the \code{\%+\%} string concatenation operator, to make it easy
+#' to assemble stings with different styles. \preformatted{ cat("... to highlight the " \%+\% red("search term") \%+\%
+#' " in a block of text\n")
+#' }
+#'
+#' Styles can be combined using the \code{$} operator: \preformatted{ cat(yellow$bgMagenta$bold('Hello world!\n'))
+#' } See also \code{\link{combine_styles}}.
+#'
+#' Styles can also be nested, and then inner style takes
+#' precedence: \preformatted{ cat(green(
+#' 'I am a green line ' \%+\%
+#' blue$underline$bold('with a blue substring') \%+\%
+#' ' that becomes green again!\n'
+#' ))
+#' }
+#'
+#' It is easy to define your own themes: \preformatted{ error <- red $ bold
+#' warn <- magenta $ underline
+#' note <- cyan
+#' cat(error("Error: subscript out of bounds!\n"))
+#' cat(warn("Warning: shorter argument was recycled.\n"))
+#' cat(note("Note: no such directory.\n"))
+#' }
+#'
+#' @aliases
+#' reset bold blurred italic underline inverse hidden strikethrough
+#' black red green yellow blue magenta cyan white silver
+#' bgBlack bgRed bgGreen bgYellow bgBlue bgMagenta bgCyan bgWhite
+#'
+#' @export reset bold blurred italic underline inverse hidden strikethrough
+#' @export black red green yellow blue magenta cyan white silver
+#' @export bgBlack bgRed bgGreen bgYellow bgBlue bgMagenta bgCyan bgWhite
+#'
+#' @seealso \code{\link{make_style}} for using the 256 ANSI colors.
+#' @examples
+#' cat(blue("Hello", "world!"))
+#'
+#' cat("... to highlight the " \%+\% red("search term") \%+\%
+#' " in a block of text")
+#'
+#' cat(yellow$bgMagenta$bold('Hello world!'))
+#'
+#' cat(green(
+#' 'I am a green line ' \%+\%
+#' blue$underline$bold('with a blue substring') \%+\%
+#' ' that becomes green again!'
+#' ))
+#'
+#' error <- red $ bold
+#' warn <- magenta $ underline
+#' note <- cyan
+#' cat(error("Error: subscript out of bounds!\n"))
+#' cat(warn("Warning: shorter argument was recycled.\n"))
+#' cat(note("Note: no such directory.\n"))
+#'
+NULL
+
+#' ANSI escape sequences of crayon styles
+#'
+#' You can use this function to list all availables crayon styles,
+#' via \code{names(styles())}, or to explicitly apply an ANSI
+#' escape seauence to a string.
+#'
+#' @return A named list. Each list element is a list of two
+#' strings, named \sQuote{open} and \sQuote{close}.
+#'
+#' @seealso \code{\link{crayon}} for the beginning of the crayon manual.
+#' @export
+#' @examples
+#' names(styles())
+#' cat(styles()[["bold"]]$close)
+
+styles <- function() {
+ my_styles
+}
+
+my_styles <- structure(list(), names = character())
+
+sapply(names(builtin_styles), function(style) {
+ my_styles[[style]] <<- builtin_styles[[style]]
+ assign(style, make_style(style), envir = asNamespace(packageName()))
+})
+
+.onLoad <- function(libname, pkgname) {
+ num_colors(forget = TRUE)
+}
+
+.onAttach <- function(libname, pkgname) {
+ ub <- unlockBinding
+ ub("my_styles", asNamespace(pkgname))
+}
+
+define_style <- function(name, ansi_seq) {
+ my_styles[[name]] <<- ansi_seq
+}
+
+#' Remove a style
+#'
+#' @param style The name of the style to remove. No error is given
+#' for non-existing names.
+#' @return Nothing.
+#'
+#' @family styles
+#' @export
+#' @examples
+#' make_style(new_style = "maroon", bg = TRUE)
+#' cat(style("I am maroon", "new_style"), "\n")
+#' drop_style("new_style")
+#' "new_style" %in% names(styles())
+
+drop_style <- function(style) {
+ my_styles[[style]] <<- NULL
+ invisible()
+}
diff --git a/R/parts.r b/R/parts.r
new file mode 100644
index 0000000..59c727b
--- /dev/null
+++ b/R/parts.r
@@ -0,0 +1,58 @@
+
+#' Switch on or off a style
+#'
+#' Make a style active. The text printed to the screen from now
+#' on will use this style.
+#'
+#' @details
+#' This function is very rarely needed, e.g. for colored user
+#' input. For other reasons, just call the style as a function on
+#' the string.
+#'
+#' @param x Style.
+#' @param ... Ignored.
+#'
+#' @export
+#' @examples
+#' ## The input is red (if color is supported)
+#' get_name <- function() {
+#' cat("Enter your name:", start(red))
+#' input <- readline()
+#' cat(finish(red))
+#' input
+#' }
+#' name <- get_name()
+#' name
+
+start.crayon <- function(x, ...) {
+ if (has_color()) {
+ paste(
+ vapply(attr(x, "_styles"), "[[", "", "open"),
+ collapse = ""
+ )
+ } else {
+ ""
+ }
+}
+
+#' @importFrom stats start
+#' @rdname start.crayon
+#' @export
+
+finish <- function(x, ...)
+ UseMethod("finish")
+
+#' @rdname start.crayon
+#' @export
+#' @method finish crayon
+
+finish.crayon <- function(x, ...) {
+ if (has_color()) {
+ paste(
+ rev(vapply(attr(x, "_styles"), "[[", "", "close")),
+ collapse = ""
+ )
+ } else {
+ ""
+ }
+}
diff --git a/R/print.r b/R/print.r
new file mode 100644
index 0000000..93838d8
--- /dev/null
+++ b/R/print.r
@@ -0,0 +1,14 @@
+
+#' @export
+#' @method print crayon
+
+print.crayon <- function(x, ...) {
+ st <- paste(names(attr(x, "_styles")), collapse = ", ")
+ cat(sep = "",
+ "Crayon style function, ",
+ st,
+ ": ",
+ x("example output."),
+ "\n"
+ )
+}
diff --git a/R/show.r b/R/show.r
new file mode 100644
index 0000000..250b270
--- /dev/null
+++ b/R/show.r
@@ -0,0 +1,50 @@
+
+#' @include style-var.r
+NULL
+
+#' Show the ANSI color table on the screen
+#'
+#' @param colors Number of colors to show, meaningful values
+#' are 8 and 256. It is automatically set to the number of
+#' supported colors, if not specified.
+#' @return The printed string, invisibly.
+#'
+#' @export
+
+show_ansi_colors <- function(colors = num_colors()) {
+ if (colors < 8) {
+ cat("Colors are not supported")
+ } else if (colors < 256) {
+ cat(ansi_colors_8, sep = "")
+ invisible(ansi_colors_8)
+ } else {
+ cat(ansi_colors_256, sep = "")
+ invisible(ansi_colors_256)
+ }
+}
+
+ansi_colors_256_col <-
+ sapply(0:5, function(r) {
+ sapply(0:5, function(g) {
+ c(sapply(0:5, function(b) {
+ s <- paste0("r:", r, " g:", g, " b:", b, " ")
+ style(s, as = "grey", bg = rgb(r, g, b, maxColorValue = 5))
+ }), "\n")
+ })
+ })
+
+ansi_colors_256_grey <-
+ sapply(0:23, function(g) {
+ s <- paste0(" grey ", format(g, width = 2), " ")
+ style(s, as = "grey",
+ bg = make_style(grey(g / 23), grey = TRUE, bg = TRUE)) %+%
+ (if ((g + 1) %% 6) "" else "\n")
+ })
+
+ansi_colors_256 <- c(ansi_colors_256_col, "\n", ansi_colors_256_grey)
+
+ansi_colors_8 <-
+ multicol(sapply(seq_along(builtin_styles), function(s) {
+ st <- names(builtin_styles)[s]
+ styled <- st %+% ": " %+% style("foobar", as = st) %+% " "
+ }))
diff --git a/R/string.r b/R/string.r
new file mode 100644
index 0000000..a8636ae
--- /dev/null
+++ b/R/string.r
@@ -0,0 +1,57 @@
+
+#' Convert to character
+#'
+#' This function just calls \code{as.character}, but it is
+#' easier to type and read.
+#'
+#' @param x Object to be coerced.
+#' @param ... Further arguments to pass to \code{as.character}.
+#' @return Character value.
+#'
+#' @export
+
+chr <- function(x, ...) as.character(x, ...)
+
+#' Concatenate character vectors
+#'
+#' The length of the two arguments must match, or
+#' one of them must be of length one. If the length of
+#' one argument is one, then the output's length will
+#' match the length of the other argument. See examples
+#' below.
+#'
+#' @param lhs Left hand side, character vector.
+#' @param rhs Right hand side, character vector.
+#' @return Concatenated vectors.
+#'
+#' @name concat
+#' @export
+#'
+#' @examples
+#' "foo" %+% "bar"
+#'
+#' letters[1:10] %+% chr(1:10)
+#'
+#' letters[1:10] %+% "-" %+% chr(1:10)
+#'
+#' ## This is empty (unlike for parse)
+#' character() %+% "*"
+
+`%+%` <- function(lhs, rhs) {
+
+ stopifnot(is.character(lhs), is.character(rhs))
+ stopifnot(length(lhs) == length(rhs) || length(lhs) == 1 || length(rhs) == 1)
+
+ if (length(lhs) == 0 && length(rhs) == 0) {
+ paste0(lhs, rhs)
+
+ } else if (length(lhs) == 0) {
+ lhs
+
+ } else if (length(rhs) == 0) {
+ rhs
+
+ } else {
+ paste0(lhs, rhs)
+ }
+}
diff --git a/R/string_operations.r b/R/string_operations.r
new file mode 100644
index 0000000..33eac5b
--- /dev/null
+++ b/R/string_operations.r
@@ -0,0 +1,255 @@
+
+## Create a mapping between the string and its style-less version.
+## This is useful to work with the colored string.
+
+#' @importFrom utils tail
+
+map_to_ansi <- function(x, text = NULL) {
+
+ if (is.null(text)) {
+ text <- non_matching(re_table(ansi_regex, x), x, empty=TRUE)
+ }
+
+ map <- lapply(
+ text,
+ function(text) {
+ cbind(
+ pos = cumsum(c(1, text[, "length"], Inf)),
+ offset = c(text[, "start"] - 1, tail(text[, "end"], 1), NA)
+ )
+ })
+
+ function(pos) {
+ pos <- rep(pos, length.out = length(map))
+ mapply(pos, map, FUN = function(pos, table) {
+ if (pos < 1) {
+ pos
+ } else {
+ slot <- which(pos < table[, "pos"])[1] - 1
+ table[slot, "offset"] + pos - table[slot, "pos"] + 1
+ }
+ })
+ }
+}
+
+
+#' Count number of characters in an ANSI colored string
+#'
+#' This is a color-aware counterpart of \code{base::nchar},
+#' which does not do well, since it also counts the ANSI control
+#' characters.
+#'
+#' @param x Character vector, potentially ANSO styled, or a vector to be
+#' coarced to character.
+#' @param ... Additional arguments, passed on to \code{base::nchar}
+#' after removing ANSI escape sequences.
+#' @return Numeric vector, the length of the strings in the character
+#' vector.
+#'
+#' @family ANSI string operations
+#' @export
+#' @examples
+#' str <- paste(
+#' red("red"),
+#' "default",
+#' green("green")
+#' )
+#'
+#' cat(str, "\n")
+#' nchar(str)
+#' col_nchar(str)
+#' nchar(strip_style(str))
+
+col_nchar <- function(x, ...) {
+ base::nchar(strip_style(x), ...)
+}
+
+
+#' Substring(s) of an ANSI colored string
+#'
+#' This is a color-aware counterpart of \code{base::substr}.
+#' It works exactly like the original, but keeps the colors
+#' in the substrings. The ANSI escape sequences are ignored when
+#' calculating the positions within the string.
+#'
+#' @param x Character vector, potentially ANSI styled, or a vector to
+#' coarced to character.
+#' @param start Starting index or indices, recycled to match the length
+#' of \code{x}.
+#' @param stop Ending index or indices, recycled to match the length
+#' of \code{x}.
+#' @return Character vector of the same length as \code{x}, containing
+#' the requested substrings. ANSI styles are retained.
+#'
+#' @family ANSI string operations
+#' @export
+#' @examples
+#' str <- paste(
+#' red("red"),
+#' "default",
+#' green("green")
+#' )
+#'
+#' cat(str, "\n")
+#' cat(col_substr(str, 1, 5), "\n")
+#' cat(col_substr(str, 1, 15), "\n")
+#' cat(col_substr(str, 3, 7), "\n")
+#'
+#' substr(strip_style(str), 1, 5)
+#' substr(strip_style(str), 1, 15)
+#' substr(strip_style(str), 3, 7)
+#'
+#' str2 <- "another " %+%
+#' red("multi-", sep = "", underline("style")) %+%
+#' " text"
+#'
+#' cat(str2, "\n")
+#' cat(col_substr(c(str, str2), c(3,5), c(7, 18)), sep = "\n")
+#' substr(strip_style(c(str, str2)), c(3,5), c(7, 18))
+
+col_substr <- function(x, start, stop) {
+ if(!is.character(x)) x <- as.character(x)
+ if(!length(x)) return(x)
+ start <- as.integer(start)
+ stop <- as.integer(stop)
+ if(!length(start) || !length(stop))
+ stop("invalid substring arguments")
+ if(anyNA(start) || anyNA(stop))
+ stop("non-numeric substring arguments not supported")
+ ansi <- re_table(ansi_regex, x)
+ text <- non_matching(ansi, x, empty=TRUE)
+ mapper <- map_to_ansi(x, text = text)
+ nstart <- mapper(start)
+ nstop <- mapper(stop)
+
+ bef <- base::substr(x, 1, nstart - 1)
+ aft <- base::substr(x, nstop + 1, base::nchar(x))
+ ansi_bef <- vapply(regmatches(bef, gregexpr(ansi_regex, bef)),
+ paste, collapse = "", FUN.VALUE = "")
+ ansi_aft <- vapply(regmatches(aft, gregexpr(ansi_regex, aft)),
+ paste, collapse = "", FUN.VALUE = "")
+
+ paste(sep = "", ansi_bef, base::substr(x, nstart, nstop), ansi_aft)
+}
+
+#' Substring(s) of an ANSI colored string
+#'
+#' This is the color-aware counterpart of \code{base::substring}.
+#' It works exactly like the original, but keeps the colors in the
+#' substrings. The ANSI escape sequences are ignored when
+#' calculating the positions within the string.
+#'
+#' @param text Character vector, potentially ANSI styled, or a vector to
+#' coarced to character. It is recycled to the longest of \code{first}
+#' and \code{last}.
+#' @param first Starting index or indices, recycled to match the length
+#' of \code{x}.
+#' @param last Ending index or indices, recycled to match the length
+#' of \code{x}.
+#' @return Character vector of the same length as \code{x}, containing
+#' the requested substrings. ANSI styles are retained.
+#'
+#' @family ANSI string operations
+#' @export
+#' @examples
+#' str <- paste(
+#' red("red"),
+#' "default",
+#' green("green")
+#' )
+#'
+#' cat(str, "\n")
+#' cat(col_substring(str, 1, 5), "\n")
+#' cat(col_substring(str, 1, 15), "\n")
+#' cat(col_substring(str, 3, 7), "\n")
+#'
+#' substring(strip_style(str), 1, 5)
+#' substring(strip_style(str), 1, 15)
+#' substring(strip_style(str), 3, 7)
+#'
+#' str2 <- "another " %+%
+#' red("multi-", sep = "", underline("style")) %+%
+#' " text"
+#'
+#' cat(str2, "\n")
+#' cat(col_substring(str2, c(3,5), c(7, 18)), sep = "\n")
+#' substring(strip_style(str2), c(3,5), c(7, 18))
+
+col_substring <- function(text, first, last = 1000000L) {
+ if (!is.character(text)) text <- as.character(text)
+ n <- max(lt <- length(text), length(first), length(last))
+ if (lt && lt < n) text <- rep_len(text, length.out = n)
+ col_substr(text, as.integer(first), as.integer(last))
+}
+
+
+#' Split an ANSI colored string
+#'
+#' This is the color-aware counterpart of \code{base::strsplit}.
+#' It works almost exactly like the original, but keeps the colors in the
+#' substrings.
+#'
+#' @param x Character vector, potentially ANSI styled, or a vector to
+#' coarced to character.
+#' @param split Character vector of length 1 (or object which can be coerced to
+#' such) containing regular expression(s) (unless \code{fixed = TRUE}) to use
+#' for splitting. If empty matches occur, in particular if \code{split} has
+#' zero characters, \code{x} is split into single characters.
+#' @param ... Extra arguments are passed to \code{base::strsplit}.
+#' @return A list of the same length as \code{x}, the \eqn{i}-th element of
+#' which contains the vector of splits of \code{x[i]}. ANSI styles are
+#' retained.
+#'
+#' @family ANSI string operations
+#' @export
+#' @importFrom utils head
+#' @examples
+#' str <- red("I am red---") %+%
+#' green("and I am green-") %+%
+#' underline("I underlined")
+#'
+#' cat(str, "\n")
+#'
+#' # split at dashes, keep color
+#' cat(col_strsplit(str, "[-]+")[[1]], sep = "\n")
+#' strsplit(strip_style(str), "[-]+")
+#'
+#' # split to characters, keep color
+#' cat(col_strsplit(str, "")[[1]], "\n", sep = " ")
+#' strsplit(strip_style(str), "")
+
+col_strsplit <- function(x, split, ...) {
+ split <- try(as.character(split), silent=TRUE)
+ if(inherits(split, "try-error") || !is.character(split) || length(split) > 1L)
+ stop("`split` must be character of length <= 1, or must coerce to that")
+ if(!length(split)) split <- ""
+ plain <- strip_style(x)
+ splits <- re_table(split, plain, ...)
+ chunks <- non_matching(splits, plain, empty = TRUE)
+ # silently recycle `split`; doesn't matter currently since we don't support
+ # split longer than 1, but might in future
+ split.r <- rep(split, length.out=length(x))
+ # Drop empty chunks to align with `substr` behavior
+ chunks <- lapply(
+ seq_along(chunks),
+ function(i) {
+ y <- chunks[[i]]
+ # empty split means drop empty first match
+ if(nrow(y) && !nzchar(split.r[[i]]) && !head(y, 1L)[, "length"]) {
+ y <- y[-1L, , drop=FALSE]
+ }
+ # drop empty last matches
+ if(nrow(y) && !tail(y, 1L)[, "length"]) y[-nrow(y), , drop=FALSE] else y
+ }
+ )
+ zero.chunks <- !vapply(chunks, nrow, integer(1L))
+ # Pull out zero chunks from colored string b/c col_substring won't work
+ # with them
+ res <- vector("list", length(chunks))
+ res[zero.chunks] <- list(character(0L))
+ res[!zero.chunks] <- mapply(
+ chunks[!zero.chunks], x[!zero.chunks], SIMPLIFY = FALSE,
+ FUN = function(tab, xx) col_substring(xx, tab[, "start"], tab[, "end"])
+ )
+ res
+}
diff --git a/R/style-var.r b/R/style-var.r
new file mode 100644
index 0000000..b8fc67c
--- /dev/null
+++ b/R/style-var.r
@@ -0,0 +1,45 @@
+
+#' Add style to a string
+#'
+#' See \code{names(styles)}, or the crayon manual for available styles.
+#'
+#' @param string Character vector to style.
+#' @param as Style function to apply, either the function object,
+#' or its name, or an object to pass to \code{\link{make_style}}.
+#' @param bg Background style, a style function, or a name that
+#' is passed to \code{\link{make_style}}.
+#' @return Styled character vector.
+#'
+#' @export
+#' @importFrom methods is
+#'
+#' @examples
+#' ## These are equivalent
+#' style("foobar", bold)
+#' style("foobar", "bold")
+#' bold("foobar")
+
+style <- function(string, as = NULL, bg = NULL) {
+
+ as <- use_or_make_style(as)
+ bg <- use_or_make_style(bg, bg = TRUE)
+
+ if (!is(as, "crayon")) stop("Cannot make style from 'as'")
+ if (!is(bg, "crayon")) stop("Cannot make style from 'bg'")
+
+ as(bg(string))
+}
+
+#' @importFrom methods is
+
+use_or_make_style <- function(style, bg = FALSE) {
+ if (is.null(style)) {
+ structure(base::identity, class = "crayon")
+ } else if (is(style, "crayon")) {
+ style
+ } else if (style %in% names(styles())) {
+ make_crayon(styles()[style])
+ } else {
+ make_style(style, bg = bg)
+ }
+}
diff --git a/R/styles.r b/R/styles.r
new file mode 100644
index 0000000..2d866c7
--- /dev/null
+++ b/R/styles.r
@@ -0,0 +1,72 @@
+
+## ----------------------------------------------------------------------
+## Styles
+
+codes <- list(
+ reset = c(0, 0),
+ bold = c(1, 22), # 21 isn't widely supported and 22 does the same thing
+ blurred = c(2, 22),
+ italic = c(3, 23),
+ underline = c(4, 24),
+ inverse = c(7, 27),
+ hidden = c(8, 28),
+ strikethrough = c(9, 29),
+
+ black = c(30, 39),
+ red = c(31, 39),
+ green = c(32, 39),
+ yellow = c(33, 39),
+ blue = c(34, 39),
+ magenta = c(35, 39),
+ cyan = c(36, 39),
+ white = c(37, 39),
+ silver = c(90, 39),
+
+ bgBlack = c(40, 49),
+ bgRed = c(41, 49),
+ bgGreen = c(42, 49),
+ bgYellow = c(43, 49),
+ bgBlue = c(44, 49),
+ bgMagenta = c(45, 49),
+ bgCyan = c(46, 49),
+ bgWhite = c(47, 49)
+)
+
+## ANSI fg color -> R color
+
+ansi_fg_r <- c(
+ "black" = "black",
+ "red" = "red",
+ "green" = "green",
+ "yellow" = "yellow",
+ "blue" = "blue",
+ "magenta" = "magenta",
+ "cyan" = "cyan",
+ "white" = "white",
+ "silver" = "grey"
+)
+
+ansi_fg_rgb <- col2rgb(ansi_fg_r)
+
+ansi_bg_r <- c(
+ "bgBlack" = "black",
+ "bgRed" = "red",
+ "bgGreen" = "green",
+ "bgYellow" = "yellow",
+ "bgBlue" = "blue",
+ "bgMagenta" = "magenta",
+ "bgCyan" = "cyan",
+ "bgWhite" = "white"
+)
+
+ansi_bg_rgb <- col2rgb(ansi_bg_r)
+
+make_chr_style <- function(code) {
+ list(
+ open = '\u001b[' %+% chr(codes[[code]][1]) %+% 'm',
+ close = '\u001b[' %+% chr(codes[[code]][2]) %+% 'm'
+ )
+}
+
+builtin_styles <- lapply(names(codes), make_chr_style)
+names(builtin_styles) <- names(codes)
diff --git a/R/utils.r b/R/utils.r
new file mode 100644
index 0000000..3938dae
--- /dev/null
+++ b/R/utils.r
@@ -0,0 +1,129 @@
+
+data_frame <- function(...) {
+
+ args <- list(...)
+
+ ## Replicate arguments if needed
+ len <- vapply(args, length, numeric(1))
+ stopifnot(length(setdiff(len, 1)) <= 1)
+ len <- max(0, max(len))
+ args <- lapply(args, function(x) rep(x, length.out = len))
+
+ ## Names
+ names <- as.character(names(args))
+ length(names) <- length(args)
+ names <- ifelse(
+ is.na(names) | names == "",
+ paste0("V", seq_along(args)),
+ names)
+
+ structure(args,
+ class = "data.frame",
+ names = names,
+ row.names = seq_along(args[[1]]))
+}
+
+check_string <- function(x) {
+ stopifnot(is.character(x), length(x) == 1, !is.na(x))
+}
+
+mypaste <- function(..., sep = " ") {
+ args <- list(...)
+ if (any(!sapply(args, is.character))) stop("Need character strings")
+ len <- setdiff(sapply(args, length), 1)
+ if (length(len) > 1) {
+ stop("All character vectors must have the same length (or length 1)")
+ }
+
+ paste(..., sep = sep)
+}
+
+scale <- function(x, from = c(0, 255), to = c(0, 5), round = TRUE) {
+ y <- (x - from[1]) /
+ (from[2] - from[1]) *
+ (to[2] - to[1]) +
+ to[1]
+
+ if (round) {
+ round(y)
+ } else {
+ y
+ }
+}
+
+capitalize <- function(x) {
+ substr(x, 1, 1) <- toupper(substr(x, 1, 1))
+ x
+}
+
+multicol <- function(x) {
+ xs <- strip_style(x)
+ max_len <- max(nchar(xs))
+ to_add <- max_len - nchar(xs)
+ x <- paste0(x, substring(" ", 1, to_add))
+ screen_width <- getOption("width")
+ num_cols <- trunc(screen_width / max_len)
+ num_rows <- ceiling(length(x) / num_cols)
+ x <- c(x, rep("", num_cols * num_rows - length(x)))
+ xm <- matrix(x, ncol = num_cols, byrow = TRUE)
+ apply(xm, 1, paste, collapse = "") %+% "\n"
+}
+
+re_table <- function(...) {
+ lapply(gregexpr(...), function(x) {
+ res <- cbind(
+ start = x,
+ end = x + attr(x, "match.length") - 1,
+ length = attr(x, "match.length")
+ )
+ res <- res[res[, "start"] != -1, , drop=FALSE]
+ })
+}
+
+## Create the non-matching table from the matching table
+
+non_matching <- function(table, str, empty = FALSE) {
+ mapply(table, str, SIMPLIFY = FALSE, FUN = function(t, s) {
+ if (! nrow(t)) {
+ cbind(start = 1, end = base::nchar(s), length = base::nchar(s))
+ } else {
+ start <- c(1, t[, "end"] + 1)
+ end <- c(t[, "start"] - 1, base::nchar(s))
+ res <- cbind(start = start, end = end, length = end - start + 1)
+ if (!empty) res[ res[, "length"] != 0, , drop = FALSE ] else res
+ }
+ })
+}
+
+myseq <- function(from, to, by = 1) {
+ stopifnot(by != 0)
+ if (by > 0) {
+ if (to < from) {
+ integer()
+ } else {
+ seq(from, to, by = by)
+ }
+ } else {
+ if (to > from) {
+ integer()
+ } else {
+ seq(from, to, by = by)
+ }
+ }
+}
+
+`%:%` <- myseq
+
+emacs_version <- function() {
+ ver <- Sys.getenv("INSIDE_EMACS")
+ if (ver == "") return(NA_integer_)
+
+ ver <- gsub("'", "", ver)
+ ver <- strsplit(ver, ",", fixed = TRUE)[[1]]
+ ver <- strsplit(ver, ".", fixed = TRUE)[[1]]
+ as.numeric(ver)
+}
+
+inside_emacs <- function() {
+ Sys.getenv("EMACS") != ""
+}
diff --git a/debian/README.test b/debian/README.test
deleted file mode 100644
index 53fb4d7..0000000
--- a/debian/README.test
+++ /dev/null
@@ -1,8 +0,0 @@
-Notes on how this package can be tested.
-────────────────────────────────────────
-
-This package can be tested by running the provided test:
-
- sh ./run-unit-test
-
-in order to confirm its integrity.
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 1fe84c5..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,19 +0,0 @@
-r-cran-crayon (1.3.2-1) unstable; urgency=medium
-
- * New upstream version
- * Convert to dh-r
- * Canonical homepage for CRAN
-
- -- Andreas Tille <tille at debian.org> Mon, 07 Nov 2016 19:39:54 +0100
-
-r-cran-crayon (1.3.1-1) unstable; urgency=medium
-
- * New upstream version
-
- -- Andreas Tille <tille at debian.org> Sat, 31 Oct 2015 08:36:27 +0100
-
-r-cran-crayon (1.3.0-1) unstable; urgency=medium
-
- * Initial upload (Closes: #791854)
-
- -- Andreas Tille <tille at debian.org> Sat, 09 May 2015 08:52:18 +0200
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 3e84117..0000000
--- a/debian/control
+++ /dev/null
@@ -1,28 +0,0 @@
-Source: r-cran-crayon
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>
-Section: gnu-r
-Priority: optional
-Build-Depends: debhelper (>= 9),
- dh-r,
- r-base-dev,
- r-cran-memoise,
- r-cran-digest
-Standards-Version: 3.9.8
-Vcs-Browser: https://anonscm.debian.org/viewvc/debian-med/trunk/packages/R/r-cran-crayon/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/R/r-cran-crayon/
-Homepage: https://cran.r-project.org/package=crayon
-
-Package: r-cran-crayon
-Architecture: any
-Depends: ${shlibs:Depends},
- ${misc:Depends},
- ${R:Depends}
-Recommends: ${R:Recommends}
-Suggests: ${R:Suggests}
-Description: GNU R colored terminal output
- Colored terminal output on terminals that support ANSI color and
- highlight codes. ANSI color support is automatically detected. Colors
- and highlighting can be combined and nested. New styles can also be
- created easily. This package was inspired by the chalk JavaScript
- project.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 24938f2..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,35 +0,0 @@
-Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Contact: Gabor Csardi <csardi.gabor at gmail.com>
-Upstream-Name: crayon
-Source: https://cran.r-project.org/package=crayon
-Comment: The license is mentioned on the source download page as
- link to the license text http://cran.r-project.org/web/licenses/MIT
-
-Files: *
-Copyright: 2014-2016 Gabor Csardi <csardi.gabor at gmail.com>
-License: MIT
-
-Files: debian/*
-Copyright: 2015-2016 Andreas Tille <tille at debian.org>
-License: MIT
-
-License: MIT
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
- .
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
diff --git a/debian/docs b/debian/docs
deleted file mode 100644
index 960011c..0000000
--- a/debian/docs
+++ /dev/null
@@ -1,3 +0,0 @@
-tests
-debian/README.test
-debian/tests/run-unit-test
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 2a9f630..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/make -f
-
-debRreposname := $(shell dpkg-parsechangelog | awk '/^Source:/ {print $$2}' | sed 's/r-\([a-z]\+\)-.*/\1/')
-awkString := "'/^(Package|Bundle):/ {print $$2 }'"
-cranNameOrig := $(shell awk "$(awkString)" DESCRIPTION)
-cranName := $(shell echo "$(cranNameOrig)" | tr 'A-Z' 'a-z')
-package := r-$(debRreposname)-$(cranName)
-debRdir := usr/lib/R/site-library
-debRlib := $(CURDIR)/debian/$(package)/$(debRdir)
-
-%:
- dh $@ --buildsystem R
-
-override_dh_install:
- dh_install
- rm -rf debian/$(package)/usr/lib/R/site-library/$(cranName)/LICENSE
- mkdir -p debian/$(package)/usr/share/R/site-library/$(cranName)
- mv debian/$(package)/usr/lib/R/site-library/$(cranName)/*.png debian/$(package)/usr/share/R/site-library/$(cranName)
- for img in debian/$(package)/usr/share/R/site-library/$(cranName)/*.png ; do \
- ln -s ../../../../share/R/site-library/$(cranName)/`basename $${img}` debian/$(package)/usr/lib/R/site-library/$(cranName)/`basename $${img}` ; \
- done
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/tests/control b/debian/tests/control
deleted file mode 100644
index b044b0c..0000000
--- a/debian/tests/control
+++ /dev/null
@@ -1,3 +0,0 @@
-Tests: run-unit-test
-Depends: @, r-cran-testthat
-Restrictions: allow-stderr
diff --git a/debian/tests/run-unit-test b/debian/tests/run-unit-test
deleted file mode 100644
index f3d1af1..0000000
--- a/debian/tests/run-unit-test
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh -e
-
-pkg=r-cran-crayon
-
-if [ "$ADTTMP" = "" ] ; then
- ADTTMP=`mktemp -d /tmp/${pkg}-test.XXXXXX`
-fi
-cd $ADTTMP
-cp -a /usr/share/doc/${pkg}/tests/* $ADTTMP
-find . -name "*.gz" -exec gunzip \{\} \;
-# Make sure we are using C locale to pass all tests
-LC_ALL=C R --no-save < testthat.R
-rm -rf $ADTTMP/*
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index 52b38f7..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,2 +0,0 @@
-version=3
-http://cran.r-project.org/src/contrib/crayon_([\d.-]*)\.tar.gz
diff --git a/inst/ANSI-256-OSX.png b/inst/ANSI-256-OSX.png
new file mode 100644
index 0000000..495c9ee
Binary files /dev/null and b/inst/ANSI-256-OSX.png differ
diff --git a/inst/ANSI-8-OSX.png b/inst/ANSI-8-OSX.png
new file mode 100644
index 0000000..c1e7120
Binary files /dev/null and b/inst/ANSI-8-OSX.png differ
diff --git a/inst/NEWS.md b/inst/NEWS.md
new file mode 100644
index 0000000..823069f
--- /dev/null
+++ b/inst/NEWS.md
@@ -0,0 +1,70 @@
+# 1.3.2
+
+* Removed dependency to `memoise` (@brodieG, #25)
+
+* Fixed a test case that changed the `crayon.enabled`
+ setting, potentially (@brodieG)
+
+* Added `crayon.colors` option, to specify the number of
+ colors explicitly
+
+* `TERM=xterm` and `tput colors=8` will use 256 colors,
+ as 256 colors are usually supported in this case (#17)
+
+* Support colors in ConEmu and cmder, on Windows
+
+* Fix color detection in Emacs tramp
+
+* `col_strsplit` and `col_substr` corner cases:
+
+ * handle empty chunks at beginning or end of strings
+ like `base::strsplit` (@brodieG, #26)
+
+ * explicitly deal with 'split' values that are not
+ length 1 as that is not currently supported
+
+ * handle zero length `x` argument in `col_substr`, and
+ add more explicit error messages for corner cases
+
+* Some performance improvements to `col_substr` (@brodieG)
+
+* Change rgb to ANSI code mapping, based on the "paint" ruby gem
+ (@richfitz, #33, #34)
+
+# 1.3.1
+
+* Fixed some `R CMD check` problems.
+
+# 1.3.0
+
+* Colors are turned on by default in Emacs ESS 23.x and above.
+
+* Functions to turn on and off a style: `start`, `finish`.
+
+* Really fix `tput` corner cases (@jimhester, #21)
+
+# 1.2.1
+
+* Fix detecting number of colors when `tput` exists, but
+ fails with an error and/or does not return anything useful.
+ (@jimhester, #18, #19)
+
+# 1.2.0
+
+* Fix detection of number of colors, it was cached from
+ installation time (#17).
+
+* Color aware string operations. They are slow and experimental
+ currently.
+
+# 1.1.0
+
+* `show_ansi_colors()` prints all supported colors on the screen.
+
+* 256 colors, on terminals that support it.
+
+* Disable colors on Windows, they are not supported in the default setup.
+
+# 1.0.0
+
+* First released version.
diff --git a/inst/README.markdown b/inst/README.markdown
new file mode 100644
index 0000000..520afdf
--- /dev/null
+++ b/inst/README.markdown
@@ -0,0 +1,140 @@
+
+<h1 align="center">
+ <br>
+ <br>
+ <img width="400" src="./inst/logo.png" alt="crayon">
+ <br>
+ <br>
+ <br>
+</h1>
+
+> Stylish terminal output in R
+
+[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active)
+[![Linux Build Status](https://travis-ci.org/gaborcsardi/crayon.svg?branch=master)](https://travis-ci.org/gaborcsardi/crayon)
+[![Windows Build status](https://ci.appveyor.com/api/projects/status/github/gaborcsardi/crayon?svg=true)](https://ci.appveyor.com/project/gaborcsardi/crayon)
+[![](http://www.r-pkg.org/badges/version/crayon)](http://cran.rstudio.com/web/packages/crayon/index.html)
+[![CRAN RStudio mirror downloads](http://cranlogs.r-pkg.org/badges/crayon)](http://cran.r-project.org/web/packages/crayon/index.html)
+[![Coverage Status](https://img.shields.io/codecov/c/github/gaborcsardi/crayon/master.svg)](https://codecov.io/github/gaborcsardi/crayon?branch=master)
+
+With crayon it is easy to add color to terminal output, create styles for notes, warnings, errors; and combine styles.
+
+ANSI color support is automatically detected and used. Crayon was largely
+inspired by [chalk](https://github.com/sindresorhus/chalk).
+
+## Installation
+
+```r
+devtools::install_github("gaborcsardi/crayon")
+library(crayon)
+```
+
+## Styles
+
+Crayon defines several styles, that can be combined. Each style in the list
+has a corresponding function with the same name.
+
+### General styles
+
+* `reset`
+* `bold`
+* `blurred` (usually called `dim`, renamed to avoid name clash)
+* `italic` (not widely supported)
+* `underline`
+* `inverse`
+* `hidden`
+* `strikethrough` (not widely supported)
+
+### Text colors
+
+* `black`
+* `red`
+* `green`
+* `yellow`
+* `blue`
+* `magenta`
+* `cyan`
+* `white`
+* `silver` (usually called `gray`, renamed to avoid name clash)
+
+### Background colors
+
+* `bgBlack`
+* `bgRed`
+* `bgGreen`
+* `bgYellow`
+* `bgBlue`
+* `bgMagenta`
+* `bgCyan`
+* `bgWhite`
+
+### Screenshot on OSX
+
+![](/inst/ANSI-8-OSX.png)
+
+## Usage
+
+The styling functions take any number of character vectors as arguments,
+and they concatenate and style them:
+
+```r
+library(crayon)
+cat(blue("Hello", "world!\n"))
+```
+
+Crayon defines the `%+%` string concatenation operator, to make it easy
+to assemble stings with different styles.
+
+```r
+cat("... to highlight the " %+% red("search term") %+% " in a block of text\n")
+```
+
+Styles can be combined using the `$` operator:
+
+```r
+cat(yellow$bgMagenta$bold('Hello world!\n'))
+```
+
+Styles can also be nested, and then inner style takes precedence:
+
+```r
+cat(green(
+ 'I am a green line ' %+%
+ blue$underline$bold('with a blue substring') %+%
+ ' that becomes green again!\n'
+))
+```
+
+It is easy to define your own themes:
+
+```r
+error <- red $ bold
+warn <- magenta $ underline
+note <- cyan
+cat(error("Error: subscript out of bounds!\n"))
+cat(warn("Warning: shorter argument was recycled.\n"))
+cat(note("Note: no such directory.\n"))
+```
+
+## 256 colors
+
+Most modern terminals support the ANSI standard for 256 colors,
+and you can define new styles that make use of them. The `make_style`
+function defines a new style. It can handle R's built in color names
+(see the output of `colors()`), and also RGB specifications, via the
+`rbg()` function. It automatically chooses the ANSI colors that
+are closest to the specified R and RGB colors, and it also has
+a fallback to terminals with 8 ANSI colors only.
+
+```r
+ivory <- make_style("ivory")
+bgMaroon <- make_style("maroon", bg = TRUE)
+fancy <- combine_styles(ivory, bgMaroon)
+cat(fancy("This will have some fancy colors"), "\n")
+```
+
+![](/inst/ANSI-256-OSX.png)
+
+## License
+
+MIT @ Gábor Csárdi
diff --git a/inst/logo.png b/inst/logo.png
new file mode 100644
index 0000000..94e839f
Binary files /dev/null and b/inst/logo.png differ
diff --git a/inst/logo.svg.gz b/inst/logo.svg.gz
new file mode 100644
index 0000000..a8cc032
Binary files /dev/null and b/inst/logo.svg.gz differ
diff --git a/man/chr.Rd b/man/chr.Rd
new file mode 100644
index 0000000..df7f332
--- /dev/null
+++ b/man/chr.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/string.r
+\name{chr}
+\alias{chr}
+\title{Convert to character}
+\usage{
+chr(x, ...)
+}
+\arguments{
+\item{x}{Object to be coerced.}
+
+\item{...}{Further arguments to pass to \code{as.character}.}
+}
+\value{
+Character value.
+}
+\description{
+This function just calls \code{as.character}, but it is
+easier to type and read.
+}
+
diff --git a/man/col_nchar.Rd b/man/col_nchar.Rd
new file mode 100644
index 0000000..16d173c
--- /dev/null
+++ b/man/col_nchar.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/string_operations.r
+\name{col_nchar}
+\alias{col_nchar}
+\title{Count number of characters in an ANSI colored string}
+\usage{
+col_nchar(x, ...)
+}
+\arguments{
+\item{x}{Character vector, potentially ANSO styled, or a vector to be
+coarced to character.}
+
+\item{...}{Additional arguments, passed on to \code{base::nchar}
+after removing ANSI escape sequences.}
+}
+\value{
+Numeric vector, the length of the strings in the character
+ vector.
+}
+\description{
+This is a color-aware counterpart of \code{base::nchar},
+which does not do well, since it also counts the ANSI control
+characters.
+}
+\examples{
+str <- paste(
+ red("red"),
+ "default",
+ green("green")
+)
+
+cat(str, "\\n")
+nchar(str)
+col_nchar(str)
+nchar(strip_style(str))
+}
+\seealso{
+Other ANSI string operations: \code{\link{col_strsplit}},
+ \code{\link{col_substring}}, \code{\link{col_substr}}
+}
+
diff --git a/man/col_strsplit.Rd b/man/col_strsplit.Rd
new file mode 100644
index 0000000..36b5582
--- /dev/null
+++ b/man/col_strsplit.Rd
@@ -0,0 +1,49 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/string_operations.r
+\name{col_strsplit}
+\alias{col_strsplit}
+\title{Split an ANSI colored string}
+\usage{
+col_strsplit(x, split, ...)
+}
+\arguments{
+\item{x}{Character vector, potentially ANSI styled, or a vector to
+coarced to character.}
+
+\item{split}{Character vector of length 1 (or object which can be coerced to
+such) containing regular expression(s) (unless \code{fixed = TRUE}) to use
+for splitting. If empty matches occur, in particular if \code{split} has
+zero characters, \code{x} is split into single characters.}
+
+\item{...}{Extra arguments are passed to \code{base::strsplit}.}
+}
+\value{
+A list of the same length as \code{x}, the \eqn{i}-th element of
+ which contains the vector of splits of \code{x[i]}. ANSI styles are
+ retained.
+}
+\description{
+This is the color-aware counterpart of \code{base::strsplit}.
+It works almost exactly like the original, but keeps the colors in the
+substrings.
+}
+\examples{
+str <- red("I am red---") \%+\%
+ green("and I am green-") \%+\%
+ underline("I underlined")
+
+cat(str, "\\n")
+
+# split at dashes, keep color
+cat(col_strsplit(str, "[-]+")[[1]], sep = "\\n")
+strsplit(strip_style(str), "[-]+")
+
+# split to characters, keep color
+cat(col_strsplit(str, "")[[1]], "\\n", sep = " ")
+strsplit(strip_style(str), "")
+}
+\seealso{
+Other ANSI string operations: \code{\link{col_nchar}},
+ \code{\link{col_substring}}, \code{\link{col_substr}}
+}
+
diff --git a/man/col_substr.Rd b/man/col_substr.Rd
new file mode 100644
index 0000000..63d1592
--- /dev/null
+++ b/man/col_substr.Rd
@@ -0,0 +1,57 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/string_operations.r
+\name{col_substr}
+\alias{col_substr}
+\title{Substring(s) of an ANSI colored string}
+\usage{
+col_substr(x, start, stop)
+}
+\arguments{
+\item{x}{Character vector, potentially ANSI styled, or a vector to
+coarced to character.}
+
+\item{start}{Starting index or indices, recycled to match the length
+of \code{x}.}
+
+\item{stop}{Ending index or indices, recycled to match the length
+of \code{x}.}
+}
+\value{
+Character vector of the same length as \code{x}, containing
+ the requested substrings. ANSI styles are retained.
+}
+\description{
+This is a color-aware counterpart of \code{base::substr}.
+It works exactly like the original, but keeps the colors
+in the substrings. The ANSI escape sequences are ignored when
+calculating the positions within the string.
+}
+\examples{
+str <- paste(
+ red("red"),
+ "default",
+ green("green")
+)
+
+cat(str, "\\n")
+cat(col_substr(str, 1, 5), "\\n")
+cat(col_substr(str, 1, 15), "\\n")
+cat(col_substr(str, 3, 7), "\\n")
+
+substr(strip_style(str), 1, 5)
+substr(strip_style(str), 1, 15)
+substr(strip_style(str), 3, 7)
+
+str2 <- "another " \%+\%
+ red("multi-", sep = "", underline("style")) \%+\%
+ " text"
+
+cat(str2, "\\n")
+cat(col_substr(c(str, str2), c(3,5), c(7, 18)), sep = "\\n")
+substr(strip_style(c(str, str2)), c(3,5), c(7, 18))
+}
+\seealso{
+Other ANSI string operations: \code{\link{col_nchar}},
+ \code{\link{col_strsplit}}, \code{\link{col_substring}}
+}
+
diff --git a/man/col_substring.Rd b/man/col_substring.Rd
new file mode 100644
index 0000000..df35131
--- /dev/null
+++ b/man/col_substring.Rd
@@ -0,0 +1,58 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/string_operations.r
+\name{col_substring}
+\alias{col_substring}
+\title{Substring(s) of an ANSI colored string}
+\usage{
+col_substring(text, first, last = 1000000L)
+}
+\arguments{
+\item{text}{Character vector, potentially ANSI styled, or a vector to
+coarced to character. It is recycled to the longest of \code{first}
+and \code{last}.}
+
+\item{first}{Starting index or indices, recycled to match the length
+of \code{x}.}
+
+\item{last}{Ending index or indices, recycled to match the length
+of \code{x}.}
+}
+\value{
+Character vector of the same length as \code{x}, containing
+ the requested substrings. ANSI styles are retained.
+}
+\description{
+This is the color-aware counterpart of \code{base::substring}.
+It works exactly like the original, but keeps the colors in the
+substrings. The ANSI escape sequences are ignored when
+calculating the positions within the string.
+}
+\examples{
+str <- paste(
+ red("red"),
+ "default",
+ green("green")
+)
+
+cat(str, "\\n")
+cat(col_substring(str, 1, 5), "\\n")
+cat(col_substring(str, 1, 15), "\\n")
+cat(col_substring(str, 3, 7), "\\n")
+
+substring(strip_style(str), 1, 5)
+substring(strip_style(str), 1, 15)
+substring(strip_style(str), 3, 7)
+
+str2 <- "another " \%+\%
+ red("multi-", sep = "", underline("style")) \%+\%
+ " text"
+
+cat(str2, "\\n")
+cat(col_substring(str2, c(3,5), c(7, 18)), sep = "\\n")
+substring(strip_style(str2), c(3,5), c(7, 18))
+}
+\seealso{
+Other ANSI string operations: \code{\link{col_nchar}},
+ \code{\link{col_strsplit}}, \code{\link{col_substr}}
+}
+
diff --git a/man/combine_styles.Rd b/man/combine_styles.Rd
new file mode 100644
index 0000000..ddb0d95
--- /dev/null
+++ b/man/combine_styles.Rd
@@ -0,0 +1,56 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/combine.r
+\name{combine_styles}
+\alias{$.crayon}
+\alias{combine_styles}
+\title{Combine two or more ANSI styles}
+\usage{
+combine_styles(...)
+
+\method{$}{crayon}(crayon, style)
+}
+\arguments{
+\item{...}{The styles to combine. They will be applied from
+right to left.}
+
+\item{crayon}{A style function.}
+
+\item{style}{A style name that is included in \code{names(styles())}.}
+}
+\value{
+The combined style function.
+}
+\description{
+Combine two or more styles or style functions into a new style function
+that can be called on strings to style them.
+}
+\details{
+It does not usually make sense to combine two foreground
+colors (or two background colors), because only the first one
+applied will be used.
+
+It does make sense to combine different kind of styles,
+e.g. background color, foreground color, bold font.
+
+The \code{$} operator can also be used to combine styles.
+Not that the left hand side of \code{$} is a style function,
+and the right hand side is the name of a style in \code{styles()}.
+}
+\examples{
+## Use style names
+alert <- combine_styles("bold", "red4", "bgCyan")
+cat(alert("Warning!"), "\\n")
+
+## Or style functions
+alert <- combine_styles(bold, red, bgCyan)
+cat(alert("Warning!"), "\\n")
+
+## Combine a composite style
+alert <- combine_styles(bold, combine_styles(red, bgCyan))
+cat(alert("Warning!"), "\\n")
+
+## Shorter notation
+alert <- bold $ red $ bgCyan
+cat(alert("Warning!"), "\\n")
+}
+
diff --git a/man/concat.Rd b/man/concat.Rd
new file mode 100644
index 0000000..0fa2e14
--- /dev/null
+++ b/man/concat.Rd
@@ -0,0 +1,35 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/string.r
+\name{concat}
+\alias{\%+\%}
+\alias{concat}
+\title{Concatenate character vectors}
+\usage{
+lhs \%+\% rhs
+}
+\arguments{
+\item{lhs}{Left hand side, character vector.}
+
+\item{rhs}{Right hand side, character vector.}
+}
+\value{
+Concatenated vectors.
+}
+\description{
+The length of the two arguments must match, or
+one of them must be of length one. If the length of
+one argument is one, then the output's length will
+match the length of the other argument. See examples
+below.
+}
+\examples{
+"foo" \%+\% "bar"
+
+letters[1:10] \%+\% chr(1:10)
+
+letters[1:10] \%+\% "-" \%+\% chr(1:10)
+
+## This is empty (unlike for parse)
+character() \%+\% "*"
+}
+
diff --git a/man/crayon.Rd b/man/crayon.Rd
new file mode 100644
index 0000000..578879c
--- /dev/null
+++ b/man/crayon.Rd
@@ -0,0 +1,158 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/crayon-package.r, R/machinery.r
+\docType{package}
+\name{crayon}
+\alias{bgBlack}
+\alias{bgBlue}
+\alias{bgCyan}
+\alias{bgGreen}
+\alias{bgMagenta}
+\alias{bgRed}
+\alias{bgWhite}
+\alias{bgYellow}
+\alias{black}
+\alias{blue}
+\alias{blurred}
+\alias{bold}
+\alias{crayon}
+\alias{crayon-package}
+\alias{cyan}
+\alias{green}
+\alias{hidden}
+\alias{inverse}
+\alias{italic}
+\alias{magenta}
+\alias{red}
+\alias{reset}
+\alias{silver}
+\alias{strikethrough}
+\alias{underline}
+\alias{white}
+\alias{yellow}
+\title{Colored terminal output}
+\usage{
+## Simple styles
+red(...)
+bold(...)
+...
+
+## See more styling below
+}
+\arguments{
+\item{...}{Strings to style.}
+}
+\description{
+With crayon it is easy to add color to terminal output, create styles
+for notes, warnings, errors; and combine styles.
+}
+\details{
+ANSI color support is automatically detected and used. Crayon was largely
+inspired by chalk \url{https://github.com/sindresorhus/chalk}.
+
+Crayon defines several styles, that can be combined. Each style in the list
+has a corresponding function with the same name.
+}
+\section{Genaral styles}{
+
+
+\itemize{
+ \item reset
+ \item bold
+ \item blurred (usually called \sQuote{dim}, renamed to avoid name clash)
+ \item italic (not widely supported)
+ \item underline
+ \item inverse
+ \item hidden
+ \item strikethrough (not widely supported)
+}
+}
+
+\section{Text colors}{
+
+
+\itemize{
+ \item black
+ \item red
+ \item green
+ \item yellow
+ \item blue
+ \item magenta
+ \item cyan
+ \item white
+ \item silver (usually called \sQuote{gray}, renamed to avoid name clash)
+}
+}
+
+\section{Background colors}{
+
+
+\itemize{
+ \item bgBlack
+ \item bgRed
+ \item bgGreen
+ \item bgYellow
+ \item bgBlue
+ \item bgMagenta
+ \item bgCyan
+ \item bgWhite
+}
+}
+
+\section{Styling}{
+
+
+The styling functions take any number of character vectors as arguments,
+and they concatenate and style them: \preformatted{ library(crayon)
+ cat(blue("Hello", "world!\n"))
+}
+
+Crayon defines the \code{\%+\%} string concatenation operator, to make it easy
+to assemble stings with different styles. \preformatted{ cat("... to highlight the " \%+\% red("search term") \%+\%
+ " in a block of text\n")
+}
+
+Styles can be combined using the \code{$} operator: \preformatted{ cat(yellow$bgMagenta$bold('Hello world!\n'))
+} See also \code{\link{combine_styles}}.
+
+Styles can also be nested, and then inner style takes
+precedence: \preformatted{ cat(green(
+ 'I am a green line ' \%+\%
+ blue$underline$bold('with a blue substring') \%+\%
+ ' that becomes green again!\n'
+ ))
+}
+
+It is easy to define your own themes: \preformatted{ error <- red $ bold
+ warn <- magenta $ underline
+ note <- cyan
+ cat(error("Error: subscript out of bounds!\n"))
+ cat(warn("Warning: shorter argument was recycled.\n"))
+ cat(note("Note: no such directory.\n"))
+}
+}
+\examples{
+cat(blue("Hello", "world!"))
+
+cat("... to highlight the " \\\%+\\\% red("search term") \\\%+\\\%
+ " in a block of text")
+
+cat(yellow$bgMagenta$bold('Hello world!'))
+
+cat(green(
+ 'I am a green line ' \\\%+\\\%
+ blue$underline$bold('with a blue substring') \\\%+\\\%
+ ' that becomes green again!'
+))
+
+error <- red $ bold
+warn <- magenta $ underline
+note <- cyan
+cat(error("Error: subscript out of bounds!\\n"))
+cat(warn("Warning: shorter argument was recycled.\\n"))
+cat(note("Note: no such directory.\\n"))
+
+}
+\seealso{
+\code{\link{make_style}} for using the 256 ANSI colors.
+}
+
diff --git a/man/drop_style.Rd b/man/drop_style.Rd
new file mode 100644
index 0000000..2320818
--- /dev/null
+++ b/man/drop_style.Rd
@@ -0,0 +1,28 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/machinery.r
+\name{drop_style}
+\alias{drop_style}
+\title{Remove a style}
+\usage{
+drop_style(style)
+}
+\arguments{
+\item{style}{The name of the style to remove. No error is given
+for non-existing names.}
+}
+\value{
+Nothing.
+}
+\description{
+Remove a style
+}
+\examples{
+make_style(new_style = "maroon", bg = TRUE)
+cat(style("I am maroon", "new_style"), "\\n")
+drop_style("new_style")
+"new_style" \%in\% names(styles())
+}
+\seealso{
+Other styles: \code{\link{make_style}}
+}
+
diff --git a/man/has_color.Rd b/man/has_color.Rd
new file mode 100644
index 0000000..5ff8c9e
--- /dev/null
+++ b/man/has_color.Rd
@@ -0,0 +1,39 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/has_color.r
+\name{has_color}
+\alias{has_color}
+\title{Does the current R session support ANSI colors?}
+\usage{
+has_color()
+}
+\value{
+\code{TRUE} if the current R session supports color.
+}
+\description{
+Does the current R session support ANSI colors?
+}
+\details{
+The following algorithm is used to detect ANSI support: \itemize{
+ \item If the \code{crayon.enabled} option is set to \code{TRUE}
+ with \code{options()}, then \code{TRUE} is returned. If it is
+ set to something else than \code{TRUE} (typically \code{FALSE}),
+ then \code{FALSE} is returned.
+ \item Otherwise, if the standard output is not a terminal, then
+ \code{FALSE} is returned.
+ \item Otherwise, if the platform is Windows, \code{TRUE} is returned
+ if running in ConEmu (\url{https://conemu.github.io/}) or
+ cmder (\url{http://cmder.net}) with ANSI color support.
+ Otherwise \code{FALSE} is returned.
+ \item Otherwise, if the \code{COLORTERM} environment variable is
+ set, \code{TRUE} is returned.
+ \item Otherwise, if the \code{TERM} environment variable starts
+ with \code{screen}, \code{xterm} or \code{vt100}, or matches
+ \code{color}, \code{ansi}, \code{cygwin} or \code{linux}
+ (with case insentive matching), then \code{TRUE} is returned.
+ \item Otherwise \code{FALSE} is returned.
+}
+}
+\examples{
+has_color()
+}
+
diff --git a/man/has_style.Rd b/man/has_style.Rd
new file mode 100644
index 0000000..e950bdc
--- /dev/null
+++ b/man/has_style.Rd
@@ -0,0 +1,25 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/has_ansi.r
+\name{has_style}
+\alias{has_style}
+\title{Check if a sting has some ANSI styling}
+\usage{
+has_style(string)
+}
+\arguments{
+\item{string}{The string to check. It can also be a character
+vector.}
+}
+\value{
+Logical vector, \code{TRUE} for the strings that have some
+ ANSI styling.
+}
+\description{
+Check if a sting has some ANSI styling
+}
+\examples{
+## The second one has style if crayon is enabled
+has_style("foobar")
+has_style(red("foobar"))
+}
+
diff --git a/man/make_style.Rd b/man/make_style.Rd
new file mode 100644
index 0000000..97f5313
--- /dev/null
+++ b/man/make_style.Rd
@@ -0,0 +1,85 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/machinery.r
+\name{make_style}
+\alias{make_style}
+\title{Create an ANSI color style}
+\usage{
+make_style(..., bg = FALSE, grey = FALSE, colors = num_colors())
+}
+\arguments{
+\item{...}{The style to create. See details and examples below.}
+
+\item{bg}{Whether the color applies to the background.}
+
+\item{grey}{Whether to specifically create a grey color.
+This flag is included, because ANSI 256 has a finer color scale
+for greys, then the usual 0:5 scale for R, G and B components.
+It is only used for RGB color specifications (either numerically
+or via a hexa string), and it is ignored on eigth color ANSI
+terminals.}
+
+\item{colors}{Number of colors, detected automatically
+by default.}
+}
+\value{
+A function that can be used to color strings.
+}
+\description{
+Create a style, or a style function, or both. This function
+is intended for those who wish to use 256 ANSI colors,
+instead of the more widely supported eight colors.
+}
+\details{
+The crayon package comes with predefined styles (see
+\code{\link{styles}} for a list) and functions for the basic eight-color
+ANSI standard (\code{red}, \code{blue}, etc., see \link{crayon}).
+
+There are no predefined styles or style functions for the 256 color
+ANSI mode, however, because we simply did not want to create that
+many styles and functions. Instead, \code{make_style} can be
+used to create a style (or a style function, or both).
+
+There are two ways to use this function: \enumerate{
+ \item If its first argument is not named, then it returns a function
+ that can be used to color strings.
+ \item If its first argument is named, then it also creates a
+ style with the given name. This style can be used in
+ \code{\link{style}}. One can still use the return value
+ of the function, to create a style function.
+}
+
+The style (the code{...} argument) can be anything of the
+following: \itemize{
+ \item An R color name, see \code{colors()}.
+ \item A 6- or 8-digit hexa color string, e.g. \code{#ff0000} means
+ red. Transparency (alpha channel) values are ignored.
+ \item A one-column matrix with three rows for the red, green
+ and blue channels, as returned by \code{col2rgb} (in the base
+ grDevices package).
+}
+
+\code{make_style} detects the number of colors to use
+automatically (this can be overridden using the \code{colors}
+argument). If the number of colors is less than 256 (detected or given),
+then it falls back to the color in the ANSI eight color mode that
+is closest to the specified (RGB or R) color.
+
+See the examples below.
+}
+\examples{
+## Create a style function without creating a style
+pink <- make_style("pink")
+bgMaroon <- make_style(rgb(0.93, 0.19, 0.65), bg = TRUE)
+cat(bgMaroon(pink("I am pink if your terminal wants it, too.\\n")))
+
+## Create a new style for pink and maroon background
+make_style(pink = "pink")
+make_style(bgMaroon = rgb(0.93, 0.19, 0.65), bg = TRUE)
+"pink" \%in\% names(styles())
+"bgMaroon" \%in\% names(styles())
+cat(style("I am pink, too!\\n", "pink", bg = "bgMaroon"))
+}
+\seealso{
+Other styles: \code{\link{drop_style}}
+}
+
diff --git a/man/num_colors.Rd b/man/num_colors.Rd
new file mode 100644
index 0000000..ec4b434
--- /dev/null
+++ b/man/num_colors.Rd
@@ -0,0 +1,43 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/has_color.r
+\name{num_colors}
+\alias{num_colors}
+\title{Number of colors the terminal supports}
+\usage{
+num_colors(forget = FALSE)
+}
+\arguments{
+\item{forget}{Whether to forget the cached result of the color check.}
+}
+\value{
+Numeric scalar, the number of colors the terminal supports.
+}
+\description{
+Number of colors the terminal supports
+}
+\details{
+If the \code{crayon.colors} option is set, then we
+just use that. It should be an integer number. You can use this
+option as a workaround if crayon does not detect the number of
+colors accurately.
+
+In Emacs, we report eight colors.
+
+Otherwise, we use the \code{tput} shell command to detect the
+number of colors. If \code{tput} is not available,
+but we think that the terminal supports colors, then
+eigth colors are assumed.
+
+If tput returns 8, but TERM is xterm, we return 256, as most xterm
+compatible terminals in fact do support 256 colors.
+There is some discussion about this here:
+\url{https://github.com/gaborcsardi/crayon/issues/17}
+
+For efficiency, \code{num_colors} caches its result. To
+re-check the number of colors, set the \code{forget} argument to
+\code{TRUE}.
+}
+\examples{
+num_colors()
+}
+
diff --git a/man/show_ansi_colors.Rd b/man/show_ansi_colors.Rd
new file mode 100644
index 0000000..ed9726b
--- /dev/null
+++ b/man/show_ansi_colors.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/show.r
+\name{show_ansi_colors}
+\alias{show_ansi_colors}
+\title{Show the ANSI color table on the screen}
+\usage{
+show_ansi_colors(colors = num_colors())
+}
+\arguments{
+\item{colors}{Number of colors to show, meaningful values
+are 8 and 256. It is automatically set to the number of
+supported colors, if not specified.}
+}
+\value{
+The printed string, invisibly.
+}
+\description{
+Show the ANSI color table on the screen
+}
+
diff --git a/man/start.crayon.Rd b/man/start.crayon.Rd
new file mode 100644
index 0000000..9859a4e
--- /dev/null
+++ b/man/start.crayon.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/parts.r
+\name{start.crayon}
+\alias{finish}
+\alias{finish.crayon}
+\alias{start.crayon}
+\title{Switch on or off a style}
+\usage{
+\method{start}{crayon}(x, ...)
+
+finish(x, ...)
+
+\method{finish}{crayon}(x, ...)
+}
+\arguments{
+\item{x}{Style.}
+
+\item{...}{Ignored.}
+}
+\description{
+Make a style active. The text printed to the screen from now
+on will use this style.
+}
+\details{
+This function is very rarely needed, e.g. for colored user
+input. For other reasons, just call the style as a function on
+the string.
+}
+\examples{
+## The input is red (if color is supported)
+get_name <- function() {
+ cat("Enter your name:", start(red))
+ input <- readline()
+ cat(finish(red))
+ input
+}
+name <- get_name()
+name
+}
+
diff --git a/man/strip_style.Rd b/man/strip_style.Rd
new file mode 100644
index 0000000..d83e25e
--- /dev/null
+++ b/man/strip_style.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/has_ansi.r
+\name{strip_style}
+\alias{strip_style}
+\title{Remove ANSI escape sequences from a string}
+\usage{
+strip_style(string)
+}
+\arguments{
+\item{string}{The input string.}
+}
+\value{
+The cleaned up string.
+}
+\description{
+Remove ANSI escape sequences from a string
+}
+\examples{
+strip_style(red("foobar")) == "foobar"
+}
+
diff --git a/man/style.Rd b/man/style.Rd
new file mode 100644
index 0000000..9fd4151
--- /dev/null
+++ b/man/style.Rd
@@ -0,0 +1,30 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/style-var.r
+\name{style}
+\alias{style}
+\title{Add style to a string}
+\usage{
+style(string, as = NULL, bg = NULL)
+}
+\arguments{
+\item{string}{Character vector to style.}
+
+\item{as}{Style function to apply, either the function object,
+or its name, or an object to pass to \code{\link{make_style}}.}
+
+\item{bg}{Background style, a style function, or a name that
+is passed to \code{\link{make_style}}.}
+}
+\value{
+Styled character vector.
+}
+\description{
+See \code{names(styles)}, or the crayon manual for available styles.
+}
+\examples{
+## These are equivalent
+style("foobar", bold)
+style("foobar", "bold")
+bold("foobar")
+}
+
diff --git a/man/styles.Rd b/man/styles.Rd
new file mode 100644
index 0000000..8b834b5
--- /dev/null
+++ b/man/styles.Rd
@@ -0,0 +1,25 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/machinery.r
+\name{styles}
+\alias{styles}
+\title{ANSI escape sequences of crayon styles}
+\usage{
+styles()
+}
+\value{
+A named list. Each list element is a list of two
+ strings, named \sQuote{open} and \sQuote{close}.
+}
+\description{
+You can use this function to list all availables crayon styles,
+via \code{names(styles())}, or to explicitly apply an ANSI
+escape seauence to a string.
+}
+\examples{
+names(styles())
+cat(styles()[["bold"]]$close)
+}
+\seealso{
+\code{\link{crayon}} for the beginning of the crayon manual.
+}
+
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..4f61344
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,6 @@
+
+if (Sys.getenv("NOT_CRAN") != "") {
+ library(crayon)
+ library(testthat)
+ test_check("crayon")
+}
diff --git a/tests/testthat/test-color.r b/tests/testthat/test-color.r
new file mode 100644
index 0000000..298703e
--- /dev/null
+++ b/tests/testthat/test-color.r
@@ -0,0 +1,48 @@
+
+context("Colors and highlighting")
+
+op <- options()
+on.exit(options(op))
+options(crayon.enabled = TRUE)
+
+test_that("Coloring and highlighting works", {
+
+ expect_equal(underline("foo"), '\u001b[4mfoo\u001b[24m')
+ expect_equal(red('foo'), '\u001b[31mfoo\u001b[39m')
+ expect_equal(bgRed('foo'), '\u001b[41mfoo\u001b[49m')
+
+})
+
+test_that("Applying multiple styles at once works", {
+
+ expect_equal(red$bgGreen$underline('foo'),
+ '\u001b[31m\u001b[42m\u001b[4mfoo\u001b[24m\u001b[49m\u001b[39m')
+ expect_equal(underline$red$bgGreen('foo'),
+ '\u001b[4m\u001b[31m\u001b[42mfoo\u001b[49m\u001b[39m\u001b[24m')
+})
+
+test_that("Nested styles are supported", {
+
+ expect_equal(
+ red('foo' %+% underline$bgBlue('bar') %+% '!'),
+ '\u001b[31mfoo\u001b[4m\u001b[44mbar\u001b[49m\u001b[24m!\u001b[39m')
+})
+
+test_that("Nested styles of the same type are supported", {
+
+ expect_equal(
+ red('a' %+% blue('b' %+% green('c') %+% 'b') %+% 'c'),
+ '\u001b[31ma\u001b[34mb\u001b[32mc\u001b[34mb\u001b[31mc\u001b[39m')
+})
+
+test_that("Reset all styles", {
+
+ expect_equal(reset(red$bgGreen$underline('foo') %+% 'foo'),
+ '\u001b[0m\u001b[31m\u001b[42m\u001b[4mfoo\u001b[24m\u001b[49m\u001b[39mfoo\u001b[0m')
+})
+
+test_that("Variable number of arguments", {
+
+ expect_equal(red('foo', 'bar'), '\u001b[31mfoo bar\u001b[39m')
+
+})
diff --git a/tests/testthat/test-has-color.r b/tests/testthat/test-has-color.r
new file mode 100644
index 0000000..607d181
--- /dev/null
+++ b/tests/testthat/test-has-color.r
@@ -0,0 +1,76 @@
+
+context("Color detection")
+
+test_that("Color is detected properly", {
+
+ op <- options()
+ on.exit(options(op), add = TRUE)
+
+ ## If disabled, then no
+ options(crayon.enabled = FALSE)
+ hc <- has_color()
+ options(op)
+ expect_false(hc)
+
+ ## If enabled, then yes
+ options(crayon.enabled = TRUE)
+ hc <- has_color()
+ expect_true(hc)
+
+})
+
+test_that("number of colors is detected", {
+
+ nc <- num_colors()
+ expect_more_than(nc, 0)
+ expect_equal(nc, as.integer(nc))
+
+})
+
+test_that("closure based memoization works", {
+
+ # use `crayon.colors` option to force expected color return
+ old.opt <- options(crayon.colors = 42, crayon.enabled=TRUE)
+ expect_equal(num_colors(forget = TRUE), 42)
+ options(crayon.colors = 43)
+ expect_equal(num_colors(), 42)
+ expect_equal(num_colors(forget = TRUE), 43)
+
+ # reset options and run one more time
+ options(old.opt)
+ expect_equal(num_colors(), 43)
+
+ # reset cache to original value
+ try(num_colors(forget = TRUE))
+})
+
+test_that("tput errors are handled gracefully", {
+
+ # if tput errors num_colors is 8
+ with_mock(
+ `base::system` = function(...) stop("Error!"),
+
+ expect_equal(num_colors(forget = TRUE), 8)
+ )
+
+ # if tput returns nothing num_colors is 8
+ with_mock(
+ `base::system` = function(...) character(0),
+
+ expect_equal(num_colors(forget = TRUE), 8)
+ )
+
+ # if tput returns a non-number num_colors is 8
+ with_mock(
+ `base::system` = function(...) "no colors found!",
+
+ expect_equal(num_colors(forget = TRUE), 8)
+ )
+
+ # if tput returns a number the result is that number
+ with_mock(
+ `base::system` = function(...) "16",
+
+ expect_equal(num_colors(forget = TRUE), 16)
+ )
+})
diff --git a/tests/testthat/test-has-style.r b/tests/testthat/test-has-style.r
new file mode 100644
index 0000000..6de7c20
--- /dev/null
+++ b/tests/testthat/test-has-style.r
@@ -0,0 +1,28 @@
+
+context("Does a string have ANSI style?")
+
+op <- options()
+on.exit(options(op))
+options(crayon.enabled = TRUE)
+
+test_that("has_style works", {
+
+ expect_false(has_style("foobar"))
+ for (st in names(styles)) {
+ expect_true(has_style(style("foobar", st)))
+ }
+
+})
+
+context("Strip style from string")
+
+test_that("strip_style works", {
+
+ expect_equal("", strip_style(""))
+ expect_equal("foobar", strip_style("foobar"))
+ expect_equal("foobar", strip_style(red$underline$bold("foobar")))
+
+ for (st in names(styles)) {
+ expect_equal("foobar", strip_style(style("foobar", st)))
+ }
+})
diff --git a/tests/testthat/test-make-style.r b/tests/testthat/test-make-style.r
new file mode 100644
index 0000000..f42f184
--- /dev/null
+++ b/tests/testthat/test-make-style.r
@@ -0,0 +1,58 @@
+
+context("Making new ANSI 256 styles")
+
+test_that("make_style without name", {
+
+ st <- styles()
+ pink <- make_style("pink")
+ expect_true(is(pink, "crayon"))
+ expect_identical(st, styles())
+
+})
+
+test_that("make_style with name", {
+
+ st <- styles()
+ make_style(foobarnonono = "pink")
+ expect_true("foobarnonono" %in% names(styles()))
+ drop_style("foobarnonono")
+ expect_false("foobarnonono" %in% names(styles()))
+
+})
+
+test_that("hexa color regex works", {
+
+ positive <- c("#000000", "#ffffff", "#0f0f0f", "#f0f0f0",
+ "#00000000", "#ffffffff", "#0f0f0f00", "#f0f0f055")
+
+ negative <- c("", "#12345", "123456", "1234567", "12345678",
+ "#1234567", "#1234ffg", "#gggggx", "foo#123456",
+ "foo#123456bar")
+
+ for (color in positive) {
+ expect_true(grepl(hash_color_regex, color))
+ expect_true(grepl(hash_color_regex, toupper(color)))
+ }
+
+ for (color in negative) {
+ expect_false(grepl(hash_color_regex, color))
+ expect_false(grepl(hash_color_regex, toupper(color)))
+ }
+
+})
+
+test_that("we fall back for ANSI 8 if needed", {
+
+ yellow3 <- make_style("yellow3", colors = 8)
+ expect_equal(attr(yellow, "_styles")[[1]],
+ attr(yellow3, "_styles")[[1]])
+
+})
+
+test_that("we can create a style from an R color", {
+
+ red4 <- make_style("red4")
+ red_text <- red4("text")
+ expect_true(!has_color() || has_style(red_text))
+
+})
diff --git a/tests/testthat/test-operations.R b/tests/testthat/test-operations.R
new file mode 100644
index 0000000..3cefbb1
--- /dev/null
+++ b/tests/testthat/test-operations.R
@@ -0,0 +1,192 @@
+
+context("String operations")
+
+str <- c("",
+ "plain",
+ "\033[31m",
+ "\033[39m",
+ "\033[31mred\033[39m",
+ "\033[31mred\033[39m\033[31mred\033[39m",
+ "foo\033[31mred\033[39m",
+ "\033[31mred\033[39mfoo")
+
+test_that("col_nchar", {
+ for (s in str) {
+ expect_equal(col_nchar(s), nchar(strip_style(s)), info = s)
+ }
+})
+
+test_that("col_substr", {
+ for (s in str) {
+ for (i in 1 %:% col_nchar(s)) {
+ for (j in i %:% col_nchar(s)) {
+ expect_equal(strip_style(col_substr(s, i, j)),
+ substr(strip_style(s), i, j), info = paste(s, i, j))
+ }
+ }
+ }
+})
+
+test_that("col_substr keeps color", {
+ expect_equal(col_substr("\033[31mred\033[39m", 1, 1),
+ "\033[31mr\033[39m")
+ expect_equal(col_substr("foo\033[31mred\033[39m", 4, 4),
+ "\033[31mr\033[39m")
+ expect_equal(col_substr("foo\033[31mred\033[39mbar", 4, 4),
+ "\033[31mr\033[39m")
+ expect_equal(col_substr("\033[31mred\033[39mfoo\033[31mred\033[39mbar", 7, 7),
+ "\033[31m\033[39m\033[31mr\033[39m")
+})
+
+test_that("col_substr, start after string end", {
+ expect_equal(col_substr("red", 4, 4), "")
+ expect_equal(col_substr("red", 4, 5), "")
+ expect_equal(strip_style(col_substr("\033[31mred\033[39m", 4, 4)), "")
+ expect_equal(strip_style(col_substr("\033[31mred\033[39m", 4, 5)), "")
+
+ expect_equal(col_substr("red", 3, 4), "d")
+ expect_equal(col_substr("red", 3, 5), "d")
+ expect_equal(strip_style(col_substr("\033[31mred\033[39m", 3, 4)), "d")
+ expect_equal(strip_style(col_substr("\033[31mred\033[39m", 3, 5)), "d")
+})
+
+test_that("col_substr, multiple strings", {
+ set.seed(42)
+ for (i in 1:100) {
+ strs <- sample(str, 4)
+ num_starts <- sample(1:5, 1)
+ num_stops <- sample(1:5, 1)
+ starts <- sample(1:5, num_starts, replace = TRUE)
+ stops <- sample(1:5, num_stops, replace = TRUE)
+ r1 <- strip_style(col_substr(strs, starts, stops))
+ r2 <- substr(strip_style(strs), starts, stops)
+ expect_equal(r1, r2)
+ }
+})
+
+test_that("col_substr corner cases", {
+ # Zero length input
+
+ c0 <- character(0L)
+ o0 <- structure(list(), class="abc")
+ co0 <- structure(character(0L), class="abc")
+ expect_identical(col_substr(c0, 1, 1), substr(c0, 1, 1))
+ expect_identical(col_substr(o0, 1, 1), substr(o0, 1, 1))
+ expect_identical(col_substr(co0, 1, 1), substr(co0, 1, 1))
+
+ expect_identical(col_substring(c0, 1, 1), substring(c0, 1, 1))
+ expect_identical(col_substring(o0, 1, 1), substring(o0, 1, 1))
+ expect_identical(col_substring(co0, 1, 1), substring(co0, 1, 1))
+
+ # Character start/stop
+ expect_identical(col_substr("abc", "1", 1), substr("abc", "1", 1))
+ expect_identical(col_substr("abc", 1, "1"), substr("abc", 1, "1"))
+
+ # non-numeric arguments cause errors; NOTE: this actually "works"
+ # with 'substr' but not implemented in 'col_substr'
+ expect_error(col_substr("abc", "hello", 1), "non-numeric")
+
+})
+
+test_that("col_substring", {
+ for (s in str) {
+ for (i in 1 %:% col_nchar(s)) {
+ for (j in i %:% col_nchar(s)) {
+ expect_equal(strip_style(col_substring(s, i, j)),
+ substring(strip_style(s), i, j), info = paste(s, i, j))
+ }
+ }
+ }
+})
+
+test_that("col_substring, multiple strings", {
+ set.seed(42)
+ for (i in 1:100) {
+ strs <- sample(str, 4)
+ num_starts <- sample(1:5, 1)
+ num_stops <- sample(1:5, 1)
+ starts <- sample(1:5, num_starts, replace = TRUE)
+ stops <- sample(1:5, num_stops, replace = TRUE)
+ r1 <- strip_style(col_substring(strs, starts, stops))
+ r2 <- substring(strip_style(strs), starts, stops)
+ expect_equal(r1, r2)
+ }
+})
+
+test_that("col_substring corner cases", {
+ # Zero length input
+
+ c0 <- character(0L)
+ o0 <- structure(list(), class="abc")
+ co0 <- structure(character(0L), class="abc")
+ expect_identical(col_substring(c0, 1, 1), substring(c0, 1, 1))
+ expect_identical(col_substring(o0, 1, 1), substring(o0, 1, 1))
+ expect_identical(col_substring(co0, 1, 1), substring(co0, 1, 1))
+})
+
+test_that("col_strsplit", {
+ red <- "\033[31mred\033[39m"
+
+ str <- "plain-plain"
+ expect_equal(col_strsplit(str, "-"), strsplit(str, "-"))
+
+ str <- red %+% "-plain"
+ expect_equal(strip_style(col_strsplit(str, "-")[[1]]),
+ strsplit(strip_style(str), "-")[[1]])
+
+ expect_equal(col_strsplit(str, "e"),
+ list(c("\033[31mr\033[39m", "\033[31md\033[39m-plain")))
+
+ str <- red %+% "-" %+% red %+% "-" %+% red
+ expect_equal(strip_style(col_strsplit(str, "-")[[1]]),
+ strsplit(strip_style(str), "-")[[1]])
+
+ # with leading and trailing separators
+ str.2 <- "-" %+% red %+% "-" %+% red %+% "-" %+% red %+% "-"
+ expect_equal(strip_style(col_strsplit(str.2, "-")[[1]]),
+ strsplit(strip_style(str.2), "-")[[1]])
+
+ # greater than length 1
+ str.3 <- paste0("-", c(green("hello"), red("goodbye")), "-world-")
+ expect_equal(strip_style(unlist(col_strsplit(str.3, "-"))),
+ unlist(strsplit(strip_style(str.3), "-")))
+})
+
+test_that("col_strsplit multiple strings", {
+ red <- "\033[31mred\033[39m"
+ str <- c("plain-plain-" %+% red %+% "-plain-" %+% red,
+ red %+% "-" %+% red,
+ red)
+
+ r1 <- lapply(col_strsplit(str, "-"), strip_style)
+ r2 <- strsplit(strip_style(str), "-")
+
+})
+
+test_that("col_strsplit edge cases", {
+ expect_equal(col_strsplit("", "-"), list(character(0L)))
+ expect_equal(
+ strip_style(col_strsplit("\033[31m\033[39m", "-")[[1]]), character(0L)
+ )
+ # special cases
+ expect_equal(col_strsplit("", ""), strsplit("", ""))
+ expect_equal(col_strsplit("a", "a"), strsplit("a", "a"))
+ # this following test isn't working yet
+ expect_equal(col_strsplit("a", ""), strsplit("a", ""))
+ expect_equal(col_strsplit("", "a"), strsplit("", "a"))
+ # Longer strings
+ expect_identical(
+ col_strsplit(c("", "a", "aa"), "a"), strsplit(c("", "a", "aa"), "a")
+ )
+ expect_identical(
+ col_strsplit(c("abaa", "ababza"), "b."), strsplit(c("abaa", "ababza"), "b.")
+ )
+})
+
+test_that("Weird length 'split'", {
+ expect_error(col_strsplit(c("ab", "bd"), c("b", "d")), "must be character")
+ expect_identical(col_strsplit("ab", NULL), strsplit("ab", NULL))
+ expect_identical(
+ col_strsplit("ab", character(0L)), strsplit("ab", character(0L))
+ )
+})
diff --git a/tests/testthat/test-style-var.r b/tests/testthat/test-style-var.r
new file mode 100644
index 0000000..1c92305
--- /dev/null
+++ b/tests/testthat/test-style-var.r
@@ -0,0 +1,16 @@
+
+context("General style function")
+
+op <- options(crayon.enabled = TRUE)
+on.exit(options(op))
+
+test_that("style works", {
+
+ x1 <- style("foobar", bold)
+ x2 <- style("foobar", "bold")
+ x3 <- bold("foobar")
+
+ expect_equal(x1, x2)
+ expect_equal(x2, x3)
+
+})
diff --git a/tests/testthat/test-styles.r b/tests/testthat/test-styles.r
new file mode 100644
index 0000000..d9ae1d2
--- /dev/null
+++ b/tests/testthat/test-styles.r
@@ -0,0 +1,51 @@
+
+context("Defining new styles")
+
+test_that("new styles are local to importing package", {
+
+ skip("This is not implemented, yet.")
+
+ lib_dir <- tempfile()
+
+ on.exit(try(unloadNamespace("foo1"), silent = TRUE), add = TRUE)
+ on.exit(try(unloadNamespace("foo2"), silent = TRUE), add = TRUE)
+ on.exit(try(unlink(lib_dir, recursive = TRUE), silent = TRUE), add = TRUE)
+
+ make_packages(
+ lib_dir = lib_dir,
+ imports = "crayon",
+
+ foo1 = {
+ f <- function() {
+ make_style(pink = "pink")
+ }
+ g <- function() {
+ names(styles())
+ }
+ },
+
+ foo2 = {
+ f <- function() {
+ make_style(maroon = "maroon")
+ }
+ g <- function() {
+ names(styles())
+ }
+ }
+ )
+
+ ## Add style in 'foo1', does not effect 'foo2', or attached crayon
+ foo1::f()
+ expect_true("pink" %in% foo1::g())
+ expect_false("pink" %in% foo2::g())
+ expect_false("pink" %in% names(styles()))
+
+ ## Attached style change does not affect imports in packages
+ on.exit(drop_style("ivory444"), add = TRUE)
+ make_style(ivory444 = "ivory")
+ expect_true("ivory444" %in% names(styles()))
+ expect_false("ivory444" %in% foo1::g())
+ expect_false("ivory444" %in% foo2::g())
+
+ ## TODO: what if the package(s) are not attached
+})
diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R
new file mode 100644
index 0000000..018b786
--- /dev/null
+++ b/tests/testthat/test-utils.R
@@ -0,0 +1,9 @@
+context("utils")
+
+test_that("non_matching", {
+ chr <- "abc"
+ splits <- crayon:::re_table("", chr)
+ res.mx <- cbind(start=c(1L, 1L:3L), end=0L:3L, length=c(0L, 1L, 1L, 1L))
+ expect_equal(crayon:::non_matching(splits, chr, empty=TRUE)[[1L]], res.mx)
+ expect_equal(crayon:::non_matching(splits, chr)[[1L]], res.mx[-1L,])
+})
diff --git a/tests/testthat/test-vectors.r b/tests/testthat/test-vectors.r
new file mode 100644
index 0000000..24ed45a
--- /dev/null
+++ b/tests/testthat/test-vectors.r
@@ -0,0 +1,64 @@
+
+context("Styling of character vectors")
+
+op <- options()
+on.exit(options(op))
+options(crayon.enabled = TRUE)
+
+foobar <- c("foo", "bar")
+bigyo <- c("bi", "gyo")
+
+test_that("Coloring and highlighting works", {
+
+ expect_equal(underline(foobar),
+ c('\u001b[4mfoo\u001b[24m', '\u001b[4mbar\u001b[24m'))
+ expect_equal(red(foobar),
+ c('\u001b[31mfoo\u001b[39m', '\u001b[31mbar\u001b[39m'))
+ expect_equal(bgRed(foobar),
+ c('\u001b[41mfoo\u001b[49m', '\u001b[41mbar\u001b[49m'))
+
+})
+
+test_that("Applying multiple styles at once works", {
+
+ expect_equal(red$bgGreen$underline(foobar),
+ c('\u001b[31m\u001b[42m\u001b[4mfoo\u001b[24m\u001b[49m\u001b[39m',
+ '\u001b[31m\u001b[42m\u001b[4mbar\u001b[24m\u001b[49m\u001b[39m'))
+ expect_equal(underline$red$bgGreen(foobar),
+ c('\u001b[4m\u001b[31m\u001b[42mfoo\u001b[49m\u001b[39m\u001b[24m',
+ '\u001b[4m\u001b[31m\u001b[42mbar\u001b[49m\u001b[39m\u001b[24m'))
+})
+
+test_that("Nested styles are supported", {
+
+ expect_equal(
+ red(foobar %+% underline$bgBlue(bigyo) %+% '!'),
+ c('\u001b[31mfoo\u001b[4m\u001b[44mbi\u001b[49m\u001b[24m!\u001b[39m',
+ '\u001b[31mbar\u001b[4m\u001b[44mgyo\u001b[49m\u001b[24m!\u001b[39m'))
+})
+
+test_that("Nested styles of the same type are supported", {
+
+ aA <- c("a", "A")
+ bB <- c("b", "B")
+ cC <- c("c", "C")
+ expect_equal(
+ red(aA %+% blue(bB %+% green(cC) %+% bB) %+% cC),
+ c('\u001b[31ma\u001b[34mb\u001b[32mc\u001b[34mb\u001b[31mc\u001b[39m',
+ '\u001b[31mA\u001b[34mB\u001b[32mC\u001b[34mB\u001b[31mC\u001b[39m'))
+})
+
+test_that("Reset all styles", {
+
+ expect_equal(reset(red$bgGreen$underline(foobar) %+% foobar),
+ c('\u001b[0m\u001b[31m\u001b[42m\u001b[4mfoo\u001b[24m\u001b[49m\u001b[39mfoo\u001b[0m',
+ '\u001b[0m\u001b[31m\u001b[42m\u001b[4mbar\u001b[24m\u001b[49m\u001b[39mbar\u001b[0m'))
+})
+
+test_that("Variable number of arguments", {
+
+ expect_equal(red(foobar, 'bar'),
+ c('\u001b[31mfoo bar\u001b[39m',
+ '\u001b[31mbar bar\u001b[39m'))
+
+})
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-crayon.git
More information about the debian-med-commit
mailing list