[med-svn] [r-cran-crayon] 02/05: New upstream version 1.3.4

Andreas Tille tille at debian.org
Mon Oct 9 10:01:17 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 c9f4336cf3249ffe17d9484d4349c11c5547bf58
Author: Andreas Tille <tille at debian.org>
Date:   Mon Oct 9 11:53:59 2017 +0200

    New upstream version 1.3.4
---
 DESCRIPTION                      |  14 ++---
 MD5                              |  75 ++++++++++++++-------------
 NAMESPACE                        |   5 ++
 R/ansi-256.r                     |   2 +
 R/combine.r                      |  11 ++--
 R/disposable.r                   |   4 ++
 R/has_ansi.r                     |   4 +-
 R/has_color.r                    | 104 ++++++++++++++++++++++++++-----------
 R/machinery.r                    |  55 ++++++++++----------
 R/show.r                         |  21 +++++---
 R/string.r                       |  24 ++++++++-
 R/string_operations.r            | 101 +++++++++++++++++++++++++++++-------
 R/style-var.r                    |   6 +--
 R/utils.r                        |  59 +++++++++++----------
 inst/NEWS.md                     |   9 ++++
 inst/README.markdown             |  10 ++--
 man/chr.Rd                       |   5 +-
 man/col_align.Rd                 |  34 +++++++++++++
 man/col_nchar.Rd                 |  12 ++---
 man/col_strsplit.Rd              |  14 ++---
 man/col_substr.Rd                |  10 ++--
 man/col_substring.Rd             |  10 ++--
 man/combine_styles.Rd            |   5 +-
 man/concat.Rd                    |   3 +-
 man/crayon.Rd                    | 107 ++++++++++++++++++++-------------------
 man/drop_style.Rd                |   1 -
 man/has_color.Rd                 |  48 +++++++++++-------
 man/has_style.Rd                 |   7 ++-
 man/make_style.Rd                |  31 ++++++------
 man/num_colors.Rd                |   8 +--
 man/show_ansi_colors.Rd          |   1 -
 man/start.crayon.Rd              |   3 +-
 man/strip_style.Rd               |   1 -
 man/style.Rd                     |   5 +-
 man/styles.Rd                    |   5 +-
 tests/testthat.R                 |   8 ++-
 tests/testthat/test-ansi256.R    |  62 +++++++++++++++++++++++
 tests/testthat/test-combine.R    |  37 ++++++++++++++
 tests/testthat/test-has-color.r  |  93 ++++++++++++++++++++--------------
 tests/testthat/test-operations.R |  45 +++++++++++++++-
 40 files changed, 708 insertions(+), 351 deletions(-)

diff --git a/DESCRIPTION b/DESCRIPTION
index 26e6dd5..00a894f 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
 Package: crayon
 Title: Colored Terminal Output
-Version: 1.3.2
+Version: 1.3.4
 Authors at R: c(
     person("Gábor", "Csárdi", , "csardi.gabor at gmail.com",
     role = c("aut", "cre")),
@@ -15,20 +15,20 @@ Description: Colored terminal output on terminals that support 'ANSI'
     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
+URL: https://github.com/r-lib/crayon#readme
+BugReports: https://github.com/r-lib/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
+Suggests: mockery, rstudioapi, testthat, withr
+RoxygenNote: 6.0.1.9000
 Encoding: UTF-8
 NeedsCompilation: no
-Packaged: 2016-06-28 17:18:56 UTC; gaborcsardi
+Packaged: 2017-09-15 18:14:04 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
+Date/Publication: 2017-09-16 19:49:46 UTC
diff --git a/MD5 b/MD5
index a467e18..625124c 100644
--- a/MD5
+++ b/MD5
@@ -1,51 +1,54 @@
-33b0d86445b3fae78955de3d4b58e397 *DESCRIPTION
+fcafd6fb2f2dd0a0b0c8ff0855663372 *DESCRIPTION
 71e081e70f89b75ba2781efdce97f3b4 *LICENSE
-f163aae74ce504ed0d69967904db6ec6 *NAMESPACE
-4463bc9d9bd9c2d4423a17a8d795787b *R/ansi-256.r
-d91d95b5a9db91ec97b5b1c03a2eb01d *R/combine.r
+fa5450309e5dfb8f1228c21dbcabe13e *NAMESPACE
+3f3f25348f24cddea31d27e570e80f2d *R/ansi-256.r
+53f6dacf2beb3b02d5061418be1f3e59 *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
+1dea456a152b15068d387e5293a9d356 *R/disposable.r
+2f81515cb44e98df8c0d7fbfbee66275 *R/has_ansi.r
+aa122d4d4c62f6cb2d5b515e6d8a8ea8 *R/has_color.r
+62a4a2b5c44a80d89d2c8cd4a577b756 *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
+946e0d722d4bf4b1e75fab3acb1cbb51 *R/show.r
+7e76265f3a2abd1366dc806ff7f3b2d8 *R/string.r
+4efe7ba7147a73d15cbfaa0eb25c8b77 *R/string_operations.r
+4b59ab10cac98ddae1286489d03f2f15 *R/style-var.r
 7117857a16121744d8ab7e5bddfec9f9 *R/styles.r
-605e414985e272917c23aedeb28e90c6 *R/utils.r
+22233210c1f3b1f6c1946eac6a260fcf *R/utils.r
 175d390058b8a9b12995525725218cf4 *inst/ANSI-256-OSX.png
 5a18e3dc240c1f6cfffd6059c8de5345 *inst/ANSI-8-OSX.png
-9d77d6031aa5556b770c65ce4f15ab5e *inst/NEWS.md
-53d5ddc63652f7c3a613f5a2cf66bf52 *inst/README.markdown
+81704b1006d374022289331ecc13dc68 *inst/NEWS.md
+a462ed01f93031392397d00c31ddffee *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
+1ec2951d9bc3714bd46316973a4d866e *man/chr.Rd
+fcbad9dbc35168cdba8cdbcb3384139e *man/col_align.Rd
+527cd474f28bcd7db706f5968ff9e634 *man/col_nchar.Rd
+5df135dd5c0ec107f6b0f57a4860df2e *man/col_strsplit.Rd
+730af5b93c1c52ef567cde762b0f5490 *man/col_substr.Rd
+7a7cec9f890a5167767ef036026acd95 *man/col_substring.Rd
+59f07d2ad62b626b581e9d0c01fd8f53 *man/combine_styles.Rd
+71589002458992bacfbed2a258231ec6 *man/concat.Rd
+626f51773a2b316db9b050ecdfef7bf8 *man/crayon.Rd
+9f4c4c60dbc2dc77ff144420c27bcd02 *man/drop_style.Rd
+45a188029323e54c804fce0a2cce9d89 *man/has_color.Rd
+4230bd0b59f6271490e96b77b8d1c0b2 *man/has_style.Rd
+5d5454533d2cf82ddf54b246b4e9a19e *man/make_style.Rd
+0303bf8834540bba439f206e05dd21b3 *man/num_colors.Rd
+5c77b114f8f137cb57717c3a4cc2e9a8 *man/show_ansi_colors.Rd
+ae5359585b92d4323fc3507488fbe92d *man/start.crayon.Rd
+9a91f8fceb889eb55808c8aad13d8fc3 *man/strip_style.Rd
+2fc0045cd3614113ad14a42789e6db7c *man/style.Rd
+66308c7d1350128c2d1a3a1d6175d494 *man/styles.Rd
+943758c7965d5d220d1162fb9ce9199b *tests/testthat.R
+e620d8f60e7c0193acc63dddff6accd8 *tests/testthat/test-ansi256.R
 541b9fb498b4a115f9f740c71cc3c98f *tests/testthat/test-color.r
-e4702f5e1469b8fc41014478dcbdcee0 *tests/testthat/test-has-color.r
+62f82aaf88bcac6e77a5ca85828342bd *tests/testthat/test-combine.R
+c1054945f87e14f9eaaa87dd9bd0fcf7 *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
+a92c4c79209e24c6267056884aefcd48 *tests/testthat/test-operations.R
 ce7bc77df0975d3f9aef7064c5dca438 *tests/testthat/test-style-var.r
 9d653af368745efcbb41342e5b369920 *tests/testthat/test-styles.r
 8e8cbd732867bd818e324c5b15e0a224 *tests/testthat/test-utils.R
diff --git a/NAMESPACE b/NAMESPACE
index 0cc0b22..095ff82 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,6 +1,8 @@
 # Generated by roxygen2: do not edit by hand
 
 S3method("$",crayon)
+S3method("-",crayon)
+S3method(as.character,crayon)
 S3method(finish,crayon)
 S3method(print,crayon)
 S3method(start,crayon)
@@ -18,6 +20,7 @@ export(blue)
 export(blurred)
 export(bold)
 export(chr)
+export(col_align)
 export(col_nchar)
 export(col_strsplit)
 export(col_substr)
@@ -48,6 +51,8 @@ export(white)
 export(yellow)
 importFrom(grDevices,col2rgb)
 importFrom(grDevices,colors)
+importFrom(grDevices,grey)
+importFrom(grDevices,rgb)
 importFrom(methods,is)
 importFrom(stats,start)
 importFrom(utils,head)
diff --git a/R/ansi-256.r b/R/ansi-256.r
index b766df3..b20fd6e 100644
--- a/R/ansi-256.r
+++ b/R/ansi-256.r
@@ -1,10 +1,12 @@
 
+# nocov start
 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
+#nocov end
 
 ansi256 <- function(rgb, bg = FALSE, grey = FALSE) {
   codes <- if (bg) bgcodes else fgcodes
diff --git a/R/combine.r b/R/combine.r
index a53f248..fbf731c 100644
--- a/R/combine.r
+++ b/R/combine.r
@@ -11,9 +11,9 @@
 #' 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()}.
+#' The `$` operator can also be used to combine styles.
+#' Not that the left hand side of `$` is a style function,
+#' and the right hand side is the name of a style in [styles()].
 #'
 #' @param ... The styles to combine. They will be applied from
 #'   right to left.
@@ -45,11 +45,12 @@ combine_styles <- function(...) {
 
 #' @rdname combine_styles
 #' @param crayon A style function.
-#' @param style A style name that is included in \code{names(styles())}.
+#' @param style A style name that is included in `names(styles())`.
 #' @export
 #' @method $ crayon
 
 `$.crayon` <- function(crayon, style) {
-  attr(crayon, "_styles") <- c(attr(crayon, "_styles"), my_styles[style])
+  attr(crayon, "_styles") <-
+    c(attr(crayon, "_styles"), data_env$my_styles[style])
   crayon
 }
diff --git a/R/disposable.r b/R/disposable.r
index 3e5c312..222bce1 100644
--- a/R/disposable.r
+++ b/R/disposable.r
@@ -1,4 +1,6 @@
 
+## nocov start
+
 install_quietly <- TRUE
 
 with_wd <- function(dir, expr) {
@@ -96,3 +98,5 @@ make_packages <- function(..., lib_dir = tempfile(),
   }
   invisible(lib_dir)
 }
+
+## nocov end
diff --git a/R/has_ansi.r b/R/has_ansi.r
index 2aebc5e..641d6da 100644
--- a/R/has_ansi.r
+++ b/R/has_ansi.r
@@ -3,11 +3,11 @@ 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
+#' Check if a string 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
+#' @return Logical vector, `TRUE` for the strings that have some
 #'   ANSI styling.
 #'
 #' @export
diff --git a/R/has_color.r b/R/has_color.r
index fcc2f7e..523b966 100644
--- a/R/has_color.r
+++ b/R/has_color.r
@@ -5,26 +5,39 @@
 #'
 #' @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 If the `crayon.enabled` option is set to `TRUE`
+#'     with [options()], then `TRUE` is returned. If it is
+#'     set to something else than `TRUE` (typically `FALSE`),
+#'     then `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
+#'     `FALSE` is returned.
+#'   \item Otherwise, if the platform is Windows, `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.
+#'     Otherwise `FALSE` is returned.
+#'   \item Otherwise, if the `COLORTERM` environment variable is
+#'     set, `TRUE` is returned.
+#'   \item Otherwise, if the `TERM` environment variable starts
+#'     with `screen`, `xterm` or `vt100`, or matches
+#'     `color`, `ansi`, `cygwin` or `linux`
+#'     (with case insentive matching), then `TRUE` is returned.
+#'   \item Otherwise `FALSE` is returned.
 #' }
 #'
-#' @return \code{TRUE} if the current R session supports color.
+#' @section Sinks:
+#' Note that `has_color()` returns `FALSE` if a sink is active
+#' (see [sink()]). It assumes that the constructed string will be printed
+#' to the standard output, and `sink()` redirects to a file, and usually
+#' you don't want ANSI colors in the file.
+#'
+#' The same applies to the case when R's standard output is redirected
+#' to a file, from the command line, e.g.:
+#' ```
+#' R -q -e 'cat(crayon::red("no color here\n"))' > /tmp/crayon-test.txt
+#' cat /tmp/crayon-test.txt
+#' ```
+#'
+#' @return `TRUE` if the current R session supports color.
 #'
 #' @export
 #' @examples
@@ -36,11 +49,16 @@ has_color <- function() {
   enabled <- getOption("crayon.enabled")
   if (!is.null(enabled)) { return(isTRUE(enabled))  }
 
+  ## RStudio with (potential) ANSI support?
+  if (rstudio_with_ansi_support() && sink.number() == 0) {
+    return(TRUE)
+  }
+
   ## 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 (os_type() == "windows") {
     if (Sys.getenv("ConEmuANSI") == "ON") { return(TRUE) }
     if (Sys.getenv("CMDER_ROOT") != "") { return(TRUE) }
 
@@ -49,7 +67,7 @@ has_color <- function() {
   }
 
   ## Running in a recent Emacs?
-  if (inside_emacs() && emacs_version()[1] >= 23) { return(TRUE) }
+  if (inside_emacs() && ! is.na(emacs_version()[1]) && emacs_version()[1] >= 23) { return(TRUE) }
 
   ## COLORTERM set?
   if ("COLORTERM" %in% names(Sys.getenv())) { return(TRUE) }
@@ -65,26 +83,27 @@ has_color <- function() {
 #' Number of colors the terminal supports
 #'
 #' @details
-#' If the \code{crayon.colors} option is set, then we
+#' If the `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,
+#' Otherwise, we use the `tput` shell command to detect the
+#' number of colors. If `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}
+#' \url{https://github.com/r-lib/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}.
+#' For efficiency, `num_colors()` caches its result. To
+#' re-check the number of colors, set the `forget` argument to
+#' `TRUE`. The cached value is only used if no sinks are active,
+#' see also [has_color()] for more information about sinks.
 #'
 #' @param forget Whether to forget the cached result of the color check.
 #' @return Numeric scalar, the number of colors the terminal supports.
@@ -98,8 +117,15 @@ num_colors <- (function() {
   cache <- NULL
 
   function(forget=FALSE) {
-    if (forget || is.null(cache)) cache <<- i_num_colors()
-    cache
+    if (forget || is.null(cache)) {
+      result <- i_num_colors()
+      if (sink.number() == 0 && rstudio_initialized()) {
+        cache <<- result
+      }
+    } else {
+      result <- cache
+    }
+    result
   }
 })()
 
@@ -109,16 +135,36 @@ i_num_colors <- function() {
   cols <- getOption("crayon.colors")
   if (!is.null(cols)) { return(as.integer(cols)) }
 
-  ## Otherwise try to detect. If no color support, then 1
+  ## RStudio
+  if (rstudio_with_ansi_support()) { return(256) }
+
   if (!has_color()) { return(1) }
 
   ## Emacs
   if (inside_emacs()) { return(8) }
 
+  ## Are we in a windows terminal with color support?
+  if (os_type() == "windows") {
+    if (Sys.getenv("ConEmuANSI") == "ON" ||
+        Sys.getenv("CMDER_ROOT") != "") {
+      return(get_terminal_colors())
+    }
+
+    ## Are we in another windows terminal or GUI? :(
+    return(1)
+  }
+
+  ## Otherwise
+  get_terminal_colors()
+}
+
+get_terminal_colors <- function() {
   ## 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))))
+  cols <- suppressWarnings(try(
+    silent = TRUE,
+    as.numeric(system("tput colors 2>/dev/null", intern = TRUE))[1]
+  ))
   if (inherits(cols, "try-error") || !length(cols) || is.na(cols)) { return(8) }
   if (cols %in% c(-1, 0, 1)) { return(1) }
 
diff --git a/R/machinery.r b/R/machinery.r
index 7bcdb0a..791ba90 100644
--- a/R/machinery.r
+++ b/R/machinery.r
@@ -17,7 +17,7 @@ crayon_template <- function(...) {
 hash_color_regex <- "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$"
 
 is_builtin_style <- function(x) {
-  x %in% names(builtin_styles)
+  is_string(x) && x %in% names(builtin_styles)
 }
 
 #' @importFrom grDevices colors
@@ -56,12 +56,12 @@ style_from_rgb <- function(rgb, bg, num_colors, grey) {
 #'
 #' @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}).
+#' [styles()] for a list) and functions for the basic eight-color
+#' ANSI standard (`red`, `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
+#' many styles and functions. Instead, `make_style()` can be
 #' used to create a style (or a style function, or both).
 #'
 #' There are two ways to use this function: \enumerate{
@@ -69,22 +69,22 @@ style_from_rgb <- function(rgb, bg, num_colors, grey) {
 #'     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
+#'     [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
+#'   \item An R color name, see [colors()].
+#'   \item A 6- or 8-digit hexa color string, e.g. `#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
+#'     and blue channels, as returned by `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}
+#' `make_style()` detects the number of colors to use
+#' automatically (this can be overridden using the `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.
@@ -231,8 +231,8 @@ make_crayon <- function(ansi_seq) {
 #'       " 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 be combined using the `$` operator: \preformatted{  cat(yellow$bgMagenta$bold('Hello world!\n'))
+#' } See also [combine_styles()].
 #'
 #' Styles can also be nested, and then inner style takes
 #' precedence: \preformatted{  cat(green(
@@ -259,18 +259,18 @@ make_crayon <- function(ansi_seq) {
 #' @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.
+#' @seealso [make_style()] for using the 256 ANSI colors.
 #' @examples
 #' cat(blue("Hello", "world!"))
 #'
-#' cat("... to highlight the " \%+\% red("search term") \%+\%
+#' 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') \%+\%
+#'  'I am a green line ' %+%
+#'  blue$underline$bold('with a blue substring') %+%
 #'  ' that becomes green again!'
 #' ))
 #'
@@ -286,40 +286,37 @@ 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
+#' via `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.
+#' @seealso [crayon()] for the beginning of the crayon manual.
 #' @export
 #' @examples
 #' names(styles())
 #' cat(styles()[["bold"]]$close)
 
 styles <- function() {
-  my_styles
+  data_env$my_styles
 }
 
-my_styles <- structure(list(), names = character())
+data_env <- new.env(parent = emptyenv())
+
+data_env$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()))
+  data_env$my_styles[[style]] <- builtin_styles[[style]]
+  assign(style, make_style(style), envir = asNamespace("crayon"))
 })
 
 .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
+  data_env$my_styles[[name]] <- ansi_seq
 }
 
 #' Remove a style
@@ -337,6 +334,6 @@ define_style <- function(name, ansi_seq) {
 #' "new_style" %in% names(styles())
 
 drop_style <- function(style) {
-  my_styles[[style]] <<- NULL
+  data_env$my_styles[[style]] <- NULL
   invisible()
 }
diff --git a/R/show.r b/R/show.r
index 250b270..682d434 100644
--- a/R/show.r
+++ b/R/show.r
@@ -15,15 +15,17 @@ show_ansi_colors <- function(colors = num_colors()) {
   if (colors < 8) {
     cat("Colors are not supported")
   } else if (colors < 256) {
-    cat(ansi_colors_8, sep = "")
+    cat(ansi_colors_8(), sep = "")
     invisible(ansi_colors_8)
   } else {
-    cat(ansi_colors_256, sep = "")
+    cat(ansi_colors_256(), sep = "")
     invisible(ansi_colors_256)
   }
 }
 
-ansi_colors_256_col <-
+#' @importFrom grDevices rgb
+
+ansi_colors_256_col <- function() {
   sapply(0:5, function(r) {
     sapply(0:5, function(g) {
       c(sapply(0:5, function(b) {
@@ -32,19 +34,26 @@ ansi_colors_256_col <-
       }), "\n")
     })
   })
+}
+
+#' @importFrom grDevices grey
 
-ansi_colors_256_grey <-
+ansi_colors_256_grey <- function() {
   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_256 <- function() {
+  c(ansi_colors_256_col(), "\n", ansi_colors_256_grey())
+}
 
-ansi_colors_8 <-
+ansi_colors_8 <- function () {
   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
index a8636ae..1d9bdc8 100644
--- a/R/string.r
+++ b/R/string.r
@@ -1,11 +1,11 @@
 
 #' Convert to character
 #'
-#' This function just calls \code{as.character}, but it is
+#' This function just calls [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}.
+#' @param ... Further arguments to pass to `as.character()`.
 #' @return Character value.
 #'
 #' @export
@@ -55,3 +55,23 @@ chr <- function(x, ...) as.character(x, ...)
     paste0(lhs, rhs)
   }
 }
+
+#' @export
+as.character.crayon <- function(x, ...) {
+  start(x)
+}
+
+#' @export
+`-.crayon` <- function(e1, e2) {
+  if (!missing(e2)) {
+    base::`-`(e1, e2)
+  }
+  my_styles <- attr(e1, "_styles")
+  if (has_color()) {
+    for (i in seq_along(my_styles)) {
+      my_styles[[i]]$open <- my_styles[[i]]$close
+    }
+  }
+  attr(e1, "_styles") <- my_styles
+  e1
+}
diff --git a/R/string_operations.r b/R/string_operations.r
index 33eac5b..38c1729 100644
--- a/R/string_operations.r
+++ b/R/string_operations.r
@@ -35,13 +35,13 @@ map_to_ansi <- function(x, text = NULL) {
 
 #' Count number of characters in an ANSI colored string
 #'
-#' This is a color-aware counterpart of \code{base::nchar},
+#' This is a color-aware counterpart of [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}
+#' @param ... Additional arguments, passed on to `base::nchar()`
 #'   after removing ANSI escape sequences.
 #' @return Numeric vector, the length of the strings in the character
 #'   vector.
@@ -67,7 +67,7 @@ col_nchar <- function(x, ...) {
 
 #' Substring(s) of an ANSI colored string
 #'
-#' This is a color-aware counterpart of \code{base::substr}.
+#' This is a color-aware counterpart of [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.
@@ -75,10 +75,10 @@ col_nchar <- function(x, ...) {
 #' @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}.
+#'   of `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
+#'   of `x`.
+#' @return Character vector of the same length as `x`, containing
 #'   the requested substrings. ANSI styles are retained.
 #'
 #' @family ANSI string operations
@@ -134,19 +134,19 @@ col_substr <- function(x, start, stop) {
 
 #' Substring(s) of an ANSI colored string
 #'
-#' This is the color-aware counterpart of \code{base::substring}.
+#' This is the color-aware counterpart of [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}.
+#'   coarced to character. It is recycled to the longest of `first`
+#'   and `last`.
 #' @param first Starting index or indices, recycled to match the length
-#'   of \code{x}.
+#'   of `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
+#'   of `x`.
+#' @return Character vector of the same length as `x`, containing
 #'   the requested substrings. ANSI styles are retained.
 #'
 #' @family ANSI string operations
@@ -185,19 +185,19 @@ col_substring <- function(text, first, last = 1000000L) {
 
 #' Split an ANSI colored string
 #'
-#' This is the color-aware counterpart of \code{base::strsplit}.
+#' This is the color-aware counterpart of [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
+#'   such) containing regular expression(s) (unless `fixed = TRUE`) to use
+#'   for splitting.  If empty matches occur, in particular if `split` has
+#'   zero characters, `x` is split into single characters.
+#' @param ... Extra arguments are passed to `base::strsplit()`.
+#' @return A list of the same length as `x`, the \eqn{i}-th element of
+#'   which contains the vector of splits of `x[i]`. ANSI styles are
 #'   retained.
 #'
 #' @family ANSI string operations
@@ -253,3 +253,66 @@ col_strsplit <- function(x, split, ...) {
   )
   res
 }
+
+#' Align an ANSI colored string
+#'
+#' @param text The character vector to align.
+#' @param width Width of the field to align in.
+#' @param align Whether to align `"left"`, `"center"` or `"right"`.
+#' @param type Passed on to [col_nchar()] and there to [nchar()]
+#' @return The aligned character vector.
+#'
+#' @family ANSI string operations
+#' @export
+#' @examples
+#' col_align(red("foobar"), 20, "left")
+#' col_align(red("foobar"), 20, "center")
+#' col_align(red("foobar"), 20, "right")
+
+col_align <- function(text, width = getOption("width"),
+                      align = c("left", "center", "right"),
+                      type = "width") {
+
+  align <- match.arg(align)
+  nc <- col_nchar(text, type = type)
+
+  if (!length(text)) return(text)
+
+  if (align == "left") {
+    paste0(text, make_space(width - nc))
+
+  } else if (align == "center") {
+    paste0(make_space(ceiling((width - nc) / 2)),
+           text,
+           make_space(floor((width - nc) / 2)))
+
+  } else {
+    paste0(make_space(width - nc), text)
+  }
+}
+
+make_space <- function(num, filling = " ") {
+  num <- pmax(0, num)
+  res <- strrep(filling, num)
+  Encoding(res) <- Encoding(filling)
+  res
+}
+
+strrep <- function (x, times) {
+  x = as.character(x)
+  if (length(x) == 0L) return(x)
+
+  mapply(
+    function(x, times) {
+      if (is.na(x) || is.na(times)) {
+        NA_character_
+      } else if (times <= 0L) {
+        ""
+      } else {
+        paste0(rep(x, times), collapse = "")
+      }
+    },
+    x, times,
+    USE.NAMES = FALSE
+  )
+}
diff --git a/R/style-var.r b/R/style-var.r
index b8fc67c..7dbe395 100644
--- a/R/style-var.r
+++ b/R/style-var.r
@@ -1,13 +1,13 @@
 
 #' Add style to a string
 #'
-#' See \code{names(styles)}, or the crayon manual for available styles.
+#' See `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}}.
+#'   or its name, or an object to pass to [make_style()].
 #' @param bg Background style, a style function, or a name that
-#'   is passed to \code{\link{make_style}}.
+#'   is passed to [make_style()].
 #' @return Styled character vector.
 #'
 #' @export
diff --git a/R/utils.r b/R/utils.r
index 3938dae..d0e1267 100644
--- a/R/utils.r
+++ b/R/utils.r
@@ -1,35 +1,14 @@
 
-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]]))
+is_string <- function(x) {
+  is.character(x) && length(x) == 1 && !is.na(x)
 }
 
 check_string <- function(x) {
-  stopifnot(is.character(x), length(x) == 1, !is.na(x))
+  stopifnot(is_string(x))
 }
 
 mypaste <- function(..., sep = " ") {
-  args <- list(...)
-  if (any(!sapply(args, is.character))) stop("Need character strings")
+  args <- lapply(list(...), as.character)
   len <- setdiff(sapply(args, length), 1)
   if (length(len) > 1) {
     stop("All character vectors must have the same length (or length 1)")
@@ -125,5 +104,33 @@ emacs_version <- function() {
 }
 
 inside_emacs <- function() {
-  Sys.getenv("EMACS") != ""
+    Sys.getenv("EMACS") != "" || Sys.getenv("INSIDE_EMACS") != ""
+}
+
+rstudio_with_ansi_support <- function() {
+  if (Sys.getenv("RSTUDIO", "") == "") return(FALSE)
+
+  ## This is set *before* the rstudio initialization, in 1.1 and above
+  if ((cols <- Sys.getenv("RSTUDIO_CONSOLE_COLOR", "")) != "" &&
+      !is.na(as.numeric(cols))) {
+    return(TRUE)
+  }
+
+  ## This only works if the initialization is complete
+  requireNamespace("rstudioapi", quietly = TRUE) &&
+    rstudioapi::isAvailable() &&
+    rstudioapi::hasFun("getConsoleHasColor")
+}
+
+rstudio_initialized <- function() {
+  ## Not in RStudio, so no worries
+  if (Sys.getenv("RSTUDIO") == "") return(TRUE)
+
+  ## Otherwise check
+  requireNamespace("rstudioapi", quietly = TRUE) &&
+    rstudioapi::isAvailable()
+}
+
+os_type <- function() {
+  .Platform$OS.type
 }
diff --git a/inst/NEWS.md b/inst/NEWS.md
index 823069f..cf08eb8 100644
--- a/inst/NEWS.md
+++ b/inst/NEWS.md
@@ -1,3 +1,12 @@
+
+# 1.3.4
+
+* Style fucntions convert arguments to character now
+
+* Autodetect RStudio ANSI support
+
+* `col_align()` gains `type` argument, default `"width"` (#54).
+
 # 1.3.2
 
 * Removed dependency to `memoise` (@brodieG, #25)
diff --git a/inst/README.markdown b/inst/README.markdown
index 520afdf..c1ef8f1 100644
--- a/inst/README.markdown
+++ b/inst/README.markdown
@@ -11,11 +11,11 @@
 > 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)
+[![Linux Build Status](https://travis-ci.org/r-lib/crayon.svg?branch=master)](https://travis-ci.org/r-lib/crayon)
+[![Windows Build status](https://ci.appveyor.com/api/projects/status/github/r-lib/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)
+[![Coverage Status](https://img.shields.io/codecov/c/github/r-lib/crayon/master.svg)](https://codecov.io/github/r-lib/crayon?branch=master)
 
 With crayon it is easy to add color to terminal output, create styles for notes, warnings, errors; and combine styles.
 
@@ -25,7 +25,7 @@ inspired by [chalk](https://github.com/sindresorhus/chalk).
 ## Installation
 
 ```r
-devtools::install_github("gaborcsardi/crayon")
+devtools::install_github("r-lib/crayon")
 library(crayon)
 ```
 
@@ -122,7 +122,7 @@ 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
+`rgb()` 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.
 
diff --git a/man/chr.Rd b/man/chr.Rd
index df7f332..38d6f10 100644
--- a/man/chr.Rd
+++ b/man/chr.Rd
@@ -9,13 +9,12 @@ chr(x, ...)
 \arguments{
 \item{x}{Object to be coerced.}
 
-\item{...}{Further arguments to pass to \code{as.character}.}
+\item{...}{Further arguments to pass to \code{as.character()}.}
 }
 \value{
 Character value.
 }
 \description{
-This function just calls \code{as.character}, but it is
+This function just calls \code{\link[=as.character]{as.character()}}, but it is
 easier to type and read.
 }
-
diff --git a/man/col_align.Rd b/man/col_align.Rd
new file mode 100644
index 0000000..3b7e00f
--- /dev/null
+++ b/man/col_align.Rd
@@ -0,0 +1,34 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/string_operations.r
+\name{col_align}
+\alias{col_align}
+\title{Align an ANSI colored string}
+\usage{
+col_align(text, width = getOption("width"), align = c("left", "center",
+  "right"), type = "width")
+}
+\arguments{
+\item{text}{The character vector to align.}
+
+\item{width}{Width of the field to align in.}
+
+\item{align}{Whether to align \code{"left"}, \code{"center"} or \code{"right"}.}
+
+\item{type}{Passed on to \code{\link[=col_nchar]{col_nchar()}} and there to \code{\link[=nchar]{nchar()}}}
+}
+\value{
+The aligned character vector.
+}
+\description{
+Align an ANSI colored string
+}
+\examples{
+col_align(red("foobar"), 20, "left")
+col_align(red("foobar"), 20, "center")
+col_align(red("foobar"), 20, "right")
+}
+\seealso{
+Other ANSI string operations: \code{\link{col_nchar}},
+  \code{\link{col_strsplit}}, \code{\link{col_substring}},
+  \code{\link{col_substr}}
+}
diff --git a/man/col_nchar.Rd b/man/col_nchar.Rd
index 16d173c..4beefce 100644
--- a/man/col_nchar.Rd
+++ b/man/col_nchar.Rd
@@ -10,15 +10,15 @@ col_nchar(x, ...)
 \item{x}{Character vector, potentially ANSO styled, or a vector to be
 coarced to character.}
 
-\item{...}{Additional arguments, passed on to \code{base::nchar}
+\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.
+vector.
 }
 \description{
-This is a color-aware counterpart of \code{base::nchar},
+This is a color-aware counterpart of \code{\link[base:nchar]{base::nchar()}},
 which does not do well, since it also counts the ANSI control
 characters.
 }
@@ -35,7 +35,7 @@ col_nchar(str)
 nchar(strip_style(str))
 }
 \seealso{
-Other ANSI string operations: \code{\link{col_strsplit}},
-  \code{\link{col_substring}}, \code{\link{col_substr}}
+Other ANSI string operations: \code{\link{col_align}},
+  \code{\link{col_strsplit}}, \code{\link{col_substring}},
+  \code{\link{col_substr}}
 }
-
diff --git a/man/col_strsplit.Rd b/man/col_strsplit.Rd
index 36b5582..0a54001 100644
--- a/man/col_strsplit.Rd
+++ b/man/col_strsplit.Rd
@@ -15,15 +15,15 @@ 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}.}
+\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.
+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}.
+This is the color-aware counterpart of \code{\link[base:strsplit]{base::strsplit()}}.
 It works almost exactly like the original, but keeps the colors in the
 substrings.
 }
@@ -43,7 +43,7 @@ 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}}
+Other ANSI string operations: \code{\link{col_align}},
+  \code{\link{col_nchar}}, \code{\link{col_substring}},
+  \code{\link{col_substr}}
 }
-
diff --git a/man/col_substr.Rd b/man/col_substr.Rd
index 63d1592..f17ccd0 100644
--- a/man/col_substr.Rd
+++ b/man/col_substr.Rd
@@ -18,10 +18,10 @@ of \code{x}.}
 }
 \value{
 Character vector of the same length as \code{x}, containing
-  the requested substrings. ANSI styles are retained.
+the requested substrings. ANSI styles are retained.
 }
 \description{
-This is a color-aware counterpart of \code{base::substr}.
+This is a color-aware counterpart of \code{\link[base:substr]{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.
@@ -51,7 +51,7 @@ 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}}
+Other ANSI string operations: \code{\link{col_align}},
+  \code{\link{col_nchar}}, \code{\link{col_strsplit}},
+  \code{\link{col_substring}}
 }
-
diff --git a/man/col_substring.Rd b/man/col_substring.Rd
index df35131..493c619 100644
--- a/man/col_substring.Rd
+++ b/man/col_substring.Rd
@@ -19,10 +19,10 @@ of \code{x}.}
 }
 \value{
 Character vector of the same length as \code{x}, containing
-  the requested substrings. ANSI styles are retained.
+the requested substrings. ANSI styles are retained.
 }
 \description{
-This is the color-aware counterpart of \code{base::substring}.
+This is the color-aware counterpart of \code{\link[base:substring]{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.
@@ -52,7 +52,7 @@ 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}}
+Other ANSI string operations: \code{\link{col_align}},
+  \code{\link{col_nchar}}, \code{\link{col_strsplit}},
+  \code{\link{col_substr}}
 }
-
diff --git a/man/combine_styles.Rd b/man/combine_styles.Rd
index ddb0d95..a0e411a 100644
--- a/man/combine_styles.Rd
+++ b/man/combine_styles.Rd
@@ -1,8 +1,8 @@
 % Generated by roxygen2: do not edit by hand
 % Please edit documentation in R/combine.r
 \name{combine_styles}
-\alias{$.crayon}
 \alias{combine_styles}
+\alias{$.crayon}
 \title{Combine two or more ANSI styles}
 \usage{
 combine_styles(...)
@@ -34,7 +34,7 @@ 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()}.
+and the right hand side is the name of a style in \code{\link[=styles]{styles()}}.
 }
 \examples{
 ## Use style names
@@ -53,4 +53,3 @@ cat(alert("Warning!"), "\\n")
 alert <- bold $ red $ bgCyan
 cat(alert("Warning!"), "\\n")
 }
-
diff --git a/man/concat.Rd b/man/concat.Rd
index 0fa2e14..5aa19ce 100644
--- a/man/concat.Rd
+++ b/man/concat.Rd
@@ -1,8 +1,8 @@
 % Generated by roxygen2: do not edit by hand
 % Please edit documentation in R/string.r
 \name{concat}
-\alias{\%+\%}
 \alias{concat}
+\alias{\%+\%}
 \title{Concatenate character vectors}
 \usage{
 lhs \%+\% rhs
@@ -32,4 +32,3 @@ letters[1:10] \%+\% "-" \%+\% chr(1:10)
 ## This is empty (unlike for parse)
 character() \%+\% "*"
 }
-
diff --git a/man/crayon.Rd b/man/crayon.Rd
index 578879c..f48bc44 100644
--- a/man/crayon.Rd
+++ b/man/crayon.Rd
@@ -2,33 +2,34 @@
 % 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{crayon}
 \alias{reset}
-\alias{silver}
-\alias{strikethrough}
+\alias{bold}
+\alias{blurred}
+\alias{italic}
 \alias{underline}
-\alias{white}
+\alias{inverse}
+\alias{hidden}
+\alias{strikethrough}
+\alias{black}
+\alias{red}
+\alias{green}
 \alias{yellow}
+\alias{blue}
+\alias{magenta}
+\alias{cyan}
+\alias{white}
+\alias{silver}
+\alias{bgBlack}
+\alias{bgRed}
+\alias{bgGreen}
+\alias{bgYellow}
+\alias{bgBlue}
+\alias{bgMagenta}
+\alias{bgCyan}
+\alias{bgWhite}
 \title{Colored terminal output}
 \usage{
 ## Simple styles
@@ -56,14 +57,14 @@ has a corresponding function with the same name.
 
 
 \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)
+\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)
 }
 }
 
@@ -71,15 +72,15 @@ has a corresponding function with the same name.
 
 
 \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)
+\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)
 }
 }
 
@@ -87,14 +88,14 @@ has a corresponding function with the same name.
 
 
 \itemize{
-  \item bgBlack
-  \item bgRed
-  \item bgGreen
-  \item bgYellow
-  \item bgBlue
-  \item bgMagenta
-  \item bgCyan
-  \item bgWhite
+\item bgBlack
+\item bgRed
+\item bgGreen
+\item bgYellow
+\item bgBlue
+\item bgMagenta
+\item bgCyan
+\item bgWhite
 }
 }
 
@@ -112,7 +113,7 @@ to assemble stings with different styles. \preformatted{  cat("... to highlight
 }
 
 Styles can be combined using the \code{$} operator: \preformatted{  cat(yellow$bgMagenta$bold('Hello world!\n'))
-} See also \code{\link{combine_styles}}.
+} See also \code{\link[=combine_styles]{combine_styles()}}.
 
 Styles can also be nested, and then inner style takes
 precedence: \preformatted{  cat(green(
@@ -130,17 +131,18 @@ It is easy to define your own themes: \preformatted{  error <- red $ bold
   cat(note("Note: no such directory.\n"))
 }
 }
+
 \examples{
 cat(blue("Hello", "world!"))
 
-cat("... to highlight the " \\\%+\\\% red("search term") \\\%+\\\%
+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') \\\%+\\\%
+ 'I am a green line ' \%+\%
+ blue$underline$bold('with a blue substring') \%+\%
  ' that becomes green again!'
 ))
 
@@ -153,6 +155,5 @@ cat(note("Note: no such directory.\\n"))
 
 }
 \seealso{
-\code{\link{make_style}} for using the 256 ANSI colors.
+\code{\link[=make_style]{make_style()}} for using the 256 ANSI colors.
 }
-
diff --git a/man/drop_style.Rd b/man/drop_style.Rd
index 2320818..4f4a684 100644
--- a/man/drop_style.Rd
+++ b/man/drop_style.Rd
@@ -25,4 +25,3 @@ drop_style("new_style")
 \seealso{
 Other styles: \code{\link{make_style}}
 }
-
diff --git a/man/has_color.Rd b/man/has_color.Rd
index 5ff8c9e..1be3faa 100644
--- a/man/has_color.Rd
+++ b/man/has_color.Rd
@@ -14,26 +14,38 @@ 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.
+\item If the \code{crayon.enabled} option is set to \code{TRUE}
+with \code{\link[=options]{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.
 }
 }
+\section{Sinks}{
+
+Note that \code{has_color()} returns \code{FALSE} if a sink is active
+(see \code{\link[=sink]{sink()}}). It assumes that the constructed string will be printed
+to the standard output, and \code{sink()} redirects to a file, and usually
+you don't want ANSI colors in the file.
+
+The same applies to the case when R's standard output is redirected
+to a file, from the command line, e.g.:\preformatted{R -q -e 'cat(crayon::red("no color here\n"))' > /tmp/crayon-test.txt
+cat /tmp/crayon-test.txt
+}
+}
+
 \examples{
 has_color()
 }
-
diff --git a/man/has_style.Rd b/man/has_style.Rd
index e950bdc..7dd8635 100644
--- a/man/has_style.Rd
+++ b/man/has_style.Rd
@@ -2,7 +2,7 @@
 % Please edit documentation in R/has_ansi.r
 \name{has_style}
 \alias{has_style}
-\title{Check if a sting has some ANSI styling}
+\title{Check if a string has some ANSI styling}
 \usage{
 has_style(string)
 }
@@ -12,14 +12,13 @@ vector.}
 }
 \value{
 Logical vector, \code{TRUE} for the strings that have some
-  ANSI styling.
+ANSI styling.
 }
 \description{
-Check if a sting has some ANSI styling
+Check if a string 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
index 97f5313..4144a78 100644
--- a/man/make_style.Rd
+++ b/man/make_style.Rd
@@ -31,34 +31,34 @@ 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
+\code{\link[=styles]{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
+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.
+\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]{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).
+\item An R color name, see \code{\link[=colors]{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
+\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
@@ -82,4 +82,3 @@ 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
index ec4b434..d998024 100644
--- a/man/num_colors.Rd
+++ b/man/num_colors.Rd
@@ -31,13 +31,13 @@ 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}
+\url{https://github.com/r-lib/crayon/issues/17}
 
-For efficiency, \code{num_colors} caches its result. To
+For efficiency, \code{num_colors()} caches its result. To
 re-check the number of colors, set the \code{forget} argument to
-\code{TRUE}.
+\code{TRUE}. The cached value is only used if no sinks are active,
+see also \code{\link[=has_color]{has_color()}} for more information about sinks.
 }
 \examples{
 num_colors()
 }
-
diff --git a/man/show_ansi_colors.Rd b/man/show_ansi_colors.Rd
index ed9726b..20f361f 100644
--- a/man/show_ansi_colors.Rd
+++ b/man/show_ansi_colors.Rd
@@ -17,4 +17,3 @@ 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
index 9859a4e..d47a93d 100644
--- a/man/start.crayon.Rd
+++ b/man/start.crayon.Rd
@@ -1,9 +1,9 @@
 % Generated by roxygen2: do not edit by hand
 % Please edit documentation in R/parts.r
 \name{start.crayon}
+\alias{start.crayon}
 \alias{finish}
 \alias{finish.crayon}
-\alias{start.crayon}
 \title{Switch on or off a style}
 \usage{
 \method{start}{crayon}(x, ...)
@@ -37,4 +37,3 @@ get_name <- function() {
 name <- get_name()
 name
 }
-
diff --git a/man/strip_style.Rd b/man/strip_style.Rd
index d83e25e..0eb4484 100644
--- a/man/strip_style.Rd
+++ b/man/strip_style.Rd
@@ -18,4 +18,3 @@ Remove ANSI escape sequences from a string
 \examples{
 strip_style(red("foobar")) == "foobar"
 }
-
diff --git a/man/style.Rd b/man/style.Rd
index 9fd4151..5309e75 100644
--- a/man/style.Rd
+++ b/man/style.Rd
@@ -10,10 +10,10 @@ style(string, as = NULL, bg = NULL)
 \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}}.}
+or its name, or an object to pass to \code{\link[=make_style]{make_style()}}.}
 
 \item{bg}{Background style, a style function, or a name that
-is passed to \code{\link{make_style}}.}
+is passed to \code{\link[=make_style]{make_style()}}.}
 }
 \value{
 Styled character vector.
@@ -27,4 +27,3 @@ style("foobar", bold)
 style("foobar", "bold")
 bold("foobar")
 }
-
diff --git a/man/styles.Rd b/man/styles.Rd
index 8b834b5..9a6acf2 100644
--- a/man/styles.Rd
+++ b/man/styles.Rd
@@ -8,7 +8,7 @@ styles()
 }
 \value{
 A named list. Each list element is a list of two
-  strings, named \sQuote{open} and \sQuote{close}.
+strings, named \sQuote{open} and \sQuote{close}.
 }
 \description{
 You can use this function to list all availables crayon styles,
@@ -20,6 +20,5 @@ names(styles())
 cat(styles()[["bold"]]$close)
 }
 \seealso{
-\code{\link{crayon}} for the beginning of the crayon manual.
+\code{\link[=crayon]{crayon()}} for the beginning of the crayon manual.
 }
-
diff --git a/tests/testthat.R b/tests/testthat.R
index 4f61344..dd2e07c 100644
--- a/tests/testthat.R
+++ b/tests/testthat.R
@@ -1,6 +1,4 @@
 
-if (Sys.getenv("NOT_CRAN") != "") {
-  library(crayon)
-  library(testthat)
-  test_check("crayon")
-}
+library(crayon)
+library(testthat)
+test_check("crayon")
diff --git a/tests/testthat/test-ansi256.R b/tests/testthat/test-ansi256.R
new file mode 100644
index 0000000..83e8e01
--- /dev/null
+++ b/tests/testthat/test-ansi256.R
@@ -0,0 +1,62 @@
+
+context("ansi-256")
+
+## These are really basic cases...
+
+test_that("ansi256", {
+  cases <- list(
+    list(c(0,0,0), 233),
+    list(c(255,0,0), 197),
+    list(c(0,255,0), 47),
+    list(c(0,0,255), 22),
+    list(c(255,255,255), 256)
+  )
+
+  for (case in cases) {
+    exp1 <- list(
+      open = paste0("\033[38;5;", case[[2]] - 1, "m"),
+      close = "\033[39m"
+    )
+    exp2 <- list(
+      open = paste0("\033[48;5;", case[[2]] - 1, "m"),
+      close = "\033[49m"
+    )
+    expect_equal(ansi256(case[[1]], bg = FALSE), exp1)
+    expect_equal(ansi256(case[[1]], bg = TRUE), exp2)
+  }
+})
+
+test_that("ansi256, grey", {
+  cases <- list(
+    list(c(0,0,0), 233),
+    list(c(128,128,128), 245),
+    list(c(255,255,255), 256)
+  )
+
+  for (case in cases) {
+    exp1 <- list(
+      open = paste0("\033[38;5;", case[[2]] - 1, "m"),
+      close = "\033[39m"
+    )
+    exp2 <- list(
+      open = paste0("\033[48;5;", case[[2]] - 1, "m"),
+      close = "\033[49m"
+    )
+    expect_equal(ansi256(case[[1]], grey = TRUE, bg = FALSE), exp1)
+    expect_equal(ansi256(case[[1]], grey = TRUE, bg = TRUE), exp2)
+  }
+})
+
+test_that("ansi256_rgb_index", {
+  cases <- list(
+    list(c(0,0,0), 233),
+    list(c(255,0,0), 197),
+    list(c(0,255,0), 47),
+    list(c(0,0,255), 22),
+    list(c(255,255,255), 256)
+  )
+
+  for (case in cases) {
+    expect_equal(do.call(ansi256_rgb_index, as.list(case[[1]])), case[[2]])
+  }
+})
diff --git a/tests/testthat/test-combine.R b/tests/testthat/test-combine.R
new file mode 100644
index 0000000..ead6594
--- /dev/null
+++ b/tests/testthat/test-combine.R
@@ -0,0 +1,37 @@
+
+context("combine_styles")
+
+test_that("one style", {
+  expect_equal(combine_styles(red), red)
+  expect_equal(combine_styles(bold), bold)
+})
+
+test_that("style objects", {
+  withr::with_options(
+    list(crayon.enabled = TRUE, crayon.colors = 256), {
+      expect_equal(
+        combine_styles(red, bold)("blah"),
+        red(bold("blah"))
+      )
+      expect_equal(
+        combine_styles(red, bold, underline)("foo"),
+        red(bold(underline("foo")))
+      )
+    }
+  )
+})
+
+test_that("create styles on the fly", {
+  withr::with_options(
+    list(crayon.enabled = TRUE, crayon.colors = 256), {
+      expect_equal(
+        combine_styles("darkolivegreen", bold)("blah"),
+        make_style("darkolivegreen")((bold("blah")))
+      )
+      expect_equal(
+        combine_styles(bold, "darkolivegreen", underline)("foo"),
+        bold(make_style("darkolivegreen")(underline("foo")))
+      )
+    }
+  )
+})
diff --git a/tests/testthat/test-has-color.r b/tests/testthat/test-has-color.r
index 607d181..6c52b8f 100644
--- a/tests/testthat/test-has-color.r
+++ b/tests/testthat/test-has-color.r
@@ -1,30 +1,58 @@
 
 context("Color detection")
 
-test_that("Color is detected properly", {
-
-  op <- options()
-  on.exit(options(op), add = TRUE)
+test_that("has_color, option", {
+
+  withr::with_options(
+    c(crayon.enabled = FALSE),
+    expect_false(has_color())
+  )
+  withr::with_options(
+    c(crayon.enabled = TRUE),
+    expect_true(has_color())
+  )
+})
 
-  ## If disabled, then no
-  options(crayon.enabled = FALSE)
-  hc <- has_color()
-  options(op)
-  expect_false(hc)
+test_that("has_color, rstudio", {
+  mockery::stub(has_color, "rstudio_with_ansi_support", TRUE)
+  mockery::stub(has_color, "rstudioapi::callFun", TRUE)
+  expect_true(has_color())
+})
 
-  ## If enabled, then yes
-  options(crayon.enabled = TRUE)
-  hc <- has_color()
-  expect_true(hc)
+test_that("has_color, not a terminal", {
+  mockery::stub(has_color, "rstudio_with_ansi_support", FALSE)
+  mockery::stub(has_color, "isatty", FALSE)
+  withr::with_options(
+    list(crayon.enabled = NULL),
+    expect_false(has_color())
+  )
+})
 
+test_that("has_color, windows terminal", {
+  mockery::stub(has_color, "rstudio_with_ansi_support", FALSE)
+  mockery::stub(has_color, "os_type", "windows")
+  withr::with_envvar(
+    c(ConEmuANSI = "ON", CMDER_ROOT = ""),
+    expect_true(has_color())
+  )
+  withr::with_envvar(
+    c(ConEmuANSI = "OFF", CMDER_ROOT = "/foobar"),
+    expect_true(has_color())
+  )
+  withr::with_options(
+    list(crayon.enabled = NULL),
+    withr::with_envvar(
+      c(ConEmuANSI = "OFF", CMDER_ROOT = NA_character_),
+      expect_false(has_color())
+    )
+  )
 })
 
 test_that("number of colors is detected", {
 
   nc <- num_colors()
-  expect_more_than(nc, 0)
+  expect_true(nc > 0)
   expect_equal(nc, as.integer(nc))
-
 })
 
 test_that("closure based memoization works", {
@@ -46,31 +74,18 @@ test_that("closure based memoization works", {
 
 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!",
+  ## if tput errors num_colors is 8
+  mockery::stub(get_terminal_colors, "system", function(...) stop("Error!"))
+  expect_equal(get_terminal_colors(), 8)
 
-            expect_equal(num_colors(forget = TRUE), 8)
-            )
+  ## if tput returns nothing num_colors is 8
+  mockery::stub(get_terminal_colors, "system", function(...) character(0))
+  expect_equal(get_terminal_colors(), 8)
 
-  # if tput returns a number the result is that number
-  with_mock(
-            `base::system` = function(...) "16",
+  ## if tput returns a non-number num_colors is 8
+  mockery::stub(get_terminal_colors, "system", function(...) "no colors!")
 
-            expect_equal(num_colors(forget = TRUE), 16)
-            )
+  ## if tput returns a number the result is that number
+  mockery::stub(get_terminal_colors, "system", function(...) 16)
+  expect_equal(get_terminal_colors(), 16)
 })
diff --git a/tests/testthat/test-operations.R b/tests/testthat/test-operations.R
index 3cefbb1..4cae284 100644
--- a/tests/testthat/test-operations.R
+++ b/tests/testthat/test-operations.R
@@ -84,7 +84,9 @@ test_that("col_substr corner cases", {
 
   # 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")
+  suppressWarnings(
+    expect_error(col_substr("abc", "hello", 1), "non-numeric")
+  )
 
 })
 
@@ -190,3 +192,44 @@ test_that("Weird length 'split'", {
     col_strsplit("ab", character(0L)), strsplit("ab", character(0L))
   )
 })
+
+test_that("col_align", {
+  expect_equal(col_align(character()), character())
+  expect_equal(col_align("", 0), "")
+  expect_equal(col_align(" ", 0), " ")
+  expect_equal(col_align(" ", 1), " ")
+  expect_equal(col_align(" ", 2), "  ")
+  expect_equal(col_align("a", 1), "a")
+  expect_equal(col_align(letters, 1), letters)
+  expect_equal(col_align(letters, 0), letters)
+  expect_equal(col_align(letters, -1), letters)
+
+  expect_equal(col_align(letters, 2), paste0(letters, " "))
+  expect_equal(col_align(letters, 3, "center"), paste0(" ", letters, " "))
+  expect_equal(col_align(letters, 2, "right"), paste0(" ", letters))
+
+  expect_equal(
+    col_align(c("foo", "foobar", "", "a"), 6, "left"),
+    c("foo   ", "foobar", "      ", "a     "))
+
+  expect_equal(
+    col_align(c("foo", "foobar", "", "a"), 6, "center"),
+    c("  foo ", "foobar", "      ", "   a  "))
+
+  expect_equal(
+    col_align(c("foo", "foobar", "", "a"), 6, "right"),
+    c("   foo", "foobar", "      ", "     a"))
+
+  # #54: alignment of wide characters
+  expect_equal(
+    col_align(c("foo", "\u6210\u4ea4\u65e5", "", "a"), 6, "left"),
+    c("foo   ", "\u6210\u4ea4\u65e5", "      ", "a     "))
+
+  expect_equal(
+    col_align(c("foo", "\u6210\u4ea4\u65e5", "", "a"), 6, "center"),
+    c("  foo ", "\u6210\u4ea4\u65e5", "      ", "   a  "))
+
+  expect_equal(
+    col_align(c("foo", "\u6210\u4ea4\u65e5", "", "a"), 6, "right"),
+    c("   foo", "\u6210\u4ea4\u65e5", "      ", "     a"))
+})

-- 
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