From 87211eca482322242f5fa4a09af256b4a133903c Mon Sep 17 00:00:00 2001 From: hadley Date: Mon, 15 May 2017 14:04:33 -0500 Subject: [PATCH 001/133] Add GitHub links to DESCRIPTION --- DESCRIPTION | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index bda9da4b8..4d74d5789 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,3 +10,5 @@ Description: Provides a `colformat` generic designed for formatting columns License: GPL-3 Encoding: UTF-8 LazyData: true +URL: https://github.com/hadley/colformat +BugReports: https://github.com/hadley/colformat/issues From 7281527b2b656c6592bbe01cb5390d1932a1e314 Mon Sep 17 00:00:00 2001 From: hadley Date: Mon, 15 May 2017 14:16:29 -0500 Subject: [PATCH 002/133] Add colformat generic --- DESCRIPTION | 2 ++ NAMESPACE | 6 ++++-- R/colformat.R | 16 ++++++++++++++++ man/colformat.Rd | 22 ++++++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 R/colformat.R create mode 100644 man/colformat.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 4d74d5789..f0e90bf79 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,3 +12,5 @@ Encoding: UTF-8 LazyData: true URL: https://github.com/hadley/colformat BugReports: https://github.com/hadley/colformat/issues +Imports: crayon +RoxygenNote: 6.0.1 diff --git a/NAMESPACE b/NAMESPACE index 884a6312a..f3d8f0f91 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,2 +1,4 @@ -# Generated by roxygen2: fake comment so roxygen2 overwrites silently. -exportPattern("^[^\\.]") +# Generated by roxygen2: do not edit by hand + +S3method(print,colformat) +export(colformat) diff --git a/R/colformat.R b/R/colformat.R new file mode 100644 index 000000000..b62009111 --- /dev/null +++ b/R/colformat.R @@ -0,0 +1,16 @@ +#' Format a vector suitable for tabular display +#' +#' @param x A vector to format +#' @param width Preferred width of output +#' @param ... Other arguments passed to methods +#' @return A character vector where every element is the same width. +#' Should have `colformat` for better default print method. +#' @export +colformat <- function(x, ..., width = NA) { + UseMethod("colformat") +} + +#' @export +print.colformat <- function(x, ...) { + cat(paste(x, collapse = "\n")) +} diff --git a/man/colformat.Rd b/man/colformat.Rd new file mode 100644 index 000000000..8f55fc15a --- /dev/null +++ b/man/colformat.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/colformat.R +\name{colformat} +\alias{colformat} +\title{Format a vector suitable for tabular display} +\usage{ +colformat(x, ..., width = NA) +} +\arguments{ +\item{x}{A vector to format} + +\item{...}{Other arguments passed to methods} + +\item{width}{Preferred width of output} +} +\value{ +A character vector where every element is the same width. + Should have `colformat` for better default print method. +} +\description{ +Format a vector suitable for tabular display +} From 4eb03715eb3fee76052e5be7ce2169d1e29f8650 Mon Sep 17 00:00:00 2001 From: hadley Date: Mon, 15 May 2017 15:33:37 -0500 Subject: [PATCH 003/133] Method for logical --- DESCRIPTION | 4 +++- NAMESPACE | 2 ++ R/colformat.R | 45 +++++++++++++++++++++++++++++++++++++++++++-- R/utils.R | 12 ++++++++++++ man/colformat.Rd | 5 +++++ 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 R/utils.R diff --git a/DESCRIPTION b/DESCRIPTION index f0e90bf79..6284302ff 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,5 +12,7 @@ Encoding: UTF-8 LazyData: true URL: https://github.com/hadley/colformat BugReports: https://github.com/hadley/colformat/issues -Imports: crayon +Imports: + boxes, + crayon RoxygenNote: 6.0.1 diff --git a/NAMESPACE b/NAMESPACE index f3d8f0f91..779286c14 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,4 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method(colformat,logical) +S3method(format,colformat) S3method(print,colformat) export(colformat) diff --git a/R/colformat.R b/R/colformat.R index b62009111..edc8cb097 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -6,11 +6,52 @@ #' @return A character vector where every element is the same width. #' Should have `colformat` for better default print method. #' @export +#' @examples +#' +#' x <- sample(c(TRUE, FALSE, NA), 10, replace = TRUE, prob = c(0.4, 0.4, 0.2)) +#' colformat(x) colformat <- function(x, ..., width = NA) { UseMethod("colformat") } #' @export -print.colformat <- function(x, ...) { - cat(paste(x, collapse = "\n")) +format.colformat <- function(x, title = "title", ...) { + align <- attr(x, "align") + width <- max(nchar(title), attr(x, "width")) + + structure( + col_align(x, width = width, align = align), + title = col_align(title, width = width, align = align), + width = width + ) +} + +#' @export +print.colformat <- function(x, title = "title", ...) { + x <- format(x, title = title, ...) + + cat_line(attr(x, "title")) + cat_line(boxes::rule(line = 1, width = attr(x, "width"))) + cat_line(paste(x, collapse = "\n")) +} + +new_colformat <- function(x, width, align = "left") { + structure( + x, + width = width, + align = align, + class = "colformat" + ) +} + +# Methods ----------------------------------------------------------------- + +#' @export +colformat.logical <- function(x, title, ..., width = NA) { + out <- character(length(x)) + out[x & !is.na(x)] <- crayon::green("*") + out[!x & !is.na(x)] <- crayon::silver("-") + out[is.na(x)] <- col_na() + + new_colformat(out, width = 1, align = "right") } diff --git a/R/utils.R b/R/utils.R new file mode 100644 index 000000000..aee8e42d9 --- /dev/null +++ b/R/utils.R @@ -0,0 +1,12 @@ +cat_line <- function(...) { + cat(..., "\n", sep = "") +} + +col_na <- function() { + crayon::red("?") +} + +col_align <- function(x, width, align) { + vapply(x, crayon::col_align, width = width, align = align, + FUN.VALUE = character(1)) +} diff --git a/man/colformat.Rd b/man/colformat.Rd index 8f55fc15a..fcac02c2d 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -20,3 +20,8 @@ A character vector where every element is the same width. \description{ Format a vector suitable for tabular display } +\examples{ + +x <- sample(c(TRUE, FALSE, NA), 10, replace = TRUE, prob = c(0.4, 0.4, 0.2)) +colformat(x) +} From 768d65d11bea14ba15b7bfe5cecd00cbd30d4222 Mon Sep 17 00:00:00 2001 From: hadley Date: Mon, 15 May 2017 15:52:34 -0500 Subject: [PATCH 004/133] Add character method --- NAMESPACE | 1 + R/colformat.R | 24 +++++++++++++++++++++--- R/utils.R | 9 +++++++++ man/colformat.Rd | 9 +++++++-- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 779286c14..3753ffbe1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method(colformat,character) S3method(colformat,logical) S3method(format,colformat) S3method(print,colformat) diff --git a/R/colformat.R b/R/colformat.R index edc8cb097..490a707d1 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -3,13 +3,18 @@ #' @param x A vector to format #' @param width Preferred width of output #' @param ... Other arguments passed to methods -#' @return A character vector where every element is the same width. -#' Should have `colformat` for better default print method. +#' @return A character vector with class `colformat` and +#' `width` and `align` attributes. #' @export #' @examples #' #' x <- sample(c(TRUE, FALSE, NA), 10, replace = TRUE, prob = c(0.4, 0.4, 0.2)) #' colformat(x) +#' +#' x <- c("This is string is rather long", NA, "?", "Short") +#' colformat(x) +#' colformat(x, width = 30) +#' colformat(x, width = 5) colformat <- function(x, ..., width = NA) { UseMethod("colformat") } @@ -47,7 +52,7 @@ new_colformat <- function(x, width, align = "left") { # Methods ----------------------------------------------------------------- #' @export -colformat.logical <- function(x, title, ..., width = NA) { +colformat.logical <- function(x, ..., width = NA) { out <- character(length(x)) out[x & !is.na(x)] <- crayon::green("*") out[!x & !is.na(x)] <- crayon::silver("-") @@ -55,3 +60,16 @@ colformat.logical <- function(x, title, ..., width = NA) { new_colformat(out, width = 1, align = "right") } + +#' @export +colformat.character <- function(x, ..., width = NA) { + if (is.na(width)) { + width <- pmin(max(nchar(x), na.rm = TRUE), 20) + } + + x <- encodeString(x, na.encode = FALSE) + out <- str_trunc(x, width = width) + out[is.na(out)] <- col_na() + + new_colformat(out, width = width, align = "left") +} diff --git a/R/utils.R b/R/utils.R index aee8e42d9..6c1183f50 100644 --- a/R/utils.R +++ b/R/utils.R @@ -10,3 +10,12 @@ col_align <- function(x, width, align) { vapply(x, crayon::col_align, width = width, align = align, FUN.VALUE = character(1)) } + +str_trunc <- function(x, width = 20) { + str_width <- nchar(x, type = "width") + + too_long <- !is.na(x) & str_width > width + x[too_long] <- paste0(substr(x[too_long], 1, width - 1), "\u2026") + + x +} diff --git a/man/colformat.Rd b/man/colformat.Rd index fcac02c2d..83f3a503a 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -14,8 +14,8 @@ colformat(x, ..., width = NA) \item{width}{Preferred width of output} } \value{ -A character vector where every element is the same width. - Should have `colformat` for better default print method. +A character vector with class `colformat` and + `width` and `align` attributes. } \description{ Format a vector suitable for tabular display @@ -24,4 +24,9 @@ Format a vector suitable for tabular display x <- sample(c(TRUE, FALSE, NA), 10, replace = TRUE, prob = c(0.4, 0.4, 0.2)) colformat(x) + +x <- c("This is string is rather long", NA, "?", "Short") +colformat(x) +colformat(x, width = 30) +colformat(x, width = 5) } From 93bd065e6e5db8d5d434108d3f64bf09ba5f9969 Mon Sep 17 00:00:00 2001 From: hadley Date: Mon, 15 May 2017 15:56:10 -0500 Subject: [PATCH 005/133] Add date + date/time methods And start extracting out style functions --- DESCRIPTION | 1 - NAMESPACE | 2 ++ R/colformat.R | 43 +++++++++++++++++++++++++++++++++++-------- R/styles.R | 20 ++++++++++++++++++++ R/utils.R | 4 ---- man/colformat.Rd | 20 ++++++++++++++++++-- 6 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 R/styles.R diff --git a/DESCRIPTION b/DESCRIPTION index 6284302ff..cd5018371 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,6 +13,5 @@ LazyData: true URL: https://github.com/hadley/colformat BugReports: https://github.com/hadley/colformat/issues Imports: - boxes, crayon RoxygenNote: 6.0.1 diff --git a/NAMESPACE b/NAMESPACE index 3753ffbe1..d3969e301 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,7 @@ # Generated by roxygen2: do not edit by hand +S3method(colformat,Date) +S3method(colformat,POSIXct) S3method(colformat,character) S3method(colformat,logical) S3method(format,colformat) diff --git a/R/colformat.R b/R/colformat.R index 490a707d1..43426e4ab 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -1,21 +1,24 @@ #' Format a vector suitable for tabular display #' #' @param x A vector to format -#' @param width Preferred width of output #' @param ... Other arguments passed to methods #' @return A character vector with class `colformat` and #' `width` and `align` attributes. #' @export #' @examples #' -#' x <- sample(c(TRUE, FALSE, NA), 10, replace = TRUE, prob = c(0.4, 0.4, 0.2)) +#' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) #' colformat(x) #' #' x <- c("This is string is rather long", NA, "?", "Short") #' colformat(x) #' colformat(x, width = 30) #' colformat(x, width = 5) -colformat <- function(x, ..., width = NA) { +#' +#' date <- as.Date("2017-05-15") +#' colformat(date + c(1, NA, 3:5)) +#' colformat(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) +colformat <- function(x, ...) { UseMethod("colformat") } @@ -35,8 +38,7 @@ format.colformat <- function(x, title = "title", ...) { print.colformat <- function(x, title = "title", ...) { x <- format(x, title = title, ...) - cat_line(attr(x, "title")) - cat_line(boxes::rule(line = 1, width = attr(x, "width"))) + cat_line(crayon::bold(attr(x, "title"))) cat_line(paste(x, collapse = "\n")) } @@ -52,16 +54,41 @@ new_colformat <- function(x, width, align = "left") { # Methods ----------------------------------------------------------------- #' @export -colformat.logical <- function(x, ..., width = NA) { +#' @rdname colformat +colformat.logical <- function(x, ...) { out <- character(length(x)) - out[x & !is.na(x)] <- crayon::green("*") - out[!x & !is.na(x)] <- crayon::silver("-") + out[x & !is.na(x)] <- style_accent("*") + out[!x & !is.na(x)] <- style_subtle("-") out[is.na(x)] <- col_na() new_colformat(out, width = 1, align = "right") } #' @export +#' @rdname colformat +colformat.Date <- function(x, ...) { + x <- format(x, format = "%Y-%m-%d") + x[is.na(x)] <- col_na() + + new_colformat(x, width = 11, align = "right") +} + +#' @export +#' @rdname colformat +colformat.POSIXct <- function(x, ...) { + date <- format(x, format = "%Y-%m-%d") + time <- format(x, format = "%H:%M:%S") + + datetime <- paste0(date, " " , style_subtle(time)) + datetime[is.na(x)] <- col_na() + + new_colformat(datetime, width = 19, align = "right") +} + + +#' @export +#' @param width Preferred width of output +#' @rdname colformat colformat.character <- function(x, ..., width = NA) { if (is.na(width)) { width <- pmin(max(nchar(x), na.rm = TRUE), 20) diff --git a/R/styles.R b/R/styles.R new file mode 100644 index 000000000..597c161bf --- /dev/null +++ b/R/styles.R @@ -0,0 +1,20 @@ +style_accent <- function(x) { + crayon::green(x) +} + +style_subtle <- function(...) { + style_grey(0.8, ...) +} + +style_na <- function(x) { + crayon::bold(crayon::style(x, grDevices::rgb(5, 3, 0, maxColorValue = 5))) +} + +style_grey <- function(level, ...) { + crayon::style( + paste0(...), + crayon::make_style(grDevices::grey(level), grey = TRUE) + ) +} + +col_na <- function() style_na("?") diff --git a/R/utils.R b/R/utils.R index 6c1183f50..9ab61a210 100644 --- a/R/utils.R +++ b/R/utils.R @@ -2,10 +2,6 @@ cat_line <- function(...) { cat(..., "\n", sep = "") } -col_na <- function() { - crayon::red("?") -} - col_align <- function(x, width, align) { vapply(x, crayon::col_align, width = width, align = align, FUN.VALUE = character(1)) diff --git a/man/colformat.Rd b/man/colformat.Rd index 83f3a503a..d9626dd38 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -2,9 +2,21 @@ % Please edit documentation in R/colformat.R \name{colformat} \alias{colformat} +\alias{colformat.logical} +\alias{colformat.Date} +\alias{colformat.POSIXct} +\alias{colformat.character} \title{Format a vector suitable for tabular display} \usage{ -colformat(x, ..., width = NA) +colformat(x, ...) + +\method{colformat}{logical}(x, ...) + +\method{colformat}{Date}(x, ...) + +\method{colformat}{POSIXct}(x, ...) + +\method{colformat}{character}(x, ..., width = NA) } \arguments{ \item{x}{A vector to format} @@ -22,11 +34,15 @@ Format a vector suitable for tabular display } \examples{ -x <- sample(c(TRUE, FALSE, NA), 10, replace = TRUE, prob = c(0.4, 0.4, 0.2)) +x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) colformat(x) x <- c("This is string is rather long", NA, "?", "Short") colformat(x) colformat(x, width = 30) colformat(x, width = 5) + +date <- as.Date("2017-05-15") +colformat(date + c(1, NA, 3:5)) +colformat(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) } From 3d63d87190a4553ebb4d692792cd233d7a455546 Mon Sep 17 00:00:00 2001 From: hadley Date: Mon, 15 May 2017 16:28:23 -0500 Subject: [PATCH 006/133] Tweak colours --- R/styles.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/styles.R b/R/styles.R index 597c161bf..1a428a34e 100644 --- a/R/styles.R +++ b/R/styles.R @@ -3,11 +3,11 @@ style_accent <- function(x) { } style_subtle <- function(...) { - style_grey(0.8, ...) + style_grey(0.5, ...) } style_na <- function(x) { - crayon::bold(crayon::style(x, grDevices::rgb(5, 3, 0, maxColorValue = 5))) + crayon::bold(crayon::style(x, grDevices::rgb(5, 2, 0, maxColorValue = 5))) } style_grey <- function(level, ...) { From 7ce3e184b440faa87c5bc648fa6455704b51facc Mon Sep 17 00:00:00 2001 From: hadley Date: Tue, 16 May 2017 14:11:01 -0500 Subject: [PATCH 007/133] First pass at significant digit formatting --- NAMESPACE | 4 ++ R/colformat.R | 15 ++++++ R/sigfig.R | 110 ++++++++++++++++++++++++++++++++++++++++++ R/styles.R | 11 ++++- man/colformat.Rd | 13 +++++ man/decimal_format.Rd | 30 ++++++++++++ 6 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 R/sigfig.R create mode 100644 man/decimal_format.Rd diff --git a/NAMESPACE b/NAMESPACE index d3969e301..f7dd3f865 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,6 +4,10 @@ S3method(colformat,Date) S3method(colformat,POSIXct) S3method(colformat,character) S3method(colformat,logical) +S3method(colformat,numeric) S3method(format,colformat) +S3method(format,decimal_format) S3method(print,colformat) +S3method(print,decimal_format) export(colformat) +export(decimal_format) diff --git a/R/colformat.R b/R/colformat.R index 43426e4ab..36958fcde 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -6,6 +6,12 @@ #' `width` and `align` attributes. #' @export #' @examples +#' x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +#' colformat(x) +#' colformat(-x) +#' colformat(runif(10)) +#' colformat(rcauchy(10)) +#' colformat(c(1, 0.5, 1e-10, NA, NaN, Inf, -Inf)) #' #' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) #' colformat(x) @@ -64,6 +70,15 @@ colformat.logical <- function(x, ...) { new_colformat(out, width = 1, align = "right") } +#' @export +#' @rdname colformat +#' @param sigfig Minimum number of significant figures to display. Numbers +#' larger than 1 will potentially show more signficiant figures than this +#' but they will be greyed out. +colformat.numeric <- function(x, ..., sigfig = 3) { + decimal_format(x, sigfig = sigfig) +} + #' @export #' @rdname colformat colformat.Date <- function(x, ...) { diff --git a/R/sigfig.R b/R/sigfig.R new file mode 100644 index 000000000..4df4f5855 --- /dev/null +++ b/R/sigfig.R @@ -0,0 +1,110 @@ +#' Create a decimal table +#' +#' This is a key building block of number formatting. It carefully uses +#' colour and alignment to make it as easy as possible to compare numbers. +#' +#' @return A tibble with four columns: +#' * `neg`: negative sign or space, if needed +#' * `lhs`: whole number +#' * `dec`: decimal point, if needed +#' * `rhs`: remainder of number +#' +#' @keywords internal +#' @export +#' @examples +#' x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +#' decimal_format(x, 3) +#' +#' x <- sample(x) * sample(c(-1, 1), length(x), rep = TRUE) +#' decimal_format(x, 3) +#' +#' decimal_format(c(Inf, -Inf, NA, NaN), 3) +#' decimal_format(c(1e10, 1e-10), 3) +decimal_format <- function(x, sigfig = 3) { + stopifnot(is.numeric(sigfig), length(sigfig) == 1) + sigfig <- as.integer(sigfig) + if (sigfig < 1L) { + stop("Must show at least one significant figure", call. = FALSE) + } + + n <- length(x) + abs_x <- abs(x) + + # If already bigger than sigfig, can round to zero. + # Otherwise ensure we have sigfig digits shown + exp <- floor(log10(abs_x)) + digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) + + # Compute column components in natural type then as fixed width strings + + neg <- !is.na(x) & x < 0 + if (any(neg)) { + neg_col <- ifelse(neg, style_neg("-"), " ") + } else { + neg_col <- rep("", n) + } + + num <- is.finite(x) + + lhs <- round(abs_x, 0) + lhs_str <- sprintf("%.0f", lhs) + lhs_width <- max(nchar(lhs_str)) + lhs_sig <- substr(lhs_str, 1, sigfig) + lhs_non <- substr(lhs_str, sigfig + 1, nchar(lhs_str)) + + lhs_col <- ifelse(num, + paste0(ifelse(neg & lhs != 0, style_neg(lhs_sig), lhs_sig), style_subtle(lhs_non)), + style_na(lhs_str) + ) + lhs_col <- crayon::col_align(lhs_col, width = lhs_width, align = "right") + + dec <- !is.na(digits) & digits > 0 + if (any(dec)) { + dec_col <- ifelse(dec, ".", " ") + } else { + dec_col <- rep("", n) + } + + rhs_num <- as.character(abs(round((abs_x - lhs) * 10 ^ digits))) + rhs_zero <- strrep("0", pmax(0, digits - sigfig)) + + rhs_col <- ifelse(dec, + paste0(style_subtle(rhs_zero), ifelse(neg, style_neg(rhs_num), rhs_num)), + "" + ) + + structure( + list( + neg = neg_col, + lhs = lhs_col, + dec = dec_col, + rhs = rhs_col + ), + class = "decimal_format" + ) +} + + +#' @export +format.decimal_format <- function(x, title = "title", ...) { + + row <- paste0(x$neg, x$lhs, x$dec, x$rhs) + width <- max(nchar(title), crayon::col_nchar(row)) + + structure( + row, + title = col_align(title, width = width, align = "right"), + width = width + ) +} + +#' @export +print.decimal_format <- function(x, title = "title", ...) { + align <- attr(x, "align") + width <- max(nchar(title), attr(x, "width")) + + x <- format(x, title = title, ...) + + cat_line(crayon::bold(attr(x, "title"))) + cat_line(paste(x, collapse = "\n")) +} diff --git a/R/styles.R b/R/styles.R index 1a428a34e..4a0083329 100644 --- a/R/styles.R +++ b/R/styles.R @@ -10,6 +10,11 @@ style_na <- function(x) { crayon::bold(crayon::style(x, grDevices::rgb(5, 2, 0, maxColorValue = 5))) } +style_neg <- function(x) { + crayon::red(x) +} + + style_grey <- function(level, ...) { crayon::style( paste0(...), @@ -17,4 +22,8 @@ style_grey <- function(level, ...) { ) } -col_na <- function() style_na("?") +col_na <- function(width = 1L) { + width <- pmax(1, width - 1) + + style_na(paste0(strrep(" ", width), "?")) +} diff --git a/man/colformat.Rd b/man/colformat.Rd index d9626dd38..d05e61375 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -3,6 +3,7 @@ \name{colformat} \alias{colformat} \alias{colformat.logical} +\alias{colformat.numeric} \alias{colformat.Date} \alias{colformat.POSIXct} \alias{colformat.character} @@ -12,6 +13,8 @@ colformat(x, ...) \method{colformat}{logical}(x, ...) +\method{colformat}{numeric}(x, ..., sigfig = 3) + \method{colformat}{Date}(x, ...) \method{colformat}{POSIXct}(x, ...) @@ -23,6 +26,10 @@ colformat(x, ...) \item{...}{Other arguments passed to methods} +\item{sigfig}{Minimum number of significant figures to display. Numbers +larger than 1 will potentially show more signficiant figures than this +but they will be greyed out.} + \item{width}{Preferred width of output} } \value{ @@ -33,6 +40,12 @@ A character vector with class `colformat` and Format a vector suitable for tabular display } \examples{ +x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +colformat(x) +colformat(-x) +colformat(runif(10)) +colformat(rcauchy(10)) +colformat(c(1, 0.5, 1e-10, NA, NaN, Inf, -Inf)) x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) colformat(x) diff --git a/man/decimal_format.Rd b/man/decimal_format.Rd new file mode 100644 index 000000000..6e09cd28a --- /dev/null +++ b/man/decimal_format.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sigfig.R +\name{decimal_format} +\alias{decimal_format} +\title{Create a decimal table} +\usage{ +decimal_format(x, sigfig = 3) +} +\value{ +A tibble with four columns: +* `neg`: negative sign or space, if needed +* `lhs`: whole number +* `dec`: decimal point, if needed +* `rhs`: remainder of number +} +\description{ +This is a key building block of number formatting. It carefully uses +colour and alignment to make it as easy as possible to compare numbers. +} +\examples{ +x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +decimal_format(x, 3) + +x <- sample(x) * sample(c(-1, 1), length(x), rep = TRUE) +decimal_format(x, 3) + +decimal_format(c(Inf, -Inf, NA, NaN), 3) +decimal_format(c(1e10, 1e-10), 3) +} +\keyword{internal} From 5a36c29cc685735dd723b76e2747a04f0ea9e673 Mon Sep 17 00:00:00 2001 From: hadley Date: Tue, 16 May 2017 16:54:37 -0500 Subject: [PATCH 008/133] Add tests and fix rounding bug --- DESCRIPTION | 1 + R/sigfig.R | 2 +- tests/testthat.R | 4 ++++ tests/testthat/test-decimal_format.R | 26 ++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/testthat.R create mode 100644 tests/testthat/test-decimal_format.R diff --git a/DESCRIPTION b/DESCRIPTION index cd5018371..8ebed3b11 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,3 +15,4 @@ BugReports: https://github.com/hadley/colformat/issues Imports: crayon RoxygenNote: 6.0.1 +Suggests: testthat diff --git a/R/sigfig.R b/R/sigfig.R index 4df4f5855..486ff7d16 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -46,7 +46,7 @@ decimal_format <- function(x, sigfig = 3) { num <- is.finite(x) - lhs <- round(abs_x, 0) + lhs <- trunc(abs_x) lhs_str <- sprintf("%.0f", lhs) lhs_width <- max(nchar(lhs_str)) lhs_sig <- substr(lhs_str, 1, sigfig) diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 000000000..d258b07b6 --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,4 @@ +library(testthat) +library(colformat) + +test_check("colformat") diff --git a/tests/testthat/test-decimal_format.R b/tests/testthat/test-decimal_format.R new file mode 100644 index 000000000..0ff9983a5 --- /dev/null +++ b/tests/testthat/test-decimal_format.R @@ -0,0 +1,26 @@ +context("decimal_format") + +decimal_bw <- function(x, ...) { + old <- options(crayon.enabled = FALSE) + on.exit(options(old)) + + decimal_format(x, ...) +} + +test_that("special values appear in LHS", { + x <- c(NA, NaN, Inf) + f <- decimal_bw(x) + + expect_equal(f$lhs, format(x)) +}) + +test_that("negative values get - in neg", { + f <- decimal_bw(c(-Inf, Inf)) + expect_equal(f$neg, c("-", " ")) +}) + +test_that("trailing zeros pad to sigfigs", { + f <- decimal_bw(c(1.5, 0.5)) + expect_equal(f$lhs, c("1", "0")) + expect_equal(f$rhs, c("50", "500")) +}) From 6a9523b3fdac298dc7976e9ac4532ad202ca515f Mon Sep 17 00:00:00 2001 From: hadley Date: Tue, 16 May 2017 17:06:22 -0500 Subject: [PATCH 009/133] Ensure we always get right number of sigfigs --- R/sigfig.R | 13 +++++++++---- tests/testthat/test-decimal_format.R | 8 ++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/R/sigfig.R b/R/sigfig.R index 486ff7d16..99b0866bf 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -34,9 +34,10 @@ decimal_format <- function(x, sigfig = 3) { # Otherwise ensure we have sigfig digits shown exp <- floor(log10(abs_x)) digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) + rhs_digits <- pmax(digits - pmax(exp, 0), 0) + dec <- !is.na(digits) & rhs_digits > 0 - # Compute column components in natural type then as fixed width strings - + # Do we need negative signs? neg <- !is.na(x) & x < 0 if (any(neg)) { neg_col <- ifelse(neg, style_neg("-"), " ") @@ -44,6 +45,7 @@ decimal_format <- function(x, sigfig = 3) { neg_col <- rep("", n) } + # Digits on LHS of . num <- is.finite(x) lhs <- trunc(abs_x) @@ -56,16 +58,19 @@ decimal_format <- function(x, sigfig = 3) { paste0(ifelse(neg & lhs != 0, style_neg(lhs_sig), lhs_sig), style_subtle(lhs_non)), style_na(lhs_str) ) + lhs_col[!dec && lhs == 0] <- "" + lhs_col <- crayon::col_align(lhs_col, width = lhs_width, align = "right") - dec <- !is.na(digits) & digits > 0 + # Decimal column if (any(dec)) { dec_col <- ifelse(dec, ".", " ") } else { dec_col <- rep("", n) } - rhs_num <- as.character(abs(round((abs_x - lhs) * 10 ^ digits))) + # Digits on RHS of . + rhs_num <- as.character(abs(round((abs_x - lhs) * 10 ^ rhs_digits))) rhs_zero <- strrep("0", pmax(0, digits - sigfig)) rhs_col <- ifelse(dec, diff --git a/tests/testthat/test-decimal_format.R b/tests/testthat/test-decimal_format.R index 0ff9983a5..a764b5ecc 100644 --- a/tests/testthat/test-decimal_format.R +++ b/tests/testthat/test-decimal_format.R @@ -24,3 +24,11 @@ test_that("trailing zeros pad to sigfigs", { expect_equal(f$lhs, c("1", "0")) expect_equal(f$rhs, c("50", "500")) }) + +test_that("sigfigs split between lhs and rhs", { + x <- c(1.50, 10.50, 100.50) + f <- decimal_bw(x) + + expect_equal(f$lhs, format(trunc(x))) + expect_equal(f$rhs, c("50", "5", "")) +}) From 9d9ce080100788a9667064ad0ee8280899f68c4e Mon Sep 17 00:00:00 2001 From: hadley Date: Tue, 16 May 2017 17:08:23 -0500 Subject: [PATCH 010/133] Don't colour - --- R/sigfig.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/sigfig.R b/R/sigfig.R index 99b0866bf..407fa3e7d 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -40,7 +40,7 @@ decimal_format <- function(x, sigfig = 3) { # Do we need negative signs? neg <- !is.na(x) & x < 0 if (any(neg)) { - neg_col <- ifelse(neg, style_neg("-"), " ") + neg_col <- ifelse(neg, "-", " ") } else { neg_col <- rep("", n) } From 49ef9e6fd3126739d1109ffbf9cc0287b64bc5dd Mon Sep 17 00:00:00 2001 From: hadley Date: Tue, 16 May 2017 17:41:45 -0500 Subject: [PATCH 011/133] Improve rounding & add missing zeros --- R/sigfig.R | 27 ++++++++++++++++++++------- tests/testthat/test-decimal_format.R | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/R/sigfig.R b/R/sigfig.R index 407fa3e7d..2a27795e2 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -29,11 +29,13 @@ decimal_format <- function(x, sigfig = 3) { n <- length(x) abs_x <- abs(x) + round_x <- signif(abs_x, sigfig) # If already bigger than sigfig, can round to zero. # Otherwise ensure we have sigfig digits shown exp <- floor(log10(abs_x)) digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) + rhs_digits <- pmax(digits - pmax(exp, 0), 0) dec <- !is.na(digits) & rhs_digits > 0 @@ -48,33 +50,41 @@ decimal_format <- function(x, sigfig = 3) { # Digits on LHS of . num <- is.finite(x) - lhs <- trunc(abs_x) + lhs <- trunc(round_x) + lhs_zero <- lhs == 0 lhs_str <- sprintf("%.0f", lhs) lhs_width <- max(nchar(lhs_str)) lhs_sig <- substr(lhs_str, 1, sigfig) lhs_non <- substr(lhs_str, sigfig + 1, nchar(lhs_str)) lhs_col <- ifelse(num, - paste0(ifelse(neg & lhs != 0, style_neg(lhs_sig), lhs_sig), style_subtle(lhs_non)), + paste0( + ifelse(neg & lhs_zero, style_subtle(lhs_sig), style_num(lhs_sig, neg)), + style_subtle(lhs_non) + ), style_na(lhs_str) ) - lhs_col[!dec && lhs == 0] <- "" lhs_col <- crayon::col_align(lhs_col, width = lhs_width, align = "right") # Decimal column if (any(dec)) { - dec_col <- ifelse(dec, ".", " ") + dec_col <- ifelse(dec, style_num(".", neg & !lhs_zero), " ") } else { dec_col <- rep("", n) } # Digits on RHS of . - rhs_num <- as.character(abs(round((abs_x - lhs) * 10 ^ rhs_digits))) - rhs_zero <- strrep("0", pmax(0, digits - sigfig)) + rhs <- round_x - lhs + + rhs_num <- as.character(abs(round(rhs * 10 ^ rhs_digits))) + rhs_zero <- strrep("0", pmax(0, rhs_digits - nchar(rhs_num))) rhs_col <- ifelse(dec, - paste0(style_subtle(rhs_zero), ifelse(neg, style_neg(rhs_num), rhs_num)), + paste0( + ifelse(lhs_zero, style_subtle(rhs_zero), style_num(rhs_zero, neg)), + style_num(rhs_num, neg) + ), "" ) @@ -89,6 +99,9 @@ decimal_format <- function(x, sigfig = 3) { ) } +style_num <- function(x, negative) { + ifelse(negative, style_neg(x), x) +} #' @export format.decimal_format <- function(x, title = "title", ...) { diff --git a/tests/testthat/test-decimal_format.R b/tests/testthat/test-decimal_format.R index a764b5ecc..a3d2551cf 100644 --- a/tests/testthat/test-decimal_format.R +++ b/tests/testthat/test-decimal_format.R @@ -32,3 +32,17 @@ test_that("sigfigs split between lhs and rhs", { expect_equal(f$lhs, format(trunc(x))) expect_equal(f$rhs, c("50", "5", "")) }) + +test_that("leading 0 added to rhs", { + f <- decimal_bw(1.01) + + expect_equal(f$lhs, "1") + expect_equal(f$rhs, "01") +}) + +test_that("values rounded up as expect", { + f <- decimal_bw(c(18.9, 18.99)) + + expect_equal(f$lhs, c("18", "19")) + expect_equal(f$rhs, c("9", "0")) +}) From 5cd3f02b15f232628c1e3de8a72f4eba3325f37d Mon Sep 17 00:00:00 2001 From: hadley Date: Tue, 16 May 2017 17:55:43 -0500 Subject: [PATCH 012/133] NA style tweaks --- R/colformat.R | 2 +- R/sigfig.R | 2 +- R/styles.R | 6 +++--- man/colformat.Rd | 2 +- man/decimal_format.Rd | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/R/colformat.R b/R/colformat.R index 36958fcde..eb1c67d9b 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -10,7 +10,7 @@ #' colformat(x) #' colformat(-x) #' colformat(runif(10)) -#' colformat(rcauchy(10)) +#' colformat(rcauchy(20)) #' colformat(c(1, 0.5, 1e-10, NA, NaN, Inf, -Inf)) #' #' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) diff --git a/R/sigfig.R b/R/sigfig.R index 2a27795e2..924cbc959 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -15,7 +15,7 @@ #' x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) #' decimal_format(x, 3) #' -#' x <- sample(x) * sample(c(-1, 1), length(x), rep = TRUE) +#' x <- x * sample(c(-1, 1), length(x), rep = TRUE) #' decimal_format(x, 3) #' #' decimal_format(c(Inf, -Inf, NA, NaN), 3) diff --git a/R/styles.R b/R/styles.R index 4a0083329..f7e3bd3c0 100644 --- a/R/styles.R +++ b/R/styles.R @@ -7,7 +7,7 @@ style_subtle <- function(...) { } style_na <- function(x) { - crayon::bold(crayon::style(x, grDevices::rgb(5, 2, 0, maxColorValue = 5))) + crayon::style(x, bg = grDevices::rgb(5, 5, 2, maxColorValue = 5)) } style_neg <- function(x) { @@ -23,7 +23,7 @@ style_grey <- function(level, ...) { } col_na <- function(width = 1L) { - width <- pmax(1, width - 1) + width <- pmax(0, width - 1) - style_na(paste0(strrep(" ", width), "?")) + paste0(strrep(" ", width), style_na("?")) } diff --git a/man/colformat.Rd b/man/colformat.Rd index d05e61375..c79ecd6bd 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -44,7 +44,7 @@ x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) colformat(x) colformat(-x) colformat(runif(10)) -colformat(rcauchy(10)) +colformat(rcauchy(20)) colformat(c(1, 0.5, 1e-10, NA, NaN, Inf, -Inf)) x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) diff --git a/man/decimal_format.Rd b/man/decimal_format.Rd index 6e09cd28a..0d988e290 100644 --- a/man/decimal_format.Rd +++ b/man/decimal_format.Rd @@ -21,7 +21,7 @@ colour and alignment to make it as easy as possible to compare numbers. x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) decimal_format(x, 3) -x <- sample(x) * sample(c(-1, 1), length(x), rep = TRUE) +x <- x * sample(c(-1, 1), length(x), rep = TRUE) decimal_format(x, 3) decimal_format(c(Inf, -Inf, NA, NaN), 3) From c2b780c5f6433fb95bbd51391563b163116f5f62 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 07:54:38 -0500 Subject: [PATCH 013/133] Reset crayon colour detecting on attach (To work around bug on Hadley's computer) --- R/zzz.R | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 R/zzz.R diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 000000000..b01fdbbf5 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,3 @@ +.onAttach <- function(...) { + crayon::num_colors(TRUE) +} From db2873872be855f9d366651d19c2264a2cae5df2 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 08:04:09 -0500 Subject: [PATCH 014/133] Ensure rhs is fixed width --- R/sigfig.R | 2 ++ tests/testthat/test-decimal_format.R | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/R/sigfig.R b/R/sigfig.R index 924cbc959..52859297a 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -87,6 +87,8 @@ decimal_format <- function(x, sigfig = 3) { ), "" ) + # ensure all same width + rhs_col <- crayon::col_align(rhs_col, max(rhs_digits, na.rm = TRUE), "left") structure( list( diff --git a/tests/testthat/test-decimal_format.R b/tests/testthat/test-decimal_format.R index a3d2551cf..a65456967 100644 --- a/tests/testthat/test-decimal_format.R +++ b/tests/testthat/test-decimal_format.R @@ -22,7 +22,7 @@ test_that("negative values get - in neg", { test_that("trailing zeros pad to sigfigs", { f <- decimal_bw(c(1.5, 0.5)) expect_equal(f$lhs, c("1", "0")) - expect_equal(f$rhs, c("50", "500")) + expect_equal(f$rhs, c("50 ", "500")) }) test_that("sigfigs split between lhs and rhs", { @@ -30,7 +30,7 @@ test_that("sigfigs split between lhs and rhs", { f <- decimal_bw(x) expect_equal(f$lhs, format(trunc(x))) - expect_equal(f$rhs, c("50", "5", "")) + expect_equal(f$rhs, c("50", "5 ", " ")) }) test_that("leading 0 added to rhs", { From f6128a33f813eab225852c52ee09c81e41711edb Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 08:06:46 -0500 Subject: [PATCH 015/133] Extract out common printing code --- R/colformat.R | 11 ++--------- R/column.R | 17 +++++++++++++++++ R/sigfig.R | 15 ++------------- 3 files changed, 21 insertions(+), 22 deletions(-) create mode 100644 R/column.R diff --git a/R/colformat.R b/R/colformat.R index eb1c67d9b..c78929f8d 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -33,19 +33,12 @@ format.colformat <- function(x, title = "title", ...) { align <- attr(x, "align") width <- max(nchar(title), attr(x, "width")) - structure( - col_align(x, width = width, align = align), - title = col_align(title, width = width, align = align), - width = width - ) + new_column(x, title = title, width = width, align = attr(x, "align")) } #' @export print.colformat <- function(x, title = "title", ...) { - x <- format(x, title = title, ...) - - cat_line(crayon::bold(attr(x, "title"))) - cat_line(paste(x, collapse = "\n")) + print(format(x, title = title, ...)) } new_colformat <- function(x, width, align = "left") { diff --git a/R/column.R b/R/column.R new file mode 100644 index 000000000..b19415f31 --- /dev/null +++ b/R/column.R @@ -0,0 +1,17 @@ +new_column <- function(row, title, width, align = "right") { + row <- col_align(row, width = width, align = align) + title <- col_align(title, width = width, align = align) + + structure( + row, + title = title, + width = width, + class = "column" + ) +} + +#' @export +print.column <- function(x, ...) { + cat_line(crayon::bold(attr(x, "title"))) + cat_line(paste(x, collapse = "\n")) +} diff --git a/R/sigfig.R b/R/sigfig.R index 52859297a..61cae6b85 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -107,24 +107,13 @@ style_num <- function(x, negative) { #' @export format.decimal_format <- function(x, title = "title", ...) { - row <- paste0(x$neg, x$lhs, x$dec, x$rhs) width <- max(nchar(title), crayon::col_nchar(row)) - structure( - row, - title = col_align(title, width = width, align = "right"), - width = width - ) + new_column(row, title = title, width = width, align = "right") } #' @export print.decimal_format <- function(x, title = "title", ...) { - align <- attr(x, "align") - width <- max(nchar(title), attr(x, "width")) - - x <- format(x, title = title, ...) - - cat_line(crayon::bold(attr(x, "title"))) - cat_line(paste(x, collapse = "\n")) + print(format(x, title = title, ...)) } From 8158253adf4ceb97fe5a19b229b0d15ba28d5017 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 08:34:36 -0500 Subject: [PATCH 016/133] Make subtle style a little subtler --- R/styles.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/styles.R b/R/styles.R index f7e3bd3c0..6b86c1128 100644 --- a/R/styles.R +++ b/R/styles.R @@ -3,7 +3,7 @@ style_accent <- function(x) { } style_subtle <- function(...) { - style_grey(0.5, ...) + style_grey(0.6, ...) } style_na <- function(x) { From a50560c233db49af2f7d08c4a407dabcf979f5ff Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 09:03:35 -0500 Subject: [PATCH 017/133] Guess width if not supplied --- R/colformat.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/colformat.R b/R/colformat.R index c78929f8d..09c51a61c 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -41,7 +41,7 @@ print.colformat <- function(x, title = "title", ...) { print(format(x, title = title, ...)) } -new_colformat <- function(x, width, align = "left") { +new_colformat <- function(x, width = max(crayon::col_nchar(x)), align = "left") { structure( x, width = width, From 05ac970159acecd69c713c3167332da9e9d87ac9 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 09:08:07 -0500 Subject: [PATCH 018/133] Scientific formatting --- NAMESPACE | 2 ++ R/scientific.R | 60 +++++++++++++++++++++++++++++++++++++++++ man/format_scentific.Rd | 21 +++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 R/scientific.R create mode 100644 man/format_scentific.Rd diff --git a/NAMESPACE b/NAMESPACE index f7dd3f865..dc58be194 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,6 +8,8 @@ S3method(colformat,numeric) S3method(format,colformat) S3method(format,decimal_format) S3method(print,colformat) +S3method(print,column) S3method(print,decimal_format) export(colformat) export(decimal_format) +export(format_scentific) diff --git a/R/scientific.R b/R/scientific.R new file mode 100644 index 000000000..818eed019 --- /dev/null +++ b/R/scientific.R @@ -0,0 +1,60 @@ +#' Format numbers in scientific notation +#' +#' Uses colour, careful alignment, and superscripts to display numbers +#' in scientific notation. +#' +#' @param x A numeric vector +#' @param sigfig Number of signficiant figures to display. +#' @export +#' @examples +#' x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) +#' format_scentific(x) +format_scentific <- function(x, sigfig = 3) { + stopifnot(is.numeric(x)) + + n <- length(x) + abs_x <- abs(x) + round_x <- signif(abs_x, sigfig) + num <- is.finite(x) + abs_x[!num] <- NA # supress warning from log10 + + # Compute exponent and mantissa + exp <- as.integer(log10(abs_x)) + exp_chr <- ifelse(num, supernum(exp), "") + + mnt <- round_x * 10 ^ (-exp) + mnt_chr <- ifelse(num, + format(mnt, digits = sigfig - 1), + crayon::col_align(style_na(as.character(round_x)), sigfig + 1, "right") + ) + + new_colformat(paste0(mnt_chr, exp_chr)) +} + +supernum <- function(x) { + stopifnot(is.integer(x)) + + if (any(x < 0)) { + neg <- ifelse(x < 0, "\u207b", "\u207a") + } else { + neg <- rep("", length(x)) + } + + digits <- vapply(abs(x), supernum1, character(1)) + exp <- paste0(neg, format(digits, justify = "right")) + + paste0(style_subtle("e"), style_num(exp, x < 0)) +} + +supernum1 <- function(x) { + chars <- strsplit(as.character(x), "")[[1]] + + # super <- c("⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹") + super <- c( + "\u2070", "\u00b9", "\u00b2", "\u00b3", "\u2074", + "\u2075", "\u2076", "\u2077", "\u2078", "\u2079" + ) + names(super) <- 0:9 + + paste0(super[chars], collapse = "") +} diff --git a/man/format_scentific.Rd b/man/format_scentific.Rd new file mode 100644 index 000000000..a46e7dc6b --- /dev/null +++ b/man/format_scentific.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/scientific.R +\name{format_scentific} +\alias{format_scentific} +\title{Format numbers in scientific notation} +\usage{ +format_scentific(x, sigfig = 3) +} +\arguments{ +\item{x}{A numeric vector} + +\item{sigfig}{Number of signficiant figures} +} +\description{ +Uses colour, careful alignment, and superscripts to display numbers +in scientific notation. +} +\examples{ +x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) +format_scentific(x) +} From edca2ad43b7389d1ad0b5921146980a44f49bf83 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 09:21:08 -0500 Subject: [PATCH 019/133] Make format_decimal more like format_scientific --- NAMESPACE | 2 +- R/colformat.R | 2 +- R/scientific.R | 4 +-- R/sigfig.R | 28 +++++++-------- R/utils.R | 10 ++++++ man/decimal_format.Rd | 30 ---------------- man/format_decimal.Rd | 36 +++++++++++++++++++ man/format_scentific.Rd | 2 +- ...decimal_format.R => test-format_decimal.R} | 18 +++++----- 9 files changed, 74 insertions(+), 58 deletions(-) delete mode 100644 man/decimal_format.Rd create mode 100644 man/format_decimal.Rd rename tests/testthat/{test-decimal_format.R => test-format_decimal.R} (72%) diff --git a/NAMESPACE b/NAMESPACE index dc58be194..081808d9d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,5 +11,5 @@ S3method(print,colformat) S3method(print,column) S3method(print,decimal_format) export(colformat) -export(decimal_format) +export(format_decimal) export(format_scentific) diff --git a/R/colformat.R b/R/colformat.R index 09c51a61c..aa1a1125b 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -69,7 +69,7 @@ colformat.logical <- function(x, ...) { #' larger than 1 will potentially show more signficiant figures than this #' but they will be greyed out. colformat.numeric <- function(x, ..., sigfig = 3) { - decimal_format(x, sigfig = sigfig) + format_decimal(x, sigfig = sigfig) } #' @export diff --git a/R/scientific.R b/R/scientific.R index 818eed019..76c7b51fa 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -3,14 +3,14 @@ #' Uses colour, careful alignment, and superscripts to display numbers #' in scientific notation. #' -#' @param x A numeric vector -#' @param sigfig Number of signficiant figures to display. +#' @inheritParams format_decimal #' @export #' @examples #' x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) #' format_scentific(x) format_scentific <- function(x, sigfig = 3) { stopifnot(is.numeric(x)) + sigfig <- check_sigfig(sigfig) n <- length(x) abs_x <- abs(x) diff --git a/R/sigfig.R b/R/sigfig.R index 61cae6b85..6146a6d89 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -1,7 +1,9 @@ -#' Create a decimal table +#' Format numbers in decimal notation #' -#' This is a key building block of number formatting. It carefully uses -#' colour and alignment to make it as easy as possible to compare numbers. +#' This formatting system is designed to make it as easy as possible to +#' compare columns of numbers. Significant digits are coloured black or red +#' (for positive and negative numbers) and non-significant digits are coloured +#' in paler gray. #' #' @return A tibble with four columns: #' * `neg`: negative sign or space, if needed @@ -9,23 +11,21 @@ #' * `dec`: decimal point, if needed #' * `rhs`: remainder of number #' -#' @keywords internal +#' @param x A numeric vector +#' @param sigfig Number of signficiant figures to display. #' @export #' @examples #' x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) -#' decimal_format(x, 3) +#' format_decimal(x, 3) #' #' x <- x * sample(c(-1, 1), length(x), rep = TRUE) -#' decimal_format(x, 3) +#' format_decimal(x, 3) #' -#' decimal_format(c(Inf, -Inf, NA, NaN), 3) -#' decimal_format(c(1e10, 1e-10), 3) -decimal_format <- function(x, sigfig = 3) { - stopifnot(is.numeric(sigfig), length(sigfig) == 1) - sigfig <- as.integer(sigfig) - if (sigfig < 1L) { - stop("Must show at least one significant figure", call. = FALSE) - } +#' format_decimal(c(Inf, -Inf, NA, NaN), 3) +#' format_decimal(c(1e10, 1e-10), 3) +format_decimal <- function(x, sigfig = 3) { + stopifnot(is.numeric(x)) + sigfig <- check_sigfig(sigfig) n <- length(x) abs_x <- abs(x) diff --git a/R/utils.R b/R/utils.R index 9ab61a210..a15df046c 100644 --- a/R/utils.R +++ b/R/utils.R @@ -15,3 +15,13 @@ str_trunc <- function(x, width = 20) { x } + +check_sigfig <- function(x) { + stopifnot(is.numeric(x), length(x) == 1) + x <- as.integer(x) + if (x < 1L) { + stop("Must show at least one significant figure", call. = FALSE) + } + + x +} diff --git a/man/decimal_format.Rd b/man/decimal_format.Rd deleted file mode 100644 index 0d988e290..000000000 --- a/man/decimal_format.Rd +++ /dev/null @@ -1,30 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/sigfig.R -\name{decimal_format} -\alias{decimal_format} -\title{Create a decimal table} -\usage{ -decimal_format(x, sigfig = 3) -} -\value{ -A tibble with four columns: -* `neg`: negative sign or space, if needed -* `lhs`: whole number -* `dec`: decimal point, if needed -* `rhs`: remainder of number -} -\description{ -This is a key building block of number formatting. It carefully uses -colour and alignment to make it as easy as possible to compare numbers. -} -\examples{ -x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) -decimal_format(x, 3) - -x <- x * sample(c(-1, 1), length(x), rep = TRUE) -decimal_format(x, 3) - -decimal_format(c(Inf, -Inf, NA, NaN), 3) -decimal_format(c(1e10, 1e-10), 3) -} -\keyword{internal} diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd new file mode 100644 index 000000000..b9f60d005 --- /dev/null +++ b/man/format_decimal.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sigfig.R +\name{format_decimal} +\alias{format_decimal} +\title{Format numbers in decimal notation} +\usage{ +format_decimal(x, sigfig = 3) +} +\arguments{ +\item{x}{A numeric vector} + +\item{sigfig}{Number of signficiant figures to display.} +} +\value{ +A tibble with four columns: +* `neg`: negative sign or space, if needed +* `lhs`: whole number +* `dec`: decimal point, if needed +* `rhs`: remainder of number +} +\description{ +This formatting system is designed to make it as easy as possible to +compare columns of numbers. Significant digits are coloured black or red +(for positive and negative numbers) and non-significant digits are coloured +in paler gray. +} +\examples{ +x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +format_decimal(x, 3) + +x <- x * sample(c(-1, 1), length(x), rep = TRUE) +format_decimal(x, 3) + +format_decimal(c(Inf, -Inf, NA, NaN), 3) +format_decimal(c(1e10, 1e-10), 3) +} diff --git a/man/format_scentific.Rd b/man/format_scentific.Rd index a46e7dc6b..b7f24f302 100644 --- a/man/format_scentific.Rd +++ b/man/format_scentific.Rd @@ -9,7 +9,7 @@ format_scentific(x, sigfig = 3) \arguments{ \item{x}{A numeric vector} -\item{sigfig}{Number of signficiant figures} +\item{sigfig}{Number of signficiant figures to display.} } \description{ Uses colour, careful alignment, and superscripts to display numbers diff --git a/tests/testthat/test-decimal_format.R b/tests/testthat/test-format_decimal.R similarity index 72% rename from tests/testthat/test-decimal_format.R rename to tests/testthat/test-format_decimal.R index a65456967..105f5880f 100644 --- a/tests/testthat/test-decimal_format.R +++ b/tests/testthat/test-format_decimal.R @@ -1,47 +1,47 @@ -context("decimal_format") +context("format_decimal") -decimal_bw <- function(x, ...) { +format_decimal_bw <- function(x, ...) { old <- options(crayon.enabled = FALSE) on.exit(options(old)) - decimal_format(x, ...) + format_decimal(x, ...) } test_that("special values appear in LHS", { x <- c(NA, NaN, Inf) - f <- decimal_bw(x) + f <- format_decimal_bw(x) expect_equal(f$lhs, format(x)) }) test_that("negative values get - in neg", { - f <- decimal_bw(c(-Inf, Inf)) + f <- format_decimal_bw(c(-Inf, Inf)) expect_equal(f$neg, c("-", " ")) }) test_that("trailing zeros pad to sigfigs", { - f <- decimal_bw(c(1.5, 0.5)) + f <- format_decimal_bw(c(1.5, 0.5)) expect_equal(f$lhs, c("1", "0")) expect_equal(f$rhs, c("50 ", "500")) }) test_that("sigfigs split between lhs and rhs", { x <- c(1.50, 10.50, 100.50) - f <- decimal_bw(x) + f <- format_decimal_bw(x) expect_equal(f$lhs, format(trunc(x))) expect_equal(f$rhs, c("50", "5 ", " ")) }) test_that("leading 0 added to rhs", { - f <- decimal_bw(1.01) + f <- format_decimal_bw(1.01) expect_equal(f$lhs, "1") expect_equal(f$rhs, "01") }) test_that("values rounded up as expect", { - f <- decimal_bw(c(18.9, 18.99)) + f <- format_decimal_bw(c(18.9, 18.99)) expect_equal(f$lhs, c("18", "19")) expect_equal(f$rhs, c("9", "0")) From d1cb639c88d4734369906cc6a085539adc227880 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 09:21:16 -0500 Subject: [PATCH 020/133] Don't round on LHS --- R/sigfig.R | 2 +- tests/testthat/test-format_decimal.R | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/R/sigfig.R b/R/sigfig.R index 6146a6d89..bb837e43a 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -29,11 +29,11 @@ format_decimal <- function(x, sigfig = 3) { n <- length(x) abs_x <- abs(x) - round_x <- signif(abs_x, sigfig) # If already bigger than sigfig, can round to zero. # Otherwise ensure we have sigfig digits shown exp <- floor(log10(abs_x)) + round_x <- signif(abs_x, pmax(sigfig, exp + 1)) digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) rhs_digits <- pmax(digits - pmax(exp, 0), 0) diff --git a/tests/testthat/test-format_decimal.R b/tests/testthat/test-format_decimal.R index 105f5880f..c9fa8b899 100644 --- a/tests/testthat/test-format_decimal.R +++ b/tests/testthat/test-format_decimal.R @@ -46,3 +46,8 @@ test_that("values rounded up as expect", { expect_equal(f$lhs, c("18", "19")) expect_equal(f$rhs, c("9", "0")) }) + +test_that("values on LHS not rounded", { + f <- format_decimal_bw(123456) + expect_equal(f$lhs, "123456") +}) From 8333309b16c83bead757626830df0eb899059612 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 09:23:54 -0500 Subject: [PATCH 021/133] Remove missings --- R/scientific.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/scientific.R b/R/scientific.R index 76c7b51fa..981e061d6 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -34,7 +34,7 @@ format_scentific <- function(x, sigfig = 3) { supernum <- function(x) { stopifnot(is.integer(x)) - if (any(x < 0)) { + if (any(x < 0, na.rm = TRUE)) { neg <- ifelse(x < 0, "\u207b", "\u207a") } else { neg <- rep("", length(x)) From 2feb784857b2b4aa98ceeea3534a08dfa9d474f8 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 09:41:01 -0500 Subject: [PATCH 022/133] Sparkline bar graphs --- NAMESPACE | 2 ++ R/spark-bar.R | 32 ++++++++++++++++++++++++++++++++ R/utils.R | 12 ++++++++++++ man/spark_bar.Rd | 21 +++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 R/spark-bar.R create mode 100644 man/spark_bar.Rd diff --git a/NAMESPACE b/NAMESPACE index 081808d9d..897d9ae4c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,6 +10,8 @@ S3method(format,decimal_format) S3method(print,colformat) S3method(print,column) S3method(print,decimal_format) +S3method(print,spark) export(colformat) export(format_decimal) export(format_scentific) +export(spark_bar) diff --git a/R/spark-bar.R b/R/spark-bar.R new file mode 100644 index 000000000..f360c640b --- /dev/null +++ b/R/spark-bar.R @@ -0,0 +1,32 @@ +#' Draw a sparkline bar graph +#' +#' @export +#' @param x A numeric vector between 0 and 1 +#' @export +#' @examples +#' x <- seq(0, 1, length = 15) +#' spark_bar(x) +#' spark_bar(sample(x)) +#' +#' spark_bar(c(0, NA, 0.5, NA, 1)) +spark_bar <- function(x) { + stopifnot(is.numeric(x)) + + bars <- c("\u2581", "\u2582", "\u2583", "\u2585", "\u2587") + + factor <- cut( + x, + breaks = seq(0, 1, length = length(bars) + 1), + labels = bars, + include.lowest = TRUE + ) + chars <- as.character(factor) + chars[is.na(chars)] <- style_na(" ") + + structure(paste0(chars, collapse = ""), class = "spark") +} + +#' @export +print.spark <- function(x, ...) { + cat(x, "\n", sep = "") +} diff --git a/R/utils.R b/R/utils.R index a15df046c..1a5e482c9 100644 --- a/R/utils.R +++ b/R/utils.R @@ -25,3 +25,15 @@ check_sigfig <- function(x) { x } + +ruler <- function(width = getOption("width")) { + x <- seq_len(width) + y <- rep("-", length(x)) + + y[x %% 10 == 0] <- as.character((x[x %% 10 == 0] %/% 10) %% 10) + y[x %% 5 == 0] <- "+" + + cat(y, "\n", sep = "") + cat(x %% 10, "\n", sep = "") +} + diff --git a/man/spark_bar.Rd b/man/spark_bar.Rd new file mode 100644 index 000000000..ef990fa85 --- /dev/null +++ b/man/spark_bar.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spark-bar.R +\name{spark_bar} +\alias{spark_bar} +\title{Draw a sparkline bar graph} +\usage{ +spark_bar(x) +} +\arguments{ +\item{x}{A numeric vector between 0 and 1} +} +\description{ +Draw a sparkline bar graph +} +\examples{ +x <- seq(0, 1, length = 15) +spark_bar(x) +spark_bar(sample(x)) + +spark_bar(c(0, NA, 0.5, NA, 1)) +} From 12e2a7f8fecf15767507bce7a5bd76b75a1a263a Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 10:05:31 -0500 Subject: [PATCH 023/133] Implement spark_line with Braille chars --- NAMESPACE | 1 + R/spark-bar.R | 2 +- R/spark-line.R | 37 +++++++++++++++++++++++++++++++++++++ man/spark_bar.Rd | 4 ++-- man/spark_line.Rd | 18 ++++++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 R/spark-line.R create mode 100644 man/spark_line.Rd diff --git a/NAMESPACE b/NAMESPACE index 897d9ae4c..7d49954e6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,3 +15,4 @@ export(colformat) export(format_decimal) export(format_scentific) export(spark_bar) +export(spark_line) diff --git a/R/spark-bar.R b/R/spark-bar.R index f360c640b..1971dc787 100644 --- a/R/spark-bar.R +++ b/R/spark-bar.R @@ -1,4 +1,4 @@ -#' Draw a sparkline bar graph +#' Draw a sparkline bar graph with unicode block characters #' #' @export #' @param x A numeric vector between 0 and 1 diff --git a/R/spark-line.R b/R/spark-line.R new file mode 100644 index 000000000..299a64a1a --- /dev/null +++ b/R/spark-line.R @@ -0,0 +1,37 @@ +#' Draw a sparkline line graph with Braille characters. +#' +#' @inheritParams spark_bar +#' @export +#' @examples +#' x <- seq(0, 1, length = 10) +#' spark_line(x) +spark_line <- function(x) { + stopifnot(is.numeric(x)) + + y <- findInterval(x, seq(0, 1, length = 5), all.inside = TRUE) + + ind <- matrix(y, ncol = 2, byrow = TRUE) + ind[, 2] <- ind[, 2] + 4 + + chars <- apply(ind, 1, braille) + structure(paste0(chars, collapse = ""), class = "spark") +} + +#' @export +print.spark <- function(x, ...) { + cat(x, "\n", sep = "") +} + +# https://en.wikipedia.org/wiki/Braille_Patterns +braille <- function(x) { + # remap to braille sequence + x <- c(7L, 3L, 2L, 1L, 8L, 6L, 5L, 4L)[x] + + raised <- 1:8 %in% x + binary <- raised * 2 ^ (0:7) + + # offset in hex is 2800 + val <- 10240 + sum(raised * 2 ^ (0:7)) + + intToUtf8(val) +} diff --git a/man/spark_bar.Rd b/man/spark_bar.Rd index ef990fa85..fccbf43a6 100644 --- a/man/spark_bar.Rd +++ b/man/spark_bar.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/spark-bar.R \name{spark_bar} \alias{spark_bar} -\title{Draw a sparkline bar graph} +\title{Draw a sparkline bar graph with unicode block characters} \usage{ spark_bar(x) } @@ -10,7 +10,7 @@ spark_bar(x) \item{x}{A numeric vector between 0 and 1} } \description{ -Draw a sparkline bar graph +Draw a sparkline bar graph with unicode block characters } \examples{ x <- seq(0, 1, length = 15) diff --git a/man/spark_line.Rd b/man/spark_line.Rd new file mode 100644 index 000000000..ab2f45abf --- /dev/null +++ b/man/spark_line.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spark-line.R +\name{spark_line} +\alias{spark_line} +\title{Draw a sparkline line graph with Braille characters.} +\usage{ +spark_line(x) +} +\arguments{ +\item{x}{A numeric vector between 0 and 1} +} +\description{ +Draw a sparkline line graph with Braille characters. +} +\examples{ +x <- seq(0, 1, length = 10) +spark_line(x) +} From cac452104717e2902136ad6a0fa27691e380b03d Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 10:12:56 -0500 Subject: [PATCH 024/133] Add note about box elements --- R/spark-bar.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/R/spark-bar.R b/R/spark-bar.R index 1971dc787..2583db291 100644 --- a/R/spark-bar.R +++ b/R/spark-bar.R @@ -12,6 +12,9 @@ spark_bar <- function(x) { stopifnot(is.numeric(x)) + # Full set has too many rendering problems on common fonts + # https://en.wikipedia.org/wiki/Block_Elements + # vapply(0x2581:0x2588, intToUtf8, character(1)) bars <- c("\u2581", "\u2582", "\u2583", "\u2585", "\u2587") factor <- cut( From 94e795d34a5b0fa363ec2bdc19dd92f56d88c17d Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 10:20:52 -0500 Subject: [PATCH 025/133] Add readme --- .Rbuildignore | 2 ++ README.Rmd | 46 ++++++++++++++++++++++++++++++++++++++++ README.md | 43 +++++++++++++++++++++++++++++++++++++ man/figures/colours.png | Bin 0 -> 27470 bytes 4 files changed, 91 insertions(+) create mode 100644 README.Rmd create mode 100644 README.md create mode 100644 man/figures/colours.png diff --git a/.Rbuildignore b/.Rbuildignore index 91114bf2f..8453a6f6c 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,2 +1,4 @@ ^.*\.Rproj$ ^\.Rproj\.user$ +^README\.Rmd$ +^README-.*\.png$ diff --git a/README.Rmd b/README.Rmd new file mode 100644 index 000000000..08b286cef --- /dev/null +++ b/README.Rmd @@ -0,0 +1,46 @@ +--- +output: github_document +--- + + + +```{r, echo = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + fig.path = "README-" +) +``` + +# colformat + +colformat provides tools for styling columns of data, artfully using colour and unicode characters to + +## Installation + +```{r, eval = FALSE} +# install.packages("devtools") +devtools::install_github("hadley/colformat") +``` + +## Usage + +colformat is not designed for end-users but will eventually be incorporated in packages like [tibble](http://tibble.tidyverse.org). + +```{r} +library(colformat) + +x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +colformat(x) +``` + +If you render this in a console that supports colour, you'll see something that looks like this: + +```{r, echo = FALSE, out.width = "200px"} +knitr::include_graphics("man/figures/colours.png") +``` + + +## Related work + +* [TextPlots](https://github.com/sunetos/TextPlots.jl) diff --git a/README.md b/README.md new file mode 100644 index 000000000..dd3d4fc28 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ + + +colformat +========= + +colformat provides tools for styling columns of data, artfully using colour and unicode characters to + +Installation +------------ + +``` r +# install.packages("devtools") +devtools::install_github("hadley/colformat") +``` + +Usage +----- + +colformat is not designed for end-users but will eventually be incorporated in packages like [tibble](http://tibble.tidyverse.org). + +``` r +library(colformat) + +x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +colformat(x) +#> title +#> 1234567890 +#> 123457 +#> 1235 +#> NA +#> 1.23 +#> 0.0123 +#> 0.000000123 +``` + +If you render this in a console that supports colour, you'll see something that looks like this: + + + +Related work +------------ + +- [TextPlots](https://github.com/sunetos/TextPlots.jl) diff --git a/man/figures/colours.png b/man/figures/colours.png new file mode 100644 index 0000000000000000000000000000000000000000..fbea6012bcec55f52cc159c85d2dd9ab28c37bad GIT binary patch literal 27470 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 Date: Wed, 17 May 2017 11:40:10 -0500 Subject: [PATCH 026/133] Add inspirations to readme --- README.Rmd | 7 +++++-- README.md | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.Rmd b/README.Rmd index 08b286cef..b92b1ed8c 100644 --- a/README.Rmd +++ b/README.Rmd @@ -41,6 +41,9 @@ knitr::include_graphics("man/figures/colours.png") ``` -## Related work +## Inspirations -* [TextPlots](https://github.com/sunetos/TextPlots.jl) +* [TextPlots](https://github.com/sunetos/TextPlots.jl) for use of Braille + characters + +* [spark](https://github.com/holman/spark) for use of block characters. diff --git a/README.md b/README.md index dd3d4fc28..7de86624f 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,9 @@ If you render this in a console that supports colour, you'll see something that -Related work +Inspirations ------------ -- [TextPlots](https://github.com/sunetos/TextPlots.jl) +- [TextPlots](https://github.com/sunetos/TextPlots.jl) for use of Braille characters + +- [spark](https://github.com/holman/spark) for use of block characters. From f0eb708c0f62941245fd02ad0b78c526dc429738 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 11:58:47 -0500 Subject: [PATCH 027/133] Switch to scientific if too wide --- R/colformat.R | 24 ++++++++++++++++++++---- man/colformat.Rd | 15 ++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/R/colformat.R b/R/colformat.R index aa1a1125b..ce4fc1882 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -6,12 +6,18 @@ #' `width` and `align` attributes. #' @export #' @examples -#' x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +#' x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) #' colformat(x) #' colformat(-x) #' colformat(runif(10)) #' colformat(rcauchy(20)) -#' colformat(c(1, 0.5, 1e-10, NA, NaN, Inf, -Inf)) +#' +#' # Special values are highlighted +#' colformat(c(runif(5), NA, NaN, Inf, -Inf)) +#' +#' # Very wide ranges will be displayed in scientific format +#' colformat(c(1e10, 1e-10)) +#' colformat(c(1e10, 1e-10), sci_threshold = Inf) #' #' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) #' colformat(x) @@ -68,8 +74,18 @@ colformat.logical <- function(x, ...) { #' @param sigfig Minimum number of significant figures to display. Numbers #' larger than 1 will potentially show more signficiant figures than this #' but they will be greyed out. -colformat.numeric <- function(x, ..., sigfig = 3) { - format_decimal(x, sigfig = sigfig) +#' @param sci_threshold If decimal display is wider than this threshold, +#' use scientific display instead. +colformat.numeric <- function(x, ..., sigfig = 3, sci_threshold = 15) { + dec <- format_decimal(x, sigfig = sigfig) + + # This is somewhat inefficient but we can fix if it becomes a bottleneck + width <- attr(format(dec), "width") + if (width <= sci_threshold) { + dec + } else { + format_scentific(x, sigfig = sigfig) + } } #' @export diff --git a/man/colformat.Rd b/man/colformat.Rd index c79ecd6bd..7fdd0bdb3 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -13,7 +13,7 @@ colformat(x, ...) \method{colformat}{logical}(x, ...) -\method{colformat}{numeric}(x, ..., sigfig = 3) +\method{colformat}{numeric}(x, ..., sigfig = 3, sci_threshold = 15) \method{colformat}{Date}(x, ...) @@ -30,6 +30,9 @@ colformat(x, ...) larger than 1 will potentially show more signficiant figures than this but they will be greyed out.} +\item{sci_threshold}{If decimal display is wider than this threshold, +use scientific display instead.} + \item{width}{Preferred width of output} } \value{ @@ -40,12 +43,18 @@ A character vector with class `colformat` and Format a vector suitable for tabular display } \examples{ -x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) colformat(x) colformat(-x) colformat(runif(10)) colformat(rcauchy(20)) -colformat(c(1, 0.5, 1e-10, NA, NaN, Inf, -Inf)) + +# Special values are highlighted +colformat(c(runif(5), NA, NaN, Inf, -Inf)) + +# Very wide ranges will be displayed in scientific format +colformat(c(1e10, 1e-10)) +colformat(c(1e10, 1e-10), sci_threshold = Inf) x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) colformat(x) From d22d2a4fe665a197ac7578aeaf168fdacf73d554 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 12:07:00 -0500 Subject: [PATCH 028/133] Fix rounding in scientific format --- R/scientific.R | 24 +++++++++++++++++------- man/format_scentific.Rd | 4 +++- tests/testthat/test-format_scientific.R | 13 +++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 tests/testthat/test-format_scientific.R diff --git a/R/scientific.R b/R/scientific.R index 981e061d6..b3b8a5de0 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -4,11 +4,12 @@ #' in scientific notation. #' #' @inheritParams format_decimal +#' @param superscript If `TRUE`, will use superscript numbers in exponent. #' @export #' @examples #' x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) #' format_scentific(x) -format_scentific <- function(x, sigfig = 3) { +format_scentific <- function(x, sigfig = 3, superscript = TRUE) { stopifnot(is.numeric(x)) sigfig <- check_sigfig(sigfig) @@ -19,28 +20,37 @@ format_scentific <- function(x, sigfig = 3) { abs_x[!num] <- NA # supress warning from log10 # Compute exponent and mantissa - exp <- as.integer(log10(abs_x)) - exp_chr <- ifelse(num, supernum(exp), "") + exp <- as.integer(floor(log10(abs_x))) + exp_chr <- ifelse(num, supernum(exp, superscript = superscript), "") mnt <- round_x * 10 ^ (-exp) mnt_chr <- ifelse(num, - format(mnt, digits = sigfig - 1), + format(mnt, digits = sigfig), crayon::col_align(style_na(as.character(round_x)), sigfig + 1, "right") ) new_colformat(paste0(mnt_chr, exp_chr)) } -supernum <- function(x) { +supernum <- function(x, superscript = TRUE) { stopifnot(is.integer(x)) if (any(x < 0, na.rm = TRUE)) { - neg <- ifelse(x < 0, "\u207b", "\u207a") + if (superscript) { + neg <- ifelse(x < 0, "\u207b", "\u207a") + } else { + neg <- ifelse(x < 0, "-", "+") + } + } else { neg <- rep("", length(x)) } - digits <- vapply(abs(x), supernum1, character(1)) + if (superscript) { + digits <- vapply(abs(x), supernum1, character(1)) + } else { + digits <- as.character(abs(x)) + } exp <- paste0(neg, format(digits, justify = "right")) paste0(style_subtle("e"), style_num(exp, x < 0)) diff --git a/man/format_scentific.Rd b/man/format_scentific.Rd index b7f24f302..6d05e42c9 100644 --- a/man/format_scentific.Rd +++ b/man/format_scentific.Rd @@ -4,12 +4,14 @@ \alias{format_scentific} \title{Format numbers in scientific notation} \usage{ -format_scentific(x, sigfig = 3) +format_scentific(x, sigfig = 3, superscript = TRUE) } \arguments{ \item{x}{A numeric vector} \item{sigfig}{Number of signficiant figures to display.} + +\item{superscript}{If `TRUE`, will use superscript numbers in exponent.} } \description{ Uses colour, careful alignment, and superscripts to display numbers diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R new file mode 100644 index 000000000..0aa69af16 --- /dev/null +++ b/tests/testthat/test-format_scientific.R @@ -0,0 +1,13 @@ +context("format_scientific") + +format_scientific_bw <- function(x, ...) { + old <- options(crayon.enabled = FALSE) + on.exit(options(old)) + + format_scentific(x, ...) +} + +test_that("negative values displayed correct", { + f <- as.vector(format_scientific_bw(-0.123, superscript = FALSE)) + expect_equal(f, "1.23e-1") +}) From fa6c13c49f0b9e470f568a09fc4aacf061869116 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 14:35:50 -0500 Subject: [PATCH 029/133] More info about bar glyphs Tweaking styling of NA --- R/spark-bar.R | 24 +++++++++++++++++------- R/styles.R | 6 +++++- man/spark_bar.Rd | 15 ++++++++++++--- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/R/spark-bar.R b/R/spark-bar.R index 2583db291..86e577c05 100644 --- a/R/spark-bar.R +++ b/R/spark-bar.R @@ -1,21 +1,31 @@ #' Draw a sparkline bar graph with unicode block characters #' +#' Rendered using [block elements](https://en.wikipedia.org/wiki/Block_Elements). +#' In most common fixed width fonts these are rendered wider than regular +#' characters which means they are not suitable if you need precise alignment. +#' #' @export #' @param x A numeric vector between 0 and 1 +#' @param safe Nominally there are 8 block elements from 1/8 height to full +#' height (8/8). However, the half-height and full-height blocks appear +#' to be rendered inconsistently (possibly due to font substitution). #' @export #' @examples -#' x <- seq(0, 1, length = 15) +#' x <- seq(0, 1, length = 6) #' spark_bar(x) #' spark_bar(sample(x)) #' +#' # This might work if you're lucky +#' spark_bar(seq(0, 1, length = 8), safe = FALSE) +#' #' spark_bar(c(0, NA, 0.5, NA, 1)) -spark_bar <- function(x) { +spark_bar <- function(x, safe = TRUE) { stopifnot(is.numeric(x)) - # Full set has too many rendering problems on common fonts - # https://en.wikipedia.org/wiki/Block_Elements - # vapply(0x2581:0x2588, intToUtf8, character(1)) - bars <- c("\u2581", "\u2582", "\u2583", "\u2585", "\u2587") + bars <- vapply(0x2581:0x2588, intToUtf8, character(1)) + if (safe) { + bars <- bars[-c(4, 8)] + } factor <- cut( x, @@ -24,7 +34,7 @@ spark_bar <- function(x) { include.lowest = TRUE ) chars <- as.character(factor) - chars[is.na(chars)] <- style_na(" ") + chars[is.na(chars)] <- crayon::style(bars[length(bars)], colour_na()) structure(paste0(chars, collapse = ""), class = "spark") } diff --git a/R/styles.R b/R/styles.R index 6b86c1128..dacc33fb0 100644 --- a/R/styles.R +++ b/R/styles.R @@ -6,8 +6,12 @@ style_subtle <- function(...) { style_grey(0.6, ...) } +colour_na <- function() { + grDevices::rgb(5, 5, 2, maxColorValue = 5) +} + style_na <- function(x) { - crayon::style(x, bg = grDevices::rgb(5, 5, 2, maxColorValue = 5)) + crayon::style(x, bg = na_colour) } style_neg <- function(x) { diff --git a/man/spark_bar.Rd b/man/spark_bar.Rd index fccbf43a6..34a6ab55a 100644 --- a/man/spark_bar.Rd +++ b/man/spark_bar.Rd @@ -4,18 +4,27 @@ \alias{spark_bar} \title{Draw a sparkline bar graph with unicode block characters} \usage{ -spark_bar(x) +spark_bar(x, safe = TRUE) } \arguments{ \item{x}{A numeric vector between 0 and 1} + +\item{safe}{Nominally there are 8 block elements from 1/8 height to full +height (8/8). However, the half-height and full-height blocks appear +to be rendered inconsistently (possibly due to font substitution).} } \description{ -Draw a sparkline bar graph with unicode block characters +Rendered using [block elements](https://en.wikipedia.org/wiki/Block_Elements). +In most common fixed width fonts these are rendered wider than regular +characters which means they are not suitable if you need precise alignment. } \examples{ -x <- seq(0, 1, length = 15) +x <- seq(0, 1, length = 6) spark_bar(x) spark_bar(sample(x)) +# This might work if you're lucky +spark_bar(seq(0, 1, length = 8), safe = FALSE) + spark_bar(c(0, NA, 0.5, NA, 1)) } From fba9cf564f9443301f9ef0bd88b16ed084d33c8a Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 14:36:04 -0500 Subject: [PATCH 030/133] Some more history --- README.Rmd | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.Rmd b/README.Rmd index b92b1ed8c..23ba3677c 100644 --- a/README.Rmd +++ b/README.Rmd @@ -47,3 +47,5 @@ knitr::include_graphics("man/figures/colours.png") characters * [spark](https://github.com/holman/spark) for use of block characters. + +The earliest use of this technique that I could find is [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). diff --git a/README.md b/README.md index 7de86624f..d0bb76e65 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,5 @@ Inspirations - [TextPlots](https://github.com/sunetos/TextPlots.jl) for use of Braille characters - [spark](https://github.com/holman/spark) for use of block characters. + +The earliest use of this technique that I could find is [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). From 7ac4901caf7899630a0ba94692a95fb0c7a682b7 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 15:16:39 -0500 Subject: [PATCH 031/133] Fix typo --- R/styles.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/styles.R b/R/styles.R index dacc33fb0..7ba225c9f 100644 --- a/R/styles.R +++ b/R/styles.R @@ -11,7 +11,7 @@ colour_na <- function() { } style_na <- function(x) { - crayon::style(x, bg = na_colour) + crayon::style(x, bg = colour_na()) } style_neg <- function(x) { From c58258aa157950ab79778839092a757b32c20c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 17 May 2017 22:16:47 +0200 Subject: [PATCH 032/133] add remote --- DESCRIPTION | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 8ebed3b11..de11026d0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,3 +16,5 @@ Imports: crayon RoxygenNote: 6.0.1 Suggests: testthat +Remotes: + gaborcsardi/crayon From 454c2c2956f34fe66ba563f98a71199b5fce26ce Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 15:17:35 -0500 Subject: [PATCH 033/133] Update readme example --- README.Rmd | 2 +- README.md | 17 ++++++++--------- man/figures/colours.png | Bin 27470 -> 21671 bytes 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.Rmd b/README.Rmd index 23ba3677c..b80450413 100644 --- a/README.Rmd +++ b/README.Rmd @@ -30,7 +30,7 @@ colformat is not designed for end-users but will eventually be incorporated in p ```{r} library(colformat) -x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10)) colformat(x) ``` diff --git a/README.md b/README.md index d0bb76e65..2e3930422 100644 --- a/README.md +++ b/README.md @@ -21,16 +21,15 @@ colformat is not designed for end-users but will eventually be incorporated in p ``` r library(colformat) -x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) +x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10)) colformat(x) -#> title -#> 1234567890 -#> 123457 -#> 1235 -#> NA -#> 1.23 -#> 0.0123 -#> 0.000000123 +#> title +#> 1234567890 +#> 123457 +#> 1235 +#> NA +#> 1.23 +#> 0.0123 ``` If you render this in a console that supports colour, you'll see something that looks like this: diff --git a/man/figures/colours.png b/man/figures/colours.png index fbea6012bcec55f52cc159c85d2dd9ab28c37bad..2b026d16409ea50ce117e73365818a621d5800c2 100644 GIT binary patch delta 18649 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 a0{;(o*w**U%c!yd0000I0Se1;Ory6ufBin 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 jkLdoBs*8IaBcc From 753a28cf5e1968fc87d9e8abca3a54a6e4ee12f0 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 16:39:51 -0500 Subject: [PATCH 034/133] Improve formatting of scientific numbers --- R/scientific.R | 7 +++++-- tests/testthat/test-format_scientific.R | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/R/scientific.R b/R/scientific.R index b3b8a5de0..005437d22 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -25,7 +25,7 @@ format_scentific <- function(x, sigfig = 3, superscript = TRUE) { mnt <- round_x * 10 ^ (-exp) mnt_chr <- ifelse(num, - format(mnt, digits = sigfig), + sprintf(paste0("%.", sigfig - 1, "f"), mnt), crayon::col_align(style_na(as.character(round_x)), sigfig + 1, "right") ) @@ -35,13 +35,14 @@ format_scentific <- function(x, sigfig = 3, superscript = TRUE) { supernum <- function(x, superscript = TRUE) { stopifnot(is.integer(x)) + neg <- !is.na(x) & x < 0 if (any(x < 0, na.rm = TRUE)) { if (superscript) { neg <- ifelse(x < 0, "\u207b", "\u207a") } else { neg <- ifelse(x < 0, "-", "+") } - + neg[is.na(x)] <- " " } else { neg <- rep("", length(x)) } @@ -51,6 +52,8 @@ supernum <- function(x, superscript = TRUE) { } else { digits <- as.character(abs(x)) } + digits <- ifelse(is.na(x), "", digits) + exp <- paste0(neg, format(digits, justify = "right")) paste0(style_subtle("e"), style_num(exp, x < 0)) diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index 0aa69af16..f15e18a83 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -11,3 +11,9 @@ test_that("negative values displayed correct", { f <- as.vector(format_scientific_bw(-0.123, superscript = FALSE)) expect_equal(f, "1.23e-1") }) + +test_that("exponents correct in presence of NA", { + f <- as.vector(format_scientific_bw(c(NA, 1e-5), superscript = FALSE)) + + expect_equal(f, c(" NA", "1.00e-5")) +}) From 201b42e437a81352872f5658b584d2f681b92223 Mon Sep 17 00:00:00 2001 From: hadley Date: Wed, 17 May 2017 16:45:19 -0500 Subject: [PATCH 035/133] Correct spelling --- NAMESPACE | 2 +- R/colformat.R | 2 +- R/scientific.R | 4 ++-- man/{format_scentific.Rd => format_scientific.Rd} | 8 ++++---- tests/testthat/test-format_scientific.R | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) rename man/{format_scentific.Rd => format_scientific.Rd} (80%) diff --git a/NAMESPACE b/NAMESPACE index 7d49954e6..9b0e71a3c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,6 +13,6 @@ S3method(print,decimal_format) S3method(print,spark) export(colformat) export(format_decimal) -export(format_scentific) +export(format_scientific) export(spark_bar) export(spark_line) diff --git a/R/colformat.R b/R/colformat.R index ce4fc1882..6bb641652 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -84,7 +84,7 @@ colformat.numeric <- function(x, ..., sigfig = 3, sci_threshold = 15) { if (width <= sci_threshold) { dec } else { - format_scentific(x, sigfig = sigfig) + format_scientific(x, sigfig = sigfig) } } diff --git a/R/scientific.R b/R/scientific.R index 005437d22..93f872c43 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -8,8 +8,8 @@ #' @export #' @examples #' x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) -#' format_scentific(x) -format_scentific <- function(x, sigfig = 3, superscript = TRUE) { +#' format_scientific(x) +format_scientific <- function(x, sigfig = 3, superscript = TRUE) { stopifnot(is.numeric(x)) sigfig <- check_sigfig(sigfig) diff --git a/man/format_scentific.Rd b/man/format_scientific.Rd similarity index 80% rename from man/format_scentific.Rd rename to man/format_scientific.Rd index 6d05e42c9..35bbf3544 100644 --- a/man/format_scentific.Rd +++ b/man/format_scientific.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/scientific.R -\name{format_scentific} -\alias{format_scentific} +\name{format_scientific} +\alias{format_scientific} \title{Format numbers in scientific notation} \usage{ -format_scentific(x, sigfig = 3, superscript = TRUE) +format_scientific(x, sigfig = 3, superscript = TRUE) } \arguments{ \item{x}{A numeric vector} @@ -19,5 +19,5 @@ in scientific notation. } \examples{ x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) -format_scentific(x) +format_scientific(x) } diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index f15e18a83..a683c0e30 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -4,7 +4,7 @@ format_scientific_bw <- function(x, ...) { old <- options(crayon.enabled = FALSE) on.exit(options(old)) - format_scentific(x, ...) + format_scientific(x, ...) } test_that("negative values displayed correct", { From d630542cc7dc021c9fc13407962a6829a7569a53 Mon Sep 17 00:00:00 2001 From: hadley Date: Thu, 18 May 2017 08:37:39 -0500 Subject: [PATCH 036/133] Link to pragmata pro --- README.Rmd | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.Rmd b/README.Rmd index b80450413..b85d2aa6b 100644 --- a/README.Rmd +++ b/README.Rmd @@ -49,3 +49,5 @@ knitr::include_graphics("man/figures/colours.png") * [spark](https://github.com/holman/spark) for use of block characters. The earliest use of this technique that I could find is [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). + +Exercising these ideas to their fullest requires a font with good support for block drawing characters. [PragamataPro](https://www.fsd.it/shop/fonts/pragmatapro/) is one such font. diff --git a/README.md b/README.md index 2e3930422..9ed7153a2 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,5 @@ Inspirations - [spark](https://github.com/holman/spark) for use of block characters. The earliest use of this technique that I could find is [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). + +Exercising these ideas to their fullest requires a font with good support for block drawing characters. [PragamataPro](https://www.fsd.it/shop/fonts/pragmatapro/) is one such font. From f05b04201c3a5e76f949dba311447d0437cc26de Mon Sep 17 00:00:00 2001 From: hadley Date: Thu, 18 May 2017 08:57:49 -0500 Subject: [PATCH 037/133] Clarify "this technique" --- README.Rmd | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.Rmd b/README.Rmd index b85d2aa6b..8cf1d29eb 100644 --- a/README.Rmd +++ b/README.Rmd @@ -48,6 +48,6 @@ knitr::include_graphics("man/figures/colours.png") * [spark](https://github.com/holman/spark) for use of block characters. -The earliest use of this technique that I could find is [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). +The earliest use of unicode characters to generate sparklines appears to be [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). Exercising these ideas to their fullest requires a font with good support for block drawing characters. [PragamataPro](https://www.fsd.it/shop/fonts/pragmatapro/) is one such font. diff --git a/README.md b/README.md index 9ed7153a2..7583bd224 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,6 @@ Inspirations - [spark](https://github.com/holman/spark) for use of block characters. -The earliest use of this technique that I could find is [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). +The earliest use of unicode characters to generate sparklines appears to be [from 2009](https://blog.jonudell.net/2009/01/13/fuel-prices-and-pageviews/). Exercising these ideas to their fullest requires a font with good support for block drawing characters. [PragamataPro](https://www.fsd.it/shop/fonts/pragmatapro/) is one such font. From fa9b098a4cefbf9e263cd5fa9a5bf637e1bbe116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 31 May 2017 15:33:04 +0200 Subject: [PATCH 038/133] Formatting of decimal columns (#7) * refactoring of `format_decimal()` - split into functions - `style_num()` gains `subtle` argument - also mark lhs and dot as insignificant if lhs is zero * acceptance testing - add acceptance test - `show_output_in_terminal()` - import rlang - simplify output for tests * enhance tests * use Travis * show output --- .Rbuildignore | 1 + .travis.yml | 8 ++ DESCRIPTION | 3 +- NAMESPACE | 1 + R/sigfig.R | 97 +++++++++++++-------- R/styles.R | 28 ++++-- R/zzz.R | 3 + README.Rmd | 6 +- README.md | 2 + tests/testthat/helper-output.R | 10 +++ tests/testthat/out/basic.txt | 9 ++ tests/testthat/out/decimal-insignif.txt | 8 ++ tests/testthat/out/scientific-short-neg.txt | 5 ++ tests/testthat/out/scientific.txt | 5 ++ tests/testthat/test-format_decimal.R | 21 +++++ tests/testthat/test-format_scientific.R | 5 ++ 16 files changed, 165 insertions(+), 47 deletions(-) create mode 100644 .travis.yml create mode 100644 tests/testthat/helper-output.R create mode 100644 tests/testthat/out/basic.txt create mode 100644 tests/testthat/out/decimal-insignif.txt create mode 100644 tests/testthat/out/scientific-short-neg.txt create mode 100644 tests/testthat/out/scientific.txt diff --git a/.Rbuildignore b/.Rbuildignore index 8453a6f6c..1c92417ad 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -2,3 +2,4 @@ ^\.Rproj\.user$ ^README\.Rmd$ ^README-.*\.png$ +^\.travis\.yml$ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..b4697975b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +# R for travis: see documentation at https://docs.travis-ci.com/user/languages/r + +language: R +sudo: false +cache: packages + +before_script: +- head -n 100 tests/testthat/out/* diff --git a/DESCRIPTION b/DESCRIPTION index de11026d0..bf3bdedfb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,7 +13,8 @@ LazyData: true URL: https://github.com/hadley/colformat BugReports: https://github.com/hadley/colformat/issues Imports: - crayon + crayon, + rlang RoxygenNote: 6.0.1 Suggests: testthat Remotes: diff --git a/NAMESPACE b/NAMESPACE index 9b0e71a3c..7225086a7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -16,3 +16,4 @@ export(format_decimal) export(format_scientific) export(spark_bar) export(spark_line) +import(rlang) diff --git a/R/sigfig.R b/R/sigfig.R index bb837e43a..0ccd30f9c 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -27,29 +27,15 @@ format_decimal <- function(x, sigfig = 3) { stopifnot(is.numeric(x)) sigfig <- check_sigfig(sigfig) - n <- length(x) abs_x <- abs(x) - # If already bigger than sigfig, can round to zero. - # Otherwise ensure we have sigfig digits shown - exp <- floor(log10(abs_x)) - round_x <- signif(abs_x, pmax(sigfig, exp + 1)) - digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) - - rhs_digits <- pmax(digits - pmax(exp, 0), 0) - dec <- !is.na(digits) & rhs_digits > 0 + rhs_digits <- compute_rhs_digits(abs_x, sigfig) + dec <- rhs_digits > 0 # Do we need negative signs? neg <- !is.na(x) & x < 0 - if (any(neg)) { - neg_col <- ifelse(neg, "-", " ") - } else { - neg_col <- rep("", n) - } - - # Digits on LHS of . - num <- is.finite(x) + round_x <- signif(abs_x, pmax(sigfig, compute_exp(abs_x) + 1)) lhs <- trunc(round_x) lhs_zero <- lhs == 0 lhs_str <- sprintf("%.0f", lhs) @@ -57,52 +43,91 @@ format_decimal <- function(x, sigfig = 3) { lhs_sig <- substr(lhs_str, 1, sigfig) lhs_non <- substr(lhs_str, sigfig + 1, nchar(lhs_str)) + # Digits on RHS of . + rhs <- round_x - lhs + rhs_digits <- compute_rhs_digits(abs_x, sigfig) + rhs_num <- as.character(abs(round(rhs * 10 ^ rhs_digits))) + + structure( + list( + neg = format_neg(neg), + lhs = format_lhs( + neg, lhs_zero, lhs_str, lhs_width, lhs_sig, lhs_non, + num = is.finite(x)), + dec = format_dec(neg, dec, lhs_zero), + rhs = format_rhs(neg, dec, lhs_zero, rhs_num, rhs_digits) + ), + class = "decimal_format" + ) +} + +compute_rhs_digits <- function(x, sigfig) { + # If already bigger than sigfig, can round to zero. + # Otherwise ensure we have sigfig digits shown + exp <- compute_exp(x) + digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) + rhs_digits <- pmax(digits - pmax(exp, 0), 0) + rhs_digits +} + +compute_exp <- function(x) { + ret <- rep_along(x, Inf) + nonzero <- which(x != 0 & is.finite(x)) + ret[nonzero] <- floor(log10(x[nonzero])) + ret +} + +format_neg <- function(neg) { + if (any(neg)) { + neg_col <- ifelse(neg, "-", " ") + } else { + neg_col <- rep_along(neg, "") + } + neg_col +} + +format_lhs <- function(neg, lhs_zero, lhs_str, lhs_width, lhs_sig, lhs_non, num) { lhs_col <- ifelse(num, paste0( - ifelse(neg & lhs_zero, style_subtle(lhs_sig), style_num(lhs_sig, neg)), + style_num(lhs_sig, neg, lhs_zero), style_subtle(lhs_non) ), style_na(lhs_str) ) lhs_col <- crayon::col_align(lhs_col, width = lhs_width, align = "right") + lhs_col +} +format_dec <- function(neg, dec, lhs_zero) { # Decimal column if (any(dec)) { - dec_col <- ifelse(dec, style_num(".", neg & !lhs_zero), " ") + dec_col <- ifelse(dec, style_num(".", neg, lhs_zero), " ") } else { - dec_col <- rep("", n) + dec_col <- rep_along(neg, "") } + dec_col +} - # Digits on RHS of . - rhs <- round_x - lhs - - rhs_num <- as.character(abs(round(rhs * 10 ^ rhs_digits))) +format_rhs <- function(neg, dec, lhs_zero, rhs_num, rhs_digits) { rhs_zero <- strrep("0", pmax(0, rhs_digits - nchar(rhs_num))) rhs_col <- ifelse(dec, paste0( - ifelse(lhs_zero, style_subtle(rhs_zero), style_num(rhs_zero, neg)), + style_num(rhs_zero, neg, lhs_zero), style_num(rhs_num, neg) ), "" ) + # ensure all same width rhs_col <- crayon::col_align(rhs_col, max(rhs_digits, na.rm = TRUE), "left") - structure( - list( - neg = neg_col, - lhs = lhs_col, - dec = dec_col, - rhs = rhs_col - ), - class = "decimal_format" - ) + rhs_col } -style_num <- function(x, negative) { - ifelse(negative, style_neg(x), x) +style_num <- function(x, negative, subtle = rep_along(x, FALSE)) { + ifelse(subtle, style_subtle(x), ifelse(negative, style_neg(x), x)) } #' @export diff --git a/R/styles.R b/R/styles.R index 7ba225c9f..464edf953 100644 --- a/R/styles.R +++ b/R/styles.R @@ -1,22 +1,32 @@ -style_accent <- function(x) { - crayon::green(x) +keep_empty <- function(fun) { + function(x) { + ret <- rep_along(x, "") + update <- is.na(x) | x != "" + x <- x[update] + ret[update] <- fun(x) + ret + } } -style_subtle <- function(...) { - style_grey(0.6, ...) -} +style_accent <- keep_empty(function(x) { + crayon::green(x) +}) + +style_subtle <- keep_empty(function(x) { + style_grey(0.6, x) +}) colour_na <- function() { grDevices::rgb(5, 5, 2, maxColorValue = 5) } -style_na <- function(x) { +style_na <- keep_empty(function(x) { crayon::style(x, bg = colour_na()) -} +}) -style_neg <- function(x) { +style_neg <- keep_empty(function(x) { crayon::red(x) -} +}) style_grey <- function(level, ...) { diff --git a/R/zzz.R b/R/zzz.R index b01fdbbf5..9a9a25e82 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,3 +1,6 @@ +#' @import rlang +NULL + .onAttach <- function(...) { crayon::num_colors(TRUE) } diff --git a/README.Rmd b/README.Rmd index 8cf1d29eb..477793b53 100644 --- a/README.Rmd +++ b/README.Rmd @@ -1,5 +1,7 @@ --- -output: github_document +output: + github_document: + html_preview: false --- @@ -14,6 +16,8 @@ knitr::opts_chunk$set( # colformat +[![Travis-CI Build Status](https://travis-ci.org/hadley/colformat.svg?branch=master)](https://travis-ci.org/hadley/colformat) + colformat provides tools for styling columns of data, artfully using colour and unicode characters to ## Installation diff --git a/README.md b/README.md index 7583bd224..454c8c13d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ colformat ========= +[![Travis-CI Build Status](https://travis-ci.org/hadley/colformat.svg?branch=master)](https://travis-ci.org/hadley/colformat) + colformat provides tools for styling columns of data, artfully using colour and unicode characters to Installation diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R new file mode 100644 index 000000000..c82d1cb6a --- /dev/null +++ b/tests/testthat/helper-output.R @@ -0,0 +1,10 @@ +expect_colformat_output <- function(x, ..., filename) { + old <- options(crayon.enabled = TRUE) + on.exit(options(old)) + + expect_output_file(print(colformat(x, ...)), file.path("out", filename), update = TRUE) +} + +show_output_in_terminal <- function() { + system2("xterm", c("-e", shQuote("head tests/testthat/out/*; sleep 600"))) +} diff --git a/tests/testthat/out/basic.txt b/tests/testthat/out/basic.txt new file mode 100644 index 000000000..9da22638f --- /dev/null +++ b/tests/testthat/out/basic.txt @@ -0,0 +1,9 @@ + title +- 0.00100 + 0.0100 +- 0.100 + 1.00 +- 10.0 + 100 +- 1000 + 10000 diff --git a/tests/testthat/out/decimal-insignif.txt b/tests/testthat/out/decimal-insignif.txt new file mode 100644 index 000000000..d88f1fb17 --- /dev/null +++ b/tests/testthat/out/decimal-insignif.txt @@ -0,0 +1,8 @@ + title + 0.00123 + 0.0123 + 0.123 + 1.23 + 12.3 + 123 +1235 diff --git a/tests/testthat/out/scientific-short-neg.txt b/tests/testthat/out/scientific-short-neg.txt new file mode 100644 index 000000000..a7b424a79 --- /dev/null +++ b/tests/testthat/out/scientific-short-neg.txt @@ -0,0 +1,5 @@ +title  +1.00e ³ +1.00e ⁹ +1.00e¹⁵ +1.00e²² diff --git a/tests/testthat/out/scientific.txt b/tests/testthat/out/scientific.txt new file mode 100644 index 000000000..5dfe3ddbf --- /dev/null +++ b/tests/testthat/out/scientific.txt @@ -0,0 +1,5 @@ +title  +1.00e⁻⁹ +1.00e⁻⁶ +1.00e⁺³ +1.00e⁺⁹ diff --git a/tests/testthat/test-format_decimal.R b/tests/testthat/test-format_decimal.R index c9fa8b899..a87419bfb 100644 --- a/tests/testthat/test-format_decimal.R +++ b/tests/testthat/test-format_decimal.R @@ -7,6 +7,17 @@ format_decimal_bw <- function(x, ...) { format_decimal(x, ...) } +test_that("compute_rhs_digits() works", { + x <- c(NA, NaN, Inf, 0, 1, 100, 1e10, 0.001, 1e-20) + expect_equal(compute_rhs_digits(x, 3), c(0, 0, 0, 0, 2, 0, 0, 5, 22)) + expect_equal(compute_rhs_digits(x, 7), c(0, 0, 0, 0, 6, 3, 0, 9, 26)) +}) + +test_that("compute_exp() does not return NA for positive input", { + x <- c(NA, NaN, Inf, 0, 1, 100, 0.001) + expect_equal(compute_exp(x), c(Inf, Inf, Inf, Inf, 0, 2, -3)) +}) + test_that("special values appear in LHS", { x <- c(NA, NaN, Inf) f <- format_decimal_bw(x) @@ -14,6 +25,11 @@ test_that("special values appear in LHS", { expect_equal(f$lhs, format(x)) }) +test_that("all-positive values get nothing in neg", { + f <- format_decimal_bw(c(0, Inf)) + expect_equal(f$neg, c("", "")) +}) + test_that("negative values get - in neg", { f <- format_decimal_bw(c(-Inf, Inf)) expect_equal(f$neg, c("-", " ")) @@ -51,3 +67,8 @@ test_that("values on LHS not rounded", { f <- format_decimal_bw(123456) expect_equal(f$lhs, "123456") }) + +test_that("output test", { + expect_colformat_output((10 ^ (-3:4)) * c(-1, 1), filename = "basic.txt") + expect_colformat_output(1.23456 * 10 ^ (-3:3), filename = "decimal-insignif.txt") +}) diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index a683c0e30..e7c7d2e60 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -17,3 +17,8 @@ test_that("exponents correct in presence of NA", { expect_equal(f, c(" NA", "1.00e-5")) }) + +test_that("output test", { + expect_colformat_output(10 ^ c(-9, -6, 3, 9), filename = "scientific.txt") + expect_colformat_output((10 ^ c(3, 9, 15, 22)) * c(-1, 1), filename = "scientific-short-neg.txt") +}) From 8ea8d039e208fcc06f1586e1912d7a16444b9a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 5 Jun 2017 00:24:45 +0200 Subject: [PATCH 039/133] Delayed formatting (#10) * extract split_decimal() * simplify * sigfig * pass s * compute format only when printing --- R/sigfig.R | 76 +++++++++++++++++++--------- tests/testthat/test-format_decimal.R | 24 ++++----- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/R/sigfig.R b/R/sigfig.R index 0ccd30f9c..04fc3bb60 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -24,40 +24,38 @@ #' format_decimal(c(Inf, -Inf, NA, NaN), 3) #' format_decimal(c(1e10, 1e-10), 3) format_decimal <- function(x, sigfig = 3) { + s <- split_decimal(x, sigfig) + + structure( + s, + class = "decimal_format" + ) +} + +split_decimal <- function(x, sigfig) { stopifnot(is.numeric(x)) sigfig <- check_sigfig(sigfig) abs_x <- abs(x) rhs_digits <- compute_rhs_digits(abs_x, sigfig) - dec <- rhs_digits > 0 # Do we need negative signs? neg <- !is.na(x) & x < 0 round_x <- signif(abs_x, pmax(sigfig, compute_exp(abs_x) + 1)) lhs <- trunc(round_x) - lhs_zero <- lhs == 0 - lhs_str <- sprintf("%.0f", lhs) - lhs_width <- max(nchar(lhs_str)) - lhs_sig <- substr(lhs_str, 1, sigfig) - lhs_non <- substr(lhs_str, sigfig + 1, nchar(lhs_str)) - - # Digits on RHS of . rhs <- round_x - lhs - rhs_digits <- compute_rhs_digits(abs_x, sigfig) - rhs_num <- as.character(abs(round(rhs * 10 ^ rhs_digits))) - structure( - list( - neg = format_neg(neg), - lhs = format_lhs( - neg, lhs_zero, lhs_str, lhs_width, lhs_sig, lhs_non, - num = is.finite(x)), - dec = format_dec(neg, dec, lhs_zero), - rhs = format_rhs(neg, dec, lhs_zero, rhs_num, rhs_digits) - ), - class = "decimal_format" + list( + sigfig = sigfig, + num = is.finite(x), + neg = neg, + lhs = lhs, + lhs_zero = (lhs == 0), + rhs = rhs, + rhs_digits = rhs_digits, + dec = rhs_digits > 0 ) } @@ -77,7 +75,8 @@ compute_exp <- function(x) { ret } -format_neg <- function(neg) { +format_neg <- function(s) { + neg <- s$neg if (any(neg)) { neg_col <- ifelse(neg, "-", " ") } else { @@ -86,7 +85,16 @@ format_neg <- function(neg) { neg_col } -format_lhs <- function(neg, lhs_zero, lhs_str, lhs_width, lhs_sig, lhs_non, num) { +format_lhs <- function(s) { + neg <- s$neg + num <- s$num + lhs_zero <- s$lhs_zero + + lhs_str <- sprintf("%.0f", s$lhs) + lhs_width <- max(nchar(lhs_str)) + lhs_sig <- substr(lhs_str, 1, s$sigfig) + lhs_non <- substr(lhs_str, s$sigfig + 1, nchar(lhs_str)) + lhs_col <- ifelse(num, paste0( style_num(lhs_sig, neg, lhs_zero), @@ -99,7 +107,11 @@ format_lhs <- function(neg, lhs_zero, lhs_str, lhs_width, lhs_sig, lhs_non, num) lhs_col } -format_dec <- function(neg, dec, lhs_zero) { +format_dec <- function(s) { + neg <- s$neg + dec <- s$dec + lhs_zero <- s$lhs_zero + # Decimal column if (any(dec)) { dec_col <- ifelse(dec, style_num(".", neg, lhs_zero), " ") @@ -109,7 +121,16 @@ format_dec <- function(neg, dec, lhs_zero) { dec_col } -format_rhs <- function(neg, dec, lhs_zero, rhs_num, rhs_digits) { +format_rhs <- function(s) { + neg <- s$neg + dec <- s$dec + lhs_zero <- s$lhs_zero + rhs_num <- s$rhs_num + rhs_digits <- s$rhs_digits + + # Digits on RHS of . + rhs_num <- as.character(abs(round(s$rhs * 10 ^ s$rhs_digits))) + rhs_zero <- strrep("0", pmax(0, rhs_digits - nchar(rhs_num))) rhs_col <- ifelse(dec, @@ -132,7 +153,12 @@ style_num <- function(x, negative, subtle = rep_along(x, FALSE)) { #' @export format.decimal_format <- function(x, title = "title", ...) { - row <- paste0(x$neg, x$lhs, x$dec, x$rhs) + neg <- format_neg(x) + lhs <- format_lhs(x) + dec <- format_dec(x) + rhs <- format_rhs(x) + + row <- paste0(neg, lhs, dec, rhs) width <- max(nchar(title), crayon::col_nchar(row)) new_column(row, title = title, width = width, align = "right") diff --git a/tests/testthat/test-format_decimal.R b/tests/testthat/test-format_decimal.R index a87419bfb..e5b0479e3 100644 --- a/tests/testthat/test-format_decimal.R +++ b/tests/testthat/test-format_decimal.R @@ -22,50 +22,50 @@ test_that("special values appear in LHS", { x <- c(NA, NaN, Inf) f <- format_decimal_bw(x) - expect_equal(f$lhs, format(x)) + expect_equal(format_lhs(f), format(x)) }) test_that("all-positive values get nothing in neg", { f <- format_decimal_bw(c(0, Inf)) - expect_equal(f$neg, c("", "")) + expect_equal(format_neg(f), c("", "")) }) test_that("negative values get - in neg", { f <- format_decimal_bw(c(-Inf, Inf)) - expect_equal(f$neg, c("-", " ")) + expect_equal(format_neg(f), c("-", " ")) }) test_that("trailing zeros pad to sigfigs", { f <- format_decimal_bw(c(1.5, 0.5)) - expect_equal(f$lhs, c("1", "0")) - expect_equal(f$rhs, c("50 ", "500")) + expect_equal(format_lhs(f), c("1", "0")) + expect_equal(format_rhs(f), c("50 ", "500")) }) test_that("sigfigs split between lhs and rhs", { x <- c(1.50, 10.50, 100.50) f <- format_decimal_bw(x) - expect_equal(f$lhs, format(trunc(x))) - expect_equal(f$rhs, c("50", "5 ", " ")) + expect_equal(format_lhs(f), format(trunc(x))) + expect_equal(format_rhs(f), c("50", "5 ", " ")) }) test_that("leading 0 added to rhs", { f <- format_decimal_bw(1.01) - expect_equal(f$lhs, "1") - expect_equal(f$rhs, "01") + expect_equal(format_lhs(f), "1") + expect_equal(format_rhs(f), "01") }) test_that("values rounded up as expect", { f <- format_decimal_bw(c(18.9, 18.99)) - expect_equal(f$lhs, c("18", "19")) - expect_equal(f$rhs, c("9", "0")) + expect_equal(format_lhs(f), c("18", "19")) + expect_equal(format_rhs(f), c("9", "0")) }) test_that("values on LHS not rounded", { f <- format_decimal_bw(123456) - expect_equal(f$lhs, "123456") + expect_equal(format_lhs(f), "123456") }) test_that("output test", { From c805a8bc79b6c1ca10a8b0b7da0bc17058cb2d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 5 Jun 2017 01:19:41 +0200 Subject: [PATCH 040/133] no LaTeX --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index b4697975b..91bfb3959 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: R sudo: false cache: packages +latex: false before_script: - head -n 100 tests/testthat/out/* From 0a35999e7d77b9b3a47b4a04662d1c2625f929d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 30 Jun 2017 16:45:07 +0200 Subject: [PATCH 041/133] Fix scientific formatting (#14) * fix negative sign for scientific notation * compute_exp() returns NA instead of Inf * compute_exp() returns integer * align * unify * always include NA and Inf in output * fix NA display for scientific notation * simplify * document --- R/scientific.R | 45 +++++++++------------ R/sigfig.R | 35 +++++++++++----- man/format_decimal.Rd | 2 +- tests/testthat/helper-output.R | 2 + tests/testthat/out/basic.txt | 3 ++ tests/testthat/out/decimal-insignif.txt | 19 +++++---- tests/testthat/out/scientific-short-neg.txt | 13 +++--- tests/testthat/out/scientific.txt | 13 +++--- tests/testthat/test-format_decimal.R | 4 +- tests/testthat/test-format_scientific.R | 9 ++--- 10 files changed, 84 insertions(+), 61 deletions(-) diff --git a/R/scientific.R b/R/scientific.R index 93f872c43..cbd0f5987 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -10,41 +10,34 @@ #' x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) #' format_scientific(x) format_scientific <- function(x, sigfig = 3, superscript = TRUE) { - stopifnot(is.numeric(x)) - sigfig <- check_sigfig(sigfig) + s <- split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) - n <- length(x) - abs_x <- abs(x) - round_x <- signif(abs_x, sigfig) - num <- is.finite(x) - abs_x[!num] <- NA # supress warning from log10 - - # Compute exponent and mantissa - exp <- as.integer(floor(log10(abs_x))) - exp_chr <- ifelse(num, supernum(exp, superscript = superscript), "") - - mnt <- round_x * 10 ^ (-exp) - mnt_chr <- ifelse(num, - sprintf(paste0("%.", sigfig - 1, "f"), mnt), - crayon::col_align(style_na(as.character(round_x)), sigfig + 1, "right") + structure( + s, + class = "decimal_format" ) +} - new_colformat(paste0(mnt_chr, exp_chr)) +format_exp <- function(x) { + supernum(x$exp, superscript = x$superscript) } supernum <- function(x, superscript = TRUE) { stopifnot(is.integer(x)) - neg <- !is.na(x) & x < 0 - if (any(x < 0, na.rm = TRUE)) { + num <- !is.na(x) + if (!any(num)) return(rep_along(x, "")) + + neg <- num & x < 0 + if (any(neg)) { if (superscript) { - neg <- ifelse(x < 0, "\u207b", "\u207a") + neg_chr <- ifelse(neg, "\u207b", "\u207a") } else { - neg <- ifelse(x < 0, "-", "+") + neg_chr <- ifelse(neg, "-", "+") } - neg[is.na(x)] <- " " + neg_chr[!num] <- " " } else { - neg <- rep("", length(x)) + neg_chr <- rep("", length(x)) } if (superscript) { @@ -52,11 +45,11 @@ supernum <- function(x, superscript = TRUE) { } else { digits <- as.character(abs(x)) } - digits <- ifelse(is.na(x), "", digits) + digits[!num] <- "" - exp <- paste0(neg, format(digits, justify = "right")) + exp <- paste0(neg_chr, format(digits, justify = "right")) - paste0(style_subtle("e"), style_num(exp, x < 0)) + paste0(style_subtle(ifelse(num, "e", " ")), style_num(exp, neg)) } supernum1 <- function(x) { diff --git a/R/sigfig.R b/R/sigfig.R index 04fc3bb60..1e76786fe 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -5,7 +5,7 @@ #' (for positive and negative numbers) and non-significant digits are coloured #' in paler gray. #' -#' @return A tibble with four columns: +#' @return A list with at least the following elements: #' * `neg`: negative sign or space, if needed #' * `lhs`: whole number #' * `dec`: decimal point, if needed @@ -32,30 +32,45 @@ format_decimal <- function(x, sigfig = 3) { ) } -split_decimal <- function(x, sigfig) { +split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { stopifnot(is.numeric(x)) sigfig <- check_sigfig(sigfig) abs_x <- abs(x) - rhs_digits <- compute_rhs_digits(abs_x, sigfig) + num <- is.finite(x) # Do we need negative signs? neg <- !is.na(x) & x < 0 - round_x <- signif(abs_x, pmax(sigfig, compute_exp(abs_x) + 1)) + # Compute exponent and mantissa + exp <- compute_exp(abs_x) + + if (scientific) { + mnt <- ifelse(num, abs_x * 10 ^ (-exp), abs_x) + round_x <- signif(mnt, sigfig) + rhs_digits <- ifelse(num, sigfig - 1, 0) + exp_display <- exp + } else { + round_x <- signif(abs_x, pmax(sigfig, exp + 1, na.rm = TRUE)) + rhs_digits <- compute_rhs_digits(abs_x, sigfig) + exp_display <- rep_along(x, NA_integer_) + } + lhs <- trunc(round_x) rhs <- round_x - lhs list( sigfig = sigfig, - num = is.finite(x), + num = num, neg = neg, lhs = lhs, lhs_zero = (lhs == 0), rhs = rhs, rhs_digits = rhs_digits, - dec = rhs_digits > 0 + dec = rhs_digits > 0, + exp = exp_display, + superscript = superscript ) } @@ -63,15 +78,16 @@ compute_rhs_digits <- function(x, sigfig) { # If already bigger than sigfig, can round to zero. # Otherwise ensure we have sigfig digits shown exp <- compute_exp(x) + exp[is.na(exp)] <- Inf digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) rhs_digits <- pmax(digits - pmax(exp, 0), 0) rhs_digits } compute_exp <- function(x) { - ret <- rep_along(x, Inf) + ret <- rep_along(x, NA_integer_) nonzero <- which(x != 0 & is.finite(x)) - ret[nonzero] <- floor(log10(x[nonzero])) + ret[nonzero] <- as.integer(floor(log10(x[nonzero]))) ret } @@ -157,8 +173,9 @@ format.decimal_format <- function(x, title = "title", ...) { lhs <- format_lhs(x) dec <- format_dec(x) rhs <- format_rhs(x) + exp <- format_exp(x) - row <- paste0(neg, lhs, dec, rhs) + row <- paste0(neg, lhs, dec, rhs, exp) width <- max(nchar(title), crayon::col_nchar(row)) new_column(row, title = title, width = width, align = "right") diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd index b9f60d005..7b883862f 100644 --- a/man/format_decimal.Rd +++ b/man/format_decimal.Rd @@ -12,7 +12,7 @@ format_decimal(x, sigfig = 3) \item{sigfig}{Number of signficiant figures to display.} } \value{ -A tibble with four columns: +A list with at least the following elements: * `neg`: negative sign or space, if needed * `lhs`: whole number * `dec`: decimal point, if needed diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index c82d1cb6a..12c4eefd2 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -1,4 +1,6 @@ expect_colformat_output <- function(x, ..., filename) { + x <- c(x, NA, -Inf, Inf) + old <- options(crayon.enabled = TRUE) on.exit(options(old)) diff --git a/tests/testthat/out/basic.txt b/tests/testthat/out/basic.txt index 9da22638f..120547b2a 100644 --- a/tests/testthat/out/basic.txt +++ b/tests/testthat/out/basic.txt @@ -7,3 +7,6 @@ 100 - 1000 10000 + NA +- Inf + Inf diff --git a/tests/testthat/out/decimal-insignif.txt b/tests/testthat/out/decimal-insignif.txt index d88f1fb17..368de7eed 100644 --- a/tests/testthat/out/decimal-insignif.txt +++ b/tests/testthat/out/decimal-insignif.txt @@ -1,8 +1,11 @@ - title - 0.00123 - 0.0123 - 0.123 - 1.23 - 12.3 - 123 -1235 + title + 0.00123 + 0.0123 + 0.123 + 1.23 + 12.3 + 123 + 1235 + NA +- Inf + Inf diff --git a/tests/testthat/out/scientific-short-neg.txt b/tests/testthat/out/scientific-short-neg.txt index a7b424a79..a7fd33e78 100644 --- a/tests/testthat/out/scientific-short-neg.txt +++ b/tests/testthat/out/scientific-short-neg.txt @@ -1,5 +1,8 @@ -title  -1.00e ³ -1.00e ⁹ -1.00e¹⁵ -1.00e²² + title +- 1.00e ³ + 1.00e ⁹ +- 1.00e¹⁵ + 1.00e²² + NA   +-Inf   + Inf   diff --git a/tests/testthat/out/scientific.txt b/tests/testthat/out/scientific.txt index 5dfe3ddbf..327bdbd57 100644 --- a/tests/testthat/out/scientific.txt +++ b/tests/testthat/out/scientific.txt @@ -1,5 +1,8 @@ -title  -1.00e⁻⁹ -1.00e⁻⁶ -1.00e⁺³ -1.00e⁺⁹ + title + 1.00e⁻⁹ + 1.00e⁻⁶ + 1.00e⁺³ + 1.00e⁺⁹ + NA   +-Inf   + Inf   diff --git a/tests/testthat/test-format_decimal.R b/tests/testthat/test-format_decimal.R index e5b0479e3..a88598d74 100644 --- a/tests/testthat/test-format_decimal.R +++ b/tests/testthat/test-format_decimal.R @@ -13,9 +13,9 @@ test_that("compute_rhs_digits() works", { expect_equal(compute_rhs_digits(x, 7), c(0, 0, 0, 0, 6, 3, 0, 9, 26)) }) -test_that("compute_exp() does not return NA for positive input", { +test_that("compute_exp() returns NA if not relevant", { x <- c(NA, NaN, Inf, 0, 1, 100, 0.001) - expect_equal(compute_exp(x), c(Inf, Inf, Inf, Inf, 0, 2, -3)) + expect_equal(compute_exp(x), c(NA, NA, NA, NA, 0, 2, -3)) }) test_that("special values appear in LHS", { diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index e7c7d2e60..20d416438 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -8,14 +8,13 @@ format_scientific_bw <- function(x, ...) { } test_that("negative values displayed correct", { - f <- as.vector(format_scientific_bw(-0.123, superscript = FALSE)) - expect_equal(f, "1.23e-1") + f <- format_scientific_bw(-0.123, superscript = FALSE) + expect_equal(unname(format(format(f))), "-1.23e-1") }) test_that("exponents correct in presence of NA", { - f <- as.vector(format_scientific_bw(c(NA, 1e-5), superscript = FALSE)) - - expect_equal(f, c(" NA", "1.00e-5")) + f <- format_scientific_bw(c(NA, 1e-5), superscript = FALSE) + expect_equal(unname(format(format(f))), c("NA ", " 1.00e-5")) }) test_that("output test", { From 532e1df4c18e194421c6770e93efc34fc7e613f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 28 Jul 2017 16:47:36 +0200 Subject: [PATCH 042/133] Formatting data in coldata class (#16) * new wrapper class coldisplay * rename * rename * update documentation * rename to col_data --- NAMESPACE | 13 +++-- R/col-data.R | 126 +++++++++++++++++++++++++++++++++++++++++++++++ R/colformat.R | 89 +++------------------------------ man/col_data.Rd | 66 +++++++++++++++++++++++++ man/colformat.Rd | 24 --------- 5 files changed, 206 insertions(+), 112 deletions(-) create mode 100644 R/col-data.R create mode 100644 man/col_data.Rd diff --git a/NAMESPACE b/NAMESPACE index 7225086a7..7c261e73a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,16 +1,19 @@ # Generated by roxygen2: do not edit by hand -S3method(colformat,Date) -S3method(colformat,POSIXct) -S3method(colformat,character) -S3method(colformat,logical) -S3method(colformat,numeric) +S3method(col_data,Date) +S3method(col_data,POSIXct) +S3method(col_data,character) +S3method(col_data,logical) +S3method(col_data,numeric) +S3method(format,col_data) S3method(format,colformat) S3method(format,decimal_format) +S3method(print,col_data) S3method(print,colformat) S3method(print,column) S3method(print,decimal_format) S3method(print,spark) +export(col_data) export(colformat) export(format_decimal) export(format_scientific) diff --git a/R/col-data.R b/R/col-data.R new file mode 100644 index 000000000..bf1057752 --- /dev/null +++ b/R/col-data.R @@ -0,0 +1,126 @@ +#' Column data +#' +#' Internal class for formatting the data part of a column. +#' +#' @param x A vector to format +#' @param ... Other arguments passed to methods +#' @export +#' @examples +#' x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) +#' col_data(x) +#' col_data(-x) +#' col_data(runif(10)) +#' col_data(rcauchy(20)) +#' +#' # Special values are highlighted +#' col_data(c(runif(5), NA, NaN, Inf, -Inf)) +#' +#' # Very wide ranges will be displayed in scientific format +#' col_data(c(1e10, 1e-10)) +#' col_data(c(1e10, 1e-10), sci_threshold = Inf) +#' +#' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) +#' col_data(x) +#' +#' x <- c("This is string is rather long", NA, "?", "Short") +#' col_data(x) +#' col_data(x, width = 30) +#' col_data(x, width = 5) +#' +#' date <- as.Date("2017-05-15") +#' col_data(date + c(1, NA, 3:5)) +#' col_data(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) +col_data <- function(x, ...) { + UseMethod("col_data") +} + +#' @export +format.col_data <- function(x, title = "title", ...) { + align <- attr(x, "align") + width <- max(nchar(title), attr(x, "width")) + + new_column(x, title = title, width = width, align = attr(x, "align")) +} + +#' @export +print.col_data <- function(x, title = "title", ...) { + print(format(x, title = title, ...)) +} + +new_col_data <- function(x, width = max(crayon::col_nchar(x)), align = "left") { + structure( + x, + width = width, + align = align, + class = "col_data" + ) +} + +# Methods ----------------------------------------------------------------- + +#' @export +#' @rdname col_data +col_data.logical <- function(x, ...) { + out <- character(length(x)) + out[x & !is.na(x)] <- style_accent("*") + out[!x & !is.na(x)] <- style_subtle("-") + out[is.na(x)] <- col_na() + + new_col_data(out, width = 1, align = "right") +} + +#' @export +#' @rdname col_data +#' @param sigfig Minimum number of significant figures to display. Numbers +#' larger than 1 will potentially show more signficiant figures than this +#' but they will be greyed out. +#' @param sci_threshold If decimal display is wider than this threshold, +#' use scientific display instead. +col_data.numeric <- function(x, ..., sigfig = 3, sci_threshold = 15) { + dec <- format_decimal(x, sigfig = sigfig) + + # This is somewhat inefficient but we can fix if it becomes a bottleneck + width <- attr(format(dec), "width") + if (width <= sci_threshold) { + dec + } else { + format_scientific(x, sigfig = sigfig) + } +} + +#' @export +#' @rdname col_data +col_data.Date <- function(x, ...) { + x <- format(x, format = "%Y-%m-%d") + x[is.na(x)] <- col_na() + + new_col_data(x, width = 11, align = "right") +} + +#' @export +#' @rdname col_data +col_data.POSIXct <- function(x, ...) { + date <- format(x, format = "%Y-%m-%d") + time <- format(x, format = "%H:%M:%S") + + datetime <- paste0(date, " " , style_subtle(time)) + datetime[is.na(x)] <- col_na() + + new_col_data(datetime, width = 19, align = "right") +} + + +#' @export +#' @param width Preferred width of output +#' @rdname col_data +col_data.character <- function(x, ..., width = NA) { + if (is.na(width)) { + width <- pmin(max(nchar(x), na.rm = TRUE), 20) + } + + x <- encodeString(x, na.encode = FALSE) + out <- str_trunc(x, width = width) + out[is.na(out)] <- col_na() + + new_col_data(out, width = width, align = "left") +} diff --git a/R/colformat.R b/R/colformat.R index 6bb641652..3ac4d976e 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -31,96 +31,19 @@ #' colformat(date + c(1, NA, 3:5)) #' colformat(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) colformat <- function(x, ...) { - UseMethod("colformat") -} - -#' @export -format.colformat <- function(x, title = "title", ...) { - align <- attr(x, "align") - width <- max(nchar(title), attr(x, "width")) - - new_column(x, title = title, width = width, align = attr(x, "align")) -} - -#' @export -print.colformat <- function(x, title = "title", ...) { - print(format(x, title = title, ...)) -} - -new_colformat <- function(x, width = max(crayon::col_nchar(x)), align = "left") { + data <- col_data(x, ...) structure( - x, - width = width, - align = align, + list(data = data), class = "colformat" ) } -# Methods ----------------------------------------------------------------- - -#' @export -#' @rdname colformat -colformat.logical <- function(x, ...) { - out <- character(length(x)) - out[x & !is.na(x)] <- style_accent("*") - out[!x & !is.na(x)] <- style_subtle("-") - out[is.na(x)] <- col_na() - - new_colformat(out, width = 1, align = "right") -} - #' @export -#' @rdname colformat -#' @param sigfig Minimum number of significant figures to display. Numbers -#' larger than 1 will potentially show more signficiant figures than this -#' but they will be greyed out. -#' @param sci_threshold If decimal display is wider than this threshold, -#' use scientific display instead. -colformat.numeric <- function(x, ..., sigfig = 3, sci_threshold = 15) { - dec <- format_decimal(x, sigfig = sigfig) - - # This is somewhat inefficient but we can fix if it becomes a bottleneck - width <- attr(format(dec), "width") - if (width <= sci_threshold) { - dec - } else { - format_scientific(x, sigfig = sigfig) - } -} - -#' @export -#' @rdname colformat -colformat.Date <- function(x, ...) { - x <- format(x, format = "%Y-%m-%d") - x[is.na(x)] <- col_na() - - new_colformat(x, width = 11, align = "right") -} - -#' @export -#' @rdname colformat -colformat.POSIXct <- function(x, ...) { - date <- format(x, format = "%Y-%m-%d") - time <- format(x, format = "%H:%M:%S") - - datetime <- paste0(date, " " , style_subtle(time)) - datetime[is.na(x)] <- col_na() - - new_colformat(datetime, width = 19, align = "right") +format.colformat <- function(x, title = "title", ...) { + format(x$data, title = title, ...) } - #' @export -#' @param width Preferred width of output -#' @rdname colformat -colformat.character <- function(x, ..., width = NA) { - if (is.na(width)) { - width <- pmin(max(nchar(x), na.rm = TRUE), 20) - } - - x <- encodeString(x, na.encode = FALSE) - out <- str_trunc(x, width = width) - out[is.na(out)] <- col_na() - - new_colformat(out, width = width, align = "left") +print.colformat <- function(x, title = "title", ...) { + print(format(x, title = title, ...)) } diff --git a/man/col_data.Rd b/man/col_data.Rd new file mode 100644 index 000000000..2ba5514db --- /dev/null +++ b/man/col_data.Rd @@ -0,0 +1,66 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/coldata.R +\name{col_data} +\alias{col_data} +\alias{col_data.logical} +\alias{col_data.numeric} +\alias{col_data.Date} +\alias{col_data.POSIXct} +\alias{col_data.character} +\title{Column data} +\usage{ +col_data(x, ...) + +\method{col_data}{logical}(x, ...) + +\method{col_data}{numeric}(x, ..., sigfig = 3, sci_threshold = 15) + +\method{col_data}{Date}(x, ...) + +\method{col_data}{POSIXct}(x, ...) + +\method{col_data}{character}(x, ..., width = NA) +} +\arguments{ +\item{x}{A vector to format} + +\item{...}{Other arguments passed to methods} + +\item{sigfig}{Minimum number of significant figures to display. Numbers +larger than 1 will potentially show more signficiant figures than this +but they will be greyed out.} + +\item{sci_threshold}{If decimal display is wider than this threshold, +use scientific display instead.} + +\item{width}{Preferred width of output} +} +\description{ +Internal class for formatting the data part of a column. +} +\examples{ +x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) +col_data(x) +col_data(-x) +col_data(runif(10)) +col_data(rcauchy(20)) + +# Special values are highlighted +col_data(c(runif(5), NA, NaN, Inf, -Inf)) + +# Very wide ranges will be displayed in scientific format +col_data(c(1e10, 1e-10)) +col_data(c(1e10, 1e-10), sci_threshold = Inf) + +x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) +col_data(x) + +x <- c("This is string is rather long", NA, "?", "Short") +col_data(x) +col_data(x, width = 30) +col_data(x, width = 5) + +date <- as.Date("2017-05-15") +col_data(date + c(1, NA, 3:5)) +col_data(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) +} diff --git a/man/colformat.Rd b/man/colformat.Rd index 7fdd0bdb3..3ce1e2aad 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -2,38 +2,14 @@ % Please edit documentation in R/colformat.R \name{colformat} \alias{colformat} -\alias{colformat.logical} -\alias{colformat.numeric} -\alias{colformat.Date} -\alias{colformat.POSIXct} -\alias{colformat.character} \title{Format a vector suitable for tabular display} \usage{ colformat(x, ...) - -\method{colformat}{logical}(x, ...) - -\method{colformat}{numeric}(x, ..., sigfig = 3, sci_threshold = 15) - -\method{colformat}{Date}(x, ...) - -\method{colformat}{POSIXct}(x, ...) - -\method{colformat}{character}(x, ..., width = NA) } \arguments{ \item{x}{A vector to format} \item{...}{Other arguments passed to methods} - -\item{sigfig}{Minimum number of significant figures to display. Numbers -larger than 1 will potentially show more signficiant figures than this -but they will be greyed out.} - -\item{sci_threshold}{If decimal display is wider than this threshold, -use scientific display instead.} - -\item{width}{Preferred width of output} } \value{ A character vector with class `colformat` and From 264e9f4d9493670a8653cd14e1cbfce90c55fac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 28 Jul 2017 17:20:08 +0200 Subject: [PATCH 043/133] document --- man/col_data.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/col_data.Rd b/man/col_data.Rd index 2ba5514db..278e5ddca 100644 --- a/man/col_data.Rd +++ b/man/col_data.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/coldata.R +% Please edit documentation in R/col-data.R \name{col_data} \alias{col_data} \alias{col_data.logical} From 7f9c5539bbff2a4d450dac51005cff9ba47e87ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 28 Jul 2017 19:51:49 +0200 Subject: [PATCH 044/133] Add tests for other formats (#17) * test for character * test for logical * test for date * test for time --- tests/testthat/helper-output.R | 3 ++- tests/testthat/out/date.txt | 3 +++ tests/testthat/out/letters-long.txt | 3 +++ tests/testthat/out/letters.txt | 7 +++++++ tests/testthat/out/logical.txt | 4 ++++ tests/testthat/out/time.txt | 3 +++ tests/testthat/test-format_character.R | 6 ++++++ tests/testthat/test-format_date.R | 5 +++++ tests/testthat/test-format_logical.R | 5 +++++ tests/testthat/test-format_time.R | 5 +++++ 10 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/testthat/out/date.txt create mode 100644 tests/testthat/out/letters-long.txt create mode 100644 tests/testthat/out/letters.txt create mode 100644 tests/testthat/out/logical.txt create mode 100644 tests/testthat/out/time.txt create mode 100644 tests/testthat/test-format_character.R create mode 100644 tests/testthat/test-format_date.R create mode 100644 tests/testthat/test-format_logical.R create mode 100644 tests/testthat/test-format_time.R diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 12c4eefd2..51acfb08c 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -1,5 +1,6 @@ expect_colformat_output <- function(x, ..., filename) { - x <- c(x, NA, -Inf, Inf) + x <- c(x, NA) + if (is.numeric(x)) x <- c(x, -Inf, Inf) old <- options(crayon.enabled = TRUE) on.exit(options(old)) diff --git a/tests/testthat/out/date.txt b/tests/testthat/out/date.txt new file mode 100644 index 000000000..dcf859a5e --- /dev/null +++ b/tests/testthat/out/date.txt @@ -0,0 +1,3 @@ + title + 2017-07-28 + ? diff --git a/tests/testthat/out/letters-long.txt b/tests/testthat/out/letters-long.txt new file mode 100644 index 000000000..7894756ad --- /dev/null +++ b/tests/testthat/out/letters-long.txt @@ -0,0 +1,3 @@ +title  +abcdefghijklmnopqrs… +? diff --git a/tests/testthat/out/letters.txt b/tests/testthat/out/letters.txt new file mode 100644 index 000000000..ab0174fe7 --- /dev/null +++ b/tests/testthat/out/letters.txt @@ -0,0 +1,7 @@ +title +a +b +c +d +e +? diff --git a/tests/testthat/out/logical.txt b/tests/testthat/out/logical.txt new file mode 100644 index 000000000..89f66657f --- /dev/null +++ b/tests/testthat/out/logical.txt @@ -0,0 +1,4 @@ +title + * + - + ? diff --git a/tests/testthat/out/time.txt b/tests/testthat/out/time.txt new file mode 100644 index 000000000..275402ff4 --- /dev/null +++ b/tests/testthat/out/time.txt @@ -0,0 +1,3 @@ + title +2017-07-28 18:04:35 + ? diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R new file mode 100644 index 000000000..4b7bd0c57 --- /dev/null +++ b/tests/testthat/test-format_character.R @@ -0,0 +1,6 @@ +context("format_character") + +test_that("output test", { + expect_colformat_output(letters[1:5], filename = "letters.txt") + expect_colformat_output(paste(letters, collapse = ""), filename = "letters-long.txt") +}) diff --git a/tests/testthat/test-format_date.R b/tests/testthat/test-format_date.R new file mode 100644 index 000000000..220795272 --- /dev/null +++ b/tests/testthat/test-format_date.R @@ -0,0 +1,5 @@ +context("format_date") + +test_that("output test", { + expect_colformat_output(as.Date("2017-07-28"), filename = "date.txt") +}) diff --git a/tests/testthat/test-format_logical.R b/tests/testthat/test-format_logical.R new file mode 100644 index 000000000..4af64ab32 --- /dev/null +++ b/tests/testthat/test-format_logical.R @@ -0,0 +1,5 @@ +context("format_logical") + +test_that("output test", { + expect_colformat_output(c(TRUE, FALSE), filename = "logical.txt") +}) diff --git a/tests/testthat/test-format_time.R b/tests/testthat/test-format_time.R new file mode 100644 index 000000000..08f33f48e --- /dev/null +++ b/tests/testthat/test-format_time.R @@ -0,0 +1,5 @@ +context("format_time") + +test_that("output test", { + expect_colformat_output(as.POSIXct("2017-07-28 18:04:35 +0200"), filename = "time.txt") +}) From 5d8b6c37236a91211c0ca329b2f492174875365e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 28 Jul 2017 22:32:08 +0200 Subject: [PATCH 045/133] Corrected test output (#18) * 16 colors, independent of environment * black on yellow --- R/spark-bar.R | 2 +- R/styles.R | 10 +++++----- tests/testthat/helper-output.R | 9 +++++++-- tests/testthat/out/basic.txt | 6 +++--- tests/testthat/out/date.txt | 2 +- tests/testthat/out/decimal-insignif.txt | 6 +++--- tests/testthat/out/letters-long.txt | 2 +- tests/testthat/out/letters.txt | 2 +- tests/testthat/out/logical.txt | 2 +- tests/testthat/out/scientific-short-neg.txt | 6 +++--- tests/testthat/out/scientific.txt | 6 +++--- tests/testthat/out/time.txt | 2 +- 12 files changed, 30 insertions(+), 25 deletions(-) diff --git a/R/spark-bar.R b/R/spark-bar.R index 86e577c05..402653250 100644 --- a/R/spark-bar.R +++ b/R/spark-bar.R @@ -34,7 +34,7 @@ spark_bar <- function(x, safe = TRUE) { include.lowest = TRUE ) chars <- as.character(factor) - chars[is.na(chars)] <- crayon::style(bars[length(bars)], colour_na()) + chars[is.na(chars)] <- style_spark_na(bars[length(bars)]) structure(paste0(chars, collapse = ""), class = "spark") } diff --git a/R/styles.R b/R/styles.R index 464edf953..b0e900dd8 100644 --- a/R/styles.R +++ b/R/styles.R @@ -16,13 +16,13 @@ style_subtle <- keep_empty(function(x) { style_grey(0.6, x) }) -colour_na <- function() { - grDevices::rgb(5, 5, 2, maxColorValue = 5) +style_spark_na <- function(x) { + crayon::yellow(x) } -style_na <- keep_empty(function(x) { - crayon::style(x, bg = colour_na()) -}) +style_na <- function(x) { + crayon::bgYellow(crayon::black(x)) +} style_neg <- keep_empty(function(x) { crayon::red(x) diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 51acfb08c..82700f0ff 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -2,8 +2,13 @@ expect_colformat_output <- function(x, ..., filename) { x <- c(x, NA) if (is.numeric(x)) x <- c(x, -Inf, Inf) - old <- options(crayon.enabled = TRUE) - on.exit(options(old)) + old <- options(crayon.enabled = TRUE, crayon.colors = 16L) + crayon::num_colors(forget = TRUE) + + on.exit({ + options(old) + crayon::num_colors(forget = TRUE) + }) expect_output_file(print(colformat(x, ...)), file.path("out", filename), update = TRUE) } diff --git a/tests/testthat/out/basic.txt b/tests/testthat/out/basic.txt index 120547b2a..09e371567 100644 --- a/tests/testthat/out/basic.txt +++ b/tests/testthat/out/basic.txt @@ -7,6 +7,6 @@ 100 - 1000 10000 - NA -- Inf - Inf + NA +- Inf + Inf diff --git a/tests/testthat/out/date.txt b/tests/testthat/out/date.txt index dcf859a5e..575315a47 100644 --- a/tests/testthat/out/date.txt +++ b/tests/testthat/out/date.txt @@ -1,3 +1,3 @@  title 2017-07-28 - ? + ? diff --git a/tests/testthat/out/decimal-insignif.txt b/tests/testthat/out/decimal-insignif.txt index 368de7eed..47491b5db 100644 --- a/tests/testthat/out/decimal-insignif.txt +++ b/tests/testthat/out/decimal-insignif.txt @@ -6,6 +6,6 @@ 12.3 123 1235 - NA -- Inf - Inf + NA +- Inf + Inf diff --git a/tests/testthat/out/letters-long.txt b/tests/testthat/out/letters-long.txt index 7894756ad..5496c09e7 100644 --- a/tests/testthat/out/letters-long.txt +++ b/tests/testthat/out/letters-long.txt @@ -1,3 +1,3 @@ title  abcdefghijklmnopqrs… -? +? diff --git a/tests/testthat/out/letters.txt b/tests/testthat/out/letters.txt index ab0174fe7..3986f42ae 100644 --- a/tests/testthat/out/letters.txt +++ b/tests/testthat/out/letters.txt @@ -4,4 +4,4 @@ b c d e -? +? diff --git a/tests/testthat/out/logical.txt b/tests/testthat/out/logical.txt index 89f66657f..6e3fd7636 100644 --- a/tests/testthat/out/logical.txt +++ b/tests/testthat/out/logical.txt @@ -1,4 +1,4 @@ title * - - ? + ? diff --git a/tests/testthat/out/scientific-short-neg.txt b/tests/testthat/out/scientific-short-neg.txt index a7fd33e78..094c04164 100644 --- a/tests/testthat/out/scientific-short-neg.txt +++ b/tests/testthat/out/scientific-short-neg.txt @@ -3,6 +3,6 @@ 1.00e ⁹ - 1.00e¹⁵ 1.00e²² - NA   --Inf   - Inf   + NA   +-Inf   + Inf   diff --git a/tests/testthat/out/scientific.txt b/tests/testthat/out/scientific.txt index 327bdbd57..4026420a1 100644 --- a/tests/testthat/out/scientific.txt +++ b/tests/testthat/out/scientific.txt @@ -3,6 +3,6 @@ 1.00e⁻⁶ 1.00e⁺³ 1.00e⁺⁹ - NA   --Inf   - Inf   + NA   +-Inf   + Inf   diff --git a/tests/testthat/out/time.txt b/tests/testthat/out/time.txt index 275402ff4..3e8185be0 100644 --- a/tests/testthat/out/time.txt +++ b/tests/testthat/out/time.txt @@ -1,3 +1,3 @@  title 2017-07-28 18:04:35 - ? + ? From 85cf585d1d6632f7513810e0809303dabd4074e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 30 Jul 2017 10:24:57 +0200 Subject: [PATCH 046/133] Flexible width (#20) * 16 colors, independent of environment * black on yellow * copy type_sum() from tibble * collect type and title in colformat * implement col_type * add format.col_type() * use title from colformat object * always print type * obtain width * new_column() records alignment instead of applying it * store width in attribute * add cast * don't need max() * get_width() et al. * format.colformat() gains width argument * colformat() re-gains width argument * scientific notation is selected only if width is insufficient * dynamic adaptation of column widths CC @hadley * document * no column title by default * formatting of title * get rid of now broken examples * rename * remove unused argument * document args * import methods * simplify * show column type in grey * underscores instead of space * titles are ticked * up example --- DESCRIPTION | 1 + NAMESPACE | 11 ++ R/col-data.R | 82 ++++------ R/colformat.R | 47 ++++-- R/column.R | 13 +- R/compat-purrr.R | 162 ++++++++++++++++++++ R/scientific.R | 7 +- R/sigfig.R | 44 ++++-- R/styles.R | 5 +- R/tick.R | 16 ++ R/title.R | 28 ++++ R/type-sum.R | 69 +++++++++ R/utils.R | 5 +- R/width.R | 27 ++++ man/col_data.Rd | 35 +---- man/colformat.Rd | 12 +- man/format_decimal.Rd | 4 +- man/type_sum.Rd | 16 ++ tests/testthat/helper-output.R | 21 ++- tests/testthat/out/basic.txt | 2 +- tests/testthat/out/date.txt | 2 +- tests/testthat/out/decimal-insignif.txt | 2 +- tests/testthat/out/letters-long-03.txt | 3 + tests/testthat/out/letters-long-10.txt | 3 + tests/testthat/out/letters-long.txt | 6 +- tests/testthat/out/letters.txt | 2 +- tests/testthat/out/logical.txt | 2 +- tests/testthat/out/numeric-04.txt | 5 + tests/testthat/out/numeric-07.txt | 5 + tests/testthat/out/numeric-10.txt | 5 + tests/testthat/out/numeric-15.txt | 5 + tests/testthat/out/numeric-22.txt | 5 + tests/testthat/out/scientific-short-neg.txt | 2 +- tests/testthat/out/scientific.txt | 2 +- tests/testthat/out/time.txt | 2 +- tests/testthat/out/title-crayon.txt | 11 ++ tests/testthat/out/title-longer.txt | 11 ++ tests/testthat/out/title-none.txt | 10 ++ tests/testthat/out/title-short.txt | 11 ++ tests/testthat/out/title-too-long.txt | 11 ++ tests/testthat/test-format_character.R | 2 + tests/testthat/test-format_numeric.R | 13 ++ tests/testthat/test-format_scientific.R | 15 +- tests/testthat/test-ticks.R | 18 +++ tests/testthat/test-title.R | 9 ++ 45 files changed, 610 insertions(+), 159 deletions(-) create mode 100644 R/compat-purrr.R create mode 100644 R/tick.R create mode 100644 R/title.R create mode 100644 R/type-sum.R create mode 100644 R/width.R create mode 100644 man/type_sum.Rd create mode 100644 tests/testthat/out/letters-long-03.txt create mode 100644 tests/testthat/out/letters-long-10.txt create mode 100644 tests/testthat/out/numeric-04.txt create mode 100644 tests/testthat/out/numeric-07.txt create mode 100644 tests/testthat/out/numeric-10.txt create mode 100644 tests/testthat/out/numeric-15.txt create mode 100644 tests/testthat/out/numeric-22.txt create mode 100644 tests/testthat/out/title-crayon.txt create mode 100644 tests/testthat/out/title-longer.txt create mode 100644 tests/testthat/out/title-none.txt create mode 100644 tests/testthat/out/title-short.txt create mode 100644 tests/testthat/out/title-too-long.txt create mode 100644 tests/testthat/test-format_numeric.R create mode 100644 tests/testthat/test-ticks.R create mode 100644 tests/testthat/test-title.R diff --git a/DESCRIPTION b/DESCRIPTION index bf3bdedfb..dbd92528e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -14,6 +14,7 @@ URL: https://github.com/hadley/colformat BugReports: https://github.com/hadley/colformat/issues Imports: crayon, + methods, rlang RoxygenNote: 6.0.1 Suggests: testthat diff --git a/NAMESPACE b/NAMESPACE index 7c261e73a..3cf28a39c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,8 @@ S3method(col_data,character) S3method(col_data,logical) S3method(col_data,numeric) S3method(format,col_data) +S3method(format,col_title) +S3method(format,col_type) S3method(format,colformat) S3method(format,decimal_format) S3method(print,col_data) @@ -13,10 +15,19 @@ S3method(print,colformat) S3method(print,column) S3method(print,decimal_format) S3method(print,spark) +S3method(type_sum,Date) +S3method(type_sum,POSIXt) +S3method(type_sum,data.frame) +S3method(type_sum,default) +S3method(type_sum,difftime) +S3method(type_sum,factor) +S3method(type_sum,ordered) +S3method(type_sum,tbl_df) export(col_data) export(colformat) export(format_decimal) export(format_scientific) export(spark_bar) export(spark_line) +export(type_sum) import(rlang) diff --git a/R/col-data.R b/R/col-data.R index bf1057752..90a69b9ea 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -5,55 +5,38 @@ #' @param x A vector to format #' @param ... Other arguments passed to methods #' @export -#' @examples -#' x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) -#' col_data(x) -#' col_data(-x) -#' col_data(runif(10)) -#' col_data(rcauchy(20)) -#' -#' # Special values are highlighted -#' col_data(c(runif(5), NA, NaN, Inf, -Inf)) -#' -#' # Very wide ranges will be displayed in scientific format -#' col_data(c(1e10, 1e-10)) -#' col_data(c(1e10, 1e-10), sci_threshold = Inf) -#' -#' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) -#' col_data(x) -#' -#' x <- c("This is string is rather long", NA, "?", "Short") -#' col_data(x) -#' col_data(x, width = 30) -#' col_data(x, width = 5) -#' -#' date <- as.Date("2017-05-15") -#' col_data(date + c(1, NA, 3:5)) -#' col_data(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) col_data <- function(x, ...) { UseMethod("col_data") } #' @export -format.col_data <- function(x, title = "title", ...) { +format.col_data <- function(x, width, ...) { align <- attr(x, "align") - width <- max(nchar(title), attr(x, "width")) + desired_width <- get_width(x) + if (width < desired_width) { + data <- str_trunc(x, width) + } else { + data <- x + } - new_column(x, title = title, width = width, align = attr(x, "align")) + new_column(data, width = width, align = align) } #' @export -print.col_data <- function(x, title = "title", ...) { - print(format(x, title = title, ...)) +print.col_data <- function(x, ...) { + print(format(x, ...)) } -new_col_data <- function(x, width = max(crayon::col_nchar(x)), align = "left") { - structure( +new_col_data <- function(x, width = max(crayon::col_nchar(x)), align = "left", + min_width = NULL) { + ret <- structure( x, - width = width, align = align, class = "col_data" ) + ret <- set_width(ret, width) + ret <- set_min_width(ret, min_width) + ret } # Methods ----------------------------------------------------------------- @@ -74,18 +57,18 @@ col_data.logical <- function(x, ...) { #' @param sigfig Minimum number of significant figures to display. Numbers #' larger than 1 will potentially show more signficiant figures than this #' but they will be greyed out. -#' @param sci_threshold If decimal display is wider than this threshold, -#' use scientific display instead. -col_data.numeric <- function(x, ..., sigfig = 3, sci_threshold = 15) { - dec <- format_decimal(x, sigfig = sigfig) - - # This is somewhat inefficient but we can fix if it becomes a bottleneck - width <- attr(format(dec), "width") - if (width <= sci_threshold) { - dec - } else { - format_scientific(x, sigfig = sigfig) - } +col_data.numeric <- function(x, ..., sigfig = 3) { + dec <- format_decimal(x, ..., sigfig = sigfig) + sci <- format_scientific(x, ..., sigfig = sigfig) + + ret <- structure( + list(dec = dec, sci = sci), + class = "decimal_format" + ) + + ret <- set_width(ret, max(get_widths(ret))) + ret <- set_min_width(ret, min(get_min_widths(ret))) + ret } #' @export @@ -111,16 +94,13 @@ col_data.POSIXct <- function(x, ...) { #' @export -#' @param width Preferred width of output #' @rdname col_data -col_data.character <- function(x, ..., width = NA) { - if (is.na(width)) { - width <- pmin(max(nchar(x), na.rm = TRUE), 20) - } +col_data.character <- function(x, ...) { + width <- max(nchar(x, type = "width"), na.rm = TRUE) x <- encodeString(x, na.encode = FALSE) out <- str_trunc(x, width = width) out[is.na(out)] <- col_na() - new_col_data(out, width = width, align = "left") + new_col_data(out, width = width, align = "left", min_width = min(width, 3L)) } diff --git a/R/colformat.R b/R/colformat.R index 3ac4d976e..c28b42f46 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -1,9 +1,9 @@ #' Format a vector suitable for tabular display #' #' @param x A vector to format +#' @param title An optional title for the column +#' @param width Default width, optional #' @param ... Other arguments passed to methods -#' @return A character vector with class `colformat` and -#' `width` and `align` attributes. #' @export #' @examples #' x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) @@ -16,8 +16,8 @@ #' colformat(c(runif(5), NA, NaN, Inf, -Inf)) #' #' # Very wide ranges will be displayed in scientific format +#' colformat(c(1e10, 1e-10), width = 20) #' colformat(c(1e10, 1e-10)) -#' colformat(c(1e10, 1e-10), sci_threshold = Inf) #' #' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) #' colformat(x) @@ -30,20 +30,47 @@ #' date <- as.Date("2017-05-15") #' colformat(date + c(1, NA, 3:5)) #' colformat(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) -colformat <- function(x, ...) { +colformat <- function(x, title = NULL, width = NULL, ...) { + title <- col_title(title, ...) + type <- col_type(x, ...) data <- col_data(x, ...) - structure( - list(data = data), + ret <- structure( + list(title = title, type = type, data = data), class = "colformat" ) + ret <- set_width(ret, width) + ret } #' @export -format.colformat <- function(x, title = "title", ...) { - format(x$data, title = title, ...) +format.colformat <- function(x, width = NULL, ...) { + if (is.null(width)) { + width <- get_width(x) + } + + if (is.null(width)) { + widths <- get_widths(x) + width <- max(widths) + } + + min_widths <- max(get_min_widths(x)) + if (width < min_widths) width <- min_widths + + title_format <- format(x$title, width = width, ...) + type_format <- format(x$type, width = width, ...) + data_format <- format(x$data, width = width, ...) + align <- attr(data_format, "align") + + col_data <- c(title_format, type_format, data_format) + + new_column( + crayon::col_align(col_data, width = width, align = align), + width, + align = "left" + ) } #' @export -print.colformat <- function(x, title = "title", ...) { - print(format(x, title = title, ...)) +print.colformat <- function(x, ...) { + print(format(x, ...)) } diff --git a/R/column.R b/R/column.R index b19415f31..3ee2b6f2c 100644 --- a/R/column.R +++ b/R/column.R @@ -1,17 +1,14 @@ -new_column <- function(row, title, width, align = "right") { - row <- col_align(row, width = width, align = align) - title <- col_align(title, width = width, align = align) - - structure( +new_column <- function(row, width = NULL, align = NULL) { + ret <- structure( row, - title = title, - width = width, + align = align, class = "column" ) + ret <- set_width(ret, width) + ret } #' @export print.column <- function(x, ...) { - cat_line(crayon::bold(attr(x, "title"))) cat_line(paste(x, collapse = "\n")) } diff --git a/R/compat-purrr.R b/R/compat-purrr.R new file mode 100644 index 000000000..7ec8f41c5 --- /dev/null +++ b/R/compat-purrr.R @@ -0,0 +1,162 @@ +# nocov start - compat-purrr (last updated: rlang 0.1.9000) + +# This file serves as a reference for compatibility functions for +# purrr. They are not drop-in replacements but allow a similar style +# of programming. This is useful in cases where purrr is too heavy a +# package to depend on. Please find the most recent version in rlang's +# repository. + +map <- function(.x, .f, ...) { + lapply(.x, .f, ...) +} +map_mold <- function(.x, .f, .mold, ...) { + out <- vapply(.x, .f, .mold, ..., USE.NAMES = FALSE) + names(out) <- names(.x) + out +} +map_lgl <- function(.x, .f, ...) { + map_mold(.x, .f, logical(1), ...) +} +map_int <- function(.x, .f, ...) { + map_mold(.x, .f, integer(1), ...) +} +map_dbl <- function(.x, .f, ...) { + map_mold(.x, .f, double(1), ...) +} +map_chr <- function(.x, .f, ...) { + map_mold(.x, .f, character(1), ...) +} +map_cpl <- function(.x, .f, ...) { + map_mold(.x, .f, complex(1), ...) +} + +pluck <- function(.x, .f) { + map(.x, `[[`, .f) +} +pluck_lgl <- function(.x, .f) { + map_lgl(.x, `[[`, .f) +} +pluck_int <- function(.x, .f) { + map_int(.x, `[[`, .f) +} +pluck_dbl <- function(.x, .f) { + map_dbl(.x, `[[`, .f) +} +pluck_chr <- function(.x, .f) { + map_chr(.x, `[[`, .f) +} +pluck_cpl <- function(.x, .f) { + map_cpl(.x, `[[`, .f) +} + +map2 <- function(.x, .y, .f, ...) { + Map(.f, .x, .y, ...) +} +map2_lgl <- function(.x, .y, .f, ...) { + as.vector(map2(.x, .y, .f, ...), "logical") +} +map2_int <- function(.x, .y, .f, ...) { + as.vector(map2(.x, .y, .f, ...), "integer") +} +map2_dbl <- function(.x, .y, .f, ...) { + as.vector(map2(.x, .y, .f, ...), "double") +} +map2_chr <- function(.x, .y, .f, ...) { + as.vector(map2(.x, .y, .f, ...), "character") +} +map2_cpl <- function(.x, .y, .f, ...) { + as.vector(map2(.x, .y, .f, ...), "complex") +} + +args_recycle <- function(args) { + lengths <- map_int(args, length) + n <- max(lengths) + + stopifnot(all(lengths == 1L | lengths == n)) + to_recycle <- lengths == 1L + args[to_recycle] <- map(args[to_recycle], function(x) rep.int(x, n)) + + args +} +pmap <- function(.l, .f, ...) { + args <- args_recycle(.l) + do.call("mapply", c( + FUN = list(quote(.f)), + args, MoreArgs = quote(list(...)), + SIMPLIFY = FALSE, USE.NAMES = FALSE + )) +} + +probe <- function(.x, .p, ...) { + if (is_logical(.p)) { + stopifnot(length(.p) == length(.x)) + .p + } else { + map_lgl(.x, .p, ...) + } +} + +keep <- function(.x, .f, ...) { + .x[probe(.x, .f, ...)] +} +discard <- function(.x, .p, ...) { + sel <- probe(.x, .p, ...) + .x[is.na(sel) | !sel] +} +map_if <- function(.x, .p, .f, ...) { + matches <- probe(.x, .p) + .x[matches] <- map(.x[matches], .f, ...) + .x +} + +compact <- function(.x) { + Filter(length, .x) +} + +transpose <- function(.l) { + inner_names <- names(.l[[1]]) + if (is.null(inner_names)) { + fields <- seq_along(.l[[1]]) + } else { + fields <- set_names(inner_names) + } + + map(fields, function(i) { + map(.l, .subset2, i) + }) +} + +every <- function(.x, .p, ...) { + for (i in seq_along(.x)) { + if (!rlang::is_true(.p(.x[[i]], ...))) return(FALSE) + } + TRUE +} +some <- function(.x, .p, ...) { + for (i in seq_along(.x)) { + if (rlang::is_true(.p(.x[[i]], ...))) return(TRUE) + } + FALSE +} +negate <- function(.p) { + function(...) !.p(...) +} + +reduce <- function(.x, .f, ..., .init) { + f <- function(x, y) .f(x, y, ...) + Reduce(f, .x, init = .init) +} +reduce_right <- function(.x, .f, ..., .init) { + f <- function(x, y) .f(y, x, ...) + Reduce(f, .x, init = .init, right = TRUE) +} +accumulate <- function(.x, .f, ..., .init) { + f <- function(x, y) .f(x, y, ...) + Reduce(f, .x, init = .init, accumulate = TRUE) +} +accumulate_right <- function(.x, .f, ..., .init) { + f <- function(x, y) .f(y, x, ...) + Reduce(f, .x, init = .init, right = TRUE, accumulate = TRUE) +} + +# nocov end diff --git a/R/scientific.R b/R/scientific.R index cbd0f5987..9a065d494 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -10,12 +10,7 @@ #' x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) #' format_scientific(x) format_scientific <- function(x, sigfig = 3, superscript = TRUE) { - s <- split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) - - structure( - s, - class = "decimal_format" - ) + split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) } format_exp <- function(x) { diff --git a/R/sigfig.R b/R/sigfig.R index 1e76786fe..27280f46f 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -13,6 +13,7 @@ #' #' @param x A numeric vector #' @param sigfig Number of signficiant figures to display. +#' @param ... Ignored #' @export #' @examples #' x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) @@ -23,13 +24,8 @@ #' #' format_decimal(c(Inf, -Inf, NA, NaN), 3) #' format_decimal(c(1e10, 1e-10), 3) -format_decimal <- function(x, sigfig = 3) { - s <- split_decimal(x, sigfig) - - structure( - s, - class = "decimal_format" - ) +format_decimal <- function(x, sigfig = 3, ...) { + split_decimal(x, sigfig) } split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { @@ -60,7 +56,7 @@ split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { lhs <- trunc(round_x) rhs <- round_x - lhs - list( + ret <- list( sigfig = sigfig, num = num, neg = neg, @@ -72,6 +68,8 @@ split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { exp = exp_display, superscript = superscript ) + + set_width(ret, max(crayon::col_nchar(assemble_decimal(ret)))) } compute_rhs_digits <- function(x, sigfig) { @@ -167,21 +165,37 @@ style_num <- function(x, negative, subtle = rep_along(x, FALSE)) { ifelse(subtle, style_subtle(x), ifelse(negative, style_neg(x), x)) } -#' @export -format.decimal_format <- function(x, title = "title", ...) { +assemble_decimal <- function(x) { neg <- format_neg(x) lhs <- format_lhs(x) dec <- format_dec(x) rhs <- format_rhs(x) exp <- format_exp(x) - row <- paste0(neg, lhs, dec, rhs, exp) - width <- max(nchar(title), crayon::col_nchar(row)) + paste0(neg, lhs, dec, rhs, exp) +} + +#' @export +format.decimal_format <- function(x, width, ...) { + if (width < get_min_width(x)) { + stop( + "Need at least width ", get_min_width(x), " requested ", width, ".", + call = FALSE + ) + } + + if (width >= get_width(x$dec)) { + row <- assemble_decimal(x$dec) + } else { + row <- assemble_decimal(x$sci) + } - new_column(row, title = title, width = width, align = "right") + used_width <- max(crayon::col_nchar(row)) + row <- paste0(strrep(" ", width - used_width), row) + new_column(row, width = width, align = "right") } #' @export -print.decimal_format <- function(x, title = "title", ...) { - print(format(x, title = title, ...)) +print.decimal_format <- function(x, ...) { + print(format(x, ...)) } diff --git a/R/styles.R b/R/styles.R index b0e900dd8..687712ecd 100644 --- a/R/styles.R +++ b/R/styles.R @@ -1,9 +1,8 @@ keep_empty <- function(fun) { function(x) { ret <- rep_along(x, "") - update <- is.na(x) | x != "" - x <- x[update] - ret[update] <- fun(x) + update <- which(is.na(x) | x != "") + ret[update] <- fun(x[update]) ret } } diff --git a/R/tick.R b/R/tick.R new file mode 100644 index 000000000..89e19256d --- /dev/null +++ b/R/tick.R @@ -0,0 +1,16 @@ +format_title <- function(x, width) { + needs_ticks <- !is_syntactic(x) + x[needs_ticks] <- tick(x[needs_ticks]) + str_trunc(x, width) +} + +is_syntactic <- function(x) { + ret <- make.names(x) == x + ret[is.na(x)] <- FALSE + ret +} + +tick <- function(x) { + x[is.na(x)] <- "NA" + encodeString(x, quote = "`") +} diff --git a/R/title.R b/R/title.R new file mode 100644 index 000000000..c41aed4b1 --- /dev/null +++ b/R/title.R @@ -0,0 +1,28 @@ +col_title <- function(title, ...) { + ret <- structure( + list( + title = title + ), + class = "col_title" + ) + + if (is.null(title)) { + width <- 0L + } else { + width <- nchar(format_title(title, width = Inf), "width") + } + ret <- set_width(ret, width) + ret <- set_min_width(ret, 3L) + ret +} + +#' @export +format.col_title <- function(x, width, ...) { + title <- x$title + if (is.null(title)) return(character()) + + desired_width <- get_width(x) + title <- format_title(title, width) + + crayon::bold(title) +} diff --git a/R/type-sum.R b/R/type-sum.R new file mode 100644 index 000000000..079fab232 --- /dev/null +++ b/R/type-sum.R @@ -0,0 +1,69 @@ +style_type <- function(x) { + style_subtle(x) +} + +col_type <- function(x, ...) { + type <- type_sum(x) + ret <- structure( + list( + type = type + ), + class = "col_type" + ) + ret <- set_width(ret, width = nchar(type, type = "width") + 2L) + ret <- set_min_width(ret, 3L) + ret +} + +#' @export +format.col_type <- function(x, width = NULL, ...) { + if (is.null(width) || width >= get_width(x)) type <- x$type + else type <- substr(x$type, 1, width - 2) + style_type(paste0("<", type, ">")) +} + +#' Provide a succinct summary of an object +#' +#' @description +#' `type_sum()` gives a brief summary of object type. Objects that commonly +#' occur in a data frame should return a string with four or less characters. +#' +#' @param x an object to summarise. Generally only methods of atomic vectors +#' and variants have been implemented. +#' +#' @export +type_sum <- function(x) UseMethod("type_sum") + +#' @export +type_sum.ordered <- function(x) "ord" +#' @export +type_sum.factor <- function(x) "fctr" +#' @export +type_sum.POSIXt <- function(x) "dttm" +#' @export +type_sum.difftime <- function(x) "time" +#' @export +type_sum.Date <- function(x) "date" +#' @export +type_sum.data.frame <- function(x) class(x)[[1]] +#' @export +type_sum.tbl_df <- function(x) "tibble" +#' @export +type_sum.default <- function(x) { + if (!is.object(x)) { + switch(typeof(x), + logical = "lgl", + integer = "int", + double = "dbl", + character = "chr", + complex = "cplx", + closure = "fun", + environment = "env", + typeof(x) + ) + } else if (!isS4(x)) { + paste0("S3: ", class(x)[[1]]) + } else { + paste0("S4: ", methods::is(x)[[1]]) + } +} diff --git a/R/utils.R b/R/utils.R index 1a5e482c9..bcfeae541 100644 --- a/R/utils.R +++ b/R/utils.R @@ -10,8 +10,8 @@ col_align <- function(x, width, align) { str_trunc <- function(x, width = 20) { str_width <- nchar(x, type = "width") - too_long <- !is.na(x) & str_width > width - x[too_long] <- paste0(substr(x[too_long], 1, width - 1), "\u2026") + too_wide <- !is.na(x) & str_width > width + x[too_wide] <- paste0(substr(x[too_wide], 1, width - 1), "\u2026") x } @@ -36,4 +36,3 @@ ruler <- function(width = getOption("width")) { cat(y, "\n", sep = "") cat(x %% 10, "\n", sep = "") } - diff --git a/R/width.R b/R/width.R new file mode 100644 index 000000000..de8ec3522 --- /dev/null +++ b/R/width.R @@ -0,0 +1,27 @@ +get_width <- function(x) { + attr(x, "width") +} + +set_width <- function(x, width) { + if (is.null(width)) return(x) + attr(x, "width") <- as.integer(width) + x +} + +get_widths <- function(x) { + map_int(x, get_width) +} + +get_min_width <- function(x) { + attr(x, "min_width") %||% get_width(x) +} + +set_min_width <- function(x, min_width) { + if (is.null(min_width)) return(x) + attr(x, "min_width") <- as.integer(min_width) + x +} + +get_min_widths <- function(x) { + map_int(x, get_min_width) +} diff --git a/man/col_data.Rd b/man/col_data.Rd index 278e5ddca..528158bc4 100644 --- a/man/col_data.Rd +++ b/man/col_data.Rd @@ -13,13 +13,13 @@ col_data(x, ...) \method{col_data}{logical}(x, ...) -\method{col_data}{numeric}(x, ..., sigfig = 3, sci_threshold = 15) +\method{col_data}{numeric}(x, ..., sigfig = 3) \method{col_data}{Date}(x, ...) \method{col_data}{POSIXct}(x, ...) -\method{col_data}{character}(x, ..., width = NA) +\method{col_data}{character}(x, ...) } \arguments{ \item{x}{A vector to format} @@ -29,38 +29,7 @@ col_data(x, ...) \item{sigfig}{Minimum number of significant figures to display. Numbers larger than 1 will potentially show more signficiant figures than this but they will be greyed out.} - -\item{sci_threshold}{If decimal display is wider than this threshold, -use scientific display instead.} - -\item{width}{Preferred width of output} } \description{ Internal class for formatting the data part of a column. } -\examples{ -x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) -col_data(x) -col_data(-x) -col_data(runif(10)) -col_data(rcauchy(20)) - -# Special values are highlighted -col_data(c(runif(5), NA, NaN, Inf, -Inf)) - -# Very wide ranges will be displayed in scientific format -col_data(c(1e10, 1e-10)) -col_data(c(1e10, 1e-10), sci_threshold = Inf) - -x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) -col_data(x) - -x <- c("This is string is rather long", NA, "?", "Short") -col_data(x) -col_data(x, width = 30) -col_data(x, width = 5) - -date <- as.Date("2017-05-15") -col_data(date + c(1, NA, 3:5)) -col_data(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) -} diff --git a/man/colformat.Rd b/man/colformat.Rd index 3ce1e2aad..1776f10b0 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -4,17 +4,17 @@ \alias{colformat} \title{Format a vector suitable for tabular display} \usage{ -colformat(x, ...) +colformat(x, title = NULL, width = NULL, ...) } \arguments{ \item{x}{A vector to format} +\item{title}{An optional title for the column} + +\item{width}{Default width, optional} + \item{...}{Other arguments passed to methods} } -\value{ -A character vector with class `colformat` and - `width` and `align` attributes. -} \description{ Format a vector suitable for tabular display } @@ -29,8 +29,8 @@ colformat(rcauchy(20)) colformat(c(runif(5), NA, NaN, Inf, -Inf)) # Very wide ranges will be displayed in scientific format +colformat(c(1e10, 1e-10), width = 20) colformat(c(1e10, 1e-10)) -colformat(c(1e10, 1e-10), sci_threshold = Inf) x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) colformat(x) diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd index 7b883862f..fd8669a23 100644 --- a/man/format_decimal.Rd +++ b/man/format_decimal.Rd @@ -4,12 +4,14 @@ \alias{format_decimal} \title{Format numbers in decimal notation} \usage{ -format_decimal(x, sigfig = 3) +format_decimal(x, sigfig = 3, ...) } \arguments{ \item{x}{A numeric vector} \item{sigfig}{Number of signficiant figures to display.} + +\item{...}{Ignored} } \value{ A list with at least the following elements: diff --git a/man/type_sum.Rd b/man/type_sum.Rd new file mode 100644 index 000000000..43f6e051a --- /dev/null +++ b/man/type_sum.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/type-sum.R +\name{type_sum} +\alias{type_sum} +\title{Provide a succinct summary of an object} +\usage{ +type_sum(x) +} +\arguments{ +\item{x}{an object to summarise. Generally only methods of atomic vectors +and variants have been implemented.} +} +\description{ +`type_sum()` gives a brief summary of object type. Objects that commonly +occur in a data frame should return a string with four or less characters. +} diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 82700f0ff..131d1b57b 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -1,16 +1,23 @@ -expect_colformat_output <- function(x, ..., filename) { - x <- c(x, NA) - if (is.numeric(x)) x <- c(x, -Inf, Inf) - - old <- options(crayon.enabled = TRUE, crayon.colors = 16L) - crayon::num_colors(forget = TRUE) +expect_colformat_output <- function(x, ..., filename, xp = add_special(x), crayon = TRUE) { + if (crayon) { + old <- options(crayon.enabled = TRUE, crayon.colors = 16L) + crayon::num_colors(forget = TRUE) + } else { + old <- options(crayon.enabled = FALSE) + } on.exit({ options(old) crayon::num_colors(forget = TRUE) }) - expect_output_file(print(colformat(x, ...)), file.path("out", filename), update = TRUE) + expect_output_file(print(colformat(xp, ...)), file.path("out", filename), update = TRUE) +} + +add_special <- function(x) { + x <- c(x, NA) + if (is.numeric(x)) x <- c(x, -Inf, Inf) + x } show_output_in_terminal <- function() { diff --git a/tests/testthat/out/basic.txt b/tests/testthat/out/basic.txt index 09e371567..385ae28d1 100644 --- a/tests/testthat/out/basic.txt +++ b/tests/testthat/out/basic.txt @@ -1,4 +1,4 @@ - title +  - 0.00100 0.0100 - 0.100 diff --git a/tests/testthat/out/date.txt b/tests/testthat/out/date.txt index 575315a47..401645592 100644 --- a/tests/testthat/out/date.txt +++ b/tests/testthat/out/date.txt @@ -1,3 +1,3 @@ - title +  2017-07-28 ? diff --git a/tests/testthat/out/decimal-insignif.txt b/tests/testthat/out/decimal-insignif.txt index 47491b5db..22b6cdfe3 100644 --- a/tests/testthat/out/decimal-insignif.txt +++ b/tests/testthat/out/decimal-insignif.txt @@ -1,4 +1,4 @@ - title +  0.00123 0.0123 0.123 diff --git a/tests/testthat/out/letters-long-03.txt b/tests/testthat/out/letters-long-03.txt new file mode 100644 index 000000000..85ca94055 --- /dev/null +++ b/tests/testthat/out/letters-long-03.txt @@ -0,0 +1,3 @@ + +ab… +[… diff --git a/tests/testthat/out/letters-long-10.txt b/tests/testthat/out/letters-long-10.txt new file mode 100644 index 000000000..2530426ca --- /dev/null +++ b/tests/testthat/out/letters-long-10.txt @@ -0,0 +1,3 @@ + +abcdefghi… +[30… diff --git a/tests/testthat/out/letters-long.txt b/tests/testthat/out/letters-long.txt index 5496c09e7..5f9ab3a70 100644 --- a/tests/testthat/out/letters-long.txt +++ b/tests/testthat/out/letters-long.txt @@ -1,3 +1,3 @@ -title  -abcdefghijklmnopqrs… -? + +abcdefghijklmnopqrstuvwxyz +? diff --git a/tests/testthat/out/letters.txt b/tests/testthat/out/letters.txt index 3986f42ae..0726f81ad 100644 --- a/tests/testthat/out/letters.txt +++ b/tests/testthat/out/letters.txt @@ -1,4 +1,4 @@ -title + a b c diff --git a/tests/testthat/out/logical.txt b/tests/testthat/out/logical.txt index 6e3fd7636..7dadc4b81 100644 --- a/tests/testthat/out/logical.txt +++ b/tests/testthat/out/logical.txt @@ -1,4 +1,4 @@ -title + * - ? diff --git a/tests/testthat/out/numeric-04.txt b/tests/testthat/out/numeric-04.txt new file mode 100644 index 000000000..a208fd0cc --- /dev/null +++ b/tests/testthat/out/numeric-04.txt @@ -0,0 +1,5 @@ +  +1.00e⁻⁹ +1.00e⁻⁶ +1.00e⁺³ +1.00e⁺⁹ diff --git a/tests/testthat/out/numeric-07.txt b/tests/testthat/out/numeric-07.txt new file mode 100644 index 000000000..a208fd0cc --- /dev/null +++ b/tests/testthat/out/numeric-07.txt @@ -0,0 +1,5 @@ +  +1.00e⁻⁹ +1.00e⁻⁶ +1.00e⁺³ +1.00e⁺⁹ diff --git a/tests/testthat/out/numeric-10.txt b/tests/testthat/out/numeric-10.txt new file mode 100644 index 000000000..27e0d170c --- /dev/null +++ b/tests/testthat/out/numeric-10.txt @@ -0,0 +1,5 @@ +  + 1.00e⁻⁹ + 1.00e⁻⁶ + 1.00e⁺³ + 1.00e⁺⁹ diff --git a/tests/testthat/out/numeric-15.txt b/tests/testthat/out/numeric-15.txt new file mode 100644 index 000000000..282cc9aa2 --- /dev/null +++ b/tests/testthat/out/numeric-15.txt @@ -0,0 +1,5 @@ +  + 1.00e⁻⁹ + 1.00e⁻⁶ + 1.00e⁺³ + 1.00e⁺⁹ diff --git a/tests/testthat/out/numeric-22.txt b/tests/testthat/out/numeric-22.txt new file mode 100644 index 000000000..e302755ab --- /dev/null +++ b/tests/testthat/out/numeric-22.txt @@ -0,0 +1,5 @@ +  + 0.00000000100 + 0.00000100 + 1000 +1000000000 diff --git a/tests/testthat/out/scientific-short-neg.txt b/tests/testthat/out/scientific-short-neg.txt index 094c04164..1cb501289 100644 --- a/tests/testthat/out/scientific-short-neg.txt +++ b/tests/testthat/out/scientific-short-neg.txt @@ -1,4 +1,4 @@ - title +  - 1.00e ³ 1.00e ⁹ - 1.00e¹⁵ diff --git a/tests/testthat/out/scientific.txt b/tests/testthat/out/scientific.txt index 4026420a1..346861d25 100644 --- a/tests/testthat/out/scientific.txt +++ b/tests/testthat/out/scientific.txt @@ -1,4 +1,4 @@ - title +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/time.txt b/tests/testthat/out/time.txt index 3e8185be0..18522276b 100644 --- a/tests/testthat/out/time.txt +++ b/tests/testthat/out/time.txt @@ -1,3 +1,3 @@ - title +  2017-07-28 18:04:35 ? diff --git a/tests/testthat/out/title-crayon.txt b/tests/testthat/out/title-crayon.txt new file mode 100644 index 000000000..c5b963cc4 --- /dev/null +++ b/tests/testthat/out/title-crayon.txt @@ -0,0 +1,11 @@ + crayon +  + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out/title-longer.txt b/tests/testthat/out/title-longer.txt new file mode 100644 index 000000000..0d0452f41 --- /dev/null +++ b/tests/testthat/out/title-longer.txt @@ -0,0 +1,11 @@ +somewhat_wider + + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA + - Inf + Inf diff --git a/tests/testthat/out/title-none.txt b/tests/testthat/out/title-none.txt new file mode 100644 index 000000000..ad3ab5170 --- /dev/null +++ b/tests/testthat/out/title-none.txt @@ -0,0 +1,10 @@ + + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out/title-short.txt b/tests/testthat/out/title-short.txt new file mode 100644 index 000000000..899e17848 --- /dev/null +++ b/tests/testthat/out/title-short.txt @@ -0,0 +1,11 @@ + short + + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out/title-too-long.txt b/tests/testthat/out/title-too-long.txt new file mode 100644 index 000000000..57d395e88 --- /dev/null +++ b/tests/testthat/out/title-too-long.txt @@ -0,0 +1,11 @@ +much_too… + + 1.00e¹ + 1.00e² + 1.00e³ + 1.00e⁴ + 1.00e⁵ + 1.00e⁶ + NA +-Inf + Inf diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index 4b7bd0c57..aa7c210a9 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -3,4 +3,6 @@ context("format_character") test_that("output test", { expect_colformat_output(letters[1:5], filename = "letters.txt") expect_colformat_output(paste(letters, collapse = ""), filename = "letters-long.txt") + expect_colformat_output(paste(letters, collapse = ""), width = 10, filename = "letters-long-10.txt") + expect_colformat_output(paste(letters, collapse = ""), width = 3, filename = "letters-long-03.txt") }) diff --git a/tests/testthat/test-format_numeric.R b/tests/testthat/test-format_numeric.R new file mode 100644 index 000000000..f9dcf2c2b --- /dev/null +++ b/tests/testthat/test-format_numeric.R @@ -0,0 +1,13 @@ +context("format_numeric") + +test_that("same colformat at different widths", { + v <- 10 ^ c(-9, -6, 3, 9) + x <- colformat(v) + expect_equal(get_min_width(x$data), 7) + expect_equal(get_width(x$data), 22) + expect_colformat_output(xp = v, width = 4, filename = "numeric-04.txt") + expect_colformat_output(xp = v, width = 7, filename = "numeric-07.txt") + expect_colformat_output(xp = v, width = 10, filename = "numeric-10.txt") + expect_colformat_output(xp = v, width = 15, filename = "numeric-15.txt") + expect_colformat_output(xp = v, width = 22, filename = "numeric-22.txt") +}) diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index 20d416438..4f641d2f4 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -4,20 +4,25 @@ format_scientific_bw <- function(x, ...) { old <- options(crayon.enabled = FALSE) on.exit(options(old)) - format_scientific(x, ...) + ret <- col_data(x, ...) + # Hack: Pretend decimal format requires 100 characters + ret$dec <- set_width(ret$dec, 100) + ret <- set_width(ret, 100) + ret <- set_min_width(ret, min(get_min_widths(ret))) + format(ret, width = get_min_width(ret)) } test_that("negative values displayed correct", { f <- format_scientific_bw(-0.123, superscript = FALSE) - expect_equal(unname(format(format(f))), "-1.23e-1") + expect_equal(unname(format(f)), "-1.23e-1") }) test_that("exponents correct in presence of NA", { f <- format_scientific_bw(c(NA, 1e-5), superscript = FALSE) - expect_equal(unname(format(format(f))), c("NA ", " 1.00e-5")) + expect_equal(unname(format(f)), c("NA ", " 1.00e-5")) }) test_that("output test", { - expect_colformat_output(10 ^ c(-9, -6, 3, 9), filename = "scientific.txt") - expect_colformat_output((10 ^ c(3, 9, 15, 22)) * c(-1, 1), filename = "scientific-short-neg.txt") + expect_colformat_output(10 ^ c(-9, -6, 3, 9), width = 10, filename = "scientific.txt") + expect_colformat_output((10 ^ c(3, 9, 15, 22)) * c(-1, 1), width = 10, filename = "scientific-short-neg.txt") }) diff --git a/tests/testthat/test-ticks.R b/tests/testthat/test-ticks.R new file mode 100644 index 000000000..02240917e --- /dev/null +++ b/tests/testthat/test-ticks.R @@ -0,0 +1,18 @@ +context("ticks") + +test_that("title ticks without width restriction", { + expect_equal(format_title("proper_title", Inf), "proper_title") + expect_equal(format_title("needs ticks", Inf), "`needs ticks`") + expect_equal(format_title("'ticks'", Inf), "`'ticks'`") + expect_equal(format_title("embedded\nnewline", Inf), "`embedded\\nnewline`") +}) + +test_that("title ticks and width", { + expect_equal(format_title("proper_title", 15), "proper_title") + expect_equal(format_title("proper_title", 12), "proper_title") + expect_equal(format_title("proper_title", 10), "proper_ti\u2026") + expect_equal(format_title("a b", 6), "`a b`") + expect_equal(format_title("a b", 5), "`a b`") + expect_equal(format_title("a b", 4), "`a \u2026") + expect_equal(format_title("a b", 3), "`a\u2026") +}) diff --git a/tests/testthat/test-title.R b/tests/testthat/test-title.R new file mode 100644 index 000000000..e96c88230 --- /dev/null +++ b/tests/testthat/test-title.R @@ -0,0 +1,9 @@ +context("title") + +test_that("with and without title", { + expect_colformat_output(10 ^ (1:6), filename = "title-none.txt", crayon = FALSE) + expect_colformat_output(10 ^ (1:6), title = "crayon", filename = "title-crayon.txt") + expect_colformat_output(10 ^ (1:6), title = "short", filename = "title-short.txt", crayon = FALSE) + expect_colformat_output(10 ^ (1:6), title = "somewhat_wider", filename = "title-longer.txt", crayon = FALSE) + expect_colformat_output(10 ^ (1:6), title = "much_too_wide", width = 7, filename = "title-too-long.txt", crayon = FALSE) +}) From 04029b8e7700127f5a4710228c5b1bb1dfdf4340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 30 Jul 2017 10:20:36 +0200 Subject: [PATCH 047/133] rename col_ prefix to cf_, closes #19 --- NAMESPACE | 20 +++++------ R/col-data.R | 48 ++++++++++++------------- R/colformat.R | 10 +++--- R/styles.R | 2 +- R/title.R | 6 ++-- R/type-sum.R | 6 ++-- R/utils.R | 2 +- man/{col_data.Rd => cf_data.Rd} | 26 +++++++------- tests/testthat/test-format_scientific.R | 2 +- 9 files changed, 61 insertions(+), 61 deletions(-) rename man/{col_data.Rd => cf_data.Rd} (56%) diff --git a/NAMESPACE b/NAMESPACE index 3cf28a39c..f53d8f4dc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,16 +1,16 @@ # Generated by roxygen2: do not edit by hand -S3method(col_data,Date) -S3method(col_data,POSIXct) -S3method(col_data,character) -S3method(col_data,logical) -S3method(col_data,numeric) -S3method(format,col_data) -S3method(format,col_title) -S3method(format,col_type) +S3method(cf_data,Date) +S3method(cf_data,POSIXct) +S3method(cf_data,character) +S3method(cf_data,logical) +S3method(cf_data,numeric) +S3method(format,cf_data) +S3method(format,cf_title) +S3method(format,cf_type) S3method(format,colformat) S3method(format,decimal_format) -S3method(print,col_data) +S3method(print,cf_data) S3method(print,colformat) S3method(print,column) S3method(print,decimal_format) @@ -23,7 +23,7 @@ S3method(type_sum,difftime) S3method(type_sum,factor) S3method(type_sum,ordered) S3method(type_sum,tbl_df) -export(col_data) +export(cf_data) export(colformat) export(format_decimal) export(format_scientific) diff --git a/R/col-data.R b/R/col-data.R index 90a69b9ea..9ff58394b 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -5,12 +5,12 @@ #' @param x A vector to format #' @param ... Other arguments passed to methods #' @export -col_data <- function(x, ...) { - UseMethod("col_data") +cf_data <- function(x, ...) { + UseMethod("cf_data") } #' @export -format.col_data <- function(x, width, ...) { +format.cf_data <- function(x, width, ...) { align <- attr(x, "align") desired_width <- get_width(x) if (width < desired_width) { @@ -23,16 +23,16 @@ format.col_data <- function(x, width, ...) { } #' @export -print.col_data <- function(x, ...) { +print.cf_data <- function(x, ...) { print(format(x, ...)) } -new_col_data <- function(x, width = max(crayon::col_nchar(x)), align = "left", +new_cf_data <- function(x, width = max(crayon::col_nchar(x)), align = "left", min_width = NULL) { ret <- structure( x, align = align, - class = "col_data" + class = "cf_data" ) ret <- set_width(ret, width) ret <- set_min_width(ret, min_width) @@ -42,22 +42,22 @@ new_col_data <- function(x, width = max(crayon::col_nchar(x)), align = "left", # Methods ----------------------------------------------------------------- #' @export -#' @rdname col_data -col_data.logical <- function(x, ...) { +#' @rdname cf_data +cf_data.logical <- function(x, ...) { out <- character(length(x)) out[x & !is.na(x)] <- style_accent("*") out[!x & !is.na(x)] <- style_subtle("-") - out[is.na(x)] <- col_na() + out[is.na(x)] <- cf_na() - new_col_data(out, width = 1, align = "right") + new_cf_data(out, width = 1, align = "right") } #' @export -#' @rdname col_data +#' @rdname cf_data #' @param sigfig Minimum number of significant figures to display. Numbers #' larger than 1 will potentially show more signficiant figures than this #' but they will be greyed out. -col_data.numeric <- function(x, ..., sigfig = 3) { +cf_data.numeric <- function(x, ..., sigfig = 3) { dec <- format_decimal(x, ..., sigfig = sigfig) sci <- format_scientific(x, ..., sigfig = sigfig) @@ -72,35 +72,35 @@ col_data.numeric <- function(x, ..., sigfig = 3) { } #' @export -#' @rdname col_data -col_data.Date <- function(x, ...) { +#' @rdname cf_data +cf_data.Date <- function(x, ...) { x <- format(x, format = "%Y-%m-%d") - x[is.na(x)] <- col_na() + x[is.na(x)] <- cf_na() - new_col_data(x, width = 11, align = "right") + new_cf_data(x, width = 11, align = "right") } #' @export -#' @rdname col_data -col_data.POSIXct <- function(x, ...) { +#' @rdname cf_data +cf_data.POSIXct <- function(x, ...) { date <- format(x, format = "%Y-%m-%d") time <- format(x, format = "%H:%M:%S") datetime <- paste0(date, " " , style_subtle(time)) - datetime[is.na(x)] <- col_na() + datetime[is.na(x)] <- cf_na() - new_col_data(datetime, width = 19, align = "right") + new_cf_data(datetime, width = 19, align = "right") } #' @export -#' @rdname col_data -col_data.character <- function(x, ...) { +#' @rdname cf_data +cf_data.character <- function(x, ...) { width <- max(nchar(x, type = "width"), na.rm = TRUE) x <- encodeString(x, na.encode = FALSE) out <- str_trunc(x, width = width) - out[is.na(out)] <- col_na() + out[is.na(out)] <- cf_na() - new_col_data(out, width = width, align = "left", min_width = min(width, 3L)) + new_cf_data(out, width = width, align = "left", min_width = min(width, 3L)) } diff --git a/R/colformat.R b/R/colformat.R index c28b42f46..ed14bbef3 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -31,9 +31,9 @@ #' colformat(date + c(1, NA, 3:5)) #' colformat(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) colformat <- function(x, title = NULL, width = NULL, ...) { - title <- col_title(title, ...) - type <- col_type(x, ...) - data <- col_data(x, ...) + title <- cf_title(title, ...) + type <- cf_type(x, ...) + data <- cf_data(x, ...) ret <- structure( list(title = title, type = type, data = data), class = "colformat" @@ -61,10 +61,10 @@ format.colformat <- function(x, width = NULL, ...) { data_format <- format(x$data, width = width, ...) align <- attr(data_format, "align") - col_data <- c(title_format, type_format, data_format) + cf_data <- c(title_format, type_format, data_format) new_column( - crayon::col_align(col_data, width = width, align = align), + crayon::col_align(cf_data, width = width, align = align), width, align = "left" ) diff --git a/R/styles.R b/R/styles.R index 687712ecd..704b672bd 100644 --- a/R/styles.R +++ b/R/styles.R @@ -35,7 +35,7 @@ style_grey <- function(level, ...) { ) } -col_na <- function(width = 1L) { +cf_na <- function(width = 1L) { width <- pmax(0, width - 1) paste0(strrep(" ", width), style_na("?")) diff --git a/R/title.R b/R/title.R index c41aed4b1..7c2b879ee 100644 --- a/R/title.R +++ b/R/title.R @@ -1,9 +1,9 @@ -col_title <- function(title, ...) { +cf_title <- function(title, ...) { ret <- structure( list( title = title ), - class = "col_title" + class = "cf_title" ) if (is.null(title)) { @@ -17,7 +17,7 @@ col_title <- function(title, ...) { } #' @export -format.col_title <- function(x, width, ...) { +format.cf_title <- function(x, width, ...) { title <- x$title if (is.null(title)) return(character()) diff --git a/R/type-sum.R b/R/type-sum.R index 079fab232..7fc4053b1 100644 --- a/R/type-sum.R +++ b/R/type-sum.R @@ -2,13 +2,13 @@ style_type <- function(x) { style_subtle(x) } -col_type <- function(x, ...) { +cf_type <- function(x, ...) { type <- type_sum(x) ret <- structure( list( type = type ), - class = "col_type" + class = "cf_type" ) ret <- set_width(ret, width = nchar(type, type = "width") + 2L) ret <- set_min_width(ret, 3L) @@ -16,7 +16,7 @@ col_type <- function(x, ...) { } #' @export -format.col_type <- function(x, width = NULL, ...) { +format.cf_type <- function(x, width = NULL, ...) { if (is.null(width) || width >= get_width(x)) type <- x$type else type <- substr(x$type, 1, width - 2) style_type(paste0("<", type, ">")) diff --git a/R/utils.R b/R/utils.R index bcfeae541..463769bc2 100644 --- a/R/utils.R +++ b/R/utils.R @@ -2,7 +2,7 @@ cat_line <- function(...) { cat(..., "\n", sep = "") } -col_align <- function(x, width, align) { +cf_align <- function(x, width, align) { vapply(x, crayon::col_align, width = width, align = align, FUN.VALUE = character(1)) } diff --git a/man/col_data.Rd b/man/cf_data.Rd similarity index 56% rename from man/col_data.Rd rename to man/cf_data.Rd index 528158bc4..84c346838 100644 --- a/man/col_data.Rd +++ b/man/cf_data.Rd @@ -1,25 +1,25 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/col-data.R -\name{col_data} -\alias{col_data} -\alias{col_data.logical} -\alias{col_data.numeric} -\alias{col_data.Date} -\alias{col_data.POSIXct} -\alias{col_data.character} +\name{cf_data} +\alias{cf_data} +\alias{cf_data.logical} +\alias{cf_data.numeric} +\alias{cf_data.Date} +\alias{cf_data.POSIXct} +\alias{cf_data.character} \title{Column data} \usage{ -col_data(x, ...) +cf_data(x, ...) -\method{col_data}{logical}(x, ...) +\method{cf_data}{logical}(x, ...) -\method{col_data}{numeric}(x, ..., sigfig = 3) +\method{cf_data}{numeric}(x, ..., sigfig = 3) -\method{col_data}{Date}(x, ...) +\method{cf_data}{Date}(x, ...) -\method{col_data}{POSIXct}(x, ...) +\method{cf_data}{POSIXct}(x, ...) -\method{col_data}{character}(x, ...) +\method{cf_data}{character}(x, ...) } \arguments{ \item{x}{A vector to format} diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index 4f641d2f4..8f79a1274 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -4,7 +4,7 @@ format_scientific_bw <- function(x, ...) { old <- options(crayon.enabled = FALSE) on.exit(options(old)) - ret <- col_data(x, ...) + ret <- cf_data(x, ...) # Hack: Pretend decimal format requires 100 characters ret$dec <- set_width(ret$dec, 100) ret <- set_width(ret, 100) From 318a765d86d17fddd85187e4bd1ce14927397e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 30 Jul 2017 10:59:02 +0200 Subject: [PATCH 048/133] get rid of warnings --- R/utils.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/R/utils.R b/R/utils.R index 463769bc2..39ec3348b 100644 --- a/R/utils.R +++ b/R/utils.R @@ -8,6 +8,8 @@ cf_align <- function(x, width, align) { } str_trunc <- function(x, width = 20) { + if (width == Inf) return(x) + str_width <- nchar(x, type = "width") too_wide <- !is.na(x) & str_width > width From f7a03ca0bc61fbae94dc4f4c3b4b168b0e4e871b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 31 Jul 2017 08:29:31 +0200 Subject: [PATCH 049/133] Formatting list columns and factor columns (#23) * copy col_sum() and is_s3_vector() from tibble * cf_data.default() * import dim_desc() from tibble * cf_data.list() * document * add missing * oops * simplify default implementation * fix output * fix character, add factor tests --- NAMESPACE | 15 ++++++ R/col-data.R | 27 +++++++++-- R/col-type.R | 23 +++++++++ R/dim.R | 38 +++++++++++++++ R/type-sum.R | 72 +++++++++++++++++++---------- man/cf_data.Rd | 6 +++ man/dim_desc.Rd | 19 ++++++++ man/type_sum.Rd | 17 +++++++ tests/testthat/out/escaped.txt | 2 + tests/testthat/out/factor.txt | 7 +++ tests/testthat/out/letters.txt | 14 +++--- tests/testthat/out/list-each.txt | 4 ++ tests/testthat/out/list-na.txt | 3 ++ tests/testthat/out/list-null.txt | 3 ++ tests/testthat/out/list-one.txt | 3 ++ tests/testthat/out/ordered.txt | 7 +++ tests/testthat/test-format_factor.R | 7 +++ tests/testthat/test-format_list.R | 7 +++ tests/testthat/test-obj-sum.R | 40 ++++++++++++++++ 19 files changed, 279 insertions(+), 35 deletions(-) create mode 100644 R/col-type.R create mode 100644 R/dim.R create mode 100644 man/dim_desc.Rd create mode 100644 tests/testthat/out/escaped.txt create mode 100644 tests/testthat/out/factor.txt create mode 100644 tests/testthat/out/list-each.txt create mode 100644 tests/testthat/out/list-na.txt create mode 100644 tests/testthat/out/list-null.txt create mode 100644 tests/testthat/out/list-one.txt create mode 100644 tests/testthat/out/ordered.txt create mode 100644 tests/testthat/test-format_factor.R create mode 100644 tests/testthat/test-format_list.R create mode 100644 tests/testthat/test-obj-sum.R diff --git a/NAMESPACE b/NAMESPACE index f53d8f4dc..dbe69b76f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,8 @@ S3method(cf_data,Date) S3method(cf_data,POSIXct) S3method(cf_data,character) +S3method(cf_data,default) +S3method(cf_data,list) S3method(cf_data,logical) S3method(cf_data,numeric) S3method(format,cf_data) @@ -10,6 +12,16 @@ S3method(format,cf_title) S3method(format,cf_type) S3method(format,colformat) S3method(format,decimal_format) +S3method(is_vector_s3,Date) +S3method(is_vector_s3,POSIXct) +S3method(is_vector_s3,data.frame) +S3method(is_vector_s3,default) +S3method(is_vector_s3,difftime) +S3method(is_vector_s3,factor) +S3method(is_vector_s3,ordered) +S3method(obj_sum,POSIXlt) +S3method(obj_sum,default) +S3method(obj_sum,list) S3method(print,cf_data) S3method(print,colformat) S3method(print,column) @@ -25,8 +37,11 @@ S3method(type_sum,ordered) S3method(type_sum,tbl_df) export(cf_data) export(colformat) +export(dim_desc) export(format_decimal) export(format_scientific) +export(is_vector_s3) +export(obj_sum) export(spark_bar) export(spark_line) export(type_sum) diff --git a/R/col-data.R b/R/col-data.R index 9ff58394b..c9880c879 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -96,11 +96,30 @@ cf_data.POSIXct <- function(x, ...) { #' @export #' @rdname cf_data cf_data.character <- function(x, ...) { - width <- max(nchar(x, type = "width"), na.rm = TRUE) - - x <- encodeString(x, na.encode = FALSE) - out <- str_trunc(x, width = width) + out <- encodeString(x, na.encode = FALSE) out[is.na(out)] <- cf_na() + width <- max(nchar(out, type = "width")) + new_cf_data(out, width = width, align = "left", min_width = min(width, 3L)) } + +#' @export +#' @rdname cf_data +cf_data.list <- function(x, ...) { + out <- paste0("<", obj_sum(x), ">") + + width <- max(nchar(out, type = "width")) + + new_cf_data(style_list(out), width = width, align = "left", min_width = min(width, 3L)) +} + +style_list <- function(x) { + style_subtle(x) +} + +#' @export +#' @rdname cf_data +cf_data.default <- function(x, ...) { + cf_data(as.character(x), ...) +} diff --git a/R/col-type.R b/R/col-type.R new file mode 100644 index 000000000..2f199cbb8 --- /dev/null +++ b/R/col-type.R @@ -0,0 +1,23 @@ +style_type <- function(x) { + style_subtle(x) +} + +cf_type <- function(x, ...) { + type <- type_sum(x) + ret <- structure( + list( + type = type + ), + class = "cf_type" + ) + ret <- set_width(ret, width = nchar(type, type = "width") + 2L) + ret <- set_min_width(ret, 3L) + ret +} + +#' @export +format.cf_type <- function(x, width = NULL, ...) { + if (is.null(width) || width >= get_width(x)) type <- x$type + else type <- substr(x$type, 1, width - 2) + style_type(paste0("<", type, ">")) +} diff --git a/R/dim.R b/R/dim.R new file mode 100644 index 000000000..3d262f9a3 --- /dev/null +++ b/R/dim.R @@ -0,0 +1,38 @@ +#' Format dimensions +#' +#' Multi-dimensional objects are formatted as `a x b x ...`, for vectors the +#' length is returned. +#' +#' @param x The object to format the dimensions for +#' +#' @export +#' @examples +#' dim_desc(1:10) +#' dim_desc(Titanic) +dim_desc <- function(x) { + dim <- dim(x) %||% length(x) + format_dim <- map_chr(dim, big_mark) + format_dim[is.na(dim)] <- "??" + paste0(format_dim, collapse = spaces_around(mult_sign())) +} + +size_sum <- function(x) { + if (!is_vector_s3(x)) return("") + + paste0(" [", dim_desc(x), "]" ) +} + +mult_sign <- function() { + "x" +} + +spaces_around <- function(x) { + paste0(" ", x, " ") +} + +# function for the thousand separator, +# returns "," unless it's used for the decimal point, in which case returns "." +big_mark <- function(x, ...) { + mark <- if (identical(getOption("OutDec"), ",")) "." else "," + formatC(x, big.mark = mark, ...) +} diff --git a/R/type-sum.R b/R/type-sum.R index 7fc4053b1..783f8fed9 100644 --- a/R/type-sum.R +++ b/R/type-sum.R @@ -1,27 +1,3 @@ -style_type <- function(x) { - style_subtle(x) -} - -cf_type <- function(x, ...) { - type <- type_sum(x) - ret <- structure( - list( - type = type - ), - class = "cf_type" - ) - ret <- set_width(ret, width = nchar(type, type = "width") + 2L) - ret <- set_min_width(ret, 3L) - ret -} - -#' @export -format.cf_type <- function(x, width = NULL, ...) { - if (is.null(width) || width >= get_width(x)) type <- x$type - else type <- substr(x$type, 1, width - 2) - style_type(paste0("<", type, ">")) -} - #' Provide a succinct summary of an object #' #' @description @@ -67,3 +43,51 @@ type_sum.default <- function(x) { paste0("S4: ", methods::is(x)[[1]]) } } + +#' @description +#' `obj_sum()` also includes the size of the object if `is_vector_s3()` +#' is `TRUE`. +#' +#' @keywords internal +#' @examples +#' obj_sum(1:10) +#' obj_sum(matrix(1:10)) +#' obj_sum(Sys.Date()) +#' obj_sum(Sys.time()) +#' obj_sum(mean) +#' @rdname type_sum +#' @export +obj_sum <- function(x) UseMethod("obj_sum") + +#' @export +obj_sum.default <- function(x) { + paste0(type_sum(x), size_sum(x)) +} + +#' @export +obj_sum.list <- function(x) { + map_chr(x, obj_sum.default) +} + +#' @export +obj_sum.POSIXlt <- function(x) { + rep("POSIXlt", length(x)) +} + +#' @export +#' @rdname type_sum +is_vector_s3 <- function(x) UseMethod("is_vector_s3") +#' @export +is_vector_s3.ordered <- function(x) TRUE +#' @export +is_vector_s3.factor <- function(x) TRUE +#' @export +is_vector_s3.Date <- function(x) TRUE +#' @export +is_vector_s3.POSIXct <- function(x) TRUE +#' @export +is_vector_s3.difftime <- function(x) TRUE +#' @export +is_vector_s3.data.frame <- function(x) TRUE +#' @export +is_vector_s3.default <- function(x) !is.object(x) && is_vector(x) diff --git a/man/cf_data.Rd b/man/cf_data.Rd index 84c346838..da7c2a127 100644 --- a/man/cf_data.Rd +++ b/man/cf_data.Rd @@ -7,6 +7,8 @@ \alias{cf_data.Date} \alias{cf_data.POSIXct} \alias{cf_data.character} +\alias{cf_data.list} +\alias{cf_data.default} \title{Column data} \usage{ cf_data(x, ...) @@ -20,6 +22,10 @@ cf_data(x, ...) \method{cf_data}{POSIXct}(x, ...) \method{cf_data}{character}(x, ...) + +\method{cf_data}{list}(x, ...) + +\method{cf_data}{default}(x, ...) } \arguments{ \item{x}{A vector to format} diff --git a/man/dim_desc.Rd b/man/dim_desc.Rd new file mode 100644 index 000000000..2eea4dd25 --- /dev/null +++ b/man/dim_desc.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dim.R +\name{dim_desc} +\alias{dim_desc} +\title{Format dimensions} +\usage{ +dim_desc(x) +} +\arguments{ +\item{x}{The object to format the dimensions for} +} +\description{ +Multi-dimensional objects are formatted as `a x b x ...`, for vectors the +length is returned. +} +\examples{ +dim_desc(1:10) +dim_desc(Titanic) +} diff --git a/man/type_sum.Rd b/man/type_sum.Rd index 43f6e051a..0e375038b 100644 --- a/man/type_sum.Rd +++ b/man/type_sum.Rd @@ -2,9 +2,15 @@ % Please edit documentation in R/type-sum.R \name{type_sum} \alias{type_sum} +\alias{obj_sum} +\alias{is_vector_s3} \title{Provide a succinct summary of an object} \usage{ type_sum(x) + +obj_sum(x) + +is_vector_s3(x) } \arguments{ \item{x}{an object to summarise. Generally only methods of atomic vectors @@ -13,4 +19,15 @@ and variants have been implemented.} \description{ `type_sum()` gives a brief summary of object type. Objects that commonly occur in a data frame should return a string with four or less characters. + +`obj_sum()` also includes the size of the object if `is_vector_s3()` +is `TRUE`. } +\examples{ +obj_sum(1:10) +obj_sum(matrix(1:10)) +obj_sum(Sys.Date()) +obj_sum(Sys.time()) +obj_sum(mean) +} +\keyword{internal} diff --git a/tests/testthat/out/escaped.txt b/tests/testthat/out/escaped.txt new file mode 100644 index 000000000..e67fc6ae9 --- /dev/null +++ b/tests/testthat/out/escaped.txt @@ -0,0 +1,2 @@ + +a\nb diff --git a/tests/testthat/out/factor.txt b/tests/testthat/out/factor.txt new file mode 100644 index 000000000..faab1dd29 --- /dev/null +++ b/tests/testthat/out/factor.txt @@ -0,0 +1,7 @@ + +a +b +c +d +e +? diff --git a/tests/testthat/out/letters.txt b/tests/testthat/out/letters.txt index 0726f81ad..73e3b5baa 100644 --- a/tests/testthat/out/letters.txt +++ b/tests/testthat/out/letters.txt @@ -1,7 +1,7 @@ - -a -b -c -d -e -? + +a +b +c +d +e +? diff --git a/tests/testthat/out/list-each.txt b/tests/testthat/out/list-each.txt new file mode 100644 index 000000000..91ea53183 --- /dev/null +++ b/tests/testthat/out/list-each.txt @@ -0,0 +1,4 @@ + + + + diff --git a/tests/testthat/out/list-na.txt b/tests/testthat/out/list-na.txt new file mode 100644 index 000000000..c3f27d9a9 --- /dev/null +++ b/tests/testthat/out/list-na.txt @@ -0,0 +1,3 @@ + + + diff --git a/tests/testthat/out/list-null.txt b/tests/testthat/out/list-null.txt new file mode 100644 index 000000000..25093d886 --- /dev/null +++ b/tests/testthat/out/list-null.txt @@ -0,0 +1,3 @@ + + + diff --git a/tests/testthat/out/list-one.txt b/tests/testthat/out/list-one.txt new file mode 100644 index 000000000..27ae6351f --- /dev/null +++ b/tests/testthat/out/list-one.txt @@ -0,0 +1,3 @@ + + + diff --git a/tests/testthat/out/ordered.txt b/tests/testthat/out/ordered.txt new file mode 100644 index 000000000..9ff9f14ad --- /dev/null +++ b/tests/testthat/out/ordered.txt @@ -0,0 +1,7 @@ + +a +b +c +d +e +? diff --git a/tests/testthat/test-format_factor.R b/tests/testthat/test-format_factor.R new file mode 100644 index 000000000..afeb4d1b0 --- /dev/null +++ b/tests/testthat/test-format_factor.R @@ -0,0 +1,7 @@ +context("format_factor") + +test_that("output test", { + expect_colformat_output(xp = factor(c(letters[1:5], NA)), filename = "factor.txt") + expect_colformat_output(xp = ordered(c(letters[1:5], NA)), filename = "ordered.txt") + expect_colformat_output(xp = factor("a\nb"), filename = "escaped.txt") +}) diff --git a/tests/testthat/test-format_list.R b/tests/testthat/test-format_list.R new file mode 100644 index 000000000..580815030 --- /dev/null +++ b/tests/testthat/test-format_list.R @@ -0,0 +1,7 @@ +context("format_list") + +test_that("output test", { + expect_colformat_output(xp = as.list(1:3), filename = "list-each.txt") + expect_colformat_output(xp = list(1:3, NULL), filename = "list-null.txt") + expect_colformat_output(list(1:3), filename = "list-na.txt") +}) diff --git a/tests/testthat/test-obj-sum.R b/tests/testthat/test-obj-sum.R new file mode 100644 index 000000000..3281cb979 --- /dev/null +++ b/tests/testthat/test-obj-sum.R @@ -0,0 +1,40 @@ +context("obj_sum") + +# obj_sum ---------------------------------------------------------------- + +test_that("shows only first class name for S4", { + A <- methods::setClass("A") + expect_equal(obj_sum(A), "S4: classGeneratorFunction") +}) + +test_that("shows only first class name for S3", { + x <- structure(list(), class = c("a", "b", "c")) + expect_equal(obj_sum(x), "S3: a") +}) + +test_that("NULL handled specially", { + expect_equal(obj_sum(NULL), "NULL") +}) + +test_that("data frame and tibbles include rows and cols", { + expect_equal(obj_sum(mtcars), "data.frame [32 x 11]") +}) + +test_that("common data vectors treated as atomic", { + expect_equal(obj_sum(factor(1:3)), "fctr [3]") + expect_equal(obj_sum(ordered(1:3)), "ord [3]") + expect_equal(obj_sum(Sys.Date() + 1:3), "date [3]") + expect_equal(obj_sum(Sys.time() + 1:3), "dttm [3]") +}) + +test_that("difftime is shown as time", { + expect_equal(obj_sum(Sys.time() - Sys.time() + 1:3), "time [3]") +}) + + +# type_sum ---------------------------------------------------------------- + +test_that("less common objects get abbreviations", { + expect_equal(type_sum(environment()), "env") + expect_equal(type_sum(environment), "fun") +}) From 2a743eb837f5ada95e65ae58feb8790b04bd05a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 7 Aug 2017 09:15:59 +0200 Subject: [PATCH 050/133] New multicolformat() (#24) * new class cf_vertical, avoid misuse of new_column() * align individual components * fix computation of width for character columns * underline column type at full width * extract function * extract cf_format_parts() * accept NA as title * never use scientific width as max width * don't pass ellipsis * first draft * extract mcf_compute_col_widths() * rename * redistribute remaining space * add output tests * underline entire header * use names for test * for multicolumn(), all columns are titled or none are * add longer title * document algorithm for space distribution * more consistent distribution of remaining space * minumum width for column type is 5 * simplify * document --- NAMESPACE | 4 +- R/col-data.R | 4 +- R/col-type.R | 2 +- R/colformat.R | 35 ++++-- R/column.R | 12 +- R/multi.R | 122 ++++++++++++++++++++ R/title.R | 11 +- R/utils.R | 2 +- man/multicolformat.Rd | 41 +++++++ tests/testthat/helper-output.R | 6 +- tests/testthat/out/basic.txt | 2 +- tests/testthat/out/date.txt | 2 +- tests/testthat/out/decimal-insignif.txt | 2 +- tests/testthat/out/escaped.txt | 2 +- tests/testthat/out/factor.txt | 14 +-- tests/testthat/out/letters-long-03.txt | 6 +- tests/testthat/out/letters-long-10.txt | 2 +- tests/testthat/out/letters-long.txt | 2 +- tests/testthat/out/letters.txt | 14 +-- tests/testthat/out/list-each.txt | 2 +- tests/testthat/out/list-na.txt | 2 +- tests/testthat/out/list-null.txt | 2 +- tests/testthat/out/logical.txt | 2 +- tests/testthat/out/multi-04.txt | 1 + tests/testthat/out/multi-05.txt | 1 + tests/testthat/out/multi-06.txt | 5 + tests/testthat/out/multi-07.txt | 5 + tests/testthat/out/multi-08.txt | 5 + tests/testthat/out/multi-09.txt | 5 + tests/testthat/out/multi-10.txt | 5 + tests/testthat/out/multi-11.txt | 5 + tests/testthat/out/multi-12.txt | 5 + tests/testthat/out/multi-13.txt | 5 + tests/testthat/out/multi-14.txt | 5 + tests/testthat/out/multi-15.txt | 5 + tests/testthat/out/multi-16.txt | 5 + tests/testthat/out/multi-17.txt | 5 + tests/testthat/out/multi-18.txt | 5 + tests/testthat/out/multi-19.txt | 5 + tests/testthat/out/multi-20.txt | 5 + tests/testthat/out/multi-21.txt | 5 + tests/testthat/out/multi-22.txt | 5 + tests/testthat/out/multi-23.txt | 5 + tests/testthat/out/multi-24.txt | 5 + tests/testthat/out/multi-25.txt | 5 + tests/testthat/out/multi-26.txt | 5 + tests/testthat/out/multi-27.txt | 5 + tests/testthat/out/multi-28.txt | 5 + tests/testthat/out/multi-29.txt | 5 + tests/testthat/out/multi-30.txt | 5 + tests/testthat/out/multi-31.txt | 5 + tests/testthat/out/multi-32.txt | 5 + tests/testthat/out/multi-33.txt | 5 + tests/testthat/out/multi-34.txt | 5 + tests/testthat/out/multi-35.txt | 5 + tests/testthat/out/multi-36.txt | 5 + tests/testthat/out/multi-37.txt | 5 + tests/testthat/out/multi-38.txt | 5 + tests/testthat/out/multi-39.txt | 5 + tests/testthat/out/numeric-04.txt | 2 +- tests/testthat/out/numeric-07.txt | 2 +- tests/testthat/out/numeric-10.txt | 2 +- tests/testthat/out/numeric-15.txt | 2 +- tests/testthat/out/numeric-22.txt | 2 +- tests/testthat/out/ordered.txt | 14 +-- tests/testthat/out/scientific-short-neg.txt | 2 +- tests/testthat/out/scientific.txt | 2 +- tests/testthat/out/time.txt | 2 +- tests/testthat/out/title-crayon.txt | 2 +- tests/testthat/test-format_multi.R | 41 +++++++ 70 files changed, 471 insertions(+), 67 deletions(-) create mode 100644 R/multi.R create mode 100644 man/multicolformat.Rd create mode 100644 tests/testthat/out/multi-04.txt create mode 100644 tests/testthat/out/multi-05.txt create mode 100644 tests/testthat/out/multi-06.txt create mode 100644 tests/testthat/out/multi-07.txt create mode 100644 tests/testthat/out/multi-08.txt create mode 100644 tests/testthat/out/multi-09.txt create mode 100644 tests/testthat/out/multi-10.txt create mode 100644 tests/testthat/out/multi-11.txt create mode 100644 tests/testthat/out/multi-12.txt create mode 100644 tests/testthat/out/multi-13.txt create mode 100644 tests/testthat/out/multi-14.txt create mode 100644 tests/testthat/out/multi-15.txt create mode 100644 tests/testthat/out/multi-16.txt create mode 100644 tests/testthat/out/multi-17.txt create mode 100644 tests/testthat/out/multi-18.txt create mode 100644 tests/testthat/out/multi-19.txt create mode 100644 tests/testthat/out/multi-20.txt create mode 100644 tests/testthat/out/multi-21.txt create mode 100644 tests/testthat/out/multi-22.txt create mode 100644 tests/testthat/out/multi-23.txt create mode 100644 tests/testthat/out/multi-24.txt create mode 100644 tests/testthat/out/multi-25.txt create mode 100644 tests/testthat/out/multi-26.txt create mode 100644 tests/testthat/out/multi-27.txt create mode 100644 tests/testthat/out/multi-28.txt create mode 100644 tests/testthat/out/multi-29.txt create mode 100644 tests/testthat/out/multi-30.txt create mode 100644 tests/testthat/out/multi-31.txt create mode 100644 tests/testthat/out/multi-32.txt create mode 100644 tests/testthat/out/multi-33.txt create mode 100644 tests/testthat/out/multi-34.txt create mode 100644 tests/testthat/out/multi-35.txt create mode 100644 tests/testthat/out/multi-36.txt create mode 100644 tests/testthat/out/multi-37.txt create mode 100644 tests/testthat/out/multi-38.txt create mode 100644 tests/testthat/out/multi-39.txt create mode 100644 tests/testthat/test-format_multi.R diff --git a/NAMESPACE b/NAMESPACE index dbe69b76f..f8be53d50 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -12,6 +12,7 @@ S3method(format,cf_title) S3method(format,cf_type) S3method(format,colformat) S3method(format,decimal_format) +S3method(format,multicolformat) S3method(is_vector_s3,Date) S3method(is_vector_s3,POSIXct) S3method(is_vector_s3,data.frame) @@ -23,9 +24,10 @@ S3method(obj_sum,POSIXlt) S3method(obj_sum,default) S3method(obj_sum,list) S3method(print,cf_data) +S3method(print,cf_vertical) S3method(print,colformat) -S3method(print,column) S3method(print,decimal_format) +S3method(print,multicolformat) S3method(print,spark) S3method(type_sum,Date) S3method(type_sum,POSIXt) diff --git a/R/col-data.R b/R/col-data.R index c9880c879..398a132e0 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -66,7 +66,7 @@ cf_data.numeric <- function(x, ..., sigfig = 3) { class = "decimal_format" ) - ret <- set_width(ret, max(get_widths(ret))) + ret <- set_width(ret, get_width(ret$dec)) ret <- set_min_width(ret, min(get_min_widths(ret))) ret } @@ -99,7 +99,7 @@ cf_data.character <- function(x, ...) { out <- encodeString(x, na.encode = FALSE) out[is.na(out)] <- cf_na() - width <- max(nchar(out, type = "width")) + width <- max(crayon::col_nchar(out)) new_cf_data(out, width = width, align = "left", min_width = min(width, 3L)) } diff --git a/R/col-type.R b/R/col-type.R index 2f199cbb8..357971560 100644 --- a/R/col-type.R +++ b/R/col-type.R @@ -11,7 +11,7 @@ cf_type <- function(x, ...) { class = "cf_type" ) ret <- set_width(ret, width = nchar(type, type = "width") + 2L) - ret <- set_min_width(ret, 3L) + ret <- set_min_width(ret, 5L) ret } diff --git a/R/colformat.R b/R/colformat.R index ed14bbef3..ff6da28de 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -44,6 +44,20 @@ colformat <- function(x, title = NULL, width = NULL, ...) { #' @export format.colformat <- function(x, width = NULL, ...) { + width <- cf_get_width(x, width) + out <- cf_format_parts(x, width) + + cf_data <- c(out$title_format, crayon::underline(out$type_format), out$data_format) + + new_vertical(cf_data) +} + +#' @export +print.colformat <- function(x, ...) { + print(format(x, ...)) +} + +cf_get_width <- function(x, width) { if (is.null(width)) { width <- get_width(x) } @@ -56,21 +70,22 @@ format.colformat <- function(x, width = NULL, ...) { min_widths <- max(get_min_widths(x)) if (width < min_widths) width <- min_widths + width +} + +cf_format_parts <- function(x, width, ...) { title_format <- format(x$title, width = width, ...) type_format <- format(x$type, width = width, ...) data_format <- format(x$data, width = width, ...) align <- attr(data_format, "align") - cf_data <- c(title_format, type_format, data_format) + title_format <- crayon::col_align(title_format, width = width, align = align) + type_format <- crayon::col_align(type_format, width = width, align = align) + data_format <- crayon::col_align(data_format, width = width, align = align) - new_column( - crayon::col_align(cf_data, width = width, align = align), - width, - align = "left" + list( + title_format = title_format, + type_format = type_format, + data_format = data_format ) } - -#' @export -print.colformat <- function(x, ...) { - print(format(x, ...)) -} diff --git a/R/column.R b/R/column.R index 3ee2b6f2c..424b5f76f 100644 --- a/R/column.R +++ b/R/column.R @@ -2,13 +2,21 @@ new_column <- function(row, width = NULL, align = NULL) { ret <- structure( row, align = align, - class = "column" + class = c("cf_column", "cf_vertical") ) ret <- set_width(ret, width) ret } +new_vertical <- function(row) { + ret <- structure( + row, + class = "cf_vertical" + ) + ret +} + #' @export -print.column <- function(x, ...) { +print.cf_vertical <- function(x, ...) { cat_line(paste(x, collapse = "\n")) } diff --git a/R/multi.R b/R/multi.R new file mode 100644 index 000000000..b81b4c74d --- /dev/null +++ b/R/multi.R @@ -0,0 +1,122 @@ +#' Format multiple vectors in a tabular display +#' +#' The vectors are formatted to fit horizontally into a user-supplied number of +#' characters per row. +#' +#' @param x A list of vectors to format +#' @param width Default width of the entire output, optional +#' @param ... Ignored +multicolformat <- function(x, width = NULL, ...) { + if (is_named(x)) { + ret <- map2(x, names(x), colformat) + } else { + ret <- map(x, colformat) + } + ret <- structure(ret, class = "multicolformat") + ret <- set_width(ret, width) + ret +} + +#' @export +format.multicolformat <- function(x, width = NULL, ...) { + if (is.null(width)) { + width <- get_width(x) + } + + if (is.null(width)) { + width <- getOption("width") + } + + col_widths <- mcf_get_width(x, width) + out <- map2(x[seq_along(col_widths)], col_widths, cf_format_parts) + mcf_data <- c( + invoke(paste, map(out, `[[`, "title_format")), + crayon::underline(invoke(paste, map(out, `[[`, "type_format"))), + invoke(paste, map(out, `[[`, "data_format")) + ) + + new_vertical(mcf_data) +} + +#' @export +print.multicolformat <- function(x, ...) { + print(format(x, ...)) +} + +#' @rdname multicolformat +#' @usage NULL +#' @aliases NULL +mcf_get_width <- function(x, width) { + max_widths <- map_int(map(x, get_widths), max) + min_widths <- map_int(map(x, get_min_widths), max) + + #' @details + #' In a first pass, for each column it is decided if it is hidden, shown with + #' its minimum width or shown with its maximum width. + col_widths <- mcf_compute_col_widths(min_widths, max_widths, width) + + #' Remaining space is then distributed proportionally to columns that do not + #' use their desired width. + max_widths <- max_widths[seq_along(col_widths)] + added_space <- mcf_distribute_space(col_widths, max_widths, width) + + col_widths + added_space +} + +#' @rdname multicolformat +#' @usage NULL +#' @aliases NULL +mcf_compute_col_widths <- function(min_widths, max_widths, width) { + #' @details + #' For computing the column widths, two cases are distinguished: + #' 1. When taking the minimum width for each column (plus one inter-column + #' space), at least one column does not fit. + cum_min_widths <- cumsum(min_widths + 1L) + allowed_min <- which(cum_min_widths <= width) + if (length(allowed_min) < length(min_widths)) { + #' In this case, the minimum width is assigned to all columns that do fit, + #' the non-fitting columns are stripped. + col_widths <- min_widths[allowed_min] + } else { + #' 1. All columns fit with their minimum width. In this case, starting at + #' the leftmost column, the maximum width is allocated to the columns + #' until all available space is used. + cum_max_widths <- cumsum(max_widths + 1L) + rev_cum_rev_min_widths <- rev(cumsum(rev(min_widths) + 1L)) + + allowed_max <- (cum_max_widths + c(rev_cum_rev_min_widths[-1L], 0L)) <= width + col_widths <- ifelse(allowed_max, max_widths, min_widths) + } + + col_widths +} + +#' @rdname multicolformat +#' @usage NULL +#' @aliases NULL +mcf_distribute_space <- function(col_widths, max_widths, width) { + missing_space <- max_widths - col_widths + # Shortcut to avoid division by zero + if (all(missing_space == 0L)) return(rep_along(col_widths, 0L)) + + #' @details + #' The remaining space is distributed from left to right. + #' Each column gains space proportional to the fraction of missing and + #' remaining space, + remaining_width <- min(width - sum(col_widths + 1L), sum(missing_space)) + added_space_prop <- missing_space / sum(missing_space) * remaining_width + + #' rounded down. + added_space_ceil <- ceiling(added_space_prop) + added_space_floor <- floor(added_space_prop) + added_space_diff <- added_space_ceil - added_space_floor + added_space <- ifelse( + #' Any space remaining after rounding is distributed from left to right, + #' one space per column. + sum(added_space_floor) + cumsum(added_space_diff) <= remaining_width, + added_space_ceil, + added_space_floor + ) + + added_space +} diff --git a/R/title.R b/R/title.R index 7c2b879ee..3b8ab5b06 100644 --- a/R/title.R +++ b/R/title.R @@ -1,4 +1,10 @@ cf_title <- function(title, ...) { + if (is.null(title)) { + width <- 0L + } else { + width <- nchar(format_title(title, width = Inf), "width") + } + ret <- structure( list( title = title @@ -6,11 +12,6 @@ cf_title <- function(title, ...) { class = "cf_title" ) - if (is.null(title)) { - width <- 0L - } else { - width <- nchar(format_title(title, width = Inf), "width") - } ret <- set_width(ret, width) ret <- set_min_width(ret, 3L) ret diff --git a/R/utils.R b/R/utils.R index 39ec3348b..683b42cf8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -7,7 +7,7 @@ cf_align <- function(x, width, align) { FUN.VALUE = character(1)) } -str_trunc <- function(x, width = 20) { +str_trunc <- function(x, width) { if (width == Inf) return(x) str_width <- nchar(x, type = "width") diff --git a/man/multicolformat.Rd b/man/multicolformat.Rd new file mode 100644 index 000000000..402bb8e57 --- /dev/null +++ b/man/multicolformat.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/multi.R +\name{multicolformat} +\alias{multicolformat} +\title{Format multiple vectors in a tabular display} +\usage{ +multicolformat(x, width = NULL, ...) +} +\arguments{ +\item{x}{A list of vectors to format} + +\item{width}{Default width of the entire output, optional} + +\item{...}{Ignored} +} +\description{ +The vectors are formatted to fit horizontally into a user-supplied number of +characters per row. +} +\details{ +In a first pass, for each column it is decided if it is hidden, shown with +its minimum width or shown with its maximum width. +Remaining space is then distributed proportionally to columns that do not +use their desired width. + +For computing the column widths, two cases are distinguished: +1. When taking the minimum width for each column (plus one inter-column + space), at least one column does not fit. + In this case, the minimum width is assigned to all columns that do fit, + the non-fitting columns are stripped. +1. All columns fit with their minimum width. In this case, starting at + the leftmost column, the maximum width is allocated to the columns + until all available space is used. + +The remaining space is distributed from left to right. +Each column gains space proportional to the fraction of missing and +remaining space, +rounded down. +Any space remaining after rounding is distributed from left to right, +one space per column. +} diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 131d1b57b..046968d94 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -1,4 +1,6 @@ -expect_colformat_output <- function(x, ..., filename, xp = add_special(x), crayon = TRUE) { +expect_colformat_output <- function(x, ..., filename, + xp = add_special(x), xf = colformat(xp, ...), + crayon = TRUE) { if (crayon) { old <- options(crayon.enabled = TRUE, crayon.colors = 16L) crayon::num_colors(forget = TRUE) @@ -11,7 +13,7 @@ expect_colformat_output <- function(x, ..., filename, xp = add_special(x), crayo crayon::num_colors(forget = TRUE) }) - expect_output_file(print(colformat(xp, ...)), file.path("out", filename), update = TRUE) + expect_output_file(print(xf), file.path("out", filename), update = TRUE) } add_special <- function(x) { diff --git a/tests/testthat/out/basic.txt b/tests/testthat/out/basic.txt index 385ae28d1..1016155ec 100644 --- a/tests/testthat/out/basic.txt +++ b/tests/testthat/out/basic.txt @@ -1,4 +1,4 @@ -  +  - 0.00100 0.0100 - 0.100 diff --git a/tests/testthat/out/date.txt b/tests/testthat/out/date.txt index 401645592..d5e948b26 100644 --- a/tests/testthat/out/date.txt +++ b/tests/testthat/out/date.txt @@ -1,3 +1,3 @@ -  +  2017-07-28 ? diff --git a/tests/testthat/out/decimal-insignif.txt b/tests/testthat/out/decimal-insignif.txt index 22b6cdfe3..cf4889d2a 100644 --- a/tests/testthat/out/decimal-insignif.txt +++ b/tests/testthat/out/decimal-insignif.txt @@ -1,4 +1,4 @@ -  +  0.00123 0.0123 0.123 diff --git a/tests/testthat/out/escaped.txt b/tests/testthat/out/escaped.txt index e67fc6ae9..189b7e6e7 100644 --- a/tests/testthat/out/escaped.txt +++ b/tests/testthat/out/escaped.txt @@ -1,2 +1,2 @@ - + a\nb diff --git a/tests/testthat/out/factor.txt b/tests/testthat/out/factor.txt index faab1dd29..c52d7a305 100644 --- a/tests/testthat/out/factor.txt +++ b/tests/testthat/out/factor.txt @@ -1,7 +1,7 @@ - -a -b -c -d -e -? + +a +b +c +d +e +? diff --git a/tests/testthat/out/letters-long-03.txt b/tests/testthat/out/letters-long-03.txt index 85ca94055..b8de5c9e5 100644 --- a/tests/testthat/out/letters-long-03.txt +++ b/tests/testthat/out/letters-long-03.txt @@ -1,3 +1,3 @@ - -ab… -[… + +abcd… +[43… diff --git a/tests/testthat/out/letters-long-10.txt b/tests/testthat/out/letters-long-10.txt index 2530426ca..4a848b0f7 100644 --- a/tests/testthat/out/letters-long-10.txt +++ b/tests/testthat/out/letters-long-10.txt @@ -1,3 +1,3 @@ - +  abcdefghi… [30… diff --git a/tests/testthat/out/letters-long.txt b/tests/testthat/out/letters-long.txt index 5f9ab3a70..76fe78345 100644 --- a/tests/testthat/out/letters-long.txt +++ b/tests/testthat/out/letters-long.txt @@ -1,3 +1,3 @@ - +  abcdefghijklmnopqrstuvwxyz ? diff --git a/tests/testthat/out/letters.txt b/tests/testthat/out/letters.txt index 73e3b5baa..a868b613c 100644 --- a/tests/testthat/out/letters.txt +++ b/tests/testthat/out/letters.txt @@ -1,7 +1,7 @@ - -a -b -c -d -e -? + +a +b +c +d +e +? diff --git a/tests/testthat/out/list-each.txt b/tests/testthat/out/list-each.txt index 91ea53183..d4c627c50 100644 --- a/tests/testthat/out/list-each.txt +++ b/tests/testthat/out/list-each.txt @@ -1,4 +1,4 @@ - +     diff --git a/tests/testthat/out/list-na.txt b/tests/testthat/out/list-na.txt index c3f27d9a9..9dcf4cdd3 100644 --- a/tests/testthat/out/list-na.txt +++ b/tests/testthat/out/list-na.txt @@ -1,3 +1,3 @@ - +    diff --git a/tests/testthat/out/list-null.txt b/tests/testthat/out/list-null.txt index 25093d886..8f1cac88f 100644 --- a/tests/testthat/out/list-null.txt +++ b/tests/testthat/out/list-null.txt @@ -1,3 +1,3 @@ - +    diff --git a/tests/testthat/out/logical.txt b/tests/testthat/out/logical.txt index 7dadc4b81..701b9202a 100644 --- a/tests/testthat/out/logical.txt +++ b/tests/testthat/out/logical.txt @@ -1,4 +1,4 @@ - + * - ? diff --git a/tests/testthat/out/multi-04.txt b/tests/testthat/out/multi-04.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/testthat/out/multi-04.txt @@ -0,0 +1 @@ + diff --git a/tests/testthat/out/multi-05.txt b/tests/testthat/out/multi-05.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/testthat/out/multi-05.txt @@ -0,0 +1 @@ + diff --git a/tests/testthat/out/multi-06.txt b/tests/testthat/out/multi-06.txt new file mode 100644 index 000000000..bfe550c84 --- /dev/null +++ b/tests/testthat/out/multi-06.txt @@ -0,0 +1,5 @@ +colu… + + 1.23 + 2.23 + 3.23 diff --git a/tests/testthat/out/multi-07.txt b/tests/testthat/out/multi-07.txt new file mode 100644 index 000000000..e02c5b154 --- /dev/null +++ b/tests/testthat/out/multi-07.txt @@ -0,0 +1,5 @@ +colum… +  + 1.23 + 2.23 + 3.23 diff --git a/tests/testthat/out/multi-08.txt b/tests/testthat/out/multi-08.txt new file mode 100644 index 000000000..5a0e41f17 --- /dev/null +++ b/tests/testthat/out/multi-08.txt @@ -0,0 +1,5 @@ +column… +  + 1.23 + 2.23 + 3.23 diff --git a/tests/testthat/out/multi-09.txt b/tests/testthat/out/multi-09.txt new file mode 100644 index 000000000..07c24dbde --- /dev/null +++ b/tests/testthat/out/multi-09.txt @@ -0,0 +1,5 @@ +column_… +  + 1.23 + 2.23 + 3.23 diff --git a/tests/testthat/out/multi-10.txt b/tests/testthat/out/multi-10.txt new file mode 100644 index 000000000..ac6325fdb --- /dev/null +++ b/tests/testthat/out/multi-10.txt @@ -0,0 +1,5 @@ +column_z… +  + 1.23 + 2.23 + 3.23 diff --git a/tests/testthat/out/multi-11.txt b/tests/testthat/out/multi-11.txt new file mode 100644 index 000000000..b4be4d43c --- /dev/null +++ b/tests/testthat/out/multi-11.txt @@ -0,0 +1,5 @@ +column_ze… +  + 1.23 + 2.23 + 3.23 diff --git a/tests/testthat/out/multi-12.txt b/tests/testthat/out/multi-12.txt new file mode 100644 index 000000000..2d4bd076e --- /dev/null +++ b/tests/testthat/out/multi-12.txt @@ -0,0 +1,5 @@ +colu… col_… +  + 1.23 a + 2.23 b + 3.23 c diff --git a/tests/testthat/out/multi-13.txt b/tests/testthat/out/multi-13.txt new file mode 100644 index 000000000..572285a68 --- /dev/null +++ b/tests/testthat/out/multi-13.txt @@ -0,0 +1,5 @@ +colum… col_… +   + 1.23 a + 2.23 b + 3.23 c diff --git a/tests/testthat/out/multi-14.txt b/tests/testthat/out/multi-14.txt new file mode 100644 index 000000000..c8e8a4a22 --- /dev/null +++ b/tests/testthat/out/multi-14.txt @@ -0,0 +1,5 @@ +column… col_… +   + 1.23 a + 2.23 b + 3.23 c diff --git a/tests/testthat/out/multi-15.txt b/tests/testthat/out/multi-15.txt new file mode 100644 index 000000000..7e1118975 --- /dev/null +++ b/tests/testthat/out/multi-15.txt @@ -0,0 +1,5 @@ +column_… col_… +   + 1.23 a + 2.23 b + 3.23 c diff --git a/tests/testthat/out/multi-16.txt b/tests/testthat/out/multi-16.txt new file mode 100644 index 000000000..89d1447a5 --- /dev/null +++ b/tests/testthat/out/multi-16.txt @@ -0,0 +1,5 @@ +column_z… col_… +   + 1.23 a + 2.23 b + 3.23 c diff --git a/tests/testthat/out/multi-17.txt b/tests/testthat/out/multi-17.txt new file mode 100644 index 000000000..7868ff47f --- /dev/null +++ b/tests/testthat/out/multi-17.txt @@ -0,0 +1,5 @@ +column_ze… col_… +   + 1.23 a + 2.23 b + 3.23 c diff --git a/tests/testthat/out/multi-18.txt b/tests/testthat/out/multi-18.txt new file mode 100644 index 000000000..c2990baf6 --- /dev/null +++ b/tests/testthat/out/multi-18.txt @@ -0,0 +1,5 @@ +colu… col_… col_… +   + 1.23 a a + 2.23 b b + 3.23 c c diff --git a/tests/testthat/out/multi-19.txt b/tests/testthat/out/multi-19.txt new file mode 100644 index 000000000..2bd7e4a27 --- /dev/null +++ b/tests/testthat/out/multi-19.txt @@ -0,0 +1,5 @@ +colum… col_… col_… +    + 1.23 a a + 2.23 b b + 3.23 c c diff --git a/tests/testthat/out/multi-20.txt b/tests/testthat/out/multi-20.txt new file mode 100644 index 000000000..3bdec1f7c --- /dev/null +++ b/tests/testthat/out/multi-20.txt @@ -0,0 +1,5 @@ +column… col_… col_… +    + 1.23 a a + 2.23 b b + 3.23 c c diff --git a/tests/testthat/out/multi-21.txt b/tests/testthat/out/multi-21.txt new file mode 100644 index 000000000..282e89d2a --- /dev/null +++ b/tests/testthat/out/multi-21.txt @@ -0,0 +1,5 @@ +column_… col_… col_… +    + 1.23 a a + 2.23 b b + 3.23 c c diff --git a/tests/testthat/out/multi-22.txt b/tests/testthat/out/multi-22.txt new file mode 100644 index 000000000..d90f8134b --- /dev/null +++ b/tests/testthat/out/multi-22.txt @@ -0,0 +1,5 @@ +column_z… col_… col_… +    + 1.23 a a + 2.23 b b + 3.23 c c diff --git a/tests/testthat/out/multi-23.txt b/tests/testthat/out/multi-23.txt new file mode 100644 index 000000000..8dede0ff4 --- /dev/null +++ b/tests/testthat/out/multi-23.txt @@ -0,0 +1,5 @@ +column_ze… col_… col_… +    + 1.23 a a + 2.23 b b + 3.23 c c diff --git a/tests/testthat/out/multi-24.txt b/tests/testthat/out/multi-24.txt new file mode 100644 index 000000000..fda92f4a2 --- /dev/null +++ b/tests/testthat/out/multi-24.txt @@ -0,0 +1,5 @@ +colu… col_… col_… col_… +    + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-25.txt b/tests/testthat/out/multi-25.txt new file mode 100644 index 000000000..340be5a76 --- /dev/null +++ b/tests/testthat/out/multi-25.txt @@ -0,0 +1,5 @@ +colum… col_… col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-26.txt b/tests/testthat/out/multi-26.txt new file mode 100644 index 000000000..ae271339f --- /dev/null +++ b/tests/testthat/out/multi-26.txt @@ -0,0 +1,5 @@ +column… col_… col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-27.txt b/tests/testthat/out/multi-27.txt new file mode 100644 index 000000000..8b9eb016c --- /dev/null +++ b/tests/testthat/out/multi-27.txt @@ -0,0 +1,5 @@ +column_… col_… col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-28.txt b/tests/testthat/out/multi-28.txt new file mode 100644 index 000000000..269707970 --- /dev/null +++ b/tests/testthat/out/multi-28.txt @@ -0,0 +1,5 @@ +column_z… col_… col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-29.txt b/tests/testthat/out/multi-29.txt new file mode 100644 index 000000000..26901b047 --- /dev/null +++ b/tests/testthat/out/multi-29.txt @@ -0,0 +1,5 @@ +column_z… col_02 col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-30.txt b/tests/testthat/out/multi-30.txt new file mode 100644 index 000000000..e3aaaf81f --- /dev/null +++ b/tests/testthat/out/multi-30.txt @@ -0,0 +1,5 @@ +column_ze… col_02 col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-31.txt b/tests/testthat/out/multi-31.txt new file mode 100644 index 000000000..6e787131a --- /dev/null +++ b/tests/testthat/out/multi-31.txt @@ -0,0 +1,5 @@ +column_zer… col_02 col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-32.txt b/tests/testthat/out/multi-32.txt new file mode 100644 index 000000000..6f73cd293 --- /dev/null +++ b/tests/testthat/out/multi-32.txt @@ -0,0 +1,5 @@ +column_zero… col_02 col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-33.txt b/tests/testthat/out/multi-33.txt new file mode 100644 index 000000000..a26cc9fd9 --- /dev/null +++ b/tests/testthat/out/multi-33.txt @@ -0,0 +1,5 @@ +column_zero… col_02 col_03 col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-34.txt b/tests/testthat/out/multi-34.txt new file mode 100644 index 000000000..aac3df2ea --- /dev/null +++ b/tests/testthat/out/multi-34.txt @@ -0,0 +1,5 @@ +column_zero_one col_… col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-35.txt b/tests/testthat/out/multi-35.txt new file mode 100644 index 000000000..b19c0fc82 --- /dev/null +++ b/tests/testthat/out/multi-35.txt @@ -0,0 +1,5 @@ +column_zero_one col_02 col_… col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-36.txt b/tests/testthat/out/multi-36.txt new file mode 100644 index 000000000..a5e5193fa --- /dev/null +++ b/tests/testthat/out/multi-36.txt @@ -0,0 +1,5 @@ +column_zero_one col_02 col_03 col_… +     + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-37.txt b/tests/testthat/out/multi-37.txt new file mode 100644 index 000000000..8316ac658 --- /dev/null +++ b/tests/testthat/out/multi-37.txt @@ -0,0 +1,5 @@ +column_zero_one col_02 col_03 col_04 +      + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-38.txt b/tests/testthat/out/multi-38.txt new file mode 100644 index 000000000..8316ac658 --- /dev/null +++ b/tests/testthat/out/multi-38.txt @@ -0,0 +1,5 @@ +column_zero_one col_02 col_03 col_04 +      + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/multi-39.txt b/tests/testthat/out/multi-39.txt new file mode 100644 index 000000000..8316ac658 --- /dev/null +++ b/tests/testthat/out/multi-39.txt @@ -0,0 +1,5 @@ +column_zero_one col_02 col_03 col_04 +      + 1.23 a a a + 2.23 b b b + 3.23 c c c diff --git a/tests/testthat/out/numeric-04.txt b/tests/testthat/out/numeric-04.txt index a208fd0cc..0d95f2aec 100644 --- a/tests/testthat/out/numeric-04.txt +++ b/tests/testthat/out/numeric-04.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-07.txt b/tests/testthat/out/numeric-07.txt index a208fd0cc..0d95f2aec 100644 --- a/tests/testthat/out/numeric-07.txt +++ b/tests/testthat/out/numeric-07.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-10.txt b/tests/testthat/out/numeric-10.txt index 27e0d170c..f3fb10ddf 100644 --- a/tests/testthat/out/numeric-10.txt +++ b/tests/testthat/out/numeric-10.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-15.txt b/tests/testthat/out/numeric-15.txt index 282cc9aa2..088d9345c 100644 --- a/tests/testthat/out/numeric-15.txt +++ b/tests/testthat/out/numeric-15.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-22.txt b/tests/testthat/out/numeric-22.txt index e302755ab..915b2832f 100644 --- a/tests/testthat/out/numeric-22.txt +++ b/tests/testthat/out/numeric-22.txt @@ -1,4 +1,4 @@ -  +  0.00000000100 0.00000100 1000 diff --git a/tests/testthat/out/ordered.txt b/tests/testthat/out/ordered.txt index 9ff9f14ad..1c5dc622f 100644 --- a/tests/testthat/out/ordered.txt +++ b/tests/testthat/out/ordered.txt @@ -1,7 +1,7 @@ - -a -b -c -d -e -? + +a +b +c +d +e +? diff --git a/tests/testthat/out/scientific-short-neg.txt b/tests/testthat/out/scientific-short-neg.txt index 1cb501289..ec32f4d03 100644 --- a/tests/testthat/out/scientific-short-neg.txt +++ b/tests/testthat/out/scientific-short-neg.txt @@ -1,4 +1,4 @@ -  +  - 1.00e ³ 1.00e ⁹ - 1.00e¹⁵ diff --git a/tests/testthat/out/scientific.txt b/tests/testthat/out/scientific.txt index 346861d25..b933611f4 100644 --- a/tests/testthat/out/scientific.txt +++ b/tests/testthat/out/scientific.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/time.txt b/tests/testthat/out/time.txt index 18522276b..84c9ff627 100644 --- a/tests/testthat/out/time.txt +++ b/tests/testthat/out/time.txt @@ -1,3 +1,3 @@ -  +  2017-07-28 18:04:35 ? diff --git a/tests/testthat/out/title-crayon.txt b/tests/testthat/out/title-crayon.txt index c5b963cc4..e35501f9f 100644 --- a/tests/testthat/out/title-crayon.txt +++ b/tests/testthat/out/title-crayon.txt @@ -1,5 +1,5 @@ crayon -  +  10.0 100 1000 diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R new file mode 100644 index 000000000..ce3bd47ff --- /dev/null +++ b/tests/testthat/test-format_multi.R @@ -0,0 +1,41 @@ +context("format_multi") + +test_that("output test", { + x <- list(column_zero_one = 1:3 + 0.23, col_02 = letters[1:3], col_03 = factor(letters[1:3]), col_04 = ordered(letters[1:3])) + expect_colformat_output(xf = multicolformat(x, width = 4), filename = "multi-04.txt") + expect_colformat_output(xf = multicolformat(x, width = 5), filename = "multi-05.txt") + expect_colformat_output(xf = multicolformat(x, width = 6), filename = "multi-06.txt") + expect_colformat_output(xf = multicolformat(x, width = 7), filename = "multi-07.txt") + expect_colformat_output(xf = multicolformat(x, width = 8), filename = "multi-08.txt") + expect_colformat_output(xf = multicolformat(x, width = 9), filename = "multi-09.txt") + expect_colformat_output(xf = multicolformat(x, width = 10), filename = "multi-10.txt") + expect_colformat_output(xf = multicolformat(x, width = 11), filename = "multi-11.txt") + expect_colformat_output(xf = multicolformat(x, width = 12), filename = "multi-12.txt") + expect_colformat_output(xf = multicolformat(x, width = 13), filename = "multi-13.txt") + expect_colformat_output(xf = multicolformat(x, width = 14), filename = "multi-14.txt") + expect_colformat_output(xf = multicolformat(x, width = 15), filename = "multi-15.txt") + expect_colformat_output(xf = multicolformat(x, width = 16), filename = "multi-16.txt") + expect_colformat_output(xf = multicolformat(x, width = 17), filename = "multi-17.txt") + expect_colformat_output(xf = multicolformat(x, width = 18), filename = "multi-18.txt") + expect_colformat_output(xf = multicolformat(x, width = 19), filename = "multi-19.txt") + expect_colformat_output(xf = multicolformat(x, width = 20), filename = "multi-20.txt") + expect_colformat_output(xf = multicolformat(x, width = 21), filename = "multi-21.txt") + expect_colformat_output(xf = multicolformat(x, width = 22), filename = "multi-22.txt") + expect_colformat_output(xf = multicolformat(x, width = 23), filename = "multi-23.txt") + expect_colformat_output(xf = multicolformat(x, width = 24), filename = "multi-24.txt") + expect_colformat_output(xf = multicolformat(x, width = 25), filename = "multi-25.txt") + expect_colformat_output(xf = multicolformat(x, width = 26), filename = "multi-26.txt") + expect_colformat_output(xf = multicolformat(x, width = 27), filename = "multi-27.txt") + expect_colformat_output(xf = multicolformat(x, width = 28), filename = "multi-28.txt") + expect_colformat_output(xf = multicolformat(x, width = 29), filename = "multi-29.txt") + expect_colformat_output(xf = multicolformat(x, width = 30), filename = "multi-30.txt") + expect_colformat_output(xf = multicolformat(x, width = 31), filename = "multi-31.txt") + expect_colformat_output(xf = multicolformat(x, width = 32), filename = "multi-32.txt") + expect_colformat_output(xf = multicolformat(x, width = 33), filename = "multi-33.txt") + expect_colformat_output(xf = multicolformat(x, width = 34), filename = "multi-34.txt") + expect_colformat_output(xf = multicolformat(x, width = 35), filename = "multi-35.txt") + expect_colformat_output(xf = multicolformat(x, width = 36), filename = "multi-36.txt") + expect_colformat_output(xf = multicolformat(x, width = 37), filename = "multi-37.txt") + expect_colformat_output(xf = multicolformat(x, width = 38), filename = "multi-38.txt") + expect_colformat_output(xf = multicolformat(x, width = 39), filename = "multi-39.txt") +}) From fda757e0e0a56ab061c203efc7d61e74ca3d478d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 8 Aug 2017 20:24:48 +0200 Subject: [PATCH 051/133] Format integers without decimal period (#26) * format integers without decimal period * add braces --- R/sigfig.R | 6 +++++- tests/testthat/helper-output.R | 4 +++- tests/testthat/out/integer-06.txt | 5 +++++ tests/testthat/out/integer-07.txt | 5 +++++ tests/testthat/out/integer-08.txt | 5 +++++ tests/testthat/out/integer-09.txt | 5 +++++ tests/testthat/test-format_integer.R | 9 +++++++++ 7 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/out/integer-06.txt create mode 100644 tests/testthat/out/integer-07.txt create mode 100644 tests/testthat/out/integer-08.txt create mode 100644 tests/testthat/out/integer-09.txt create mode 100644 tests/testthat/test-format_integer.R diff --git a/R/sigfig.R b/R/sigfig.R index 27280f46f..de5c27240 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -77,7 +77,11 @@ compute_rhs_digits <- function(x, sigfig) { # Otherwise ensure we have sigfig digits shown exp <- compute_exp(x) exp[is.na(exp)] <- Inf - digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) + if (is.integer(x)) { + digits <- 0 + } else { + digits <- ifelse(exp > sigfig, 0, sigfig - exp - ifelse(exp <= 0, 1, 0)) + } rhs_digits <- pmax(digits - pmax(exp, 0), 0) rhs_digits } diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 046968d94..4d6575083 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -18,7 +18,9 @@ expect_colformat_output <- function(x, ..., filename, add_special <- function(x) { x <- c(x, NA) - if (is.numeric(x)) x <- c(x, -Inf, Inf) + if (is.numeric(x) && is.double(x)) { + x <- c(x, -Inf, Inf) + } x } diff --git a/tests/testthat/out/integer-06.txt b/tests/testthat/out/integer-06.txt new file mode 100644 index 000000000..c1e98244e --- /dev/null +++ b/tests/testthat/out/integer-06.txt @@ -0,0 +1,5 @@ +  + 1.00e⁷ + 1.00e⁷ + 1.00e⁷ +NA   diff --git a/tests/testthat/out/integer-07.txt b/tests/testthat/out/integer-07.txt new file mode 100644 index 000000000..c1e98244e --- /dev/null +++ b/tests/testthat/out/integer-07.txt @@ -0,0 +1,5 @@ +  + 1.00e⁷ + 1.00e⁷ + 1.00e⁷ +NA   diff --git a/tests/testthat/out/integer-08.txt b/tests/testthat/out/integer-08.txt new file mode 100644 index 000000000..cf69a061c --- /dev/null +++ b/tests/testthat/out/integer-08.txt @@ -0,0 +1,5 @@ +  +10000001 +10000002 +10000003 + NA diff --git a/tests/testthat/out/integer-09.txt b/tests/testthat/out/integer-09.txt new file mode 100644 index 000000000..d7640dc50 --- /dev/null +++ b/tests/testthat/out/integer-09.txt @@ -0,0 +1,5 @@ +  + 10000001 + 10000002 + 10000003 + NA diff --git a/tests/testthat/test-format_integer.R b/tests/testthat/test-format_integer.R new file mode 100644 index 000000000..e6f9890e3 --- /dev/null +++ b/tests/testthat/test-format_integer.R @@ -0,0 +1,9 @@ +context("format_numeric") + +test_that("integer output will use scientific if necessary", { + x <- 10000000L + 1:3 + expect_colformat_output(x, width = 6, filename = "integer-06.txt") + expect_colformat_output(x, width = 7, filename = "integer-07.txt") + expect_colformat_output(x, width = 8, filename = "integer-08.txt") + expect_colformat_output(x, width = 9, filename = "integer-09.txt") +}) From 08bc578db0d9fef354e3aaee25ecf8214fa498d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 8 Aug 2017 20:41:08 +0200 Subject: [PATCH 052/133] rename --- R/{title.R => col-title.R} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename R/{title.R => col-title.R} (100%) diff --git a/R/title.R b/R/col-title.R similarity index 100% rename from R/title.R rename to R/col-title.R From f1e17b458cbf1e77a1cf5695736e5ac83241cbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 8 Aug 2017 20:46:54 +0200 Subject: [PATCH 053/133] document --- R/colformat.R | 3 +++ man/colformat.Rd | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/R/colformat.R b/R/colformat.R index ff6da28de..8ca2a9718 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -1,5 +1,8 @@ #' Format a vector suitable for tabular display #' +#' `colformat()` formats a vector using one row for a title (if given), +#' one row for the type, and `length(x)` rows for the data. +#' #' @param x A vector to format #' @param title An optional title for the column #' @param width Default width, optional diff --git a/man/colformat.Rd b/man/colformat.Rd index 1776f10b0..8915bab30 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -16,7 +16,8 @@ colformat(x, title = NULL, width = NULL, ...) \item{...}{Other arguments passed to methods} } \description{ -Format a vector suitable for tabular display +`colformat()` formats a vector using one row for a title (if given), +one row for the type, and `length(x)` rows for the data. } \examples{ x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) From ec229367a8c3534d3d0bee65662ef4fa258c49c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 13 Aug 2017 11:26:23 +0200 Subject: [PATCH 054/133] New rowidformat() (#27) * new rowidformat() * use rowidformat() in multicolformat() * fix check * document * subtle styling for row IDs * document * add test result * oops * keep alignment --- NAMESPACE | 4 ++++ R/colformat.R | 11 +++++++++ R/multi.R | 15 +++++++++++-- R/rowid-data.R | 26 ++++++++++++++++++++++ R/rowid-title.R | 17 ++++++++++++++ R/rowid-type.R | 20 +++++++++++++++++ man/multicolformat.Rd | 5 ++++- tests/testthat/out/multi-04.txt | 6 ++++- tests/testthat/out/multi-05.txt | 6 ++++- tests/testthat/out/multi-06.txt | 10 ++++----- tests/testthat/out/multi-07.txt | 10 ++++----- tests/testthat/out/multi-08.txt | 8 +++---- tests/testthat/out/multi-09.txt | 8 +++---- tests/testthat/out/multi-10.txt | 8 +++---- tests/testthat/out/multi-11.txt | 8 +++---- tests/testthat/out/multi-12.txt | 10 ++++----- tests/testthat/out/multi-13.txt | 10 ++++----- tests/testthat/out/multi-14.txt | 8 +++---- tests/testthat/out/multi-15.txt | 8 +++---- tests/testthat/out/multi-16.txt | 8 +++---- tests/testthat/out/multi-17.txt | 8 +++---- tests/testthat/out/multi-18.txt | 10 ++++----- tests/testthat/out/multi-19.txt | 10 ++++----- tests/testthat/out/multi-20.txt | 8 +++---- tests/testthat/out/multi-21.txt | 8 +++---- tests/testthat/out/multi-22.txt | 8 +++---- tests/testthat/out/multi-23.txt | 8 +++---- tests/testthat/out/multi-24.txt | 10 ++++----- tests/testthat/out/multi-25.txt | 10 ++++----- tests/testthat/out/multi-26.txt | 8 +++---- tests/testthat/out/multi-27.txt | 8 +++---- tests/testthat/out/multi-28.txt | 8 +++---- tests/testthat/out/multi-29.txt | 10 ++++----- tests/testthat/out/multi-30.txt | 10 ++++----- tests/testthat/out/multi-31.txt | 8 +++---- tests/testthat/out/multi-32.txt | 8 +++---- tests/testthat/out/multi-33.txt | 10 ++++----- tests/testthat/out/multi-34.txt | 10 ++++----- tests/testthat/out/multi-35.txt | 10 ++++----- tests/testthat/out/multi-36.txt | 10 ++++----- tests/testthat/out/multi-37.txt | 10 ++++----- tests/testthat/out/multi-38.txt | 10 ++++----- tests/testthat/out/multi-39.txt | 10 ++++----- tests/testthat/out/rowid-3.txt | 4 ++++ tests/testthat/out/rowid-star-title-12.txt | 14 ++++++++++++ tests/testthat/test-format_rowid.R | 12 ++++++++++ 46 files changed, 288 insertions(+), 158 deletions(-) create mode 100644 R/rowid-data.R create mode 100644 R/rowid-title.R create mode 100644 R/rowid-type.R create mode 100644 tests/testthat/out/rowid-3.txt create mode 100644 tests/testthat/out/rowid-star-title-12.txt create mode 100644 tests/testthat/test-format_rowid.R diff --git a/NAMESPACE b/NAMESPACE index f8be53d50..0c9ef5f11 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,6 +13,9 @@ S3method(format,cf_type) S3method(format,colformat) S3method(format,decimal_format) S3method(format,multicolformat) +S3method(format,rif_data) +S3method(format,rif_title) +S3method(format,rif_type) S3method(is_vector_s3,Date) S3method(is_vector_s3,POSIXct) S3method(is_vector_s3,data.frame) @@ -28,6 +31,7 @@ S3method(print,cf_vertical) S3method(print,colformat) S3method(print,decimal_format) S3method(print,multicolformat) +S3method(print,rif_data) S3method(print,spark) S3method(type_sum,Date) S3method(type_sum,POSIXt) diff --git a/R/colformat.R b/R/colformat.R index 8ca2a9718..ec7726e67 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -45,6 +45,17 @@ colformat <- function(x, title = NULL, width = NULL, ...) { ret } +rowidformat <- function(n, has_title_row = FALSE, has_star = FALSE, ...) { + title <- rif_title(has_title_row, ...) + type <- rif_type(has_star, ...) + data <- rif_data(n, ...) + ret <- structure( + list(title = title, type = type, data = data), + class = "colformat" + ) + ret +} + #' @export format.colformat <- function(x, width = NULL, ...) { width <- cf_get_width(x, width) diff --git a/R/multi.R b/R/multi.R index b81b4c74d..3bdea7e8d 100644 --- a/R/multi.R +++ b/R/multi.R @@ -4,14 +4,25 @@ #' characters per row. #' #' @param x A list of vectors to format +#' @param has_row_id Include a column indicating row IDs? Pass `"*"` to mark +#' the row ID column with a star. #' @param width Default width of the entire output, optional #' @param ... Ignored -multicolformat <- function(x, width = NULL, ...) { - if (is_named(x)) { +multicolformat <- function(x, has_row_id = TRUE, width = NULL, ...) { + has_title <- is_named(x) + if (has_title) { ret <- map2(x, names(x), colformat) } else { ret <- map(x, colformat) } + if (!is_false(has_row_id) && length(x) > 0) { + rowid <- rowidformat( + length(x[[1]]), + has_star = identical(has_row_id, "*"), + has_title_row = has_title + ) + ret <- c(list(rowid), ret) + } ret <- structure(ret, class = "multicolformat") ret <- set_width(ret, width) ret diff --git a/R/rowid-data.R b/R/rowid-data.R new file mode 100644 index 000000000..7ba30dcb7 --- /dev/null +++ b/R/rowid-data.R @@ -0,0 +1,26 @@ +style_rowid <- function(x) { + style_subtle(x) +} + +rif_data <- function(n, ...) { + ret <- structure( + list(n = n), + class = "rif_data" + ) + ret <- set_width(ret, as.integer(floor(log10(n)) + 1)) + ret +} + +#' @export +format.rif_data <- function(x, width, ...) { + new_column( + style_rowid(format(seq_len(x$n), width = width)), + width = width, + align = "right" + ) +} + +#' @export +print.rif_data <- function(x, ...) { + print(format(x, ...)) +} diff --git a/R/rowid-title.R b/R/rowid-title.R new file mode 100644 index 000000000..64c6d0606 --- /dev/null +++ b/R/rowid-title.R @@ -0,0 +1,17 @@ +rif_title <- function(has_title, ...) { + ret <- structure( + list( + has_title = has_title + ), + class = "rif_title" + ) + + ret <- set_width(ret, 0L) + ret +} + +#' @export +format.rif_title <- function(x, width, ...) { + if (!x$has_title) character() + else "" +} diff --git a/R/rowid-type.R b/R/rowid-type.R new file mode 100644 index 000000000..6cfd88adb --- /dev/null +++ b/R/rowid-type.R @@ -0,0 +1,20 @@ +style_star <- function(x) { + style_subtle(x) +} + +rif_type <- function(has_star, ...) { + star <- if (has_star) "*" else "" + ret <- structure( + list( + star = star + ), + class = "rif_type" + ) + ret <- set_width(ret, 1L) + ret +} + +#' @export +format.rif_type <- function(x, width = NULL, ...) { + style_star(x$star) +} diff --git a/man/multicolformat.Rd b/man/multicolformat.Rd index 402bb8e57..ad7cfa2da 100644 --- a/man/multicolformat.Rd +++ b/man/multicolformat.Rd @@ -4,11 +4,14 @@ \alias{multicolformat} \title{Format multiple vectors in a tabular display} \usage{ -multicolformat(x, width = NULL, ...) +multicolformat(x, has_row_id = TRUE, width = NULL, ...) } \arguments{ \item{x}{A list of vectors to format} +\item{has_row_id}{Include a column indicating row IDs? Pass `"*"` to mark +the row ID column with a star.} + \item{width}{Default width of the entire output, optional} \item{...}{Ignored} diff --git a/tests/testthat/out/multi-04.txt b/tests/testthat/out/multi-04.txt index 8b1378917..56ba19759 100644 --- a/tests/testthat/out/multi-04.txt +++ b/tests/testthat/out/multi-04.txt @@ -1 +1,5 @@ - + +  +1 +2 +3 diff --git a/tests/testthat/out/multi-05.txt b/tests/testthat/out/multi-05.txt index 8b1378917..56ba19759 100644 --- a/tests/testthat/out/multi-05.txt +++ b/tests/testthat/out/multi-05.txt @@ -1 +1,5 @@ - + +  +1 +2 +3 diff --git a/tests/testthat/out/multi-06.txt b/tests/testthat/out/multi-06.txt index bfe550c84..56ba19759 100644 --- a/tests/testthat/out/multi-06.txt +++ b/tests/testthat/out/multi-06.txt @@ -1,5 +1,5 @@ -colu… - - 1.23 - 2.23 - 3.23 + +  +1 +2 +3 diff --git a/tests/testthat/out/multi-07.txt b/tests/testthat/out/multi-07.txt index e02c5b154..56ba19759 100644 --- a/tests/testthat/out/multi-07.txt +++ b/tests/testthat/out/multi-07.txt @@ -1,5 +1,5 @@ -colum… -  - 1.23 - 2.23 - 3.23 + +  +1 +2 +3 diff --git a/tests/testthat/out/multi-08.txt b/tests/testthat/out/multi-08.txt index 5a0e41f17..1fc41fe8b 100644 --- a/tests/testthat/out/multi-08.txt +++ b/tests/testthat/out/multi-08.txt @@ -1,5 +1,5 @@ -column… + colu…   - 1.23 - 2.23 - 3.23 +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out/multi-09.txt b/tests/testthat/out/multi-09.txt index 07c24dbde..604f37433 100644 --- a/tests/testthat/out/multi-09.txt +++ b/tests/testthat/out/multi-09.txt @@ -1,5 +1,5 @@ -column_… + colum…   - 1.23 - 2.23 - 3.23 +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out/multi-10.txt b/tests/testthat/out/multi-10.txt index ac6325fdb..4264fc1a8 100644 --- a/tests/testthat/out/multi-10.txt +++ b/tests/testthat/out/multi-10.txt @@ -1,5 +1,5 @@ -column_z… + column…   - 1.23 - 2.23 - 3.23 +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out/multi-11.txt b/tests/testthat/out/multi-11.txt index b4be4d43c..26b4236d5 100644 --- a/tests/testthat/out/multi-11.txt +++ b/tests/testthat/out/multi-11.txt @@ -1,5 +1,5 @@ -column_ze… + column_…   - 1.23 - 2.23 - 3.23 +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out/multi-12.txt b/tests/testthat/out/multi-12.txt index 2d4bd076e..e2dbaf76a 100644 --- a/tests/testthat/out/multi-12.txt +++ b/tests/testthat/out/multi-12.txt @@ -1,5 +1,5 @@ -colu… col_… -  - 1.23 a - 2.23 b - 3.23 c + column_z… +  +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out/multi-13.txt b/tests/testthat/out/multi-13.txt index 572285a68..c21e50adb 100644 --- a/tests/testthat/out/multi-13.txt +++ b/tests/testthat/out/multi-13.txt @@ -1,5 +1,5 @@ -colum… col_… -   - 1.23 a - 2.23 b - 3.23 c + column_ze… +  +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out/multi-14.txt b/tests/testthat/out/multi-14.txt index c8e8a4a22..da61ec00c 100644 --- a/tests/testthat/out/multi-14.txt +++ b/tests/testthat/out/multi-14.txt @@ -1,5 +1,5 @@ -column… col_… + colu… col_…    - 1.23 a - 2.23 b - 3.23 c +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out/multi-15.txt b/tests/testthat/out/multi-15.txt index 7e1118975..aabe82176 100644 --- a/tests/testthat/out/multi-15.txt +++ b/tests/testthat/out/multi-15.txt @@ -1,5 +1,5 @@ -column_… col_… + colum… col_…    - 1.23 a - 2.23 b - 3.23 c +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out/multi-16.txt b/tests/testthat/out/multi-16.txt index 89d1447a5..3ef1c9f7a 100644 --- a/tests/testthat/out/multi-16.txt +++ b/tests/testthat/out/multi-16.txt @@ -1,5 +1,5 @@ -column_z… col_… + column… col_…    - 1.23 a - 2.23 b - 3.23 c +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out/multi-17.txt b/tests/testthat/out/multi-17.txt index 7868ff47f..871aed019 100644 --- a/tests/testthat/out/multi-17.txt +++ b/tests/testthat/out/multi-17.txt @@ -1,5 +1,5 @@ -column_ze… col_… + column_… col_…    - 1.23 a - 2.23 b - 3.23 c +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out/multi-18.txt b/tests/testthat/out/multi-18.txt index c2990baf6..5cf85cf8c 100644 --- a/tests/testthat/out/multi-18.txt +++ b/tests/testthat/out/multi-18.txt @@ -1,5 +1,5 @@ -colu… col_… col_… -   - 1.23 a a - 2.23 b b - 3.23 c c + column_z… col_… +   +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out/multi-19.txt b/tests/testthat/out/multi-19.txt index 2bd7e4a27..b4c1a67af 100644 --- a/tests/testthat/out/multi-19.txt +++ b/tests/testthat/out/multi-19.txt @@ -1,5 +1,5 @@ -colum… col_… col_… -    - 1.23 a a - 2.23 b b - 3.23 c c + column_ze… col_… +   +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out/multi-20.txt b/tests/testthat/out/multi-20.txt index 3bdec1f7c..0bec644a0 100644 --- a/tests/testthat/out/multi-20.txt +++ b/tests/testthat/out/multi-20.txt @@ -1,5 +1,5 @@ -column… col_… col_… + colu… col_… col_…     - 1.23 a a - 2.23 b b - 3.23 c c +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out/multi-21.txt b/tests/testthat/out/multi-21.txt index 282e89d2a..843a737ee 100644 --- a/tests/testthat/out/multi-21.txt +++ b/tests/testthat/out/multi-21.txt @@ -1,5 +1,5 @@ -column_… col_… col_… + colum… col_… col_…     - 1.23 a a - 2.23 b b - 3.23 c c +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out/multi-22.txt b/tests/testthat/out/multi-22.txt index d90f8134b..fd5852ee9 100644 --- a/tests/testthat/out/multi-22.txt +++ b/tests/testthat/out/multi-22.txt @@ -1,5 +1,5 @@ -column_z… col_… col_… + column… col_… col_…     - 1.23 a a - 2.23 b b - 3.23 c c +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out/multi-23.txt b/tests/testthat/out/multi-23.txt index 8dede0ff4..79c25a9f0 100644 --- a/tests/testthat/out/multi-23.txt +++ b/tests/testthat/out/multi-23.txt @@ -1,5 +1,5 @@ -column_ze… col_… col_… + column_… col_… col_…     - 1.23 a a - 2.23 b b - 3.23 c c +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out/multi-24.txt b/tests/testthat/out/multi-24.txt index fda92f4a2..a885bc721 100644 --- a/tests/testthat/out/multi-24.txt +++ b/tests/testthat/out/multi-24.txt @@ -1,5 +1,5 @@ -colu… col_… col_… col_… -    - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_z… col_… col_… +    +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out/multi-25.txt b/tests/testthat/out/multi-25.txt index 340be5a76..46da975a3 100644 --- a/tests/testthat/out/multi-25.txt +++ b/tests/testthat/out/multi-25.txt @@ -1,5 +1,5 @@ -colum… col_… col_… col_… -     - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_ze… col_… col_… +    +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out/multi-26.txt b/tests/testthat/out/multi-26.txt index ae271339f..436367a26 100644 --- a/tests/testthat/out/multi-26.txt +++ b/tests/testthat/out/multi-26.txt @@ -1,5 +1,5 @@ -column… col_… col_… col_… + colu… col_… col_… col_…      - 1.23 a a a - 2.23 b b b - 3.23 c c c +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-27.txt b/tests/testthat/out/multi-27.txt index 8b9eb016c..0f0a46f51 100644 --- a/tests/testthat/out/multi-27.txt +++ b/tests/testthat/out/multi-27.txt @@ -1,5 +1,5 @@ -column_… col_… col_… col_… + colum… col_… col_… col_…      - 1.23 a a a - 2.23 b b b - 3.23 c c c +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-28.txt b/tests/testthat/out/multi-28.txt index 269707970..ab441302a 100644 --- a/tests/testthat/out/multi-28.txt +++ b/tests/testthat/out/multi-28.txt @@ -1,5 +1,5 @@ -column_z… col_… col_… col_… + column… col_… col_… col_…      - 1.23 a a a - 2.23 b b b - 3.23 c c c +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-29.txt b/tests/testthat/out/multi-29.txt index 26901b047..fc8a9ebed 100644 --- a/tests/testthat/out/multi-29.txt +++ b/tests/testthat/out/multi-29.txt @@ -1,5 +1,5 @@ -column_z… col_02 col_… col_… -     - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_… col_… col_… col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-30.txt b/tests/testthat/out/multi-30.txt index e3aaaf81f..905ea3198 100644 --- a/tests/testthat/out/multi-30.txt +++ b/tests/testthat/out/multi-30.txt @@ -1,5 +1,5 @@ -column_ze… col_02 col_… col_… -     - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_z… col_… col_… col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-31.txt b/tests/testthat/out/multi-31.txt index 6e787131a..fb11a7b80 100644 --- a/tests/testthat/out/multi-31.txt +++ b/tests/testthat/out/multi-31.txt @@ -1,5 +1,5 @@ -column_zer… col_02 col_… col_… + column_z… col_02 col_… col_…      - 1.23 a a a - 2.23 b b b - 3.23 c c c +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-32.txt b/tests/testthat/out/multi-32.txt index 6f73cd293..04372bbd5 100644 --- a/tests/testthat/out/multi-32.txt +++ b/tests/testthat/out/multi-32.txt @@ -1,5 +1,5 @@ -column_zero… col_02 col_… col_… + column_ze… col_02 col_… col_…      - 1.23 a a a - 2.23 b b b - 3.23 c c c +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-33.txt b/tests/testthat/out/multi-33.txt index a26cc9fd9..8fd85deeb 100644 --- a/tests/testthat/out/multi-33.txt +++ b/tests/testthat/out/multi-33.txt @@ -1,5 +1,5 @@ -column_zero… col_02 col_03 col_… -     - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_zer… col_02 col_… col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-34.txt b/tests/testthat/out/multi-34.txt index aac3df2ea..1cd38c778 100644 --- a/tests/testthat/out/multi-34.txt +++ b/tests/testthat/out/multi-34.txt @@ -1,5 +1,5 @@ -column_zero_one col_… col_… col_… -     - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_zero… col_02 col_… col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-35.txt b/tests/testthat/out/multi-35.txt index b19c0fc82..f329c7c28 100644 --- a/tests/testthat/out/multi-35.txt +++ b/tests/testthat/out/multi-35.txt @@ -1,5 +1,5 @@ -column_zero_one col_02 col_… col_… -     - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_zero… col_02 col_03 col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-36.txt b/tests/testthat/out/multi-36.txt index a5e5193fa..4c54dbe67 100644 --- a/tests/testthat/out/multi-36.txt +++ b/tests/testthat/out/multi-36.txt @@ -1,5 +1,5 @@ -column_zero_one col_02 col_03 col_… -     - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_zero_one col_… col_… col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-37.txt b/tests/testthat/out/multi-37.txt index 8316ac658..8ada97ef7 100644 --- a/tests/testthat/out/multi-37.txt +++ b/tests/testthat/out/multi-37.txt @@ -1,5 +1,5 @@ -column_zero_one col_02 col_03 col_04 -      - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_zero_one col_02 col_… col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-38.txt b/tests/testthat/out/multi-38.txt index 8316ac658..17e5b5382 100644 --- a/tests/testthat/out/multi-38.txt +++ b/tests/testthat/out/multi-38.txt @@ -1,5 +1,5 @@ -column_zero_one col_02 col_03 col_04 -      - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_zero_one col_02 col_03 col_… +     +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/multi-39.txt b/tests/testthat/out/multi-39.txt index 8316ac658..765c45fed 100644 --- a/tests/testthat/out/multi-39.txt +++ b/tests/testthat/out/multi-39.txt @@ -1,5 +1,5 @@ -column_zero_one col_02 col_03 col_04 -      - 1.23 a a a - 2.23 b b b - 3.23 c c c + column_zero_one col_02 col_03 col_04 +      +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out/rowid-3.txt b/tests/testthat/out/rowid-3.txt new file mode 100644 index 000000000..1a77ce9b5 --- /dev/null +++ b/tests/testthat/out/rowid-3.txt @@ -0,0 +1,4 @@ +  +1 +2 +3 diff --git a/tests/testthat/out/rowid-star-title-12.txt b/tests/testthat/out/rowid-star-title-12.txt new file mode 100644 index 000000000..4d8751ee2 --- /dev/null +++ b/tests/testthat/out/rowid-star-title-12.txt @@ -0,0 +1,14 @@ + + * + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 diff --git a/tests/testthat/test-format_rowid.R b/tests/testthat/test-format_rowid.R new file mode 100644 index 000000000..5d20ef900 --- /dev/null +++ b/tests/testthat/test-format_rowid.R @@ -0,0 +1,12 @@ +context("format_rowid") + +test_that("output test", { + expect_colformat_output( + xf = rowidformat(3), + filename = "rowid-3.txt" + ) + expect_colformat_output( + xf = rowidformat(12, has_title = TRUE, has_star = TRUE), + filename = "rowid-star-title-12.txt" + ) +}) From aa947e721e20d119d531f016309572ca322a16ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 4 Sep 2017 16:45:06 +0200 Subject: [PATCH 055/133] Prepare for tibble integration (#33) * export * extra_cols() * fix corner case: vectors of length zero * remove output if no rows * Revert "remove output if no rows" This reverts commit 647bae3514a8ee64781f41208b5b3242d21a8c08. * oops * minor formatting changes for tibble compatibility * remove output if no rows necessary for tibble * fix formatting of wide characters * left-align logical * explicit arg name * oops * new squeeze() * export squeeze() for tibble * PR merged now * support knit_print() * continuous underline * use letters for logical, closes #29 * NA uses brackets if color not available, closes #30 * fix loading code * oops * suggest knitr * oops * no special handling for POSIXlt * add tests from tibble * use crayon::col_substr() * adapt and add tests --- DESCRIPTION | 4 +- NAMESPACE | 10 ++- R/col-data.R | 25 ++++--- R/col-type.R | 8 +- R/colformat.R | 8 +- R/column.R | 10 ++- R/multi.R | 82 +++++++++++++++++++-- R/rowid-data.R | 2 +- R/sigfig.R | 26 ++++--- R/styles.R | 7 +- R/type-sum.R | 2 +- R/utils.R | 2 +- R/zzz.R | 33 ++++++++- man/cf_data.Rd | 4 +- man/extra_cols.Rd | 18 +++++ man/multicolformat.Rd | 8 ++ tests/testthat/helper-output.R | 13 ++++ tests/testthat/out/basic.txt | 2 +- tests/testthat/out/date.txt | 6 +- tests/testthat/out/deal1.txt | 4 + tests/testthat/out/deal2.txt | 4 + tests/testthat/out/deal3.txt | 4 + tests/testthat/out/decimal-insignif.txt | 2 +- tests/testthat/out/escaped.txt | 2 +- tests/testthat/out/factor.txt | 2 +- tests/testthat/out/integer-06.txt | 2 +- tests/testthat/out/integer-07.txt | 2 +- tests/testthat/out/integer-08.txt | 2 +- tests/testthat/out/integer-09.txt | 2 +- tests/testthat/out/letters-long-03.txt | 2 +- tests/testthat/out/letters-long-10.txt | 4 +- tests/testthat/out/letters-long.txt | 4 +- tests/testthat/out/letters.txt | 2 +- tests/testthat/out/list-each.txt | 2 +- tests/testthat/out/list-na.txt | 2 +- tests/testthat/out/list-narrow.txt | 2 + tests/testthat/out/list-null.txt | 2 +- tests/testthat/out/logical.txt | 6 +- tests/testthat/out/multi-04.txt | 2 +- tests/testthat/out/multi-05.txt | 2 +- tests/testthat/out/multi-06.txt | 2 +- tests/testthat/out/multi-07.txt | 2 +- tests/testthat/out/multi-08.txt | 2 +- tests/testthat/out/multi-09.txt | 2 +- tests/testthat/out/multi-10.txt | 2 +- tests/testthat/out/multi-11.txt | 2 +- tests/testthat/out/multi-12.txt | 2 +- tests/testthat/out/multi-13.txt | 2 +- tests/testthat/out/multi-14.txt | 2 +- tests/testthat/out/multi-15.txt | 2 +- tests/testthat/out/multi-16.txt | 2 +- tests/testthat/out/multi-17.txt | 2 +- tests/testthat/out/multi-18.txt | 2 +- tests/testthat/out/multi-19.txt | 2 +- tests/testthat/out/multi-20.txt | 2 +- tests/testthat/out/multi-21.txt | 2 +- tests/testthat/out/multi-22.txt | 2 +- tests/testthat/out/multi-23.txt | 2 +- tests/testthat/out/multi-24.txt | 2 +- tests/testthat/out/multi-25.txt | 2 +- tests/testthat/out/multi-26.txt | 2 +- tests/testthat/out/multi-27.txt | 2 +- tests/testthat/out/multi-28.txt | 2 +- tests/testthat/out/multi-29.txt | 2 +- tests/testthat/out/multi-30.txt | 2 +- tests/testthat/out/multi-31.txt | 2 +- tests/testthat/out/multi-32.txt | 2 +- tests/testthat/out/multi-33.txt | 2 +- tests/testthat/out/multi-34.txt | 2 +- tests/testthat/out/multi-35.txt | 2 +- tests/testthat/out/multi-36.txt | 2 +- tests/testthat/out/multi-37.txt | 2 +- tests/testthat/out/multi-38.txt | 2 +- tests/testthat/out/multi-39.txt | 2 +- tests/testthat/out/numeric-04.txt | 2 +- tests/testthat/out/numeric-07.txt | 2 +- tests/testthat/out/numeric-10.txt | 2 +- tests/testthat/out/numeric-15.txt | 2 +- tests/testthat/out/numeric-22.txt | 2 +- tests/testthat/out/ordered.txt | 2 +- tests/testthat/out/rowid-3.txt | 2 +- tests/testthat/out/rowid-star-title-12.txt | 2 +- tests/testthat/out/scientific-short-neg.txt | 2 +- tests/testthat/out/scientific.txt | 2 +- tests/testthat/out/tibble-all--30.txt | 5 ++ tests/testthat/out/tibble-all--300.txt | 5 ++ tests/testthat/out/tibble-iris-3-20.txt | 5 ++ tests/testthat/out/tibble-iris-5-30.txt | 7 ++ tests/testthat/out/tibble-mtcars-8-30.txt | 10 +++ tests/testthat/out/tibble-newline.txt | 4 + tests/testthat/out/tibble-non-syntactic.txt | 3 + tests/testthat/out/time-posix.txt | 3 + tests/testthat/out/time.txt | 4 +- tests/testthat/out/title-crayon.txt | 2 +- tests/testthat/test-format_character.R | 3 + tests/testthat/test-format_decimal.R | 5 ++ tests/testthat/test-format_list.R | 1 + tests/testthat/test-format_multi.R | 49 ++++++++++++ tests/testthat/test-format_time.R | 1 + 99 files changed, 402 insertions(+), 119 deletions(-) create mode 100644 man/extra_cols.Rd create mode 100644 tests/testthat/out/deal1.txt create mode 100644 tests/testthat/out/deal2.txt create mode 100644 tests/testthat/out/deal3.txt create mode 100644 tests/testthat/out/list-narrow.txt create mode 100644 tests/testthat/out/tibble-all--30.txt create mode 100644 tests/testthat/out/tibble-all--300.txt create mode 100644 tests/testthat/out/tibble-iris-3-20.txt create mode 100644 tests/testthat/out/tibble-iris-5-30.txt create mode 100644 tests/testthat/out/tibble-mtcars-8-30.txt create mode 100644 tests/testthat/out/tibble-newline.txt create mode 100644 tests/testthat/out/tibble-non-syntactic.txt create mode 100644 tests/testthat/out/time-posix.txt diff --git a/DESCRIPTION b/DESCRIPTION index dbd92528e..bdc4dd062 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,7 +16,9 @@ Imports: crayon, methods, rlang +Suggests: + knitr, + testthat RoxygenNote: 6.0.1 -Suggests: testthat Remotes: gaborcsardi/crayon diff --git a/NAMESPACE b/NAMESPACE index 0c9ef5f11..a84ae92c2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,17 +1,19 @@ # Generated by roxygen2: do not edit by hand S3method(cf_data,Date) -S3method(cf_data,POSIXct) +S3method(cf_data,POSIXt) S3method(cf_data,character) S3method(cf_data,default) S3method(cf_data,list) S3method(cf_data,logical) S3method(cf_data,numeric) +S3method(extra_cols,mcf_squeezed) S3method(format,cf_data) S3method(format,cf_title) S3method(format,cf_type) S3method(format,colformat) S3method(format,decimal_format) +S3method(format,mcf_squeezed) S3method(format,multicolformat) S3method(format,rif_data) S3method(format,rif_title) @@ -30,11 +32,12 @@ S3method(print,cf_data) S3method(print,cf_vertical) S3method(print,colformat) S3method(print,decimal_format) +S3method(print,mcf_squeezed) S3method(print,multicolformat) S3method(print,rif_data) S3method(print,spark) S3method(type_sum,Date) -S3method(type_sum,POSIXt) +S3method(type_sum,POSIXct) S3method(type_sum,data.frame) S3method(type_sum,default) S3method(type_sum,difftime) @@ -44,11 +47,14 @@ S3method(type_sum,tbl_df) export(cf_data) export(colformat) export(dim_desc) +export(extra_cols) export(format_decimal) export(format_scientific) export(is_vector_s3) +export(multicolformat) export(obj_sum) export(spark_bar) export(spark_line) +export(squeeze) export(type_sum) import(rlang) diff --git a/R/col-data.R b/R/col-data.R index 398a132e0..207501c1f 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -27,8 +27,8 @@ print.cf_data <- function(x, ...) { print(format(x, ...)) } -new_cf_data <- function(x, width = max(crayon::col_nchar(x)), align = "left", - min_width = NULL) { +new_cf_data <- function(x, width = max(crayon::col_nchar(x, type = "width"), 0L), + align = "left", min_width = NULL) { ret <- structure( x, align = align, @@ -45,11 +45,11 @@ new_cf_data <- function(x, width = max(crayon::col_nchar(x)), align = "left", #' @rdname cf_data cf_data.logical <- function(x, ...) { out <- character(length(x)) - out[x & !is.na(x)] <- style_accent("*") - out[!x & !is.na(x)] <- style_subtle("-") + out[x] <- style_accent("T") + out[!x] <- style_subtle("F") out[is.na(x)] <- cf_na() - new_cf_data(out, width = 1, align = "right") + new_cf_data(out, width = 1, align = "left") } #' @export @@ -77,29 +77,32 @@ cf_data.Date <- function(x, ...) { x <- format(x, format = "%Y-%m-%d") x[is.na(x)] <- cf_na() - new_cf_data(x, width = 11, align = "right") + new_cf_data(x, width = 10, align = "left") } #' @export #' @rdname cf_data -cf_data.POSIXct <- function(x, ...) { +cf_data.POSIXt <- function(x, ...) { date <- format(x, format = "%Y-%m-%d") time <- format(x, format = "%H:%M:%S") datetime <- paste0(date, " " , style_subtle(time)) datetime[is.na(x)] <- cf_na() - new_cf_data(datetime, width = 19, align = "right") + new_cf_data(datetime, width = 19, align = "left") } #' @export #' @rdname cf_data cf_data.character <- function(x, ...) { - out <- encodeString(x, na.encode = FALSE) - out[is.na(out)] <- cf_na() + out <- x + needs_quotes <- which(!is_syntactic(x)) + is_na <- is.na(x) + out[needs_quotes] <- encodeString(x[needs_quotes], quote ='"', na.encode = FALSE) + out[is_na] <- cf_na(use_brackets_if_no_color = TRUE) - width <- max(crayon::col_nchar(out)) + width <- max(crayon::col_nchar(out, type = "width"), 0L) new_cf_data(out, width = width, align = "left", min_width = min(width, 3L)) } diff --git a/R/col-type.R b/R/col-type.R index 357971560..5e9214d2e 100644 --- a/R/col-type.R +++ b/R/col-type.R @@ -1,5 +1,5 @@ style_type <- function(x) { - style_subtle(x) + crayon::underline(style_subtle(x)) } cf_type <- function(x, ...) { @@ -18,6 +18,8 @@ cf_type <- function(x, ...) { #' @export format.cf_type <- function(x, width = NULL, ...) { if (is.null(width) || width >= get_width(x)) type <- x$type - else type <- substr(x$type, 1, width - 2) - style_type(paste0("<", type, ">")) + else type <- crayon::col_substr(x$type, 1, width - 2) + # Style is applied later on because we want a continuous underline over the + # whole width and over all columns in multicolformat() + paste0("<", type, ">") } diff --git a/R/colformat.R b/R/colformat.R index ec7726e67..9cdbd81f1 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -61,7 +61,7 @@ format.colformat <- function(x, width = NULL, ...) { width <- cf_get_width(x, width) out <- cf_format_parts(x, width) - cf_data <- c(out$title_format, crayon::underline(out$type_format), out$data_format) + cf_data <- c(out$title_format, style_type(out$type_format), out$data_format) new_vertical(cf_data) } @@ -103,3 +103,9 @@ cf_format_parts <- function(x, width, ...) { data_format = data_format ) } + +cf_format_abbrev <- function(x, ...) { + title_format <- format(x$title, width = Inf, ...) + type_format <- format(x$type, width = Inf, ...) + paste0(title_format, "\u00a0", type_format) +} diff --git a/R/column.R b/R/column.R index 424b5f76f..45a89b4db 100644 --- a/R/column.R +++ b/R/column.R @@ -8,15 +8,19 @@ new_column <- function(row, width = NULL, align = NULL) { ret } -new_vertical <- function(row) { +new_vertical <- function(row, ..., extra_class = NULL) { ret <- structure( row, - class = "cf_vertical" + ..., + class = c(extra_class, "cf_vertical") ) ret } #' @export print.cf_vertical <- function(x, ...) { - cat_line(paste(x, collapse = "\n")) + if (length(x) > 0) { + cat_line(paste(x, collapse = "\n")) + } + invisible(x) } diff --git a/R/multi.R b/R/multi.R index 3bdea7e8d..53cf6d54d 100644 --- a/R/multi.R +++ b/R/multi.R @@ -8,6 +8,7 @@ #' the row ID column with a star. #' @param width Default width of the entire output, optional #' @param ... Ignored +#' @export multicolformat <- function(x, has_row_id = TRUE, width = NULL, ...) { has_title <- is_named(x) if (has_title) { @@ -23,13 +24,24 @@ multicolformat <- function(x, has_row_id = TRUE, width = NULL, ...) { ) ret <- c(list(rowid), ret) } - ret <- structure(ret, class = "multicolformat") + zero_height <- length(x) == 0L || length(x[[1]]) == 0L + ret <- structure(ret, zero_height = zero_height, class = "multicolformat") ret <- set_width(ret, width) ret } +#' @description +#' The `squeeze()` function is called by [format()] and [print()] and usually +#' doesn't need to be called manually. +#' It returns an object suitable for printing and formatting at a fixed width +#' with additional information about omitted columns. +#' +#' @rdname multicolformat #' @export -format.multicolformat <- function(x, width = NULL, ...) { +squeeze <- function(x, width = NULL, ...) { + # Hacky shortcut for zero-height corner case + if (attr(x, "zero_height")) return(new_mcf_sqeezed(character(), x[names2(x) != ""])) + if (is.null(width)) { width <- get_width(x) } @@ -40,13 +52,69 @@ format.multicolformat <- function(x, width = NULL, ...) { col_widths <- mcf_get_width(x, width) out <- map2(x[seq_along(col_widths)], col_widths, cf_format_parts) - mcf_data <- c( - invoke(paste, map(out, `[[`, "title_format")), - crayon::underline(invoke(paste, map(out, `[[`, "type_format"))), - invoke(paste, map(out, `[[`, "data_format")) + + new_mcf_sqeezed(out, x[seq2_along(length(col_widths) + 1L, x)]) +} + +new_mcf_sqeezed <- function(x, extra_cols) { + structure( + x, + extra_cols = map_chr(extra_cols, cf_format_abbrev), + class = "mcf_squeezed" ) +} + +#' @export +format.mcf_squeezed <- function(x, ...) { + xt <- list( + title = map(x, `[[`, "title_format"), + type = map(x, `[[`, "type_format"), + data = map(x, `[[`, "data_format") + ) + + formatted <- c( + invoke(paste, xt$title), + style_type(invoke(paste, xt$type)), + invoke(paste, xt$data) + ) + + new_vertical(formatted) +} - new_vertical(mcf_data) +#' @export +print.mcf_squeezed <- function(x, ...) { + print(format(x, ...), ...) + invisible(x) +} + +# Method registration happens in .onLoad() +knit_print.mcf_squeezed <- function(x, ...) { + header <- map_chr(x, `[[`, "title_format") + col <- map(x, function(xx) c(xx[["type_format"]], xx[["data_format"]])) + + knitr::kable(as.data.frame(col), row.names = NA, col.names = header) +} + +#' Retrieve information about columns that didn't fit the available width +#' +#' Formatting a [multicolformat] object may lead to some columns being omitted +#' due to width restrictions. This method returns a character vector that +#' describes each of the omitted columns. +#' @param x The result of [format()] on a [multicolformat] object +#' @param ... Unused +#' @export +extra_cols <- function(x, ...) { + UseMethod("extra_cols") +} + +#' @export +extra_cols.mcf_squeezed <- function(x, ...) { + attr(x, "extra_cols") +} + +#' @export +format.multicolformat <- function(x, ...) { + format(squeeze(x, ...)) } #' @export diff --git a/R/rowid-data.R b/R/rowid-data.R index 7ba30dcb7..8b83ae153 100644 --- a/R/rowid-data.R +++ b/R/rowid-data.R @@ -7,7 +7,7 @@ rif_data <- function(n, ...) { list(n = n), class = "rif_data" ) - ret <- set_width(ret, as.integer(floor(log10(n)) + 1)) + ret <- set_width(ret, as.integer(floor(log10(max(n, 1))) + 1)) ret } diff --git a/R/sigfig.R b/R/sigfig.R index de5c27240..5f71aadef 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -44,11 +44,11 @@ split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { if (scientific) { mnt <- ifelse(num, abs_x * 10 ^ (-exp), abs_x) - round_x <- signif(mnt, sigfig) + round_x <- safe_signif(mnt, sigfig) rhs_digits <- ifelse(num, sigfig - 1, 0) exp_display <- exp } else { - round_x <- signif(abs_x, pmax(sigfig, exp + 1, na.rm = TRUE)) + round_x <- safe_signif(abs_x, pmax(sigfig, exp + 1, na.rm = TRUE)) rhs_digits <- compute_rhs_digits(abs_x, sigfig) exp_display <- rep_along(x, NA_integer_) } @@ -69,7 +69,12 @@ split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { superscript = superscript ) - set_width(ret, max(crayon::col_nchar(assemble_decimal(ret)))) + set_width(ret, max(crayon::col_nchar(assemble_decimal(ret), type = "width"), 0L)) +} + +safe_signif <- function(x, digits) { + if (length(x) == 0L) return(numeric()) + signif(x, digits) } compute_rhs_digits <- function(x, sigfig) { @@ -109,17 +114,18 @@ format_lhs <- function(s) { lhs_zero <- s$lhs_zero lhs_str <- sprintf("%.0f", s$lhs) - lhs_width <- max(nchar(lhs_str)) - lhs_sig <- substr(lhs_str, 1, s$sigfig) - lhs_non <- substr(lhs_str, s$sigfig + 1, nchar(lhs_str)) + lhs_width <- max(nchar(lhs_str), 0L) + lhs_sig <- crayon::col_substr(lhs_str, 1, s$sigfig) + lhs_non <- crayon::col_substr(lhs_str, s$sigfig + 1, nchar(lhs_str)) - lhs_col <- ifelse(num, + # as.character() to support corner case of length zero + lhs_col <- as.character(ifelse(num, paste0( style_num(lhs_sig, neg, lhs_zero), style_subtle(lhs_non) ), style_na(lhs_str) - ) + )) lhs_col <- crayon::col_align(lhs_col, width = lhs_width, align = "right") lhs_col @@ -181,6 +187,8 @@ assemble_decimal <- function(x) { #' @export format.decimal_format <- function(x, width, ...) { + if (length(x$dec$num) == 0L) return(character()) + if (width < get_min_width(x)) { stop( "Need at least width ", get_min_width(x), " requested ", width, ".", @@ -194,7 +202,7 @@ format.decimal_format <- function(x, width, ...) { row <- assemble_decimal(x$sci) } - used_width <- max(crayon::col_nchar(row)) + used_width <- max(crayon::col_nchar(row, type = "width"), 0L) row <- paste0(strrep(" ", width - used_width), row) new_column(row, width = width, align = "right") } diff --git a/R/styles.R b/R/styles.R index 704b672bd..cac834044 100644 --- a/R/styles.R +++ b/R/styles.R @@ -35,8 +35,7 @@ style_grey <- function(level, ...) { ) } -cf_na <- function(width = 1L) { - width <- pmax(0, width - 1) - - paste0(strrep(" ", width), style_na("?")) +cf_na <- function(use_brackets_if_no_color = FALSE) { + if (use_brackets_if_no_color && !crayon::has_color()) "" + else style_na("NA") } diff --git a/R/type-sum.R b/R/type-sum.R index 783f8fed9..f1e09a2f6 100644 --- a/R/type-sum.R +++ b/R/type-sum.R @@ -15,7 +15,7 @@ type_sum.ordered <- function(x) "ord" #' @export type_sum.factor <- function(x) "fctr" #' @export -type_sum.POSIXt <- function(x) "dttm" +type_sum.POSIXct <- function(x) "dttm" #' @export type_sum.difftime <- function(x) "time" #' @export diff --git a/R/utils.R b/R/utils.R index 683b42cf8..339c6dd2d 100644 --- a/R/utils.R +++ b/R/utils.R @@ -13,7 +13,7 @@ str_trunc <- function(x, width) { str_width <- nchar(x, type = "width") too_wide <- !is.na(x) & str_width > width - x[too_wide] <- paste0(substr(x[too_wide], 1, width - 1), "\u2026") + x[too_wide] <- paste0(crayon::col_substr(x[too_wide], 1, width - 1), "\u2026") x } diff --git a/R/zzz.R b/R/zzz.R index 9a9a25e82..4b6e2c37c 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -2,5 +2,36 @@ NULL .onAttach <- function(...) { - crayon::num_colors(TRUE) + crayon::num_colors(forget = TRUE) +} + +.onLoad <- function(libname, pkgname) { + register_s3_method("knitr", "knit_print", "mcf_squeezed") + + invisible() +} + +register_s3_method <- function(pkg, generic, class, fun = NULL) { + stopifnot(is.character(pkg), length(pkg) == 1) + stopifnot(is.character(generic), length(generic) == 1) + stopifnot(is.character(class), length(class) == 1) + if (is.null(fun)) { + fun <- get(paste0(generic, ".", class), envir = parent.frame()) + } + stopifnot(is.function(fun)) + + + if (pkg %in% loadedNamespaces()) { + envir <- asNamespace(pkg) + registerS3method(generic, class, fun, envir = envir) + } + + # Always register hook in case package is later unloaded & reloaded + setHook( + packageEvent(pkg, "onLoad"), + function(...) { + envir <- asNamespace(pkg) + registerS3method(generic, class, fun, envir = envir) + } + ) } diff --git a/man/cf_data.Rd b/man/cf_data.Rd index da7c2a127..d2e451800 100644 --- a/man/cf_data.Rd +++ b/man/cf_data.Rd @@ -5,7 +5,7 @@ \alias{cf_data.logical} \alias{cf_data.numeric} \alias{cf_data.Date} -\alias{cf_data.POSIXct} +\alias{cf_data.POSIXt} \alias{cf_data.character} \alias{cf_data.list} \alias{cf_data.default} @@ -19,7 +19,7 @@ cf_data(x, ...) \method{cf_data}{Date}(x, ...) -\method{cf_data}{POSIXct}(x, ...) +\method{cf_data}{POSIXt}(x, ...) \method{cf_data}{character}(x, ...) diff --git a/man/extra_cols.Rd b/man/extra_cols.Rd new file mode 100644 index 000000000..0ee0d6e38 --- /dev/null +++ b/man/extra_cols.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/multi.R +\name{extra_cols} +\alias{extra_cols} +\title{Retrieve information about columns that didn't fit the available width} +\usage{ +extra_cols(x, ...) +} +\arguments{ +\item{x}{The result of [format()] on a [multicolformat] object} + +\item{...}{Unused} +} +\description{ +Formatting a [multicolformat] object may lead to some columns being omitted +due to width restrictions. This method returns a character vector that +describes each of the omitted columns. +} diff --git a/man/multicolformat.Rd b/man/multicolformat.Rd index ad7cfa2da..38a6b009e 100644 --- a/man/multicolformat.Rd +++ b/man/multicolformat.Rd @@ -2,9 +2,12 @@ % Please edit documentation in R/multi.R \name{multicolformat} \alias{multicolformat} +\alias{squeeze} \title{Format multiple vectors in a tabular display} \usage{ multicolformat(x, has_row_id = TRUE, width = NULL, ...) + +squeeze(x, width = NULL, ...) } \arguments{ \item{x}{A list of vectors to format} @@ -19,6 +22,11 @@ the row ID column with a star.} \description{ The vectors are formatted to fit horizontally into a user-supplied number of characters per row. + +The `squeeze()` function is called by [format()] and [print()] and usually +doesn't need to be called manually. +It returns an object suitable for printing and formatting at a fixed width +with additional information about omitted columns. } \details{ In a first pass, for each column it is decided if it is hidden, shown with diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 4d6575083..de531a028 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -27,3 +27,16 @@ add_special <- function(x) { show_output_in_terminal <- function() { system2("xterm", c("-e", shQuote("head tests/testthat/out/*; sleep 600"))) } + +# A data frame with all major types +df_all <- list( + a = c(1, 2.5, NA), + b = c(1:2, NA), + c = c(T, F, NA), + d = c("a", "b", NA), + e = factor(c("a", "b", NA)), + f = as.Date("2015-12-09") + c(1:2, NA), + g = as.POSIXct("2015-12-09 10:51:34 UTC") + c(1:2, NA), + h = as.list(c(1:2, NA)), + i = list(list(1, 2:3), list(4:6), list(NA)) +) diff --git a/tests/testthat/out/basic.txt b/tests/testthat/out/basic.txt index 1016155ec..b9f5d9e6f 100644 --- a/tests/testthat/out/basic.txt +++ b/tests/testthat/out/basic.txt @@ -1,4 +1,4 @@ -  +  - 0.00100 0.0100 - 0.100 diff --git a/tests/testthat/out/date.txt b/tests/testthat/out/date.txt index d5e948b26..402379304 100644 --- a/tests/testthat/out/date.txt +++ b/tests/testthat/out/date.txt @@ -1,3 +1,3 @@ -  - 2017-07-28 - ? +  +2017-07-28 +NA diff --git a/tests/testthat/out/deal1.txt b/tests/testthat/out/deal1.txt new file mode 100644 index 000000000..2c782d678 --- /dev/null +++ b/tests/testthat/out/deal1.txt @@ -0,0 +1,4 @@ +成交 +  +成交日 +NA diff --git a/tests/testthat/out/deal2.txt b/tests/testthat/out/deal2.txt new file mode 100644 index 000000000..9fe8202de --- /dev/null +++ b/tests/testthat/out/deal2.txt @@ -0,0 +1,4 @@ +成交日 +  +成交 +NA diff --git a/tests/testthat/out/deal3.txt b/tests/testthat/out/deal3.txt new file mode 100644 index 000000000..f78c2110a --- /dev/null +++ b/tests/testthat/out/deal3.txt @@ -0,0 +1,4 @@ +成交日 +  + 1 + NA diff --git a/tests/testthat/out/decimal-insignif.txt b/tests/testthat/out/decimal-insignif.txt index cf4889d2a..9d27484ae 100644 --- a/tests/testthat/out/decimal-insignif.txt +++ b/tests/testthat/out/decimal-insignif.txt @@ -1,4 +1,4 @@ -  +  0.00123 0.0123 0.123 diff --git a/tests/testthat/out/escaped.txt b/tests/testthat/out/escaped.txt index 189b7e6e7..8465968ac 100644 --- a/tests/testthat/out/escaped.txt +++ b/tests/testthat/out/escaped.txt @@ -1,2 +1,2 @@  -a\nb +"a\nb" diff --git a/tests/testthat/out/factor.txt b/tests/testthat/out/factor.txt index c52d7a305..2989e9e15 100644 --- a/tests/testthat/out/factor.txt +++ b/tests/testthat/out/factor.txt @@ -4,4 +4,4 @@ b c d e -? +NA diff --git a/tests/testthat/out/integer-06.txt b/tests/testthat/out/integer-06.txt index c1e98244e..f166a3e05 100644 --- a/tests/testthat/out/integer-06.txt +++ b/tests/testthat/out/integer-06.txt @@ -1,4 +1,4 @@ -  +  1.00e⁷ 1.00e⁷ 1.00e⁷ diff --git a/tests/testthat/out/integer-07.txt b/tests/testthat/out/integer-07.txt index c1e98244e..f166a3e05 100644 --- a/tests/testthat/out/integer-07.txt +++ b/tests/testthat/out/integer-07.txt @@ -1,4 +1,4 @@ -  +  1.00e⁷ 1.00e⁷ 1.00e⁷ diff --git a/tests/testthat/out/integer-08.txt b/tests/testthat/out/integer-08.txt index cf69a061c..3390cb1f1 100644 --- a/tests/testthat/out/integer-08.txt +++ b/tests/testthat/out/integer-08.txt @@ -1,4 +1,4 @@ -  +  10000001 10000002 10000003 diff --git a/tests/testthat/out/integer-09.txt b/tests/testthat/out/integer-09.txt index d7640dc50..7e042b5c2 100644 --- a/tests/testthat/out/integer-09.txt +++ b/tests/testthat/out/integer-09.txt @@ -1,4 +1,4 @@ -  +  10000001 10000002 10000003 diff --git a/tests/testthat/out/letters-long-03.txt b/tests/testthat/out/letters-long-03.txt index b8de5c9e5..306a1a690 100644 --- a/tests/testthat/out/letters-long-03.txt +++ b/tests/testthat/out/letters-long-03.txt @@ -1,3 +1,3 @@  abcd… -[43… +NA… diff --git a/tests/testthat/out/letters-long-10.txt b/tests/testthat/out/letters-long-10.txt index 4a848b0f7..ad761c405 100644 --- a/tests/testthat/out/letters-long-10.txt +++ b/tests/testthat/out/letters-long-10.txt @@ -1,3 +1,3 @@ -  +  abcdefghi… -[30… +NA… diff --git a/tests/testthat/out/letters-long.txt b/tests/testthat/out/letters-long.txt index 76fe78345..48df833e6 100644 --- a/tests/testthat/out/letters-long.txt +++ b/tests/testthat/out/letters-long.txt @@ -1,3 +1,3 @@ -  +  abcdefghijklmnopqrstuvwxyz -? +NA diff --git a/tests/testthat/out/letters.txt b/tests/testthat/out/letters.txt index a868b613c..f60096313 100644 --- a/tests/testthat/out/letters.txt +++ b/tests/testthat/out/letters.txt @@ -4,4 +4,4 @@ b c d e -? +NA diff --git a/tests/testthat/out/list-each.txt b/tests/testthat/out/list-each.txt index d4c627c50..f04cada17 100644 --- a/tests/testthat/out/list-each.txt +++ b/tests/testthat/out/list-each.txt @@ -1,4 +1,4 @@ -  +     diff --git a/tests/testthat/out/list-na.txt b/tests/testthat/out/list-na.txt index 9dcf4cdd3..e05047a9c 100644 --- a/tests/testthat/out/list-na.txt +++ b/tests/testthat/out/list-na.txt @@ -1,3 +1,3 @@ -  +    diff --git a/tests/testthat/out/list-narrow.txt b/tests/testthat/out/list-narrow.txt new file mode 100644 index 000000000..26da64e68 --- /dev/null +++ b/tests/testthat/out/list-narrow.txt @@ -0,0 +1,2 @@ +  +  +    diff --git a/tests/testthat/out/logical.txt b/tests/testthat/out/logical.txt index 701b9202a..3f89116a8 100644 --- a/tests/testthat/out/logical.txt +++ b/tests/testthat/out/logical.txt @@ -1,4 +1,4 @@  - * - - - ? +T +F +NA diff --git a/tests/testthat/out/multi-04.txt b/tests/testthat/out/multi-04.txt index 56ba19759..08e9b9c13 100644 --- a/tests/testthat/out/multi-04.txt +++ b/tests/testthat/out/multi-04.txt @@ -1,5 +1,5 @@ -  +  1 2 3 diff --git a/tests/testthat/out/multi-05.txt b/tests/testthat/out/multi-05.txt index 56ba19759..08e9b9c13 100644 --- a/tests/testthat/out/multi-05.txt +++ b/tests/testthat/out/multi-05.txt @@ -1,5 +1,5 @@ -  +  1 2 3 diff --git a/tests/testthat/out/multi-06.txt b/tests/testthat/out/multi-06.txt index 56ba19759..08e9b9c13 100644 --- a/tests/testthat/out/multi-06.txt +++ b/tests/testthat/out/multi-06.txt @@ -1,5 +1,5 @@ -  +  1 2 3 diff --git a/tests/testthat/out/multi-07.txt b/tests/testthat/out/multi-07.txt index 56ba19759..08e9b9c13 100644 --- a/tests/testthat/out/multi-07.txt +++ b/tests/testthat/out/multi-07.txt @@ -1,5 +1,5 @@ -  +  1 2 3 diff --git a/tests/testthat/out/multi-08.txt b/tests/testthat/out/multi-08.txt index 1fc41fe8b..eb362a646 100644 --- a/tests/testthat/out/multi-08.txt +++ b/tests/testthat/out/multi-08.txt @@ -1,5 +1,5 @@ colu… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-09.txt b/tests/testthat/out/multi-09.txt index 604f37433..c13147457 100644 --- a/tests/testthat/out/multi-09.txt +++ b/tests/testthat/out/multi-09.txt @@ -1,5 +1,5 @@ colum… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-10.txt b/tests/testthat/out/multi-10.txt index 4264fc1a8..abaa394b4 100644 --- a/tests/testthat/out/multi-10.txt +++ b/tests/testthat/out/multi-10.txt @@ -1,5 +1,5 @@ column… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-11.txt b/tests/testthat/out/multi-11.txt index 26b4236d5..adaffeb95 100644 --- a/tests/testthat/out/multi-11.txt +++ b/tests/testthat/out/multi-11.txt @@ -1,5 +1,5 @@ column_… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-12.txt b/tests/testthat/out/multi-12.txt index e2dbaf76a..caee265d1 100644 --- a/tests/testthat/out/multi-12.txt +++ b/tests/testthat/out/multi-12.txt @@ -1,5 +1,5 @@ column_z… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-13.txt b/tests/testthat/out/multi-13.txt index c21e50adb..db5914da5 100644 --- a/tests/testthat/out/multi-13.txt +++ b/tests/testthat/out/multi-13.txt @@ -1,5 +1,5 @@ column_ze… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-14.txt b/tests/testthat/out/multi-14.txt index da61ec00c..c6f5b277b 100644 --- a/tests/testthat/out/multi-14.txt +++ b/tests/testthat/out/multi-14.txt @@ -1,5 +1,5 @@ colu… col_… -   +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-15.txt b/tests/testthat/out/multi-15.txt index aabe82176..ca1aff45d 100644 --- a/tests/testthat/out/multi-15.txt +++ b/tests/testthat/out/multi-15.txt @@ -1,5 +1,5 @@ colum… col_… -   +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-16.txt b/tests/testthat/out/multi-16.txt index 3ef1c9f7a..e727c3eea 100644 --- a/tests/testthat/out/multi-16.txt +++ b/tests/testthat/out/multi-16.txt @@ -1,5 +1,5 @@ column… col_… -   +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-17.txt b/tests/testthat/out/multi-17.txt index 871aed019..9f778f90b 100644 --- a/tests/testthat/out/multi-17.txt +++ b/tests/testthat/out/multi-17.txt @@ -1,5 +1,5 @@ column_… col_… -   +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-18.txt b/tests/testthat/out/multi-18.txt index 5cf85cf8c..ef8680e1c 100644 --- a/tests/testthat/out/multi-18.txt +++ b/tests/testthat/out/multi-18.txt @@ -1,5 +1,5 @@ column_z… col_… -   +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-19.txt b/tests/testthat/out/multi-19.txt index b4c1a67af..d512cad7c 100644 --- a/tests/testthat/out/multi-19.txt +++ b/tests/testthat/out/multi-19.txt @@ -1,5 +1,5 @@ column_ze… col_… -   +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-20.txt b/tests/testthat/out/multi-20.txt index 0bec644a0..1642b616f 100644 --- a/tests/testthat/out/multi-20.txt +++ b/tests/testthat/out/multi-20.txt @@ -1,5 +1,5 @@ colu… col_… col_… -    +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-21.txt b/tests/testthat/out/multi-21.txt index 843a737ee..3ae051a7c 100644 --- a/tests/testthat/out/multi-21.txt +++ b/tests/testthat/out/multi-21.txt @@ -1,5 +1,5 @@ colum… col_… col_… -    +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-22.txt b/tests/testthat/out/multi-22.txt index fd5852ee9..16295675b 100644 --- a/tests/testthat/out/multi-22.txt +++ b/tests/testthat/out/multi-22.txt @@ -1,5 +1,5 @@ column… col_… col_… -    +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-23.txt b/tests/testthat/out/multi-23.txt index 79c25a9f0..d28398ea0 100644 --- a/tests/testthat/out/multi-23.txt +++ b/tests/testthat/out/multi-23.txt @@ -1,5 +1,5 @@ column_… col_… col_… -    +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-24.txt b/tests/testthat/out/multi-24.txt index a885bc721..850d44dc8 100644 --- a/tests/testthat/out/multi-24.txt +++ b/tests/testthat/out/multi-24.txt @@ -1,5 +1,5 @@ column_z… col_… col_… -    +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-25.txt b/tests/testthat/out/multi-25.txt index 46da975a3..219bfb7eb 100644 --- a/tests/testthat/out/multi-25.txt +++ b/tests/testthat/out/multi-25.txt @@ -1,5 +1,5 @@ column_ze… col_… col_… -    +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-26.txt b/tests/testthat/out/multi-26.txt index 436367a26..f897a3817 100644 --- a/tests/testthat/out/multi-26.txt +++ b/tests/testthat/out/multi-26.txt @@ -1,5 +1,5 @@ colu… col_… col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-27.txt b/tests/testthat/out/multi-27.txt index 0f0a46f51..62185fab4 100644 --- a/tests/testthat/out/multi-27.txt +++ b/tests/testthat/out/multi-27.txt @@ -1,5 +1,5 @@ colum… col_… col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-28.txt b/tests/testthat/out/multi-28.txt index ab441302a..eef81a488 100644 --- a/tests/testthat/out/multi-28.txt +++ b/tests/testthat/out/multi-28.txt @@ -1,5 +1,5 @@ column… col_… col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-29.txt b/tests/testthat/out/multi-29.txt index fc8a9ebed..c37c79cd0 100644 --- a/tests/testthat/out/multi-29.txt +++ b/tests/testthat/out/multi-29.txt @@ -1,5 +1,5 @@ column_… col_… col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-30.txt b/tests/testthat/out/multi-30.txt index 905ea3198..9ce3b6f48 100644 --- a/tests/testthat/out/multi-30.txt +++ b/tests/testthat/out/multi-30.txt @@ -1,5 +1,5 @@ column_z… col_… col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-31.txt b/tests/testthat/out/multi-31.txt index fb11a7b80..d133cab15 100644 --- a/tests/testthat/out/multi-31.txt +++ b/tests/testthat/out/multi-31.txt @@ -1,5 +1,5 @@ column_z… col_02 col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-32.txt b/tests/testthat/out/multi-32.txt index 04372bbd5..cceca5016 100644 --- a/tests/testthat/out/multi-32.txt +++ b/tests/testthat/out/multi-32.txt @@ -1,5 +1,5 @@ column_ze… col_02 col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-33.txt b/tests/testthat/out/multi-33.txt index 8fd85deeb..c438eab41 100644 --- a/tests/testthat/out/multi-33.txt +++ b/tests/testthat/out/multi-33.txt @@ -1,5 +1,5 @@ column_zer… col_02 col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-34.txt b/tests/testthat/out/multi-34.txt index 1cd38c778..91d17fc18 100644 --- a/tests/testthat/out/multi-34.txt +++ b/tests/testthat/out/multi-34.txt @@ -1,5 +1,5 @@ column_zero… col_02 col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-35.txt b/tests/testthat/out/multi-35.txt index f329c7c28..2978977e6 100644 --- a/tests/testthat/out/multi-35.txt +++ b/tests/testthat/out/multi-35.txt @@ -1,5 +1,5 @@ column_zero… col_02 col_03 col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-36.txt b/tests/testthat/out/multi-36.txt index 4c54dbe67..9bc140a81 100644 --- a/tests/testthat/out/multi-36.txt +++ b/tests/testthat/out/multi-36.txt @@ -1,5 +1,5 @@ column_zero_one col_… col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-37.txt b/tests/testthat/out/multi-37.txt index 8ada97ef7..e7a2f9868 100644 --- a/tests/testthat/out/multi-37.txt +++ b/tests/testthat/out/multi-37.txt @@ -1,5 +1,5 @@ column_zero_one col_02 col_… col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-38.txt b/tests/testthat/out/multi-38.txt index 17e5b5382..eaefdb3c5 100644 --- a/tests/testthat/out/multi-38.txt +++ b/tests/testthat/out/multi-38.txt @@ -1,5 +1,5 @@ column_zero_one col_02 col_03 col_… -     +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-39.txt b/tests/testthat/out/multi-39.txt index 765c45fed..a4f3a4425 100644 --- a/tests/testthat/out/multi-39.txt +++ b/tests/testthat/out/multi-39.txt @@ -1,5 +1,5 @@ column_zero_one col_02 col_03 col_04 -      +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/numeric-04.txt b/tests/testthat/out/numeric-04.txt index 0d95f2aec..996a7528c 100644 --- a/tests/testthat/out/numeric-04.txt +++ b/tests/testthat/out/numeric-04.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-07.txt b/tests/testthat/out/numeric-07.txt index 0d95f2aec..996a7528c 100644 --- a/tests/testthat/out/numeric-07.txt +++ b/tests/testthat/out/numeric-07.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-10.txt b/tests/testthat/out/numeric-10.txt index f3fb10ddf..a1b47b2d5 100644 --- a/tests/testthat/out/numeric-10.txt +++ b/tests/testthat/out/numeric-10.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-15.txt b/tests/testthat/out/numeric-15.txt index 088d9345c..d5f9c49d3 100644 --- a/tests/testthat/out/numeric-15.txt +++ b/tests/testthat/out/numeric-15.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-22.txt b/tests/testthat/out/numeric-22.txt index 915b2832f..4b9828eb4 100644 --- a/tests/testthat/out/numeric-22.txt +++ b/tests/testthat/out/numeric-22.txt @@ -1,4 +1,4 @@ -  +  0.00000000100 0.00000100 1000 diff --git a/tests/testthat/out/ordered.txt b/tests/testthat/out/ordered.txt index 1c5dc622f..92859947c 100644 --- a/tests/testthat/out/ordered.txt +++ b/tests/testthat/out/ordered.txt @@ -4,4 +4,4 @@ b c d e -? +NA diff --git a/tests/testthat/out/rowid-3.txt b/tests/testthat/out/rowid-3.txt index 1a77ce9b5..3e892644f 100644 --- a/tests/testthat/out/rowid-3.txt +++ b/tests/testthat/out/rowid-3.txt @@ -1,4 +1,4 @@ -  +  1 2 3 diff --git a/tests/testthat/out/rowid-star-title-12.txt b/tests/testthat/out/rowid-star-title-12.txt index 4d8751ee2..159fdc08a 100644 --- a/tests/testthat/out/rowid-star-title-12.txt +++ b/tests/testthat/out/rowid-star-title-12.txt @@ -1,5 +1,5 @@ - * + *  1  2  3 diff --git a/tests/testthat/out/scientific-short-neg.txt b/tests/testthat/out/scientific-short-neg.txt index ec32f4d03..099df56c9 100644 --- a/tests/testthat/out/scientific-short-neg.txt +++ b/tests/testthat/out/scientific-short-neg.txt @@ -1,4 +1,4 @@ -  +  - 1.00e ³ 1.00e ⁹ - 1.00e¹⁵ diff --git a/tests/testthat/out/scientific.txt b/tests/testthat/out/scientific.txt index b933611f4..6b8f723df 100644 --- a/tests/testthat/out/scientific.txt +++ b/tests/testthat/out/scientific.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/tibble-all--30.txt b/tests/testthat/out/tibble-all--30.txt new file mode 100644 index 000000000..e16955bf3 --- /dev/null +++ b/tests/testthat/out/tibble-all--30.txt @@ -0,0 +1,5 @@ + a b c d + +1 1.00 1 T a +2 2.50 2 F b +3 NA NA NA diff --git a/tests/testthat/out/tibble-all--300.txt b/tests/testthat/out/tibble-all--300.txt new file mode 100644 index 000000000..9e0d0e2d7 --- /dev/null +++ b/tests/testthat/out/tibble-all--300.txt @@ -0,0 +1,5 @@ + a b c d e f g h i + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 +3 NA NA NA NA NA diff --git a/tests/testthat/out/tibble-iris-3-20.txt b/tests/testthat/out/tibble-iris-3-20.txt new file mode 100644 index 000000000..8d9878711 --- /dev/null +++ b/tests/testthat/out/tibble-iris-3-20.txt @@ -0,0 +1,5 @@ + Sepa… Sepa… Peta… + +1 5.10 3.50 1.40 +2 4.90 3.00 1.40 +3 4.70 3.20 1.30 diff --git a/tests/testthat/out/tibble-iris-5-30.txt b/tests/testthat/out/tibble-iris-5-30.txt new file mode 100644 index 000000000..9eef46e0b --- /dev/null +++ b/tests/testthat/out/tibble-iris-5-30.txt @@ -0,0 +1,7 @@ + Sepal.… Sepal… Petal… Peta… + +1 5.10 3.50 1.40 0.200 +2 4.90 3.00 1.40 0.200 +3 4.70 3.20 1.30 0.200 +4 4.60 3.10 1.50 0.200 +5 5.00 3.60 1.40 0.200 diff --git a/tests/testthat/out/tibble-mtcars-8-30.txt b/tests/testthat/out/tibble-mtcars-8-30.txt new file mode 100644 index 000000000..3219c55c7 --- /dev/null +++ b/tests/testthat/out/tibble-mtcars-8-30.txt @@ -0,0 +1,10 @@ + mpg cyl disp hp +* +1 21.0 6.00 160 110 +2 21.0 6.00 160 110 +3 22.8 4.00 108 93.0 +4 21.4 6.00 258 110 +5 18.7 8.00 360 175 +6 18.1 6.00 225 105 +7 14.3 8.00 360 245 +8 24.4 4.00 147 62.0 diff --git a/tests/testthat/out/tibble-newline.txt b/tests/testthat/out/tibble-newline.txt new file mode 100644 index 000000000..dad287a3d --- /dev/null +++ b/tests/testthat/out/tibble-newline.txt @@ -0,0 +1,4 @@ + `\n` `\r` + +1 "\n" "\n" +2 "\"" "\n" diff --git a/tests/testthat/out/tibble-non-syntactic.txt b/tests/testthat/out/tibble-non-syntactic.txt new file mode 100644 index 000000000..9b1d9c2aa --- /dev/null +++ b/tests/testthat/out/tibble-non-syntactic.txt @@ -0,0 +1,3 @@ + `mean(x)` `var(x)` + +1 5.00 3.00 diff --git a/tests/testthat/out/time-posix.txt b/tests/testthat/out/time-posix.txt new file mode 100644 index 000000000..0962b83ed --- /dev/null +++ b/tests/testthat/out/time-posix.txt @@ -0,0 +1,3 @@ +  +2017-07-28 18:04:35 +NA diff --git a/tests/testthat/out/time.txt b/tests/testthat/out/time.txt index 84c9ff627..31d6a9547 100644 --- a/tests/testthat/out/time.txt +++ b/tests/testthat/out/time.txt @@ -1,3 +1,3 @@ -  +  2017-07-28 18:04:35 - ? +NA diff --git a/tests/testthat/out/title-crayon.txt b/tests/testthat/out/title-crayon.txt index e35501f9f..a9c055390 100644 --- a/tests/testthat/out/title-crayon.txt +++ b/tests/testthat/out/title-crayon.txt @@ -1,5 +1,5 @@ crayon -  +  10.0 100 1000 diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index aa7c210a9..d9884ec47 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -5,4 +5,7 @@ test_that("output test", { expect_colformat_output(paste(letters, collapse = ""), filename = "letters-long.txt") expect_colformat_output(paste(letters, collapse = ""), width = 10, filename = "letters-long-10.txt") expect_colformat_output(paste(letters, collapse = ""), width = 3, filename = "letters-long-03.txt") + expect_colformat_output("\u6210\u4ea4\u65e5", title = "\u6210\u4ea4", filename = "deal1.txt") + expect_colformat_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") + expect_colformat_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") }) diff --git a/tests/testthat/test-format_decimal.R b/tests/testthat/test-format_decimal.R index a88598d74..b12822ef6 100644 --- a/tests/testthat/test-format_decimal.R +++ b/tests/testthat/test-format_decimal.R @@ -68,6 +68,11 @@ test_that("values on LHS not rounded", { expect_equal(format_lhs(f), "123456") }) +test_that("corner cases", { + expect_equal(format_lhs(format_decimal_bw(numeric())), character()) + expect_equal(format_lhs(format_decimal_bw(numeric(), scientific = TRUE)), character()) +}) + test_that("output test", { expect_colformat_output((10 ^ (-3:4)) * c(-1, 1), filename = "basic.txt") expect_colformat_output(1.23456 * 10 ^ (-3:3), filename = "decimal-insignif.txt") diff --git a/tests/testthat/test-format_list.R b/tests/testthat/test-format_list.R index 580815030..52f2ec8b9 100644 --- a/tests/testthat/test-format_list.R +++ b/tests/testthat/test-format_list.R @@ -4,4 +4,5 @@ test_that("output test", { expect_colformat_output(xp = as.list(1:3), filename = "list-each.txt") expect_colformat_output(xp = list(1:3, NULL), filename = "list-null.txt") expect_colformat_output(list(1:3), filename = "list-na.txt") + expect_colformat_output(xp = list(iris), width = 10, filename = "list-narrow.txt") }) diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index ce3bd47ff..5a0616041 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -39,3 +39,52 @@ test_that("output test", { expect_colformat_output(xf = multicolformat(x, width = 38), filename = "multi-38.txt") expect_colformat_output(xf = multicolformat(x, width = 39), filename = "multi-39.txt") }) + +test_that("tests from tibble", { + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(mtcars[1:8, ], has_row_id = "*", width = 30), + filename = "tibble-mtcars-8-30.txt" + ) + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(iris[1:5, ], width = 30), + filename = "tibble-iris-5-30.txt" + ) + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(iris[1:3, ], width = 20), + filename = "tibble-iris-3-20.txt" + ) + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(df_all, width = 30), + filename = "tibble-all--30.txt" + ) + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(df_all, width = 300), + filename = "tibble-all--300.txt" + ) + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(list(`\n` = c("\n", '"'), `\r` = factor("\n")), width = 30), + filename = "tibble-newline.txt" + ) + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(list("mean(x)" = 5, "var(x)" = 3), width = 30), + filename = "tibble-non-syntactic.txt" + ) +}) + +test_that("empty", { + expect_equal( + format(multicolformat(list(a = character(), b = logical()), width = 30)), + structure(character(), class = "cf_vertical") + ) + expect_equal( + format(multicolformat(iris[1:5, character()], width = 30)), + structure(character(), class = "cf_vertical") + ) +}) diff --git a/tests/testthat/test-format_time.R b/tests/testthat/test-format_time.R index 08f33f48e..cec3eacdc 100644 --- a/tests/testthat/test-format_time.R +++ b/tests/testthat/test-format_time.R @@ -2,4 +2,5 @@ context("format_time") test_that("output test", { expect_colformat_output(as.POSIXct("2017-07-28 18:04:35 +0200"), filename = "time.txt") + expect_colformat_output(as.POSIXlt("2017-07-28 18:04:35 +0200"), filename = "time-posix.txt") }) From 583d1f16d2e03a1699eced3d8782d44c9a7cefeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 4 Sep 2017 16:56:52 +0200 Subject: [PATCH 056/133] extra columns gain subtle style, closes #37 --- R/col-type.R | 6 +++++- R/colformat.R | 4 ++-- R/multi.R | 2 +- tests/testthat/out/multi-extra-10.txt | 3 +++ tests/testthat/test-format_multi.R | 5 +++++ 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 tests/testthat/out/multi-extra-10.txt diff --git a/R/col-type.R b/R/col-type.R index 5e9214d2e..1b1170813 100644 --- a/R/col-type.R +++ b/R/col-type.R @@ -1,5 +1,9 @@ +style_type_header <- function(x) { + crayon::underline(style_type(x)) +} + style_type <- function(x) { - crayon::underline(style_subtle(x)) + style_subtle(x) } cf_type <- function(x, ...) { diff --git a/R/colformat.R b/R/colformat.R index 9cdbd81f1..d3057ffdb 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -61,7 +61,7 @@ format.colformat <- function(x, width = NULL, ...) { width <- cf_get_width(x, width) out <- cf_format_parts(x, width) - cf_data <- c(out$title_format, style_type(out$type_format), out$data_format) + cf_data <- c(out$title_format, style_type_header(out$type_format), out$data_format) new_vertical(cf_data) } @@ -106,6 +106,6 @@ cf_format_parts <- function(x, width, ...) { cf_format_abbrev <- function(x, ...) { title_format <- format(x$title, width = Inf, ...) - type_format <- format(x$type, width = Inf, ...) + type_format <- style_type(format(x$type, width = Inf, ...)) paste0(title_format, "\u00a0", type_format) } diff --git a/R/multi.R b/R/multi.R index 53cf6d54d..e597ee346 100644 --- a/R/multi.R +++ b/R/multi.R @@ -74,7 +74,7 @@ format.mcf_squeezed <- function(x, ...) { formatted <- c( invoke(paste, xt$title), - style_type(invoke(paste, xt$type)), + style_type_header(invoke(paste, xt$type)), invoke(paste, xt$data) ) diff --git a/tests/testthat/out/multi-extra-10.txt b/tests/testthat/out/multi-extra-10.txt new file mode 100644 index 000000000..3fdcdbaa0 --- /dev/null +++ b/tests/testthat/out/multi-extra-10.txt @@ -0,0 +1,3 @@ +col_02  +col_03  +col_04  diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 5a0616041..b28df790f 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -38,6 +38,11 @@ test_that("output test", { expect_colformat_output(xf = multicolformat(x, width = 37), filename = "multi-37.txt") expect_colformat_output(xf = multicolformat(x, width = 38), filename = "multi-38.txt") expect_colformat_output(xf = multicolformat(x, width = 39), filename = "multi-39.txt") + + expect_colformat_output( + xf = new_vertical(extra_cols(squeeze(multicolformat(x), width = 10))), + filename = "multi-extra-10.txt" + ) }) test_that("tests from tibble", { From 5694b78a19134c0c6bd4d2938265898d95acf053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 4 Sep 2017 16:58:34 +0200 Subject: [PATCH 057/133] add API and documentation --- .Rbuildignore | 1 + API | 46 ++++++++++++++++++++++++++++++++++++++++ DESCRIPTION | 1 + man/colformat.Rd | 4 ++-- man/dim_desc.Rd | 2 +- man/extra_cols.Rd | 4 ++-- man/format_decimal.Rd | 10 +++++---- man/format_scientific.Rd | 2 +- man/multicolformat.Rd | 20 +++++++++-------- man/spark_bar.Rd | 2 +- man/type_sum.Rd | 6 +++--- 11 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 API diff --git a/.Rbuildignore b/.Rbuildignore index 1c92417ad..8ec3a1722 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -3,3 +3,4 @@ ^README\.Rmd$ ^README-.*\.png$ ^\.travis\.yml$ +^API$ diff --git a/API b/API new file mode 100644 index 000000000..ea81b689a --- /dev/null +++ b/API @@ -0,0 +1,46 @@ +# API for colformat package + +## Exported functions + +cf_data(x, ...) +colformat(x, title = NULL, width = NULL, ...) +dim_desc(x) +extra_cols(x, ...) +format_decimal(x, sigfig = 3, ...) +format_scientific(x, sigfig = 3, superscript = TRUE) +is_vector_s3(x) +multicolformat(x, has_row_id = TRUE, width = NULL, ...) +obj_sum(x) +spark_bar(x, safe = TRUE) +spark_line(x) +squeeze(x, width = NULL, ...) +type_sum(x) + +## S3 methods + +cf_data.Date(x, ...) +cf_data.POSIXt(x, ...) +cf_data.character(x, ...) +cf_data.default(x, ...) +cf_data.list(x, ...) +cf_data.logical(x, ...) +cf_data.numeric(x, ..., sigfig = 3) +extra_cols.mcf_squeezed(x, ...) +is_vector_s3.Date(x) +is_vector_s3.POSIXct(x) +is_vector_s3.data.frame(x) +is_vector_s3.default(x) +is_vector_s3.difftime(x) +is_vector_s3.factor(x) +is_vector_s3.ordered(x) +obj_sum.POSIXlt(x) +obj_sum.default(x) +obj_sum.list(x) +type_sum.Date(x) +type_sum.POSIXct(x) +type_sum.data.frame(x) +type_sum.default(x) +type_sum.difftime(x) +type_sum.factor(x) +type_sum.ordered(x) +type_sum.tbl_df(x) diff --git a/DESCRIPTION b/DESCRIPTION index bdc4dd062..cffbfc2a5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,6 +19,7 @@ Imports: Suggests: knitr, testthat +Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi::api_roclet")) RoxygenNote: 6.0.1 Remotes: gaborcsardi/crayon diff --git a/man/colformat.Rd b/man/colformat.Rd index 8915bab30..962d30bf7 100644 --- a/man/colformat.Rd +++ b/man/colformat.Rd @@ -16,8 +16,8 @@ colformat(x, title = NULL, width = NULL, ...) \item{...}{Other arguments passed to methods} } \description{ -`colformat()` formats a vector using one row for a title (if given), -one row for the type, and `length(x)` rows for the data. +\code{colformat()} formats a vector using one row for a title (if given), +one row for the type, and \code{length(x)} rows for the data. } \examples{ x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) diff --git a/man/dim_desc.Rd b/man/dim_desc.Rd index 2eea4dd25..980abeed4 100644 --- a/man/dim_desc.Rd +++ b/man/dim_desc.Rd @@ -10,7 +10,7 @@ dim_desc(x) \item{x}{The object to format the dimensions for} } \description{ -Multi-dimensional objects are formatted as `a x b x ...`, for vectors the +Multi-dimensional objects are formatted as \code{a x b x ...}, for vectors the length is returned. } \examples{ diff --git a/man/extra_cols.Rd b/man/extra_cols.Rd index 0ee0d6e38..0d0e67a04 100644 --- a/man/extra_cols.Rd +++ b/man/extra_cols.Rd @@ -7,12 +7,12 @@ extra_cols(x, ...) } \arguments{ -\item{x}{The result of [format()] on a [multicolformat] object} +\item{x}{The result of \code{\link[=format]{format()}} on a \link{multicolformat} object} \item{...}{Unused} } \description{ -Formatting a [multicolformat] object may lead to some columns being omitted +Formatting a \link{multicolformat} object may lead to some columns being omitted due to width restrictions. This method returns a character vector that describes each of the omitted columns. } diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd index fd8669a23..1da0fb25f 100644 --- a/man/format_decimal.Rd +++ b/man/format_decimal.Rd @@ -15,10 +15,12 @@ format_decimal(x, sigfig = 3, ...) } \value{ A list with at least the following elements: -* `neg`: negative sign or space, if needed -* `lhs`: whole number -* `dec`: decimal point, if needed -* `rhs`: remainder of number +\itemize{ +\item \code{neg}: negative sign or space, if needed +\item \code{lhs}: whole number +\item \code{dec}: decimal point, if needed +\item \code{rhs}: remainder of number +} } \description{ This formatting system is designed to make it as easy as possible to diff --git a/man/format_scientific.Rd b/man/format_scientific.Rd index 35bbf3544..1bcd3c54f 100644 --- a/man/format_scientific.Rd +++ b/man/format_scientific.Rd @@ -11,7 +11,7 @@ format_scientific(x, sigfig = 3, superscript = TRUE) \item{sigfig}{Number of signficiant figures to display.} -\item{superscript}{If `TRUE`, will use superscript numbers in exponent.} +\item{superscript}{If \code{TRUE}, will use superscript numbers in exponent.} } \description{ Uses colour, careful alignment, and superscripts to display numbers diff --git a/man/multicolformat.Rd b/man/multicolformat.Rd index 38a6b009e..ea325d7eb 100644 --- a/man/multicolformat.Rd +++ b/man/multicolformat.Rd @@ -12,7 +12,7 @@ squeeze(x, width = NULL, ...) \arguments{ \item{x}{A list of vectors to format} -\item{has_row_id}{Include a column indicating row IDs? Pass `"*"` to mark +\item{has_row_id}{Include a column indicating row IDs? Pass \code{"*"} to mark the row ID column with a star.} \item{width}{Default width of the entire output, optional} @@ -23,7 +23,7 @@ the row ID column with a star.} The vectors are formatted to fit horizontally into a user-supplied number of characters per row. -The `squeeze()` function is called by [format()] and [print()] and usually +The \code{squeeze()} function is called by \code{\link[=format]{format()}} and \code{\link[=print]{print()}} and usually doesn't need to be called manually. It returns an object suitable for printing and formatting at a fixed width with additional information about omitted columns. @@ -35,13 +35,15 @@ Remaining space is then distributed proportionally to columns that do not use their desired width. For computing the column widths, two cases are distinguished: -1. When taking the minimum width for each column (plus one inter-column - space), at least one column does not fit. - In this case, the minimum width is assigned to all columns that do fit, - the non-fitting columns are stripped. -1. All columns fit with their minimum width. In this case, starting at - the leftmost column, the maximum width is allocated to the columns - until all available space is used. +\enumerate{ +\item When taking the minimum width for each column (plus one inter-column +space), at least one column does not fit. +In this case, the minimum width is assigned to all columns that do fit, +the non-fitting columns are stripped. +\item All columns fit with their minimum width. In this case, starting at +the leftmost column, the maximum width is allocated to the columns +until all available space is used. +} The remaining space is distributed from left to right. Each column gains space proportional to the fraction of missing and diff --git a/man/spark_bar.Rd b/man/spark_bar.Rd index 34a6ab55a..e499dcd29 100644 --- a/man/spark_bar.Rd +++ b/man/spark_bar.Rd @@ -14,7 +14,7 @@ height (8/8). However, the half-height and full-height blocks appear to be rendered inconsistently (possibly due to font substitution).} } \description{ -Rendered using [block elements](https://en.wikipedia.org/wiki/Block_Elements). +Rendered using \href{https://en.wikipedia.org/wiki/Block_Elements}{block elements}. In most common fixed width fonts these are rendered wider than regular characters which means they are not suitable if you need precise alignment. } diff --git a/man/type_sum.Rd b/man/type_sum.Rd index 0e375038b..043d90dca 100644 --- a/man/type_sum.Rd +++ b/man/type_sum.Rd @@ -17,11 +17,11 @@ is_vector_s3(x) and variants have been implemented.} } \description{ -`type_sum()` gives a brief summary of object type. Objects that commonly +\code{type_sum()} gives a brief summary of object type. Objects that commonly occur in a data frame should return a string with four or less characters. -`obj_sum()` also includes the size of the object if `is_vector_s3()` -is `TRUE`. +\code{obj_sum()} also includes the size of the object if \code{is_vector_s3()} +is \code{TRUE}. } \examples{ obj_sum(1:10) From 8b5e87bcde1706c61546cb5121e5cb4ef467875e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 7 Sep 2017 11:47:25 +0200 Subject: [PATCH 058/133] Fix string formatting (#39) * only quote strings that contain space, control characters, or quotes * show quotes also for empty string, quotes always in subtle style --- R/col-data.R | 6 ++++-- R/tick.R | 4 ++++ tests/testthat/out/escaped.txt | 2 +- tests/testthat/out/tibble-space.txt | 6 ++++++ tests/testthat/test-format_multi.R | 5 +++++ 5 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tests/testthat/out/tibble-space.txt diff --git a/R/col-data.R b/R/col-data.R index 207501c1f..d7624f22d 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -97,9 +97,11 @@ cf_data.POSIXt <- function(x, ...) { #' @rdname cf_data cf_data.character <- function(x, ...) { out <- x - needs_quotes <- which(!is_syntactic(x)) + needs_quotes <- which(!is_proper_string(x)) is_na <- is.na(x) - out[needs_quotes] <- encodeString(x[needs_quotes], quote ='"', na.encode = FALSE) + quoted <- encodeString(x[needs_quotes], quote = '"', na.encode = FALSE) + quoted <- gsub('^"|"$', style_subtle('"'), quoted) + out[needs_quotes] <- quoted out[is_na] <- cf_na(use_brackets_if_no_color = TRUE) width <- max(crayon::col_nchar(out, type = "width"), 0L) diff --git a/R/tick.R b/R/tick.R index 89e19256d..caf746d39 100644 --- a/R/tick.R +++ b/R/tick.R @@ -10,6 +10,10 @@ is_syntactic <- function(x) { ret } +is_proper_string <- function(x) { + grepl("^[^[:blank:][:cntrl:]\"]+$", x, perl = TRUE) +} + tick <- function(x) { x[is.na(x)] <- "NA" encodeString(x, quote = "`") diff --git a/tests/testthat/out/escaped.txt b/tests/testthat/out/escaped.txt index 8465968ac..82c0678a3 100644 --- a/tests/testthat/out/escaped.txt +++ b/tests/testthat/out/escaped.txt @@ -1,2 +1,2 @@  -"a\nb" +"a\nb" diff --git a/tests/testthat/out/tibble-space.txt b/tests/testthat/out/tibble-space.txt new file mode 100644 index 000000000..6b8f6170e --- /dev/null +++ b/tests/testthat/out/tibble-space.txt @@ -0,0 +1,6 @@ + a + +1 "" +2 " " +3 "a " +4 " a" diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index b28df790f..94d7d0bc8 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -76,6 +76,11 @@ test_that("tests from tibble", { xf = multicolformat(list(`\n` = c("\n", '"'), `\r` = factor("\n")), width = 30), filename = "tibble-newline.txt" ) + expect_colformat_output( + crayon = FALSE, + xf = multicolformat(list(a = c("", " ", "a ", " a")), width = 30), + filename = "tibble-space.txt" + ) expect_colformat_output( crayon = FALSE, xf = multicolformat(list("mean(x)" = 5, "var(x)" = 3), width = 30), From 831aade59fc76f7cafa5ccf13b1caa159016e66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 12 Sep 2017 11:24:45 +0200 Subject: [PATCH 059/133] colformat -> pillar, multicolformat -> colonnade --- API | 6 +- DESCRIPTION | 8 +- NAMESPACE | 12 +-- R/col-type.R | 2 +- R/colformat.R | 38 ++++---- R/multi.R | 24 ++--- README.Rmd | 14 +-- README.md | 14 +-- man/{multicolformat.Rd => colonnade.Rd} | 6 +- man/extra_cols.Rd | 4 +- man/{colformat.Rd => pillar.Rd} | 34 +++---- tests/testthat.R | 4 +- tests/testthat/helper-output.R | 4 +- tests/testthat/test-format_character.R | 14 +-- tests/testthat/test-format_date.R | 2 +- tests/testthat/test-format_decimal.R | 4 +- tests/testthat/test-format_factor.R | 6 +- tests/testthat/test-format_integer.R | 8 +- tests/testthat/test-format_list.R | 8 +- tests/testthat/test-format_logical.R | 2 +- tests/testthat/test-format_multi.R | 112 ++++++++++++------------ tests/testthat/test-format_numeric.R | 14 +-- tests/testthat/test-format_rowid.R | 4 +- tests/testthat/test-format_scientific.R | 4 +- tests/testthat/test-format_time.R | 4 +- tests/testthat/test-title.R | 10 +-- 26 files changed, 181 insertions(+), 181 deletions(-) rename man/{multicolformat.Rd => colonnade.Rd} (94%) rename man/{colformat.Rd => pillar.Rd} (59%) diff --git a/API b/API index ea81b689a..4b9c44f7d 100644 --- a/API +++ b/API @@ -1,16 +1,16 @@ -# API for colformat package +# API for pillar package ## Exported functions cf_data(x, ...) -colformat(x, title = NULL, width = NULL, ...) +colonnade(x, has_row_id = TRUE, width = NULL, ...) dim_desc(x) extra_cols(x, ...) format_decimal(x, sigfig = 3, ...) format_scientific(x, sigfig = 3, superscript = TRUE) is_vector_s3(x) -multicolformat(x, has_row_id = TRUE, width = NULL, ...) obj_sum(x) +pillar(x, title = NULL, width = NULL, ...) spark_bar(x, safe = TRUE) spark_line(x) squeeze(x, width = NULL, ...) diff --git a/DESCRIPTION b/DESCRIPTION index cffbfc2a5..3d527506e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,17 +1,17 @@ -Package: colformat +Package: pillar Title: Coloured Formatting for Columns Version: 0.0.0.9000 Authors@R: c( person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")), person("RStudio", role = "cph") ) -Description: Provides a `colformat` generic designed for formatting columns +Description: Provides a `pillar` generic designed for formatting columns of data using the full range of colours provided by modern terminals. License: GPL-3 Encoding: UTF-8 LazyData: true -URL: https://github.com/hadley/colformat -BugReports: https://github.com/hadley/colformat/issues +URL: https://github.com/hadley/pillar +BugReports: https://github.com/hadley/pillar/issues Imports: crayon, methods, diff --git a/NAMESPACE b/NAMESPACE index a84ae92c2..a736d61af 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,10 +11,10 @@ S3method(extra_cols,mcf_squeezed) S3method(format,cf_data) S3method(format,cf_title) S3method(format,cf_type) -S3method(format,colformat) +S3method(format,colonnade) S3method(format,decimal_format) S3method(format,mcf_squeezed) -S3method(format,multicolformat) +S3method(format,pillar) S3method(format,rif_data) S3method(format,rif_title) S3method(format,rif_type) @@ -30,10 +30,10 @@ S3method(obj_sum,default) S3method(obj_sum,list) S3method(print,cf_data) S3method(print,cf_vertical) -S3method(print,colformat) +S3method(print,colonnade) S3method(print,decimal_format) S3method(print,mcf_squeezed) -S3method(print,multicolformat) +S3method(print,pillar) S3method(print,rif_data) S3method(print,spark) S3method(type_sum,Date) @@ -45,14 +45,14 @@ S3method(type_sum,factor) S3method(type_sum,ordered) S3method(type_sum,tbl_df) export(cf_data) -export(colformat) +export(colonnade) export(dim_desc) export(extra_cols) export(format_decimal) export(format_scientific) export(is_vector_s3) -export(multicolformat) export(obj_sum) +export(pillar) export(spark_bar) export(spark_line) export(squeeze) diff --git a/R/col-type.R b/R/col-type.R index 1b1170813..1be780550 100644 --- a/R/col-type.R +++ b/R/col-type.R @@ -24,6 +24,6 @@ format.cf_type <- function(x, width = NULL, ...) { if (is.null(width) || width >= get_width(x)) type <- x$type else type <- crayon::col_substr(x$type, 1, width - 2) # Style is applied later on because we want a continuous underline over the - # whole width and over all columns in multicolformat() + # whole width and over all columns in colonnade() paste0("<", type, ">") } diff --git a/R/colformat.R b/R/colformat.R index d3057ffdb..388389cd4 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -1,6 +1,6 @@ #' Format a vector suitable for tabular display #' -#' `colformat()` formats a vector using one row for a title (if given), +#' `pillar()` formats a vector using one row for a title (if given), #' one row for the type, and `length(x)` rows for the data. #' #' @param x A vector to format @@ -10,36 +10,36 @@ #' @export #' @examples #' x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) -#' colformat(x) -#' colformat(-x) -#' colformat(runif(10)) -#' colformat(rcauchy(20)) +#' pillar(x) +#' pillar(-x) +#' pillar(runif(10)) +#' pillar(rcauchy(20)) #' #' # Special values are highlighted -#' colformat(c(runif(5), NA, NaN, Inf, -Inf)) +#' pillar(c(runif(5), NA, NaN, Inf, -Inf)) #' #' # Very wide ranges will be displayed in scientific format -#' colformat(c(1e10, 1e-10), width = 20) -#' colformat(c(1e10, 1e-10)) +#' pillar(c(1e10, 1e-10), width = 20) +#' pillar(c(1e10, 1e-10)) #' #' x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) -#' colformat(x) +#' pillar(x) #' #' x <- c("This is string is rather long", NA, "?", "Short") -#' colformat(x) -#' colformat(x, width = 30) -#' colformat(x, width = 5) +#' pillar(x) +#' pillar(x, width = 30) +#' pillar(x, width = 5) #' #' date <- as.Date("2017-05-15") -#' colformat(date + c(1, NA, 3:5)) -#' colformat(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) -colformat <- function(x, title = NULL, width = NULL, ...) { +#' pillar(date + c(1, NA, 3:5)) +#' pillar(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) +pillar <- function(x, title = NULL, width = NULL, ...) { title <- cf_title(title, ...) type <- cf_type(x, ...) data <- cf_data(x, ...) ret <- structure( list(title = title, type = type, data = data), - class = "colformat" + class = "pillar" ) ret <- set_width(ret, width) ret @@ -51,13 +51,13 @@ rowidformat <- function(n, has_title_row = FALSE, has_star = FALSE, ...) { data <- rif_data(n, ...) ret <- structure( list(title = title, type = type, data = data), - class = "colformat" + class = "pillar" ) ret } #' @export -format.colformat <- function(x, width = NULL, ...) { +format.pillar <- function(x, width = NULL, ...) { width <- cf_get_width(x, width) out <- cf_format_parts(x, width) @@ -67,7 +67,7 @@ format.colformat <- function(x, width = NULL, ...) { } #' @export -print.colformat <- function(x, ...) { +print.pillar <- function(x, ...) { print(format(x, ...)) } diff --git a/R/multi.R b/R/multi.R index e597ee346..37694b584 100644 --- a/R/multi.R +++ b/R/multi.R @@ -9,12 +9,12 @@ #' @param width Default width of the entire output, optional #' @param ... Ignored #' @export -multicolformat <- function(x, has_row_id = TRUE, width = NULL, ...) { +colonnade <- function(x, has_row_id = TRUE, width = NULL, ...) { has_title <- is_named(x) if (has_title) { - ret <- map2(x, names(x), colformat) + ret <- map2(x, names(x), pillar) } else { - ret <- map(x, colformat) + ret <- map(x, pillar) } if (!is_false(has_row_id) && length(x) > 0) { rowid <- rowidformat( @@ -25,7 +25,7 @@ multicolformat <- function(x, has_row_id = TRUE, width = NULL, ...) { ret <- c(list(rowid), ret) } zero_height <- length(x) == 0L || length(x[[1]]) == 0L - ret <- structure(ret, zero_height = zero_height, class = "multicolformat") + ret <- structure(ret, zero_height = zero_height, class = "colonnade") ret <- set_width(ret, width) ret } @@ -36,7 +36,7 @@ multicolformat <- function(x, has_row_id = TRUE, width = NULL, ...) { #' It returns an object suitable for printing and formatting at a fixed width #' with additional information about omitted columns. #' -#' @rdname multicolformat +#' @rdname colonnade #' @export squeeze <- function(x, width = NULL, ...) { # Hacky shortcut for zero-height corner case @@ -97,10 +97,10 @@ knit_print.mcf_squeezed <- function(x, ...) { #' Retrieve information about columns that didn't fit the available width #' -#' Formatting a [multicolformat] object may lead to some columns being omitted +#' Formatting a [colonnade] object may lead to some columns being omitted #' due to width restrictions. This method returns a character vector that #' describes each of the omitted columns. -#' @param x The result of [format()] on a [multicolformat] object +#' @param x The result of [format()] on a [colonnade] object #' @param ... Unused #' @export extra_cols <- function(x, ...) { @@ -113,16 +113,16 @@ extra_cols.mcf_squeezed <- function(x, ...) { } #' @export -format.multicolformat <- function(x, ...) { +format.colonnade <- function(x, ...) { format(squeeze(x, ...)) } #' @export -print.multicolformat <- function(x, ...) { +print.colonnade <- function(x, ...) { print(format(x, ...)) } -#' @rdname multicolformat +#' @rdname colonnade #' @usage NULL #' @aliases NULL mcf_get_width <- function(x, width) { @@ -142,7 +142,7 @@ mcf_get_width <- function(x, width) { col_widths + added_space } -#' @rdname multicolformat +#' @rdname colonnade #' @usage NULL #' @aliases NULL mcf_compute_col_widths <- function(min_widths, max_widths, width) { @@ -170,7 +170,7 @@ mcf_compute_col_widths <- function(min_widths, max_widths, width) { col_widths } -#' @rdname multicolformat +#' @rdname colonnade #' @usage NULL #' @aliases NULL mcf_distribute_space <- function(col_widths, max_widths, width) { diff --git a/README.Rmd b/README.Rmd index 477793b53..dade53b59 100644 --- a/README.Rmd +++ b/README.Rmd @@ -14,28 +14,28 @@ knitr::opts_chunk$set( ) ``` -# colformat +# pillar -[![Travis-CI Build Status](https://travis-ci.org/hadley/colformat.svg?branch=master)](https://travis-ci.org/hadley/colformat) +[![Travis-CI Build Status](https://travis-ci.org/hadley/pillar.svg?branch=master)](https://travis-ci.org/hadley/pillar) -colformat provides tools for styling columns of data, artfully using colour and unicode characters to +pillar provides tools for styling columns of data, artfully using colour and unicode characters to ## Installation ```{r, eval = FALSE} # install.packages("devtools") -devtools::install_github("hadley/colformat") +devtools::install_github("hadley/pillar") ``` ## Usage -colformat is not designed for end-users but will eventually be incorporated in packages like [tibble](http://tibble.tidyverse.org). +pillar is not designed for end-users but will eventually be incorporated in packages like [tibble](http://tibble.tidyverse.org). ```{r} -library(colformat) +library(pillar) x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10)) -colformat(x) +pillar(x) ``` If you render this in a console that supports colour, you'll see something that looks like this: diff --git a/README.md b/README.md index 454c8c13d..0314d550b 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,30 @@ -colformat +pillar ========= -[![Travis-CI Build Status](https://travis-ci.org/hadley/colformat.svg?branch=master)](https://travis-ci.org/hadley/colformat) +[![Travis-CI Build Status](https://travis-ci.org/hadley/pillar.svg?branch=master)](https://travis-ci.org/hadley/pillar) -colformat provides tools for styling columns of data, artfully using colour and unicode characters to +pillar provides tools for styling columns of data, artfully using colour and unicode characters to Installation ------------ ``` r # install.packages("devtools") -devtools::install_github("hadley/colformat") +devtools::install_github("hadley/pillar") ``` Usage ----- -colformat is not designed for end-users but will eventually be incorporated in packages like [tibble](http://tibble.tidyverse.org). +pillar is not designed for end-users but will eventually be incorporated in packages like [tibble](http://tibble.tidyverse.org). ``` r -library(colformat) +library(pillar) x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10)) -colformat(x) +pillar(x) #> title #> 1234567890 #> 123457 diff --git a/man/multicolformat.Rd b/man/colonnade.Rd similarity index 94% rename from man/multicolformat.Rd rename to man/colonnade.Rd index ea325d7eb..5c199ad42 100644 --- a/man/multicolformat.Rd +++ b/man/colonnade.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/multi.R -\name{multicolformat} -\alias{multicolformat} +\name{colonnade} +\alias{colonnade} \alias{squeeze} \title{Format multiple vectors in a tabular display} \usage{ -multicolformat(x, has_row_id = TRUE, width = NULL, ...) +colonnade(x, has_row_id = TRUE, width = NULL, ...) squeeze(x, width = NULL, ...) } diff --git a/man/extra_cols.Rd b/man/extra_cols.Rd index 0d0e67a04..1328f63ab 100644 --- a/man/extra_cols.Rd +++ b/man/extra_cols.Rd @@ -7,12 +7,12 @@ extra_cols(x, ...) } \arguments{ -\item{x}{The result of \code{\link[=format]{format()}} on a \link{multicolformat} object} +\item{x}{The result of \code{\link[=format]{format()}} on a \link{colonnade} object} \item{...}{Unused} } \description{ -Formatting a \link{multicolformat} object may lead to some columns being omitted +Formatting a \link{colonnade} object may lead to some columns being omitted due to width restrictions. This method returns a character vector that describes each of the omitted columns. } diff --git a/man/colformat.Rd b/man/pillar.Rd similarity index 59% rename from man/colformat.Rd rename to man/pillar.Rd index 962d30bf7..952ded227 100644 --- a/man/colformat.Rd +++ b/man/pillar.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/colformat.R -\name{colformat} -\alias{colformat} +\name{pillar} +\alias{pillar} \title{Format a vector suitable for tabular display} \usage{ -colformat(x, title = NULL, width = NULL, ...) +pillar(x, title = NULL, width = NULL, ...) } \arguments{ \item{x}{A vector to format} @@ -16,32 +16,32 @@ colformat(x, title = NULL, width = NULL, ...) \item{...}{Other arguments passed to methods} } \description{ -\code{colformat()} formats a vector using one row for a title (if given), +\code{pillar()} formats a vector using one row for a title (if given), one row for the type, and \code{length(x)} rows for the data. } \examples{ x <- 123456789 * (10 ^ c(-1, -3, -5, NA, -8, -10)) -colformat(x) -colformat(-x) -colformat(runif(10)) -colformat(rcauchy(20)) +pillar(x) +pillar(-x) +pillar(runif(10)) +pillar(rcauchy(20)) # Special values are highlighted -colformat(c(runif(5), NA, NaN, Inf, -Inf)) +pillar(c(runif(5), NA, NaN, Inf, -Inf)) # Very wide ranges will be displayed in scientific format -colformat(c(1e10, 1e-10), width = 20) -colformat(c(1e10, 1e-10)) +pillar(c(1e10, 1e-10), width = 20) +pillar(c(1e10, 1e-10)) x <- c(FALSE, NA, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE) -colformat(x) +pillar(x) x <- c("This is string is rather long", NA, "?", "Short") -colformat(x) -colformat(x, width = 30) -colformat(x, width = 5) +pillar(x) +pillar(x, width = 30) +pillar(x, width = 5) date <- as.Date("2017-05-15") -colformat(date + c(1, NA, 3:5)) -colformat(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) +pillar(date + c(1, NA, 3:5)) +pillar(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) } diff --git a/tests/testthat.R b/tests/testthat.R index d258b07b6..4a2f6aaba 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,4 +1,4 @@ library(testthat) -library(colformat) +library(pillar) -test_check("colformat") +test_check("pillar") diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index de531a028..954b4feb6 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -1,5 +1,5 @@ -expect_colformat_output <- function(x, ..., filename, - xp = add_special(x), xf = colformat(xp, ...), +expect_pillar_output <- function(x, ..., filename, + xp = add_special(x), xf = pillar(xp, ...), crayon = TRUE) { if (crayon) { old <- options(crayon.enabled = TRUE, crayon.colors = 16L) diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index d9884ec47..ee3070ab3 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -1,11 +1,11 @@ context("format_character") test_that("output test", { - expect_colformat_output(letters[1:5], filename = "letters.txt") - expect_colformat_output(paste(letters, collapse = ""), filename = "letters-long.txt") - expect_colformat_output(paste(letters, collapse = ""), width = 10, filename = "letters-long-10.txt") - expect_colformat_output(paste(letters, collapse = ""), width = 3, filename = "letters-long-03.txt") - expect_colformat_output("\u6210\u4ea4\u65e5", title = "\u6210\u4ea4", filename = "deal1.txt") - expect_colformat_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") - expect_colformat_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") + expect_pillar_output(letters[1:5], filename = "letters.txt") + expect_pillar_output(paste(letters, collapse = ""), filename = "letters-long.txt") + expect_pillar_output(paste(letters, collapse = ""), width = 10, filename = "letters-long-10.txt") + expect_pillar_output(paste(letters, collapse = ""), width = 3, filename = "letters-long-03.txt") + expect_pillar_output("\u6210\u4ea4\u65e5", title = "\u6210\u4ea4", filename = "deal1.txt") + expect_pillar_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") + expect_pillar_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") }) diff --git a/tests/testthat/test-format_date.R b/tests/testthat/test-format_date.R index 220795272..300f7502f 100644 --- a/tests/testthat/test-format_date.R +++ b/tests/testthat/test-format_date.R @@ -1,5 +1,5 @@ context("format_date") test_that("output test", { - expect_colformat_output(as.Date("2017-07-28"), filename = "date.txt") + expect_pillar_output(as.Date("2017-07-28"), filename = "date.txt") }) diff --git a/tests/testthat/test-format_decimal.R b/tests/testthat/test-format_decimal.R index b12822ef6..309127bef 100644 --- a/tests/testthat/test-format_decimal.R +++ b/tests/testthat/test-format_decimal.R @@ -74,6 +74,6 @@ test_that("corner cases", { }) test_that("output test", { - expect_colformat_output((10 ^ (-3:4)) * c(-1, 1), filename = "basic.txt") - expect_colformat_output(1.23456 * 10 ^ (-3:3), filename = "decimal-insignif.txt") + expect_pillar_output((10 ^ (-3:4)) * c(-1, 1), filename = "basic.txt") + expect_pillar_output(1.23456 * 10 ^ (-3:3), filename = "decimal-insignif.txt") }) diff --git a/tests/testthat/test-format_factor.R b/tests/testthat/test-format_factor.R index afeb4d1b0..74bc4339e 100644 --- a/tests/testthat/test-format_factor.R +++ b/tests/testthat/test-format_factor.R @@ -1,7 +1,7 @@ context("format_factor") test_that("output test", { - expect_colformat_output(xp = factor(c(letters[1:5], NA)), filename = "factor.txt") - expect_colformat_output(xp = ordered(c(letters[1:5], NA)), filename = "ordered.txt") - expect_colformat_output(xp = factor("a\nb"), filename = "escaped.txt") + expect_pillar_output(xp = factor(c(letters[1:5], NA)), filename = "factor.txt") + expect_pillar_output(xp = ordered(c(letters[1:5], NA)), filename = "ordered.txt") + expect_pillar_output(xp = factor("a\nb"), filename = "escaped.txt") }) diff --git a/tests/testthat/test-format_integer.R b/tests/testthat/test-format_integer.R index e6f9890e3..5a407e2db 100644 --- a/tests/testthat/test-format_integer.R +++ b/tests/testthat/test-format_integer.R @@ -2,8 +2,8 @@ context("format_numeric") test_that("integer output will use scientific if necessary", { x <- 10000000L + 1:3 - expect_colformat_output(x, width = 6, filename = "integer-06.txt") - expect_colformat_output(x, width = 7, filename = "integer-07.txt") - expect_colformat_output(x, width = 8, filename = "integer-08.txt") - expect_colformat_output(x, width = 9, filename = "integer-09.txt") + expect_pillar_output(x, width = 6, filename = "integer-06.txt") + expect_pillar_output(x, width = 7, filename = "integer-07.txt") + expect_pillar_output(x, width = 8, filename = "integer-08.txt") + expect_pillar_output(x, width = 9, filename = "integer-09.txt") }) diff --git a/tests/testthat/test-format_list.R b/tests/testthat/test-format_list.R index 52f2ec8b9..c7a3f341f 100644 --- a/tests/testthat/test-format_list.R +++ b/tests/testthat/test-format_list.R @@ -1,8 +1,8 @@ context("format_list") test_that("output test", { - expect_colformat_output(xp = as.list(1:3), filename = "list-each.txt") - expect_colformat_output(xp = list(1:3, NULL), filename = "list-null.txt") - expect_colformat_output(list(1:3), filename = "list-na.txt") - expect_colformat_output(xp = list(iris), width = 10, filename = "list-narrow.txt") + expect_pillar_output(xp = as.list(1:3), filename = "list-each.txt") + expect_pillar_output(xp = list(1:3, NULL), filename = "list-null.txt") + expect_pillar_output(list(1:3), filename = "list-na.txt") + expect_pillar_output(xp = list(iris), width = 10, filename = "list-narrow.txt") }) diff --git a/tests/testthat/test-format_logical.R b/tests/testthat/test-format_logical.R index 4af64ab32..672d4cc84 100644 --- a/tests/testthat/test-format_logical.R +++ b/tests/testthat/test-format_logical.R @@ -1,5 +1,5 @@ context("format_logical") test_that("output test", { - expect_colformat_output(c(TRUE, FALSE), filename = "logical.txt") + expect_pillar_output(c(TRUE, FALSE), filename = "logical.txt") }) diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 94d7d0bc8..2e8264bf6 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -2,99 +2,99 @@ context("format_multi") test_that("output test", { x <- list(column_zero_one = 1:3 + 0.23, col_02 = letters[1:3], col_03 = factor(letters[1:3]), col_04 = ordered(letters[1:3])) - expect_colformat_output(xf = multicolformat(x, width = 4), filename = "multi-04.txt") - expect_colformat_output(xf = multicolformat(x, width = 5), filename = "multi-05.txt") - expect_colformat_output(xf = multicolformat(x, width = 6), filename = "multi-06.txt") - expect_colformat_output(xf = multicolformat(x, width = 7), filename = "multi-07.txt") - expect_colformat_output(xf = multicolformat(x, width = 8), filename = "multi-08.txt") - expect_colformat_output(xf = multicolformat(x, width = 9), filename = "multi-09.txt") - expect_colformat_output(xf = multicolformat(x, width = 10), filename = "multi-10.txt") - expect_colformat_output(xf = multicolformat(x, width = 11), filename = "multi-11.txt") - expect_colformat_output(xf = multicolformat(x, width = 12), filename = "multi-12.txt") - expect_colformat_output(xf = multicolformat(x, width = 13), filename = "multi-13.txt") - expect_colformat_output(xf = multicolformat(x, width = 14), filename = "multi-14.txt") - expect_colformat_output(xf = multicolformat(x, width = 15), filename = "multi-15.txt") - expect_colformat_output(xf = multicolformat(x, width = 16), filename = "multi-16.txt") - expect_colformat_output(xf = multicolformat(x, width = 17), filename = "multi-17.txt") - expect_colformat_output(xf = multicolformat(x, width = 18), filename = "multi-18.txt") - expect_colformat_output(xf = multicolformat(x, width = 19), filename = "multi-19.txt") - expect_colformat_output(xf = multicolformat(x, width = 20), filename = "multi-20.txt") - expect_colformat_output(xf = multicolformat(x, width = 21), filename = "multi-21.txt") - expect_colformat_output(xf = multicolformat(x, width = 22), filename = "multi-22.txt") - expect_colformat_output(xf = multicolformat(x, width = 23), filename = "multi-23.txt") - expect_colformat_output(xf = multicolformat(x, width = 24), filename = "multi-24.txt") - expect_colformat_output(xf = multicolformat(x, width = 25), filename = "multi-25.txt") - expect_colformat_output(xf = multicolformat(x, width = 26), filename = "multi-26.txt") - expect_colformat_output(xf = multicolformat(x, width = 27), filename = "multi-27.txt") - expect_colformat_output(xf = multicolformat(x, width = 28), filename = "multi-28.txt") - expect_colformat_output(xf = multicolformat(x, width = 29), filename = "multi-29.txt") - expect_colformat_output(xf = multicolformat(x, width = 30), filename = "multi-30.txt") - expect_colformat_output(xf = multicolformat(x, width = 31), filename = "multi-31.txt") - expect_colformat_output(xf = multicolformat(x, width = 32), filename = "multi-32.txt") - expect_colformat_output(xf = multicolformat(x, width = 33), filename = "multi-33.txt") - expect_colformat_output(xf = multicolformat(x, width = 34), filename = "multi-34.txt") - expect_colformat_output(xf = multicolformat(x, width = 35), filename = "multi-35.txt") - expect_colformat_output(xf = multicolformat(x, width = 36), filename = "multi-36.txt") - expect_colformat_output(xf = multicolformat(x, width = 37), filename = "multi-37.txt") - expect_colformat_output(xf = multicolformat(x, width = 38), filename = "multi-38.txt") - expect_colformat_output(xf = multicolformat(x, width = 39), filename = "multi-39.txt") + expect_pillar_output(xf = colonnade(x, width = 4), filename = "multi-04.txt") + expect_pillar_output(xf = colonnade(x, width = 5), filename = "multi-05.txt") + expect_pillar_output(xf = colonnade(x, width = 6), filename = "multi-06.txt") + expect_pillar_output(xf = colonnade(x, width = 7), filename = "multi-07.txt") + expect_pillar_output(xf = colonnade(x, width = 8), filename = "multi-08.txt") + expect_pillar_output(xf = colonnade(x, width = 9), filename = "multi-09.txt") + expect_pillar_output(xf = colonnade(x, width = 10), filename = "multi-10.txt") + expect_pillar_output(xf = colonnade(x, width = 11), filename = "multi-11.txt") + expect_pillar_output(xf = colonnade(x, width = 12), filename = "multi-12.txt") + expect_pillar_output(xf = colonnade(x, width = 13), filename = "multi-13.txt") + expect_pillar_output(xf = colonnade(x, width = 14), filename = "multi-14.txt") + expect_pillar_output(xf = colonnade(x, width = 15), filename = "multi-15.txt") + expect_pillar_output(xf = colonnade(x, width = 16), filename = "multi-16.txt") + expect_pillar_output(xf = colonnade(x, width = 17), filename = "multi-17.txt") + expect_pillar_output(xf = colonnade(x, width = 18), filename = "multi-18.txt") + expect_pillar_output(xf = colonnade(x, width = 19), filename = "multi-19.txt") + expect_pillar_output(xf = colonnade(x, width = 20), filename = "multi-20.txt") + expect_pillar_output(xf = colonnade(x, width = 21), filename = "multi-21.txt") + expect_pillar_output(xf = colonnade(x, width = 22), filename = "multi-22.txt") + expect_pillar_output(xf = colonnade(x, width = 23), filename = "multi-23.txt") + expect_pillar_output(xf = colonnade(x, width = 24), filename = "multi-24.txt") + expect_pillar_output(xf = colonnade(x, width = 25), filename = "multi-25.txt") + expect_pillar_output(xf = colonnade(x, width = 26), filename = "multi-26.txt") + expect_pillar_output(xf = colonnade(x, width = 27), filename = "multi-27.txt") + expect_pillar_output(xf = colonnade(x, width = 28), filename = "multi-28.txt") + expect_pillar_output(xf = colonnade(x, width = 29), filename = "multi-29.txt") + expect_pillar_output(xf = colonnade(x, width = 30), filename = "multi-30.txt") + expect_pillar_output(xf = colonnade(x, width = 31), filename = "multi-31.txt") + expect_pillar_output(xf = colonnade(x, width = 32), filename = "multi-32.txt") + expect_pillar_output(xf = colonnade(x, width = 33), filename = "multi-33.txt") + expect_pillar_output(xf = colonnade(x, width = 34), filename = "multi-34.txt") + expect_pillar_output(xf = colonnade(x, width = 35), filename = "multi-35.txt") + expect_pillar_output(xf = colonnade(x, width = 36), filename = "multi-36.txt") + expect_pillar_output(xf = colonnade(x, width = 37), filename = "multi-37.txt") + expect_pillar_output(xf = colonnade(x, width = 38), filename = "multi-38.txt") + expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") - expect_colformat_output( - xf = new_vertical(extra_cols(squeeze(multicolformat(x), width = 10))), + expect_pillar_output( + xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), filename = "multi-extra-10.txt" ) }) test_that("tests from tibble", { - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(mtcars[1:8, ], has_row_id = "*", width = 30), + xf = colonnade(mtcars[1:8, ], has_row_id = "*", width = 30), filename = "tibble-mtcars-8-30.txt" ) - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(iris[1:5, ], width = 30), + xf = colonnade(iris[1:5, ], width = 30), filename = "tibble-iris-5-30.txt" ) - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(iris[1:3, ], width = 20), + xf = colonnade(iris[1:3, ], width = 20), filename = "tibble-iris-3-20.txt" ) - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(df_all, width = 30), + xf = colonnade(df_all, width = 30), filename = "tibble-all--30.txt" ) - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(df_all, width = 300), + xf = colonnade(df_all, width = 300), filename = "tibble-all--300.txt" ) - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(list(`\n` = c("\n", '"'), `\r` = factor("\n")), width = 30), + xf = colonnade(list(`\n` = c("\n", '"'), `\r` = factor("\n")), width = 30), filename = "tibble-newline.txt" ) - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(list(a = c("", " ", "a ", " a")), width = 30), + xf = colonnade(list(a = c("", " ", "a ", " a")), width = 30), filename = "tibble-space.txt" ) - expect_colformat_output( + expect_pillar_output( crayon = FALSE, - xf = multicolformat(list("mean(x)" = 5, "var(x)" = 3), width = 30), + xf = colonnade(list("mean(x)" = 5, "var(x)" = 3), width = 30), filename = "tibble-non-syntactic.txt" ) }) test_that("empty", { expect_equal( - format(multicolformat(list(a = character(), b = logical()), width = 30)), + format(colonnade(list(a = character(), b = logical()), width = 30)), structure(character(), class = "cf_vertical") ) expect_equal( - format(multicolformat(iris[1:5, character()], width = 30)), + format(colonnade(iris[1:5, character()], width = 30)), structure(character(), class = "cf_vertical") ) }) diff --git a/tests/testthat/test-format_numeric.R b/tests/testthat/test-format_numeric.R index f9dcf2c2b..f5b1da5b6 100644 --- a/tests/testthat/test-format_numeric.R +++ b/tests/testthat/test-format_numeric.R @@ -1,13 +1,13 @@ context("format_numeric") -test_that("same colformat at different widths", { +test_that("same pillar at different widths", { v <- 10 ^ c(-9, -6, 3, 9) - x <- colformat(v) + x <- pillar(v) expect_equal(get_min_width(x$data), 7) expect_equal(get_width(x$data), 22) - expect_colformat_output(xp = v, width = 4, filename = "numeric-04.txt") - expect_colformat_output(xp = v, width = 7, filename = "numeric-07.txt") - expect_colformat_output(xp = v, width = 10, filename = "numeric-10.txt") - expect_colformat_output(xp = v, width = 15, filename = "numeric-15.txt") - expect_colformat_output(xp = v, width = 22, filename = "numeric-22.txt") + expect_pillar_output(xp = v, width = 4, filename = "numeric-04.txt") + expect_pillar_output(xp = v, width = 7, filename = "numeric-07.txt") + expect_pillar_output(xp = v, width = 10, filename = "numeric-10.txt") + expect_pillar_output(xp = v, width = 15, filename = "numeric-15.txt") + expect_pillar_output(xp = v, width = 22, filename = "numeric-22.txt") }) diff --git a/tests/testthat/test-format_rowid.R b/tests/testthat/test-format_rowid.R index 5d20ef900..5f1313a2e 100644 --- a/tests/testthat/test-format_rowid.R +++ b/tests/testthat/test-format_rowid.R @@ -1,11 +1,11 @@ context("format_rowid") test_that("output test", { - expect_colformat_output( + expect_pillar_output( xf = rowidformat(3), filename = "rowid-3.txt" ) - expect_colformat_output( + expect_pillar_output( xf = rowidformat(12, has_title = TRUE, has_star = TRUE), filename = "rowid-star-title-12.txt" ) diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index 8f79a1274..584265385 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -23,6 +23,6 @@ test_that("exponents correct in presence of NA", { }) test_that("output test", { - expect_colformat_output(10 ^ c(-9, -6, 3, 9), width = 10, filename = "scientific.txt") - expect_colformat_output((10 ^ c(3, 9, 15, 22)) * c(-1, 1), width = 10, filename = "scientific-short-neg.txt") + expect_pillar_output(10 ^ c(-9, -6, 3, 9), width = 10, filename = "scientific.txt") + expect_pillar_output((10 ^ c(3, 9, 15, 22)) * c(-1, 1), width = 10, filename = "scientific-short-neg.txt") }) diff --git a/tests/testthat/test-format_time.R b/tests/testthat/test-format_time.R index cec3eacdc..e3cb03976 100644 --- a/tests/testthat/test-format_time.R +++ b/tests/testthat/test-format_time.R @@ -1,6 +1,6 @@ context("format_time") test_that("output test", { - expect_colformat_output(as.POSIXct("2017-07-28 18:04:35 +0200"), filename = "time.txt") - expect_colformat_output(as.POSIXlt("2017-07-28 18:04:35 +0200"), filename = "time-posix.txt") + expect_pillar_output(as.POSIXct("2017-07-28 18:04:35 +0200"), filename = "time.txt") + expect_pillar_output(as.POSIXlt("2017-07-28 18:04:35 +0200"), filename = "time-posix.txt") }) diff --git a/tests/testthat/test-title.R b/tests/testthat/test-title.R index e96c88230..1730e85ea 100644 --- a/tests/testthat/test-title.R +++ b/tests/testthat/test-title.R @@ -1,9 +1,9 @@ context("title") test_that("with and without title", { - expect_colformat_output(10 ^ (1:6), filename = "title-none.txt", crayon = FALSE) - expect_colformat_output(10 ^ (1:6), title = "crayon", filename = "title-crayon.txt") - expect_colformat_output(10 ^ (1:6), title = "short", filename = "title-short.txt", crayon = FALSE) - expect_colformat_output(10 ^ (1:6), title = "somewhat_wider", filename = "title-longer.txt", crayon = FALSE) - expect_colformat_output(10 ^ (1:6), title = "much_too_wide", width = 7, filename = "title-too-long.txt", crayon = FALSE) + expect_pillar_output(10 ^ (1:6), filename = "title-none.txt", crayon = FALSE) + expect_pillar_output(10 ^ (1:6), title = "crayon", filename = "title-crayon.txt") + expect_pillar_output(10 ^ (1:6), title = "short", filename = "title-short.txt", crayon = FALSE) + expect_pillar_output(10 ^ (1:6), title = "somewhat_wider", filename = "title-longer.txt", crayon = FALSE) + expect_pillar_output(10 ^ (1:6), title = "much_too_wide", width = 7, filename = "title-too-long.txt", crayon = FALSE) }) From 3c43a5f91a39062cd40b6d753c6280fc7877858b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 15 Sep 2017 21:01:49 +0200 Subject: [PATCH 060/133] Clean up API (#38) * unexport unused * decimal_format -> cf_decimal * get rid of now broken examples * export new_cf_data() * cf_data() supports NA * export style_subtle() * export style_neg() * remove examples * export style_num() * make testthat helpers available * add style_signif() * Revert "add style_signif()" This reverts commit 3250ead839322abda0a65fcd5e71721d96d0cdb4. * add to API * fix typo * add ellipsis * cf_data(na_pos) * use col_nchar() instead of nchar() * export expectation function * properly align NA * Merge commit '8b5e87bcde1706c61546cb5121e5cb4ef467875e' * document * colformat -> pillar, multicolformat -> colonnade * cf_data -> pillar_shaft * rename var * fix R CMD check * up remote --- API | 25 ++++---- DESCRIPTION | 2 +- NAMESPACE | 42 +++++++------- R/col-data.R | 76 ++++++++++++++----------- R/col-title.R | 6 +- R/col-type.R | 6 +- R/colformat.R | 21 ++++--- R/column.R | 6 +- R/multi.R | 32 +++++------ R/scientific.R | 4 -- R/sigfig.R | 31 ++++------ R/styles.R | 11 +++- R/testthat.R | 42 ++++++++++++++ R/utils.R | 4 +- R/zzz.R | 2 +- man/cf_data.Rd | 41 ------------- man/expect_pillar_output.Rd | 34 +++++++++++ man/format_decimal.Rd | 10 ---- man/format_scientific.Rd | 4 -- man/pillar_shaft.Rd | 53 +++++++++++++++++ man/style_subtle.Rd | 24 ++++++++ tests/testthat/helper-output.R | 26 --------- tests/testthat/out/letters-long-03.txt | 2 +- tests/testthat/out/letters-long-10.txt | 2 +- tests/testthat/test-format_multi.R | 4 +- tests/testthat/test-format_scientific.R | 2 +- 26 files changed, 295 insertions(+), 217 deletions(-) create mode 100644 R/testthat.R delete mode 100644 man/cf_data.Rd create mode 100644 man/expect_pillar_output.Rd create mode 100644 man/pillar_shaft.Rd create mode 100644 man/style_subtle.Rd diff --git a/API b/API index 4b9c44f7d..699902a0d 100644 --- a/API +++ b/API @@ -2,30 +2,26 @@ ## Exported functions -cf_data(x, ...) colonnade(x, has_row_id = TRUE, width = NULL, ...) dim_desc(x) +expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, ...), crayon = TRUE) extra_cols(x, ...) -format_decimal(x, sigfig = 3, ...) -format_scientific(x, sigfig = 3, superscript = TRUE) is_vector_s3(x) +new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), 0L), align = "left", min_width = NULL, na_indent = 0L) obj_sum(x) pillar(x, title = NULL, width = NULL, ...) +pillar_shaft(x, ...) spark_bar(x, safe = TRUE) spark_line(x) squeeze(x, width = NULL, ...) +style_neg(x) +style_num(x, negative, significant = rep_along(x, TRUE)) +style_subtle(x) type_sum(x) ## S3 methods -cf_data.Date(x, ...) -cf_data.POSIXt(x, ...) -cf_data.character(x, ...) -cf_data.default(x, ...) -cf_data.list(x, ...) -cf_data.logical(x, ...) -cf_data.numeric(x, ..., sigfig = 3) -extra_cols.mcf_squeezed(x, ...) +extra_cols.squeezed_colonnade(x, ...) is_vector_s3.Date(x) is_vector_s3.POSIXct(x) is_vector_s3.data.frame(x) @@ -36,6 +32,13 @@ is_vector_s3.ordered(x) obj_sum.POSIXlt(x) obj_sum.default(x) obj_sum.list(x) +pillar_shaft.Date(x, ...) +pillar_shaft.POSIXt(x, ...) +pillar_shaft.character(x, ...) +pillar_shaft.default(x, ...) +pillar_shaft.list(x, ...) +pillar_shaft.logical(x, ...) +pillar_shaft.numeric(x, ..., sigfig = 3) type_sum.Date(x) type_sum.POSIXct(x) type_sum.data.frame(x) diff --git a/DESCRIPTION b/DESCRIPTION index 3d527506e..20f8c68e8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,4 +22,4 @@ Suggests: Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi::api_roclet")) RoxygenNote: 6.0.1 Remotes: - gaborcsardi/crayon + r-lib/crayon diff --git a/NAMESPACE b/NAMESPACE index a736d61af..87862a47f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,23 +1,16 @@ # Generated by roxygen2: do not edit by hand -S3method(cf_data,Date) -S3method(cf_data,POSIXt) -S3method(cf_data,character) -S3method(cf_data,default) -S3method(cf_data,list) -S3method(cf_data,logical) -S3method(cf_data,numeric) -S3method(extra_cols,mcf_squeezed) -S3method(format,cf_data) -S3method(format,cf_title) -S3method(format,cf_type) +S3method(extra_cols,squeezed_colonnade) S3method(format,colonnade) -S3method(format,decimal_format) -S3method(format,mcf_squeezed) S3method(format,pillar) +S3method(format,pillar_decimal) +S3method(format,pillar_shaft) +S3method(format,pillar_title) +S3method(format,pillar_type) S3method(format,rif_data) S3method(format,rif_title) S3method(format,rif_type) +S3method(format,squeezed_colonnade) S3method(is_vector_s3,Date) S3method(is_vector_s3,POSIXct) S3method(is_vector_s3,data.frame) @@ -28,14 +21,20 @@ S3method(is_vector_s3,ordered) S3method(obj_sum,POSIXlt) S3method(obj_sum,default) S3method(obj_sum,list) -S3method(print,cf_data) -S3method(print,cf_vertical) +S3method(pillar_shaft,Date) +S3method(pillar_shaft,POSIXt) +S3method(pillar_shaft,character) +S3method(pillar_shaft,default) +S3method(pillar_shaft,list) +S3method(pillar_shaft,logical) +S3method(pillar_shaft,numeric) S3method(print,colonnade) -S3method(print,decimal_format) -S3method(print,mcf_squeezed) S3method(print,pillar) +S3method(print,pillar_shaft) +S3method(print,pillar_vertical) S3method(print,rif_data) S3method(print,spark) +S3method(print,squeezed_colonnade) S3method(type_sum,Date) S3method(type_sum,POSIXct) S3method(type_sum,data.frame) @@ -44,17 +43,20 @@ S3method(type_sum,difftime) S3method(type_sum,factor) S3method(type_sum,ordered) S3method(type_sum,tbl_df) -export(cf_data) export(colonnade) export(dim_desc) +export(expect_pillar_output) export(extra_cols) -export(format_decimal) -export(format_scientific) export(is_vector_s3) +export(new_pillar_shaft) export(obj_sum) export(pillar) +export(pillar_shaft) export(spark_bar) export(spark_line) export(squeeze) +export(style_neg) +export(style_num) +export(style_subtle) export(type_sum) import(rlang) diff --git a/R/col-data.R b/R/col-data.R index d7624f22d..b8713e4b8 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -3,14 +3,14 @@ #' Internal class for formatting the data part of a column. #' #' @param x A vector to format -#' @param ... Other arguments passed to methods +#' @param ... Unused, for extensibility. #' @export -cf_data <- function(x, ...) { - UseMethod("cf_data") +pillar_shaft <- function(x, ...) { + UseMethod("pillar_shaft") } #' @export -format.cf_data <- function(x, width, ...) { +format.pillar_shaft <- function(x, width, ...) { align <- attr(x, "align") desired_width <- get_width(x) if (width < desired_width) { @@ -18,21 +18,31 @@ format.cf_data <- function(x, width, ...) { } else { data <- x } + data[is.na(x)] <- paste0(strrep(" ", attr(x, "na_indent")), pillar_na()) new_column(data, width = width, align = align) } #' @export -print.cf_data <- function(x, ...) { +print.pillar_shaft <- function(x, ...) { print(format(x, ...)) } -new_cf_data <- function(x, width = max(crayon::col_nchar(x, type = "width"), 0L), - align = "left", min_width = NULL) { +#' @export +#' @param width The maximum column width. +#' @param align Alignment of the column. +#' @param min_width The minimum allowed column width, `width` if omitted. +#' @param na_indent Indention of `NA` values. +#' @rdname pillar_shaft +new_pillar_shaft <- function(x, ..., + width = max(crayon::col_nchar(x, type = "width"), 0L), + align = "left", min_width = NULL, + na_indent = 0L) { ret <- structure( x, align = align, - class = "cf_data" + na_indent = na_indent, + class = "pillar_shaft" ) ret <- set_width(ret, width) ret <- set_min_width(ret, min_width) @@ -42,28 +52,27 @@ new_cf_data <- function(x, width = max(crayon::col_nchar(x, type = "width"), 0L) # Methods ----------------------------------------------------------------- #' @export -#' @rdname cf_data -cf_data.logical <- function(x, ...) { - out <- character(length(x)) +#' @rdname pillar_shaft +pillar_shaft.logical <- function(x, ...) { + out <- rep(NA, length(x)) out[x] <- style_accent("T") out[!x] <- style_subtle("F") - out[is.na(x)] <- cf_na() - new_cf_data(out, width = 1, align = "left") + new_pillar_shaft(out, width = 1, align = "left") } #' @export -#' @rdname cf_data +#' @rdname pillar_shaft #' @param sigfig Minimum number of significant figures to display. Numbers #' larger than 1 will potentially show more signficiant figures than this #' but they will be greyed out. -cf_data.numeric <- function(x, ..., sigfig = 3) { +pillar_shaft.numeric <- function(x, ..., sigfig = 3) { dec <- format_decimal(x, ..., sigfig = sigfig) sci <- format_scientific(x, ..., sigfig = sigfig) ret <- structure( list(dec = dec, sci = sci), - class = "decimal_format" + class = c("pillar_decimal", "pillar_shaft") ) ret <- set_width(ret, get_width(ret$dec)) @@ -72,51 +81,50 @@ cf_data.numeric <- function(x, ..., sigfig = 3) { } #' @export -#' @rdname cf_data -cf_data.Date <- function(x, ...) { +#' @rdname pillar_shaft +pillar_shaft.Date <- function(x, ...) { x <- format(x, format = "%Y-%m-%d") - x[is.na(x)] <- cf_na() - new_cf_data(x, width = 10, align = "left") + new_pillar_shaft(x, width = 10, align = "left") } #' @export -#' @rdname cf_data -cf_data.POSIXt <- function(x, ...) { +#' @rdname pillar_shaft +pillar_shaft.POSIXt <- function(x, ...) { date <- format(x, format = "%Y-%m-%d") time <- format(x, format = "%H:%M:%S") datetime <- paste0(date, " " , style_subtle(time)) - datetime[is.na(x)] <- cf_na() + datetime[is.na(x)] <- NA - new_cf_data(datetime, width = 19, align = "left") + new_pillar_shaft(datetime, width = 19, align = "left") } #' @export -#' @rdname cf_data -cf_data.character <- function(x, ...) { +#' @rdname pillar_shaft +pillar_shaft.character <- function(x, ...) { out <- x needs_quotes <- which(!is_proper_string(x)) is_na <- is.na(x) quoted <- encodeString(x[needs_quotes], quote = '"', na.encode = FALSE) quoted <- gsub('^"|"$', style_subtle('"'), quoted) out[needs_quotes] <- quoted - out[is_na] <- cf_na(use_brackets_if_no_color = TRUE) + out[is_na] <- pillar_na(use_brackets_if_no_color = TRUE) width <- max(crayon::col_nchar(out, type = "width"), 0L) - new_cf_data(out, width = width, align = "left", min_width = min(width, 3L)) + new_pillar_shaft(out, width = width, align = "left", min_width = min(width, 3L)) } #' @export -#' @rdname cf_data -cf_data.list <- function(x, ...) { +#' @rdname pillar_shaft +pillar_shaft.list <- function(x, ...) { out <- paste0("<", obj_sum(x), ">") width <- max(nchar(out, type = "width")) - new_cf_data(style_list(out), width = width, align = "left", min_width = min(width, 3L)) + new_pillar_shaft(style_list(out), width = width, align = "left", min_width = min(width, 3L)) } style_list <- function(x) { @@ -124,7 +132,7 @@ style_list <- function(x) { } #' @export -#' @rdname cf_data -cf_data.default <- function(x, ...) { - cf_data(as.character(x), ...) +#' @rdname pillar_shaft +pillar_shaft.default <- function(x, ...) { + pillar_shaft(as.character(x), ...) } diff --git a/R/col-title.R b/R/col-title.R index 3b8ab5b06..623c3aae0 100644 --- a/R/col-title.R +++ b/R/col-title.R @@ -1,4 +1,4 @@ -cf_title <- function(title, ...) { +pillar_title <- function(title, ...) { if (is.null(title)) { width <- 0L } else { @@ -9,7 +9,7 @@ cf_title <- function(title, ...) { list( title = title ), - class = "cf_title" + class = "pillar_title" ) ret <- set_width(ret, width) @@ -18,7 +18,7 @@ cf_title <- function(title, ...) { } #' @export -format.cf_title <- function(x, width, ...) { +format.pillar_title <- function(x, width, ...) { title <- x$title if (is.null(title)) return(character()) diff --git a/R/col-type.R b/R/col-type.R index 1be780550..414098b8f 100644 --- a/R/col-type.R +++ b/R/col-type.R @@ -6,13 +6,13 @@ style_type <- function(x) { style_subtle(x) } -cf_type <- function(x, ...) { +pillar_type <- function(x, ...) { type <- type_sum(x) ret <- structure( list( type = type ), - class = "cf_type" + class = "pillar_type" ) ret <- set_width(ret, width = nchar(type, type = "width") + 2L) ret <- set_min_width(ret, 5L) @@ -20,7 +20,7 @@ cf_type <- function(x, ...) { } #' @export -format.cf_type <- function(x, width = NULL, ...) { +format.pillar_type <- function(x, width = NULL, ...) { if (is.null(width) || width >= get_width(x)) type <- x$type else type <- crayon::col_substr(x$type, 1, width - 2) # Style is applied later on because we want a continuous underline over the diff --git a/R/colformat.R b/R/colformat.R index 388389cd4..ebcc4cfef 100644 --- a/R/colformat.R +++ b/R/colformat.R @@ -34,9 +34,9 @@ #' pillar(date + c(1, NA, 3:5)) #' pillar(as.POSIXct(date) + c(30, NA, 600, 3600, 86400)) pillar <- function(x, title = NULL, width = NULL, ...) { - title <- cf_title(title, ...) - type <- cf_type(x, ...) - data <- cf_data(x, ...) + title <- pillar_title(title, ...) + type <- pillar_type(x, ...) + data <- pillar_shaft(x, ...) ret <- structure( list(title = title, type = type, data = data), class = "pillar" @@ -58,12 +58,11 @@ rowidformat <- function(n, has_title_row = FALSE, has_star = FALSE, ...) { #' @export format.pillar <- function(x, width = NULL, ...) { - width <- cf_get_width(x, width) - out <- cf_format_parts(x, width) + width <- pillar_get_width(x, width) + out <- pillar_format_parts(x, width) - cf_data <- c(out$title_format, style_type_header(out$type_format), out$data_format) - - new_vertical(cf_data) + fmt <- c(out$title_format, style_type_header(out$type_format), out$data_format) + new_vertical(fmt) } #' @export @@ -71,7 +70,7 @@ print.pillar <- function(x, ...) { print(format(x, ...)) } -cf_get_width <- function(x, width) { +pillar_get_width <- function(x, width) { if (is.null(width)) { width <- get_width(x) } @@ -87,7 +86,7 @@ cf_get_width <- function(x, width) { width } -cf_format_parts <- function(x, width, ...) { +pillar_format_parts <- function(x, width, ...) { title_format <- format(x$title, width = width, ...) type_format <- format(x$type, width = width, ...) data_format <- format(x$data, width = width, ...) @@ -104,7 +103,7 @@ cf_format_parts <- function(x, width, ...) { ) } -cf_format_abbrev <- function(x, ...) { +pillar_format_abbrev <- function(x, ...) { title_format <- format(x$title, width = Inf, ...) type_format <- style_type(format(x$type, width = Inf, ...)) paste0(title_format, "\u00a0", type_format) diff --git a/R/column.R b/R/column.R index 45a89b4db..bf93b5c91 100644 --- a/R/column.R +++ b/R/column.R @@ -2,7 +2,7 @@ new_column <- function(row, width = NULL, align = NULL) { ret <- structure( row, align = align, - class = c("cf_column", "cf_vertical") + class = c("pillar_column", "pillar_vertical") ) ret <- set_width(ret, width) ret @@ -12,13 +12,13 @@ new_vertical <- function(row, ..., extra_class = NULL) { ret <- structure( row, ..., - class = c(extra_class, "cf_vertical") + class = c(extra_class, "pillar_vertical") ) ret } #' @export -print.cf_vertical <- function(x, ...) { +print.pillar_vertical <- function(x, ...) { if (length(x) > 0) { cat_line(paste(x, collapse = "\n")) } diff --git a/R/multi.R b/R/multi.R index 37694b584..3fabffa5d 100644 --- a/R/multi.R +++ b/R/multi.R @@ -40,7 +40,7 @@ colonnade <- function(x, has_row_id = TRUE, width = NULL, ...) { #' @export squeeze <- function(x, width = NULL, ...) { # Hacky shortcut for zero-height corner case - if (attr(x, "zero_height")) return(new_mcf_sqeezed(character(), x[names2(x) != ""])) + if (attr(x, "zero_height")) return(new_colonnade_sqeezed(character(), x[names2(x) != ""])) if (is.null(width)) { width <- get_width(x) @@ -50,22 +50,22 @@ squeeze <- function(x, width = NULL, ...) { width <- getOption("width") } - col_widths <- mcf_get_width(x, width) - out <- map2(x[seq_along(col_widths)], col_widths, cf_format_parts) + col_widths <- colonnade_get_width(x, width) + out <- map2(x[seq_along(col_widths)], col_widths, pillar_format_parts) - new_mcf_sqeezed(out, x[seq2_along(length(col_widths) + 1L, x)]) + new_colonnade_sqeezed(out, x[seq2_along(length(col_widths) + 1L, x)]) } -new_mcf_sqeezed <- function(x, extra_cols) { +new_colonnade_sqeezed <- function(x, extra_cols) { structure( x, - extra_cols = map_chr(extra_cols, cf_format_abbrev), - class = "mcf_squeezed" + extra_cols = map_chr(extra_cols, pillar_format_abbrev), + class = "squeezed_colonnade" ) } #' @export -format.mcf_squeezed <- function(x, ...) { +format.squeezed_colonnade <- function(x, ...) { xt <- list( title = map(x, `[[`, "title_format"), type = map(x, `[[`, "type_format"), @@ -82,13 +82,13 @@ format.mcf_squeezed <- function(x, ...) { } #' @export -print.mcf_squeezed <- function(x, ...) { +print.squeezed_colonnade <- function(x, ...) { print(format(x, ...), ...) invisible(x) } # Method registration happens in .onLoad() -knit_print.mcf_squeezed <- function(x, ...) { +knit_print.squeezed_colonnade <- function(x, ...) { header <- map_chr(x, `[[`, "title_format") col <- map(x, function(xx) c(xx[["type_format"]], xx[["data_format"]])) @@ -108,7 +108,7 @@ extra_cols <- function(x, ...) { } #' @export -extra_cols.mcf_squeezed <- function(x, ...) { +extra_cols.squeezed_colonnade <- function(x, ...) { attr(x, "extra_cols") } @@ -125,19 +125,19 @@ print.colonnade <- function(x, ...) { #' @rdname colonnade #' @usage NULL #' @aliases NULL -mcf_get_width <- function(x, width) { +colonnade_get_width <- function(x, width) { max_widths <- map_int(map(x, get_widths), max) min_widths <- map_int(map(x, get_min_widths), max) #' @details #' In a first pass, for each column it is decided if it is hidden, shown with #' its minimum width or shown with its maximum width. - col_widths <- mcf_compute_col_widths(min_widths, max_widths, width) + col_widths <- colonnade_compute_col_widths(min_widths, max_widths, width) #' Remaining space is then distributed proportionally to columns that do not #' use their desired width. max_widths <- max_widths[seq_along(col_widths)] - added_space <- mcf_distribute_space(col_widths, max_widths, width) + added_space <- colonnade_distribute_space(col_widths, max_widths, width) col_widths + added_space } @@ -145,7 +145,7 @@ mcf_get_width <- function(x, width) { #' @rdname colonnade #' @usage NULL #' @aliases NULL -mcf_compute_col_widths <- function(min_widths, max_widths, width) { +colonnade_compute_col_widths <- function(min_widths, max_widths, width) { #' @details #' For computing the column widths, two cases are distinguished: #' 1. When taking the minimum width for each column (plus one inter-column @@ -173,7 +173,7 @@ mcf_compute_col_widths <- function(min_widths, max_widths, width) { #' @rdname colonnade #' @usage NULL #' @aliases NULL -mcf_distribute_space <- function(col_widths, max_widths, width) { +colonnade_distribute_space <- function(col_widths, max_widths, width) { missing_space <- max_widths - col_widths # Shortcut to avoid division by zero if (all(missing_space == 0L)) return(rep_along(col_widths, 0L)) diff --git a/R/scientific.R b/R/scientific.R index 9a065d494..6cfa99365 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -5,10 +5,6 @@ #' #' @inheritParams format_decimal #' @param superscript If `TRUE`, will use superscript numbers in exponent. -#' @export -#' @examples -#' x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) -#' format_scientific(x) format_scientific <- function(x, sigfig = 3, superscript = TRUE) { split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) } diff --git a/R/sigfig.R b/R/sigfig.R index 5f71aadef..b6d3e94c4 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -14,16 +14,6 @@ #' @param x A numeric vector #' @param sigfig Number of signficiant figures to display. #' @param ... Ignored -#' @export -#' @examples -#' x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) -#' format_decimal(x, 3) -#' -#' x <- x * sample(c(-1, 1), length(x), rep = TRUE) -#' format_decimal(x, 3) -#' -#' format_decimal(c(Inf, -Inf, NA, NaN), 3) -#' format_decimal(c(1e10, 1e-10), 3) format_decimal <- function(x, sigfig = 3, ...) { split_decimal(x, sigfig) } @@ -121,7 +111,7 @@ format_lhs <- function(s) { # as.character() to support corner case of length zero lhs_col <- as.character(ifelse(num, paste0( - style_num(lhs_sig, neg, lhs_zero), + style_num(lhs_sig, neg, !lhs_zero), style_subtle(lhs_non) ), style_na(lhs_str) @@ -138,7 +128,7 @@ format_dec <- function(s) { # Decimal column if (any(dec)) { - dec_col <- ifelse(dec, style_num(".", neg, lhs_zero), " ") + dec_col <- ifelse(dec, style_num(".", neg, !lhs_zero), " ") } else { dec_col <- rep_along(neg, "") } @@ -159,7 +149,7 @@ format_rhs <- function(s) { rhs_col <- ifelse(dec, paste0( - style_num(rhs_zero, neg, lhs_zero), + style_num(rhs_zero, neg, !lhs_zero), style_num(rhs_num, neg) ), "" @@ -171,8 +161,12 @@ format_rhs <- function(s) { rhs_col } -style_num <- function(x, negative, subtle = rep_along(x, FALSE)) { - ifelse(subtle, style_subtle(x), ifelse(negative, style_neg(x), x)) +#' @export +#' @param negative,significant Logical vector the same length as `x` that +#' indicate if the values are negative and significant, respectively +#' @rdname style_subtle +style_num <- function(x, negative, significant = rep_along(x, TRUE)) { + ifelse(significant, ifelse(negative, style_neg(x), x), style_subtle(x)) } assemble_decimal <- function(x) { @@ -186,7 +180,7 @@ assemble_decimal <- function(x) { } #' @export -format.decimal_format <- function(x, width, ...) { +format.pillar_decimal <- function(x, width, ...) { if (length(x$dec$num) == 0L) return(character()) if (width < get_min_width(x)) { @@ -206,8 +200,3 @@ format.decimal_format <- function(x, width, ...) { row <- paste0(strrep(" ", width - used_width), row) new_column(row, width = width, align = "right") } - -#' @export -print.decimal_format <- function(x, ...) { - print(format(x, ...)) -} diff --git a/R/styles.R b/R/styles.R index cac834044..36e39b036 100644 --- a/R/styles.R +++ b/R/styles.R @@ -11,6 +11,13 @@ style_accent <- keep_empty(function(x) { crayon::green(x) }) +#' Styling helpers +#' +#' Functions that allow implementers of formatters for custom data types to +#' maintain a consistent style with the default data types. +#' +#' @param x The character vector to style. +#' @export style_subtle <- keep_empty(function(x) { style_grey(0.6, x) }) @@ -23,6 +30,8 @@ style_na <- function(x) { crayon::bgYellow(crayon::black(x)) } +#' @export +#' @rdname style_subtle style_neg <- keep_empty(function(x) { crayon::red(x) }) @@ -35,7 +44,7 @@ style_grey <- function(level, ...) { ) } -cf_na <- function(use_brackets_if_no_color = FALSE) { +pillar_na <- function(use_brackets_if_no_color = FALSE) { if (use_brackets_if_no_color && !crayon::has_color()) "" else style_na("NA") } diff --git a/R/testthat.R b/R/testthat.R new file mode 100644 index 000000000..51cf85673 --- /dev/null +++ b/R/testthat.R @@ -0,0 +1,42 @@ +#' Test helpers +#' +#' Helper functions for packages that implement their own pillar. +#' `expect_pillar_output()` is an expectation that allows storing the +#' desired result in a file, and comparing the output with the file contents. +#' +#' @param x An object to be formatted. +#' @param ... Passed on to [pillar()] if `xf` is left at its default. +#' @param filename File name that contains the desired output. +#' @param xp Pass a value here instead of `x` if you want to omit appending +#' `NA` and `Inf` values. +#' @param xf Pass the result of a [pillar()] call here for full control. +#' @param crayon Color the output? +#' @export +expect_pillar_output <- function(x, ..., filename, + xp = add_special(x), xf = pillar(xp, ...), + crayon = TRUE) { + if (crayon) { + old <- options(crayon.enabled = TRUE, crayon.colors = 16L) + crayon::num_colors(forget = TRUE) + } else { + old <- options(crayon.enabled = FALSE) + } + + on.exit({ + options(old) + crayon::num_colors(forget = TRUE) + }) + + testthat::expect_output_file(print(xf), file.path("out", filename), update = TRUE) +} + +#' `add_special()` is not exported, and used only for initializing default +#' values to `expect_pillar_output()`. +#' @rdname expect_pillar_output +add_special <- function(x) { + x <- c(x, NA) + if (is.numeric(x) && is.double(x)) { + x <- c(x, -Inf, Inf) + } + x +} diff --git a/R/utils.R b/R/utils.R index 339c6dd2d..e78726ff0 100644 --- a/R/utils.R +++ b/R/utils.R @@ -2,7 +2,7 @@ cat_line <- function(...) { cat(..., "\n", sep = "") } -cf_align <- function(x, width, align) { +pillar_align <- function(x, width, align) { vapply(x, crayon::col_align, width = width, align = align, FUN.VALUE = character(1)) } @@ -10,7 +10,7 @@ cf_align <- function(x, width, align) { str_trunc <- function(x, width) { if (width == Inf) return(x) - str_width <- nchar(x, type = "width") + str_width <- crayon::col_nchar(x, type = "width") too_wide <- !is.na(x) & str_width > width x[too_wide] <- paste0(crayon::col_substr(x[too_wide], 1, width - 1), "\u2026") diff --git a/R/zzz.R b/R/zzz.R index 4b6e2c37c..57b1efd29 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -6,7 +6,7 @@ NULL } .onLoad <- function(libname, pkgname) { - register_s3_method("knitr", "knit_print", "mcf_squeezed") + register_s3_method("knitr", "knit_print", "squeezed_colonnade") invisible() } diff --git a/man/cf_data.Rd b/man/cf_data.Rd deleted file mode 100644 index d2e451800..000000000 --- a/man/cf_data.Rd +++ /dev/null @@ -1,41 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/col-data.R -\name{cf_data} -\alias{cf_data} -\alias{cf_data.logical} -\alias{cf_data.numeric} -\alias{cf_data.Date} -\alias{cf_data.POSIXt} -\alias{cf_data.character} -\alias{cf_data.list} -\alias{cf_data.default} -\title{Column data} -\usage{ -cf_data(x, ...) - -\method{cf_data}{logical}(x, ...) - -\method{cf_data}{numeric}(x, ..., sigfig = 3) - -\method{cf_data}{Date}(x, ...) - -\method{cf_data}{POSIXt}(x, ...) - -\method{cf_data}{character}(x, ...) - -\method{cf_data}{list}(x, ...) - -\method{cf_data}{default}(x, ...) -} -\arguments{ -\item{x}{A vector to format} - -\item{...}{Other arguments passed to methods} - -\item{sigfig}{Minimum number of significant figures to display. Numbers -larger than 1 will potentially show more signficiant figures than this -but they will be greyed out.} -} -\description{ -Internal class for formatting the data part of a column. -} diff --git a/man/expect_pillar_output.Rd b/man/expect_pillar_output.Rd new file mode 100644 index 000000000..47c6413aa --- /dev/null +++ b/man/expect_pillar_output.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/testthat.R +\name{expect_pillar_output} +\alias{expect_pillar_output} +\alias{add_special} +\title{Test helpers} +\usage{ +expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, + ...), crayon = TRUE) + +add_special(x) +} +\arguments{ +\item{x}{An object to be formatted.} + +\item{...}{Passed on to \code{\link[=pillar]{pillar()}} if \code{xf} is left at its default.} + +\item{filename}{File name that contains the desired output.} + +\item{xp}{Pass a value here instead of \code{x} if you want to omit appending +\code{NA} and \code{Inf} values.} + +\item{xf}{Pass the result of a \code{\link[=pillar]{pillar()}} call here for full control.} + +\item{crayon}{Color the output?} +} +\description{ +Helper functions for packages that implement their own pillar. +\code{expect_pillar_output()} is an expectation that allows storing the +desired result in a file, and comparing the output with the file contents. + +\code{add_special()} is not exported, and used only for initializing default +values to \code{expect_pillar_output()}. +} diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd index 1da0fb25f..86c9cfd1b 100644 --- a/man/format_decimal.Rd +++ b/man/format_decimal.Rd @@ -28,13 +28,3 @@ compare columns of numbers. Significant digits are coloured black or red (for positive and negative numbers) and non-significant digits are coloured in paler gray. } -\examples{ -x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10, -15)) -format_decimal(x, 3) - -x <- x * sample(c(-1, 1), length(x), rep = TRUE) -format_decimal(x, 3) - -format_decimal(c(Inf, -Inf, NA, NaN), 3) -format_decimal(c(1e10, 1e-10), 3) -} diff --git a/man/format_scientific.Rd b/man/format_scientific.Rd index 1bcd3c54f..dd0496c63 100644 --- a/man/format_scientific.Rd +++ b/man/format_scientific.Rd @@ -17,7 +17,3 @@ format_scientific(x, sigfig = 3, superscript = TRUE) Uses colour, careful alignment, and superscripts to display numbers in scientific notation. } -\examples{ -x <- c(runif(10) * 10 ^ (sample(-100:100, 5)), NA, Inf, NaN) -format_scientific(x) -} diff --git a/man/pillar_shaft.Rd b/man/pillar_shaft.Rd new file mode 100644 index 000000000..6459be894 --- /dev/null +++ b/man/pillar_shaft.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/col-data.R +\name{pillar_shaft} +\alias{pillar_shaft} +\alias{new_pillar_shaft} +\alias{pillar_shaft.logical} +\alias{pillar_shaft.numeric} +\alias{pillar_shaft.Date} +\alias{pillar_shaft.POSIXt} +\alias{pillar_shaft.character} +\alias{pillar_shaft.list} +\alias{pillar_shaft.default} +\title{Column data} +\usage{ +pillar_shaft(x, ...) + +new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), + 0L), align = "left", min_width = NULL, na_indent = 0L) + +\method{pillar_shaft}{logical}(x, ...) + +\method{pillar_shaft}{numeric}(x, ..., sigfig = 3) + +\method{pillar_shaft}{Date}(x, ...) + +\method{pillar_shaft}{POSIXt}(x, ...) + +\method{pillar_shaft}{character}(x, ...) + +\method{pillar_shaft}{list}(x, ...) + +\method{pillar_shaft}{default}(x, ...) +} +\arguments{ +\item{x}{A vector to format} + +\item{...}{Unused, for extensibility.} + +\item{width}{The maximum column width.} + +\item{align}{Alignment of the column.} + +\item{min_width}{The minimum allowed column width, \code{width} if omitted.} + +\item{na_indent}{Indention of \code{NA} values.} + +\item{sigfig}{Minimum number of significant figures to display. Numbers +larger than 1 will potentially show more signficiant figures than this +but they will be greyed out.} +} +\description{ +Internal class for formatting the data part of a column. +} diff --git a/man/style_subtle.Rd b/man/style_subtle.Rd new file mode 100644 index 000000000..39759abf4 --- /dev/null +++ b/man/style_subtle.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sigfig.R, R/styles.R +\name{style_num} +\alias{style_num} +\alias{style_subtle} +\alias{style_neg} +\title{Styling helpers} +\usage{ +style_num(x, negative, significant = rep_along(x, TRUE)) + +style_subtle(x) + +style_neg(x) +} +\arguments{ +\item{x}{The character vector to style.} + +\item{negative, significant}{Logical vector the same length as \code{x} that +indicate if the values are negative and significant, respectively} +} +\description{ +Functions that allow implementers of formatters for custom data types to +maintain a consistent style with the default data types. +} diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 954b4feb6..5dda8a9e6 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -1,29 +1,3 @@ -expect_pillar_output <- function(x, ..., filename, - xp = add_special(x), xf = pillar(xp, ...), - crayon = TRUE) { - if (crayon) { - old <- options(crayon.enabled = TRUE, crayon.colors = 16L) - crayon::num_colors(forget = TRUE) - } else { - old <- options(crayon.enabled = FALSE) - } - - on.exit({ - options(old) - crayon::num_colors(forget = TRUE) - }) - - expect_output_file(print(xf), file.path("out", filename), update = TRUE) -} - -add_special <- function(x) { - x <- c(x, NA) - if (is.numeric(x) && is.double(x)) { - x <- c(x, -Inf, Inf) - } - x -} - show_output_in_terminal <- function() { system2("xterm", c("-e", shQuote("head tests/testthat/out/*; sleep 600"))) } diff --git a/tests/testthat/out/letters-long-03.txt b/tests/testthat/out/letters-long-03.txt index 306a1a690..3f9aa0c9b 100644 --- a/tests/testthat/out/letters-long-03.txt +++ b/tests/testthat/out/letters-long-03.txt @@ -1,3 +1,3 @@  abcd… -NA… +NA diff --git a/tests/testthat/out/letters-long-10.txt b/tests/testthat/out/letters-long-10.txt index ad761c405..88aa5e18e 100644 --- a/tests/testthat/out/letters-long-10.txt +++ b/tests/testthat/out/letters-long-10.txt @@ -1,3 +1,3 @@   abcdefghi… -NA… +NA diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 2e8264bf6..9f76655ad 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -91,10 +91,10 @@ test_that("tests from tibble", { test_that("empty", { expect_equal( format(colonnade(list(a = character(), b = logical()), width = 30)), - structure(character(), class = "cf_vertical") + structure(character(), class = "pillar_vertical") ) expect_equal( format(colonnade(iris[1:5, character()], width = 30)), - structure(character(), class = "cf_vertical") + structure(character(), class = "pillar_vertical") ) }) diff --git a/tests/testthat/test-format_scientific.R b/tests/testthat/test-format_scientific.R index 584265385..c44eee746 100644 --- a/tests/testthat/test-format_scientific.R +++ b/tests/testthat/test-format_scientific.R @@ -4,7 +4,7 @@ format_scientific_bw <- function(x, ...) { old <- options(crayon.enabled = FALSE) on.exit(options(old)) - ret <- cf_data(x, ...) + ret <- pillar_shaft(x, ...) # Hack: Pretend decimal format requires 100 characters ret$dec <- set_width(ret$dec, 100) ret <- set_width(ret, 100) From 85a7c6b5947d7834bb5769214ad79d8c1c4cc1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 15 Sep 2017 21:46:54 +0200 Subject: [PATCH 061/133] up README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0314d550b..e5e80fbb1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ pillar -========= +====== [![Travis-CI Build Status](https://travis-ci.org/hadley/pillar.svg?branch=master)](https://travis-ci.org/hadley/pillar) @@ -25,7 +25,7 @@ library(pillar) x <- 123456789 * (10 ^ c(1, -3, -5, NA, -8, -10)) pillar(x) -#> title +#> #> 1234567890 #> 123457 #> 1235 From 5b792f25b0fd56ad64d40a84268cc8a5e42409d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 15 Sep 2017 22:13:53 +0200 Subject: [PATCH 062/133] API description @hadley: I don't really like it yet, seems to complicated at this point. --- README.Rmd | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/README.Rmd b/README.Rmd index dade53b59..1825bcd5f 100644 --- a/README.Rmd +++ b/README.Rmd @@ -45,6 +45,53 @@ knitr::include_graphics("man/figures/colours.png") ``` +## Extending + +The primary user of this package is [tibble](https://github.com/tidyverse/tibble), which in the current development version already lets pillar do all the formatting work. Packages that implement a data type to be used in a tibble column can add color with only a few changes: + +1. Implement the `pillar_shaft()` method for your data type. +1. Add pillar to `Suggests` and implement dynamic method registration + - If you don't mind the dependency, you can also add it to `Imports`, and import the methods you override with a regular `NAMESPACE` import. + +[tidyverse/hms#43](https://github.com/tidyverse/hms/pull/43) shows the changes that were necessary to add colored output for the hms package: + +- [`pillar.R`](https://github.com/tidyverse/hms/pull/43/files#diff-a63dd6b1da682a8549d03475ac91cdcf) for the actual implementation (old name `colformat.R`) +- [`DESCRIPTION`](https://github.com/tidyverse/hms/pull/43/files#diff-35ba4a2677442e210c23a00a5601aba3) for the dependency +- [`zzz.R`](https://github.com/tidyverse/hms/pull/43/files#diff-e549505eb95036528ca3b125f62915a6) for the dynamic method registration + +Some more detail is given below. + +### Implementing `pillar_shaft.your_class_name()` + +This method accepts a vector of arbitrary length and is expected to return an S3 object with the following properties: + +- It has an attribute `"width"` +- It can have an attribute `"min_width"`, if missing, `"width"` is used +- It must implement a method `format(x, width, ...)` that can be called with any value between `min_width` and `width` + - This method must return an object that inherits from `character` and has attributes `"align"` (with supported values `"left"`, `"right"`, and `"center"`) and `"width"` + +The function `new_pillar_shaft()` returns such an object, and also correctly formats `NA` values. In many cases, the implementation of `pillar_shaft.your_class_name()` will format the data as a character vector (using color for emphasis) and simply call `new_pillar_shaft()`. See `pillar_shaft.numeric()` for a code that allows changing the display depending on the available width. + +### Useful helpers + +- `style_neg()` to format negative values +- `style_num()` to format numbers +- `style_subtle()` to de-emphasize + + +### Dynamic method registration + +If you avoid the strong dependency on pillar, you need a helper, `register_s3_method()`, which you can borrow e.g. from hms. In `.onLoad()`, call this helper as follows: + +```r +register_s3_method("pillar", "pillar_shaft", "your_class_name") +``` + +Replace `"your_class_name"` with the name of the S3 class of your data type. + + + + ## Inspirations * [TextPlots](https://github.com/sunetos/TextPlots.jl) for use of Braille diff --git a/README.md b/README.md index e5e80fbb1..8ca4b43d8 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,50 @@ If you render this in a console that supports colour, you'll see something that +Extending +--------- + +The primary user of this package is [tibble](https://github.com/tidyverse/tibble), which in the current development version already lets pillar do all the formatting work. Packages that implement a data type to be used in a tibble column can add color with only a few changes: + +1. Implement the `pillar_shaft()` method for your data type. +2. Add pillar to `Suggests` and implement dynamic method registration + - If you don't mind the dependency, you can also add it to `Imports`, and import the methods you override with a regular `NAMESPACE` import. + +[tidyverse/hms\#43](https://github.com/tidyverse/hms/pull/43) shows the changes that were necessary to add colored output for the hms package: + +- [`pillar.R`](https://github.com/tidyverse/hms/pull/43/files#diff-a63dd6b1da682a8549d03475ac91cdcf) for the actual implementation (old name `colformat.R`) +- [`DESCRIPTION`](https://github.com/tidyverse/hms/pull/43/files#diff-35ba4a2677442e210c23a00a5601aba3) for the dependency +- [`zzz.R`](https://github.com/tidyverse/hms/pull/43/files#diff-e549505eb95036528ca3b125f62915a6) for the dynamic method registration + +Some more detail is given below. + +### Implementing `pillar_shaft.your_class_name()` + +This method accepts a vector of arbitrary length and is expected to return an S3 object with the following properties: + +- It has an attribute `"width"` +- It can have an attribute `"min_width"`, if missing, `"width"` is used +- It must implement a method `format(x, width, ...)` that can be called with any value between `min_width` and `width` + - This method must return an object that inherits from `character` and has attributes `"align"` (with supported values `"left"`, `"right"`, and `"center"`) and `"width"` + +The function `new_pillar_shaft()` returns such an object, and also correctly formats `NA` values. In many cases, the implementation of `pillar_shaft.your_class_name()` will format the data as a character vector (using color for emphasis) and simply call `new_pillar_shaft()`. See `pillar_shaft.numeric()` for a code that allows changing the display depending on the available width. + +### Useful helpers + +- `style_neg()` to format negative values +- `style_num()` to format numbers +- `style_subtle()` to de-emphasize + +### Dynamic method registration + +If you avoid the strong dependency on pillar, you need a helper, `register_s3_method()`, which you can borrow e.g. from hms. In `.onLoad()`, call this helper as follows: + +``` r +register_s3_method("pillar", "pillar_shaft", "your_class_name") +``` + +Replace `"your_class_name"` with the name of the S3 class of your data type. + Inspirations ------------ From 71a3d86d27f22c936adc333a532e799f775a8317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 16 Sep 2017 10:11:27 +0200 Subject: [PATCH 063/133] rename, closes #48 --- colformat.Rproj => pillar.Rproj | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename colformat.Rproj => pillar.Rproj (100%) diff --git a/colformat.Rproj b/pillar.Rproj similarity index 100% rename from colformat.Rproj rename to pillar.Rproj From cf70bedc592d405c339bb9bb7aa5bddcbc623e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 16 Sep 2017 14:40:50 +0200 Subject: [PATCH 064/133] Refactorings for supporting multiple chunks (#50) * prepare for output of multiple chunks * use data frame to communicate column widths * separate rowid from the colonnade because it will be repeated for each chunk * use data frames for space distribution * use data frames for width computation * document * space distribution gets access to width of rowid * extract function * operate only on chunk_widths * return zero width for columns that don't fit for simpler computations * chunk -> tier --- R/multi.R | 112 ++++++++++++++++++++----- man/colonnade.Rd | 9 +- tests/testthat/out/multi-04.txt | 5 -- tests/testthat/out/multi-05.txt | 5 -- tests/testthat/out/multi-06.txt | 5 -- tests/testthat/out/multi-07.txt | 5 -- tests/testthat/out/tibble-all--300.txt | 10 +-- 7 files changed, 104 insertions(+), 47 deletions(-) diff --git a/R/multi.R b/R/multi.R index 3fabffa5d..6055320de 100644 --- a/R/multi.R +++ b/R/multi.R @@ -22,10 +22,11 @@ colonnade <- function(x, has_row_id = TRUE, width = NULL, ...) { has_star = identical(has_row_id, "*"), has_title_row = has_title ) - ret <- c(list(rowid), ret) + } else { + rowid <- NULL } zero_height <- length(x) == 0L || length(x[[1]]) == 0L - ret <- structure(ret, zero_height = zero_height, class = "colonnade") + ret <- structure(ret, zero_height = zero_height, rowid = rowid, class = "colonnade") ret <- set_width(ret, width) ret } @@ -40,7 +41,9 @@ colonnade <- function(x, has_row_id = TRUE, width = NULL, ...) { #' @export squeeze <- function(x, width = NULL, ...) { # Hacky shortcut for zero-height corner case - if (attr(x, "zero_height")) return(new_colonnade_sqeezed(character(), x[names2(x) != ""])) + if (attr(x, "zero_height")) { + return(new_colonnade_sqeezed(list(), x)) + } if (is.null(width)) { width <- get_width(x) @@ -50,10 +53,25 @@ squeeze <- function(x, width = NULL, ...) { width <- getOption("width") } - col_widths <- colonnade_get_width(x, width) - out <- map2(x[seq_along(col_widths)], col_widths, pillar_format_parts) + rowid <- attr(x, "rowid") + if (is.null(rowid)) rowid_width <- 0 + else rowid_width <- max(get_widths(rowid)) + 1L + + col_widths <- colonnade_get_width(x, width, rowid_width) + col_widths_show <- split(col_widths, factor(col_widths$tier != 0, levels = c(FALSE, TRUE))) + col_widths_shown <- col_widths_show[["TRUE"]] + col_widths_tiers <- split(col_widths_shown, col_widths_shown$tier) + out <- map(col_widths_tiers, function(tier) { + map2(x[tier$id], tier$width, pillar_format_parts) + }) + + if (!is.null(rowid)) { + rowid_formatted <- pillar_format_parts(rowid, rowid_width - 1L) + out <- map(out, function(x) c(list(rowid_formatted), x)) + } - new_colonnade_sqeezed(out, x[seq2_along(length(col_widths) + 1L, x)]) + col_widths_extra <- col_widths_show[["FALSE"]] + new_colonnade_sqeezed(out, x[col_widths_extra$id]) } new_colonnade_sqeezed <- function(x, extra_cols) { @@ -66,19 +84,22 @@ new_colonnade_sqeezed <- function(x, extra_cols) { #' @export format.squeezed_colonnade <- function(x, ...) { + formatted <- map(x, format_colonnade_tier) + new_vertical(as.character(unlist(formatted))) +} + +format_colonnade_tier <- function(x) { xt <- list( title = map(x, `[[`, "title_format"), type = map(x, `[[`, "type_format"), data = map(x, `[[`, "data_format") ) - formatted <- c( + c( invoke(paste, xt$title), style_type_header(invoke(paste, xt$type)), invoke(paste, xt$data) ) - - new_vertical(formatted) } #' @export @@ -125,21 +146,58 @@ print.colonnade <- function(x, ...) { #' @rdname colonnade #' @usage NULL #' @aliases NULL -colonnade_get_width <- function(x, width) { - max_widths <- map_int(map(x, get_widths), max) - min_widths <- map_int(map(x, get_min_widths), max) +colonnade_get_width <- function(x, width, rowid_width) { + col_df <- data.frame( + id = seq_along(x), + max_widths = map_int(map(x, get_widths), max), + min_widths = map_int(map(x, get_min_widths), max) + ) #' @details - #' In a first pass, for each column it is decided if it is hidden, shown with - #' its minimum width or shown with its maximum width. - col_widths <- colonnade_compute_col_widths(min_widths, max_widths, width) + #' In a first pass, for each pillar it is decided in which tier it is shown, + #' if at all, and how much horizontal space it may use (either its minumum + #' or its maximum width). More than one tier may be created if + #' `width > getOption("width")`, in this case each tier is at most + #' `getOption("width")` characters wide. + tier_widths <- get_tier_widths(width, rowid_width) + col_widths_df <- colonnade_compute_tiered_col_widths_df(col_df, tier_widths) - #' Remaining space is then distributed proportionally to columns that do not + #' Remaining space is then distributed proportionally to pillars that do not #' use their desired width. - max_widths <- max_widths[seq_along(col_widths)] - added_space <- colonnade_distribute_space(col_widths, max_widths, width) + colonnade_distribute_space_df(col_widths_df, tier_widths) +} - col_widths + added_space +get_tier_widths <- function(width, rowid_width, tier_width = getOption("width")) { + pos <- c( + seq(0, width - 1, by = tier_width), + width + ) + diff(pos) - rowid_width +} + +#' @rdname colonnade +#' @usage NULL +#' @aliases NULL +colonnade_compute_tiered_col_widths_df <- function(col_df, tier_widths) { + col_tier_df <- colonnade_compute_col_widths_df(col_df, tier_widths[[1]]) + col_tier_df +} + +#' @rdname colonnade +#' @usage NULL +#' @aliases NULL +colonnade_compute_col_widths_df <- function(col_df, width, tier_id = 1L) { + col_widths <- colonnade_compute_col_widths( + col_df$min_widths, + col_df$max_widths, + width + ) + n_fitting_cols <- length(col_widths) + col_widths <- c(col_widths, rep(0L, nrow(col_df) - length(col_widths))) + + col_df$width <- col_widths + col_df$tier <- ifelse(seq_along(col_widths) > n_fitting_cols, 0L, tier_id) + col_df } #' @rdname colonnade @@ -170,10 +228,26 @@ colonnade_compute_col_widths <- function(min_widths, max_widths, width) { col_widths } +#' @rdname colonnade +#' @usage NULL +#' @aliases NULL +colonnade_distribute_space_df <- function(col_widths_df, tier_widths) { + col_widths_split <- split(col_widths_df, col_widths_df$tier) + if (any(col_widths_df$tier == 0)) tier_widths <- c(NA, tier_widths) + tier_widths <- tier_widths[seq_along(col_widths_split)] + col_widths_apply <- map2(col_widths_split, tier_widths, function(x, width) { + x$width <- x$width + colonnade_distribute_space(x$width, x$max_widths, width) + x + }) + invoke(rbind, unname(col_widths_apply)) +} + #' @rdname colonnade #' @usage NULL #' @aliases NULL colonnade_distribute_space <- function(col_widths, max_widths, width) { + if (any(is.na(col_widths))) return(col_widths) + missing_space <- max_widths - col_widths # Shortcut to avoid division by zero if (all(missing_space == 0L)) return(rep_along(col_widths, 0L)) diff --git a/man/colonnade.Rd b/man/colonnade.Rd index 5c199ad42..ae2937318 100644 --- a/man/colonnade.Rd +++ b/man/colonnade.Rd @@ -29,9 +29,12 @@ It returns an object suitable for printing and formatting at a fixed width with additional information about omitted columns. } \details{ -In a first pass, for each column it is decided if it is hidden, shown with -its minimum width or shown with its maximum width. -Remaining space is then distributed proportionally to columns that do not +In a first pass, for each pillar it is decided in which chunk it is shown, +if at all, and how much horizontal space it may use (either its minumum +or its maximum width). More than one chunk may be created if +\code{width > getOption("width")}, in this case each chunk is at most +\code{getOption("width")} characters wide. +Remaining space is then distributed proportionally to pillars that do not use their desired width. For computing the column widths, two cases are distinguished: diff --git a/tests/testthat/out/multi-04.txt b/tests/testthat/out/multi-04.txt index 08e9b9c13..e69de29bb 100644 --- a/tests/testthat/out/multi-04.txt +++ b/tests/testthat/out/multi-04.txt @@ -1,5 +0,0 @@ - -  -1 -2 -3 diff --git a/tests/testthat/out/multi-05.txt b/tests/testthat/out/multi-05.txt index 08e9b9c13..e69de29bb 100644 --- a/tests/testthat/out/multi-05.txt +++ b/tests/testthat/out/multi-05.txt @@ -1,5 +0,0 @@ - -  -1 -2 -3 diff --git a/tests/testthat/out/multi-06.txt b/tests/testthat/out/multi-06.txt index 08e9b9c13..e69de29bb 100644 --- a/tests/testthat/out/multi-06.txt +++ b/tests/testthat/out/multi-06.txt @@ -1,5 +0,0 @@ - -  -1 -2 -3 diff --git a/tests/testthat/out/multi-07.txt b/tests/testthat/out/multi-07.txt index 08e9b9c13..e69de29bb 100644 --- a/tests/testthat/out/multi-07.txt +++ b/tests/testthat/out/multi-07.txt @@ -1,5 +0,0 @@ - -  -1 -2 -3 diff --git a/tests/testthat/out/tibble-all--300.txt b/tests/testthat/out/tibble-all--300.txt index 9e0d0e2d7..99be3ae7d 100644 --- a/tests/testthat/out/tibble-all--300.txt +++ b/tests/testthat/out/tibble-all--300.txt @@ -1,5 +1,5 @@ - a b c d e f g h i - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 -3 NA NA NA NA NA + a b c d e f g h i + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 NA NA Date: Sat, 16 Sep 2017 15:55:33 +0200 Subject: [PATCH 065/133] Another preparation (#52) * don't allow tiers with negative or too small widths * pillars! * more tests --- API | 2 +- DESCRIPTION | 3 +- R/multi.R | 17 ++++++----- R/testthat.R | 12 ++++++-- man/colonnade.Rd | 20 ++++++------- man/expect_pillar_output.Rd | 4 ++- tests/testthat/out/tibble-all--300-20.txt | 5 ++++ tests/testthat/out/tibble-all--300-30.txt | 5 ++++ tests/testthat/out/tibble-all--300-40.txt | 5 ++++ tests/testthat/out/tibble-all--300-50.txt | 5 ++++ tests/testthat/out/tibble-all--300-60.txt | 5 ++++ tests/testthat/out/tibble-all--300-70.txt | 5 ++++ tests/testthat/test-format_multi.R | 36 +++++++++++++++++++++++ 13 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 tests/testthat/out/tibble-all--300-20.txt create mode 100644 tests/testthat/out/tibble-all--300-30.txt create mode 100644 tests/testthat/out/tibble-all--300-40.txt create mode 100644 tests/testthat/out/tibble-all--300-50.txt create mode 100644 tests/testthat/out/tibble-all--300-60.txt create mode 100644 tests/testthat/out/tibble-all--300-70.txt diff --git a/API b/API index 699902a0d..fcffec89d 100644 --- a/API +++ b/API @@ -4,7 +4,7 @@ colonnade(x, has_row_id = TRUE, width = NULL, ...) dim_desc(x) -expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, ...), crayon = TRUE) +expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, ...), crayon = TRUE, output_width = 80L) extra_cols(x, ...) is_vector_s3(x) new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), 0L), align = "left", min_width = NULL, na_indent = 0L) diff --git a/DESCRIPTION b/DESCRIPTION index 20f8c68e8..984a9c2aa 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,4 +22,5 @@ Suggests: Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi::api_roclet")) RoxygenNote: 6.0.1 Remotes: - r-lib/crayon + r-lib/crayon, + hadley/testthat diff --git a/R/multi.R b/R/multi.R index 6055320de..aa71685c2 100644 --- a/R/multi.R +++ b/R/multi.R @@ -172,7 +172,8 @@ get_tier_widths <- function(width, rowid_width, tier_width = getOption("width")) seq(0, width - 1, by = tier_width), width ) - diff(pos) - rowid_width + widths <- diff(pos) - rowid_width + widths[widths >= 1] } #' @rdname colonnade @@ -205,18 +206,18 @@ colonnade_compute_col_widths_df <- function(col_df, width, tier_id = 1L) { #' @aliases NULL colonnade_compute_col_widths <- function(min_widths, max_widths, width) { #' @details - #' For computing the column widths, two cases are distinguished: - #' 1. When taking the minimum width for each column (plus one inter-column - #' space), at least one column does not fit. + #' For computing the pillar widths in a single tier, two cases are distinguished: + #' 1. When taking the minimum width for each pillar (plus one inter-pillar + #' space), at least one pillar does not fit. cum_min_widths <- cumsum(min_widths + 1L) allowed_min <- which(cum_min_widths <= width) if (length(allowed_min) < length(min_widths)) { - #' In this case, the minimum width is assigned to all columns that do fit, - #' the non-fitting columns are stripped. + #' In this case, the minimum width is assigned to all pillars that do fit, + #' the non-fitting pillars are stripped. col_widths <- min_widths[allowed_min] } else { - #' 1. All columns fit with their minimum width. In this case, starting at - #' the leftmost column, the maximum width is allocated to the columns + #' 1. All pillars fit with their minimum width. In this case, starting at + #' the leftmost pillar, the maximum width is allocated to the pillars #' until all available space is used. cum_max_widths <- cumsum(max_widths + 1L) rev_cum_rev_min_widths <- rev(cumsum(rev(min_widths) + 1L)) diff --git a/R/testthat.R b/R/testthat.R index 51cf85673..51799c243 100644 --- a/R/testthat.R +++ b/R/testthat.R @@ -11,10 +11,11 @@ #' `NA` and `Inf` values. #' @param xf Pass the result of a [pillar()] call here for full control. #' @param crayon Color the output? +#' @param output_width Passed on as `width` to [testthat::expect_output_file()]. #' @export expect_pillar_output <- function(x, ..., filename, - xp = add_special(x), xf = pillar(xp, ...), - crayon = TRUE) { + xp = add_special(x), xf = pillar(xp, ...), + crayon = TRUE, output_width = 80L) { if (crayon) { old <- options(crayon.enabled = TRUE, crayon.colors = 16L) crayon::num_colors(forget = TRUE) @@ -27,7 +28,12 @@ expect_pillar_output <- function(x, ..., filename, crayon::num_colors(forget = TRUE) }) - testthat::expect_output_file(print(xf), file.path("out", filename), update = TRUE) + testthat::expect_output_file( + print(xf), + file.path("out", filename), + update = TRUE, + width = output_width + ) } #' `add_special()` is not exported, and used only for initializing default diff --git a/man/colonnade.Rd b/man/colonnade.Rd index ae2937318..40755b8c1 100644 --- a/man/colonnade.Rd +++ b/man/colonnade.Rd @@ -29,22 +29,22 @@ It returns an object suitable for printing and formatting at a fixed width with additional information about omitted columns. } \details{ -In a first pass, for each pillar it is decided in which chunk it is shown, +In a first pass, for each pillar it is decided in which tier it is shown, if at all, and how much horizontal space it may use (either its minumum -or its maximum width). More than one chunk may be created if -\code{width > getOption("width")}, in this case each chunk is at most +or its maximum width). More than one tier may be created if +\code{width > getOption("width")}, in this case each tier is at most \code{getOption("width")} characters wide. Remaining space is then distributed proportionally to pillars that do not use their desired width. -For computing the column widths, two cases are distinguished: +For computing the pillar widths in a single tier, two cases are distinguished: \enumerate{ -\item When taking the minimum width for each column (plus one inter-column -space), at least one column does not fit. -In this case, the minimum width is assigned to all columns that do fit, -the non-fitting columns are stripped. -\item All columns fit with their minimum width. In this case, starting at -the leftmost column, the maximum width is allocated to the columns +\item When taking the minimum width for each pillar (plus one inter-pillar +space), at least one pillar does not fit. +In this case, the minimum width is assigned to all pillars that do fit, +the non-fitting pillars are stripped. +\item All pillars fit with their minimum width. In this case, starting at +the leftmost pillar, the maximum width is allocated to the pillars until all available space is used. } diff --git a/man/expect_pillar_output.Rd b/man/expect_pillar_output.Rd index 47c6413aa..1e25d14f1 100644 --- a/man/expect_pillar_output.Rd +++ b/man/expect_pillar_output.Rd @@ -6,7 +6,7 @@ \title{Test helpers} \usage{ expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, - ...), crayon = TRUE) + ...), crayon = TRUE, output_width = 80L) add_special(x) } @@ -23,6 +23,8 @@ add_special(x) \item{xf}{Pass the result of a \code{\link[=pillar]{pillar()}} call here for full control.} \item{crayon}{Color the output?} + +\item{output_width}{Passed on as \code{width} to \code{\link[testthat:expect_output_file]{testthat::expect_output_file()}}.} } \description{ Helper functions for packages that implement their own pillar. diff --git a/tests/testthat/out/tibble-all--300-20.txt b/tests/testthat/out/tibble-all--300-20.txt new file mode 100644 index 000000000..82ff377e9 --- /dev/null +++ b/tests/testthat/out/tibble-all--300-20.txt @@ -0,0 +1,5 @@ + a b c + +1 1.00 1 T +2 2.50 2 F +3 NA NA NA diff --git a/tests/testthat/out/tibble-all--300-30.txt b/tests/testthat/out/tibble-all--300-30.txt new file mode 100644 index 000000000..e16955bf3 --- /dev/null +++ b/tests/testthat/out/tibble-all--300-30.txt @@ -0,0 +1,5 @@ + a b c d + +1 1.00 1 T a +2 2.50 2 F b +3 NA NA NA diff --git a/tests/testthat/out/tibble-all--300-40.txt b/tests/testthat/out/tibble-all--300-40.txt new file mode 100644 index 000000000..fcf375072 --- /dev/null +++ b/tests/testthat/out/tibble-all--300-40.txt @@ -0,0 +1,5 @@ + a b c d e + +1 1.00 1 T a a +2 2.50 2 F b b +3 NA NA NA diff --git a/tests/testthat/out/tibble-all--300-50.txt b/tests/testthat/out/tibble-all--300-50.txt new file mode 100644 index 000000000..d58565c36 --- /dev/null +++ b/tests/testthat/out/tibble-all--300-50.txt @@ -0,0 +1,5 @@ + a b c d e f + +1 1.00 1 T a a 2015-12-10 +2 2.50 2 F b b 2015-12-11 +3 NA NA NA NA diff --git a/tests/testthat/out/tibble-all--300-60.txt b/tests/testthat/out/tibble-all--300-60.txt new file mode 100644 index 000000000..d58565c36 --- /dev/null +++ b/tests/testthat/out/tibble-all--300-60.txt @@ -0,0 +1,5 @@ + a b c d e f + +1 1.00 1 T a a 2015-12-10 +2 2.50 2 F b b 2015-12-11 +3 NA NA NA NA diff --git a/tests/testthat/out/tibble-all--300-70.txt b/tests/testthat/out/tibble-all--300-70.txt new file mode 100644 index 000000000..1fa36383c --- /dev/null +++ b/tests/testthat/out/tibble-all--300-70.txt @@ -0,0 +1,5 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 NA NA Date: Wed, 4 Oct 2017 22:01:46 +0200 Subject: [PATCH 066/133] adapt to recent upstream changes --- tests/testthat/test-format_decimal.R | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/testthat/test-format_decimal.R b/tests/testthat/test-format_decimal.R index 309127bef..f8d3f6423 100644 --- a/tests/testthat/test-format_decimal.R +++ b/tests/testthat/test-format_decimal.R @@ -1,10 +1,14 @@ context("format_decimal") -format_decimal_bw <- function(x, ...) { +without_color <- function(code) { old <- options(crayon.enabled = FALSE) on.exit(options(old)) - format_decimal(x, ...) + code +} + +format_decimal_bw <- function(x, ...) { + without_color(format_decimal(x, ...)) } test_that("compute_rhs_digits() works", { @@ -22,7 +26,7 @@ test_that("special values appear in LHS", { x <- c(NA, NaN, Inf) f <- format_decimal_bw(x) - expect_equal(format_lhs(f), format(x)) + expect_equal(without_color(format_lhs(f)), format(x)) }) test_that("all-positive values get nothing in neg", { @@ -37,7 +41,7 @@ test_that("negative values get - in neg", { test_that("trailing zeros pad to sigfigs", { f <- format_decimal_bw(c(1.5, 0.5)) - expect_equal(format_lhs(f), c("1", "0")) + expect_equal(without_color(format_lhs(f)), c("1", "0")) expect_equal(format_rhs(f), c("50 ", "500")) }) @@ -64,8 +68,8 @@ test_that("values rounded up as expect", { }) test_that("values on LHS not rounded", { - f <- format_decimal_bw(123456) - expect_equal(format_lhs(f), "123456") + f <- without_color(format_lhs(format_decimal(123456))) + expect_equal(f, "123456") }) test_that("corner cases", { From 901059c7199609ff07d1a83fe870a26eb40214e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 4 Oct 2017 23:14:33 +0200 Subject: [PATCH 067/133] Fitting heuristic for multiple tiers (#51) * preliminary support for multiple tiers * better fitting heuristic * remove underline, add italic * logical are shown in black * very subtle row numbers * no quotes for embedded spaces, closes #54 --- R/col-data.R | 4 +- R/col-type.R | 4 +- R/multi.R | 56 +++++++++++++++++++-- R/rowid-data.R | 2 +- R/rowid-type.R | 2 +- R/styles.R | 10 ++-- R/tick.R | 2 +- R/utils.R | 4 ++ man/colonnade.Rd | 17 +++++++ tests/testthat/out/basic.txt | 2 +- tests/testthat/out/date.txt | 2 +- tests/testthat/out/deal1.txt | 2 +- tests/testthat/out/deal2.txt | 2 +- tests/testthat/out/deal3.txt | 2 +- tests/testthat/out/decimal-insignif.txt | 2 +- tests/testthat/out/escaped.txt | 2 +- tests/testthat/out/factor.txt | 2 +- tests/testthat/out/integer-06.txt | 2 +- tests/testthat/out/integer-07.txt | 2 +- tests/testthat/out/integer-08.txt | 2 +- tests/testthat/out/integer-09.txt | 2 +- tests/testthat/out/letters-long-03.txt | 2 +- tests/testthat/out/letters-long-10.txt | 2 +- tests/testthat/out/letters-long.txt | 2 +- tests/testthat/out/letters.txt | 2 +- tests/testthat/out/list-each.txt | 2 +- tests/testthat/out/list-na.txt | 2 +- tests/testthat/out/list-narrow.txt | 2 +- tests/testthat/out/list-null.txt | 2 +- tests/testthat/out/logical.txt | 6 +-- tests/testthat/out/multi-08.txt | 2 +- tests/testthat/out/multi-09.txt | 2 +- tests/testthat/out/multi-10.txt | 2 +- tests/testthat/out/multi-11.txt | 2 +- tests/testthat/out/multi-12.txt | 2 +- tests/testthat/out/multi-13.txt | 2 +- tests/testthat/out/multi-14.txt | 2 +- tests/testthat/out/multi-15.txt | 2 +- tests/testthat/out/multi-16.txt | 2 +- tests/testthat/out/multi-17.txt | 2 +- tests/testthat/out/multi-18.txt | 2 +- tests/testthat/out/multi-19.txt | 2 +- tests/testthat/out/multi-20.txt | 2 +- tests/testthat/out/multi-21.txt | 2 +- tests/testthat/out/multi-22.txt | 2 +- tests/testthat/out/multi-23.txt | 2 +- tests/testthat/out/multi-24.txt | 2 +- tests/testthat/out/multi-25.txt | 2 +- tests/testthat/out/multi-26.txt | 2 +- tests/testthat/out/multi-27.txt | 2 +- tests/testthat/out/multi-28.txt | 2 +- tests/testthat/out/multi-29.txt | 2 +- tests/testthat/out/multi-30.txt | 2 +- tests/testthat/out/multi-31.txt | 2 +- tests/testthat/out/multi-32.txt | 2 +- tests/testthat/out/multi-33.txt | 2 +- tests/testthat/out/multi-34.txt | 2 +- tests/testthat/out/multi-35.txt | 2 +- tests/testthat/out/multi-36.txt | 2 +- tests/testthat/out/multi-37.txt | 2 +- tests/testthat/out/multi-38.txt | 2 +- tests/testthat/out/multi-39.txt | 2 +- tests/testthat/out/multi-extra-10.txt | 6 +-- tests/testthat/out/numeric-04.txt | 2 +- tests/testthat/out/numeric-07.txt | 2 +- tests/testthat/out/numeric-10.txt | 2 +- tests/testthat/out/numeric-15.txt | 2 +- tests/testthat/out/numeric-22.txt | 2 +- tests/testthat/out/ordered.txt | 2 +- tests/testthat/out/rowid-3.txt | 2 +- tests/testthat/out/rowid-star-title-12.txt | 2 +- tests/testthat/out/scientific-short-neg.txt | 2 +- tests/testthat/out/scientific.txt | 2 +- tests/testthat/out/spaces.txt | 7 +++ tests/testthat/out/tibble-all--300-20.txt | 10 ++++ tests/testthat/out/tibble-all--300-30.txt | 15 ++++++ tests/testthat/out/tibble-all--300-40.txt | 10 ++++ tests/testthat/out/tibble-all--300-50.txt | 5 ++ tests/testthat/out/tibble-all--300-60.txt | 5 ++ tests/testthat/out/tibble-all--300-70.txt | 15 ++++-- tests/testthat/out/tibble-all--300.txt | 15 ++++-- tests/testthat/out/time-posix.txt | 2 +- tests/testthat/out/time.txt | 2 +- tests/testthat/out/title-crayon.txt | 2 +- tests/testthat/test-format_character.R | 1 + 85 files changed, 230 insertions(+), 96 deletions(-) create mode 100644 tests/testthat/out/spaces.txt diff --git a/R/col-data.R b/R/col-data.R index b8713e4b8..7c4707874 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -55,8 +55,8 @@ new_pillar_shaft <- function(x, ..., #' @rdname pillar_shaft pillar_shaft.logical <- function(x, ...) { out <- rep(NA, length(x)) - out[x] <- style_accent("T") - out[!x] <- style_subtle("F") + out[x] <- "T" + out[!x] <- "F" new_pillar_shaft(out, width = 1, align = "left") } diff --git a/R/col-type.R b/R/col-type.R index 414098b8f..292ead60c 100644 --- a/R/col-type.R +++ b/R/col-type.R @@ -1,9 +1,9 @@ style_type_header <- function(x) { - crayon::underline(style_type(x)) + style_type(x) } style_type <- function(x) { - style_subtle(x) + crayon::italic(style_subtle(x)) } pillar_type <- function(x, ...) { diff --git a/R/multi.R b/R/multi.R index aa71685c2..ef80c95cc 100644 --- a/R/multi.R +++ b/R/multi.R @@ -179,9 +179,59 @@ get_tier_widths <- function(width, rowid_width, tier_width = getOption("width")) #' @rdname colonnade #' @usage NULL #' @aliases NULL -colonnade_compute_tiered_col_widths_df <- function(col_df, tier_widths) { - col_tier_df <- colonnade_compute_col_widths_df(col_df, tier_widths[[1]]) - col_tier_df +colonnade_compute_tiered_col_widths_df <- function(col_df, tier_widths, fixed_tier_df = data.frame()) { + if (nrow(col_df) == 0L) return(data.frame()) + + tier_id <- max(c(fixed_tier_df$tier), 0L) + 1L + tier_df <- colonnade_compute_col_widths_df(col_df, tier_widths[[1]], tier_id) + + #' @details + #' For fitting pillars in one or more tiers, it is first attempted to fit all + #' of them in the first tier. + if (length(tier_widths) == 1 || all(tier_df$width >= tier_df$max_widths)) { + #' If this succeeds (or if no more tiers are available), this fit is + #' accepted. + return(rbind(fixed_tier_df, tier_df)) + } + + #' Otherwise, an attempt is made to fit all remaining pillars in the remaining + #' tiers (with a recursive call). + all_tier_df <- colonnade_compute_tiered_col_widths_df( + col_df[tier_df$tier == 0, ], + tier_widths[-1], + rbind(fixed_tier_df, slice(tier_df, tier_df$tier != 0)) + ) + + #' If there still are pillars that don't fit, the minimum-width fit is accepted. + if (!all_pillars_fit(all_tier_df)) return(all_tier_df) + + #' + #' In case all remaining pillars fit all remaining tiers, a heuristic + #' selects the optimal number of pillars in the first tier. + first_expandable <- which(cumsum(tier_df$max_widths + 1L) > tier_widths[[1L]])[[1L]] + last_fitting <- utils::tail(which(c(tier_df$tier, 0L) != 0L), 1L) + #' The tier is grown starting with all pillars that are fitting with their + #' desired width (at least one pillar will be used), and + for (n_pillars_in_first_tier in seq2(max(first_expandable - 1L, 1L), last_fitting - 1L)) { + #' attempts are made to fit the remaining pillars in the remaining tiers + #' (with a recursive call for each attempt). + all_tier_try_df <- colonnade_compute_tiered_col_widths_df( + slice(col_df, seq2(n_pillars_in_first_tier + 1L, nrow(col_df))), + tier_widths[-1], + rbind(fixed_tier_df, slice(tier_df, seq_len(n_pillars_in_first_tier))) + ) + + #' The first successful fit + if (all_pillars_fit(all_tier_try_df)) return(all_tier_try_df) + } + + #' (or otherwise the initial minimum-width fit) is accepted. + all_tier_df +} + +all_pillars_fit <- function(tier_df) { + rows <- nrow(tier_df) + rows == 0 || tier_df$tier[[nrow(tier_df)]] != 0 } #' @rdname colonnade diff --git a/R/rowid-data.R b/R/rowid-data.R index 8b83ae153..95c554fbc 100644 --- a/R/rowid-data.R +++ b/R/rowid-data.R @@ -1,5 +1,5 @@ style_rowid <- function(x) { - style_subtle(x) + style_hint(x) } rif_data <- function(n, ...) { diff --git a/R/rowid-type.R b/R/rowid-type.R index 6cfd88adb..c157509c9 100644 --- a/R/rowid-type.R +++ b/R/rowid-type.R @@ -1,5 +1,5 @@ style_star <- function(x) { - style_subtle(x) + style_hint(x) } rif_type <- function(has_star, ...) { diff --git a/R/styles.R b/R/styles.R index 36e39b036..31f48726e 100644 --- a/R/styles.R +++ b/R/styles.R @@ -1,5 +1,5 @@ keep_empty <- function(fun) { - function(x) { + function(x) { ret <- rep_along(x, "") update <- which(is.na(x) | x != "") ret[update] <- fun(x[update]) @@ -7,10 +7,6 @@ keep_empty <- function(fun) { } } -style_accent <- keep_empty(function(x) { - crayon::green(x) -}) - #' Styling helpers #' #' Functions that allow implementers of formatters for custom data types to @@ -22,6 +18,10 @@ style_subtle <- keep_empty(function(x) { style_grey(0.6, x) }) +style_hint <- keep_empty(function(x) { + style_grey(0.8, x) +}) + style_spark_na <- function(x) { crayon::yellow(x) } diff --git a/R/tick.R b/R/tick.R index caf746d39..47b6e1b57 100644 --- a/R/tick.R +++ b/R/tick.R @@ -11,7 +11,7 @@ is_syntactic <- function(x) { } is_proper_string <- function(x) { - grepl("^[^[:blank:][:cntrl:]\"]+$", x, perl = TRUE) + grepl("^[^[:blank:][:cntrl:]\"](?:|[^[:cntrl:]\"]*[^[:blank:][:cntrl:]\"])$", x, perl = TRUE) } tick <- function(x) { diff --git a/R/utils.R b/R/utils.R index e78726ff0..ce47503cd 100644 --- a/R/utils.R +++ b/R/utils.R @@ -38,3 +38,7 @@ ruler <- function(width = getOption("width")) { cat(y, "\n", sep = "") cat(x %% 10, "\n", sep = "") } + +slice <- function(df, index) { + df[index, , drop = FALSE] +} diff --git a/man/colonnade.Rd b/man/colonnade.Rd index 40755b8c1..aef15bc75 100644 --- a/man/colonnade.Rd +++ b/man/colonnade.Rd @@ -37,6 +37,23 @@ or its maximum width). More than one tier may be created if Remaining space is then distributed proportionally to pillars that do not use their desired width. +For fitting pillars in one or more tiers, it is first attempted to fit all +of them in the first tier. +If this succeeds (or if no more tiers are available), this fit is +accepted. +Otherwise, an attempt is made to fit all remaining pillars in the remaining +tiers (with a recursive call). +If there still are pillars that don't fit, the minimum-width fit is accepted. + +In case all remaining pillars fit all remaining tiers, a heuristic +selects the optimal number of pillars in the first tier. +The tier is grown starting with all pillars that are fitting with their +desired width (at least one pillar will be used), and +attempts are made to fit the remaining pillars in the remaining tiers +(with a recursive call for each attempt). +The first successful fit +(or otherwise the initial minimum-width fit) is accepted. + For computing the pillar widths in a single tier, two cases are distinguished: \enumerate{ \item When taking the minimum width for each pillar (plus one inter-pillar diff --git a/tests/testthat/out/basic.txt b/tests/testthat/out/basic.txt index b9f5d9e6f..f57bc7e73 100644 --- a/tests/testthat/out/basic.txt +++ b/tests/testthat/out/basic.txt @@ -1,4 +1,4 @@ -  +  - 0.00100 0.0100 - 0.100 diff --git a/tests/testthat/out/date.txt b/tests/testthat/out/date.txt index 402379304..c780825b6 100644 --- a/tests/testthat/out/date.txt +++ b/tests/testthat/out/date.txt @@ -1,3 +1,3 @@ -  +  2017-07-28 NA diff --git a/tests/testthat/out/deal1.txt b/tests/testthat/out/deal1.txt index 2c782d678..290af49d4 100644 --- a/tests/testthat/out/deal1.txt +++ b/tests/testthat/out/deal1.txt @@ -1,4 +1,4 @@ 成交 -  +  成交日 NA diff --git a/tests/testthat/out/deal2.txt b/tests/testthat/out/deal2.txt index 9fe8202de..d607a51ce 100644 --- a/tests/testthat/out/deal2.txt +++ b/tests/testthat/out/deal2.txt @@ -1,4 +1,4 @@ 成交日 -  +  成交 NA diff --git a/tests/testthat/out/deal3.txt b/tests/testthat/out/deal3.txt index f78c2110a..ddfb72976 100644 --- a/tests/testthat/out/deal3.txt +++ b/tests/testthat/out/deal3.txt @@ -1,4 +1,4 @@ 成交日 -  +  1 NA diff --git a/tests/testthat/out/decimal-insignif.txt b/tests/testthat/out/decimal-insignif.txt index 9d27484ae..d7737b9b5 100644 --- a/tests/testthat/out/decimal-insignif.txt +++ b/tests/testthat/out/decimal-insignif.txt @@ -1,4 +1,4 @@ -  +  0.00123 0.0123 0.123 diff --git a/tests/testthat/out/escaped.txt b/tests/testthat/out/escaped.txt index 82c0678a3..cbad25655 100644 --- a/tests/testthat/out/escaped.txt +++ b/tests/testthat/out/escaped.txt @@ -1,2 +1,2 @@ - + "a\nb" diff --git a/tests/testthat/out/factor.txt b/tests/testthat/out/factor.txt index 2989e9e15..136adf81a 100644 --- a/tests/testthat/out/factor.txt +++ b/tests/testthat/out/factor.txt @@ -1,4 +1,4 @@ - + a b c diff --git a/tests/testthat/out/integer-06.txt b/tests/testthat/out/integer-06.txt index f166a3e05..c167aef8b 100644 --- a/tests/testthat/out/integer-06.txt +++ b/tests/testthat/out/integer-06.txt @@ -1,4 +1,4 @@ -  +  1.00e⁷ 1.00e⁷ 1.00e⁷ diff --git a/tests/testthat/out/integer-07.txt b/tests/testthat/out/integer-07.txt index f166a3e05..c167aef8b 100644 --- a/tests/testthat/out/integer-07.txt +++ b/tests/testthat/out/integer-07.txt @@ -1,4 +1,4 @@ -  +  1.00e⁷ 1.00e⁷ 1.00e⁷ diff --git a/tests/testthat/out/integer-08.txt b/tests/testthat/out/integer-08.txt index 3390cb1f1..b870f622d 100644 --- a/tests/testthat/out/integer-08.txt +++ b/tests/testthat/out/integer-08.txt @@ -1,4 +1,4 @@ -  +  10000001 10000002 10000003 diff --git a/tests/testthat/out/integer-09.txt b/tests/testthat/out/integer-09.txt index 7e042b5c2..7f9951981 100644 --- a/tests/testthat/out/integer-09.txt +++ b/tests/testthat/out/integer-09.txt @@ -1,4 +1,4 @@ -  +  10000001 10000002 10000003 diff --git a/tests/testthat/out/letters-long-03.txt b/tests/testthat/out/letters-long-03.txt index 3f9aa0c9b..0b71f4afb 100644 --- a/tests/testthat/out/letters-long-03.txt +++ b/tests/testthat/out/letters-long-03.txt @@ -1,3 +1,3 @@ - + abcd… NA diff --git a/tests/testthat/out/letters-long-10.txt b/tests/testthat/out/letters-long-10.txt index 88aa5e18e..ea2cd63d8 100644 --- a/tests/testthat/out/letters-long-10.txt +++ b/tests/testthat/out/letters-long-10.txt @@ -1,3 +1,3 @@ -  +  abcdefghi… NA diff --git a/tests/testthat/out/letters-long.txt b/tests/testthat/out/letters-long.txt index 48df833e6..c80964136 100644 --- a/tests/testthat/out/letters-long.txt +++ b/tests/testthat/out/letters-long.txt @@ -1,3 +1,3 @@ -  +  abcdefghijklmnopqrstuvwxyz NA diff --git a/tests/testthat/out/letters.txt b/tests/testthat/out/letters.txt index f60096313..7e94700fb 100644 --- a/tests/testthat/out/letters.txt +++ b/tests/testthat/out/letters.txt @@ -1,4 +1,4 @@ - + a b c diff --git a/tests/testthat/out/list-each.txt b/tests/testthat/out/list-each.txt index f04cada17..8d61bea64 100644 --- a/tests/testthat/out/list-each.txt +++ b/tests/testthat/out/list-each.txt @@ -1,4 +1,4 @@ -  +     diff --git a/tests/testthat/out/list-na.txt b/tests/testthat/out/list-na.txt index e05047a9c..81aa85e8d 100644 --- a/tests/testthat/out/list-na.txt +++ b/tests/testthat/out/list-na.txt @@ -1,3 +1,3 @@ -  +    diff --git a/tests/testthat/out/list-narrow.txt b/tests/testthat/out/list-narrow.txt index 26da64e68..3dca5f8a8 100644 --- a/tests/testthat/out/list-narrow.txt +++ b/tests/testthat/out/list-narrow.txt @@ -1,2 +1,2 @@ -  +    +    diff --git a/tests/testthat/out/logical.txt b/tests/testthat/out/logical.txt index 3f89116a8..b6ace8363 100644 --- a/tests/testthat/out/logical.txt +++ b/tests/testthat/out/logical.txt @@ -1,4 +1,4 @@ - -T -F + +T +F NA diff --git a/tests/testthat/out/multi-08.txt b/tests/testthat/out/multi-08.txt index eb362a646..9c7cf7952 100644 --- a/tests/testthat/out/multi-08.txt +++ b/tests/testthat/out/multi-08.txt @@ -1,5 +1,5 @@ colu… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-09.txt b/tests/testthat/out/multi-09.txt index c13147457..a3a79a519 100644 --- a/tests/testthat/out/multi-09.txt +++ b/tests/testthat/out/multi-09.txt @@ -1,5 +1,5 @@ colum… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-10.txt b/tests/testthat/out/multi-10.txt index abaa394b4..37b0dab76 100644 --- a/tests/testthat/out/multi-10.txt +++ b/tests/testthat/out/multi-10.txt @@ -1,5 +1,5 @@ column… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-11.txt b/tests/testthat/out/multi-11.txt index adaffeb95..f8501ce4c 100644 --- a/tests/testthat/out/multi-11.txt +++ b/tests/testthat/out/multi-11.txt @@ -1,5 +1,5 @@ column_… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-12.txt b/tests/testthat/out/multi-12.txt index caee265d1..02cfa070b 100644 --- a/tests/testthat/out/multi-12.txt +++ b/tests/testthat/out/multi-12.txt @@ -1,5 +1,5 @@ column_z… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-13.txt b/tests/testthat/out/multi-13.txt index db5914da5..892d29a45 100644 --- a/tests/testthat/out/multi-13.txt +++ b/tests/testthat/out/multi-13.txt @@ -1,5 +1,5 @@ column_ze… -  +  1 1.23 2 2.23 3 3.23 diff --git a/tests/testthat/out/multi-14.txt b/tests/testthat/out/multi-14.txt index c6f5b277b..eff86cea9 100644 --- a/tests/testthat/out/multi-14.txt +++ b/tests/testthat/out/multi-14.txt @@ -1,5 +1,5 @@ colu… col_… -  +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-15.txt b/tests/testthat/out/multi-15.txt index ca1aff45d..6d109243b 100644 --- a/tests/testthat/out/multi-15.txt +++ b/tests/testthat/out/multi-15.txt @@ -1,5 +1,5 @@ colum… col_… -  +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-16.txt b/tests/testthat/out/multi-16.txt index e727c3eea..584374bee 100644 --- a/tests/testthat/out/multi-16.txt +++ b/tests/testthat/out/multi-16.txt @@ -1,5 +1,5 @@ column… col_… -  +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-17.txt b/tests/testthat/out/multi-17.txt index 9f778f90b..b79d1e8ad 100644 --- a/tests/testthat/out/multi-17.txt +++ b/tests/testthat/out/multi-17.txt @@ -1,5 +1,5 @@ column_… col_… -  +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-18.txt b/tests/testthat/out/multi-18.txt index ef8680e1c..2acef9e0c 100644 --- a/tests/testthat/out/multi-18.txt +++ b/tests/testthat/out/multi-18.txt @@ -1,5 +1,5 @@ column_z… col_… -  +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-19.txt b/tests/testthat/out/multi-19.txt index d512cad7c..215cad0f6 100644 --- a/tests/testthat/out/multi-19.txt +++ b/tests/testthat/out/multi-19.txt @@ -1,5 +1,5 @@ column_ze… col_… -  +  1 1.23 a 2 2.23 b 3 3.23 c diff --git a/tests/testthat/out/multi-20.txt b/tests/testthat/out/multi-20.txt index 1642b616f..762f7da81 100644 --- a/tests/testthat/out/multi-20.txt +++ b/tests/testthat/out/multi-20.txt @@ -1,5 +1,5 @@ colu… col_… col_… -  +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-21.txt b/tests/testthat/out/multi-21.txt index 3ae051a7c..a594a85c1 100644 --- a/tests/testthat/out/multi-21.txt +++ b/tests/testthat/out/multi-21.txt @@ -1,5 +1,5 @@ colum… col_… col_… -  +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-22.txt b/tests/testthat/out/multi-22.txt index 16295675b..9c1b71eb1 100644 --- a/tests/testthat/out/multi-22.txt +++ b/tests/testthat/out/multi-22.txt @@ -1,5 +1,5 @@ column… col_… col_… -  +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-23.txt b/tests/testthat/out/multi-23.txt index d28398ea0..d30bd872b 100644 --- a/tests/testthat/out/multi-23.txt +++ b/tests/testthat/out/multi-23.txt @@ -1,5 +1,5 @@ column_… col_… col_… -  +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-24.txt b/tests/testthat/out/multi-24.txt index 850d44dc8..8a431f4ef 100644 --- a/tests/testthat/out/multi-24.txt +++ b/tests/testthat/out/multi-24.txt @@ -1,5 +1,5 @@ column_z… col_… col_… -  +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-25.txt b/tests/testthat/out/multi-25.txt index 219bfb7eb..b4ce848eb 100644 --- a/tests/testthat/out/multi-25.txt +++ b/tests/testthat/out/multi-25.txt @@ -1,5 +1,5 @@ column_ze… col_… col_… -  +  1 1.23 a a 2 2.23 b b 3 3.23 c c diff --git a/tests/testthat/out/multi-26.txt b/tests/testthat/out/multi-26.txt index f897a3817..8653ea6c3 100644 --- a/tests/testthat/out/multi-26.txt +++ b/tests/testthat/out/multi-26.txt @@ -1,5 +1,5 @@ colu… col_… col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-27.txt b/tests/testthat/out/multi-27.txt index 62185fab4..19e6108a9 100644 --- a/tests/testthat/out/multi-27.txt +++ b/tests/testthat/out/multi-27.txt @@ -1,5 +1,5 @@ colum… col_… col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-28.txt b/tests/testthat/out/multi-28.txt index eef81a488..2597f37ee 100644 --- a/tests/testthat/out/multi-28.txt +++ b/tests/testthat/out/multi-28.txt @@ -1,5 +1,5 @@ column… col_… col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-29.txt b/tests/testthat/out/multi-29.txt index c37c79cd0..4de4d4919 100644 --- a/tests/testthat/out/multi-29.txt +++ b/tests/testthat/out/multi-29.txt @@ -1,5 +1,5 @@ column_… col_… col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-30.txt b/tests/testthat/out/multi-30.txt index 9ce3b6f48..c8596b878 100644 --- a/tests/testthat/out/multi-30.txt +++ b/tests/testthat/out/multi-30.txt @@ -1,5 +1,5 @@ column_z… col_… col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-31.txt b/tests/testthat/out/multi-31.txt index d133cab15..6a10899d5 100644 --- a/tests/testthat/out/multi-31.txt +++ b/tests/testthat/out/multi-31.txt @@ -1,5 +1,5 @@ column_z… col_02 col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-32.txt b/tests/testthat/out/multi-32.txt index cceca5016..b53715727 100644 --- a/tests/testthat/out/multi-32.txt +++ b/tests/testthat/out/multi-32.txt @@ -1,5 +1,5 @@ column_ze… col_02 col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-33.txt b/tests/testthat/out/multi-33.txt index c438eab41..768f2f7be 100644 --- a/tests/testthat/out/multi-33.txt +++ b/tests/testthat/out/multi-33.txt @@ -1,5 +1,5 @@ column_zer… col_02 col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-34.txt b/tests/testthat/out/multi-34.txt index 91d17fc18..c7a4b2538 100644 --- a/tests/testthat/out/multi-34.txt +++ b/tests/testthat/out/multi-34.txt @@ -1,5 +1,5 @@ column_zero… col_02 col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-35.txt b/tests/testthat/out/multi-35.txt index 2978977e6..1bc9036cd 100644 --- a/tests/testthat/out/multi-35.txt +++ b/tests/testthat/out/multi-35.txt @@ -1,5 +1,5 @@ column_zero… col_02 col_03 col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-36.txt b/tests/testthat/out/multi-36.txt index 9bc140a81..25057e363 100644 --- a/tests/testthat/out/multi-36.txt +++ b/tests/testthat/out/multi-36.txt @@ -1,5 +1,5 @@ column_zero_one col_… col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-37.txt b/tests/testthat/out/multi-37.txt index e7a2f9868..55e2fac85 100644 --- a/tests/testthat/out/multi-37.txt +++ b/tests/testthat/out/multi-37.txt @@ -1,5 +1,5 @@ column_zero_one col_02 col_… col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-38.txt b/tests/testthat/out/multi-38.txt index eaefdb3c5..6994cf88a 100644 --- a/tests/testthat/out/multi-38.txt +++ b/tests/testthat/out/multi-38.txt @@ -1,5 +1,5 @@ column_zero_one col_02 col_03 col_… -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-39.txt b/tests/testthat/out/multi-39.txt index a4f3a4425..371414695 100644 --- a/tests/testthat/out/multi-39.txt +++ b/tests/testthat/out/multi-39.txt @@ -1,5 +1,5 @@ column_zero_one col_02 col_03 col_04 -  +  1 1.23 a a a 2 2.23 b b b 3 3.23 c c c diff --git a/tests/testthat/out/multi-extra-10.txt b/tests/testthat/out/multi-extra-10.txt index 3fdcdbaa0..f08b0dd72 100644 --- a/tests/testthat/out/multi-extra-10.txt +++ b/tests/testthat/out/multi-extra-10.txt @@ -1,3 +1,3 @@ -col_02  -col_03  -col_04  +col_02  +col_03  +col_04  diff --git a/tests/testthat/out/numeric-04.txt b/tests/testthat/out/numeric-04.txt index 996a7528c..16ae16d63 100644 --- a/tests/testthat/out/numeric-04.txt +++ b/tests/testthat/out/numeric-04.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-07.txt b/tests/testthat/out/numeric-07.txt index 996a7528c..16ae16d63 100644 --- a/tests/testthat/out/numeric-07.txt +++ b/tests/testthat/out/numeric-07.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-10.txt b/tests/testthat/out/numeric-10.txt index a1b47b2d5..86427b88a 100644 --- a/tests/testthat/out/numeric-10.txt +++ b/tests/testthat/out/numeric-10.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-15.txt b/tests/testthat/out/numeric-15.txt index d5f9c49d3..b920e7d9e 100644 --- a/tests/testthat/out/numeric-15.txt +++ b/tests/testthat/out/numeric-15.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/numeric-22.txt b/tests/testthat/out/numeric-22.txt index 4b9828eb4..5ca6a65f8 100644 --- a/tests/testthat/out/numeric-22.txt +++ b/tests/testthat/out/numeric-22.txt @@ -1,4 +1,4 @@ -  +  0.00000000100 0.00000100 1000 diff --git a/tests/testthat/out/ordered.txt b/tests/testthat/out/ordered.txt index 92859947c..a38d4e3c5 100644 --- a/tests/testthat/out/ordered.txt +++ b/tests/testthat/out/ordered.txt @@ -1,4 +1,4 @@ - + a b c diff --git a/tests/testthat/out/rowid-3.txt b/tests/testthat/out/rowid-3.txt index 3e892644f..f68fd2e83 100644 --- a/tests/testthat/out/rowid-3.txt +++ b/tests/testthat/out/rowid-3.txt @@ -1,4 +1,4 @@ -  +  1 2 3 diff --git a/tests/testthat/out/rowid-star-title-12.txt b/tests/testthat/out/rowid-star-title-12.txt index 159fdc08a..13bad482d 100644 --- a/tests/testthat/out/rowid-star-title-12.txt +++ b/tests/testthat/out/rowid-star-title-12.txt @@ -1,5 +1,5 @@ - * + *  1  2  3 diff --git a/tests/testthat/out/scientific-short-neg.txt b/tests/testthat/out/scientific-short-neg.txt index 099df56c9..a0da0c449 100644 --- a/tests/testthat/out/scientific-short-neg.txt +++ b/tests/testthat/out/scientific-short-neg.txt @@ -1,4 +1,4 @@ -  +  - 1.00e ³ 1.00e ⁹ - 1.00e¹⁵ diff --git a/tests/testthat/out/scientific.txt b/tests/testthat/out/scientific.txt index 6b8f723df..d02b74f0d 100644 --- a/tests/testthat/out/scientific.txt +++ b/tests/testthat/out/scientific.txt @@ -1,4 +1,4 @@ -  +  1.00e⁻⁹ 1.00e⁻⁶ 1.00e⁺³ diff --git a/tests/testthat/out/spaces.txt b/tests/testthat/out/spaces.txt new file mode 100644 index 000000000..37988341b --- /dev/null +++ b/tests/testthat/out/spaces.txt @@ -0,0 +1,7 @@ + +"" +" " +" a" +"a " +a b +NA diff --git a/tests/testthat/out/tibble-all--300-20.txt b/tests/testthat/out/tibble-all--300-20.txt index 82ff377e9..ad51ef13b 100644 --- a/tests/testthat/out/tibble-all--300-20.txt +++ b/tests/testthat/out/tibble-all--300-20.txt @@ -3,3 +3,13 @@ 1 1.00 1 T 2 2.50 2 F 3 NA NA NA + d e + +1 a a +2 b b +3 + f + +1 2015-12-10 +2 2015-12-11 +3 NA diff --git a/tests/testthat/out/tibble-all--300-30.txt b/tests/testthat/out/tibble-all--300-30.txt index e16955bf3..f6ad4bfb3 100644 --- a/tests/testthat/out/tibble-all--300-30.txt +++ b/tests/testthat/out/tibble-all--300-30.txt @@ -3,3 +3,18 @@ 1 1.00 1 T a 2 2.50 2 F b 3 NA NA NA + e f + +1 a 2015-12-10 +2 b 2015-12-11 +3 NA + g + +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 +3 NA + h i + +1 +2 +3 diff --git a/tests/testthat/out/tibble-all--300-40.txt b/tests/testthat/out/tibble-all--300-40.txt index fcf375072..590abad60 100644 --- a/tests/testthat/out/tibble-all--300-40.txt +++ b/tests/testthat/out/tibble-all--300-40.txt @@ -3,3 +3,13 @@ 1 1.00 1 T a a 2 2.50 2 F b b 3 NA NA NA + f g + +1 2015-12-10 2015-12-09 10:51:35 +2 2015-12-11 2015-12-09 10:51:36 +3 NA NA + h i + +1 +2 +3 diff --git a/tests/testthat/out/tibble-all--300-50.txt b/tests/testthat/out/tibble-all--300-50.txt index d58565c36..29e958a8d 100644 --- a/tests/testthat/out/tibble-all--300-50.txt +++ b/tests/testthat/out/tibble-all--300-50.txt @@ -3,3 +3,8 @@ 1 1.00 1 T a a 2015-12-10 2 2.50 2 F b b 2015-12-11 3 NA NA NA NA + g h i + +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 +3 NA diff --git a/tests/testthat/out/tibble-all--300-60.txt b/tests/testthat/out/tibble-all--300-60.txt index d58565c36..29e958a8d 100644 --- a/tests/testthat/out/tibble-all--300-60.txt +++ b/tests/testthat/out/tibble-all--300-60.txt @@ -3,3 +3,8 @@ 1 1.00 1 T a a 2015-12-10 2 2.50 2 F b b 2015-12-11 3 NA NA NA NA + g h i + +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 +3 NA diff --git a/tests/testthat/out/tibble-all--300-70.txt b/tests/testthat/out/tibble-all--300-70.txt index 1fa36383c..9ce4781ed 100644 --- a/tests/testthat/out/tibble-all--300-70.txt +++ b/tests/testthat/out/tibble-all--300-70.txt @@ -1,5 +1,10 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 NA NA +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 +3 NA NA NA NA NA + h i + +1 +2 +3 diff --git a/tests/testthat/out/tibble-all--300.txt b/tests/testthat/out/tibble-all--300.txt index 99be3ae7d..7c1a5a2b8 100644 --- a/tests/testthat/out/tibble-all--300.txt +++ b/tests/testthat/out/tibble-all--300.txt @@ -1,5 +1,10 @@ - a b c d e f g h i - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 NA NA +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 +3 NA NA NA NA NA + i + +1 +2 +3 diff --git a/tests/testthat/out/time-posix.txt b/tests/testthat/out/time-posix.txt index 0962b83ed..8c1e3a097 100644 --- a/tests/testthat/out/time-posix.txt +++ b/tests/testthat/out/time-posix.txt @@ -1,3 +1,3 @@ -  +  2017-07-28 18:04:35 NA diff --git a/tests/testthat/out/time.txt b/tests/testthat/out/time.txt index 31d6a9547..62146fd53 100644 --- a/tests/testthat/out/time.txt +++ b/tests/testthat/out/time.txt @@ -1,3 +1,3 @@ -  +  2017-07-28 18:04:35 NA diff --git a/tests/testthat/out/title-crayon.txt b/tests/testthat/out/title-crayon.txt index a9c055390..42aa799ea 100644 --- a/tests/testthat/out/title-crayon.txt +++ b/tests/testthat/out/title-crayon.txt @@ -1,5 +1,5 @@ crayon -  +  10.0 100 1000 diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index ee3070ab3..42d4ad087 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -8,4 +8,5 @@ test_that("output test", { expect_pillar_output("\u6210\u4ea4\u65e5", title = "\u6210\u4ea4", filename = "deal1.txt") expect_pillar_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") expect_pillar_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") + expect_pillar_output(c("", " ", " a", "a ", "a b"), width = 5, filename = "spaces.txt") }) From 065c65811afee78903b94d3b4640a282a5d04cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 4 Oct 2017 23:34:14 +0200 Subject: [PATCH 068/133] fix knit_print --- R/multi.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/R/multi.R b/R/multi.R index ef80c95cc..0b29a3d9d 100644 --- a/R/multi.R +++ b/R/multi.R @@ -110,6 +110,10 @@ print.squeezed_colonnade <- function(x, ...) { # Method registration happens in .onLoad() knit_print.squeezed_colonnade <- function(x, ...) { + unlist(map(x, knit_print_squeezed_colonnade_tier)) +} + +knit_print_squeezed_colonnade_tier <- function(x) { header <- map_chr(x, `[[`, "title_format") col <- map(x, function(xx) c(xx[["type_format"]], xx[["data_format"]])) From 85d70a2edfa59c0e23ba8e3dbd5c569ff5be709f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 10:50:38 +0200 Subject: [PATCH 069/133] add missing methods --- API | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/API b/API index fcffec89d..3cd692598 100644 --- a/API +++ b/API @@ -22,6 +22,16 @@ type_sum(x) ## S3 methods extra_cols.squeezed_colonnade(x, ...) +format.colonnade(x, ...) +format.pillar(x, width = NULL, ...) +format.pillar_decimal(x, width, ...) +format.pillar_shaft(x, width, ...) +format.pillar_title(x, width, ...) +format.pillar_type(x, width = NULL, ...) +format.rif_data(x, width, ...) +format.rif_title(x, width, ...) +format.rif_type(x, width = NULL, ...) +format.squeezed_colonnade(x, ...) is_vector_s3.Date(x) is_vector_s3.POSIXct(x) is_vector_s3.data.frame(x) @@ -39,6 +49,13 @@ pillar_shaft.default(x, ...) pillar_shaft.list(x, ...) pillar_shaft.logical(x, ...) pillar_shaft.numeric(x, ..., sigfig = 3) +print.colonnade(x, ...) +print.pillar(x, ...) +print.pillar_shaft(x, ...) +print.pillar_vertical(x, ...) +print.rif_data(x, ...) +print.spark(x, ...) +print.squeezed_colonnade(x, ...) type_sum.Date(x) type_sum.POSIXct(x) type_sum.data.frame(x) From e5df528184b2fb57bc8fd95ac24c75a4539adc47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 11:16:47 +0200 Subject: [PATCH 070/133] new_ornament(), closes #46, CC @hadley - New `new_ornament()` for use by classes that define pillars with variable width (#46). --- API | 3 ++- NAMESPACE | 3 ++- R/col-data.R | 4 ++-- R/column.R | 26 ++++++++++++++++++++------ R/rowid-data.R | 2 +- R/sigfig.R | 4 ++-- man/new_ornament.Rd | 23 +++++++++++++++++++++++ 7 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 man/new_ornament.Rd diff --git a/API b/API index 3cd692598..dcc586903 100644 --- a/API +++ b/API @@ -7,6 +7,7 @@ dim_desc(x) expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, ...), crayon = TRUE, output_width = 80L) extra_cols(x, ...) is_vector_s3(x) +new_ornament(x, width = NULL, align = NULL) new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), 0L), align = "left", min_width = NULL, na_indent = 0L) obj_sum(x) pillar(x, title = NULL, width = NULL, ...) @@ -24,8 +25,8 @@ type_sum(x) extra_cols.squeezed_colonnade(x, ...) format.colonnade(x, ...) format.pillar(x, width = NULL, ...) -format.pillar_decimal(x, width, ...) format.pillar_shaft(x, width, ...) +format.pillar_shaft_decimal(x, width, ...) format.pillar_title(x, width, ...) format.pillar_type(x, width = NULL, ...) format.rif_data(x, width, ...) diff --git a/NAMESPACE b/NAMESPACE index 87862a47f..9ee61abe7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,8 +3,8 @@ S3method(extra_cols,squeezed_colonnade) S3method(format,colonnade) S3method(format,pillar) -S3method(format,pillar_decimal) S3method(format,pillar_shaft) +S3method(format,pillar_shaft_decimal) S3method(format,pillar_title) S3method(format,pillar_type) S3method(format,rif_data) @@ -48,6 +48,7 @@ export(dim_desc) export(expect_pillar_output) export(extra_cols) export(is_vector_s3) +export(new_ornament) export(new_pillar_shaft) export(obj_sum) export(pillar) diff --git a/R/col-data.R b/R/col-data.R index 7c4707874..09afdacfa 100644 --- a/R/col-data.R +++ b/R/col-data.R @@ -20,7 +20,7 @@ format.pillar_shaft <- function(x, width, ...) { } data[is.na(x)] <- paste0(strrep(" ", attr(x, "na_indent")), pillar_na()) - new_column(data, width = width, align = align) + new_ornament(data, width = width, align = align) } #' @export @@ -72,7 +72,7 @@ pillar_shaft.numeric <- function(x, ..., sigfig = 3) { ret <- structure( list(dec = dec, sci = sci), - class = c("pillar_decimal", "pillar_shaft") + class = c("pillar_shaft_decimal", "pillar_shaft") ) ret <- set_width(ret, get_width(ret$dec)) diff --git a/R/column.R b/R/column.R index bf93b5c91..70dd66a5e 100644 --- a/R/column.R +++ b/R/column.R @@ -1,16 +1,30 @@ -new_column <- function(row, width = NULL, align = NULL) { - ret <- structure( - row, +#' Helper to define the contents of a pillar +#' +#' This function is useful if your data renders differently depending on the +#' available width. In this case, implement the [pillar_shaft()] method for your +#' class to return a subclass of "pillar_shaft" and have the [format()] method +#' for this subclass call `new_ornament()`. See the implementation of +#' `pillar_shaft.numeric()` and `format.pillar_shaft_decimal()` for an example. +#' +#' @param x A character vector with formatting, see [crayon] +#' @param width An optional width of the resulting pillar, computed from `x` if +#' missing +#' @param align Alignment, passed on to [crayon::col_align()] +#' +#' @export +new_ornament <- function(x, width = NULL, align = NULL) { + ret <- new_vertical( + x, align = align, - class = c("pillar_column", "pillar_vertical") + extra_class = "pillar_ornament" ) ret <- set_width(ret, width) ret } -new_vertical <- function(row, ..., extra_class = NULL) { +new_vertical <- function(x, ..., extra_class = NULL) { ret <- structure( - row, + x, ..., class = c(extra_class, "pillar_vertical") ) diff --git a/R/rowid-data.R b/R/rowid-data.R index 95c554fbc..9ae9e2d7b 100644 --- a/R/rowid-data.R +++ b/R/rowid-data.R @@ -13,7 +13,7 @@ rif_data <- function(n, ...) { #' @export format.rif_data <- function(x, width, ...) { - new_column( + new_ornament( style_rowid(format(seq_len(x$n), width = width)), width = width, align = "right" diff --git a/R/sigfig.R b/R/sigfig.R index b6d3e94c4..58adb9120 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -180,7 +180,7 @@ assemble_decimal <- function(x) { } #' @export -format.pillar_decimal <- function(x, width, ...) { +format.pillar_shaft_decimal <- function(x, width, ...) { if (length(x$dec$num) == 0L) return(character()) if (width < get_min_width(x)) { @@ -198,5 +198,5 @@ format.pillar_decimal <- function(x, width, ...) { used_width <- max(crayon::col_nchar(row, type = "width"), 0L) row <- paste0(strrep(" ", width - used_width), row) - new_column(row, width = width, align = "right") + new_ornament(row, width = width, align = "right") } diff --git a/man/new_ornament.Rd b/man/new_ornament.Rd new file mode 100644 index 000000000..b88112eaf --- /dev/null +++ b/man/new_ornament.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/column.R +\name{new_ornament} +\alias{new_ornament} +\title{Helper to define the contents of a pillar} +\usage{ +new_ornament(x, width = NULL, align = NULL) +} +\arguments{ +\item{x}{A character vector with formatting, see \link{crayon}} + +\item{width}{An optional width of the resulting pillar, computed from \code{x} if +missing} + +\item{align}{Alignment, passed on to \code{\link[crayon:col_align]{crayon::col_align()}}} +} +\description{ +This function is useful if your data renders differently depending on the +available width. In this case, implement the \code{\link[=pillar_shaft]{pillar_shaft()}} method for your +class to return a subclass of "pillar_shaft" and have the \code{\link[=format]{format()}} method +for this subclass call \code{new_ornament()}. See the implementation of +\code{pillar_shaft.numeric()} and \code{format.pillar_shaft_decimal()} for an example. +} From df8b39e01770ca52476a94b45831e9c0d2a1c7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 11:29:01 +0200 Subject: [PATCH 071/133] support infinite width - Colonnades now can have infinite widths, in this case as many tiers as needed are shown (tidyverse/tibble#322). --- R/multi.R | 17 +++++++++++------ R/width.R | 6 +++++- tests/testthat/out/multi-inf.txt | 5 +++++ tests/testthat/test-format_multi.R | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 tests/testthat/out/multi-inf.txt diff --git a/R/multi.R b/R/multi.R index 0b29a3d9d..f2c69c7cb 100644 --- a/R/multi.R +++ b/R/multi.R @@ -163,7 +163,7 @@ colonnade_get_width <- function(x, width, rowid_width) { #' or its maximum width). More than one tier may be created if #' `width > getOption("width")`, in this case each tier is at most #' `getOption("width")` characters wide. - tier_widths <- get_tier_widths(width, rowid_width) + tier_widths <- get_tier_widths(width, length(x), rowid_width) col_widths_df <- colonnade_compute_tiered_col_widths_df(col_df, tier_widths) #' Remaining space is then distributed proportionally to pillars that do not @@ -171,11 +171,16 @@ colonnade_get_width <- function(x, width, rowid_width) { colonnade_distribute_space_df(col_widths_df, tier_widths) } -get_tier_widths <- function(width, rowid_width, tier_width = getOption("width")) { - pos <- c( - seq(0, width - 1, by = tier_width), - width - ) +get_tier_widths <- function(width, ncol, rowid_width, tier_width = getOption("width")) { + if (is.finite(width)) { + pos <- c( + seq(0, width - 1, by = tier_width), + width + ) + } else { + pos <- seq(0, length.out = ncol, by = tier_width) + } + widths <- diff(pos) - rowid_width widths[widths >= 1] } diff --git a/R/width.R b/R/width.R index de8ec3522..24b47a3c2 100644 --- a/R/width.R +++ b/R/width.R @@ -4,7 +4,11 @@ get_width <- function(x) { set_width <- function(x, width) { if (is.null(width)) return(x) - attr(x, "width") <- as.integer(width) + if (is.infinite(width)) { + attr(x, "width") <- NA_integer_ + } else { + attr(x, "width") <- as.integer(width) + } x } diff --git a/tests/testthat/out/multi-inf.txt b/tests/testthat/out/multi-inf.txt new file mode 100644 index 000000000..371414695 --- /dev/null +++ b/tests/testthat/out/multi-inf.txt @@ -0,0 +1,5 @@ + column_zero_one col_02 col_03 col_04 +  +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 317f72460..88e86d363 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -38,6 +38,7 @@ test_that("output test", { expect_pillar_output(xf = colonnade(x, width = 37), filename = "multi-37.txt") expect_pillar_output(xf = colonnade(x, width = 38), filename = "multi-38.txt") expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") + expect_pillar_output(xf = colonnade(x, width = Inf), filename = "multi-inf.txt") expect_pillar_output( xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), From 6f9322480aeaf65aa04af62d6b7a686681f4ee3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 14:04:42 +0200 Subject: [PATCH 072/133] rename --- R/{column.R => ornament.R} | 0 R/{colformat.R => pillar.R} | 0 R/{col-data.R => shaft.R} | 0 R/{col-title.R => title.R} | 0 R/{col-type.R => type.R} | 0 man/new_ornament.Rd | 2 +- man/pillar.Rd | 2 +- man/pillar_shaft.Rd | 2 +- 8 files changed, 3 insertions(+), 3 deletions(-) rename R/{column.R => ornament.R} (100%) rename R/{colformat.R => pillar.R} (100%) rename R/{col-data.R => shaft.R} (100%) rename R/{col-title.R => title.R} (100%) rename R/{col-type.R => type.R} (100%) diff --git a/R/column.R b/R/ornament.R similarity index 100% rename from R/column.R rename to R/ornament.R diff --git a/R/colformat.R b/R/pillar.R similarity index 100% rename from R/colformat.R rename to R/pillar.R diff --git a/R/col-data.R b/R/shaft.R similarity index 100% rename from R/col-data.R rename to R/shaft.R diff --git a/R/col-title.R b/R/title.R similarity index 100% rename from R/col-title.R rename to R/title.R diff --git a/R/col-type.R b/R/type.R similarity index 100% rename from R/col-type.R rename to R/type.R diff --git a/man/new_ornament.Rd b/man/new_ornament.Rd index b88112eaf..0035c59c6 100644 --- a/man/new_ornament.Rd +++ b/man/new_ornament.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/column.R +% Please edit documentation in R/ornament.R \name{new_ornament} \alias{new_ornament} \title{Helper to define the contents of a pillar} diff --git a/man/pillar.Rd b/man/pillar.Rd index 952ded227..5d015903e 100644 --- a/man/pillar.Rd +++ b/man/pillar.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/colformat.R +% Please edit documentation in R/pillar.R \name{pillar} \alias{pillar} \title{Format a vector suitable for tabular display} diff --git a/man/pillar_shaft.Rd b/man/pillar_shaft.Rd index 6459be894..46c9f2e59 100644 --- a/man/pillar_shaft.Rd +++ b/man/pillar_shaft.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/col-data.R +% Please edit documentation in R/shaft.R \name{pillar_shaft} \alias{pillar_shaft} \alias{new_pillar_shaft} From 86b1a9639365d48e8716150c5a82a0131ba6d3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 14:28:01 +0200 Subject: [PATCH 073/133] unexport spark_bar() and spark_line(), #47 --- API | 2 -- NAMESPACE | 2 -- R/spark-bar.R | 4 ++-- R/spark-line.R | 3 ++- man/spark_bar.Rd | 2 ++ man/spark_line.Rd | 2 ++ 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/API b/API index dcc586903..3a0c8e586 100644 --- a/API +++ b/API @@ -12,8 +12,6 @@ new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), 0L), obj_sum(x) pillar(x, title = NULL, width = NULL, ...) pillar_shaft(x, ...) -spark_bar(x, safe = TRUE) -spark_line(x) squeeze(x, width = NULL, ...) style_neg(x) style_num(x, negative, significant = rep_along(x, TRUE)) diff --git a/NAMESPACE b/NAMESPACE index 9ee61abe7..9ae5d7976 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -53,8 +53,6 @@ export(new_pillar_shaft) export(obj_sum) export(pillar) export(pillar_shaft) -export(spark_bar) -export(spark_line) export(squeeze) export(style_neg) export(style_num) diff --git a/R/spark-bar.R b/R/spark-bar.R index 402653250..46aeb7471 100644 --- a/R/spark-bar.R +++ b/R/spark-bar.R @@ -4,13 +4,12 @@ #' In most common fixed width fonts these are rendered wider than regular #' characters which means they are not suitable if you need precise alignment. #' -#' @export #' @param x A numeric vector between 0 and 1 #' @param safe Nominally there are 8 block elements from 1/8 height to full #' height (8/8). However, the half-height and full-height blocks appear #' to be rendered inconsistently (possibly due to font substitution). -#' @export #' @examples +#' \dontrun{ #' x <- seq(0, 1, length = 6) #' spark_bar(x) #' spark_bar(sample(x)) @@ -19,6 +18,7 @@ #' spark_bar(seq(0, 1, length = 8), safe = FALSE) #' #' spark_bar(c(0, NA, 0.5, NA, 1)) +#' } spark_bar <- function(x, safe = TRUE) { stopifnot(is.numeric(x)) diff --git a/R/spark-line.R b/R/spark-line.R index 299a64a1a..579920a35 100644 --- a/R/spark-line.R +++ b/R/spark-line.R @@ -1,10 +1,11 @@ #' Draw a sparkline line graph with Braille characters. #' #' @inheritParams spark_bar -#' @export #' @examples +#' \dontrun{ #' x <- seq(0, 1, length = 10) #' spark_line(x) +#' } spark_line <- function(x) { stopifnot(is.numeric(x)) diff --git a/man/spark_bar.Rd b/man/spark_bar.Rd index e499dcd29..f82e5a830 100644 --- a/man/spark_bar.Rd +++ b/man/spark_bar.Rd @@ -19,6 +19,7 @@ In most common fixed width fonts these are rendered wider than regular characters which means they are not suitable if you need precise alignment. } \examples{ +\dontrun{ x <- seq(0, 1, length = 6) spark_bar(x) spark_bar(sample(x)) @@ -28,3 +29,4 @@ spark_bar(seq(0, 1, length = 8), safe = FALSE) spark_bar(c(0, NA, 0.5, NA, 1)) } +} diff --git a/man/spark_line.Rd b/man/spark_line.Rd index ab2f45abf..e035ad5c5 100644 --- a/man/spark_line.Rd +++ b/man/spark_line.Rd @@ -13,6 +13,8 @@ spark_line(x) Draw a sparkline line graph with Braille characters. } \examples{ +\dontrun{ x <- seq(0, 1, length = 10) spark_line(x) } +} From 74badb9ccba9cc6c943f8c0efc87e2952f8a5eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 20:40:43 +0200 Subject: [PATCH 074/133] formatting --- R/shaft.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/shaft.R b/R/shaft.R index 09afdacfa..a157b7547 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -35,9 +35,9 @@ print.pillar_shaft <- function(x, ...) { #' @param na_indent Indention of `NA` values. #' @rdname pillar_shaft new_pillar_shaft <- function(x, ..., - width = max(crayon::col_nchar(x, type = "width"), 0L), - align = "left", min_width = NULL, - na_indent = 0L) { + width = max(crayon::col_nchar(x, type = "width"), 0L), + align = "left", min_width = NULL, + na_indent = 0L) { ret <- structure( x, align = align, From be24a37da3e36142ad52d60c3d0bad8bb5279048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 21:17:42 +0200 Subject: [PATCH 075/133] special-case zero values in scientific notation, closes #58 --- R/sigfig.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/sigfig.R b/R/sigfig.R index 58adb9120..bf63ea733 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -33,9 +33,9 @@ split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { exp <- compute_exp(abs_x) if (scientific) { - mnt <- ifelse(num, abs_x * 10 ^ (-exp), abs_x) + mnt <- ifelse(num & abs_x != 0, abs_x * 10 ^ (-exp), abs_x) round_x <- safe_signif(mnt, sigfig) - rhs_digits <- ifelse(num, sigfig - 1, 0) + rhs_digits <- ifelse(num & abs_x != 0, sigfig - 1, 0) exp_display <- exp } else { round_x <- safe_signif(abs_x, pmax(sigfig, exp + 1, na.rm = TRUE)) From 21ac8fcb2725068c6514063257f7de223efe6abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 25 Oct 2017 23:05:33 +0200 Subject: [PATCH 076/133] Vignette that describes pillar support for custom data types (#59) * export functions needed for extension * add vignette --- .gitignore | 1 + API | 3 + DESCRIPTION | 5 +- NAMESPACE | 3 + R/styles.R | 4 +- R/width.R | 10 ++ man/set_width.Rd | 19 ++++ man/style_subtle.Rd | 3 + vignettes/extending.Rmd | 239 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 man/set_width.Rd create mode 100644 vignettes/extending.Rmd diff --git a/.gitignore b/.gitignore index 807ea2517..09a72cbe9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .Rproj.user .Rhistory .RData +inst/doc diff --git a/API b/API index 3a0c8e586..ab1dedbbe 100644 --- a/API +++ b/API @@ -12,7 +12,10 @@ new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), 0L), obj_sum(x) pillar(x, title = NULL, width = NULL, ...) pillar_shaft(x, ...) +set_min_width(x, min_width) +set_width(x, width) squeeze(x, width = NULL, ...) +style_na(x) style_neg(x) style_num(x, negative, significant = rep_along(x, TRUE)) style_subtle(x) diff --git a/DESCRIPTION b/DESCRIPTION index 984a9c2aa..dd5c5f39c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -18,9 +18,12 @@ Imports: rlang Suggests: knitr, - testthat + rmarkdown, + testthat, + tibble Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi::api_roclet")) RoxygenNote: 6.0.1 Remotes: r-lib/crayon, hadley/testthat +VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index 9ae5d7976..6f0a677da 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -53,7 +53,10 @@ export(new_pillar_shaft) export(obj_sum) export(pillar) export(pillar_shaft) +export(set_min_width) +export(set_width) export(squeeze) +export(style_na) export(style_neg) export(style_num) export(style_subtle) diff --git a/R/styles.R b/R/styles.R index 31f48726e..62c9c452f 100644 --- a/R/styles.R +++ b/R/styles.R @@ -26,12 +26,14 @@ style_spark_na <- function(x) { crayon::yellow(x) } +#' @rdname style_subtle +#' @export style_na <- function(x) { crayon::bgYellow(crayon::black(x)) } -#' @export #' @rdname style_subtle +#' @export style_neg <- keep_empty(function(x) { crayon::red(x) }) diff --git a/R/width.R b/R/width.R index 24b47a3c2..27929f5e0 100644 --- a/R/width.R +++ b/R/width.R @@ -2,6 +2,14 @@ get_width <- function(x) { attr(x, "width") } +#' Set width and minimum width information for an object +#' +#' Returns the input with updated `width` or `min_width` attributes. +#' +#' @param x Input to which assign a width or minimum width +#' @param width,min_width The new width +#' +#' @export set_width <- function(x, width) { if (is.null(width)) return(x) if (is.infinite(width)) { @@ -20,6 +28,8 @@ get_min_width <- function(x) { attr(x, "min_width") %||% get_width(x) } +#' @rdname set_width +#' @export set_min_width <- function(x, min_width) { if (is.null(min_width)) return(x) attr(x, "min_width") <- as.integer(min_width) diff --git a/man/set_width.Rd b/man/set_width.Rd new file mode 100644 index 000000000..1f3551b48 --- /dev/null +++ b/man/set_width.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/width.R +\name{set_width} +\alias{set_width} +\alias{set_min_width} +\title{Set width and minimum width information for an object} +\usage{ +set_width(x, width) + +set_min_width(x, min_width) +} +\arguments{ +\item{x}{Input to which assign a width or minimum width} + +\item{width, min_width}{The new width} +} +\description{ +Returns the input with updated \code{width} or \code{min_width} attributes. +} diff --git a/man/style_subtle.Rd b/man/style_subtle.Rd index 39759abf4..e20565dfa 100644 --- a/man/style_subtle.Rd +++ b/man/style_subtle.Rd @@ -3,6 +3,7 @@ \name{style_num} \alias{style_num} \alias{style_subtle} +\alias{style_na} \alias{style_neg} \title{Styling helpers} \usage{ @@ -10,6 +11,8 @@ style_num(x, negative, significant = rep_along(x, TRUE)) style_subtle(x) +style_na(x) + style_neg(x) } \arguments{ diff --git a/vignettes/extending.Rmd b/vignettes/extending.Rmd new file mode 100644 index 000000000..5138fbb4a --- /dev/null +++ b/vignettes/extending.Rmd @@ -0,0 +1,239 @@ +--- +title: "Adding pillar support to a datatype" +author: "Kirill Müller" +date: "`r Sys.Date()`" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Vignette Title} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +This short vignette assumes an S3 class that encapsulates a vector. It demonstrates how to extend this class so that it renders nicely in a tibble. + +## Prerequisites + +We define a class `"latlon"` that encodes geographic coordinates in a complex number. For simplicity, the values are printed as hours and minutes only. + +```{r} +latlon <- function(lat, lon) { + as_latlon(complex(real = lon, imaginary = lat)) +} + +as_latlon <- function(x) { + structure(x, class = "latlon") +} + +`[.latlon` <- function(x, i) { + as_latlon(NextMethod()) +} + +format.latlon <- function(x, ..., formatter = deg_rad) { + lat <- Im(x) + lon <- Re(x) + paste(formatter(Im(x), c("N", "S")), formatter(Re(x), c("E", "W"))) +} + +deg_rad <- function(x, pm) { + sign <- sign(x) + x <- abs(x) + deg <- trunc(x) + x <- x - deg + rad <- round(x * 60) + ret <- sprintf("%d°%.2d'%s", deg, rad, pm[ifelse(sign >= 0, 1, 2)]) + ret[is.na(x)] <- "" + format(ret, justify = "right") +} + +print.latlon <- function(x, ...) { + cat(format(x), sep = "\n") + invisible(x) +} + +latlon(32.7102978, -117.1704058) +``` + +More methods are needed to make this class fully compatible with data frames, see e.g. the [hms](https://github.com/tidyverse/hms/) package for a more complete example. + + +## Using in a tibble + +Columns on this class can be used in a tibble right away, but the output will be less than ideal: + +```{r} +library(tibble) +data <- tibble( + venue = "rstudio::conf", + year = 2018:2019, + loc = latlon(c(32.7102978, NA), c(-117.1704058, NA)) +) + +data +``` + +The output has two main problems: + +1. The column type is displayed as ``. While this is technically correct, we might like a shorter representation. +1. The values are formatted as complex numbers (the underlying storage), without using the `format()` method we have defined. This is by design. + +In the remainder I'll show how to fix both problems, and also how to implement rendering that adapts to the available width. + + +## Fixing the data type + +To display `` as data type, we need to override the `type_sum()` method: + +```{r} +type_sum.latlon <- function(x) { + "geo" +} +``` + +Because the value shown there doesn't depend on the data, we just return a constant. (For date-times, the column info will eventually contain information about the timezone, see [#53](https://github.com/r-lib/pillar/pull/53).) + +```{r} +data +``` + + +## Rendering the value + +To use our format method for rendering, we implement the `pillar_shaft()` method for our class. (A [*pillar*](https://en.wikipedia.org/wiki/Column#Nomenclature) is mainly a *shaft* (decorated with an *ornament*), with a *capital* above and a *base* below. Multiple pillars form a *colonnade*, which can be stacked in multiple *tiers*. This is the motivation behind the names in our API.) + +```{r} +library(pillar) +pillar_shaft.latlon <- function(x, ...) { + out <- format(x) + out[is.na(x)] <- NA + new_pillar_shaft(out, align = "right") +} +``` + +The simplest variant calls our `format()` method, everything else is handled by pillar, in particular by the `new_pillar_shaft()` helper. Note how the `align` argument affects the alignment of NA values and of the column name and type. + +```{r} +data +``` + +We could also use left alignment and indent only the `NA` values: + +```{r} +pillar_shaft.latlon <- function(x, ...) { + out <- format(x) + out[is.na(x)] <- NA + new_pillar_shaft(out, align = "left", na_indent = 5) +} + +data +``` + + +## Adaptive rendering + +If there is not enough space to render the values, the formatted values are truncated with an ellipsis. This doesn't currently apply to our class, because we haven't specified a minimum width for our values: + +```{r} +print(data, width = 35) +``` + +If we specify a minimum width when constructing the shaft, the `loc` column will be truncated: + +```{r} +pillar_shaft.latlon <- function(x, ...) { + out <- format(x) + out[is.na(x)] <- NA + new_pillar_shaft(out, align = "right", min_width = 10) +} + +print(data, width = 35) +``` + +This may be useful for character data, but for lat-lon data we may prefer to show full degrees and remove the minutes if the available space is not enough to show accurate values. A more sophisticated implementation of the `pillar_shaft()` method is required to achieve this: + +```{r} +pillar_shaft.latlon <- function(x, ...) { + deg <- format(x, formatter = deg) + deg[is.na(x)] <- style_na("NA") + deg_rad <- format(x) + deg_rad[is.na(x)] <- style_na("NA") + ret <- structure( + list(deg = deg, deg_rad = deg_rad), + class = c("pillar_shaft_latlon", "pillar_shaft") + ) + ret <- set_width(ret, max(crayon::col_nchar(deg_rad), 0)) + ret <- set_min_width(ret, max(crayon::col_nchar(deg), 0)) + ret +} +``` + +Here, `pillar_shaft()` returns an object of the `"pillar_shaft_latlon"` class (which is also a `"pillar_shaft"`) that contains the necessary information to render the values, and also minimum and maximum width values. For simplicity, both formattings are pre-rendered, and the minimum and maximum widths are computed from there. Note that we also need to take care of `NA` values explicitly. (`crayon::col_nchar()` is like `nchar()` but strips the formatting added by `style_na()`.) + +For completeness, the code that implements the degree-only formatting looks like this: + +```{r} +deg <- function(x, pm) { + sign <- sign(x) + x <- abs(x) + deg <- round(x) + ret <- sprintf("%d°%s", deg, pm[ifelse(sign >= 0, 1, 2)]) + ret[is.na(x)] <- "" + format(ret, justify = "right") +} +``` + +All that's left to do is to implement a `format()` method for our new `"pillar_shaft_latlon"` class. This method will be called with a `width` argument, which then determines which of the formattings to choose: + +```{r} +format.pillar_shaft_latlon <- function(x, width, ...) { + if (all(crayon::col_nchar(x$deg_rad) <= width)) { + ornament <- x$deg_rad + } else { + ornament <- x$deg + } + + new_ornament(ornament) +} + +data +print(data, width = 35) +``` + + +## Adding color + +Both `new_pillar_shaft()` and `new_ornament()` accept escape codes for coloring, emphasis, or other ways of highlighting text on terminals that support it. Some formattings are predefined, e.g. `style_subtle()` displays text in a light gray. For default data types, this style is used for insignificant digits. We'll be formatting the degree and minute signs in a subtle style, because they serve only as separators. You can also use the [crayon](https://cran.r-project.org/package=crayon) package to add custom formattings to your text. + +```{r} +pillar_shaft.latlon <- function(x, ...) { + out <- format(x, formatter = deg_rad_color) + out[is.na(x)] <- NA + new_pillar_shaft(out, align = "left", na_indent = 5) +} + +deg_rad_color <- function(x, pm) { + sign <- sign(x) + x <- abs(x) + deg <- trunc(x) + x <- x - deg + rad <- round(x * 60) + ret <- sprintf( + "%d%s%.2d%s%s", + deg, + style_subtle("°"), + rad, + style_subtle("'"), + pm[ifelse(sign >= 0, 1, 2)] + ) + ret[is.na(x)] <- "" + format(ret, justify = "right") +} + +data +``` + +Currently, ANSI escapes are not rendered in vignettes, so the display here isn't much different from earlier examples. This may change in the future. + + +## In a package + +Remember to use namespace imports instead of `library()` calls in package code! From e596022bd818fcde0a2cdee2ae236f3e3285afda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 31 Oct 2017 22:11:14 +0100 Subject: [PATCH 077/133] work around missing strrep() --- R/strrep.R | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 R/strrep.R diff --git a/R/strrep.R b/R/strrep.R new file mode 100644 index 000000000..2d8f07d59 --- /dev/null +++ b/R/strrep.R @@ -0,0 +1,8 @@ +if (getRversion() < "3.3.0") { + strrep <- function(x, times) { + map_chr( + times, + function(n) paste(rep(x, n), collapse = "") + ) + } +} From b5fdad5d68ff5dbd83957c3e68cfb2cb6b2d6bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 31 Oct 2017 22:24:43 +0100 Subject: [PATCH 078/133] test R 3.1 with tic --- .Rbuildignore | 1 + .travis.yml | 44 +++++++++++++++++++++++++++++++++++++++++--- tic.R | 1 + 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 tic.R diff --git a/.Rbuildignore b/.Rbuildignore index 8ec3a1722..9bb8b4a45 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -4,3 +4,4 @@ ^README-.*\.png$ ^\.travis\.yml$ ^API$ +^tic\.R$ diff --git a/.travis.yml b/.travis.yml index 91bfb3959..f89b47f51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,47 @@ # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r +# Default configuration for use with tic package +# Usually you shouldn't need to change the first part of the file -language: R +# DO NOT CHANGE THE CODE BELOW +before_install: R -q -e 'install.packages(c("remotes", "curl")); remotes::install_github("ropenscilabs/tic"); tic::prepare_all_stages(); tic::before_install()' +install: R -q -e 'tic::install()' +after_install: R -q -e 'tic::after_install()' +before_script: +- head -n 100 tests/testthat/out/* +- R -q -e 'tic::before_script()' +script: R -q -e 'tic::script()' +after_success: R -q -e 'tic::after_success()' +after_failure: R -q -e 'tic::after_failure()' +before_deploy: R -q -e 'tic::before_deploy()' +deploy: + provider: script + script: R -q -e 'tic::deploy()' + on: + all_branches: true +after_deploy: R -q -e 'tic::after_deploy()' +after_script: R -q -e 'tic::after_script()' +# DO NOT CHANGE THE CODE ABOVE + +# Custom parts: + +# Header +language: r sudo: false +dist: trusty cache: packages latex: false +r: +- 3.1 +- 3.2 +- oldrel +- release +- devel -before_script: -- head -n 100 tests/testthat/out/* +#env +env: + global: + - _R_CHECK_FORCE_SUGGESTS_=false + - MAKEFLAGS="-j 2" + +#services +services: diff --git a/tic.R b/tic.R new file mode 100644 index 000000000..ec2f06f7f --- /dev/null +++ b/tic.R @@ -0,0 +1 @@ +add_package_checks() From 170deca9b2d94a3a629dbff36caa59b2260935a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 2 Nov 2017 10:03:25 +0100 Subject: [PATCH 079/133] avoid superscript if non-UTF-8 output, closes #61 --- R/scientific.R | 1 + R/utils.R | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/R/scientific.R b/R/scientific.R index 6cfa99365..d022eb070 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -6,6 +6,7 @@ #' @inheritParams format_decimal #' @param superscript If `TRUE`, will use superscript numbers in exponent. format_scientific <- function(x, sigfig = 3, superscript = TRUE) { + if (!is_utf8_output()) superscript <- FALSE split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) } diff --git a/R/utils.R b/R/utils.R index ce47503cd..a58200da1 100644 --- a/R/utils.R +++ b/R/utils.R @@ -42,3 +42,12 @@ ruler <- function(width = getOption("width")) { slice <- function(df, index) { df[index, , drop = FALSE] } + +is_utf8_output <- function() { + l10n_info()$`UTF-8` && !is_latex_output() +} + +is_latex_output <- function() { + if (!("knitr" %in% loadedNamespaces())) return(FALSE) + get("is_latex_output", asNamespace("knitr"))() +} From 68bc6f5d99cd351c8c3a761d791e721d130b9f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 2 Nov 2017 10:05:56 +0100 Subject: [PATCH 080/133] avoid ellipsis character if non-UTF-8 output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @hadley: This uses `...` instead of `…` on Windows and in LaTeX documents. Should we use something that's only one character wide, maybe `~` or `-`? --- R/utils.R | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/R/utils.R b/R/utils.R index a58200da1..8f7e78bb3 100644 --- a/R/utils.R +++ b/R/utils.R @@ -12,8 +12,19 @@ str_trunc <- function(x, width) { str_width <- crayon::col_nchar(x, type = "width") - too_wide <- !is.na(x) & str_width > width - x[too_wide] <- paste0(crayon::col_substr(x[too_wide], 1, width - 1), "\u2026") + too_wide <- which(!is.na(x) & str_width > width) + ellipsis <- get_ellipsis() + nchar_ellipsis <- nchar(ellipsis, type = "width") + if (nchar_ellipsis > width) { + ellipsis <- substr(ellipsis, 1, width) + x[too_wide] <- ellipsis + } else { + x[too_wide] <- paste0( + crayon::col_substr(x[too_wide], 1, width - nchar_ellipsis), + ellipsis + ) + } + x } @@ -43,6 +54,10 @@ slice <- function(df, index) { df[index, , drop = FALSE] } +get_ellipsis <- function() { + if (is_utf8_output()) "\u2026" else "..." +} + is_utf8_output <- function() { l10n_info()$`UTF-8` && !is_latex_output() } From 78f9918b3ab3e013b361c3c4197f9ae13bf08532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 2 Nov 2017 14:22:23 +0100 Subject: [PATCH 081/133] use single-character continuation symbol --- R/utils.R | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/R/utils.R b/R/utils.R index 8f7e78bb3..dae83b47b 100644 --- a/R/utils.R +++ b/R/utils.R @@ -13,18 +13,7 @@ str_trunc <- function(x, width) { str_width <- crayon::col_nchar(x, type = "width") too_wide <- which(!is.na(x) & str_width > width) - ellipsis <- get_ellipsis() - nchar_ellipsis <- nchar(ellipsis, type = "width") - if (nchar_ellipsis > width) { - ellipsis <- substr(ellipsis, 1, width) - x[too_wide] <- ellipsis - } else { - x[too_wide] <- paste0( - crayon::col_substr(x[too_wide], 1, width - nchar_ellipsis), - ellipsis - ) - } - + x[too_wide] <- paste0(crayon::col_substr(x[too_wide], 1, width - 1), get_ellipsis()) x } @@ -55,7 +44,7 @@ slice <- function(df, index) { } get_ellipsis <- function() { - if (is_utf8_output()) "\u2026" else "..." + if (is_utf8_output()) "\u2026" else "~" } is_utf8_output <- function() { From 8418d3c07a7871654e54ba5997ce84e002fa2c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 3 Nov 2017 10:34:10 +0100 Subject: [PATCH 082/133] safe handling of return values of type_sum(), CC @strengejacke --- R/type.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/R/type.R b/R/type.R index 292ead60c..14052fcf3 100644 --- a/R/type.R +++ b/R/type.R @@ -7,7 +7,10 @@ style_type <- function(x) { } pillar_type <- function(x, ...) { - type <- type_sum(x) + type <- as_character(type_sum(x)) + if (length(type) == 0L) type <- "" + type <- type[[1L]] + ret <- structure( list( type = type From 3d9c5f60632027e146e684089e2a2db557c834eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Fri, 3 Nov 2017 11:33:15 +0100 Subject: [PATCH 083/133] permit NULL --- R/type.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/type.R b/R/type.R index 14052fcf3..becd88203 100644 --- a/R/type.R +++ b/R/type.R @@ -7,7 +7,7 @@ style_type <- function(x) { } pillar_type <- function(x, ...) { - type <- as_character(type_sum(x)) + type <- as_character(type_sum(x) %||% "") if (length(type) == 0L) type <- "" type <- type[[1L]] From 8a7d0ce43e65d692eca32e0db98f8f09eb385782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 8 Nov 2017 12:28:41 +0100 Subject: [PATCH 084/133] merge tibble and pillar vignettes (#64) --- vignettes/extending.Rmd | 127 +++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 21 deletions(-) diff --git a/vignettes/extending.Rmd b/vignettes/extending.Rmd index 5138fbb4a..ee32746c9 100644 --- a/vignettes/extending.Rmd +++ b/vignettes/extending.Rmd @@ -1,6 +1,6 @@ --- title: "Adding pillar support to a datatype" -author: "Kirill Müller" +author: "Kirill Müller, Hadley Wickham" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > @@ -9,29 +9,64 @@ vignette: > %\VignetteEncoding{UTF-8} --- -This short vignette assumes an S3 class that encapsulates a vector. It demonstrates how to extend this class so that it renders nicely in a tibble. +To extend the tibble package for new types of columnar data, you need to understand how printing works. The presentation of a column in a tibble is powered by four S3 generics: + +* `type_sum()` determines what goes into the column header. +* `pillar_shaft()` determines what goes into the body of the column. +* `is_vector_s3()` and `obj_sum()` are used when rendering list columns. + +If you have written an S3 or S4 class that can be used as a column, you can override these generics to make sure your data prints well in a tibble. To start, you must import the `pillar` package that powers the printing of tibbles. Either add `pillar` to the `Imports:` section of your `DESCRIPTION`, or simply call: + +```{r, eval = FALSE} +usethis::use_package("pillar") +``` + +This short vignette assumes a package that implements an S3 class `"latlon"` and uses `roxygen2` to create documentation and the `NAMESPACE` file. For this vignette to work we need to attach pillar: + +```{r} +library(pillar) +``` + + ## Prerequisites We define a class `"latlon"` that encodes geographic coordinates in a complex number. For simplicity, the values are printed as hours and minutes only. ```{r} +#' @export latlon <- function(lat, lon) { as_latlon(complex(real = lon, imaginary = lat)) } +#' @export as_latlon <- function(x) { structure(x, class = "latlon") } +#' @export +c.latlon <- function(x, ...) { + as_latlon(NextMethod()) +} + +#' @export `[.latlon` <- function(x, i) { as_latlon(NextMethod()) } +#' @export format.latlon <- function(x, ..., formatter = deg_rad) { - lat <- Im(x) - lon <- Re(x) - paste(formatter(Im(x), c("N", "S")), formatter(Re(x), c("E", "W"))) + x_valid <- which(!is.na(x)) + + lat <- unclass(Im(x[x_valid])) + lon <- unclass(Re(x[x_valid])) + + ret <- rep("", length(x)) + ret[x_valid] <- paste( + formatter(lat, c("N", "S")), + formatter(lon, c("E", "W")) + ) + format(ret, justify = "right") } deg_rad <- function(x, pm) { @@ -40,11 +75,10 @@ deg_rad <- function(x, pm) { deg <- trunc(x) x <- x - deg rad <- round(x * 60) - ret <- sprintf("%d°%.2d'%s", deg, rad, pm[ifelse(sign >= 0, 1, 2)]) - ret[is.na(x)] <- "" - format(ret, justify = "right") + sprintf("%3d°%.2d'%s", deg, rad, pm[ifelse(sign >= 0, 1, 2)]) } +#' @export print.latlon <- function(x, ...) { cat(format(x), sep = "\n") invisible(x) @@ -64,26 +98,39 @@ Columns on this class can be used in a tibble right away, but the output will be library(tibble) data <- tibble( venue = "rstudio::conf", - year = 2018:2019, - loc = latlon(c(32.7102978, NA), c(-117.1704058, NA)) + year = 2017:2019, + loc = latlon( + c(28.3411783, 32.7102978, NA), + c(-81.5480348, -117.1704058, NA) + ), + paths = list( + loc[1], + c(loc[1], loc[2]), + loc[2] + ) ) data ``` -The output has two main problems: +(The `paths` column is a list that contains arbitrary data, in our case `latlon` vectors. A list column is a powerful way to attach hierarchical or unstructured data to an observation in a data frame.) -1. The column type is displayed as ``. While this is technically correct, we might like a shorter representation. -1. The values are formatted as complex numbers (the underlying storage), without using the `format()` method we have defined. This is by design. +The output has three main problems: -In the remainder I'll show how to fix both problems, and also how to implement rendering that adapts to the available width. +1. The column type is displayed as ``. This default formatting works reasonably well for any kind of object, but the generated output may be too wide and waste precious space when displaying the tibble. +1. The cells in the `paths` column are also displayed as ``. +1. The values in the `loc` column are formatted as complex numbers (the underlying storage), without using the `format()` method we have defined. This is by design. + +In the remainder I'll show how to fix these problems, and also how to implement rendering that adapts to the available width. ## Fixing the data type -To display `` as data type, we need to override the `type_sum()` method: +To display `` as data type, we need to override the `type_sum()` method. This method should return a length-1 character vector that can be used in a column header. For your own classes, strive for an evocative abbreviation that's under 6 characters. + ```{r} +#' @export type_sum.latlon <- function(x) { "geo" } @@ -101,7 +148,7 @@ data To use our format method for rendering, we implement the `pillar_shaft()` method for our class. (A [*pillar*](https://en.wikipedia.org/wiki/Column#Nomenclature) is mainly a *shaft* (decorated with an *ornament*), with a *capital* above and a *base* below. Multiple pillars form a *colonnade*, which can be stacked in multiple *tiers*. This is the motivation behind the names in our API.) ```{r} -library(pillar) +#' @export pillar_shaft.latlon <- function(x, ...) { out <- format(x) out[is.na(x)] <- NA @@ -118,6 +165,7 @@ data We could also use left alignment and indent only the `NA` values: ```{r} +#' @export pillar_shaft.latlon <- function(x, ...) { out <- format(x) out[is.na(x)] <- NA @@ -139,6 +187,7 @@ print(data, width = 35) If we specify a minimum width when constructing the shaft, the `loc` column will be truncated: ```{r} +#' @export pillar_shaft.latlon <- function(x, ...) { out <- format(x) out[is.na(x)] <- NA @@ -151,6 +200,7 @@ print(data, width = 35) This may be useful for character data, but for lat-lon data we may prefer to show full degrees and remove the minutes if the available space is not enough to show accurate values. A more sophisticated implementation of the `pillar_shaft()` method is required to achieve this: ```{r} +#' @export pillar_shaft.latlon <- function(x, ...) { deg <- format(x, formatter = deg) deg[is.na(x)] <- style_na("NA") @@ -175,15 +225,14 @@ deg <- function(x, pm) { sign <- sign(x) x <- abs(x) deg <- round(x) - ret <- sprintf("%d°%s", deg, pm[ifelse(sign >= 0, 1, 2)]) - ret[is.na(x)] <- "" - format(ret, justify = "right") + sprintf("%d°%s", deg, pm[ifelse(sign >= 0, 1, 2)]) } ``` All that's left to do is to implement a `format()` method for our new `"pillar_shaft_latlon"` class. This method will be called with a `width` argument, which then determines which of the formattings to choose: ```{r} +#' @export format.pillar_shaft_latlon <- function(x, width, ...) { if (all(crayon::col_nchar(x$deg_rad) <= width)) { ornament <- x$deg_rad @@ -204,6 +253,7 @@ print(data, width = 35) Both `new_pillar_shaft()` and `new_ornament()` accept escape codes for coloring, emphasis, or other ways of highlighting text on terminals that support it. Some formattings are predefined, e.g. `style_subtle()` displays text in a light gray. For default data types, this style is used for insignificant digits. We'll be formatting the degree and minute signs in a subtle style, because they serve only as separators. You can also use the [crayon](https://cran.r-project.org/package=crayon) package to add custom formattings to your text. ```{r} +#' @export pillar_shaft.latlon <- function(x, ...) { out <- format(x, formatter = deg_rad_color) out[is.na(x)] <- NA @@ -234,6 +284,41 @@ data Currently, ANSI escapes are not rendered in vignettes, so the display here isn't much different from earlier examples. This may change in the future. -## In a package +## Fixing list columns + +To tweak the output in the `paths` column, we simply need to indicate that our class is an S3 vector: + +```{r} +#' @export +is_vector_s3.latlon <- function(x) TRUE + +data +``` + +This is picked up by the default implementation of `obj_sum()`, which then shows the type and the length in brackets. If your object is built on top of an atomic vector the default will be adequate. You, will, however, need to provide an `obj_sum()` method for your class if your object is vectorised and built on top of a list. + +An example of an object of this type in base R is `POSIXlt`: it is a list with 9 components. + +```{r} +x <- as.POSIXlt(Sys.time() + c(0, 60, 3600)) +str(unclass(x)) +``` + +But it pretends to be a vector with 3 elements: + +```{r} +x +length(x) +str(x) +``` + +So we need to define a method that returns a character vector the same length as `x`: + +```{r} +#' @export +obj_sum.POSIXlt <- function(x) { + rep("POSIXlt", length(x)) +} +``` + -Remember to use namespace imports instead of `library()` calls in package code! From 2564a35dd85a663891e320d71f1bf3b21bbee849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 8 Nov 2017 12:33:44 +0100 Subject: [PATCH 085/133] move vignette to tibble --- DESCRIPTION | 9 +- vignettes/extending.Rmd | 324 ---------------------------------------- 2 files changed, 2 insertions(+), 331 deletions(-) delete mode 100644 vignettes/extending.Rmd diff --git a/DESCRIPTION b/DESCRIPTION index dd5c5f39c..b555c890e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -17,13 +17,8 @@ Imports: methods, rlang Suggests: - knitr, - rmarkdown, - testthat, - tibble + testthat Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi::api_roclet")) RoxygenNote: 6.0.1 Remotes: - r-lib/crayon, - hadley/testthat -VignetteBuilder: knitr + r-lib/crayon diff --git a/vignettes/extending.Rmd b/vignettes/extending.Rmd deleted file mode 100644 index ee32746c9..000000000 --- a/vignettes/extending.Rmd +++ /dev/null @@ -1,324 +0,0 @@ ---- -title: "Adding pillar support to a datatype" -author: "Kirill Müller, Hadley Wickham" -date: "`r Sys.Date()`" -output: rmarkdown::html_vignette -vignette: > - %\VignetteIndexEntry{Vignette Title} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -To extend the tibble package for new types of columnar data, you need to understand how printing works. The presentation of a column in a tibble is powered by four S3 generics: - -* `type_sum()` determines what goes into the column header. -* `pillar_shaft()` determines what goes into the body of the column. -* `is_vector_s3()` and `obj_sum()` are used when rendering list columns. - -If you have written an S3 or S4 class that can be used as a column, you can override these generics to make sure your data prints well in a tibble. To start, you must import the `pillar` package that powers the printing of tibbles. Either add `pillar` to the `Imports:` section of your `DESCRIPTION`, or simply call: - -```{r, eval = FALSE} -usethis::use_package("pillar") -``` - -This short vignette assumes a package that implements an S3 class `"latlon"` and uses `roxygen2` to create documentation and the `NAMESPACE` file. For this vignette to work we need to attach pillar: - -```{r} -library(pillar) -``` - - - -## Prerequisites - -We define a class `"latlon"` that encodes geographic coordinates in a complex number. For simplicity, the values are printed as hours and minutes only. - -```{r} -#' @export -latlon <- function(lat, lon) { - as_latlon(complex(real = lon, imaginary = lat)) -} - -#' @export -as_latlon <- function(x) { - structure(x, class = "latlon") -} - -#' @export -c.latlon <- function(x, ...) { - as_latlon(NextMethod()) -} - -#' @export -`[.latlon` <- function(x, i) { - as_latlon(NextMethod()) -} - -#' @export -format.latlon <- function(x, ..., formatter = deg_rad) { - x_valid <- which(!is.na(x)) - - lat <- unclass(Im(x[x_valid])) - lon <- unclass(Re(x[x_valid])) - - ret <- rep("", length(x)) - ret[x_valid] <- paste( - formatter(lat, c("N", "S")), - formatter(lon, c("E", "W")) - ) - format(ret, justify = "right") -} - -deg_rad <- function(x, pm) { - sign <- sign(x) - x <- abs(x) - deg <- trunc(x) - x <- x - deg - rad <- round(x * 60) - sprintf("%3d°%.2d'%s", deg, rad, pm[ifelse(sign >= 0, 1, 2)]) -} - -#' @export -print.latlon <- function(x, ...) { - cat(format(x), sep = "\n") - invisible(x) -} - -latlon(32.7102978, -117.1704058) -``` - -More methods are needed to make this class fully compatible with data frames, see e.g. the [hms](https://github.com/tidyverse/hms/) package for a more complete example. - - -## Using in a tibble - -Columns on this class can be used in a tibble right away, but the output will be less than ideal: - -```{r} -library(tibble) -data <- tibble( - venue = "rstudio::conf", - year = 2017:2019, - loc = latlon( - c(28.3411783, 32.7102978, NA), - c(-81.5480348, -117.1704058, NA) - ), - paths = list( - loc[1], - c(loc[1], loc[2]), - loc[2] - ) -) - -data -``` - -(The `paths` column is a list that contains arbitrary data, in our case `latlon` vectors. A list column is a powerful way to attach hierarchical or unstructured data to an observation in a data frame.) - -The output has three main problems: - -1. The column type is displayed as ``. This default formatting works reasonably well for any kind of object, but the generated output may be too wide and waste precious space when displaying the tibble. -1. The cells in the `paths` column are also displayed as ``. -1. The values in the `loc` column are formatted as complex numbers (the underlying storage), without using the `format()` method we have defined. This is by design. - -In the remainder I'll show how to fix these problems, and also how to implement rendering that adapts to the available width. - - -## Fixing the data type - -To display `` as data type, we need to override the `type_sum()` method. This method should return a length-1 character vector that can be used in a column header. For your own classes, strive for an evocative abbreviation that's under 6 characters. - - -```{r} -#' @export -type_sum.latlon <- function(x) { - "geo" -} -``` - -Because the value shown there doesn't depend on the data, we just return a constant. (For date-times, the column info will eventually contain information about the timezone, see [#53](https://github.com/r-lib/pillar/pull/53).) - -```{r} -data -``` - - -## Rendering the value - -To use our format method for rendering, we implement the `pillar_shaft()` method for our class. (A [*pillar*](https://en.wikipedia.org/wiki/Column#Nomenclature) is mainly a *shaft* (decorated with an *ornament*), with a *capital* above and a *base* below. Multiple pillars form a *colonnade*, which can be stacked in multiple *tiers*. This is the motivation behind the names in our API.) - -```{r} -#' @export -pillar_shaft.latlon <- function(x, ...) { - out <- format(x) - out[is.na(x)] <- NA - new_pillar_shaft(out, align = "right") -} -``` - -The simplest variant calls our `format()` method, everything else is handled by pillar, in particular by the `new_pillar_shaft()` helper. Note how the `align` argument affects the alignment of NA values and of the column name and type. - -```{r} -data -``` - -We could also use left alignment and indent only the `NA` values: - -```{r} -#' @export -pillar_shaft.latlon <- function(x, ...) { - out <- format(x) - out[is.na(x)] <- NA - new_pillar_shaft(out, align = "left", na_indent = 5) -} - -data -``` - - -## Adaptive rendering - -If there is not enough space to render the values, the formatted values are truncated with an ellipsis. This doesn't currently apply to our class, because we haven't specified a minimum width for our values: - -```{r} -print(data, width = 35) -``` - -If we specify a minimum width when constructing the shaft, the `loc` column will be truncated: - -```{r} -#' @export -pillar_shaft.latlon <- function(x, ...) { - out <- format(x) - out[is.na(x)] <- NA - new_pillar_shaft(out, align = "right", min_width = 10) -} - -print(data, width = 35) -``` - -This may be useful for character data, but for lat-lon data we may prefer to show full degrees and remove the minutes if the available space is not enough to show accurate values. A more sophisticated implementation of the `pillar_shaft()` method is required to achieve this: - -```{r} -#' @export -pillar_shaft.latlon <- function(x, ...) { - deg <- format(x, formatter = deg) - deg[is.na(x)] <- style_na("NA") - deg_rad <- format(x) - deg_rad[is.na(x)] <- style_na("NA") - ret <- structure( - list(deg = deg, deg_rad = deg_rad), - class = c("pillar_shaft_latlon", "pillar_shaft") - ) - ret <- set_width(ret, max(crayon::col_nchar(deg_rad), 0)) - ret <- set_min_width(ret, max(crayon::col_nchar(deg), 0)) - ret -} -``` - -Here, `pillar_shaft()` returns an object of the `"pillar_shaft_latlon"` class (which is also a `"pillar_shaft"`) that contains the necessary information to render the values, and also minimum and maximum width values. For simplicity, both formattings are pre-rendered, and the minimum and maximum widths are computed from there. Note that we also need to take care of `NA` values explicitly. (`crayon::col_nchar()` is like `nchar()` but strips the formatting added by `style_na()`.) - -For completeness, the code that implements the degree-only formatting looks like this: - -```{r} -deg <- function(x, pm) { - sign <- sign(x) - x <- abs(x) - deg <- round(x) - sprintf("%d°%s", deg, pm[ifelse(sign >= 0, 1, 2)]) -} -``` - -All that's left to do is to implement a `format()` method for our new `"pillar_shaft_latlon"` class. This method will be called with a `width` argument, which then determines which of the formattings to choose: - -```{r} -#' @export -format.pillar_shaft_latlon <- function(x, width, ...) { - if (all(crayon::col_nchar(x$deg_rad) <= width)) { - ornament <- x$deg_rad - } else { - ornament <- x$deg - } - - new_ornament(ornament) -} - -data -print(data, width = 35) -``` - - -## Adding color - -Both `new_pillar_shaft()` and `new_ornament()` accept escape codes for coloring, emphasis, or other ways of highlighting text on terminals that support it. Some formattings are predefined, e.g. `style_subtle()` displays text in a light gray. For default data types, this style is used for insignificant digits. We'll be formatting the degree and minute signs in a subtle style, because they serve only as separators. You can also use the [crayon](https://cran.r-project.org/package=crayon) package to add custom formattings to your text. - -```{r} -#' @export -pillar_shaft.latlon <- function(x, ...) { - out <- format(x, formatter = deg_rad_color) - out[is.na(x)] <- NA - new_pillar_shaft(out, align = "left", na_indent = 5) -} - -deg_rad_color <- function(x, pm) { - sign <- sign(x) - x <- abs(x) - deg <- trunc(x) - x <- x - deg - rad <- round(x * 60) - ret <- sprintf( - "%d%s%.2d%s%s", - deg, - style_subtle("°"), - rad, - style_subtle("'"), - pm[ifelse(sign >= 0, 1, 2)] - ) - ret[is.na(x)] <- "" - format(ret, justify = "right") -} - -data -``` - -Currently, ANSI escapes are not rendered in vignettes, so the display here isn't much different from earlier examples. This may change in the future. - - -## Fixing list columns - -To tweak the output in the `paths` column, we simply need to indicate that our class is an S3 vector: - -```{r} -#' @export -is_vector_s3.latlon <- function(x) TRUE - -data -``` - -This is picked up by the default implementation of `obj_sum()`, which then shows the type and the length in brackets. If your object is built on top of an atomic vector the default will be adequate. You, will, however, need to provide an `obj_sum()` method for your class if your object is vectorised and built on top of a list. - -An example of an object of this type in base R is `POSIXlt`: it is a list with 9 components. - -```{r} -x <- as.POSIXlt(Sys.time() + c(0, 60, 3600)) -str(unclass(x)) -``` - -But it pretends to be a vector with 3 elements: - -```{r} -x -length(x) -str(x) -``` - -So we need to define a method that returns a character vector the same length as `x`: - -```{r} -#' @export -obj_sum.POSIXlt <- function(x) { - rep("POSIXlt", length(x)) -} -``` - - From fb1e0ec63d30b585687907f4381769c30e5d9642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 8 Nov 2017 12:46:50 +0100 Subject: [PATCH 086/133] oops --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index b555c890e..20f8c68e8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -17,6 +17,7 @@ Imports: methods, rlang Suggests: + knitr, testthat Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi::api_roclet")) RoxygenNote: 6.0.1 From 7b5c49e6f6e193ac73d088ac7d030385eceae1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 12 Nov 2017 23:58:36 +0100 Subject: [PATCH 087/133] remove unused --- R/utils.R | 5 ----- 1 file changed, 5 deletions(-) diff --git a/R/utils.R b/R/utils.R index dae83b47b..74a08c521 100644 --- a/R/utils.R +++ b/R/utils.R @@ -2,11 +2,6 @@ cat_line <- function(...) { cat(..., "\n", sep = "") } -pillar_align <- function(x, width, align) { - vapply(x, crayon::col_align, width = width, align = align, - FUN.VALUE = character(1)) -} - str_trunc <- function(x, width) { if (width == Inf) return(x) From db0ec895955a8df4cfed7fecc02508fcad088c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 00:07:14 +0100 Subject: [PATCH 088/133] avoid use of col_nchar() in argument list --- R/shaft.R | 8 ++++++-- man/pillar_shaft.Rd | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/R/shaft.R b/R/shaft.R index a157b7547..c4f34c687 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -29,15 +29,19 @@ print.pillar_shaft <- function(x, ...) { } #' @export -#' @param width The maximum column width. +#' @param width The maximum column width, by default the natural width of `x`. #' @param align Alignment of the column. #' @param min_width The minimum allowed column width, `width` if omitted. #' @param na_indent Indention of `NA` values. #' @rdname pillar_shaft new_pillar_shaft <- function(x, ..., - width = max(crayon::col_nchar(x, type = "width"), 0L), + width = NULL, align = "left", min_width = NULL, na_indent = 0L) { + if (is.null(width)) { + width <- max(crayon::col_nchar(x, type = "width"), 0L) + } + ret <- structure( x, align = align, diff --git a/man/pillar_shaft.Rd b/man/pillar_shaft.Rd index 46c9f2e59..29c08b4df 100644 --- a/man/pillar_shaft.Rd +++ b/man/pillar_shaft.Rd @@ -14,8 +14,8 @@ \usage{ pillar_shaft(x, ...) -new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), - 0L), align = "left", min_width = NULL, na_indent = 0L) +new_pillar_shaft(x, ..., width = NULL, align = "left", min_width = NULL, + na_indent = 0L) \method{pillar_shaft}{logical}(x, ...) @@ -36,7 +36,7 @@ new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), \item{...}{Unused, for extensibility.} -\item{width}{The maximum column width.} +\item{width}{The maximum column width, by default the natural width of \code{x}.} \item{align}{Alignment of the column.} From 5959bb2355b9f8c56df0bbce08c9e8bc88afe5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 00:13:25 +0100 Subject: [PATCH 089/133] use new get_extent() and get_max_extent() --- R/extent.R | 7 +++++++ R/shaft.R | 6 +++--- R/sigfig.R | 10 +++++----- R/title.R | 2 +- R/type.R | 2 +- R/utils.R | 2 +- 6 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 R/extent.R diff --git a/R/extent.R b/R/extent.R new file mode 100644 index 000000000..c52f83bb7 --- /dev/null +++ b/R/extent.R @@ -0,0 +1,7 @@ +get_extent <- function(x) { + utf8::utf8_width(crayon::strip_style(x), encode = FALSE) +} + +get_max_extent <- function(x) { + max(get_extent(x), 0L) +} diff --git a/R/shaft.R b/R/shaft.R index c4f34c687..9ab1edb75 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -39,7 +39,7 @@ new_pillar_shaft <- function(x, ..., align = "left", min_width = NULL, na_indent = 0L) { if (is.null(width)) { - width <- max(crayon::col_nchar(x, type = "width"), 0L) + width <- get_max_extent(x) } ret <- structure( @@ -116,7 +116,7 @@ pillar_shaft.character <- function(x, ...) { out[needs_quotes] <- quoted out[is_na] <- pillar_na(use_brackets_if_no_color = TRUE) - width <- max(crayon::col_nchar(out, type = "width"), 0L) + width <- get_max_extent(out) new_pillar_shaft(out, width = width, align = "left", min_width = min(width, 3L)) } @@ -126,7 +126,7 @@ pillar_shaft.character <- function(x, ...) { pillar_shaft.list <- function(x, ...) { out <- paste0("<", obj_sum(x), ">") - width <- max(nchar(out, type = "width")) + width <- get_max_extent(out) new_pillar_shaft(style_list(out), width = width, align = "left", min_width = min(width, 3L)) } diff --git a/R/sigfig.R b/R/sigfig.R index bf63ea733..995d61c31 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -59,7 +59,7 @@ split_decimal <- function(x, sigfig, scientific = FALSE, superscript = FALSE) { superscript = superscript ) - set_width(ret, max(crayon::col_nchar(assemble_decimal(ret), type = "width"), 0L)) + set_width(ret, get_max_extent(assemble_decimal(ret))) } safe_signif <- function(x, digits) { @@ -104,9 +104,9 @@ format_lhs <- function(s) { lhs_zero <- s$lhs_zero lhs_str <- sprintf("%.0f", s$lhs) - lhs_width <- max(nchar(lhs_str), 0L) + lhs_width <- get_max_extent(lhs_str) lhs_sig <- crayon::col_substr(lhs_str, 1, s$sigfig) - lhs_non <- crayon::col_substr(lhs_str, s$sigfig + 1, nchar(lhs_str)) + lhs_non <- crayon::col_substr(lhs_str, s$sigfig + 1, get_extent(lhs_str)) # as.character() to support corner case of length zero lhs_col <- as.character(ifelse(num, @@ -145,7 +145,7 @@ format_rhs <- function(s) { # Digits on RHS of . rhs_num <- as.character(abs(round(s$rhs * 10 ^ s$rhs_digits))) - rhs_zero <- strrep("0", pmax(0, rhs_digits - nchar(rhs_num))) + rhs_zero <- strrep("0", pmax(0, rhs_digits - get_extent(rhs_num))) rhs_col <- ifelse(dec, paste0( @@ -196,7 +196,7 @@ format.pillar_shaft_decimal <- function(x, width, ...) { row <- assemble_decimal(x$sci) } - used_width <- max(crayon::col_nchar(row, type = "width"), 0L) + used_width <- get_max_extent(row) row <- paste0(strrep(" ", width - used_width), row) new_ornament(row, width = width, align = "right") } diff --git a/R/title.R b/R/title.R index 623c3aae0..ec5d237ff 100644 --- a/R/title.R +++ b/R/title.R @@ -2,7 +2,7 @@ pillar_title <- function(title, ...) { if (is.null(title)) { width <- 0L } else { - width <- nchar(format_title(title, width = Inf), "width") + width <- get_extent(format_title(title, width = Inf)) } ret <- structure( diff --git a/R/type.R b/R/type.R index becd88203..cfde6d09a 100644 --- a/R/type.R +++ b/R/type.R @@ -17,7 +17,7 @@ pillar_type <- function(x, ...) { ), class = "pillar_type" ) - ret <- set_width(ret, width = nchar(type, type = "width") + 2L) + ret <- set_width(ret, width = get_extent(type) + 2L) ret <- set_min_width(ret, 5L) ret } diff --git a/R/utils.R b/R/utils.R index 74a08c521..2ae44a065 100644 --- a/R/utils.R +++ b/R/utils.R @@ -5,7 +5,7 @@ cat_line <- function(...) { str_trunc <- function(x, width) { if (width == Inf) return(x) - str_width <- crayon::col_nchar(x, type = "width") + str_width <- utf8::utf8_width(crayon::strip_style(x), encode = FALSE) too_wide <- which(!is.na(x) & str_width > width) x[too_wide] <- paste0(crayon::col_substr(x[too_wide], 1, width - 1), get_ellipsis()) From c9cc92838dc89ce6b773833ac9e3e5ae463ac06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 01:12:42 +0100 Subject: [PATCH 090/133] use cli, closes https://github.com/tidyverse/tibble/issues/271 - Use cli package to access pretty symbols. --- DESCRIPTION | 1 + R/dim.R | 2 +- R/scientific.R | 13 ++----------- R/utils.R | 6 +----- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 20f8c68e8..ecad1193a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,6 +13,7 @@ LazyData: true URL: https://github.com/hadley/pillar BugReports: https://github.com/hadley/pillar/issues Imports: + cli, crayon, methods, rlang diff --git a/R/dim.R b/R/dim.R index 3d262f9a3..7d30bf260 100644 --- a/R/dim.R +++ b/R/dim.R @@ -23,7 +23,7 @@ size_sum <- function(x) { } mult_sign <- function() { - "x" + cli::symbol$times } spaces_around <- function(x) { diff --git a/R/scientific.R b/R/scientific.R index d022eb070..352157f3c 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -6,7 +6,6 @@ #' @inheritParams format_decimal #' @param superscript If `TRUE`, will use superscript numbers in exponent. format_scientific <- function(x, sigfig = 3, superscript = TRUE) { - if (!is_utf8_output()) superscript <- FALSE split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) } @@ -23,7 +22,7 @@ supernum <- function(x, superscript = TRUE) { neg <- num & x < 0 if (any(neg)) { if (superscript) { - neg_chr <- ifelse(neg, "\u207b", "\u207a") + neg_chr <- ifelse(neg, cli::symbol$sup_minus, cli::symbol$sup_plus) } else { neg_chr <- ifelse(neg, "-", "+") } @@ -46,13 +45,5 @@ supernum <- function(x, superscript = TRUE) { supernum1 <- function(x) { chars <- strsplit(as.character(x), "")[[1]] - - # super <- c("⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹") - super <- c( - "\u2070", "\u00b9", "\u00b2", "\u00b3", "\u2074", - "\u2075", "\u2076", "\u2077", "\u2078", "\u2079" - ) - names(super) <- 0:9 - - paste0(super[chars], collapse = "") + paste0(cli::symbol[paste0("sup_", chars)], collapse = "") } diff --git a/R/utils.R b/R/utils.R index 2ae44a065..ba57cba9b 100644 --- a/R/utils.R +++ b/R/utils.R @@ -39,11 +39,7 @@ slice <- function(df, index) { } get_ellipsis <- function() { - if (is_utf8_output()) "\u2026" else "~" -} - -is_utf8_output <- function() { - l10n_info()$`UTF-8` && !is_latex_output() + cli::symbol$continue } is_latex_output <- function() { From d6c8084fd3a91e6149f2f97faff7411e4bb159b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 01:27:34 +0100 Subject: [PATCH 091/133] fix test --- tests/testthat/test-obj-sum.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-obj-sum.R b/tests/testthat/test-obj-sum.R index 3281cb979..ef74018b5 100644 --- a/tests/testthat/test-obj-sum.R +++ b/tests/testthat/test-obj-sum.R @@ -17,7 +17,7 @@ test_that("NULL handled specially", { }) test_that("data frame and tibbles include rows and cols", { - expect_equal(obj_sum(mtcars), "data.frame [32 x 11]") + expect_equal(obj_sum(mtcars), paste0("data.frame [32 ", mult_sign(), " 11]")) }) test_that("common data vectors treated as atomic", { From 2bbacf6c5f3f1f296eb49baeaa84a5912ad49db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 01:32:51 +0100 Subject: [PATCH 092/133] format AsIs columns with I(), closes #44 - AsIs columns are formatted with `I()` around the type but with otherwise identical contents. --- NAMESPACE | 3 +++ R/shaft.R | 6 ++++++ R/type-sum.R | 7 +++++++ R/utils.R | 6 ++++++ man/pillar_shaft.Rd | 3 +++ tests/testthat/out/asis-list.txt | 4 ++++ tests/testthat/out/asis-number.txt | 4 ++++ tests/testthat/test-format_asis.R | 6 ++++++ 8 files changed, 39 insertions(+) create mode 100644 tests/testthat/out/asis-list.txt create mode 100644 tests/testthat/out/asis-number.txt create mode 100644 tests/testthat/test-format_asis.R diff --git a/NAMESPACE b/NAMESPACE index 6f0a677da..359032066 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -18,9 +18,11 @@ S3method(is_vector_s3,default) S3method(is_vector_s3,difftime) S3method(is_vector_s3,factor) S3method(is_vector_s3,ordered) +S3method(obj_sum,AsIs) S3method(obj_sum,POSIXlt) S3method(obj_sum,default) S3method(obj_sum,list) +S3method(pillar_shaft,AsIs) S3method(pillar_shaft,Date) S3method(pillar_shaft,POSIXt) S3method(pillar_shaft,character) @@ -35,6 +37,7 @@ S3method(print,pillar_vertical) S3method(print,rif_data) S3method(print,spark) S3method(print,squeezed_colonnade) +S3method(type_sum,AsIs) S3method(type_sum,Date) S3method(type_sum,POSIXct) S3method(type_sum,data.frame) diff --git a/R/shaft.R b/R/shaft.R index 9ab1edb75..576f99caf 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -135,6 +135,12 @@ style_list <- function(x) { style_subtle(x) } +#' @export +#' @rdname pillar_shaft +pillar_shaft.AsIs <- function(x, ...) { + pillar_shaft(remove_as_is_class(x)) +} + #' @export #' @rdname pillar_shaft pillar_shaft.default <- function(x, ...) { diff --git a/R/type-sum.R b/R/type-sum.R index f1e09a2f6..2ca9268dd 100644 --- a/R/type-sum.R +++ b/R/type-sum.R @@ -25,6 +25,8 @@ type_sum.data.frame <- function(x) class(x)[[1]] #' @export type_sum.tbl_df <- function(x) "tibble" #' @export +type_sum.AsIs <- function(x) paste0("I(", type_sum(remove_as_is_class(x)), ")") +#' @export type_sum.default <- function(x) { if (!is.object(x)) { switch(typeof(x), @@ -74,6 +76,11 @@ obj_sum.POSIXlt <- function(x) { rep("POSIXlt", length(x)) } +#' @export +obj_sum.AsIs <- function(x) { + paste0("I(", obj_sum(remove_as_is_class(x)), ")") +} + #' @export #' @rdname type_sum is_vector_s3 <- function(x) UseMethod("is_vector_s3") diff --git a/R/utils.R b/R/utils.R index ba57cba9b..e7183c935 100644 --- a/R/utils.R +++ b/R/utils.R @@ -46,3 +46,9 @@ is_latex_output <- function() { if (!("knitr" %in% loadedNamespaces())) return(FALSE) get("is_latex_output", asNamespace("knitr"))() } + +remove_as_is_class <- function(x) { + if (all(class(x) == "AsIs")) return(unclass(x)) + class(x) <- setdiff(class(x), "AsIs") + x +} diff --git a/man/pillar_shaft.Rd b/man/pillar_shaft.Rd index 29c08b4df..ed68456f0 100644 --- a/man/pillar_shaft.Rd +++ b/man/pillar_shaft.Rd @@ -9,6 +9,7 @@ \alias{pillar_shaft.POSIXt} \alias{pillar_shaft.character} \alias{pillar_shaft.list} +\alias{pillar_shaft.AsIs} \alias{pillar_shaft.default} \title{Column data} \usage{ @@ -29,6 +30,8 @@ new_pillar_shaft(x, ..., width = NULL, align = "left", min_width = NULL, \method{pillar_shaft}{list}(x, ...) +\method{pillar_shaft}{AsIs}(x, ...) + \method{pillar_shaft}{default}(x, ...) } \arguments{ diff --git a/tests/testthat/out/asis-list.txt b/tests/testthat/out/asis-list.txt new file mode 100644 index 000000000..d1dde8dac --- /dev/null +++ b/tests/testthat/out/asis-list.txt @@ -0,0 +1,4 @@ + + + + diff --git a/tests/testthat/out/asis-number.txt b/tests/testthat/out/asis-number.txt new file mode 100644 index 000000000..b2d39800b --- /dev/null +++ b/tests/testthat/out/asis-number.txt @@ -0,0 +1,4 @@ + + 1 + 2 + 3 diff --git a/tests/testthat/test-format_asis.R b/tests/testthat/test-format_asis.R new file mode 100644 index 000000000..a489cb555 --- /dev/null +++ b/tests/testthat/test-format_asis.R @@ -0,0 +1,6 @@ +context("format_asis") + +test_that("output test", { + expect_pillar_output(xp = I(1:3), filename = "asis-number.txt") + expect_pillar_output(xp = I(list(1, 1:2, 1:3)), filename = "asis-list.txt") +}) From b620858766991dade9ba44d90993f9e8e295fe2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 21:55:44 +0100 Subject: [PATCH 093/133] need utf8 --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index ecad1193a..527a0902b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,7 +16,8 @@ Imports: cli, crayon, methods, - rlang + rlang, + utf8 Suggests: knitr, testthat From ae9b5894a9e58cfe8e794ccd26b37ebeba920cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 23:20:07 +0100 Subject: [PATCH 094/133] handle NA --- R/strrep.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/R/strrep.R b/R/strrep.R index 2d8f07d59..aefa7eaa8 100644 --- a/R/strrep.R +++ b/R/strrep.R @@ -2,7 +2,10 @@ if (getRversion() < "3.3.0") { strrep <- function(x, times) { map_chr( times, - function(n) paste(rep(x, n), collapse = "") + function(n) { + if (is.na(n)) NA_character_ + else paste(rep(x, n), collapse = "") + } ) } } From 7b13054c8291c6ec5b36ffe8e337f6149caa78dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 23:31:23 +0100 Subject: [PATCH 095/133] fix compatibility with CRAN version of testthat --- R/testthat.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/testthat.R b/R/testthat.R index 51799c243..4db444ac1 100644 --- a/R/testthat.R +++ b/R/testthat.R @@ -17,10 +17,10 @@ expect_pillar_output <- function(x, ..., filename, xp = add_special(x), xf = pillar(xp, ...), crayon = TRUE, output_width = 80L) { if (crayon) { - old <- options(crayon.enabled = TRUE, crayon.colors = 16L) + old <- options(crayon.enabled = TRUE, crayon.colors = 16L, width = output_width) crayon::num_colors(forget = TRUE) } else { - old <- options(crayon.enabled = FALSE) + old <- options(crayon.enabled = FALSE, width = output_width) } on.exit({ @@ -28,11 +28,11 @@ expect_pillar_output <- function(x, ..., filename, crayon::num_colors(forget = TRUE) }) + # FIXME: Pass output_width argument here with testthat >= 2.0.0 testthat::expect_output_file( print(xf), file.path("out", filename), - update = TRUE, - width = output_width + update = TRUE ) } From 97c1f46a3fcd72b77897637e9826a20cf673fa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 23:50:03 +0100 Subject: [PATCH 096/133] take over maintenance, CC @hadley --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 527a0902b..fd509e995 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,8 @@ Package: pillar Title: Coloured Formatting for Columns Version: 0.0.0.9000 Authors@R: c( - person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")), + person("Kirill", "Müller", , "krlmlr+r@mailbox.org", role = c("aut", "cre")), + person("Hadley", "Wickham", role = "aut"), person("RStudio", role = "cph") ) Description: Provides a `pillar` generic designed for formatting columns From 7fbec9accbac92a2fb6f95fc160b1a61524e8e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 23:50:14 +0100 Subject: [PATCH 097/133] need na.rm = TRUE --- R/extent.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/extent.R b/R/extent.R index c52f83bb7..b76494194 100644 --- a/R/extent.R +++ b/R/extent.R @@ -3,5 +3,5 @@ get_extent <- function(x) { } get_max_extent <- function(x) { - max(get_extent(x), 0L) + max(get_extent(x), 0L, na.rm = TRUE) } From e062b696573502fc435c3a97753a5379443b113a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 23:50:22 +0100 Subject: [PATCH 098/133] better check for infinity --- R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index e7183c935..fcf48efa2 100644 --- a/R/utils.R +++ b/R/utils.R @@ -3,7 +3,7 @@ cat_line <- function(...) { } str_trunc <- function(x, width) { - if (width == Inf) return(x) + if (is.infinite(width)) return(x) str_width <- utf8::utf8_width(crayon::strip_style(x), encode = FALSE) From a384abc71172477755c8ec45d66c784bf906857a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 13 Nov 2017 23:51:05 +0100 Subject: [PATCH 099/133] Use utf8 package (#65) * use utf8 package for escaping * use new get_extent() and get_max_extent() * use own implementation of align() * test example by @ patperry --- R/extent.R | 11 +++++ R/ornament.R | 2 +- R/pillar.R | 6 +-- R/shaft.R | 23 +++++++--- R/sigfig.R | 4 +- R/tick.R | 4 +- man/new_ornament.Rd | 2 +- tests/testthat/out/utf8.txt | 17 +++++++ tests/testthat/test-format_character.R | 63 ++++++++++++++++++++++++++ 9 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 tests/testthat/out/utf8.txt diff --git a/R/extent.R b/R/extent.R index b76494194..4a0932f56 100644 --- a/R/extent.R +++ b/R/extent.R @@ -5,3 +5,14 @@ get_extent <- function(x) { get_max_extent <- function(x) { max(get_extent(x), 0L, na.rm = TRUE) } + +align <- function(x, width, align = c("left", "right")) { + align <- match.arg(align) + extent <- get_extent(x) + spaces <- pmax(width - extent, 0L) + if (align == "left") { + paste0(x, strrep(" ", spaces)) + } else { + paste0(strrep(" ", spaces), x) + } +} diff --git a/R/ornament.R b/R/ornament.R index 70dd66a5e..ec26fd475 100644 --- a/R/ornament.R +++ b/R/ornament.R @@ -9,7 +9,7 @@ #' @param x A character vector with formatting, see [crayon] #' @param width An optional width of the resulting pillar, computed from `x` if #' missing -#' @param align Alignment, passed on to [crayon::col_align()] +#' @param align Alignment, one of `"left"` or `"right"` #' #' @export new_ornament <- function(x, width = NULL, align = NULL) { diff --git a/R/pillar.R b/R/pillar.R index ebcc4cfef..bbd9eb05a 100644 --- a/R/pillar.R +++ b/R/pillar.R @@ -92,9 +92,9 @@ pillar_format_parts <- function(x, width, ...) { data_format <- format(x$data, width = width, ...) align <- attr(data_format, "align") - title_format <- crayon::col_align(title_format, width = width, align = align) - type_format <- crayon::col_align(type_format, width = width, align = align) - data_format <- crayon::col_align(data_format, width = width, align = align) + title_format <- align(title_format, width = width, align = align) + type_format <- align(type_format, width = width, align = align) + data_format <- align(data_format, width = width, align = align) list( title_format = title_format, diff --git a/R/shaft.R b/R/shaft.R index 576f99caf..b96868a06 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -108,16 +108,25 @@ pillar_shaft.POSIXt <- function(x, ...) { #' @export #' @rdname pillar_shaft pillar_shaft.character <- function(x, ...) { + x <- utf8::utf8_encode(x) out <- x - needs_quotes <- which(!is_proper_string(x)) - is_na <- is.na(x) - quoted <- encodeString(x[needs_quotes], quote = '"', na.encode = FALSE) - quoted <- gsub('^"|"$', style_subtle('"'), quoted) - out[needs_quotes] <- quoted - out[is_na] <- pillar_na(use_brackets_if_no_color = TRUE) - width <- get_max_extent(out) + # Add subtle quotes if necessary + needs_quotes <- which(is_ambiguous_string(x)) + if (length(needs_quotes) > 0) { + quoted <- gsub('"', '\\"', x[needs_quotes], fixed = TRUE) + quoted <- paste0(style_subtle('"'), quoted, style_subtle('"')) + out[needs_quotes] <- quoted + } + # Format NA values separately + is_na <- which(is.na(x)) + if (length(is_na) > 0) { + na_contents <- pillar_na(use_brackets_if_no_color = TRUE) + out[is_na] <- na_contents + } + + width <- get_max_extent(out) new_pillar_shaft(out, width = width, align = "left", min_width = min(width, 3L)) } diff --git a/R/sigfig.R b/R/sigfig.R index 995d61c31..70515b309 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -117,7 +117,7 @@ format_lhs <- function(s) { style_na(lhs_str) )) - lhs_col <- crayon::col_align(lhs_col, width = lhs_width, align = "right") + lhs_col <- align(lhs_col, width = lhs_width, align = "right") lhs_col } @@ -156,7 +156,7 @@ format_rhs <- function(s) { ) # ensure all same width - rhs_col <- crayon::col_align(rhs_col, max(rhs_digits, na.rm = TRUE), "left") + rhs_col <- align(rhs_col, max(rhs_digits, 0L, na.rm = TRUE), "left") rhs_col } diff --git a/R/tick.R b/R/tick.R index 47b6e1b57..08e2512f6 100644 --- a/R/tick.R +++ b/R/tick.R @@ -10,8 +10,8 @@ is_syntactic <- function(x) { ret } -is_proper_string <- function(x) { - grepl("^[^[:blank:][:cntrl:]\"](?:|[^[:cntrl:]\"]*[^[:blank:][:cntrl:]\"])$", x, perl = TRUE) +is_ambiguous_string <- function(x) { + grepl("^$|^ | $|\\\\|\"", x) } tick <- function(x) { diff --git a/man/new_ornament.Rd b/man/new_ornament.Rd index 0035c59c6..5a1b6c082 100644 --- a/man/new_ornament.Rd +++ b/man/new_ornament.Rd @@ -12,7 +12,7 @@ new_ornament(x, width = NULL, align = NULL) \item{width}{An optional width of the resulting pillar, computed from \code{x} if missing} -\item{align}{Alignment, passed on to \code{\link[crayon:col_align]{crayon::col_align()}}} +\item{align}{Alignment, one of \code{"left"} or \code{"right"}} } \description{ This function is useful if your data renders differently depending on the diff --git a/tests/testthat/out/utf8.txt b/tests/testthat/out/utf8.txt new file mode 100644 index 000000000..3564bf213 --- /dev/null +++ b/tests/testthat/out/utf8.txt @@ -0,0 +1,17 @@ + chars desc +  + 1 "\u0001\u001f" C0 control code + 2 "\a\b\f\n\r\t" Named control code + 3 abcdefuvwxyz ASCII + 4 "\u0080\u009f" C1 control code + 5  ¡¢£¤¥úûüýþÿ Latin-1 + 6 ĀāĂ㥹ĆćĈĉĊċ Unicode + 7 !"#$%& Unicode wide + 8 "\u0e00\u2029" Unicode control + 9 x­x​x‌x‍x‎x‏x͏xx󠀁x󠀠x󠇯x Unicode ignorable +10 àáâãāa̅ăȧäảåa̋ Unicode mark +11 😀😁😂😃😄💃 Emoji +12 "x\U0010ffffx" Unassigned +13 "\xfd\xfe\xff" Invalid +14 "\\" Backslash +15 "\"" Quote diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index 42d4ad087..853c62669 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -1,5 +1,67 @@ context("format_character") +chartype_frame <- function() +{ + chars <- character() + desc <- character() + + chars[1] <- "\u0001\u001f" + desc[1] <- "C0 control code" + + chars[2] <- "\a\b\f\n\r\t" + desc[2] <- "Named control code" + + chars[3] <- "abcdefuvwxyz" + desc[3] <- "ASCII" + + chars[4] <- "\u0080\u009f" + desc[4] <- "C1 control code" + + chars[5] <- paste0("\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5", + "\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff") + desc[5] <- "Latin-1" + + chars[6] <- paste0("\u0100\u0101\u0102\u0103\u0104\u0105", + "\u0106\u0107\u0108\u0109\u010a\u010b") + desc[6] <- "Unicode" + + chars[7] <- "\uff01\uff02\uff03\uff04\uff05\uff06" + desc[7] <- "Unicode wide" + + chars[8] <- "\ue00\u2029" + desc[8] <- "Unicode control" + + chars[9] <- paste0("x\u00adx\u200bx\u200cx\u200dx\u200ex\u200f", + "x\u034fx\ufeffx", intToUtf8(0xE0001), "x", + intToUtf8(0xE0020), "x", intToUtf8(0xE01EF), "x") + desc[9] <- "Unicode ignorable" + + chars[10] <- paste0("a\u0300a\u0301a\u0302a\u0303a\u0304a\u0305", + "a\u0306a\u0307a\u0308a\u0309a\u030aa\u030b") + desc[10] <- "Unicode mark" + + chars[11] <- paste0(intToUtf8(0x1F600), intToUtf8(0x1F601), + intToUtf8(0x1F602), intToUtf8(0x1F603), + intToUtf8(0x1F604), intToUtf8(0x1F483)) + desc[11] <- "Emoji" + + chars[12] <- paste0("x", intToUtf8(0x10ffff), "x") + desc[12] <- "Unassigned" + + chars[13] <- "\xfd\xfe\xff" + desc[13] <- "Invalid" + + chars[14] <- "\\" + desc[14] <- "Backslash" + + chars[15] <- '"' + desc[15] <- "Quote" + + Encoding(chars) <- "UTF-8" + + data.frame(chars, desc, stringsAsFactors = FALSE) +} + test_that("output test", { expect_pillar_output(letters[1:5], filename = "letters.txt") expect_pillar_output(paste(letters, collapse = ""), filename = "letters-long.txt") @@ -9,4 +71,5 @@ test_that("output test", { expect_pillar_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") expect_pillar_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") expect_pillar_output(c("", " ", " a", "a ", "a b"), width = 5, filename = "spaces.txt") + expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") }) From 993e5c9604e1780a746e62c0ce3ba3d7b65be0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 14 Nov 2017 00:23:20 +0100 Subject: [PATCH 100/133] add CRAN comments --- .Rbuildignore | 1 + cran-comments.md | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 cran-comments.md diff --git a/.Rbuildignore b/.Rbuildignore index 9bb8b4a45..32c37bc82 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -5,3 +5,4 @@ ^\.travis\.yml$ ^API$ ^tic\.R$ +^cran-comments\.md$ diff --git a/cran-comments.md b/cran-comments.md new file mode 100644 index 000000000..55cc056a1 --- /dev/null +++ b/cran-comments.md @@ -0,0 +1,10 @@ +## Test environments +* local Ubuntu 17.04 install, R 3.4.2 +* ubuntu 12.04 (on travis-ci), R 3.4.2 +* win-builder (devel and release) + +## R CMD check results + +0 errors | 0 warnings | 1 note + +* This is a new release. From 9e633aa6ebad9bd35791eab31acd3e6529ffacb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 14 Nov 2017 09:51:09 +0100 Subject: [PATCH 101/133] rename argument to subclass --- R/ornament.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/ornament.R b/R/ornament.R index ec26fd475..56d176da1 100644 --- a/R/ornament.R +++ b/R/ornament.R @@ -22,11 +22,11 @@ new_ornament <- function(x, width = NULL, align = NULL) { ret } -new_vertical <- function(x, ..., extra_class = NULL) { +new_vertical <- function(x, ..., subclass = NULL) { ret <- structure( x, ..., - class = c(extra_class, "pillar_vertical") + class = c(subclass, "pillar_vertical") ) ret } From 47ff602e5f62fbb2e39b978103b70ecb03e69e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 14 Nov 2017 10:19:13 +0100 Subject: [PATCH 102/133] up API --- API | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/API b/API index ab1dedbbe..3f5a17c2d 100644 --- a/API +++ b/API @@ -8,7 +8,7 @@ expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, ...) extra_cols(x, ...) is_vector_s3(x) new_ornament(x, width = NULL, align = NULL) -new_pillar_shaft(x, ..., width = max(crayon::col_nchar(x, type = "width"), 0L), align = "left", min_width = NULL, na_indent = 0L) +new_pillar_shaft(x, ..., width = NULL, align = "left", min_width = NULL, na_indent = 0L) obj_sum(x) pillar(x, title = NULL, width = NULL, ...) pillar_shaft(x, ...) @@ -41,9 +41,11 @@ is_vector_s3.default(x) is_vector_s3.difftime(x) is_vector_s3.factor(x) is_vector_s3.ordered(x) +obj_sum.AsIs(x) obj_sum.POSIXlt(x) obj_sum.default(x) obj_sum.list(x) +pillar_shaft.AsIs(x, ...) pillar_shaft.Date(x, ...) pillar_shaft.POSIXt(x, ...) pillar_shaft.character(x, ...) @@ -58,6 +60,7 @@ print.pillar_vertical(x, ...) print.rif_data(x, ...) print.spark(x, ...) print.squeezed_colonnade(x, ...) +type_sum.AsIs(x) type_sum.Date(x) type_sum.POSIXct(x) type_sum.data.frame(x) From 47c57247ba8f4d2f2ed435e7ebbb48b0cbf00887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 15 Nov 2017 17:04:46 +0100 Subject: [PATCH 103/133] Reorganize pillar_shaft API (#66) * support arbitrary objects in new_pillar_shaft() * new_pillar_shaft_simple() * use new_pillar_shaft() * don't support subclassing * export get_extent() and get_max_extent() * unexport * reorganize and tweak * implement format.pillar_shaft() * up API * tweak doc * tweak doc * tweak doc * snapshot * lazy printing * documentation * pillar_ornament doesn't inherit from pillar_vertical anymore * up API * print shaft with natural width * support overriding printed width for shaft * document --- API | 13 ++-- DESCRIPTION | 26 ++++++++ NAMESPACE | 9 ++- R/extent.R | 12 ++++ R/ornament.R | 15 ++++- R/shaft-simple.R | 47 ++++++++++++++ R/shaft.R | 115 +++++++++++++++++---------------- R/styles.R | 4 ++ R/testthat.R | 69 +++++++++++--------- R/width.R | 3 - man/expect_known_display.Rd | 47 ++++++++++++++ man/expect_pillar_output.Rd | 36 ----------- man/get_extent.Rd | 21 ++++++ man/new_pillar_shaft.Rd | 45 +++++++++++++ man/pillar_shaft.Rd | 33 ++++++---- tests/testthat/helper-output.R | 36 +++++++++++ 16 files changed, 385 insertions(+), 146 deletions(-) create mode 100644 R/shaft-simple.R create mode 100644 man/expect_known_display.Rd delete mode 100644 man/expect_pillar_output.Rd create mode 100644 man/get_extent.Rd create mode 100644 man/new_pillar_shaft.Rd diff --git a/API b/API index 3f5a17c2d..e712c930c 100644 --- a/API +++ b/API @@ -4,16 +4,17 @@ colonnade(x, has_row_id = TRUE, width = NULL, ...) dim_desc(x) -expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, ...), crayon = TRUE, output_width = 80L) +expect_known_display(object, file, ..., width = 80L, crayon = TRUE) extra_cols(x, ...) +get_extent(x) +get_max_extent(x) is_vector_s3(x) new_ornament(x, width = NULL, align = NULL) -new_pillar_shaft(x, ..., width = NULL, align = "left", min_width = NULL, na_indent = 0L) +new_pillar_shaft(x, ..., width, min_width = width, subclass) +new_pillar_shaft_simple(formatted, ..., width = NULL, align = "left", min_width = NULL, na_indent = 0L) obj_sum(x) pillar(x, title = NULL, width = NULL, ...) pillar_shaft(x, ...) -set_min_width(x, min_width) -set_width(x, width) squeeze(x, width = NULL, ...) style_na(x) style_neg(x) @@ -28,6 +29,7 @@ format.colonnade(x, ...) format.pillar(x, width = NULL, ...) format.pillar_shaft(x, width, ...) format.pillar_shaft_decimal(x, width, ...) +format.pillar_shaft_simple(x, width, ...) format.pillar_title(x, width, ...) format.pillar_type(x, width = NULL, ...) format.rif_data(x, width, ...) @@ -55,7 +57,8 @@ pillar_shaft.logical(x, ...) pillar_shaft.numeric(x, ..., sigfig = 3) print.colonnade(x, ...) print.pillar(x, ...) -print.pillar_shaft(x, ...) +print.pillar_ornament(x, ...) +print.pillar_shaft(x, width = NULL, ...) print.pillar_vertical(x, ...) print.rif_data(x, ...) print.spark(x, ...) diff --git a/DESCRIPTION b/DESCRIPTION index fd509e995..5b1ce1b2e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -26,3 +26,29 @@ Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi RoxygenNote: 6.0.1 Remotes: r-lib/crayon +Collate: + 'compat-purrr.R' + 'dim.R' + 'extent.R' + 'multi.R' + 'ornament.R' + 'pillar.R' + 'rowid-data.R' + 'rowid-title.R' + 'rowid-type.R' + 'scientific.R' + 'shaft.R' + 'shaft-simple.R' + 'sigfig.R' + 'spark-bar.R' + 'spark-line.R' + 'strrep.R' + 'styles.R' + 'testthat.R' + 'tick.R' + 'title.R' + 'type-sum.R' + 'type.R' + 'utils.R' + 'width.R' + 'zzz.R' diff --git a/NAMESPACE b/NAMESPACE index 359032066..dc714e98b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,6 +5,7 @@ S3method(format,colonnade) S3method(format,pillar) S3method(format,pillar_shaft) S3method(format,pillar_shaft_decimal) +S3method(format,pillar_shaft_simple) S3method(format,pillar_title) S3method(format,pillar_type) S3method(format,rif_data) @@ -32,6 +33,7 @@ S3method(pillar_shaft,logical) S3method(pillar_shaft,numeric) S3method(print,colonnade) S3method(print,pillar) +S3method(print,pillar_ornament) S3method(print,pillar_shaft) S3method(print,pillar_vertical) S3method(print,rif_data) @@ -48,16 +50,17 @@ S3method(type_sum,ordered) S3method(type_sum,tbl_df) export(colonnade) export(dim_desc) -export(expect_pillar_output) +export(expect_known_display) export(extra_cols) +export(get_extent) +export(get_max_extent) export(is_vector_s3) export(new_ornament) export(new_pillar_shaft) +export(new_pillar_shaft_simple) export(obj_sum) export(pillar) export(pillar_shaft) -export(set_min_width) -export(set_width) export(squeeze) export(style_na) export(style_neg) diff --git a/R/extent.R b/R/extent.R index 4a0932f56..a7c1f6e3d 100644 --- a/R/extent.R +++ b/R/extent.R @@ -1,7 +1,19 @@ +#' Calculate display width +#' +#' `get_extent()` calculates the display width for each string in a character +#' vector. +#' +#' @param x A character vector. +#' @export get_extent <- function(x) { utf8::utf8_width(crayon::strip_style(x), encode = FALSE) } +#' @description +#' `get_max_extent()` calculates the maximum display width of all strings in a +#' character vector, zero for empty vectors. +#' @export +#' @rdname get_extent get_max_extent <- function(x) { max(get_extent(x), 0L, na.rm = TRUE) } diff --git a/R/ornament.R b/R/ornament.R index 56d176da1..8adb88185 100644 --- a/R/ornament.R +++ b/R/ornament.R @@ -13,15 +13,26 @@ #' #' @export new_ornament <- function(x, width = NULL, align = NULL) { - ret <- new_vertical( + ret <- structure( x, align = align, - extra_class = "pillar_ornament" + class = "pillar_ornament" ) ret <- set_width(ret, width) ret } +#' @export +print.pillar_ornament <- function(x, ...) { + if (length(x) > 0) { + cat_line(paste( + align(x, width = get_width(x), align = attr(x, "align")), + collapse = "\n" + )) + } + invisible(x) +} + new_vertical <- function(x, ..., subclass = NULL) { ret <- structure( x, diff --git a/R/shaft-simple.R b/R/shaft-simple.R new file mode 100644 index 000000000..16fa794f2 --- /dev/null +++ b/R/shaft-simple.R @@ -0,0 +1,47 @@ +#' @include shaft.R +#' @description +#' `new_pillar_shaft_simple()` provides an implementation of the `pillar_shaft` +#' class suitable for output that has a fixed formatting, which will be +#' truncated with a continuation character (ellipsis or `~`) if it doesn't fit +#' the available width. +#' By default, the required width is computed from the natural width of the +#' `formatted` argument. +#' +#' @details +#' The `formatted` argument may also contain ANSI escapes to change color +#' or other attributes of the text, see [crayon]. +#' +#' @param formatted An object coercible to [character]. +#' @param align Alignment of the column. +#' @param na_indent Indention of `NA` values. +#' @export +#' @rdname new_pillar_shaft +new_pillar_shaft_simple <- function(formatted, ..., width = NULL, align = "left", + min_width = NULL, na_indent = 0L) { + if (is.null(width)) { + width <- get_max_extent(as.character(formatted)) + } + + new_pillar_shaft( + list(formatted), + ..., + width = width, + min_width = min_width, + align = align, + na_indent = na_indent, + subclass = "pillar_shaft_simple" + ) +} + +#' @export +format.pillar_shaft_simple <- function(x, width, ...) { + align <- attr(x, "align") + desired_width <- get_width(x) + data <- as.character(x[[1]]) + if (width < desired_width) { + data <- str_trunc(data, width) + } + data[is.na(data)] <- paste0(strrep(" ", attr(x, "na_indent")), pillar_na()) + + new_ornament(data, width = width, align = align) +} diff --git a/R/shaft.R b/R/shaft.R index b96868a06..55e5ae17f 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -1,6 +1,35 @@ +#' Constructor for column data +#' +#' The `new_pillar_shaft()` constructor creates objects of the `"pillar_shaft"` +#' class. +#' This is a virtual or abstract class, you must specify the `subclass` +#' argument. +#' By convention, this should be a string that starts with `"pillar_shaft_"`. +#' +#' @param x An object +#' @param ... Additional attributes +#' @param width The maximum column width. +#' @param min_width The minimum allowed column width, `width` if omitted. +#' @export +new_pillar_shaft <- function(x, ..., width, min_width = width, subclass) { + stopifnot(is.character(subclass)) + stopifnot(length(subclass) > 0) + + ret <- structure( + x, + ..., + class = c(subclass, "pillar_shaft") + ) + ret <- set_width(ret, width) + ret <- set_min_width(ret, min_width) + ret +} + #' Column data #' -#' Internal class for formatting the data part of a column. +#' Internal class for formatting the data for a column. +#' `pillar_shaft()` is a coercion method that must be implemented +#' for your data type to display it in a tibble. #' #' @param x A vector to format #' @param ... Unused, for extensibility. @@ -9,48 +38,27 @@ pillar_shaft <- function(x, ...) { UseMethod("pillar_shaft") } +#' @param width Width for printing and formatting. #' @export -format.pillar_shaft <- function(x, width, ...) { - align <- attr(x, "align") - desired_width <- get_width(x) - if (width < desired_width) { - data <- str_trunc(x, width) - } else { - data <- x - } - data[is.na(x)] <- paste0(strrep(" ", attr(x, "na_indent")), pillar_na()) - - new_ornament(data, width = width, align = align) -} - -#' @export -print.pillar_shaft <- function(x, ...) { - print(format(x, ...)) +#' @rdname pillar_shaft +print.pillar_shaft <- function(x, width = NULL, ...) { + #' @description + #' This class comes with a default method for [print()] that calls [format()]. + #' If `print()` is called without `width` argument, the natural width will be + #' used when calling `format()`. + #' Usually there's no need to implement this method for your subclass. + if (is.null(width)) width <- get_width(x) + print(format(x, width = width, ...)) } #' @export -#' @param width The maximum column width, by default the natural width of `x`. -#' @param align Alignment of the column. -#' @param min_width The minimum allowed column width, `width` if omitted. -#' @param na_indent Indention of `NA` values. #' @rdname pillar_shaft -new_pillar_shaft <- function(x, ..., - width = NULL, - align = "left", min_width = NULL, - na_indent = 0L) { - if (is.null(width)) { - width <- get_max_extent(x) - } - - ret <- structure( - x, - align = align, - na_indent = na_indent, - class = "pillar_shaft" - ) - ret <- set_width(ret, width) - ret <- set_min_width(ret, min_width) - ret +format.pillar_shaft <- function(x, width, ...) { + #' @description + #' Your subclass must implement `format()`, the default implementation just + #' raises an error. + #' Your `format()` method can assume a valid value for the `width` argument. + stop("Please implement a format() method for class ", class(x)[[1]], call. = FALSE) } # Methods ----------------------------------------------------------------- @@ -62,7 +70,7 @@ pillar_shaft.logical <- function(x, ...) { out[x] <- "T" out[!x] <- "F" - new_pillar_shaft(out, width = 1, align = "left") + new_pillar_shaft_simple(out, width = 1, align = "left") } #' @export @@ -74,14 +82,14 @@ pillar_shaft.numeric <- function(x, ..., sigfig = 3) { dec <- format_decimal(x, ..., sigfig = sigfig) sci <- format_scientific(x, ..., sigfig = sigfig) - ret <- structure( - list(dec = dec, sci = sci), - class = c("pillar_shaft_decimal", "pillar_shaft") - ) + ret <- list(dec = dec, sci = sci) - ret <- set_width(ret, get_width(ret$dec)) - ret <- set_min_width(ret, min(get_min_widths(ret))) - ret + new_pillar_shaft( + ret, + width = get_width(ret$dec), + min_width = min(get_min_widths(ret)), + subclass = "pillar_shaft_decimal" + ) } #' @export @@ -89,7 +97,7 @@ pillar_shaft.numeric <- function(x, ..., sigfig = 3) { pillar_shaft.Date <- function(x, ...) { x <- format(x, format = "%Y-%m-%d") - new_pillar_shaft(x, width = 10, align = "left") + new_pillar_shaft_simple(x, width = 10, align = "left") } #' @export @@ -101,7 +109,7 @@ pillar_shaft.POSIXt <- function(x, ...) { datetime <- paste0(date, " " , style_subtle(time)) datetime[is.na(x)] <- NA - new_pillar_shaft(datetime, width = 19, align = "left") + new_pillar_shaft_simple(datetime, width = 19, align = "left") } @@ -127,7 +135,7 @@ pillar_shaft.character <- function(x, ...) { } width <- get_max_extent(out) - new_pillar_shaft(out, width = width, align = "left", min_width = min(width, 3L)) + new_pillar_shaft_simple(out, width = width, align = "left", min_width = min(width, 3L)) } #' @export @@ -137,11 +145,7 @@ pillar_shaft.list <- function(x, ...) { width <- get_max_extent(out) - new_pillar_shaft(style_list(out), width = width, align = "left", min_width = min(width, 3L)) -} - -style_list <- function(x) { - style_subtle(x) + new_pillar_shaft_simple(style_list(out), width = width, align = "left", min_width = min(width, 3L)) } #' @export @@ -153,5 +157,8 @@ pillar_shaft.AsIs <- function(x, ...) { #' @export #' @rdname pillar_shaft pillar_shaft.default <- function(x, ...) { + #' @details + #' The default method will currently coerce via [as.character()], + #' but you should not rely on this behavior. pillar_shaft(as.character(x), ...) } diff --git a/R/styles.R b/R/styles.R index 62c9c452f..214663b7a 100644 --- a/R/styles.R +++ b/R/styles.R @@ -50,3 +50,7 @@ pillar_na <- function(use_brackets_if_no_color = FALSE) { if (use_brackets_if_no_color && !crayon::has_color()) "" else style_na("NA") } + +style_list <- function(x) { + style_subtle(x) +} diff --git a/R/testthat.R b/R/testthat.R index 4db444ac1..72bd32777 100644 --- a/R/testthat.R +++ b/R/testthat.R @@ -1,26 +1,45 @@ #' Test helpers #' -#' Helper functions for packages that implement their own pillar. -#' `expect_pillar_output()` is an expectation that allows storing the -#' desired result in a file, and comparing the output with the file contents. +#' Expectation for packages that implement a data type with pillar support. +#' Allows storing the desired result in a file, +#' and comparing the output with the file contents. +#' Note that this expectation sets options that affect the formatting of the +#' pillar, see examples for usage. #' -#' @param x An object to be formatted. -#' @param ... Passed on to [pillar()] if `xf` is left at its default. -#' @param filename File name that contains the desired output. -#' @param xp Pass a value here instead of `x` if you want to omit appending -#' `NA` and `Inf` values. -#' @param xf Pass the result of a [pillar()] call here for full control. +#' @inheritParams testthat::expect_output_file +#' @param ... Unused. +#' @param width The width of the output. #' @param crayon Color the output? -#' @param output_width Passed on as `width` to [testthat::expect_output_file()]. #' @export -expect_pillar_output <- function(x, ..., filename, - xp = add_special(x), xf = pillar(xp, ...), - crayon = TRUE, output_width = 80L) { +#' @examples +#' file <- tempfile("pillar", fileext = ".txt") +#' +#' # The pillar is constructed after options have been set +#' # (need two runs because reference file doesn't exist during the first run) +#' suppressWarnings(tryCatch( +#' expect_known_display(pillar(1:3), file, crayon = FALSE), +#' expectation_failure = function(e) {} +#' )) +#' expect_known_display(pillar(1:3), file, crayon = FALSE) +#' +#' # Good: Use tidyeval to defer construction +#' pillar_quo <- rlang::quo(pillar(1:3)) +#' expect_known_display(!!pillar_quo, "integer.txt", crayon = FALSE) +#' +#' \dontrun{ +#' # Bad: Options set in the active session may affect the display +#' integer_pillar <- pillar(1:3) +#' expect_known_display(integer_pillar, "integer.txt", crayon = FALSE) +#' } +expect_known_display <- function(object, file, ..., width = 80L, crayon = TRUE) { + + object <- enquo(object) + if (crayon) { - old <- options(crayon.enabled = TRUE, crayon.colors = 16L, width = output_width) + old <- options(crayon.enabled = TRUE, crayon.colors = 16L, width = width) crayon::num_colors(forget = TRUE) } else { - old <- options(crayon.enabled = FALSE, width = output_width) + old <- options(crayon.enabled = FALSE, width = width) } on.exit({ @@ -28,21 +47,7 @@ expect_pillar_output <- function(x, ..., filename, crayon::num_colors(forget = TRUE) }) - # FIXME: Pass output_width argument here with testthat >= 2.0.0 - testthat::expect_output_file( - print(xf), - file.path("out", filename), - update = TRUE - ) -} - -#' `add_special()` is not exported, and used only for initializing default -#' values to `expect_pillar_output()`. -#' @rdname expect_pillar_output -add_special <- function(x) { - x <- c(x, NA) - if (is.numeric(x) && is.double(x)) { - x <- c(x, -Inf, Inf) - } - x + # FIXME: Use expect_known_output() for testthat >= 2.0.0, and avoid + # setting the width in the options above + testthat::expect_output_file(print(eval_tidy(object)), file, update = TRUE) } diff --git a/R/width.R b/R/width.R index 27929f5e0..7d98209d5 100644 --- a/R/width.R +++ b/R/width.R @@ -8,8 +8,6 @@ get_width <- function(x) { #' #' @param x Input to which assign a width or minimum width #' @param width,min_width The new width -#' -#' @export set_width <- function(x, width) { if (is.null(width)) return(x) if (is.infinite(width)) { @@ -29,7 +27,6 @@ get_min_width <- function(x) { } #' @rdname set_width -#' @export set_min_width <- function(x, min_width) { if (is.null(min_width)) return(x) attr(x, "min_width") <- as.integer(min_width) diff --git a/man/expect_known_display.Rd b/man/expect_known_display.Rd new file mode 100644 index 000000000..334284e2a --- /dev/null +++ b/man/expect_known_display.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/testthat.R +\name{expect_known_display} +\alias{expect_known_display} +\title{Test helpers} +\usage{ +expect_known_display(object, file, ..., width = 80L, crayon = TRUE) +} +\arguments{ +\item{object}{object to test} + +\item{file}{Path to a "golden" text file that contains the desired output.} + +\item{...}{Unused.} + +\item{width}{The width of the output.} + +\item{crayon}{Color the output?} +} +\description{ +Expectation for packages that implement a data type with pillar support. +Allows storing the desired result in a file, +and comparing the output with the file contents. +Note that this expectation sets options that affect the formatting of the +pillar, see examples for usage. +} +\examples{ +file <- tempfile("pillar", fileext = ".txt") + +# The pillar is constructed after options have been set +# (need two runs because reference file doesn't exist during the first run) +suppressWarnings(tryCatch( + expect_known_display(pillar(1:3), file, crayon = FALSE), + expectation_failure = function(e) {} +)) +expect_known_display(pillar(1:3), file, crayon = FALSE) + +# Good: Use tidyeval to defer construction +pillar_quo <- rlang::quo(pillar(1:3)) +expect_known_display(!!pillar_quo, "integer.txt", crayon = FALSE) + +\dontrun{ +# Bad: Options set in the active session may affect the display +integer_pillar <- pillar(1:3) +expect_known_display(integer_pillar, "integer.txt", crayon = FALSE) +} +} diff --git a/man/expect_pillar_output.Rd b/man/expect_pillar_output.Rd deleted file mode 100644 index 1e25d14f1..000000000 --- a/man/expect_pillar_output.Rd +++ /dev/null @@ -1,36 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/testthat.R -\name{expect_pillar_output} -\alias{expect_pillar_output} -\alias{add_special} -\title{Test helpers} -\usage{ -expect_pillar_output(x, ..., filename, xp = add_special(x), xf = pillar(xp, - ...), crayon = TRUE, output_width = 80L) - -add_special(x) -} -\arguments{ -\item{x}{An object to be formatted.} - -\item{...}{Passed on to \code{\link[=pillar]{pillar()}} if \code{xf} is left at its default.} - -\item{filename}{File name that contains the desired output.} - -\item{xp}{Pass a value here instead of \code{x} if you want to omit appending -\code{NA} and \code{Inf} values.} - -\item{xf}{Pass the result of a \code{\link[=pillar]{pillar()}} call here for full control.} - -\item{crayon}{Color the output?} - -\item{output_width}{Passed on as \code{width} to \code{\link[testthat:expect_output_file]{testthat::expect_output_file()}}.} -} -\description{ -Helper functions for packages that implement their own pillar. -\code{expect_pillar_output()} is an expectation that allows storing the -desired result in a file, and comparing the output with the file contents. - -\code{add_special()} is not exported, and used only for initializing default -values to \code{expect_pillar_output()}. -} diff --git a/man/get_extent.Rd b/man/get_extent.Rd new file mode 100644 index 000000000..eddf8fcff --- /dev/null +++ b/man/get_extent.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/extent.R +\name{get_extent} +\alias{get_extent} +\alias{get_max_extent} +\title{Calculate display width} +\usage{ +get_extent(x) + +get_max_extent(x) +} +\arguments{ +\item{x}{A character vector.} +} +\description{ +\code{get_extent()} calculates the display width for each string in a character +vector. + +\code{get_max_extent()} calculates the maximum display width of all strings in a +character vector, zero for empty vectors. +} diff --git a/man/new_pillar_shaft.Rd b/man/new_pillar_shaft.Rd new file mode 100644 index 000000000..6132e9773 --- /dev/null +++ b/man/new_pillar_shaft.Rd @@ -0,0 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/shaft.R, R/shaft-simple.R +\name{new_pillar_shaft} +\alias{new_pillar_shaft} +\alias{new_pillar_shaft_simple} +\title{Constructor for column data} +\usage{ +new_pillar_shaft(x, ..., width, min_width = width, subclass) + +new_pillar_shaft_simple(formatted, ..., width = NULL, align = "left", + min_width = NULL, na_indent = 0L) +} +\arguments{ +\item{x}{An object} + +\item{...}{Additional attributes} + +\item{width}{The maximum column width.} + +\item{min_width}{The minimum allowed column width, \code{width} if omitted.} + +\item{formatted}{An object coercible to \link{character}.} + +\item{align}{Alignment of the column.} + +\item{na_indent}{Indention of \code{NA} values.} +} +\description{ +The \code{new_pillar_shaft()} constructor creates objects of the \code{"pillar_shaft"} +class. +This is a virtual or abstract class, you must specify the \code{subclass} +argument. +By convention, this should be a string that starts with \code{"pillar_shaft_"}. + +\code{new_pillar_shaft_simple()} provides an implementation of the \code{pillar_shaft} +class suitable for output that has a fixed formatting, which will be +truncated with a continuation character (ellipsis or \code{~}) if it doesn't fit +the available width. +By default, the required width is computed from the natural width of the +\code{formatted} argument. +} +\details{ +The \code{formatted} argument may also contain ANSI escapes to change color +or other attributes of the text, see \link{crayon}. +} diff --git a/man/pillar_shaft.Rd b/man/pillar_shaft.Rd index ed68456f0..79e3a3da3 100644 --- a/man/pillar_shaft.Rd +++ b/man/pillar_shaft.Rd @@ -2,7 +2,8 @@ % Please edit documentation in R/shaft.R \name{pillar_shaft} \alias{pillar_shaft} -\alias{new_pillar_shaft} +\alias{print.pillar_shaft} +\alias{format.pillar_shaft} \alias{pillar_shaft.logical} \alias{pillar_shaft.numeric} \alias{pillar_shaft.Date} @@ -15,8 +16,9 @@ \usage{ pillar_shaft(x, ...) -new_pillar_shaft(x, ..., width = NULL, align = "left", min_width = NULL, - na_indent = 0L) +\method{print}{pillar_shaft}(x, width = NULL, ...) + +\method{format}{pillar_shaft}(x, width, ...) \method{pillar_shaft}{logical}(x, ...) @@ -39,18 +41,27 @@ new_pillar_shaft(x, ..., width = NULL, align = "left", min_width = NULL, \item{...}{Unused, for extensibility.} -\item{width}{The maximum column width, by default the natural width of \code{x}.} - -\item{align}{Alignment of the column.} - -\item{min_width}{The minimum allowed column width, \code{width} if omitted.} - -\item{na_indent}{Indention of \code{NA} values.} +\item{width}{Width for printing and formatting.} \item{sigfig}{Minimum number of significant figures to display. Numbers larger than 1 will potentially show more signficiant figures than this but they will be greyed out.} } \description{ -Internal class for formatting the data part of a column. +Internal class for formatting the data for a column. +\code{pillar_shaft()} is a coercion method that must be implemented +for your data type to display it in a tibble. + +This class comes with a default method for \code{\link[=print]{print()}} that calls \code{\link[=format]{format()}}. +If \code{print()} is called without \code{width} argument, the natural width will be +used when calling \code{format()}. +Usually there's no need to implement this method for your subclass. + +Your subclass must implement \code{format()}, the default implementation just +raises an error. +Your \code{format()} method can assume a valid value for the \code{width} argument. +} +\details{ +The default method will currently coerce via \code{\link[=as.character]{as.character()}}, +but you should not rely on this behavior. } diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 5dda8a9e6..eb4a34f49 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -14,3 +14,39 @@ df_all <- list( h = as.list(c(1:2, NA)), i = list(list(1, 2:3), list(4:6), list(NA)) ) + +expect_pillar_output <- function(x = NULL, ..., filename, xp = NULL, xf = NULL, + crayon = TRUE, output_width = 80L) { + object_quo <- quo(get_pillar_output_object(x, ..., xp = xp, xf = xf)) + expect_known_display( + object = !!object_quo, + file = file.path("out", filename), + crayon = crayon, + width = output_width + ) +} + +get_pillar_output_object <- function(x = NULL, xp = NULL, xf = NULL, ...) { + #' @details + #' - If `x` is passed, `xp` is computed by appending `NA` (and for numerics + #' `+Inf` and `-Inf`). + xp <- xp %||% add_special(x) + + #' - If `xp` is passed or available computed from `x`, `xf` is derived by + #' calling `pillar(xp, ...)`. + xf <- xf %||% pillar(xp, ...) + + #' - The output is generated by printing `xf`. + xf +} + +#' `add_special()` is not exported, and used only for initializing default +#' values to `expect_pillar_output()`. +#' @rdname expect_pillar_output +add_special <- function(x) { + x <- c(x, NA) + if (is.numeric(x) && is.double(x)) { + x <- c(x, -Inf, Inf) + } + x +} From 523d40fbb3c993c03b26e957e4d42aa062176215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 15 Nov 2017 23:39:38 +0100 Subject: [PATCH 104/133] oops --- R/testthat.R | 4 ++-- man/expect_known_display.Rd | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/testthat.R b/R/testthat.R index 72bd32777..bd743dbe3 100644 --- a/R/testthat.R +++ b/R/testthat.R @@ -24,12 +24,12 @@ #' #' # Good: Use tidyeval to defer construction #' pillar_quo <- rlang::quo(pillar(1:3)) -#' expect_known_display(!!pillar_quo, "integer.txt", crayon = FALSE) +#' expect_known_display(!!pillar_quo, file, crayon = FALSE) #' #' \dontrun{ #' # Bad: Options set in the active session may affect the display #' integer_pillar <- pillar(1:3) -#' expect_known_display(integer_pillar, "integer.txt", crayon = FALSE) +#' expect_known_display(integer_pillar, file, crayon = FALSE) #' } expect_known_display <- function(object, file, ..., width = 80L, crayon = TRUE) { diff --git a/man/expect_known_display.Rd b/man/expect_known_display.Rd index 334284e2a..a243bec67 100644 --- a/man/expect_known_display.Rd +++ b/man/expect_known_display.Rd @@ -37,11 +37,11 @@ expect_known_display(pillar(1:3), file, crayon = FALSE) # Good: Use tidyeval to defer construction pillar_quo <- rlang::quo(pillar(1:3)) -expect_known_display(!!pillar_quo, "integer.txt", crayon = FALSE) +expect_known_display(!!pillar_quo, file, crayon = FALSE) \dontrun{ # Bad: Options set in the active session may affect the display integer_pillar <- pillar(1:3) -expect_known_display(integer_pillar, "integer.txt", crayon = FALSE) +expect_known_display(integer_pillar, file, crayon = FALSE) } } From b3158bb425eca3e385e429f04be7c90a6c9ae40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 15 Nov 2017 23:55:37 +0100 Subject: [PATCH 105/133] document --- R/shaft.R | 1 + man/new_pillar_shaft.Rd | 2 ++ 2 files changed, 3 insertions(+) diff --git a/R/shaft.R b/R/shaft.R index 55e5ae17f..aea251e4e 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -10,6 +10,7 @@ #' @param ... Additional attributes #' @param width The maximum column width. #' @param min_width The minimum allowed column width, `width` if omitted. +#' @param subclass The name of the subclass. #' @export new_pillar_shaft <- function(x, ..., width, min_width = width, subclass) { stopifnot(is.character(subclass)) diff --git a/man/new_pillar_shaft.Rd b/man/new_pillar_shaft.Rd index 6132e9773..aaf69d36e 100644 --- a/man/new_pillar_shaft.Rd +++ b/man/new_pillar_shaft.Rd @@ -19,6 +19,8 @@ new_pillar_shaft_simple(formatted, ..., width = NULL, align = "left", \item{min_width}{The minimum allowed column width, \code{width} if omitted.} +\item{subclass}{The name of the subclass.} + \item{formatted}{An object coercible to \link{character}.} \item{align}{Alignment of the column.} From 9825ac3246784bd2bb018e213bbcf6f80eb97b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 00:54:37 +0100 Subject: [PATCH 106/133] test twice --- tests/testthat/helper-output.R | 38 +++++++++++-------- tests/testthat/out-native/asis-list.txt | 4 ++ tests/testthat/out-native/asis-number.txt | 4 ++ tests/testthat/out-native/basic.txt | 12 ++++++ tests/testthat/out-native/date.txt | 3 ++ tests/testthat/out-native/deal1.txt | 4 ++ tests/testthat/out-native/deal2.txt | 4 ++ tests/testthat/out-native/deal3.txt | 4 ++ .../testthat/out-native/decimal-insignif.txt | 11 ++++++ tests/testthat/out-native/escaped.txt | 2 + tests/testthat/out-native/factor.txt | 7 ++++ tests/testthat/out-native/integer-06.txt | 5 +++ tests/testthat/out-native/integer-07.txt | 5 +++ tests/testthat/out-native/integer-08.txt | 5 +++ tests/testthat/out-native/integer-09.txt | 5 +++ tests/testthat/out-native/letters-long-03.txt | 3 ++ tests/testthat/out-native/letters-long-10.txt | 3 ++ tests/testthat/out-native/letters-long.txt | 3 ++ tests/testthat/out-native/letters.txt | 7 ++++ tests/testthat/out-native/list-each.txt | 4 ++ tests/testthat/out-native/list-na.txt | 3 ++ tests/testthat/out-native/list-narrow.txt | 2 + tests/testthat/out-native/list-null.txt | 3 ++ tests/testthat/out-native/logical.txt | 4 ++ tests/testthat/out-native/multi-04.txt | 0 tests/testthat/out-native/multi-05.txt | 0 tests/testthat/out-native/multi-06.txt | 0 tests/testthat/out-native/multi-07.txt | 0 tests/testthat/out-native/multi-08.txt | 5 +++ tests/testthat/out-native/multi-09.txt | 5 +++ tests/testthat/out-native/multi-10.txt | 5 +++ tests/testthat/out-native/multi-11.txt | 5 +++ tests/testthat/out-native/multi-12.txt | 5 +++ tests/testthat/out-native/multi-13.txt | 5 +++ tests/testthat/out-native/multi-14.txt | 5 +++ tests/testthat/out-native/multi-15.txt | 5 +++ tests/testthat/out-native/multi-16.txt | 5 +++ tests/testthat/out-native/multi-17.txt | 5 +++ tests/testthat/out-native/multi-18.txt | 5 +++ tests/testthat/out-native/multi-19.txt | 5 +++ tests/testthat/out-native/multi-20.txt | 5 +++ tests/testthat/out-native/multi-21.txt | 5 +++ tests/testthat/out-native/multi-22.txt | 5 +++ tests/testthat/out-native/multi-23.txt | 5 +++ tests/testthat/out-native/multi-24.txt | 5 +++ tests/testthat/out-native/multi-25.txt | 5 +++ tests/testthat/out-native/multi-26.txt | 5 +++ tests/testthat/out-native/multi-27.txt | 5 +++ tests/testthat/out-native/multi-28.txt | 5 +++ tests/testthat/out-native/multi-29.txt | 5 +++ tests/testthat/out-native/multi-30.txt | 5 +++ tests/testthat/out-native/multi-31.txt | 5 +++ tests/testthat/out-native/multi-32.txt | 5 +++ tests/testthat/out-native/multi-33.txt | 5 +++ tests/testthat/out-native/multi-34.txt | 5 +++ tests/testthat/out-native/multi-35.txt | 5 +++ tests/testthat/out-native/multi-36.txt | 5 +++ tests/testthat/out-native/multi-37.txt | 5 +++ tests/testthat/out-native/multi-38.txt | 5 +++ tests/testthat/out-native/multi-39.txt | 5 +++ tests/testthat/out-native/multi-extra-10.txt | 3 ++ tests/testthat/out-native/multi-inf.txt | 5 +++ tests/testthat/out-native/numeric-04.txt | 5 +++ tests/testthat/out-native/numeric-07.txt | 5 +++ tests/testthat/out-native/numeric-10.txt | 5 +++ tests/testthat/out-native/numeric-15.txt | 5 +++ tests/testthat/out-native/numeric-22.txt | 5 +++ tests/testthat/out-native/ordered.txt | 7 ++++ tests/testthat/out-native/rowid-3.txt | 4 ++ .../out-native/rowid-star-title-12.txt | 14 +++++++ .../out-native/scientific-short-neg.txt | 8 ++++ tests/testthat/out-native/scientific.txt | 8 ++++ tests/testthat/out-native/spaces.txt | 7 ++++ tests/testthat/out-native/tibble-all--30.txt | 5 +++ .../out-native/tibble-all--300-20.txt | 10 +++++ .../out-native/tibble-all--300-30.txt | 10 +++++ .../out-native/tibble-all--300-40.txt | 10 +++++ .../out-native/tibble-all--300-50.txt | 10 +++++ .../out-native/tibble-all--300-60.txt | 10 +++++ .../out-native/tibble-all--300-70.txt | 10 +++++ tests/testthat/out-native/tibble-all--300.txt | 10 +++++ .../testthat/out-native/tibble-iris-3-20.txt | 5 +++ .../testthat/out-native/tibble-iris-5-30.txt | 7 ++++ .../out-native/tibble-mtcars-8-30.txt | 10 +++++ tests/testthat/out-native/tibble-newline.txt | 4 ++ .../out-native/tibble-non-syntactic.txt | 3 ++ tests/testthat/out-native/tibble-space.txt | 6 +++ tests/testthat/out-native/time-posix.txt | 3 ++ tests/testthat/out-native/time.txt | 3 ++ tests/testthat/out-native/title-crayon.txt | 11 ++++++ tests/testthat/out-native/title-longer.txt | 11 ++++++ tests/testthat/out-native/title-none.txt | 10 +++++ tests/testthat/out-native/title-short.txt | 11 ++++++ tests/testthat/out-native/title-too-long.txt | 11 ++++++ tests/testthat/out-native/utf8.txt | 17 +++++++++ tests/testthat/out/tibble-all--30.txt | 10 ++--- tests/testthat/out/tibble-all--300-20.txt | 25 +++++------- tests/testthat/out/tibble-all--300-30.txt | 30 +++++---------- tests/testthat/out/tibble-all--300-40.txt | 25 +++++------- tests/testthat/out/tibble-all--300-50.txt | 20 +++++----- tests/testthat/out/tibble-all--300-60.txt | 20 +++++----- tests/testthat/out/tibble-all--300-70.txt | 20 +++++----- tests/testthat/out/tibble-all--300.txt | 20 +++++----- tests/testthat/out/tibble-iris-3-20.txt | 10 ++--- tests/testthat/out/tibble-iris-5-30.txt | 14 +++---- tests/testthat/out/tibble-mtcars-8-30.txt | 20 +++++----- tests/testthat/out/tibble-newline.txt | 8 ++-- tests/testthat/out/tibble-non-syntactic.txt | 6 +-- tests/testthat/out/tibble-space.txt | 12 +++--- tests/testthat/out/title-longer.txt | 18 ++++----- tests/testthat/out/title-none.txt | 16 ++++---- tests/testthat/out/title-short.txt | 18 ++++----- tests/testthat/out/title-too-long.txt | 22 +++++------ 113 files changed, 705 insertions(+), 182 deletions(-) create mode 100644 tests/testthat/out-native/asis-list.txt create mode 100644 tests/testthat/out-native/asis-number.txt create mode 100644 tests/testthat/out-native/basic.txt create mode 100644 tests/testthat/out-native/date.txt create mode 100644 tests/testthat/out-native/deal1.txt create mode 100644 tests/testthat/out-native/deal2.txt create mode 100644 tests/testthat/out-native/deal3.txt create mode 100644 tests/testthat/out-native/decimal-insignif.txt create mode 100644 tests/testthat/out-native/escaped.txt create mode 100644 tests/testthat/out-native/factor.txt create mode 100644 tests/testthat/out-native/integer-06.txt create mode 100644 tests/testthat/out-native/integer-07.txt create mode 100644 tests/testthat/out-native/integer-08.txt create mode 100644 tests/testthat/out-native/integer-09.txt create mode 100644 tests/testthat/out-native/letters-long-03.txt create mode 100644 tests/testthat/out-native/letters-long-10.txt create mode 100644 tests/testthat/out-native/letters-long.txt create mode 100644 tests/testthat/out-native/letters.txt create mode 100644 tests/testthat/out-native/list-each.txt create mode 100644 tests/testthat/out-native/list-na.txt create mode 100644 tests/testthat/out-native/list-narrow.txt create mode 100644 tests/testthat/out-native/list-null.txt create mode 100644 tests/testthat/out-native/logical.txt create mode 100644 tests/testthat/out-native/multi-04.txt create mode 100644 tests/testthat/out-native/multi-05.txt create mode 100644 tests/testthat/out-native/multi-06.txt create mode 100644 tests/testthat/out-native/multi-07.txt create mode 100644 tests/testthat/out-native/multi-08.txt create mode 100644 tests/testthat/out-native/multi-09.txt create mode 100644 tests/testthat/out-native/multi-10.txt create mode 100644 tests/testthat/out-native/multi-11.txt create mode 100644 tests/testthat/out-native/multi-12.txt create mode 100644 tests/testthat/out-native/multi-13.txt create mode 100644 tests/testthat/out-native/multi-14.txt create mode 100644 tests/testthat/out-native/multi-15.txt create mode 100644 tests/testthat/out-native/multi-16.txt create mode 100644 tests/testthat/out-native/multi-17.txt create mode 100644 tests/testthat/out-native/multi-18.txt create mode 100644 tests/testthat/out-native/multi-19.txt create mode 100644 tests/testthat/out-native/multi-20.txt create mode 100644 tests/testthat/out-native/multi-21.txt create mode 100644 tests/testthat/out-native/multi-22.txt create mode 100644 tests/testthat/out-native/multi-23.txt create mode 100644 tests/testthat/out-native/multi-24.txt create mode 100644 tests/testthat/out-native/multi-25.txt create mode 100644 tests/testthat/out-native/multi-26.txt create mode 100644 tests/testthat/out-native/multi-27.txt create mode 100644 tests/testthat/out-native/multi-28.txt create mode 100644 tests/testthat/out-native/multi-29.txt create mode 100644 tests/testthat/out-native/multi-30.txt create mode 100644 tests/testthat/out-native/multi-31.txt create mode 100644 tests/testthat/out-native/multi-32.txt create mode 100644 tests/testthat/out-native/multi-33.txt create mode 100644 tests/testthat/out-native/multi-34.txt create mode 100644 tests/testthat/out-native/multi-35.txt create mode 100644 tests/testthat/out-native/multi-36.txt create mode 100644 tests/testthat/out-native/multi-37.txt create mode 100644 tests/testthat/out-native/multi-38.txt create mode 100644 tests/testthat/out-native/multi-39.txt create mode 100644 tests/testthat/out-native/multi-extra-10.txt create mode 100644 tests/testthat/out-native/multi-inf.txt create mode 100644 tests/testthat/out-native/numeric-04.txt create mode 100644 tests/testthat/out-native/numeric-07.txt create mode 100644 tests/testthat/out-native/numeric-10.txt create mode 100644 tests/testthat/out-native/numeric-15.txt create mode 100644 tests/testthat/out-native/numeric-22.txt create mode 100644 tests/testthat/out-native/ordered.txt create mode 100644 tests/testthat/out-native/rowid-3.txt create mode 100644 tests/testthat/out-native/rowid-star-title-12.txt create mode 100644 tests/testthat/out-native/scientific-short-neg.txt create mode 100644 tests/testthat/out-native/scientific.txt create mode 100644 tests/testthat/out-native/spaces.txt create mode 100644 tests/testthat/out-native/tibble-all--30.txt create mode 100644 tests/testthat/out-native/tibble-all--300-20.txt create mode 100644 tests/testthat/out-native/tibble-all--300-30.txt create mode 100644 tests/testthat/out-native/tibble-all--300-40.txt create mode 100644 tests/testthat/out-native/tibble-all--300-50.txt create mode 100644 tests/testthat/out-native/tibble-all--300-60.txt create mode 100644 tests/testthat/out-native/tibble-all--300-70.txt create mode 100644 tests/testthat/out-native/tibble-all--300.txt create mode 100644 tests/testthat/out-native/tibble-iris-3-20.txt create mode 100644 tests/testthat/out-native/tibble-iris-5-30.txt create mode 100644 tests/testthat/out-native/tibble-mtcars-8-30.txt create mode 100644 tests/testthat/out-native/tibble-newline.txt create mode 100644 tests/testthat/out-native/tibble-non-syntactic.txt create mode 100644 tests/testthat/out-native/tibble-space.txt create mode 100644 tests/testthat/out-native/time-posix.txt create mode 100644 tests/testthat/out-native/time.txt create mode 100644 tests/testthat/out-native/title-crayon.txt create mode 100644 tests/testthat/out-native/title-longer.txt create mode 100644 tests/testthat/out-native/title-none.txt create mode 100644 tests/testthat/out-native/title-short.txt create mode 100644 tests/testthat/out-native/title-too-long.txt create mode 100644 tests/testthat/out-native/utf8.txt diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index eb4a34f49..905e685a7 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -17,26 +17,34 @@ df_all <- list( expect_pillar_output <- function(x = NULL, ..., filename, xp = NULL, xf = NULL, crayon = TRUE, output_width = 80L) { - object_quo <- quo(get_pillar_output_object(x, ..., xp = xp, xf = xf)) - expect_known_display( - object = !!object_quo, - file = file.path("out", filename), - crayon = crayon, - width = output_width + object_quo <- rlang::quo(get_pillar_output_object(x, ..., xp = xp, xf = xf)) + if (l10n_info()$`UTF-8`) { + expect_known_display( + object = !!object_quo, + file = file.path("out", filename), + crayon = TRUE, + width = output_width + ) + } + withr::with_locale( + c(LC_CTYPE = "C"), + expect_known_display( + object = !!object_quo, + file = file.path("out-native", filename), + crayon = FALSE, + width = output_width + ) ) } get_pillar_output_object <- function(x = NULL, xp = NULL, xf = NULL, ...) { - #' @details - #' - If `x` is passed, `xp` is computed by appending `NA` (and for numerics - #' `+Inf` and `-Inf`). - xp <- xp %||% add_special(x) - - #' - If `xp` is passed or available computed from `x`, `xf` is derived by - #' calling `pillar(xp, ...)`. - xf <- xf %||% pillar(xp, ...) + if (is.null(xf)) { + if (is.null(xp)) { + xp <- add_special(x) + } + xf <- pillar(xp, ...) + } - #' - The output is generated by printing `xf`. xf } diff --git a/tests/testthat/out-native/asis-list.txt b/tests/testthat/out-native/asis-list.txt new file mode 100644 index 000000000..10f834f90 --- /dev/null +++ b/tests/testthat/out-native/asis-list.txt @@ -0,0 +1,4 @@ + + + + diff --git a/tests/testthat/out-native/asis-number.txt b/tests/testthat/out-native/asis-number.txt new file mode 100644 index 000000000..f99d264c3 --- /dev/null +++ b/tests/testthat/out-native/asis-number.txt @@ -0,0 +1,4 @@ + + 1 + 2 + 3 diff --git a/tests/testthat/out-native/basic.txt b/tests/testthat/out-native/basic.txt new file mode 100644 index 000000000..629c54673 --- /dev/null +++ b/tests/testthat/out-native/basic.txt @@ -0,0 +1,12 @@ + +- 0.00100 + 0.0100 +- 0.100 + 1.00 +- 10.0 + 100 +- 1000 + 10000 + NA +- Inf + Inf diff --git a/tests/testthat/out-native/date.txt b/tests/testthat/out-native/date.txt new file mode 100644 index 000000000..92c1b255c --- /dev/null +++ b/tests/testthat/out-native/date.txt @@ -0,0 +1,3 @@ + +2017-07-28 +NA diff --git a/tests/testthat/out-native/deal1.txt b/tests/testthat/out-native/deal1.txt new file mode 100644 index 000000000..814e0b3a2 --- /dev/null +++ b/tests/testthat/out-native/deal1.txt @@ -0,0 +1,4 @@ +`\u6210\u4ea4` + +"\u6210\u4ea4\u65e5" + diff --git a/tests/testthat/out-native/deal2.txt b/tests/testthat/out-native/deal2.txt new file mode 100644 index 000000000..e62c1b595 --- /dev/null +++ b/tests/testthat/out-native/deal2.txt @@ -0,0 +1,4 @@ +`\u6210\u4ea4\u65e5` + +"\u6210\u4ea4" + diff --git a/tests/testthat/out-native/deal3.txt b/tests/testthat/out-native/deal3.txt new file mode 100644 index 000000000..8f8021e37 --- /dev/null +++ b/tests/testthat/out-native/deal3.txt @@ -0,0 +1,4 @@ +`\u6210\u4ea4\u65e5` + + 1 + NA diff --git a/tests/testthat/out-native/decimal-insignif.txt b/tests/testthat/out-native/decimal-insignif.txt new file mode 100644 index 000000000..2bb6f5c17 --- /dev/null +++ b/tests/testthat/out-native/decimal-insignif.txt @@ -0,0 +1,11 @@ + + 0.00123 + 0.0123 + 0.123 + 1.23 + 12.3 + 123 + 1235 + NA +- Inf + Inf diff --git a/tests/testthat/out-native/escaped.txt b/tests/testthat/out-native/escaped.txt new file mode 100644 index 000000000..57ddff7df --- /dev/null +++ b/tests/testthat/out-native/escaped.txt @@ -0,0 +1,2 @@ + +"a\nb" diff --git a/tests/testthat/out-native/factor.txt b/tests/testthat/out-native/factor.txt new file mode 100644 index 000000000..8594ae904 --- /dev/null +++ b/tests/testthat/out-native/factor.txt @@ -0,0 +1,7 @@ + +a +b +c +d +e + diff --git a/tests/testthat/out-native/integer-06.txt b/tests/testthat/out-native/integer-06.txt new file mode 100644 index 000000000..479021af3 --- /dev/null +++ b/tests/testthat/out-native/integer-06.txt @@ -0,0 +1,5 @@ + + 1.00e7 + 1.00e7 + 1.00e7 +NA diff --git a/tests/testthat/out-native/integer-07.txt b/tests/testthat/out-native/integer-07.txt new file mode 100644 index 000000000..479021af3 --- /dev/null +++ b/tests/testthat/out-native/integer-07.txt @@ -0,0 +1,5 @@ + + 1.00e7 + 1.00e7 + 1.00e7 +NA diff --git a/tests/testthat/out-native/integer-08.txt b/tests/testthat/out-native/integer-08.txt new file mode 100644 index 000000000..ee46f063c --- /dev/null +++ b/tests/testthat/out-native/integer-08.txt @@ -0,0 +1,5 @@ + +10000001 +10000002 +10000003 + NA diff --git a/tests/testthat/out-native/integer-09.txt b/tests/testthat/out-native/integer-09.txt new file mode 100644 index 000000000..dd5a35a8d --- /dev/null +++ b/tests/testthat/out-native/integer-09.txt @@ -0,0 +1,5 @@ + + 10000001 + 10000002 + 10000003 + NA diff --git a/tests/testthat/out-native/letters-long-03.txt b/tests/testthat/out-native/letters-long-03.txt new file mode 100644 index 000000000..4c228c3ee --- /dev/null +++ b/tests/testthat/out-native/letters-long-03.txt @@ -0,0 +1,3 @@ + +abcd~ + diff --git a/tests/testthat/out-native/letters-long-10.txt b/tests/testthat/out-native/letters-long-10.txt new file mode 100644 index 000000000..38d480339 --- /dev/null +++ b/tests/testthat/out-native/letters-long-10.txt @@ -0,0 +1,3 @@ + +abcdefghi~ + diff --git a/tests/testthat/out-native/letters-long.txt b/tests/testthat/out-native/letters-long.txt new file mode 100644 index 000000000..ebf11d335 --- /dev/null +++ b/tests/testthat/out-native/letters-long.txt @@ -0,0 +1,3 @@ + +abcdefghijklmnopqrstuvwxyz + diff --git a/tests/testthat/out-native/letters.txt b/tests/testthat/out-native/letters.txt new file mode 100644 index 000000000..cb604ce93 --- /dev/null +++ b/tests/testthat/out-native/letters.txt @@ -0,0 +1,7 @@ + +a +b +c +d +e + diff --git a/tests/testthat/out-native/list-each.txt b/tests/testthat/out-native/list-each.txt new file mode 100644 index 000000000..89d2f3cdf --- /dev/null +++ b/tests/testthat/out-native/list-each.txt @@ -0,0 +1,4 @@ + + + + diff --git a/tests/testthat/out-native/list-na.txt b/tests/testthat/out-native/list-na.txt new file mode 100644 index 000000000..753fac21b --- /dev/null +++ b/tests/testthat/out-native/list-na.txt @@ -0,0 +1,3 @@ + + + diff --git a/tests/testthat/out-native/list-narrow.txt b/tests/testthat/out-native/list-narrow.txt new file mode 100644 index 000000000..a0ead9edf --- /dev/null +++ b/tests/testthat/out-native/list-narrow.txt @@ -0,0 +1,2 @@ + + + + diff --git a/tests/testthat/out-native/logical.txt b/tests/testthat/out-native/logical.txt new file mode 100644 index 000000000..52f702a1c --- /dev/null +++ b/tests/testthat/out-native/logical.txt @@ -0,0 +1,4 @@ + +T +F +NA diff --git a/tests/testthat/out-native/multi-04.txt b/tests/testthat/out-native/multi-04.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/testthat/out-native/multi-05.txt b/tests/testthat/out-native/multi-05.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/testthat/out-native/multi-06.txt b/tests/testthat/out-native/multi-06.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/testthat/out-native/multi-07.txt b/tests/testthat/out-native/multi-07.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/testthat/out-native/multi-08.txt b/tests/testthat/out-native/multi-08.txt new file mode 100644 index 000000000..f10ee837d --- /dev/null +++ b/tests/testthat/out-native/multi-08.txt @@ -0,0 +1,5 @@ + colu~ + +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out-native/multi-09.txt b/tests/testthat/out-native/multi-09.txt new file mode 100644 index 000000000..85e1fd915 --- /dev/null +++ b/tests/testthat/out-native/multi-09.txt @@ -0,0 +1,5 @@ + colum~ + +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out-native/multi-10.txt b/tests/testthat/out-native/multi-10.txt new file mode 100644 index 000000000..4a46bdf25 --- /dev/null +++ b/tests/testthat/out-native/multi-10.txt @@ -0,0 +1,5 @@ + column~ + +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out-native/multi-11.txt b/tests/testthat/out-native/multi-11.txt new file mode 100644 index 000000000..3d941d6b7 --- /dev/null +++ b/tests/testthat/out-native/multi-11.txt @@ -0,0 +1,5 @@ + column_~ + +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out-native/multi-12.txt b/tests/testthat/out-native/multi-12.txt new file mode 100644 index 000000000..e103815cb --- /dev/null +++ b/tests/testthat/out-native/multi-12.txt @@ -0,0 +1,5 @@ + column_z~ + +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out-native/multi-13.txt b/tests/testthat/out-native/multi-13.txt new file mode 100644 index 000000000..f41911c27 --- /dev/null +++ b/tests/testthat/out-native/multi-13.txt @@ -0,0 +1,5 @@ + column_ze~ + +1 1.23 +2 2.23 +3 3.23 diff --git a/tests/testthat/out-native/multi-14.txt b/tests/testthat/out-native/multi-14.txt new file mode 100644 index 000000000..7512ca7a9 --- /dev/null +++ b/tests/testthat/out-native/multi-14.txt @@ -0,0 +1,5 @@ + colu~ col_~ + +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out-native/multi-15.txt b/tests/testthat/out-native/multi-15.txt new file mode 100644 index 000000000..acf2ce7e1 --- /dev/null +++ b/tests/testthat/out-native/multi-15.txt @@ -0,0 +1,5 @@ + colum~ col_~ + +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out-native/multi-16.txt b/tests/testthat/out-native/multi-16.txt new file mode 100644 index 000000000..d1e156f47 --- /dev/null +++ b/tests/testthat/out-native/multi-16.txt @@ -0,0 +1,5 @@ + column~ col_~ + +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out-native/multi-17.txt b/tests/testthat/out-native/multi-17.txt new file mode 100644 index 000000000..3727f1efa --- /dev/null +++ b/tests/testthat/out-native/multi-17.txt @@ -0,0 +1,5 @@ + column_~ col_~ + +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out-native/multi-18.txt b/tests/testthat/out-native/multi-18.txt new file mode 100644 index 000000000..9bf9d5639 --- /dev/null +++ b/tests/testthat/out-native/multi-18.txt @@ -0,0 +1,5 @@ + column_z~ col_~ + +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out-native/multi-19.txt b/tests/testthat/out-native/multi-19.txt new file mode 100644 index 000000000..172b8b184 --- /dev/null +++ b/tests/testthat/out-native/multi-19.txt @@ -0,0 +1,5 @@ + column_ze~ col_~ + +1 1.23 a +2 2.23 b +3 3.23 c diff --git a/tests/testthat/out-native/multi-20.txt b/tests/testthat/out-native/multi-20.txt new file mode 100644 index 000000000..dbb970adf --- /dev/null +++ b/tests/testthat/out-native/multi-20.txt @@ -0,0 +1,5 @@ + colu~ col_~ col_~ + +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out-native/multi-21.txt b/tests/testthat/out-native/multi-21.txt new file mode 100644 index 000000000..9484e5a68 --- /dev/null +++ b/tests/testthat/out-native/multi-21.txt @@ -0,0 +1,5 @@ + colum~ col_~ col_~ + +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out-native/multi-22.txt b/tests/testthat/out-native/multi-22.txt new file mode 100644 index 000000000..f0887cc56 --- /dev/null +++ b/tests/testthat/out-native/multi-22.txt @@ -0,0 +1,5 @@ + column~ col_~ col_~ + +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out-native/multi-23.txt b/tests/testthat/out-native/multi-23.txt new file mode 100644 index 000000000..f89ed8a07 --- /dev/null +++ b/tests/testthat/out-native/multi-23.txt @@ -0,0 +1,5 @@ + column_~ col_~ col_~ + +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out-native/multi-24.txt b/tests/testthat/out-native/multi-24.txt new file mode 100644 index 000000000..3448a150f --- /dev/null +++ b/tests/testthat/out-native/multi-24.txt @@ -0,0 +1,5 @@ + column_z~ col_~ col_~ + +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out-native/multi-25.txt b/tests/testthat/out-native/multi-25.txt new file mode 100644 index 000000000..788141085 --- /dev/null +++ b/tests/testthat/out-native/multi-25.txt @@ -0,0 +1,5 @@ + column_ze~ col_~ col_~ + +1 1.23 a a +2 2.23 b b +3 3.23 c c diff --git a/tests/testthat/out-native/multi-26.txt b/tests/testthat/out-native/multi-26.txt new file mode 100644 index 000000000..a6bf85581 --- /dev/null +++ b/tests/testthat/out-native/multi-26.txt @@ -0,0 +1,5 @@ + colu~ col_~ col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-27.txt b/tests/testthat/out-native/multi-27.txt new file mode 100644 index 000000000..7bc9dceae --- /dev/null +++ b/tests/testthat/out-native/multi-27.txt @@ -0,0 +1,5 @@ + colum~ col_~ col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-28.txt b/tests/testthat/out-native/multi-28.txt new file mode 100644 index 000000000..ab167b288 --- /dev/null +++ b/tests/testthat/out-native/multi-28.txt @@ -0,0 +1,5 @@ + column~ col_~ col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-29.txt b/tests/testthat/out-native/multi-29.txt new file mode 100644 index 000000000..09a6e2848 --- /dev/null +++ b/tests/testthat/out-native/multi-29.txt @@ -0,0 +1,5 @@ + column_~ col_~ col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-30.txt b/tests/testthat/out-native/multi-30.txt new file mode 100644 index 000000000..4d540f20c --- /dev/null +++ b/tests/testthat/out-native/multi-30.txt @@ -0,0 +1,5 @@ + column_z~ col_~ col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-31.txt b/tests/testthat/out-native/multi-31.txt new file mode 100644 index 000000000..024ac6b02 --- /dev/null +++ b/tests/testthat/out-native/multi-31.txt @@ -0,0 +1,5 @@ + column_z~ col_02 col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-32.txt b/tests/testthat/out-native/multi-32.txt new file mode 100644 index 000000000..8eea598c0 --- /dev/null +++ b/tests/testthat/out-native/multi-32.txt @@ -0,0 +1,5 @@ + column_ze~ col_02 col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-33.txt b/tests/testthat/out-native/multi-33.txt new file mode 100644 index 000000000..bef2ded5f --- /dev/null +++ b/tests/testthat/out-native/multi-33.txt @@ -0,0 +1,5 @@ + column_zer~ col_02 col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-34.txt b/tests/testthat/out-native/multi-34.txt new file mode 100644 index 000000000..ed21800dd --- /dev/null +++ b/tests/testthat/out-native/multi-34.txt @@ -0,0 +1,5 @@ + column_zero~ col_02 col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-35.txt b/tests/testthat/out-native/multi-35.txt new file mode 100644 index 000000000..33ad79168 --- /dev/null +++ b/tests/testthat/out-native/multi-35.txt @@ -0,0 +1,5 @@ + column_zero~ col_02 col_03 col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-36.txt b/tests/testthat/out-native/multi-36.txt new file mode 100644 index 000000000..c7550c2a6 --- /dev/null +++ b/tests/testthat/out-native/multi-36.txt @@ -0,0 +1,5 @@ + column_zero_one col_~ col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-37.txt b/tests/testthat/out-native/multi-37.txt new file mode 100644 index 000000000..a1fed5504 --- /dev/null +++ b/tests/testthat/out-native/multi-37.txt @@ -0,0 +1,5 @@ + column_zero_one col_02 col_~ col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-38.txt b/tests/testthat/out-native/multi-38.txt new file mode 100644 index 000000000..c11c9268f --- /dev/null +++ b/tests/testthat/out-native/multi-38.txt @@ -0,0 +1,5 @@ + column_zero_one col_02 col_03 col_~ + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-39.txt b/tests/testthat/out-native/multi-39.txt new file mode 100644 index 000000000..11bfafcd8 --- /dev/null +++ b/tests/testthat/out-native/multi-39.txt @@ -0,0 +1,5 @@ + column_zero_one col_02 col_03 col_04 + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/multi-extra-10.txt b/tests/testthat/out-native/multi-extra-10.txt new file mode 100644 index 000000000..6b1778abb --- /dev/null +++ b/tests/testthat/out-native/multi-extra-10.txt @@ -0,0 +1,3 @@ +col_02 +col_03 +col_04 diff --git a/tests/testthat/out-native/multi-inf.txt b/tests/testthat/out-native/multi-inf.txt new file mode 100644 index 000000000..11bfafcd8 --- /dev/null +++ b/tests/testthat/out-native/multi-inf.txt @@ -0,0 +1,5 @@ + column_zero_one col_02 col_03 col_04 + +1 1.23 a a a +2 2.23 b b b +3 3.23 c c c diff --git a/tests/testthat/out-native/numeric-04.txt b/tests/testthat/out-native/numeric-04.txt new file mode 100644 index 000000000..e68295653 --- /dev/null +++ b/tests/testthat/out-native/numeric-04.txt @@ -0,0 +1,5 @@ + +1.00e-9 +1.00e-6 +1.00e+3 +1.00e+9 diff --git a/tests/testthat/out-native/numeric-07.txt b/tests/testthat/out-native/numeric-07.txt new file mode 100644 index 000000000..e68295653 --- /dev/null +++ b/tests/testthat/out-native/numeric-07.txt @@ -0,0 +1,5 @@ + +1.00e-9 +1.00e-6 +1.00e+3 +1.00e+9 diff --git a/tests/testthat/out-native/numeric-10.txt b/tests/testthat/out-native/numeric-10.txt new file mode 100644 index 000000000..20b983c88 --- /dev/null +++ b/tests/testthat/out-native/numeric-10.txt @@ -0,0 +1,5 @@ + + 1.00e-9 + 1.00e-6 + 1.00e+3 + 1.00e+9 diff --git a/tests/testthat/out-native/numeric-15.txt b/tests/testthat/out-native/numeric-15.txt new file mode 100644 index 000000000..f80b61a4b --- /dev/null +++ b/tests/testthat/out-native/numeric-15.txt @@ -0,0 +1,5 @@ + + 1.00e-9 + 1.00e-6 + 1.00e+3 + 1.00e+9 diff --git a/tests/testthat/out-native/numeric-22.txt b/tests/testthat/out-native/numeric-22.txt new file mode 100644 index 000000000..e134acd38 --- /dev/null +++ b/tests/testthat/out-native/numeric-22.txt @@ -0,0 +1,5 @@ + + 0.00000000100 + 0.00000100 + 1000 +1000000000 diff --git a/tests/testthat/out-native/ordered.txt b/tests/testthat/out-native/ordered.txt new file mode 100644 index 000000000..8b77b7ef8 --- /dev/null +++ b/tests/testthat/out-native/ordered.txt @@ -0,0 +1,7 @@ + +a +b +c +d +e + diff --git a/tests/testthat/out-native/rowid-3.txt b/tests/testthat/out-native/rowid-3.txt new file mode 100644 index 000000000..5fcc45221 --- /dev/null +++ b/tests/testthat/out-native/rowid-3.txt @@ -0,0 +1,4 @@ + +1 +2 +3 diff --git a/tests/testthat/out-native/rowid-star-title-12.txt b/tests/testthat/out-native/rowid-star-title-12.txt new file mode 100644 index 000000000..289dc2099 --- /dev/null +++ b/tests/testthat/out-native/rowid-star-title-12.txt @@ -0,0 +1,14 @@ + + * + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 diff --git a/tests/testthat/out-native/scientific-short-neg.txt b/tests/testthat/out-native/scientific-short-neg.txt new file mode 100644 index 000000000..04992b5ea --- /dev/null +++ b/tests/testthat/out-native/scientific-short-neg.txt @@ -0,0 +1,8 @@ + +- 1.00e 3 + 1.00e 9 +- 1.00e15 + 1.00e22 + NA +-Inf + Inf diff --git a/tests/testthat/out-native/scientific.txt b/tests/testthat/out-native/scientific.txt new file mode 100644 index 000000000..e12caed8b --- /dev/null +++ b/tests/testthat/out-native/scientific.txt @@ -0,0 +1,8 @@ + + 1.00e-9 + 1.00e-6 + 1.00e+3 + 1.00e+9 + NA +-Inf + Inf diff --git a/tests/testthat/out-native/spaces.txt b/tests/testthat/out-native/spaces.txt new file mode 100644 index 000000000..f32c9ef10 --- /dev/null +++ b/tests/testthat/out-native/spaces.txt @@ -0,0 +1,7 @@ + +"" +" " +" a" +"a " +a b + diff --git a/tests/testthat/out-native/tibble-all--30.txt b/tests/testthat/out-native/tibble-all--30.txt new file mode 100644 index 000000000..6e9fd9d6a --- /dev/null +++ b/tests/testthat/out-native/tibble-all--30.txt @@ -0,0 +1,5 @@ + a b c d + +1 1.00 1 T a +2 2.50 2 F b +3 NA NA NA NA diff --git a/tests/testthat/out-native/tibble-all--300-20.txt b/tests/testthat/out-native/tibble-all--300-20.txt new file mode 100644 index 000000000..249bc614d --- /dev/null +++ b/tests/testthat/out-native/tibble-all--300-20.txt @@ -0,0 +1,10 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i + +1  +2  +3  diff --git a/tests/testthat/out-native/tibble-all--300-30.txt b/tests/testthat/out-native/tibble-all--300-30.txt new file mode 100644 index 000000000..249bc614d --- /dev/null +++ b/tests/testthat/out-native/tibble-all--300-30.txt @@ -0,0 +1,10 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i + +1  +2  +3  diff --git a/tests/testthat/out-native/tibble-all--300-40.txt b/tests/testthat/out-native/tibble-all--300-40.txt new file mode 100644 index 000000000..249bc614d --- /dev/null +++ b/tests/testthat/out-native/tibble-all--300-40.txt @@ -0,0 +1,10 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i + +1  +2  +3  diff --git a/tests/testthat/out-native/tibble-all--300-50.txt b/tests/testthat/out-native/tibble-all--300-50.txt new file mode 100644 index 000000000..249bc614d --- /dev/null +++ b/tests/testthat/out-native/tibble-all--300-50.txt @@ -0,0 +1,10 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i + +1  +2  +3  diff --git a/tests/testthat/out-native/tibble-all--300-60.txt b/tests/testthat/out-native/tibble-all--300-60.txt new file mode 100644 index 000000000..249bc614d --- /dev/null +++ b/tests/testthat/out-native/tibble-all--300-60.txt @@ -0,0 +1,10 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i + +1  +2  +3  diff --git a/tests/testthat/out-native/tibble-all--300-70.txt b/tests/testthat/out-native/tibble-all--300-70.txt new file mode 100644 index 000000000..249bc614d --- /dev/null +++ b/tests/testthat/out-native/tibble-all--300-70.txt @@ -0,0 +1,10 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i + +1  +2  +3  diff --git a/tests/testthat/out-native/tibble-all--300.txt b/tests/testthat/out-native/tibble-all--300.txt new file mode 100644 index 000000000..249bc614d --- /dev/null +++ b/tests/testthat/out-native/tibble-all--300.txt @@ -0,0 +1,10 @@ + a b c d e f g h + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i + +1  +2  +3  diff --git a/tests/testthat/out-native/tibble-iris-3-20.txt b/tests/testthat/out-native/tibble-iris-3-20.txt new file mode 100644 index 000000000..84e4c248f --- /dev/null +++ b/tests/testthat/out-native/tibble-iris-3-20.txt @@ -0,0 +1,5 @@ + Sepa~ Sepa~ Peta~ + +1 5.10 3.50 1.40 +2 4.90 3.00 1.40 +3 4.70 3.20 1.30 diff --git a/tests/testthat/out-native/tibble-iris-5-30.txt b/tests/testthat/out-native/tibble-iris-5-30.txt new file mode 100644 index 000000000..5a4f3f086 --- /dev/null +++ b/tests/testthat/out-native/tibble-iris-5-30.txt @@ -0,0 +1,7 @@ + Sepal.~ Sepal~ Petal~ Peta~ + +1 5.10 3.50 1.40 0.200 +2 4.90 3.00 1.40 0.200 +3 4.70 3.20 1.30 0.200 +4 4.60 3.10 1.50 0.200 +5 5.00 3.60 1.40 0.200 diff --git a/tests/testthat/out-native/tibble-mtcars-8-30.txt b/tests/testthat/out-native/tibble-mtcars-8-30.txt new file mode 100644 index 000000000..3219c55c7 --- /dev/null +++ b/tests/testthat/out-native/tibble-mtcars-8-30.txt @@ -0,0 +1,10 @@ + mpg cyl disp hp +* +1 21.0 6.00 160 110 +2 21.0 6.00 160 110 +3 22.8 4.00 108 93.0 +4 21.4 6.00 258 110 +5 18.7 8.00 360 175 +6 18.1 6.00 225 105 +7 14.3 8.00 360 245 +8 24.4 4.00 147 62.0 diff --git a/tests/testthat/out-native/tibble-newline.txt b/tests/testthat/out-native/tibble-newline.txt new file mode 100644 index 000000000..dcbfb853b --- /dev/null +++ b/tests/testthat/out-native/tibble-newline.txt @@ -0,0 +1,4 @@ + `\n` `\r` + +1 "\n" "\n" +2 "\"" "\n" diff --git a/tests/testthat/out-native/tibble-non-syntactic.txt b/tests/testthat/out-native/tibble-non-syntactic.txt new file mode 100644 index 000000000..9b1d9c2aa --- /dev/null +++ b/tests/testthat/out-native/tibble-non-syntactic.txt @@ -0,0 +1,3 @@ + `mean(x)` `var(x)` + +1 5.00 3.00 diff --git a/tests/testthat/out-native/tibble-space.txt b/tests/testthat/out-native/tibble-space.txt new file mode 100644 index 000000000..ecc32c1ad --- /dev/null +++ b/tests/testthat/out-native/tibble-space.txt @@ -0,0 +1,6 @@ + a + +1 "" +2 " " +3 "a " +4 " a" diff --git a/tests/testthat/out-native/time-posix.txt b/tests/testthat/out-native/time-posix.txt new file mode 100644 index 000000000..54def4c68 --- /dev/null +++ b/tests/testthat/out-native/time-posix.txt @@ -0,0 +1,3 @@ + +2017-07-28 18:04:35 +NA diff --git a/tests/testthat/out-native/time.txt b/tests/testthat/out-native/time.txt new file mode 100644 index 000000000..671c38e9e --- /dev/null +++ b/tests/testthat/out-native/time.txt @@ -0,0 +1,3 @@ + +2017-07-28 18:04:35 +NA diff --git a/tests/testthat/out-native/title-crayon.txt b/tests/testthat/out-native/title-crayon.txt new file mode 100644 index 000000000..b6de1f6b8 --- /dev/null +++ b/tests/testthat/out-native/title-crayon.txt @@ -0,0 +1,11 @@ + crayon + + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out-native/title-longer.txt b/tests/testthat/out-native/title-longer.txt new file mode 100644 index 000000000..0d0452f41 --- /dev/null +++ b/tests/testthat/out-native/title-longer.txt @@ -0,0 +1,11 @@ +somewhat_wider + + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA + - Inf + Inf diff --git a/tests/testthat/out-native/title-none.txt b/tests/testthat/out-native/title-none.txt new file mode 100644 index 000000000..ad3ab5170 --- /dev/null +++ b/tests/testthat/out-native/title-none.txt @@ -0,0 +1,10 @@ + + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out-native/title-short.txt b/tests/testthat/out-native/title-short.txt new file mode 100644 index 000000000..899e17848 --- /dev/null +++ b/tests/testthat/out-native/title-short.txt @@ -0,0 +1,11 @@ + short + + 10.0 + 100 + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out-native/title-too-long.txt b/tests/testthat/out-native/title-too-long.txt new file mode 100644 index 000000000..8535c8002 --- /dev/null +++ b/tests/testthat/out-native/title-too-long.txt @@ -0,0 +1,11 @@ +much_too~ + + 1.00e1 + 1.00e2 + 1.00e3 + 1.00e4 + 1.00e5 + 1.00e6 + NA +-Inf + Inf diff --git a/tests/testthat/out-native/utf8.txt b/tests/testthat/out-native/utf8.txt new file mode 100644 index 000000000..947d2dde4 --- /dev/null +++ b/tests/testthat/out-native/utf8.txt @@ -0,0 +1,17 @@ + chars desc + + 1 "\u0001\u001f" C0 control code + 2 "\a\b\f\n\r\t" Named control code + 3 abcdefuvwxyz ASCII + 4 "\u0080\u009f" C1 control code + 5 NA Latin-1 + 6 NA Unicode + 7 NA Unicode wide + 8 "\u0e00\u2029" Unicode control + 9 xxxxxxxxxxxxNA Unicode ignorable +10 aaaaaaaaaaaaNA Unicode mark +11 NA Emoji +12 "x\U0010ffffx" Unassigned +13 "\xfd\xfe\xff" Invalid +14 "\\" Backslash +15 "\"" Quote diff --git a/tests/testthat/out/tibble-all--30.txt b/tests/testthat/out/tibble-all--30.txt index e16955bf3..15e880456 100644 --- a/tests/testthat/out/tibble-all--30.txt +++ b/tests/testthat/out/tibble-all--30.txt @@ -1,5 +1,5 @@ - a b c d - -1 1.00 1 T a -2 2.50 2 F b -3 NA NA NA + a b c d +  +1 1.00 1 T a +2 2.50 2 F b +3 NA NA NA NA diff --git a/tests/testthat/out/tibble-all--300-20.txt b/tests/testthat/out/tibble-all--300-20.txt index ad51ef13b..027d6cda1 100644 --- a/tests/testthat/out/tibble-all--300-20.txt +++ b/tests/testthat/out/tibble-all--300-20.txt @@ -1,15 +1,10 @@ - a b c - -1 1.00 1 T -2 2.50 2 F -3 NA NA NA - d e - -1 a a -2 b b -3 - f - -1 2015-12-10 -2 2015-12-11 -3 NA + a b c d e f g h +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i +  +1  +2  +3  diff --git a/tests/testthat/out/tibble-all--300-30.txt b/tests/testthat/out/tibble-all--300-30.txt index f6ad4bfb3..027d6cda1 100644 --- a/tests/testthat/out/tibble-all--300-30.txt +++ b/tests/testthat/out/tibble-all--300-30.txt @@ -1,20 +1,10 @@ - a b c d - -1 1.00 1 T a -2 2.50 2 F b -3 NA NA NA - e f - -1 a 2015-12-10 -2 b 2015-12-11 -3 NA - g - -1 2015-12-09 10:51:35 -2 2015-12-09 10:51:36 -3 NA - h i - -1 -2 -3 + a b c d e f g h +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i +  +1  +2  +3  diff --git a/tests/testthat/out/tibble-all--300-40.txt b/tests/testthat/out/tibble-all--300-40.txt index 590abad60..027d6cda1 100644 --- a/tests/testthat/out/tibble-all--300-40.txt +++ b/tests/testthat/out/tibble-all--300-40.txt @@ -1,15 +1,10 @@ - a b c d e - -1 1.00 1 T a a -2 2.50 2 F b b -3 NA NA NA - f g - -1 2015-12-10 2015-12-09 10:51:35 -2 2015-12-11 2015-12-09 10:51:36 -3 NA NA - h i - -1 -2 -3 + a b c d e f g h +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i +  +1  +2  +3  diff --git a/tests/testthat/out/tibble-all--300-50.txt b/tests/testthat/out/tibble-all--300-50.txt index 29e958a8d..027d6cda1 100644 --- a/tests/testthat/out/tibble-all--300-50.txt +++ b/tests/testthat/out/tibble-all--300-50.txt @@ -1,10 +1,10 @@ - a b c d e f - -1 1.00 1 T a a 2015-12-10 -2 2.50 2 F b b 2015-12-11 -3 NA NA NA NA - g h i - -1 2015-12-09 10:51:35 -2 2015-12-09 10:51:36 -3 NA + a b c d e f g h +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i +  +1  +2  +3  diff --git a/tests/testthat/out/tibble-all--300-60.txt b/tests/testthat/out/tibble-all--300-60.txt index 29e958a8d..027d6cda1 100644 --- a/tests/testthat/out/tibble-all--300-60.txt +++ b/tests/testthat/out/tibble-all--300-60.txt @@ -1,10 +1,10 @@ - a b c d e f - -1 1.00 1 T a a 2015-12-10 -2 2.50 2 F b b 2015-12-11 -3 NA NA NA NA - g h i - -1 2015-12-09 10:51:35 -2 2015-12-09 10:51:36 -3 NA + a b c d e f g h +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i +  +1  +2  +3  diff --git a/tests/testthat/out/tibble-all--300-70.txt b/tests/testthat/out/tibble-all--300-70.txt index 9ce4781ed..027d6cda1 100644 --- a/tests/testthat/out/tibble-all--300-70.txt +++ b/tests/testthat/out/tibble-all--300-70.txt @@ -1,10 +1,10 @@ - a b c d e f g - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 -3 NA NA NA NA NA - h i - -1 -2 -3 + a b c d e f g h +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i +  +1  +2  +3  diff --git a/tests/testthat/out/tibble-all--300.txt b/tests/testthat/out/tibble-all--300.txt index 7c1a5a2b8..027d6cda1 100644 --- a/tests/testthat/out/tibble-all--300.txt +++ b/tests/testthat/out/tibble-all--300.txt @@ -1,10 +1,10 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 -3 NA NA NA NA NA - i - -1 -2 -3 + a b c d e f g h +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  +3 NA NA NA NA NA NA NA  + i +  +1  +2  +3  diff --git a/tests/testthat/out/tibble-iris-3-20.txt b/tests/testthat/out/tibble-iris-3-20.txt index 8d9878711..0783238ed 100644 --- a/tests/testthat/out/tibble-iris-3-20.txt +++ b/tests/testthat/out/tibble-iris-3-20.txt @@ -1,5 +1,5 @@ - Sepa… Sepa… Peta… - -1 5.10 3.50 1.40 -2 4.90 3.00 1.40 -3 4.70 3.20 1.30 + Sepa… Sepa… Peta… +  +1 5.10 3.50 1.40 +2 4.90 3.00 1.40 +3 4.70 3.20 1.30 diff --git a/tests/testthat/out/tibble-iris-5-30.txt b/tests/testthat/out/tibble-iris-5-30.txt index 9eef46e0b..37ab492f0 100644 --- a/tests/testthat/out/tibble-iris-5-30.txt +++ b/tests/testthat/out/tibble-iris-5-30.txt @@ -1,7 +1,7 @@ - Sepal.… Sepal… Petal… Peta… - -1 5.10 3.50 1.40 0.200 -2 4.90 3.00 1.40 0.200 -3 4.70 3.20 1.30 0.200 -4 4.60 3.10 1.50 0.200 -5 5.00 3.60 1.40 0.200 + Sepal.… Sepal… Petal… Peta… +  +1 5.10 3.50 1.40 0.200 +2 4.90 3.00 1.40 0.200 +3 4.70 3.20 1.30 0.200 +4 4.60 3.10 1.50 0.200 +5 5.00 3.60 1.40 0.200 diff --git a/tests/testthat/out/tibble-mtcars-8-30.txt b/tests/testthat/out/tibble-mtcars-8-30.txt index 3219c55c7..984dd8519 100644 --- a/tests/testthat/out/tibble-mtcars-8-30.txt +++ b/tests/testthat/out/tibble-mtcars-8-30.txt @@ -1,10 +1,10 @@ - mpg cyl disp hp -* -1 21.0 6.00 160 110 -2 21.0 6.00 160 110 -3 22.8 4.00 108 93.0 -4 21.4 6.00 258 110 -5 18.7 8.00 360 175 -6 18.1 6.00 225 105 -7 14.3 8.00 360 245 -8 24.4 4.00 147 62.0 + mpg cyl disp hp +*  +1 21.0 6.00 160 110 +2 21.0 6.00 160 110 +3 22.8 4.00 108 93.0 +4 21.4 6.00 258 110 +5 18.7 8.00 360 175 +6 18.1 6.00 225 105 +7 14.3 8.00 360 245 +8 24.4 4.00 147 62.0 diff --git a/tests/testthat/out/tibble-newline.txt b/tests/testthat/out/tibble-newline.txt index dad287a3d..0dfffb009 100644 --- a/tests/testthat/out/tibble-newline.txt +++ b/tests/testthat/out/tibble-newline.txt @@ -1,4 +1,4 @@ - `\n` `\r` - -1 "\n" "\n" -2 "\"" "\n" + `\n` `\r` +  +1 "\n" "\n" +2 "\"" "\n" diff --git a/tests/testthat/out/tibble-non-syntactic.txt b/tests/testthat/out/tibble-non-syntactic.txt index 9b1d9c2aa..ef26b3c2a 100644 --- a/tests/testthat/out/tibble-non-syntactic.txt +++ b/tests/testthat/out/tibble-non-syntactic.txt @@ -1,3 +1,3 @@ - `mean(x)` `var(x)` - -1 5.00 3.00 + `mean(x)` `var(x)` +  +1 5.00 3.00 diff --git a/tests/testthat/out/tibble-space.txt b/tests/testthat/out/tibble-space.txt index 6b8f6170e..f19daffb1 100644 --- a/tests/testthat/out/tibble-space.txt +++ b/tests/testthat/out/tibble-space.txt @@ -1,6 +1,6 @@ - a - -1 "" -2 " " -3 "a " -4 " a" + a +  +1 "" +2 " " +3 "a " +4 " a" diff --git a/tests/testthat/out/title-longer.txt b/tests/testthat/out/title-longer.txt index 0d0452f41..04f29d621 100644 --- a/tests/testthat/out/title-longer.txt +++ b/tests/testthat/out/title-longer.txt @@ -1,11 +1,11 @@ -somewhat_wider - +somewhat_wider +  10.0 100 - 1000 - 10000 - 100000 - 1000000 - NA - - Inf - Inf + 1000 + 10000 + 100000 + 1000000 + NA + - Inf + Inf diff --git a/tests/testthat/out/title-none.txt b/tests/testthat/out/title-none.txt index ad3ab5170..df236ede7 100644 --- a/tests/testthat/out/title-none.txt +++ b/tests/testthat/out/title-none.txt @@ -1,10 +1,10 @@ - +  10.0 100 - 1000 - 10000 - 100000 - 1000000 - NA -- Inf - Inf + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out/title-short.txt b/tests/testthat/out/title-short.txt index 899e17848..1e298f4de 100644 --- a/tests/testthat/out/title-short.txt +++ b/tests/testthat/out/title-short.txt @@ -1,11 +1,11 @@ - short - + short +  10.0 100 - 1000 - 10000 - 100000 - 1000000 - NA -- Inf - Inf + 1000 + 10000 + 100000 + 1000000 + NA +- Inf + Inf diff --git a/tests/testthat/out/title-too-long.txt b/tests/testthat/out/title-too-long.txt index 57d395e88..cc05fa451 100644 --- a/tests/testthat/out/title-too-long.txt +++ b/tests/testthat/out/title-too-long.txt @@ -1,11 +1,11 @@ -much_too… - - 1.00e¹ - 1.00e² - 1.00e³ - 1.00e⁴ - 1.00e⁵ - 1.00e⁶ - NA --Inf - Inf +much_too… +  + 1.00e¹ + 1.00e² + 1.00e³ + 1.00e⁴ + 1.00e⁵ + 1.00e⁶ + NA   +-Inf   + Inf   From 450157f52de76a40430128a8cf30cb8ceaed3f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 01:01:05 +0100 Subject: [PATCH 107/133] test latin1 instead of C locale --- tests/testthat/helper-output.R | 24 +++++++++++++------- tests/testthat/out-native/deal1.txt | 8 +++---- tests/testthat/out-native/deal2.txt | 8 +++---- tests/testthat/out-native/deal3.txt | 8 +++---- tests/testthat/out-native/multi-extra-10.txt | 6 ++--- tests/testthat/out-native/utf8.txt | 12 +++++----- tests/testthat/test-format_character.R | 5 +++- tests/testthat/test-format_multi.R | 9 +++++--- 8 files changed, 47 insertions(+), 33 deletions(-) diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 905e685a7..145dbdebb 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -18,6 +18,11 @@ df_all <- list( expect_pillar_output <- function(x = NULL, ..., filename, xp = NULL, xf = NULL, crayon = TRUE, output_width = 80L) { object_quo <- rlang::quo(get_pillar_output_object(x, ..., xp = xp, xf = xf)) + expect_pillar_output_utf8(object_quo, filename, output_width) + expect_pillar_output_latin1(object_quo, filename, output_width) +} + +expect_pillar_output_utf8 <- function(object_quo, filename, output_width) { if (l10n_info()$`UTF-8`) { expect_known_display( object = !!object_quo, @@ -26,14 +31,17 @@ expect_pillar_output <- function(x = NULL, ..., filename, xp = NULL, xf = NULL, width = output_width ) } - withr::with_locale( - c(LC_CTYPE = "C"), - expect_known_display( - object = !!object_quo, - file = file.path("out-native", filename), - crayon = FALSE, - width = output_width - ) +} + +expect_pillar_output_latin1 <- function(object_quo, filename, output_width) { + old <- rlang::mut_latin1_locale() + on.exit(Sys.setlocale("LC_CTYPE", old)) + + expect_known_display( + object = !!object_quo, + file = file.path("out-native", filename), + crayon = FALSE, + width = output_width ) } diff --git a/tests/testthat/out-native/deal1.txt b/tests/testthat/out-native/deal1.txt index 814e0b3a2..3ce7ab88b 100644 --- a/tests/testthat/out-native/deal1.txt +++ b/tests/testthat/out-native/deal1.txt @@ -1,4 +1,4 @@ -`\u6210\u4ea4` - -"\u6210\u4ea4\u65e5" - +`` + + + diff --git a/tests/testthat/out-native/deal2.txt b/tests/testthat/out-native/deal2.txt index e62c1b595..45428f620 100644 --- a/tests/testthat/out-native/deal2.txt +++ b/tests/testthat/out-native/deal2.txt @@ -1,4 +1,4 @@ -`\u6210\u4ea4\u65e5` - -"\u6210\u4ea4" - +`` + + + diff --git a/tests/testthat/out-native/deal3.txt b/tests/testthat/out-native/deal3.txt index 8f8021e37..217b600c8 100644 --- a/tests/testthat/out-native/deal3.txt +++ b/tests/testthat/out-native/deal3.txt @@ -1,4 +1,4 @@ -`\u6210\u4ea4\u65e5` - - 1 - NA +`` + + 1 + NA diff --git a/tests/testthat/out-native/multi-extra-10.txt b/tests/testthat/out-native/multi-extra-10.txt index 6b1778abb..f08b0dd72 100644 --- a/tests/testthat/out-native/multi-extra-10.txt +++ b/tests/testthat/out-native/multi-extra-10.txt @@ -1,3 +1,3 @@ -col_02 -col_03 -col_04 +col_02  +col_03  +col_04  diff --git a/tests/testthat/out-native/utf8.txt b/tests/testthat/out-native/utf8.txt index 947d2dde4..3b1465730 100644 --- a/tests/testthat/out-native/utf8.txt +++ b/tests/testthat/out-native/utf8.txt @@ -4,13 +4,13 @@ 2 "\a\b\f\n\r\t" Named control code 3 abcdefuvwxyz ASCII 4 "\u0080\u009f" C1 control code - 5 NA Latin-1 - 6 NA Unicode - 7 NA Unicode wide + 5  ¡¢£¤¥úûüýþÿ Latin-1 + 6 Unicode + 7 Unicode wide 8 "\u0e00\u2029" Unicode control - 9 xxxxxxxxxxxxNA Unicode ignorable -10 aaaaaaaaaaaaNA Unicode mark -11 NA Emoji + 9 x­xxxxxxxxxxx Unicode ignorable +10 aaaaaaaaaaaa Unicode mark +11 Emoji 12 "x\U0010ffffx" Unassigned 13 "\xfd\xfe\xff" Invalid 14 "\\" Backslash diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index 853c62669..04105527c 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -71,5 +71,8 @@ test_that("output test", { expect_pillar_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") expect_pillar_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") expect_pillar_output(c("", " ", " a", "a ", "a b"), width = 5, filename = "spaces.txt") - expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") + # Output is not UTF-8 encoded + suppressWarnings( + expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") + ) }) diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 88e86d363..2cfebaa75 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -40,9 +40,12 @@ test_that("output test", { expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") expect_pillar_output(xf = colonnade(x, width = Inf), filename = "multi-inf.txt") - expect_pillar_output( - xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), - filename = "multi-extra-10.txt" + # Output is not UTF-8 encoded + suppressWarnings( + expect_pillar_output( + xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), + filename = "multi-extra-10.txt" + ) ) }) From 5362d7981680d040082ef44e629dc6d75f833d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 01:30:01 +0100 Subject: [PATCH 108/133] distinguish between own and foreign methods --- API | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/API b/API index e712c930c..bde91ea44 100644 --- a/API +++ b/API @@ -22,20 +22,9 @@ style_num(x, negative, significant = rep_along(x, TRUE)) style_subtle(x) type_sum(x) -## S3 methods +## Own S3 methods extra_cols.squeezed_colonnade(x, ...) -format.colonnade(x, ...) -format.pillar(x, width = NULL, ...) -format.pillar_shaft(x, width, ...) -format.pillar_shaft_decimal(x, width, ...) -format.pillar_shaft_simple(x, width, ...) -format.pillar_title(x, width, ...) -format.pillar_type(x, width = NULL, ...) -format.rif_data(x, width, ...) -format.rif_title(x, width, ...) -format.rif_type(x, width = NULL, ...) -format.squeezed_colonnade(x, ...) is_vector_s3.Date(x) is_vector_s3.POSIXct(x) is_vector_s3.data.frame(x) @@ -55,14 +44,6 @@ pillar_shaft.default(x, ...) pillar_shaft.list(x, ...) pillar_shaft.logical(x, ...) pillar_shaft.numeric(x, ..., sigfig = 3) -print.colonnade(x, ...) -print.pillar(x, ...) -print.pillar_ornament(x, ...) -print.pillar_shaft(x, width = NULL, ...) -print.pillar_vertical(x, ...) -print.rif_data(x, ...) -print.spark(x, ...) -print.squeezed_colonnade(x, ...) type_sum.AsIs(x) type_sum.Date(x) type_sum.POSIXct(x) @@ -72,3 +53,25 @@ type_sum.difftime(x) type_sum.factor(x) type_sum.ordered(x) type_sum.tbl_df(x) + +## Foreign S3 methods + +format.colonnade(x, ...) +format.pillar(x, width = NULL, ...) +format.pillar_shaft(x, width, ...) +format.pillar_shaft_decimal(x, width, ...) +format.pillar_shaft_simple(x, width, ...) +format.pillar_title(x, width, ...) +format.pillar_type(x, width = NULL, ...) +format.rif_data(x, width, ...) +format.rif_title(x, width, ...) +format.rif_type(x, width = NULL, ...) +format.squeezed_colonnade(x, ...) +print.colonnade(x, ...) +print.pillar(x, ...) +print.pillar_ornament(x, ...) +print.pillar_shaft(x, width = NULL, ...) +print.pillar_vertical(x, ...) +print.rif_data(x, ...) +print.spark(x, ...) +print.squeezed_colonnade(x, ...) From 5a082e175b952e4dad1ae71c0076ad297efbfd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 01:30:38 +0100 Subject: [PATCH 109/133] don't include type_sum.tbl_df(), responsibility of tibble --- API | 1 - NAMESPACE | 1 - R/type-sum.R | 2 -- 3 files changed, 4 deletions(-) diff --git a/API b/API index bde91ea44..0aba40552 100644 --- a/API +++ b/API @@ -52,7 +52,6 @@ type_sum.default(x) type_sum.difftime(x) type_sum.factor(x) type_sum.ordered(x) -type_sum.tbl_df(x) ## Foreign S3 methods diff --git a/NAMESPACE b/NAMESPACE index dc714e98b..5f9ac39f1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -47,7 +47,6 @@ S3method(type_sum,default) S3method(type_sum,difftime) S3method(type_sum,factor) S3method(type_sum,ordered) -S3method(type_sum,tbl_df) export(colonnade) export(dim_desc) export(expect_known_display) diff --git a/R/type-sum.R b/R/type-sum.R index 2ca9268dd..bdbf0f688 100644 --- a/R/type-sum.R +++ b/R/type-sum.R @@ -23,8 +23,6 @@ type_sum.Date <- function(x) "date" #' @export type_sum.data.frame <- function(x) class(x)[[1]] #' @export -type_sum.tbl_df <- function(x) "tibble" -#' @export type_sum.AsIs <- function(x) paste0("I(", type_sum(remove_as_is_class(x)), ")") #' @export type_sum.default <- function(x) { From 22635f50067bedb9e99914028d9860d9e4b8e5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 01:35:26 +0100 Subject: [PATCH 110/133] Revert "test latin1 instead of C locale" Fails on Travis. This reverts commit 450157f52de76a40430128a8cf30cb8ceaed3f18. --- tests/testthat/helper-output.R | 24 +++++++------------- tests/testthat/out-native/deal1.txt | 8 +++---- tests/testthat/out-native/deal2.txt | 8 +++---- tests/testthat/out-native/deal3.txt | 8 +++---- tests/testthat/out-native/multi-extra-10.txt | 6 ++--- tests/testthat/out-native/utf8.txt | 12 +++++----- tests/testthat/test-format_character.R | 5 +--- tests/testthat/test-format_multi.R | 9 +++----- 8 files changed, 33 insertions(+), 47 deletions(-) diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 145dbdebb..905e685a7 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -18,11 +18,6 @@ df_all <- list( expect_pillar_output <- function(x = NULL, ..., filename, xp = NULL, xf = NULL, crayon = TRUE, output_width = 80L) { object_quo <- rlang::quo(get_pillar_output_object(x, ..., xp = xp, xf = xf)) - expect_pillar_output_utf8(object_quo, filename, output_width) - expect_pillar_output_latin1(object_quo, filename, output_width) -} - -expect_pillar_output_utf8 <- function(object_quo, filename, output_width) { if (l10n_info()$`UTF-8`) { expect_known_display( object = !!object_quo, @@ -31,17 +26,14 @@ expect_pillar_output_utf8 <- function(object_quo, filename, output_width) { width = output_width ) } -} - -expect_pillar_output_latin1 <- function(object_quo, filename, output_width) { - old <- rlang::mut_latin1_locale() - on.exit(Sys.setlocale("LC_CTYPE", old)) - - expect_known_display( - object = !!object_quo, - file = file.path("out-native", filename), - crayon = FALSE, - width = output_width + withr::with_locale( + c(LC_CTYPE = "C"), + expect_known_display( + object = !!object_quo, + file = file.path("out-native", filename), + crayon = FALSE, + width = output_width + ) ) } diff --git a/tests/testthat/out-native/deal1.txt b/tests/testthat/out-native/deal1.txt index 3ce7ab88b..814e0b3a2 100644 --- a/tests/testthat/out-native/deal1.txt +++ b/tests/testthat/out-native/deal1.txt @@ -1,4 +1,4 @@ -`` - - - +`\u6210\u4ea4` + +"\u6210\u4ea4\u65e5" + diff --git a/tests/testthat/out-native/deal2.txt b/tests/testthat/out-native/deal2.txt index 45428f620..e62c1b595 100644 --- a/tests/testthat/out-native/deal2.txt +++ b/tests/testthat/out-native/deal2.txt @@ -1,4 +1,4 @@ -`` - - - +`\u6210\u4ea4\u65e5` + +"\u6210\u4ea4" + diff --git a/tests/testthat/out-native/deal3.txt b/tests/testthat/out-native/deal3.txt index 217b600c8..8f8021e37 100644 --- a/tests/testthat/out-native/deal3.txt +++ b/tests/testthat/out-native/deal3.txt @@ -1,4 +1,4 @@ -`` - - 1 - NA +`\u6210\u4ea4\u65e5` + + 1 + NA diff --git a/tests/testthat/out-native/multi-extra-10.txt b/tests/testthat/out-native/multi-extra-10.txt index f08b0dd72..6b1778abb 100644 --- a/tests/testthat/out-native/multi-extra-10.txt +++ b/tests/testthat/out-native/multi-extra-10.txt @@ -1,3 +1,3 @@ -col_02  -col_03  -col_04  +col_02 +col_03 +col_04 diff --git a/tests/testthat/out-native/utf8.txt b/tests/testthat/out-native/utf8.txt index 3b1465730..947d2dde4 100644 --- a/tests/testthat/out-native/utf8.txt +++ b/tests/testthat/out-native/utf8.txt @@ -4,13 +4,13 @@ 2 "\a\b\f\n\r\t" Named control code 3 abcdefuvwxyz ASCII 4 "\u0080\u009f" C1 control code - 5  ¡¢£¤¥úûüýþÿ Latin-1 - 6 Unicode - 7 Unicode wide + 5 NA Latin-1 + 6 NA Unicode + 7 NA Unicode wide 8 "\u0e00\u2029" Unicode control - 9 x­xxxxxxxxxxx Unicode ignorable -10 aaaaaaaaaaaa Unicode mark -11 Emoji + 9 xxxxxxxxxxxxNA Unicode ignorable +10 aaaaaaaaaaaaNA Unicode mark +11 NA Emoji 12 "x\U0010ffffx" Unassigned 13 "\xfd\xfe\xff" Invalid 14 "\\" Backslash diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index 04105527c..853c62669 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -71,8 +71,5 @@ test_that("output test", { expect_pillar_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") expect_pillar_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") expect_pillar_output(c("", " ", " a", "a ", "a b"), width = 5, filename = "spaces.txt") - # Output is not UTF-8 encoded - suppressWarnings( - expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") - ) + expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") }) diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 2cfebaa75..88e86d363 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -40,12 +40,9 @@ test_that("output test", { expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") expect_pillar_output(xf = colonnade(x, width = Inf), filename = "multi-inf.txt") - # Output is not UTF-8 encoded - suppressWarnings( - expect_pillar_output( - xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), - filename = "multi-extra-10.txt" - ) + expect_pillar_output( + xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), + filename = "multi-extra-10.txt" ) }) From aecf71f18aeb063d770dbd83bf5fdb0cc9327e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 01:42:18 +0100 Subject: [PATCH 111/133] make tests compatible with testthat 1.0.2 and 2.0.0 --- R/testthat.R | 6 +++- .../out-native/tibble-all--300-20.txt | 25 +++++++++------- .../out-native/tibble-all--300-30.txt | 30 ++++++++++++------- .../out-native/tibble-all--300-40.txt | 25 +++++++++------- .../out-native/tibble-all--300-50.txt | 20 ++++++------- .../out-native/tibble-all--300-60.txt | 20 ++++++------- .../out-native/tibble-all--300-70.txt | 20 ++++++------- tests/testthat/out/tibble-all--300-20.txt | 25 +++++++++------- tests/testthat/out/tibble-all--300-30.txt | 30 ++++++++++++------- tests/testthat/out/tibble-all--300-40.txt | 25 +++++++++------- tests/testthat/out/tibble-all--300-50.txt | 20 ++++++------- tests/testthat/out/tibble-all--300-60.txt | 20 ++++++------- tests/testthat/out/tibble-all--300-70.txt | 20 ++++++------- 13 files changed, 165 insertions(+), 121 deletions(-) diff --git a/R/testthat.R b/R/testthat.R index bd743dbe3..b563d5560 100644 --- a/R/testthat.R +++ b/R/testthat.R @@ -49,5 +49,9 @@ expect_known_display <- function(object, file, ..., width = 80L, crayon = TRUE) # FIXME: Use expect_known_output() for testthat >= 2.0.0, and avoid # setting the width in the options above - testthat::expect_output_file(print(eval_tidy(object)), file, update = TRUE) + if (utils::packageVersion("testthat") <= "1.0.2") { + testthat::expect_output_file(print(eval_tidy(object)), file, update = TRUE) + } else { + testthat::expect_known_output(eval_tidy(object), file, print = TRUE, width = width) + } } diff --git a/tests/testthat/out-native/tibble-all--300-20.txt b/tests/testthat/out-native/tibble-all--300-20.txt index 249bc614d..f2648b383 100644 --- a/tests/testthat/out-native/tibble-all--300-20.txt +++ b/tests/testthat/out-native/tibble-all--300-20.txt @@ -1,10 +1,15 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i - -1  -2  -3  + a b c + +1 1.00 1 T +2 2.50 2 F +3 NA NA NA + d e + +1 a a +2 b b +3 NA NA + f + +1 2015-12-10 +2 2015-12-11 +3 NA diff --git a/tests/testthat/out-native/tibble-all--300-30.txt b/tests/testthat/out-native/tibble-all--300-30.txt index 249bc614d..08eb091d3 100644 --- a/tests/testthat/out-native/tibble-all--300-30.txt +++ b/tests/testthat/out-native/tibble-all--300-30.txt @@ -1,10 +1,20 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i - -1  -2  -3  + a b c d + +1 1.00 1 T a +2 2.50 2 F b +3 NA NA NA NA + e f + +1 a 2015-12-10 +2 b 2015-12-11 +3 NA NA + g + +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 +3 NA + h i + +1   +2   +3   diff --git a/tests/testthat/out-native/tibble-all--300-40.txt b/tests/testthat/out-native/tibble-all--300-40.txt index 249bc614d..717e53621 100644 --- a/tests/testthat/out-native/tibble-all--300-40.txt +++ b/tests/testthat/out-native/tibble-all--300-40.txt @@ -1,10 +1,15 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i - -1  -2  -3  + a b c d e + +1 1.00 1 T a a +2 2.50 2 F b b +3 NA NA NA NA NA + f g + +1 2015-12-10 2015-12-09 10:51:35 +2 2015-12-11 2015-12-09 10:51:36 +3 NA NA + h i + +1   +2   +3   diff --git a/tests/testthat/out-native/tibble-all--300-50.txt b/tests/testthat/out-native/tibble-all--300-50.txt index 249bc614d..29a6c5f01 100644 --- a/tests/testthat/out-native/tibble-all--300-50.txt +++ b/tests/testthat/out-native/tibble-all--300-50.txt @@ -1,10 +1,10 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i - -1  -2  -3  + a b c d e f + +1 1.00 1 T a a 2015-12-10 +2 2.50 2 F b b 2015-12-11 +3 NA NA NA NA NA NA + g h i + +1 2015-12-09 10:51:35   +2 2015-12-09 10:51:36   +3 NA   diff --git a/tests/testthat/out-native/tibble-all--300-60.txt b/tests/testthat/out-native/tibble-all--300-60.txt index 249bc614d..29a6c5f01 100644 --- a/tests/testthat/out-native/tibble-all--300-60.txt +++ b/tests/testthat/out-native/tibble-all--300-60.txt @@ -1,10 +1,10 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i - -1  -2  -3  + a b c d e f + +1 1.00 1 T a a 2015-12-10 +2 2.50 2 F b b 2015-12-11 +3 NA NA NA NA NA NA + g h i + +1 2015-12-09 10:51:35   +2 2015-12-09 10:51:36   +3 NA   diff --git a/tests/testthat/out-native/tibble-all--300-70.txt b/tests/testthat/out-native/tibble-all--300-70.txt index 249bc614d..46282f5f5 100644 --- a/tests/testthat/out-native/tibble-all--300-70.txt +++ b/tests/testthat/out-native/tibble-all--300-70.txt @@ -1,10 +1,10 @@ - a b c d e f g h - -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i - -1  -2  -3  + a b c d e f g + +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 +3 NA NA NA NA NA NA NA + h i + +1   +2   +3   diff --git a/tests/testthat/out/tibble-all--300-20.txt b/tests/testthat/out/tibble-all--300-20.txt index 027d6cda1..8f5f163e9 100644 --- a/tests/testthat/out/tibble-all--300-20.txt +++ b/tests/testthat/out/tibble-all--300-20.txt @@ -1,10 +1,15 @@ - a b c d e f g h -  -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i -  -1  -2  -3  + a b c +  +1 1.00 1 T +2 2.50 2 F +3 NA NA NA + d e +  +1 a a +2 b b +3 NA NA + f +  +1 2015-12-10 +2 2015-12-11 +3 NA diff --git a/tests/testthat/out/tibble-all--300-30.txt b/tests/testthat/out/tibble-all--300-30.txt index 027d6cda1..f961766f3 100644 --- a/tests/testthat/out/tibble-all--300-30.txt +++ b/tests/testthat/out/tibble-all--300-30.txt @@ -1,10 +1,20 @@ - a b c d e f g h -  -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i -  -1  -2  -3  + a b c d +  +1 1.00 1 T a +2 2.50 2 F b +3 NA NA NA NA + e f +  +1 a 2015-12-10 +2 b 2015-12-11 +3 NA NA + g +  +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 +3 NA + h i +  +1   +2   +3   diff --git a/tests/testthat/out/tibble-all--300-40.txt b/tests/testthat/out/tibble-all--300-40.txt index 027d6cda1..c20d430da 100644 --- a/tests/testthat/out/tibble-all--300-40.txt +++ b/tests/testthat/out/tibble-all--300-40.txt @@ -1,10 +1,15 @@ - a b c d e f g h -  -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i -  -1  -2  -3  + a b c d e +  +1 1.00 1 T a a +2 2.50 2 F b b +3 NA NA NA NA NA + f g +  +1 2015-12-10 2015-12-09 10:51:35 +2 2015-12-11 2015-12-09 10:51:36 +3 NA NA + h i +  +1   +2   +3   diff --git a/tests/testthat/out/tibble-all--300-50.txt b/tests/testthat/out/tibble-all--300-50.txt index 027d6cda1..7e42eafdf 100644 --- a/tests/testthat/out/tibble-all--300-50.txt +++ b/tests/testthat/out/tibble-all--300-50.txt @@ -1,10 +1,10 @@ - a b c d e f g h -  -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i -  -1  -2  -3  + a b c d e f +  +1 1.00 1 T a a 2015-12-10 +2 2.50 2 F b b 2015-12-11 +3 NA NA NA NA NA NA + g h i +  +1 2015-12-09 10:51:35   +2 2015-12-09 10:51:36   +3 NA   diff --git a/tests/testthat/out/tibble-all--300-60.txt b/tests/testthat/out/tibble-all--300-60.txt index 027d6cda1..7e42eafdf 100644 --- a/tests/testthat/out/tibble-all--300-60.txt +++ b/tests/testthat/out/tibble-all--300-60.txt @@ -1,10 +1,10 @@ - a b c d e f g h -  -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i -  -1  -2  -3  + a b c d e f +  +1 1.00 1 T a a 2015-12-10 +2 2.50 2 F b b 2015-12-11 +3 NA NA NA NA NA NA + g h i +  +1 2015-12-09 10:51:35   +2 2015-12-09 10:51:36   +3 NA   diff --git a/tests/testthat/out/tibble-all--300-70.txt b/tests/testthat/out/tibble-all--300-70.txt index 027d6cda1..9d0b57c67 100644 --- a/tests/testthat/out/tibble-all--300-70.txt +++ b/tests/testthat/out/tibble-all--300-70.txt @@ -1,10 +1,10 @@ - a b c d e f g h -  -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  - i -  -1  -2  -3  + a b c d e f g +  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 +3 NA NA NA NA NA NA NA + h i +  +1   +2   +3   From 55f31fabb6f9080e2f6e2c625d9a291e224a1903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 02:12:23 +0100 Subject: [PATCH 112/133] bump version to 1.0.0 --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5b1ce1b2e..b5647992e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: pillar Title: Coloured Formatting for Columns -Version: 0.0.0.9000 +Version: 1.0.0 Authors@R: c( person("Kirill", "Müller", , "krlmlr+r@mailbox.org", role = c("aut", "cre")), person("Hadley", "Wickham", role = "aut"), From d0292d3fc0316bc8b9a957ef178e6765c75e2f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 02:20:55 +0100 Subject: [PATCH 113/133] NEWS --- NEWS.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 NEWS.md diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 000000000..c05500835 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,37 @@ +## pillar 1.0.0 (2017-11-16) + +Initial release. + +### User functions + + pillar(x, title = NULL, width = NULL, ...) + colonnade(x, has_row_id = TRUE, width = NULL, ...) + squeeze(x, width = NULL, ...) + +### Functions for implementers of data types + + new_pillar_shaft_simple(formatted, ..., width = NULL, align = "left", min_width = NULL, na_indent = 0L) + new_pillar_shaft(x, ..., width, min_width = width, subclass) + new_ornament(x, width = NULL, align = NULL) + get_extent(x) + get_max_extent(x) + +### Utilities + + dim_desc(x) + style_na(x) + style_neg(x) + style_num(x, negative, significant = rep_along(x, TRUE)) + style_subtle(x) + +### Testing helper + + expect_known_display(object, file, ..., width = 80L, crayon = TRUE) + +### Own S3 methods + + pillar_shaft(x, ...) # AsIs, Date, POSIXt, character, default, list, logical, numeric + type_sum(x) # AsIs, Date, POSIXct, data.frame, default, difftime, factor, ordered + is_vector_s3(x) # Date, POSIXct, data.frame, default, difftime, factor, ordered + obj_sum(x) # AsIs, POSIXlt, default, list + extra_cols(x, ...) # squeezed_colonnade From 6a8decbaf7bba3edb6db5b301eebe0ae2207605a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 02:24:21 +0100 Subject: [PATCH 114/133] fix typos --- R/multi.R | 2 +- R/shaft.R | 2 +- R/sigfig.R | 2 +- man/colonnade.Rd | 2 +- man/format_decimal.Rd | 2 +- man/format_scientific.Rd | 2 +- man/pillar_shaft.Rd | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/R/multi.R b/R/multi.R index f2c69c7cb..4341d264c 100644 --- a/R/multi.R +++ b/R/multi.R @@ -159,7 +159,7 @@ colonnade_get_width <- function(x, width, rowid_width) { #' @details #' In a first pass, for each pillar it is decided in which tier it is shown, - #' if at all, and how much horizontal space it may use (either its minumum + #' if at all, and how much horizontal space it may use (either its minimum #' or its maximum width). More than one tier may be created if #' `width > getOption("width")`, in this case each tier is at most #' `getOption("width")` characters wide. diff --git a/R/shaft.R b/R/shaft.R index aea251e4e..654bd8442 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -77,7 +77,7 @@ pillar_shaft.logical <- function(x, ...) { #' @export #' @rdname pillar_shaft #' @param sigfig Minimum number of significant figures to display. Numbers -#' larger than 1 will potentially show more signficiant figures than this +#' larger than 1 will potentially show more significant figures than this #' but they will be greyed out. pillar_shaft.numeric <- function(x, ..., sigfig = 3) { dec <- format_decimal(x, ..., sigfig = sigfig) diff --git a/R/sigfig.R b/R/sigfig.R index 70515b309..2a58c9d29 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -12,7 +12,7 @@ #' * `rhs`: remainder of number #' #' @param x A numeric vector -#' @param sigfig Number of signficiant figures to display. +#' @param sigfig Number of significant figures to display. #' @param ... Ignored format_decimal <- function(x, sigfig = 3, ...) { split_decimal(x, sigfig) diff --git a/man/colonnade.Rd b/man/colonnade.Rd index aef15bc75..4589f6f07 100644 --- a/man/colonnade.Rd +++ b/man/colonnade.Rd @@ -30,7 +30,7 @@ with additional information about omitted columns. } \details{ In a first pass, for each pillar it is decided in which tier it is shown, -if at all, and how much horizontal space it may use (either its minumum +if at all, and how much horizontal space it may use (either its minimum or its maximum width). More than one tier may be created if \code{width > getOption("width")}, in this case each tier is at most \code{getOption("width")} characters wide. diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd index 86c9cfd1b..d41292001 100644 --- a/man/format_decimal.Rd +++ b/man/format_decimal.Rd @@ -9,7 +9,7 @@ format_decimal(x, sigfig = 3, ...) \arguments{ \item{x}{A numeric vector} -\item{sigfig}{Number of signficiant figures to display.} +\item{sigfig}{Number of significant figures to display.} \item{...}{Ignored} } diff --git a/man/format_scientific.Rd b/man/format_scientific.Rd index dd0496c63..0ffb9915d 100644 --- a/man/format_scientific.Rd +++ b/man/format_scientific.Rd @@ -9,7 +9,7 @@ format_scientific(x, sigfig = 3, superscript = TRUE) \arguments{ \item{x}{A numeric vector} -\item{sigfig}{Number of signficiant figures to display.} +\item{sigfig}{Number of significant figures to display.} \item{superscript}{If \code{TRUE}, will use superscript numbers in exponent.} } diff --git a/man/pillar_shaft.Rd b/man/pillar_shaft.Rd index 79e3a3da3..d67761340 100644 --- a/man/pillar_shaft.Rd +++ b/man/pillar_shaft.Rd @@ -44,7 +44,7 @@ pillar_shaft(x, ...) \item{width}{Width for printing and formatting.} \item{sigfig}{Minimum number of significant figures to display. Numbers -larger than 1 will potentially show more signficiant figures than this +larger than 1 will potentially show more significant figures than this but they will be greyed out.} } \description{ From c03a58c4c3f93849ea30fdd42af6e6809deae8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 02:27:06 +0100 Subject: [PATCH 115/133] mute note --- R/testthat.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/testthat.R b/R/testthat.R index b563d5560..78c3b9855 100644 --- a/R/testthat.R +++ b/R/testthat.R @@ -52,6 +52,8 @@ expect_known_display <- function(object, file, ..., width = 80L, crayon = TRUE) if (utils::packageVersion("testthat") <= "1.0.2") { testthat::expect_output_file(print(eval_tidy(object)), file, update = TRUE) } else { - testthat::expect_known_output(eval_tidy(object), file, print = TRUE, width = width) + get("expect_known_output", asNamespace("testthat"))( + eval_tidy(object), file, print = TRUE, width = width + ) } } From b2dff36b1cf92858fbf1d8fc05dda36974b3a12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 02:28:10 +0100 Subject: [PATCH 116/133] get rid of remote --- DESCRIPTION | 2 -- 1 file changed, 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b5647992e..1df2664f1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,8 +24,6 @@ Suggests: testthat Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "pkgapi::api_roclet")) RoxygenNote: 6.0.1 -Remotes: - r-lib/crayon Collate: 'compat-purrr.R' 'dim.R' From 06f376a493fd7993f3afafe048865b0f8b168a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 02:30:36 +0100 Subject: [PATCH 117/133] make sure crayon is recent --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 1df2664f1..035587d6f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,7 +15,7 @@ URL: https://github.com/hadley/pillar BugReports: https://github.com/hadley/pillar/issues Imports: cli, - crayon, + crayon (>= 1.3.4), methods, rlang, utf8 From b272890e4599ee307492a8f9a1cb9c56792c4fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 09:23:50 +0100 Subject: [PATCH 118/133] use expect_output_file() also for testthat 2.0.0 --- R/testthat.R | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/R/testthat.R b/R/testthat.R index 78c3b9855..441415d13 100644 --- a/R/testthat.R +++ b/R/testthat.R @@ -52,8 +52,6 @@ expect_known_display <- function(object, file, ..., width = 80L, crayon = TRUE) if (utils::packageVersion("testthat") <= "1.0.2") { testthat::expect_output_file(print(eval_tidy(object)), file, update = TRUE) } else { - get("expect_known_output", asNamespace("testthat"))( - eval_tidy(object), file, print = TRUE, width = width - ) + testthat::expect_output_file(print(eval_tidy(object)), file, update = TRUE, width = width) } } From 6f5bcd6ababee0827ebac5bf5617530dd757e88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 15:14:11 +0100 Subject: [PATCH 119/133] continue() --- tests/testthat/helper-output.R | 4 ++++ tests/testthat/test-ticks.R | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 905e685a7..3489f87b0 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -58,3 +58,7 @@ add_special <- function(x) { } x } + +continue <- function(x) { + paste0(x, cli::symbol$continue) +} diff --git a/tests/testthat/test-ticks.R b/tests/testthat/test-ticks.R index 02240917e..235ff14d7 100644 --- a/tests/testthat/test-ticks.R +++ b/tests/testthat/test-ticks.R @@ -10,9 +10,9 @@ test_that("title ticks without width restriction", { test_that("title ticks and width", { expect_equal(format_title("proper_title", 15), "proper_title") expect_equal(format_title("proper_title", 12), "proper_title") - expect_equal(format_title("proper_title", 10), "proper_ti\u2026") + expect_equal(format_title("proper_title", 10), continue("proper_ti")) expect_equal(format_title("a b", 6), "`a b`") expect_equal(format_title("a b", 5), "`a b`") - expect_equal(format_title("a b", 4), "`a \u2026") - expect_equal(format_title("a b", 3), "`a\u2026") + expect_equal(format_title("a b", 4), continue("`a ")) + expect_equal(format_title("a b", 3), continue("`a")) }) From b22f6af496f3df444146954080475c9589ed3e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 15:14:43 +0100 Subject: [PATCH 120/133] Revert "Revert "test latin1 instead of C locale"" This reverts commit 22635f50067bedb9e99914028d9860d9e4b8e5e9. --- tests/testthat/helper-output.R | 24 +++++++++++++------- tests/testthat/out-native/deal1.txt | 8 +++---- tests/testthat/out-native/deal2.txt | 8 +++---- tests/testthat/out-native/deal3.txt | 8 +++---- tests/testthat/out-native/multi-extra-10.txt | 6 ++--- tests/testthat/out-native/utf8.txt | 12 +++++----- tests/testthat/test-format_character.R | 5 +++- tests/testthat/test-format_multi.R | 9 +++++--- 8 files changed, 47 insertions(+), 33 deletions(-) diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index 3489f87b0..a3a7ebad7 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -18,6 +18,11 @@ df_all <- list( expect_pillar_output <- function(x = NULL, ..., filename, xp = NULL, xf = NULL, crayon = TRUE, output_width = 80L) { object_quo <- rlang::quo(get_pillar_output_object(x, ..., xp = xp, xf = xf)) + expect_pillar_output_utf8(object_quo, filename, output_width) + expect_pillar_output_latin1(object_quo, filename, output_width) +} + +expect_pillar_output_utf8 <- function(object_quo, filename, output_width) { if (l10n_info()$`UTF-8`) { expect_known_display( object = !!object_quo, @@ -26,14 +31,17 @@ expect_pillar_output <- function(x = NULL, ..., filename, xp = NULL, xf = NULL, width = output_width ) } - withr::with_locale( - c(LC_CTYPE = "C"), - expect_known_display( - object = !!object_quo, - file = file.path("out-native", filename), - crayon = FALSE, - width = output_width - ) +} + +expect_pillar_output_latin1 <- function(object_quo, filename, output_width) { + old <- rlang::mut_latin1_locale() + on.exit(Sys.setlocale("LC_CTYPE", old)) + + expect_known_display( + object = !!object_quo, + file = file.path("out-native", filename), + crayon = FALSE, + width = output_width ) } diff --git a/tests/testthat/out-native/deal1.txt b/tests/testthat/out-native/deal1.txt index 814e0b3a2..3ce7ab88b 100644 --- a/tests/testthat/out-native/deal1.txt +++ b/tests/testthat/out-native/deal1.txt @@ -1,4 +1,4 @@ -`\u6210\u4ea4` - -"\u6210\u4ea4\u65e5" - +`` + + + diff --git a/tests/testthat/out-native/deal2.txt b/tests/testthat/out-native/deal2.txt index e62c1b595..45428f620 100644 --- a/tests/testthat/out-native/deal2.txt +++ b/tests/testthat/out-native/deal2.txt @@ -1,4 +1,4 @@ -`\u6210\u4ea4\u65e5` - -"\u6210\u4ea4" - +`` + + + diff --git a/tests/testthat/out-native/deal3.txt b/tests/testthat/out-native/deal3.txt index 8f8021e37..217b600c8 100644 --- a/tests/testthat/out-native/deal3.txt +++ b/tests/testthat/out-native/deal3.txt @@ -1,4 +1,4 @@ -`\u6210\u4ea4\u65e5` - - 1 - NA +`` + + 1 + NA diff --git a/tests/testthat/out-native/multi-extra-10.txt b/tests/testthat/out-native/multi-extra-10.txt index 6b1778abb..f08b0dd72 100644 --- a/tests/testthat/out-native/multi-extra-10.txt +++ b/tests/testthat/out-native/multi-extra-10.txt @@ -1,3 +1,3 @@ -col_02 -col_03 -col_04 +col_02  +col_03  +col_04  diff --git a/tests/testthat/out-native/utf8.txt b/tests/testthat/out-native/utf8.txt index 947d2dde4..3b1465730 100644 --- a/tests/testthat/out-native/utf8.txt +++ b/tests/testthat/out-native/utf8.txt @@ -4,13 +4,13 @@ 2 "\a\b\f\n\r\t" Named control code 3 abcdefuvwxyz ASCII 4 "\u0080\u009f" C1 control code - 5 NA Latin-1 - 6 NA Unicode - 7 NA Unicode wide + 5  ¡¢£¤¥úûüýþÿ Latin-1 + 6 Unicode + 7 Unicode wide 8 "\u0e00\u2029" Unicode control - 9 xxxxxxxxxxxxNA Unicode ignorable -10 aaaaaaaaaaaaNA Unicode mark -11 NA Emoji + 9 x­xxxxxxxxxxx Unicode ignorable +10 aaaaaaaaaaaa Unicode mark +11 Emoji 12 "x\U0010ffffx" Unassigned 13 "\xfd\xfe\xff" Invalid 14 "\\" Backslash diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index 853c62669..04105527c 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -71,5 +71,8 @@ test_that("output test", { expect_pillar_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") expect_pillar_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") expect_pillar_output(c("", " ", " a", "a ", "a b"), width = 5, filename = "spaces.txt") - expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") + # Output is not UTF-8 encoded + suppressWarnings( + expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") + ) }) diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 88e86d363..2cfebaa75 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -40,9 +40,12 @@ test_that("output test", { expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") expect_pillar_output(xf = colonnade(x, width = Inf), filename = "multi-inf.txt") - expect_pillar_output( - xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), - filename = "multi-extra-10.txt" + # Output is not UTF-8 encoded + suppressWarnings( + expect_pillar_output( + xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), + filename = "multi-extra-10.txt" + ) ) }) From dad6ec5d4b0229543b78a141f970585d28dfc570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 15:20:06 +0100 Subject: [PATCH 121/133] add early failure --- R/title.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/title.R b/R/title.R index ec5d237ff..704738b1e 100644 --- a/R/title.R +++ b/R/title.R @@ -3,6 +3,7 @@ pillar_title <- function(title, ...) { width <- 0L } else { width <- get_extent(format_title(title, width = Inf)) + stopifnot(!is.na(width)) } ret <- structure( From 6643f2a089515cce16fc62f37f1488d044631425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 15:20:21 +0100 Subject: [PATCH 122/133] adapt to Windows tests --- tests/testthat/out-native/multi-extra-10.txt | 6 ++-- tests/testthat/out-native/tibble-all--30.txt | 2 +- .../out-native/tibble-all--300-20.txt | 2 +- .../out-native/tibble-all--300-30.txt | 14 ++++---- .../out-native/tibble-all--300-40.txt | 12 +++---- .../out-native/tibble-all--300-50.txt | 8 ++--- .../out-native/tibble-all--300-60.txt | 8 ++--- .../out-native/tibble-all--300-70.txt | 12 +++---- tests/testthat/out-native/tibble-all--300.txt | 12 +++---- tests/testthat/out-native/tibble-newline.txt | 4 +-- tests/testthat/out-native/tibble-space.txt | 8 ++--- tests/testthat/out-native/utf8.txt | 34 +++++++++---------- 12 files changed, 61 insertions(+), 61 deletions(-) diff --git a/tests/testthat/out-native/multi-extra-10.txt b/tests/testthat/out-native/multi-extra-10.txt index f08b0dd72..e92309285 100644 --- a/tests/testthat/out-native/multi-extra-10.txt +++ b/tests/testthat/out-native/multi-extra-10.txt @@ -1,3 +1,3 @@ -col_02  -col_03  -col_04  +col_02 +col_03 +col_04 diff --git a/tests/testthat/out-native/tibble-all--30.txt b/tests/testthat/out-native/tibble-all--30.txt index 6e9fd9d6a..e16955bf3 100644 --- a/tests/testthat/out-native/tibble-all--30.txt +++ b/tests/testthat/out-native/tibble-all--30.txt @@ -2,4 +2,4 @@ 1 1.00 1 T a 2 2.50 2 F b -3 NA NA NA NA +3 NA NA NA diff --git a/tests/testthat/out-native/tibble-all--300-20.txt b/tests/testthat/out-native/tibble-all--300-20.txt index f2648b383..ad51ef13b 100644 --- a/tests/testthat/out-native/tibble-all--300-20.txt +++ b/tests/testthat/out-native/tibble-all--300-20.txt @@ -7,7 +7,7 @@ 1 a a 2 b b -3 NA NA +3 f 1 2015-12-10 diff --git a/tests/testthat/out-native/tibble-all--300-30.txt b/tests/testthat/out-native/tibble-all--300-30.txt index 08eb091d3..f6ad4bfb3 100644 --- a/tests/testthat/out-native/tibble-all--300-30.txt +++ b/tests/testthat/out-native/tibble-all--300-30.txt @@ -2,19 +2,19 @@ 1 1.00 1 T a 2 2.50 2 F b -3 NA NA NA NA +3 NA NA NA e f 1 a 2015-12-10 2 b 2015-12-11 -3 NA NA +3 NA g -1 2015-12-09 10:51:35 -2 2015-12-09 10:51:36 +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 3 NA h i -1   -2   -3   +1 +2 +3 diff --git a/tests/testthat/out-native/tibble-all--300-40.txt b/tests/testthat/out-native/tibble-all--300-40.txt index 717e53621..590abad60 100644 --- a/tests/testthat/out-native/tibble-all--300-40.txt +++ b/tests/testthat/out-native/tibble-all--300-40.txt @@ -2,14 +2,14 @@ 1 1.00 1 T a a 2 2.50 2 F b b -3 NA NA NA NA NA +3 NA NA NA f g -1 2015-12-10 2015-12-09 10:51:35 -2 2015-12-11 2015-12-09 10:51:36 +1 2015-12-10 2015-12-09 10:51:35 +2 2015-12-11 2015-12-09 10:51:36 3 NA NA h i -1   -2   -3   +1 +2 +3 diff --git a/tests/testthat/out-native/tibble-all--300-50.txt b/tests/testthat/out-native/tibble-all--300-50.txt index 29a6c5f01..29e958a8d 100644 --- a/tests/testthat/out-native/tibble-all--300-50.txt +++ b/tests/testthat/out-native/tibble-all--300-50.txt @@ -2,9 +2,9 @@ 1 1.00 1 T a a 2015-12-10 2 2.50 2 F b b 2015-12-11 -3 NA NA NA NA NA NA +3 NA NA NA NA g h i -1 2015-12-09 10:51:35   -2 2015-12-09 10:51:36   -3 NA   +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 +3 NA diff --git a/tests/testthat/out-native/tibble-all--300-60.txt b/tests/testthat/out-native/tibble-all--300-60.txt index 29a6c5f01..29e958a8d 100644 --- a/tests/testthat/out-native/tibble-all--300-60.txt +++ b/tests/testthat/out-native/tibble-all--300-60.txt @@ -2,9 +2,9 @@ 1 1.00 1 T a a 2015-12-10 2 2.50 2 F b b 2015-12-11 -3 NA NA NA NA NA NA +3 NA NA NA NA g h i -1 2015-12-09 10:51:35   -2 2015-12-09 10:51:36   -3 NA   +1 2015-12-09 10:51:35 +2 2015-12-09 10:51:36 +3 NA diff --git a/tests/testthat/out-native/tibble-all--300-70.txt b/tests/testthat/out-native/tibble-all--300-70.txt index 46282f5f5..9ce4781ed 100644 --- a/tests/testthat/out-native/tibble-all--300-70.txt +++ b/tests/testthat/out-native/tibble-all--300-70.txt @@ -1,10 +1,10 @@ a b c d e f g -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 -3 NA NA NA NA NA NA NA +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 +3 NA NA NA NA NA h i -1   -2   -3   +1 +2 +3 diff --git a/tests/testthat/out-native/tibble-all--300.txt b/tests/testthat/out-native/tibble-all--300.txt index 249bc614d..7c1a5a2b8 100644 --- a/tests/testthat/out-native/tibble-all--300.txt +++ b/tests/testthat/out-native/tibble-all--300.txt @@ -1,10 +1,10 @@ a b c d e f g h -1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35  -2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36  -3 NA NA NA NA NA NA NA  +1 1.00 1 T a a 2015-12-10 2015-12-09 10:51:35 +2 2.50 2 F b b 2015-12-11 2015-12-09 10:51:36 +3 NA NA NA NA NA i -1  -2  -3  +1 +2 +3 diff --git a/tests/testthat/out-native/tibble-newline.txt b/tests/testthat/out-native/tibble-newline.txt index dcbfb853b..dad287a3d 100644 --- a/tests/testthat/out-native/tibble-newline.txt +++ b/tests/testthat/out-native/tibble-newline.txt @@ -1,4 +1,4 @@ `\n` `\r` -1 "\n" "\n" -2 "\"" "\n" +1 "\n" "\n" +2 "\"" "\n" diff --git a/tests/testthat/out-native/tibble-space.txt b/tests/testthat/out-native/tibble-space.txt index ecc32c1ad..6b8f6170e 100644 --- a/tests/testthat/out-native/tibble-space.txt +++ b/tests/testthat/out-native/tibble-space.txt @@ -1,6 +1,6 @@ a -1 "" -2 " " -3 "a " -4 " a" +1 "" +2 " " +3 "a " +4 " a" diff --git a/tests/testthat/out-native/utf8.txt b/tests/testthat/out-native/utf8.txt index 3b1465730..49af61363 100644 --- a/tests/testthat/out-native/utf8.txt +++ b/tests/testthat/out-native/utf8.txt @@ -1,17 +1,17 @@ - chars desc - - 1 "\u0001\u001f" C0 control code - 2 "\a\b\f\n\r\t" Named control code - 3 abcdefuvwxyz ASCII - 4 "\u0080\u009f" C1 control code - 5  ¡¢£¤¥úûüýþÿ Latin-1 - 6 Unicode - 7 Unicode wide - 8 "\u0e00\u2029" Unicode control - 9 x­xxxxxxxxxxx Unicode ignorable -10 aaaaaaaaaaaa Unicode mark -11 Emoji -12 "x\U0010ffffx" Unassigned -13 "\xfd\xfe\xff" Invalid -14 "\\" Backslash -15 "\"" Quote + chars desc + + 1 "\u0001\u001f" C0 control c~ + 2 "\a\b\f\n\r\t" Named contro~ + 3 abcdefuvwxyz ASCII + 4 "\u0080\u009f" C1 control c~ + 5 Latin-1 + 6 AaAaAaCcCcCc Unicode + 7 !"#$%& Unicode wide + 8 "\u0e00\u2029" Unicode cont~ + 9 "xxxxxxxxx\U000e0001x\U000e0020x\U000e01efx" Unicode igno~ +10 a`aa^a~aaaaaaaa Unicode mark +11 "\U0001f600\U0001f601\U0001f602\U0001f603\U0001f604\U0001f483" Emoji +12 "x\U0010ffffx" Unassigned +13 "\xfd\xfe\xff" Invalid +14 "\\" Backslash +15 "\"" Quote From 6dd850a13afc9b13c51985a7db77e6b01f5fcdff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 15:25:30 +0100 Subject: [PATCH 123/133] separate Windows and Linux testing --- tests/testthat/helper-output.R | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R index a3a7ebad7..587a9f70d 100644 --- a/tests/testthat/helper-output.R +++ b/tests/testthat/helper-output.R @@ -34,15 +34,14 @@ expect_pillar_output_utf8 <- function(object_quo, filename, output_width) { } expect_pillar_output_latin1 <- function(object_quo, filename, output_width) { - old <- rlang::mut_latin1_locale() - on.exit(Sys.setlocale("LC_CTYPE", old)) - - expect_known_display( - object = !!object_quo, - file = file.path("out-native", filename), - crayon = FALSE, - width = output_width - ) + if (!l10n_info()$`UTF-8`) { + expect_known_display( + object = !!object_quo, + file = file.path("out-native", filename), + crayon = FALSE, + width = output_width + ) + } } get_pillar_output_object <- function(x = NULL, xp = NULL, xf = NULL, ...) { From 437eec5ff480ed2a0155f051a173d90f214eab3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 16 Nov 2017 15:45:12 +0100 Subject: [PATCH 124/133] shut up tests on Windows --- tests/testthat/test-format_character.R | 6 ++---- tests/testthat/test-format_multi.R | 10 ++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/testthat/test-format_character.R b/tests/testthat/test-format_character.R index 04105527c..2bca402e6 100644 --- a/tests/testthat/test-format_character.R +++ b/tests/testthat/test-format_character.R @@ -71,8 +71,6 @@ test_that("output test", { expect_pillar_output("\u6210\u4ea4", title = "\u6210\u4ea4\u65e5", filename = "deal2.txt") expect_pillar_output(1L, title = "\u6210\u4ea4\u65e5", filename = "deal3.txt") expect_pillar_output(c("", " ", " a", "a ", "a b"), width = 5, filename = "spaces.txt") - # Output is not UTF-8 encoded - suppressWarnings( - expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") - ) + skip_on_os("windows") + expect_pillar_output(xf = colonnade(chartype_frame()), width = 50, filename = "utf8.txt") }) diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 2cfebaa75..3fbf68c51 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -40,12 +40,10 @@ test_that("output test", { expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") expect_pillar_output(xf = colonnade(x, width = Inf), filename = "multi-inf.txt") - # Output is not UTF-8 encoded - suppressWarnings( - expect_pillar_output( - xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), - filename = "multi-extra-10.txt" - ) + skip_on_os("windows") + expect_pillar_output( + xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), + filename = "multi-extra-10.txt" ) }) From 7526934525528815e461a648f84470482b02ae96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 18 Nov 2017 23:30:51 +0100 Subject: [PATCH 125/133] fix DESCRIPTION --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 035587d6f..1254a2eaf 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Authors@R: c( person("Hadley", "Wickham", role = "aut"), person("RStudio", role = "cph") ) -Description: Provides a `pillar` generic designed for formatting columns +Description: Provides a 'pillar' generic designed for formatting columns of data using the full range of colours provided by modern terminals. License: GPL-3 Encoding: UTF-8 From 40637649d808a175f7f9bf65ae4f704b048087cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 18 Nov 2017 23:31:24 +0100 Subject: [PATCH 126/133] fix corner case with one tier per column and width = Inf --- R/multi.R | 2 +- tests/testthat/out/letters-inf.txt | 8 ++++++++ tests/testthat/test-format_multi.R | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/testthat/out/letters-inf.txt diff --git a/R/multi.R b/R/multi.R index 4341d264c..1fc27c184 100644 --- a/R/multi.R +++ b/R/multi.R @@ -178,7 +178,7 @@ get_tier_widths <- function(width, ncol, rowid_width, tier_width = getOption("wi width ) } else { - pos <- seq(0, length.out = ncol, by = tier_width) + pos <- seq(0, length.out = ncol + 1L, by = tier_width) } widths <- diff(pos) - rowid_width diff --git a/tests/testthat/out/letters-inf.txt b/tests/testthat/out/letters-inf.txt new file mode 100644 index 000000000..61fce3a00 --- /dev/null +++ b/tests/testthat/out/letters-inf.txt @@ -0,0 +1,8 @@ +  +1 a b c d e f g h i j k l m n o p q r s t u v w x y z +  +1 a b c d e f g h i j k l m n o p q r s t u v w x y z +  +1 a b c d e f g h i j k l m n o p q r s t u v w x y z +  +1 a b c d e f g h i j k l m n o p q r s t u v w x y z diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index 3fbf68c51..ba5309865 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -40,6 +40,14 @@ test_that("output test", { expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") expect_pillar_output(xf = colonnade(x, width = Inf), filename = "multi-inf.txt") + expect_pillar_output( + xf = colonnade( + rep(list(paste(letters, collapse = " ")), 4), + width = Inf + ), + filename = "letters-inf.txt" + ) + skip_on_os("windows") expect_pillar_output( xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), From 814f3592f183d5f7f9bc64e83f926b59fcd30b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 18 Nov 2017 23:31:57 +0100 Subject: [PATCH 127/133] new_ornament() computes extent --- R/ornament.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/ornament.R b/R/ornament.R index 8adb88185..29a962159 100644 --- a/R/ornament.R +++ b/R/ornament.R @@ -13,6 +13,7 @@ #' #' @export new_ornament <- function(x, width = NULL, align = NULL) { + if (is.null(width)) width <- get_max_extent(x) ret <- structure( x, align = align, From 21dcc4dfe641b0aa1ee089d4126eab8ea39f2a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 18 Nov 2017 23:32:41 +0100 Subject: [PATCH 128/133] remove Rd files --- R/spark-bar.R | 42 +++++++++++++++++++++--------------------- R/spark-line.R | 16 ++++++++-------- R/width.R | 14 +++++++------- man/set_width.Rd | 19 ------------------- man/spark_bar.Rd | 32 -------------------------------- man/spark_line.Rd | 20 -------------------- 6 files changed, 36 insertions(+), 107 deletions(-) delete mode 100644 man/set_width.Rd delete mode 100644 man/spark_bar.Rd delete mode 100644 man/spark_line.Rd diff --git a/R/spark-bar.R b/R/spark-bar.R index 46aeb7471..c026a0456 100644 --- a/R/spark-bar.R +++ b/R/spark-bar.R @@ -1,24 +1,24 @@ -#' Draw a sparkline bar graph with unicode block characters -#' -#' Rendered using [block elements](https://en.wikipedia.org/wiki/Block_Elements). -#' In most common fixed width fonts these are rendered wider than regular -#' characters which means they are not suitable if you need precise alignment. -#' -#' @param x A numeric vector between 0 and 1 -#' @param safe Nominally there are 8 block elements from 1/8 height to full -#' height (8/8). However, the half-height and full-height blocks appear -#' to be rendered inconsistently (possibly due to font substitution). -#' @examples -#' \dontrun{ -#' x <- seq(0, 1, length = 6) -#' spark_bar(x) -#' spark_bar(sample(x)) -#' -#' # This might work if you're lucky -#' spark_bar(seq(0, 1, length = 8), safe = FALSE) -#' -#' spark_bar(c(0, NA, 0.5, NA, 1)) -#' } +# Draw a sparkline bar graph with unicode block characters +# +# Rendered using [block elements](https://en.wikipedia.org/wiki/Block_Elements). +# In most common fixed width fonts these are rendered wider than regular +# characters which means they are not suitable if you need precise alignment. +# +# @param x A numeric vector between 0 and 1 +# @param safe Nominally there are 8 block elements from 1/8 height to full +# height (8/8). However, the half-height and full-height blocks appear +# to be rendered inconsistently (possibly due to font substitution). +# @examples +# \dontrun{ +# x <- seq(0, 1, length = 6) +# spark_bar(x) +# spark_bar(sample(x)) +# +# # This might work if you're lucky +# spark_bar(seq(0, 1, length = 8), safe = FALSE) +# +# spark_bar(c(0, NA, 0.5, NA, 1)) +# } spark_bar <- function(x, safe = TRUE) { stopifnot(is.numeric(x)) diff --git a/R/spark-line.R b/R/spark-line.R index 579920a35..ff61283c8 100644 --- a/R/spark-line.R +++ b/R/spark-line.R @@ -1,11 +1,11 @@ -#' Draw a sparkline line graph with Braille characters. -#' -#' @inheritParams spark_bar -#' @examples -#' \dontrun{ -#' x <- seq(0, 1, length = 10) -#' spark_line(x) -#' } +# Draw a sparkline line graph with Braille characters. +# +# @inheritParams spark_bar +# @examples +# \dontrun{ +# x <- seq(0, 1, length = 10) +# spark_line(x) +# } spark_line <- function(x) { stopifnot(is.numeric(x)) diff --git a/R/width.R b/R/width.R index 7d98209d5..f6ff45e48 100644 --- a/R/width.R +++ b/R/width.R @@ -2,12 +2,12 @@ get_width <- function(x) { attr(x, "width") } -#' Set width and minimum width information for an object -#' -#' Returns the input with updated `width` or `min_width` attributes. -#' -#' @param x Input to which assign a width or minimum width -#' @param width,min_width The new width +# Set width and minimum width information for an object +# +# Returns the input with updated `width` or `min_width` attributes. +# +# @param x Input to which assign a width or minimum width +# @param width,min_width The new width set_width <- function(x, width) { if (is.null(width)) return(x) if (is.infinite(width)) { @@ -26,7 +26,7 @@ get_min_width <- function(x) { attr(x, "min_width") %||% get_width(x) } -#' @rdname set_width +# @rdname set_width set_min_width <- function(x, min_width) { if (is.null(min_width)) return(x) attr(x, "min_width") <- as.integer(min_width) diff --git a/man/set_width.Rd b/man/set_width.Rd deleted file mode 100644 index 1f3551b48..000000000 --- a/man/set_width.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/width.R -\name{set_width} -\alias{set_width} -\alias{set_min_width} -\title{Set width and minimum width information for an object} -\usage{ -set_width(x, width) - -set_min_width(x, min_width) -} -\arguments{ -\item{x}{Input to which assign a width or minimum width} - -\item{width, min_width}{The new width} -} -\description{ -Returns the input with updated \code{width} or \code{min_width} attributes. -} diff --git a/man/spark_bar.Rd b/man/spark_bar.Rd deleted file mode 100644 index f82e5a830..000000000 --- a/man/spark_bar.Rd +++ /dev/null @@ -1,32 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/spark-bar.R -\name{spark_bar} -\alias{spark_bar} -\title{Draw a sparkline bar graph with unicode block characters} -\usage{ -spark_bar(x, safe = TRUE) -} -\arguments{ -\item{x}{A numeric vector between 0 and 1} - -\item{safe}{Nominally there are 8 block elements from 1/8 height to full -height (8/8). However, the half-height and full-height blocks appear -to be rendered inconsistently (possibly due to font substitution).} -} -\description{ -Rendered using \href{https://en.wikipedia.org/wiki/Block_Elements}{block elements}. -In most common fixed width fonts these are rendered wider than regular -characters which means they are not suitable if you need precise alignment. -} -\examples{ -\dontrun{ -x <- seq(0, 1, length = 6) -spark_bar(x) -spark_bar(sample(x)) - -# This might work if you're lucky -spark_bar(seq(0, 1, length = 8), safe = FALSE) - -spark_bar(c(0, NA, 0.5, NA, 1)) -} -} diff --git a/man/spark_line.Rd b/man/spark_line.Rd deleted file mode 100644 index e035ad5c5..000000000 --- a/man/spark_line.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/spark-line.R -\name{spark_line} -\alias{spark_line} -\title{Draw a sparkline line graph with Braille characters.} -\usage{ -spark_line(x) -} -\arguments{ -\item{x}{A numeric vector between 0 and 1} -} -\description{ -Draw a sparkline line graph with Braille characters. -} -\examples{ -\dontrun{ -x <- seq(0, 1, length = 10) -spark_line(x) -} -} From d1c09500bfc13a5e51c9c44ccee5812eecb7dc08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 18 Nov 2017 23:34:40 +0100 Subject: [PATCH 129/133] add examples --- R/extent.R | 5 +++++ R/multi.R | 18 +++++++++++++++++- R/ornament.R | 2 ++ R/scientific.R | 4 ++++ R/shaft.R | 6 ++++++ R/sigfig.R | 18 ++++++++++++++++-- R/styles.R | 6 ++++++ man/colonnade.Rd | 14 ++++++++++++++ man/extra_cols.Rd | 5 ++++- man/format_decimal.Rd | 11 +++++++++-- man/format_scientific.Rd | 7 +++++++ man/get_extent.Rd | 5 +++++ man/new_ornament.Rd | 3 +++ man/new_pillar_shaft.Rd | 1 + man/pillar_shaft.Rd | 6 ++++++ man/style_subtle.Rd | 14 ++++++++++++++ 16 files changed, 119 insertions(+), 6 deletions(-) diff --git a/R/extent.R b/R/extent.R index a7c1f6e3d..9481d07c8 100644 --- a/R/extent.R +++ b/R/extent.R @@ -5,6 +5,9 @@ #' #' @param x A character vector. #' @export +#' @examples +#' get_extent(c("abc", "de")) +#' get_extent("\u904b\u6c23") get_extent <- function(x) { utf8::utf8_width(crayon::strip_style(x), encode = FALSE) } @@ -14,6 +17,8 @@ get_extent <- function(x) { #' character vector, zero for empty vectors. #' @export #' @rdname get_extent +#' @examples +#' get_max_extent(c("abc", "de")) get_max_extent <- function(x) { max(get_extent(x), 0L, na.rm = TRUE) } diff --git a/R/multi.R b/R/multi.R index 1fc27c184..45e88212e 100644 --- a/R/multi.R +++ b/R/multi.R @@ -9,6 +9,18 @@ #' @param width Default width of the entire output, optional #' @param ... Ignored #' @export +#' @examples +#' colonnade(list(a = 1:3, b = letters[1:3])) +#' +#' long_string <- list(paste(letters, collapse = " ")) +#' colonnade(long_string, width = 20) +#' colonnade(long_string, has_row_id = FALSE, width = 20) +#' +#' # The width can also be overridden when calling format() or print(): +#' print(colonnade(long_string), width = 20) +#' +#' # If width is larger than getOption("width"), multiple tiers are created: +#' colonnade(rep(long_string, 4), width = Inf) colonnade <- function(x, has_row_id = TRUE, width = NULL, ...) { has_title <- is_named(x) if (has_title) { @@ -39,6 +51,8 @@ colonnade <- function(x, has_row_id = TRUE, width = NULL, ...) { #' #' @rdname colonnade #' @export +#' @examples +#' squeeze(colonnade(long_string), width = 20) squeeze <- function(x, width = NULL, ...) { # Hacky shortcut for zero-height corner case if (attr(x, "zero_height")) { @@ -125,9 +139,11 @@ knit_print_squeezed_colonnade_tier <- function(x) { #' Formatting a [colonnade] object may lead to some columns being omitted #' due to width restrictions. This method returns a character vector that #' describes each of the omitted columns. -#' @param x The result of [format()] on a [colonnade] object +#' @param x The result of [squeeze()] on a [colonnade] object #' @param ... Unused #' @export +#' @examples +#' extra_cols(squeeze(colonnade(list(a = 1:3, b = 4:6), width = 8))) extra_cols <- function(x, ...) { UseMethod("extra_cols") } diff --git a/R/ornament.R b/R/ornament.R index 29a962159..1e45692f7 100644 --- a/R/ornament.R +++ b/R/ornament.R @@ -12,6 +12,8 @@ #' @param align Alignment, one of `"left"` or `"right"` #' #' @export +#' @examples +#' new_ornament(c("abc", "de"), align = "right") new_ornament <- function(x, width = NULL, align = NULL) { if (is.null(width)) width <- get_max_extent(x) ret <- structure( diff --git a/R/scientific.R b/R/scientific.R index 352157f3c..545adb429 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -3,8 +3,12 @@ #' Uses colour, careful alignment, and superscripts to display numbers #' in scientific notation. #' +#' @seealso [format_decimal()] #' @inheritParams format_decimal #' @param superscript If `TRUE`, will use superscript numbers in exponent. +#' @examples +#' format_scientific(1.5:3.5) +#' format_scientific(1e9) format_scientific <- function(x, sigfig = 3, superscript = TRUE) { split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) } diff --git a/R/shaft.R b/R/shaft.R index 654bd8442..08fc6c739 100644 --- a/R/shaft.R +++ b/R/shaft.R @@ -5,6 +5,7 @@ #' This is a virtual or abstract class, you must specify the `subclass` #' argument. #' By convention, this should be a string that starts with `"pillar_shaft_"`. +#' See `vignette("extending", package = "tibble")` for usage examples. #' #' @param x An object #' @param ... Additional attributes @@ -35,6 +36,11 @@ new_pillar_shaft <- function(x, ..., width, min_width = width, subclass) { #' @param x A vector to format #' @param ... Unused, for extensibility. #' @export +#' @examples +#' pillar_shaft(1:3) +#' pillar_shaft(1.5:3.5) +#' pillar_shaft(NA) +#' pillar_shaft(c(1:3, NA)) pillar_shaft <- function(x, ...) { UseMethod("pillar_shaft") } diff --git a/R/sigfig.R b/R/sigfig.R index 2a58c9d29..57fb5b470 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -6,14 +6,18 @@ #' in paler gray. #' #' @return A list with at least the following elements: -#' * `neg`: negative sign or space, if needed +#' * `neg`: negative sign or space, `TRUE` if needed #' * `lhs`: whole number -#' * `dec`: decimal point, if needed +#' * `dec`: decimal point, `TRUE` if needed #' * `rhs`: remainder of number #' #' @param x A numeric vector #' @param sigfig Number of significant figures to display. #' @param ... Ignored +#' @seealso [format_scientific()] +#' @examples +#' format_decimal(1.5:3.5) +#' format_decimal(1e9) format_decimal <- function(x, sigfig = 3, ...) { split_decimal(x, sigfig) } @@ -165,6 +169,16 @@ format_rhs <- function(s) { #' @param negative,significant Logical vector the same length as `x` that #' indicate if the values are negative and significant, respectively #' @rdname style_subtle +#' @examples +#' style_num( +#' c("123", "456"), +#' negative = c(TRUE, FALSE) +#' ) +#' style_num( +#' c("123", "456"), +#' negative = c(TRUE, FALSE), +#' significant = c(FALSE, FALSE) +#' ) style_num <- function(x, negative, significant = rep_along(x, TRUE)) { ifelse(significant, ifelse(negative, style_neg(x), x), style_subtle(x)) } diff --git a/R/styles.R b/R/styles.R index 214663b7a..fb105efaa 100644 --- a/R/styles.R +++ b/R/styles.R @@ -14,6 +14,8 @@ keep_empty <- function(fun) { #' #' @param x The character vector to style. #' @export +#' @examples +#' style_subtle("text") style_subtle <- keep_empty(function(x) { style_grey(0.6, x) }) @@ -28,12 +30,16 @@ style_spark_na <- function(x) { #' @rdname style_subtle #' @export +#' @examples +#' style_na("NA") style_na <- function(x) { crayon::bgYellow(crayon::black(x)) } #' @rdname style_subtle #' @export +#' @examples +#' style_neg("123") style_neg <- keep_empty(function(x) { crayon::red(x) }) diff --git a/man/colonnade.Rd b/man/colonnade.Rd index 4589f6f07..39aab6c03 100644 --- a/man/colonnade.Rd +++ b/man/colonnade.Rd @@ -72,3 +72,17 @@ rounded down. Any space remaining after rounding is distributed from left to right, one space per column. } +\examples{ +colonnade(list(a = 1:3, b = letters[1:3])) + +long_string <- list(paste(letters, collapse = " ")) +colonnade(long_string, width = 20) +colonnade(long_string, has_row_id = FALSE, width = 20) + +# The width can also be overridden when calling format() or print(): +print(colonnade(long_string), width = 20) + +# If width is larger than getOption("width"), multiple tiers are created: +colonnade(rep(long_string, 4), width = Inf) +squeeze(colonnade(long_string), width = 20) +} diff --git a/man/extra_cols.Rd b/man/extra_cols.Rd index 1328f63ab..62c510256 100644 --- a/man/extra_cols.Rd +++ b/man/extra_cols.Rd @@ -7,7 +7,7 @@ extra_cols(x, ...) } \arguments{ -\item{x}{The result of \code{\link[=format]{format()}} on a \link{colonnade} object} +\item{x}{The result of \code{\link[=squeeze]{squeeze()}} on a \link{colonnade} object} \item{...}{Unused} } @@ -16,3 +16,6 @@ Formatting a \link{colonnade} object may lead to some columns being omitted due to width restrictions. This method returns a character vector that describes each of the omitted columns. } +\examples{ +extra_cols(squeeze(colonnade(list(a = 1:3, b = 4:6), width = 8))) +} diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd index d41292001..e52856ed8 100644 --- a/man/format_decimal.Rd +++ b/man/format_decimal.Rd @@ -16,9 +16,9 @@ format_decimal(x, sigfig = 3, ...) \value{ A list with at least the following elements: \itemize{ -\item \code{neg}: negative sign or space, if needed +\item \code{neg}: negative sign or space, \code{TRUE} if needed \item \code{lhs}: whole number -\item \code{dec}: decimal point, if needed +\item \code{dec}: decimal point, \code{TRUE} if needed \item \code{rhs}: remainder of number } } @@ -28,3 +28,10 @@ compare columns of numbers. Significant digits are coloured black or red (for positive and negative numbers) and non-significant digits are coloured in paler gray. } +\examples{ +format_decimal(1.5:3.5) +format_decimal(1e9) +} +\seealso{ +\code{\link[=format_scientific]{format_scientific()}} +} diff --git a/man/format_scientific.Rd b/man/format_scientific.Rd index 0ffb9915d..a2e205a1c 100644 --- a/man/format_scientific.Rd +++ b/man/format_scientific.Rd @@ -17,3 +17,10 @@ format_scientific(x, sigfig = 3, superscript = TRUE) Uses colour, careful alignment, and superscripts to display numbers in scientific notation. } +\examples{ +format_scientific(1.5:3.5) +format_scientific(1e9) +} +\seealso{ +\code{\link[=format_decimal]{format_decimal()}} +} diff --git a/man/get_extent.Rd b/man/get_extent.Rd index eddf8fcff..6fad243a7 100644 --- a/man/get_extent.Rd +++ b/man/get_extent.Rd @@ -19,3 +19,8 @@ vector. \code{get_max_extent()} calculates the maximum display width of all strings in a character vector, zero for empty vectors. } +\examples{ +get_extent(c("abc", "de")) +get_extent("\\u904b\\u6c23") +get_max_extent(c("abc", "de")) +} diff --git a/man/new_ornament.Rd b/man/new_ornament.Rd index 5a1b6c082..60f71225d 100644 --- a/man/new_ornament.Rd +++ b/man/new_ornament.Rd @@ -21,3 +21,6 @@ class to return a subclass of "pillar_shaft" and have the \code{\link[=format]{f for this subclass call \code{new_ornament()}. See the implementation of \code{pillar_shaft.numeric()} and \code{format.pillar_shaft_decimal()} for an example. } +\examples{ +new_ornament(c("abc", "de"), align = "right") +} diff --git a/man/new_pillar_shaft.Rd b/man/new_pillar_shaft.Rd index aaf69d36e..702bdb3d6 100644 --- a/man/new_pillar_shaft.Rd +++ b/man/new_pillar_shaft.Rd @@ -33,6 +33,7 @@ class. This is a virtual or abstract class, you must specify the \code{subclass} argument. By convention, this should be a string that starts with \code{"pillar_shaft_"}. +See \code{vignette("extending", package = "tibble")} for usage examples. \code{new_pillar_shaft_simple()} provides an implementation of the \code{pillar_shaft} class suitable for output that has a fixed formatting, which will be diff --git a/man/pillar_shaft.Rd b/man/pillar_shaft.Rd index d67761340..7987e85ed 100644 --- a/man/pillar_shaft.Rd +++ b/man/pillar_shaft.Rd @@ -65,3 +65,9 @@ Your \code{format()} method can assume a valid value for the \code{width} argume The default method will currently coerce via \code{\link[=as.character]{as.character()}}, but you should not rely on this behavior. } +\examples{ +pillar_shaft(1:3) +pillar_shaft(1.5:3.5) +pillar_shaft(NA) +pillar_shaft(c(1:3, NA)) +} diff --git a/man/style_subtle.Rd b/man/style_subtle.Rd index e20565dfa..96a7dc6f3 100644 --- a/man/style_subtle.Rd +++ b/man/style_subtle.Rd @@ -25,3 +25,17 @@ indicate if the values are negative and significant, respectively} Functions that allow implementers of formatters for custom data types to maintain a consistent style with the default data types. } +\examples{ +style_num( + c("123", "456"), + negative = c(TRUE, FALSE) +) +style_num( + c("123", "456"), + negative = c(TRUE, FALSE), + significant = c(FALSE, FALSE) +) +style_subtle("text") +style_na("NA") +style_neg("123") +} From dcdd8e6bfd5f06ca3f0272689d2a3ff17c731aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 20 Nov 2017 05:25:51 +0100 Subject: [PATCH 130/133] remove documentation again --- R/scientific.R | 22 +++++++++++----------- R/sigfig.R | 40 ++++++++++++++++++++-------------------- man/format_decimal.Rd | 37 ------------------------------------- man/format_scientific.Rd | 26 -------------------------- 4 files changed, 31 insertions(+), 94 deletions(-) delete mode 100644 man/format_decimal.Rd delete mode 100644 man/format_scientific.Rd diff --git a/R/scientific.R b/R/scientific.R index 545adb429..463b1e1a5 100644 --- a/R/scientific.R +++ b/R/scientific.R @@ -1,14 +1,14 @@ -#' Format numbers in scientific notation -#' -#' Uses colour, careful alignment, and superscripts to display numbers -#' in scientific notation. -#' -#' @seealso [format_decimal()] -#' @inheritParams format_decimal -#' @param superscript If `TRUE`, will use superscript numbers in exponent. -#' @examples -#' format_scientific(1.5:3.5) -#' format_scientific(1e9) +# Format numbers in scientific notation +# +# Uses colour, careful alignment, and superscripts to display numbers +# in scientific notation. +# +# @seealso [format_decimal()] +# @inheritParams format_decimal +# @param superscript If `TRUE`, will use superscript numbers in exponent. +# @examples +# format_scientific(1.5:3.5) +# format_scientific(1e9) format_scientific <- function(x, sigfig = 3, superscript = TRUE) { split_decimal(x, sigfig, scientific = TRUE, superscript = superscript) } diff --git a/R/sigfig.R b/R/sigfig.R index 57fb5b470..111475caf 100644 --- a/R/sigfig.R +++ b/R/sigfig.R @@ -1,23 +1,23 @@ -#' Format numbers in decimal notation -#' -#' This formatting system is designed to make it as easy as possible to -#' compare columns of numbers. Significant digits are coloured black or red -#' (for positive and negative numbers) and non-significant digits are coloured -#' in paler gray. -#' -#' @return A list with at least the following elements: -#' * `neg`: negative sign or space, `TRUE` if needed -#' * `lhs`: whole number -#' * `dec`: decimal point, `TRUE` if needed -#' * `rhs`: remainder of number -#' -#' @param x A numeric vector -#' @param sigfig Number of significant figures to display. -#' @param ... Ignored -#' @seealso [format_scientific()] -#' @examples -#' format_decimal(1.5:3.5) -#' format_decimal(1e9) +# Format numbers in decimal notation +# +# This formatting system is designed to make it as easy as possible to +# compare columns of numbers. Significant digits are coloured black or red +# (for positive and negative numbers) and non-significant digits are coloured +# in paler gray. +# +# @return A list with at least the following elements: +# * `neg`: negative sign or space, `TRUE` if needed +# * `lhs`: whole number +# * `dec`: decimal point, `TRUE` if needed +# * `rhs`: remainder of number +# +# @param x A numeric vector +# @param sigfig Number of significant figures to display. +# @param ... Ignored +# @seealso [format_scientific()] +# @examples +# format_decimal(1.5:3.5) +# format_decimal(1e9) format_decimal <- function(x, sigfig = 3, ...) { split_decimal(x, sigfig) } diff --git a/man/format_decimal.Rd b/man/format_decimal.Rd deleted file mode 100644 index e52856ed8..000000000 --- a/man/format_decimal.Rd +++ /dev/null @@ -1,37 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/sigfig.R -\name{format_decimal} -\alias{format_decimal} -\title{Format numbers in decimal notation} -\usage{ -format_decimal(x, sigfig = 3, ...) -} -\arguments{ -\item{x}{A numeric vector} - -\item{sigfig}{Number of significant figures to display.} - -\item{...}{Ignored} -} -\value{ -A list with at least the following elements: -\itemize{ -\item \code{neg}: negative sign or space, \code{TRUE} if needed -\item \code{lhs}: whole number -\item \code{dec}: decimal point, \code{TRUE} if needed -\item \code{rhs}: remainder of number -} -} -\description{ -This formatting system is designed to make it as easy as possible to -compare columns of numbers. Significant digits are coloured black or red -(for positive and negative numbers) and non-significant digits are coloured -in paler gray. -} -\examples{ -format_decimal(1.5:3.5) -format_decimal(1e9) -} -\seealso{ -\code{\link[=format_scientific]{format_scientific()}} -} diff --git a/man/format_scientific.Rd b/man/format_scientific.Rd deleted file mode 100644 index a2e205a1c..000000000 --- a/man/format_scientific.Rd +++ /dev/null @@ -1,26 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/scientific.R -\name{format_scientific} -\alias{format_scientific} -\title{Format numbers in scientific notation} -\usage{ -format_scientific(x, sigfig = 3, superscript = TRUE) -} -\arguments{ -\item{x}{A numeric vector} - -\item{sigfig}{Number of significant figures to display.} - -\item{superscript}{If \code{TRUE}, will use superscript numbers in exponent.} -} -\description{ -Uses colour, careful alignment, and superscripts to display numbers -in scientific notation. -} -\examples{ -format_scientific(1.5:3.5) -format_scientific(1e9) -} -\seealso{ -\code{\link[=format_decimal]{format_decimal()}} -} From 665c2041fbf40254c8d5d5e623cde701c2a1142e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 20 Nov 2017 05:29:06 +0100 Subject: [PATCH 131/133] remove unused --- R/title.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/title.R b/R/title.R index 704738b1e..6c0e98a05 100644 --- a/R/title.R +++ b/R/title.R @@ -23,7 +23,6 @@ format.pillar_title <- function(x, width, ...) { title <- x$title if (is.null(title)) return(character()) - desired_width <- get_width(x) title <- format_title(title, width) crayon::bold(title) From 56567380ec1e86d176c97d413d97d978d503db27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 20 Nov 2017 05:34:40 +0100 Subject: [PATCH 132/133] shut up more tests --- tests/testthat/test-format_multi.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-format_multi.R b/tests/testthat/test-format_multi.R index ba5309865..27a74c4cc 100644 --- a/tests/testthat/test-format_multi.R +++ b/tests/testthat/test-format_multi.R @@ -40,6 +40,7 @@ test_that("output test", { expect_pillar_output(xf = colonnade(x, width = 39), filename = "multi-39.txt") expect_pillar_output(xf = colonnade(x, width = Inf), filename = "multi-inf.txt") + skip_on_os("windows") expect_pillar_output( xf = colonnade( rep(list(paste(letters, collapse = " ")), 4), @@ -48,7 +49,6 @@ test_that("output test", { filename = "letters-inf.txt" ) - skip_on_os("windows") expect_pillar_output( xf = new_vertical(extra_cols(squeeze(colonnade(x), width = 10))), filename = "multi-extra-10.txt" From 3cb3acc5b86e01c20d8a260c53e7da56ffdc392c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Mon, 20 Nov 2017 05:52:04 +0100 Subject: [PATCH 133/133] up comments --- cran-comments.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cran-comments.md b/cran-comments.md index 55cc056a1..f6c3a959b 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,3 +1,12 @@ +Resubmission as requested by CRAN: + +- Added examples to documentation +- Removed .Rd files for currently private documentation +- Fixed DESCRIPTION +- Fixed a minor problem that had escaped testing + +I hope the same-version update is OK, because the originally submitted version never went public. + ## Test environments * local Ubuntu 17.04 install, R 3.4.2 * ubuntu 12.04 (on travis-ci), R 3.4.2