[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