diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b1cb1601..ef2cb028c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # R specific hooks: https://github.com/lorenzwalthert/precommit repos: - repo: https://github.com/lorenzwalthert/precommit - rev: v0.3.2.9023 + rev: v0.3.2.9027 hooks: # - id: style-files # args: [--style_pkg=styler, --style_fun=tidyverse_style] diff --git a/DESCRIPTION b/DESCRIPTION index 0e4717bbb..5439d9ade 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: formatters Title: ASCII Formatting for Values and Tables -Version: 0.5.4.9002 -Date: 2023-10-19 +Version: 0.5.4.9005 +Date: 2023-11-27 Authors@R: c( person("Gabriel", "Becker", , "gabembecker@gmail.com", role = "aut", comment = "original creator of the package"), @@ -51,6 +51,7 @@ Collate: 'generics.R' 'labels.R' 'mpf_exporters.R' + 'package.R' 'page_size.R' 'pagination.R' 'tostring.R' diff --git a/NAMESPACE b/NAMESPACE index 7a1885dc2..c3eb240e4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -88,6 +88,7 @@ export(propose_column_widths) export(prov_footer) export(ref_df_row) export(round_fmt) +export(set_default_hsep) export(spans_to_viscell) export(spread_integer) export(sprintf_format) diff --git a/NEWS.md b/NEWS.md index d2f170049..036c7c3a7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,9 @@ -## formatters 0.5.4.9002 +## formatters 0.5.4.9005 * Applied `styler` and resolved package lint. Changed default indentation from 4 spaces to 2. * Allow tables with content rows in the end be exported. + * Fixed wrapping and section dividers error. + * Allowed section divider between header and table body. + * Added the possibility of setting a general default using `set_default_hsep()` that sets up the option `getOption("formatters_default_hsep")`. ## formatters 0.5.4 * Fixed a bug in `paginate_to_mpfs()` so that formatting in listings key columns is retained with pagination [`insightsengineering/rlistings#155`](https://github.com/insightsengineering/rlistings/issues/155). diff --git a/R/format_value.R b/R/format_value.R index aa5f4aa0f..1afc04579 100644 --- a/R/format_value.R +++ b/R/format_value.R @@ -1,5 +1,3 @@ -#' @importFrom htmltools tags tagList - formats_1d <- c( "xx", "xx.", "xx.x", "xx.xx", "xx.xxx", "xx.xxxx", "xx%", "xx.%", "xx.x%", "xx.xx%", "xx.xxx%", "(N=xx)", ">999.9", ">999.99", diff --git a/R/generics.R b/R/generics.R index bafaee5f7..638140fdf 100644 --- a/R/generics.R +++ b/R/generics.R @@ -639,3 +639,40 @@ setGeneric("num_rep_cols", function(obj) standardGeneric("num_rep_cols")) #' @export #' @rdname num_rep_cols setMethod("num_rep_cols", "ANY", function(obj) 0L) + +# header_section_div ----------------------------------------------------------- +#' @keywords internal +setGeneric("header_section_div", function(obj) standardGeneric("header_section_div")) +#' @keywords internal +setMethod( + "header_section_div", "MatrixPrintForm", + function(obj) obj$header_section_div +) +#' @keywords internal +setGeneric("header_section_div<-", function(obj, value) standardGeneric("header_section_div<-")) +#' @keywords internal +setMethod( + "header_section_div<-", "MatrixPrintForm", + function(obj, value) { + obj$header_section_div <- value + obj + } +) +# horizontal_sep --------------------------------------------------------------- +#' @keywords internal +setGeneric("horizontal_sep", function(obj) standardGeneric("horizontal_sep")) +#' @keywords internal +setMethod( + "horizontal_sep", "MatrixPrintForm", + function(obj) obj$horizontal_sep +) +#' @keywords internal +setGeneric("horizontal_sep<-", function(obj, value) standardGeneric("horizontal_sep<-")) +#' @keywords internal +setMethod( + "horizontal_sep<-", "MatrixPrintForm", + function(obj, value) { + obj$horizontal_sep <- value + obj + } +) diff --git a/R/matrix_form.R b/R/matrix_form.R index 05c8b261c..e25998771 100644 --- a/R/matrix_form.R +++ b/R/matrix_form.R @@ -191,8 +191,12 @@ disp_from_spans <- function(spans) { #' @param page_titles character. Page-specific titles, as a character #' vector. #' @param main_footer character(1). Main footer as a string. -#' @param prov_footer character. Provenance footer information as a +#' @param prov_footer character. Provenance footer information as a #' character vector. +#' @param header_section_div character(1). Divider to be used between header +#' and body sections. +#' @param horizontal_sep character(1). Horizontal separator to be used for printing +#' divisors between header and table body and between different footers. #' @param expand_newlines logical(1). Should the matrix form generated #' expand rows whose values contain newlines into multiple #' 'physical' rows (as they will appear when rendered into @@ -224,6 +228,8 @@ disp_from_spans <- function(spans) { #' \item{\code{page_titles}}{see argument} #' \item{\code{main_footer}}{see argument} #' \item{\code{prov_footer}}{see argument} +#' \item{\code{header_section_div}}{see argument} +#' \item{\code{horizontal_sep}}{see argument} #' \item{\code{col_gap}}{see argument} #' \item{\code{table_inset}}{see argument} #' } @@ -252,6 +258,8 @@ MatrixPrintForm <- function(strings = NULL, page_titles = character(), main_footer = "", prov_footer = character(), + header_section_div = NA_character_, + horizontal_sep = default_hsep(), col_gap = 3, table_inset = 0L, colwidths = NULL, @@ -274,6 +282,8 @@ MatrixPrintForm <- function(strings = NULL, page_titles = page_titles, main_footer = main_footer, prov_footer = prov_footer, + header_section_div = header_section_div, + horizontal_sep = horizontal_sep, col_gap = col_gap, table_inset = as.integer(table_inset), has_topleft = has_topleft, @@ -399,7 +409,7 @@ infer_ref_info <- function(mform, colspace_only) { ret$col_path <- replicate(nrow(ret), list(NA_character_)) non_na_col <- !is.na(ret$col) ret$col_path[non_na_col] <- col_pths[ret$col[non_na_col]] - ret$ref_index <- seq_len(nrow(ret)) + ret$ref_index <- match(ret$symbol, unique(ret$symbol)) ## ret$nlines <- vapply(paste0("{", ret$symbol, "} - ", ret$msg), nlines, 1L) ret <- ret[, names(ref_df_row())] @@ -891,20 +901,6 @@ reconstruct_basic_fnote_list <- function(mf) { paste0("{", refdf$symbol, "} - ", refdf$msg) } - -fix_fnote_df <- function(df) { - ind_symb <- df$symbol == as.character(df$ref_index) - df$ref_index <- seq_len(nrow(df)) - df$symbol[ind_symb] <- as.character(df$ref_index[ind_symb]) - df -} - - - - - - - .mf_subset_core_mats <- function(mf, i, row = TRUE) { fillnum <- if (row) nrow(mf_strings(mf)) - mf_nlheader(mf) else ncol(mf) if (is.logical(i) || all(i < 0)) { @@ -975,8 +971,10 @@ mpf_subset_rows <- function(mf, i) { ncs <- ncol(mf) mf <- .mf_subset_core_mats(mf, i, row = TRUE) - map <- data.frame(old_idx = c(seq_len(ncolrows), i + ncolrows), - new_idx = c(seq_len(ncolrows), ncolrows + order(i))) + map <- data.frame( + old_idx = c(seq_len(ncolrows), i + ncolrows), + new_idx = c(seq_len(ncolrows), ncolrows + order(i)) + ) row_map <- data.frame(old_idx = i, new_idx = order(i)) @@ -985,7 +983,6 @@ mpf_subset_rows <- function(mf, i) { old_nas <- is.na(refdf$row) refdf$row <- map_to_new(refdf$row, row_map) refdf <- refdf[old_nas | !is.na(refdf$row), ] - refdf <- fix_fnote_df(refdf) mf_fnote_df(mf) <- refdf rinfo <- mf_rinfo(mf) diff --git a/R/mpf_exporters.R b/R/mpf_exporters.R index a1e1e7dec..cc400db3d 100644 --- a/R/mpf_exporters.R +++ b/R/mpf_exporters.R @@ -47,7 +47,7 @@ export_as_txt <- function(x, cpp = NA_integer_, lpp = NA_integer_, ..., - hsep = default_hsep(), + hsep = NULL, indent_size = 2, tf_wrap = paginate, max_width = NULL, @@ -57,6 +57,8 @@ export_as_txt <- function(x, rep_cols = num_rep_cols(x), verbose = FALSE, page_break = "\\s\\n") { + + if (paginate) { pages <- paginate_to_mpfs( x, diff --git a/R/package.R b/R/package.R new file mode 100644 index 000000000..95f478919 --- /dev/null +++ b/R/package.R @@ -0,0 +1,11 @@ +#' formatters Package +#' +#' Package to format tables and listings in a flexible way. +#' +"_PACKAGE" + +#' @importFrom stats na.omit +#' @importFrom utils head tail localeToCharset +#' @importFrom htmltools tags tagList +#' @import checkmate +NULL diff --git a/R/tostring.R b/R/tostring.R index 34f50f324..d5aefe933 100644 --- a/R/tostring.R +++ b/R/tostring.R @@ -1,43 +1,52 @@ -## this can't be tested from within R -# nocov start -#' @importFrom stats na.omit -#' @importFrom utils head tail localeToCharset -#' @import checkmate - -d_hsep_factory <- function() { - warn_sent <- FALSE - function() { - if (any(grepl("^UTF", localeToCharset()))) { - "\u2014" - } else { - if (!warn_sent && interactive()) { - message( - "Detected non-UTF charset. Falling back to '-' ", - "as default header/body separator. This warning ", - "will only be shown once per R session." - ) - warn_sent <<- TRUE - } - "-" - } - } -} - -#' Default horizontal Separator +#' @title Default horizontal separator #' -#' The default horizontal separator character which can be +#' @description The default horizontal separator character which can be #' displayed in the current `charset` for use in rendering table-likes. #' +#' @param hsep_char character(1). Character that will be set in the R environment +#' options as default for creating the horizontal separator. It needs to be +#' single character. Use `getOption("formatters_default_hsep")` to get its current +#' value (`NULL` if not set). +#' #' @return `unicode` 2014 (long dash for generating solid horizontal line) #' if in a locale that uses a UTF character set, otherwise an ASCII hyphen #' with a once-per-session warning. #' -#' @export #' @examples #' default_hsep() -default_hsep <- d_hsep_factory() +#' set_default_hsep("o") +#' default_hsep() +#' +#' @name default_horizontal_sep +#' @export +default_hsep <- function() { + system_default_hsep <- getOption("formatters_default_hsep") + + if (is.null(system_default_hsep)) { + if (any(grepl("^UTF", utils::localeToCharset()))) { + hsep <- "\u2014" + } else { + if (interactive()) { + warning( + "Detected non-UTF charset. Falling back to '-' ", + "as default header/body separator. This warning ", + "will only be shown once per R session." + ) # nocov + } # nocov + hsep <- "-" # nocov + } + } else { + hsep <- system_default_hsep + } + hsep +} -# nocov end +#' @name default_horizontal_sep +#' @export +set_default_hsep <- function(hsep_char) { + checkmate::assert_character(hsep_char, n.chars = 1, len = 1, null.ok = TRUE) + options("formatters_default_hsep" = hsep_char) +} .calc_cell_widths <- function(mat, colwidths, col_gap) { spans <- mat$spans @@ -383,17 +392,19 @@ decimal_align <- function(string_mat, align_mat) { #' `max_width`), or horizontal separator character (e.g. `hsep = "+"`). #' #' @inheritParams MatrixPrintForm -#' @param widths numeric (or NULL). (proposed) widths for the columns +#' @param widths numeric (or `NULL`). (proposed) widths for the columns #' of \code{x}. The expected length of this numeric vector can be #' retrieved with `ncol() + 1` as the column of row names must #' also be considered. #' @param hsep character(1). Characters to repeat to create -#' header/body separator line. +#' header/body separator line. If `NULL`, the object value will be +#' used. If `" "`, an empty separator will be printed. Check [default_hsep()] +#' for more information. #' @param tf_wrap logical(1). Should the texts for title, subtitle, #' and footnotes be wrapped? -#' @param max_width integer(1), character(1) or NULL. Width that title +#' @param max_width integer(1), character(1) or `NULL`. Width that title #' and footer (including footnotes) materials should be -#' word-wrapped to. If NULL, it is set to the current print width +#' word-wrapped to. If `NULL`, it is set to the current print width #' of the session (`getOption("width")`). If set to `"auto"`, #' the width of the table (plus any table inset) is used. Ignored #' completely if `tf_wrap` is `FALSE`. @@ -422,7 +433,7 @@ setMethod("toString", "MatrixPrintForm", function(x, tf_wrap = FALSE, max_width = NULL, col_gap = mf_colgap(x), - hsep = default_hsep()) { + hsep = NULL) { checkmate::assert_flag(tf_wrap) mat <- matrix_form(x, indent_rownames = TRUE) @@ -500,7 +511,7 @@ setMethod("toString", "MatrixPrintForm", function(x, aligns <- mf_aligns(mat) keep_mat <- mf_display(mat) ## spans <- mat$spans - ## ri <- mat$row_info + mf_ri <- mf_rinfo(mat) ref_fnotes <- mf_rfnotes(mat) nl_header <- mf_nlheader(mat) @@ -517,18 +528,27 @@ setMethod("toString", "MatrixPrintForm", function(x, # Define gap string and divisor string gap_str <- strrep(" ", col_gap) + if (is.null(hsep)) { + hsep <- horizontal_sep(mat) + } div <- substr(strrep(hsep, ncchar), 1, ncchar) + hsd <- header_section_div(mat) + if (!is.na(hsd)) { + hsd <- substr(strrep(hsd, ncchar), 1, ncchar) + } else { + hsd <- NULL # no divisor + } # text head (paste w/o NA content header and gap string) txt_head <- apply(head(content, nl_header), 1, .paste_no_na, collapse = gap_str) # txt body - sec_seps_df <- x$row_info[, c("abs_rownumber", "trailing_sep"), drop = FALSE] + sec_seps_df <- mf_ri[, c("abs_rownumber", "trailing_sep"), drop = FALSE] if (!is.null(sec_seps_df) && any(!is.na(sec_seps_df$trailing_sep))) { bdy_cont <- tail(content, -nl_header) ## unfortunately we count "header rows" wrt line grouping so it ## doesn't match the real (i.e. body) rows as is - row_grouping <- tail(x$line_grouping, -nl_header) - mf_nrheader(x) + row_grouping <- tail(mf_lgrouping(mat), - nl_header) - mf_nrheader(mat) nrbody <- NROW(bdy_cont) stopifnot(length(row_grouping) == nrbody) ## all rows with non-NA section divs and the final row (regardless of NA status) @@ -569,16 +589,17 @@ setMethod("toString", "MatrixPrintForm", function(x, } # retrieving titles and footers - allts <- all_titles(x) + allts <- all_titles(mat) + ref_fnotes <- reorder_ref_fnotes(ref_fnotes) # Fix for ref_fnotes with \n characters XXX this does not count in the pagination if (any(grepl("\n", ref_fnotes))) { ref_fnotes <- unlist(strsplit(ref_fnotes, "\n", fixed = TRUE)) } allfoots <- list( - "main_footer" = main_footer(x), - "prov_footer" = prov_footer(x), + "main_footer" = main_footer(mat), + "prov_footer" = prov_footer(mat), "ref_footnotes" = ref_fnotes ) allfoots <- allfoots[!sapply(allfoots, is.null)] @@ -605,6 +626,7 @@ setMethod("toString", "MatrixPrintForm", function(x, titles_txt, # .do_inset(div, inset) happens if there are any titles .do_inset(txt_head, inset), .do_inset(div, inset), + .do_inset(hsd, inset), # header_section_div if present .do_inset(txt_body, inset), .footer_inset_helper(allfoots, div, inset) ), collapse = "\n"), @@ -666,6 +688,24 @@ setMethod("toString", "MatrixPrintForm", function(x, footer_txt } +reorder_ref_fnotes <- function(fns) { + ind <- gsub("\\{(.*)\\}.*", "\\1", fns) + ind_num <- suppressWarnings(as.numeric(ind)) + is_num <- !is.na(ind_num) + is_asis <- ind == fns + + if (all(is_num)) { + ord_num <- order(ind_num) + ord_char <- NULL + ord_other <- NULL + } else { + ord_num <- order(ind_num[is_num]) + ord_char <- order(ind[!is_num & !is_asis]) + ord_other <- order(ind[is_asis]) + } + c(fns[is_num][ord_num], fns[!is_num & !is_asis][ord_char], ind[is_asis][ord_other]) +} + new_line_warning <- function(str_v) { if (any(unlist(sapply(str_v, grepl, pattern = "\n")))) { msg <- c( @@ -977,7 +1017,6 @@ spans_to_viscell <- function(spans) { ## ' mf <- matrix_form(tbl) ## ' propose_column_widths(mf) propose_column_widths <- function(x, indent_size = 2) { - ## stopifnot(is(x, "VTableTree")) if (!is(x, "MatrixPrintForm")) { x <- matrix_form(x, indent_rownames = TRUE, indent_size = indent_size) } diff --git a/_pkgdown.yml b/_pkgdown.yml index 5130aa38f..346b3d34d 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -17,6 +17,9 @@ navbar: icon: fa-github href: https://github.com/insightsengineering/formatters reference: + - title: Formatters Overview + contents: + - formatters-package - title: Formatting Values desc: Functions related to applying formats to values contents: @@ -98,7 +101,7 @@ reference: - wrap_txt - ifnotlen0 - table_inset - - default_hsep + - default_horizontal_sep - font_lcpi - mf_strings - page_lcpp diff --git a/inst/WORDLIST b/inst/WORDLIST index 7f545686f..db6adc39b 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,4 +1,8 @@ +Hoffmann +Rua +de decrementing +funder glyphs mandatorily paginations diff --git a/man/MatrixPrintForm.Rd b/man/MatrixPrintForm.Rd index 4fa7c739a..f46061470 100644 --- a/man/MatrixPrintForm.Rd +++ b/man/MatrixPrintForm.Rd @@ -23,6 +23,8 @@ MatrixPrintForm( page_titles = character(), main_footer = "", prov_footer = character(), + header_section_div = NA_character_, + horizontal_sep = default_hsep(), col_gap = 3, table_inset = 0L, colwidths = NULL, @@ -47,6 +49,8 @@ matrix_print_form( page_titles = character(), main_footer = "", prov_footer = character(), + header_section_div = NA_character_, + horizontal_sep = default_hsep(), col_gap = 3, table_inset = 0L, colwidths = NULL, @@ -117,9 +121,15 @@ vector.} \item{main_footer}{character(1). Main footer as a string.} -\item{prov_footer}{character. Provenance footer information as a +\item{prov_footer}{character. Provenance footer information as a character vector.} +\item{header_section_div}{character(1). Divider to be used between header +and body sections.} + +\item{horizontal_sep}{character(1). Horizontal separator to be used for printing +divisors between header and table body and between different footers.} + \item{col_gap}{numeric(1). Space (in characters) between columns} \item{table_inset}{numeric(1). Table inset. See @@ -151,6 +161,8 @@ when the table is rendered} \item{\code{page_titles}}{see argument} \item{\code{main_footer}}{see argument} \item{\code{prov_footer}}{see argument} +\item{\code{header_section_div}}{see argument} +\item{\code{horizontal_sep}}{see argument} \item{\code{col_gap}}{see argument} \item{\code{table_inset}}{see argument} } diff --git a/man/default_hsep.Rd b/man/default_horizontal_sep.Rd similarity index 50% rename from man/default_hsep.Rd rename to man/default_horizontal_sep.Rd index bf6cd8d9d..2676614db 100644 --- a/man/default_hsep.Rd +++ b/man/default_horizontal_sep.Rd @@ -1,10 +1,20 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/tostring.R -\name{default_hsep} +\name{default_horizontal_sep} +\alias{default_horizontal_sep} \alias{default_hsep} -\title{Default horizontal Separator} +\alias{set_default_hsep} +\title{Default horizontal separator} \usage{ default_hsep() + +set_default_hsep(hsep_char) +} +\arguments{ +\item{hsep_char}{character(1). Character that will be set in the R environment +options as default for creating the horizontal separator. It needs to be +single character. Use \code{getOption("formatters_default_hsep")} to get its current +value (\code{NULL} if not set).} } \value{ \code{unicode} 2014 (long dash for generating solid horizontal line) @@ -17,4 +27,7 @@ displayed in the current \code{charset} for use in rendering table-likes. } \examples{ default_hsep() +set_default_hsep("o") +default_hsep() + } diff --git a/man/export_as_txt.Rd b/man/export_as_txt.Rd index fa9b5b7f6..a04d7e07d 100644 --- a/man/export_as_txt.Rd +++ b/man/export_as_txt.Rd @@ -19,7 +19,7 @@ export_as_txt( cpp = NA_integer_, lpp = NA_integer_, ..., - hsep = default_hsep(), + hsep = NULL, indent_size = 2, tf_wrap = paginate, max_width = NULL, @@ -78,7 +78,9 @@ size). \code{NULL} indicates no vertical pagination should occur.} \item{...}{Passed to individual methods.} \item{hsep}{character(1). Characters to repeat to create -header/body separator line.} +header/body separator line. If \code{NULL}, the object value will be +used. If \code{" "}, an empty separator will be printed. Check \code{\link[=default_hsep]{default_hsep()}} +for more information.} \item{indent_size}{numeric(1). Indent size in characters. Ignored when \code{x} is already a \code{MatrixPrintForm} object in favor of information @@ -87,9 +89,9 @@ there.} \item{tf_wrap}{logical(1). Should the texts for title, subtitle, and footnotes be wrapped?} -\item{max_width}{integer(1), character(1) or NULL. Width that title +\item{max_width}{integer(1), character(1) or \code{NULL}. Width that title and footer (including footnotes) materials should be -word-wrapped to. If NULL, it is set to the current print width +word-wrapped to. If \code{NULL}, it is set to the current print width of the session (\code{getOption("width")}). If set to \code{"auto"}, the width of the table (plus any table inset) is used. Ignored completely if \code{tf_wrap} is \code{FALSE}.} diff --git a/man/formatters-package.Rd b/man/formatters-package.Rd new file mode 100644 index 000000000..232ef6237 --- /dev/null +++ b/man/formatters-package.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/package.R +\docType{package} +\name{formatters-package} +\alias{formatters} +\alias{formatters-package} +\title{formatters Package} +\description{ +Package to format tables and listings in a flexible way. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/insightsengineering/formatters} + \item Report bugs at \url{https://github.com/insightsengineering/formatters/issues} +} + +} +\author{ +\strong{Maintainer}: Joe Zhu \email{joe.zhu@roche.com} [contributor] + +Authors: +\itemize{ + \item Gabriel Becker \email{gabembecker@gmail.com} (original creator of the package) + \item Adrian Waddell \email{adrian.waddell@gene.com} +} + +Other contributors: +\itemize{ + \item Davide Garolini \email{davide.garolini@roche.com} [contributor] + \item Emily de la Rua \email{emily.de_la_rua@contractors.roche.com} [contributor] + \item Abinaya Yogasekaram \email{abinaya.yogasekaram@contractors.roche.com} [contributor] + \item F. Hoffmann-La Roche AG [copyright holder, funder] +} + +} diff --git a/man/paginate_indices.Rd b/man/paginate_indices.Rd index 49ce7adb1..59c794aa7 100644 --- a/man/paginate_indices.Rd +++ b/man/paginate_indices.Rd @@ -130,9 +130,9 @@ use with vertical pagination.} \item{tf_wrap}{logical(1). Should the texts for title, subtitle, and footnotes be wrapped?} -\item{max_width}{integer(1), character(1) or NULL. Width that title +\item{max_width}{integer(1), character(1) or \code{NULL}. Width that title and footer (including footnotes) materials should be -word-wrapped to. If NULL, it is set to the current print width +word-wrapped to. If \code{NULL}, it is set to the current print width of the session (\code{getOption("width")}). If set to \code{"auto"}, the width of the table (plus any table inset) is used. Ignored completely if \code{tf_wrap} is \code{FALSE}.} diff --git a/man/tostring.Rd b/man/tostring.Rd index 69883aaf8..01f265c7c 100644 --- a/man/tostring.Rd +++ b/man/tostring.Rd @@ -13,7 +13,7 @@ toString(x, ...) tf_wrap = FALSE, max_width = NULL, col_gap = mf_colgap(x), - hsep = default_hsep() + hsep = NULL ) } \arguments{ @@ -21,7 +21,7 @@ toString(x, ...) \item{...}{Passed to individual methods.} -\item{widths}{numeric (or NULL). (proposed) widths for the columns +\item{widths}{numeric (or \code{NULL}). (proposed) widths for the columns of \code{x}. The expected length of this numeric vector can be retrieved with \code{ncol() + 1} as the column of row names must also be considered.} @@ -29,9 +29,9 @@ also be considered.} \item{tf_wrap}{logical(1). Should the texts for title, subtitle, and footnotes be wrapped?} -\item{max_width}{integer(1), character(1) or NULL. Width that title +\item{max_width}{integer(1), character(1) or \code{NULL}. Width that title and footer (including footnotes) materials should be -word-wrapped to. If NULL, it is set to the current print width +word-wrapped to. If \code{NULL}, it is set to the current print width of the session (\code{getOption("width")}). If set to \code{"auto"}, the width of the table (plus any table inset) is used. Ignored completely if \code{tf_wrap} is \code{FALSE}.} @@ -39,7 +39,9 @@ completely if \code{tf_wrap} is \code{FALSE}.} \item{col_gap}{numeric(1). Space (in characters) between columns} \item{hsep}{character(1). Characters to repeat to create -header/body separator line.} +header/body separator line. If \code{NULL}, the object value will be +used. If \code{" "}, an empty separator will be printed. Check \code{\link[=default_hsep]{default_hsep()}} +for more information.} } \value{ A character string containing the ASCII rendering diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 280ea76df..9ecba45cd 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -17,8 +17,8 @@ make_basemf_fnotes <- function() { "{*} - symbooollllssss" ) - dfmf <- formatters:::mform_build_refdf(dfmf) - mf_rfnotes(dfmf) <- formatters:::reconstruct_basic_fnote_list(dfmf) - formatters:::mf_col_widths(dfmf) <- propose_column_widths(dfmf) + dfmf <- mform_build_refdf(dfmf) + mf_rfnotes(dfmf) <- reconstruct_basic_fnote_list(dfmf) + mf_col_widths(dfmf) <- propose_column_widths(dfmf) dfmf } diff --git a/tests/testthat/test-exporters.R b/tests/testthat/test-exporters.R index 812ac7384..04f47585d 100644 --- a/tests/testthat/test-exporters.R +++ b/tests/testthat/test-exporters.R @@ -18,9 +18,9 @@ test_that("exporters work", { "{*} - symbooollllssss" ) - dfmf <- formatters:::mform_build_refdf(dfmf) - mf_rfnotes(dfmf) <- formatters:::reconstruct_basic_fnote_list(dfmf) - formatters:::mf_col_widths(dfmf) <- propose_column_widths(dfmf) + dfmf <- mform_build_refdf(dfmf) + mf_rfnotes(dfmf) <- reconstruct_basic_fnote_list(dfmf) + mf_col_widths(dfmf) <- propose_column_widths(dfmf) ## covered below ## fil <- tempfile(fileext = ".rtf") @@ -69,7 +69,7 @@ test_that("exporters work", { exp_h_pags ) - expect_true(msg_1_pos > min(msg_asterisk_pos)) + expect_true(msg_1_pos < min(msg_asterisk_pos)) expect_identical( @@ -129,7 +129,7 @@ test_that("exporters work", { mymf_out, paste(expct_lns, collapse = "\n") ) - newmf <- formatters:::mpf_subset_rows(mymf, 1) + newmf <- mpf_subset_rows(mymf, 1) expect_identical( toString(newmf, hsep = "-"), paste(c(expct_lns[1:5], ""), collapse = "\n") @@ -148,3 +148,11 @@ test_that("export_as_txt maintains repeated columns when paginate is TRUE", { expect_identical(length(gregexpr(c("cyl"), pag_out)[[1]]), 2L) expect_identical(length(gregexpr(c("disp"), pag_out)[[1]]), 2L) }) + +test_that("export_as_txt maintains horizontal separator from table", { + dfmf <- basic_matrix_form(mtcars) + horizontal_sep(dfmf) <- "=" + # repeat first 3 columns in each page + out <- strsplit(export_as_txt(dfmf), "\n")[[1]][2] + expect_equal(out, paste0(rep("=", nchar(out)), collapse = "")) +}) diff --git a/tests/testthat/test-formatters.R b/tests/testthat/test-formatters.R index 33fdd5e6d..ff0a3e4a7 100644 --- a/tests/testthat/test-formatters.R +++ b/tests/testthat/test-formatters.R @@ -1,5 +1,10 @@ values <- c(5.123456, 7.891112) -test_that("Default hsep works", { +test_that("Default horizontal separator works", { + expect_true(is.null(getOption("formatters_default_hsep"))) + expect_error(set_default_hsep("foo")) + expect_silent(set_default_hsep("a")) + expect_equal(default_hsep(), "a") + expect_silent(set_default_hsep(NULL)) expect_true(default_hsep() %in% c("\u2014", "-")) }) test_that("make_row_df produces custom error message if used on MatrixPrintForm", { @@ -538,7 +543,7 @@ spans <- matrix( aligns <- matrix(byrow = TRUE, ncol = 2, "center") fmts <- matrix(byrow = TRUE, ncol = 2, "xx") -rinfo <- formatters:::pagdfrow(nm = "row", lab = "row", rnum = 1, pth = "row", extent = 1, rclass = "silly") +rinfo <- pagdfrow(nm = "row", lab = "row", rnum = 1, pth = "row", extent = 1, rclass = "silly") mpf <- MatrixPrintForm( strings = strs, spans = spans, aligns = aligns, @@ -896,3 +901,32 @@ test_that("fmt_config works as expected", { expect_silent(obj_na_str(x) <- "something wrong") expect_silent(obj_align(x) <- "something wrong") }) + +test_that("reorder_ref_fnotes orders referential footnotes correctly", { + # all numeric + rf <- c("{4} - test 1", "{1} - one", "{11} - eleven", "{100} - test 2", "{3} - three", "{7} - !!") + res <- reorder_ref_fnotes(rf) + + expect_identical( + res, + c("{1} - one", "{3} - three", "{4} - test 1", "{7} - !!", "{11} - eleven", "{100} - test 2") + ) + + # numeric and character + rf <- c("{*} - test 1", "{1} - one", "{11} - eleven", "{**} - test 2", "{3} - three", "{!} - !!") + res <- reorder_ref_fnotes(rf) + + expect_identical( + res, + c("{1} - one", "{3} - three", "{11} - eleven", "{!} - !!", "{*} - test 1", "{**} - test 2") + ) + + # with asterisks + rf <- c("{*} - test 1", "{1} - one", "** eleven", "{**} - test 2", "* three", "{!} - !!") + res <- reorder_ref_fnotes(rf) + + expect_identical( + res, + c("{1} - one", "{!} - !!", "{*} - test 1", "{**} - test 2", "* three", "** eleven") + ) +}) diff --git a/tests/testthat/test-pagination.R b/tests/testthat/test-pagination.R index 9d177f609..270b9785a 100644 --- a/tests/testthat/test-pagination.R +++ b/tests/testthat/test-pagination.R @@ -55,7 +55,7 @@ test_that("pagination works", { strs <- mf_strings(dfmf_sillytopleft) strs[1, 1] <- "ha\nha\nha\nha\nha\nha\n" mf_strings(dfmf_sillytopleft) <- strs - expect_silent(formatters:::mform_handle_newlines(dfmf_sillytopleft)) + expect_silent(mform_handle_newlines(dfmf_sillytopleft)) dfmf_cont <- dfmf mf_rinfo(dfmf_cont)$node_class <- "ContentRow" @@ -78,7 +78,7 @@ test_that("pagination works", { dfmf2$strings[1, 2] <- "m\npg" dfmf2$strings[1, 1] <- "tleft mats" dfmf2$has_topleft <- TRUE - dfmf2 <- formatters:::mform_handle_newlines(dfmf2) + dfmf2 <- mform_handle_newlines(dfmf2) expect_identical( dfmf2$strings[1:2, 1:2], matrix(c("", "tleft mats", "m", "pg"), nrow = 2, ncol = 2) @@ -266,9 +266,9 @@ test_that("pagination works", { "do_forced_paginate", "fakeclass", function(obj) { - pt1 <- formatters:::mpf_subset_rows(obj, 1) + pt1 <- mpf_subset_rows(obj, 1) class(pt1) <- setdiff(class(obj), "fakeclass") - pt2 <- formatters:::mpf_subset_rows(obj, 2:32) + pt2 <- mpf_subset_rows(obj, 2:32) class(pt2) <- setdiff(class(obj), "fakeclass") list(pt1, pt2) } @@ -334,8 +334,8 @@ test_that("page to lcpp stuff works", { ) expect_identical( - formatters:::calc_lcpp(), - formatters:::calc_lcpp(page_type = "letter") + calc_lcpp(), + calc_lcpp(page_type = "letter") ) }) @@ -347,8 +347,8 @@ test_that("non-monospaced fonts are caught", { expect_identical( page_lcpp("a4"), page_lcpp( - pg_width = formatters:::pg_dim_names$a4[[1]], - pg_height = formatters:::pg_dim_names$a4[[2]] + pg_width = pg_dim_names$a4[[1]], + pg_height = pg_dim_names$a4[[2]] ) ) }) diff --git a/tests/testthat/test-txt_wrap.R b/tests/testthat/test-txt_wrap.R index 73265950a..b557264a8 100644 --- a/tests/testthat/test-txt_wrap.R +++ b/tests/testthat/test-txt_wrap.R @@ -280,3 +280,18 @@ test_that("toString wrapping avoid trimming whitespaces", { res ) }) + +test_that("toString and wrapping cooperates well with separator divisors", { + # Fixes #221 + testdf <- iris[seq_len(5), seq_len(2)] + rownames(testdf) <- paste("State ", LETTERS[seq_len(nrow(testdf))]) + bmf <- basic_matrix_form(testdf) + + # Adding topleft to wrap + bmf$has_topleft <- TRUE # no setter atm + mf_strings(bmf)[1, 1] <- "LETTERS" + + sec_seps_df <- mf_rinfo(bmf)[, c("abs_rownumber", "trailing_sep"), drop = FALSE] + mf_rinfo(bmf)$trailing_sep[c(1, 3, 4)] <- " " + expect_silent(toString(bmf, widths = c(4, 4, 4))) +})