├── LICENSE ├── .gitignore ├── screenshot.png ├── tests ├── testthat.R └── testthat │ └── test-pieces.R ├── .Rbuildignore ├── Makefile ├── NAMESPACE ├── DESCRIPTION ├── man └── tab.Rd ├── README.Rmd ├── R ├── utils.R └── tab.R └── README.markdown /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2015 2 | COPYRIGHT HOLDER: Gabor Csardi 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | /tags 5 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaborcsardi/tab/HEAD/screenshot.png -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(tab) 3 | 4 | test_check("tab") 5 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^tags$ 2 | ^Makefile$ 3 | ^README.Rmd$ 4 | ^README.markdown$ 5 | ^screenshot.png$ 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: README.markdown 3 | 4 | README.markdown: README.Rmd 5 | Rscript -e "library(knitr); knit('$<', output = '$@', quiet = TRUE)" 6 | 7 | 8 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(tab) 4 | importFrom(ansistrings,ansi_nchar) 5 | importFrom(ansistrings,ansi_strsplit) 6 | importFrom(ansistrings,ansi_substr) 7 | importFrom(ansistrings,ansi_substring) 8 | importFrom(magrittr,"%>%") 9 | importFrom(methods,getFunction) 10 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: tab 2 | Title: Pretty Tables in the Terminal 3 | Version: 1.0.0 4 | Authors@R: person("Gabor", "Csardi", , "csardi.gabor@gmail.com", role = c("aut", "cre")) 5 | Description: Pretty Tables in the Terminal 6 | Pretty print a data frame in the R terminal. Supports word wrapping, 7 | cell truncation, ANSI colors, column names, alignments, padding. 8 | License: MIT + file LICENSE 9 | URL: https://github.com/gaborcsardi/tab 10 | BugReports: https://github.com/gaborcsardi/tab/issues 11 | Imports: 12 | ansistrings, 13 | magrittr, 14 | methods 15 | Suggests: testthat 16 | Remotes: 17 | gaborcsardi/ansistrings 18 | RoxygenNote: 6.0.1 19 | -------------------------------------------------------------------------------- /man/tab.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tab.R 3 | \name{tab} 4 | \alias{tab} 5 | \title{Pretty tables in the R terminal} 6 | \usage{ 7 | tab(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{The object to print. It will be coerced to a data frame.} 11 | 12 | \item{...}{Extra configuration obtions, see them below.} 13 | } 14 | \value{ 15 | The lines of the formatted table, invisibly. 16 | } 17 | \description{ 18 | Pretty tables in the R terminal 19 | } 20 | \section{Features}{ 21 | 22 | \itemize{ 23 | \item ANSI colored text via the crayon package. 24 | \item Cell wrapping. 25 | \item Minimum and maximum cell width. 26 | \item Custom column spliter string. 27 | \item Custom padding string. 28 | \item Preservation of newlines in cells. 29 | \item Headers. 30 | \item Transformation functions for headers and data. By 31 | defaults headers are in all caps, and data is formatted 32 | with \code{base::format}. 33 | \item Maximum table width (truncated here), defaults to 34 | terminal width. 35 | \item Align left, right or center. 36 | } 37 | 38 | It prints the table to the console via \code{base::cat}, and 39 | also returns the lines of the table invisibly. 40 | } 41 | 42 | \examples{ 43 | x <- data.frame( 44 | stringsAsFactors = FALSE, 45 | pkg = c("igraph", "crayon", "tab"), 46 | version = c("0.7.1", "1.1.0", "1.0.0"), 47 | description = c( 48 | paste("Routines for simple graphs and network analysis.", 49 | "igraph can handle large graphs very well and provides", 50 | "functions for generating random and regular graphs,", 51 | "graph visualization, centrality indices and much more."), 52 | paste("Crayon adds support for colored terminal output on", 53 | "terminals that support ANSI color and highlight codes.", 54 | "ANSI color support is automatically detected.", 55 | "Colors and highlighting can be combined and nested. New styles", 56 | "can also be created easily. This package was inspired by", 57 | "the chalk JavaScript project"), 58 | paste("Pretty Tables in the terminal. Pretty print a data frame", 59 | "in the R terminal. Supports word wrapping,\\ncell truncation,", 60 | "ANSI colors, column names, alignments, padding") 61 | ) 62 | ) 63 | tab(x, max_width = 40) 64 | tab(x, min_width = 10) 65 | tab(x, max_width = 50, column_splitter = " | ") 66 | 67 | x$pkg <- crayon::red(x$pkg) 68 | tab(x, max_width = 50) 69 | tab(x, align = "right", max_width = 50, column_splitter = " | ") 70 | tab(x, max_width = 50, padding_chr = ".") 71 | 72 | pkgs <- installed.packages()[,c("Package", "Version", "Depends", 73 | "Imports", "Suggests", "LibPath")][1:30,] 74 | pkgs <- as.data.frame(pkgs, stringsAsFactors = FALSE) 75 | pkgs$Package <- crayon::green(pkgs$Package) 76 | tab(pkgs, heading_transform = crayon::red) 77 | } 78 | -------------------------------------------------------------------------------- /tests/testthat/test-pieces.R: -------------------------------------------------------------------------------- 1 | 2 | context("Internal utilities") 3 | 4 | test_that("repeat_string", { 5 | 6 | cases <- list( 7 | list("rep", 10, "repreprepr"), 8 | list("rep", 1 , "r"), 9 | list("rep", 3 , "rep"), 10 | list("rep", 0 , ""), 11 | list("rep", c(1,2), c("r", "re")), 12 | list("rep", c(0,5), c("", "repre")) 13 | ) 14 | 15 | for (c in cases) expect_equal(repeat_string(c[[1]], c[[2]]), c[[3]]) 16 | 17 | }) 18 | 19 | test_that("pad_right", { 20 | 21 | cases <- list( 22 | list("foo", 5, " ", "foo "), 23 | list("foo", 3, " ", "foo"), 24 | list("", 3, " ", " "), 25 | list("", 0, " ", ""), 26 | list("foobar", 10, "-+", "foobar-+-+"), 27 | list("foobar", 9, "-+", "foobar-+-"), 28 | list(c("foo", "foobar"), 10, " ", c("foo ", "foobar ")), 29 | list(c("foo", "foobar"), 6, " ", c("foo ", "foobar")) 30 | ) 31 | 32 | for (c in cases) expect_equal(pad_right(c[[1]], c[[2]], c[[3]]), c[[4]]) 33 | 34 | }) 35 | 36 | test_that("pad_left", { 37 | 38 | cases <- list( 39 | list("foo", 5, " ", " foo"), 40 | list("foo", 3, " ", "foo"), 41 | list("", 3, " ", " "), 42 | list("", 0, " ", ""), 43 | list("foobar", 10, "-+", "-+-+foobar"), 44 | list("foobar", 9, "-+", "-+-foobar"), 45 | list(c("foo", "foobar"), 10, " ", c(" foo", " foobar")), 46 | list(c("foo", "foobar"), 6, " ", c(" foo", "foobar")) 47 | ) 48 | 49 | for (c in cases) expect_equal(pad_left(c[[1]], c[[2]], c[[3]]), c[[4]]) 50 | 51 | }) 52 | 53 | test_that("pad_center", { 54 | 55 | cases <- list( 56 | list("foo", 5, " ", " foo "), 57 | list("foo", 3, " ", "foo"), 58 | list("", 3, " ", " "), 59 | list("", 0, " ", ""), 60 | list("foobar", 10, "-+", "-+foobar-+"), 61 | list("foobar", 9, "-+", "-foobar-+"), 62 | list(c("foo", "foobar"), 10, " ", c(" foo ", " foobar ")), 63 | list(c("foo", "foobar"), 6, " ", c(" foo ", "foobar")) 64 | ) 65 | 66 | for (c in cases) expect_equal(pad_center(c[[1]], c[[2]], c[[3]]), c[[4]]) 67 | 68 | }) 69 | 70 | test_that("split_long_words", { 71 | 72 | cases <- list( 73 | list("thisistoolongforone", 6, "-", "thisi- stool- ongfo- rone"), 74 | list("a b ab thisislong no not", 6, "-", "a b ab thisi- slong no not"), 75 | list(c("onelong", "twolong"), 5, "-", c("onel- ong", "twol- ong")), 76 | list(c("a b longi c d", "xx y longo rt rt"), 5, "-", 77 | c("a b longi c d", "xx y longo rt rt")) 78 | ) 79 | 80 | for (c in cases) expect_equal(split_long_words(c[[1]], c[[2]], c[[3]]), c[[4]]) 81 | 82 | }) 83 | 84 | test_that("split_into_lines", { 85 | 86 | cases <- list( 87 | list("lorem ipsum foo foobar bar bar", 10, 88 | list(c("lorem", "ipsum foo", "foobar bar", "bar"))), 89 | list(c("foo bar foobar", "foobar", "foo bar"), 7, 90 | list(c("foo bar", "foobar"), "foobar", "foo bar")) 91 | ) 92 | 93 | for (c in cases) expect_equal(split_into_lines(c[[1]], c[[2]]), c[[3]]) 94 | 95 | }) 96 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | 2 | ```{r, setup, echo = FALSE, message = FALSE} 3 | knitr::opts_chunk$set( 4 | comment = "#>", 5 | tidy = FALSE, 6 | error = FALSE, 7 | fig.path = "", 8 | fig.width = 8, 9 | fig.height = 8) 10 | ``` 11 | 12 | # tab 13 | 14 | > Pretty tables at the R CLI 15 | 16 | For better user interface. Inspired by the 17 | [columnify](https://github.com/timoxley/columnify) project. 18 | 19 | ![Screenshot](screenshot.png) 20 | 21 | ## Install 22 | 23 | Install from Github with `devtools`. You'll also need a recent `crayon` package: 24 | 25 | ```{r, eval = FALSE} 26 | install.packages("devtools") 27 | devtools::install_github("gaborcsardi/crayon") 28 | devtools::install_github("gaborcsardi/tab") 29 | ``` 30 | 31 | ## Usage 32 | 33 | Call `tab` with your data frame. Here are some examples. 34 | 35 | ```{r} 36 | library(tab) 37 | data <- data.frame(pkg = c("igraph", "crayon", "tab"), 38 | version = c("0.7.1", "1.1.0", "1.0.0")) 39 | tab(data) 40 | ``` 41 | 42 | ### Maximum and minimum column widths 43 | 44 | ```{r} 45 | x <- data.frame( 46 | stringsAsFactors = FALSE, 47 | pkg = c("igraph", "crayon", "tab"), 48 | version = c("0.7.1", "1.1.0", "1.0.0"), 49 | description = c( 50 | paste("Routines for simple graphs and network analysis.", 51 | "igraph can handle large graphs very well and provides", 52 | "functions for generating random and regular graphs,", 53 | "graph visualization, centrality indices and much more."), 54 | paste("Crayon adds support for colored terminal output on", 55 | "terminals that support ANSI color and highlight codes.", 56 | "ANSI color support is automatically detected.", 57 | "Colors and highlighting can be combined and nested. New styles", 58 | "can also be created easily. This package was inspired by", 59 | "the chalk JavaScript project"), 60 | paste("Pretty Tables in the terminal. Pretty print a data frame", 61 | "in the R terminal. Supports word wrapping,\ncell truncation,", 62 | "ANSI colors, column names, alignments, padding") 63 | ) 64 | ) 65 | tab(x) 66 | tab(x, min_width = 10, max_width = 50) 67 | ``` 68 | 69 | ### Maximum table width 70 | 71 | ```{r} 72 | tab(x, table_width = 50) 73 | ``` 74 | 75 | ### Align right or center 76 | 77 | ```{r} 78 | tab(data, align = "right") 79 | tab(data, align = "center") 80 | ``` 81 | 82 | ### Padding character 83 | 84 | ```{r} 85 | tab(data, padding_chr = ".") 86 | ``` 87 | 88 | ### Preserve existing newlines 89 | 90 | ```{r} 91 | x2 <- data.frame(V1 = c("This is a line with \n newlines \nin it", 92 | "And another line"), 93 | V2 = c("and this is just a long line", 94 | "and a short one") 95 | ) 96 | tab(x2, preserve_newlines = TRUE, max_width = 20) 97 | ``` 98 | 99 | ### Custom column splitter 100 | 101 | 102 | ```{r} 103 | tab(data, column_splitter = " | ") 104 | ``` 105 | 106 | ## License 107 | 108 | MIT © Gabor Csardi 109 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | 2 | `%+%` <- function(l, r) { 3 | paste0(as.character(l), as.character(r)) 4 | } 5 | 6 | #' @importFrom ansistrings ansi_substring 7 | 8 | repeat_string <- function(str, len) { 9 | stopifnot(length(str) == 1) 10 | character(max(len) + 1) %>% 11 | paste(collapse = str) %>% 12 | ansi_substring(1, len) 13 | } 14 | 15 | #' @importFrom ansistrings ansi_nchar 16 | 17 | pad_right <- function(str, max, chr = " ") { 18 | str <- as.character(str) 19 | length <- max - ansi_nchar(str) 20 | ifelse(length <= 0, str, str %+% repeat_string(chr, length)) 21 | } 22 | 23 | #' @importFrom ansistrings ansi_nchar 24 | 25 | pad_center <- function(str, max, chr = " ") { 26 | str <- as.character(str) 27 | length <- max - ansi_nchar(str) 28 | ifelse(length <= 0, str, repeat_string(chr, floor(length / 2)) %+% str %+% 29 | repeat_string(chr, length - floor(length / 2))) 30 | } 31 | 32 | #' @importFrom ansistrings ansi_nchar 33 | 34 | pad_left <- function(str, max, chr = " ") { 35 | str <- as.character(str) 36 | length <- max - ansi_nchar(str) 37 | ifelse(length <= 0, str, repeat_string(chr, length) %+% str) 38 | } 39 | 40 | pad <- function(str, max, chr = " ", align = c("left", "center", "right")) { 41 | align <- match.arg(align) 42 | if (align == "left") { 43 | pad_right(str, max, chr) 44 | } else if (align == "center") { 45 | pad_center(str, max, chr) 46 | } else if (align == "right") { 47 | pad_left(str, max, chr) 48 | } 49 | } 50 | 51 | trim <- function (x) gsub("^\\s+|\\s+$", "", x) 52 | 53 | #' @importFrom ansistrings ansi_nchar 54 | 55 | split_into_lines_1 <- function(str, max) { 56 | split_into_lines_line <- function(str) { 57 | words <- str %>% 58 | trim() %>% 59 | strsplit(" ") %>% 60 | `[[`(1) 61 | lines <- list() 62 | line <- words[1] 63 | for (w in words[-1]) { 64 | if (ansi_nchar(paste(line, collapse = " ")) + ansi_nchar(w) < max) { 65 | line <- c(line, w) 66 | } else { 67 | lines <- c(lines, list(line)) 68 | line <- w 69 | } 70 | } 71 | if (length(line)) lines <- c(lines, list(line)) 72 | vapply(lines, paste, "", collapse = " ", USE.NAMES = FALSE) 73 | } 74 | str %>% 75 | strsplit("\n") %>% 76 | `[[`(1) %>% 77 | lapply(split_into_lines_line) %>% 78 | unlist() 79 | } 80 | 81 | split_into_lines <- function(str, max) { 82 | lapply(str, split_into_lines_1, max = max) 83 | } 84 | 85 | #' @importFrom ansistrings ansi_strsplit ansi_substring 86 | 87 | split_long_words <- function(str, max, truncation_chr = "-") { 88 | 89 | break_word <- function(word) { 90 | nc <- ansi_nchar(word) 91 | if (nc <= max) return(word) 92 | break_points <- seq(1, nc, by = max - 1) 93 | res <- ansi_substring(word, break_points, break_points + max - 1 - 1) 94 | if (length(res) == 1) { 95 | res 96 | } else { 97 | res[1:(length(res)-1)] <- paste(res[1:(length(res)-1)], 98 | truncation_chr, sep = "") 99 | res 100 | } 101 | } 102 | 103 | break_words <- function(words) { 104 | lapply(words, break_word) %>% 105 | unlist() 106 | } 107 | 108 | str %>% 109 | trim() %>% 110 | ansi_strsplit(split = " ") %>% 111 | lapply(break_words) %>% 112 | vapply(paste, collapse = " ", "") 113 | } 114 | 115 | #' @importFrom ansistrings ansi_substr 116 | 117 | truncate_string <- function(str, max) { 118 | ansi_substr(str, 1, max) 119 | } 120 | 121 | do_call <- function (f, ..., .args = list(), .env = parent.frame()) { 122 | f <- substitute(f) 123 | call <- make_call(f, ..., .args) 124 | eval(call, .env) 125 | } 126 | 127 | make_call <- function (f, ..., .args = list()) { 128 | if (is.character(f)) 129 | f <- as.name(f) 130 | as.call(c(f, ..., .args)) 131 | } 132 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # tab 5 | 6 | > Pretty tables at the R CLI 7 | 8 | For better user interface. Inspired by the 9 | [columnify](https://github.com/timoxley/columnify) project. 10 | 11 | ![Screenshot](screenshot.png) 12 | 13 | ## Install 14 | 15 | Install from Github with `devtools`. You'll also need a recent `crayon` package: 16 | 17 | 18 | ```r 19 | install.packages("devtools") 20 | devtools::install_github("gaborcsardi/crayon") 21 | devtools::install_github("gaborcsardi/tab") 22 | ``` 23 | 24 | ## Usage 25 | 26 | Call `tab` with your data frame. Here are some examples. 27 | 28 | 29 | ```r 30 | library(tab) 31 | data <- data.frame(pkg = c("igraph", "crayon", "tab"), 32 | version = c("0.7.1", "1.1.0", "1.0.0")) 33 | tab(data) 34 | ``` 35 | 36 | ``` 37 | #> PKG VERSION 38 | #> igraph 0.7.1 39 | #> crayon 1.1.0 40 | #> tab 1.0.0 41 | ``` 42 | 43 | ### Maximum and minimum column widths 44 | 45 | 46 | ```r 47 | x <- data.frame( 48 | stringsAsFactors = FALSE, 49 | pkg = c("igraph", "crayon", "tab"), 50 | version = c("0.7.1", "1.1.0", "1.0.0"), 51 | description = c( 52 | paste("Routines for simple graphs and network analysis.", 53 | "igraph can handle large graphs very well and provides", 54 | "functions for generating random and regular graphs,", 55 | "graph visualization, centrality indices and much more."), 56 | paste("Crayon adds support for colored terminal output on", 57 | "terminals that support ANSI color and highlight codes.", 58 | "ANSI color support is automatically detected.", 59 | "Colors and highlighting can be combined and nested. New styles", 60 | "can also be created easily. This package was inspired by", 61 | "the chalk JavaScript project"), 62 | paste("Pretty Tables in the terminal. Pretty print a data frame", 63 | "in the R terminal. Supports word wrapping,\ncell truncation,", 64 | "ANSI colors, column names, alignments, padding") 65 | ) 66 | ) 67 | tab(x) 68 | ``` 69 | 70 | ``` 71 | #> PKG VERSION DESCRIPTION 72 | #> igraph 0.7.1 Routines for simple graphs and network analysis. igraph can 73 | #> crayon 1.1.0 Crayon adds support for colored terminal output on terminals 74 | #> tab 1.0.0 Pretty Tables in the terminal. Pretty print a data frame in 75 | ``` 76 | 77 | ```r 78 | tab(x, min_width = 10, max_width = 50) 79 | ``` 80 | 81 | ``` 82 | #> PKG VERSION DESCRIPTION 83 | #> igraph 0.7.1 Routines for simple graphs and network analysis. 84 | #> igraph can handle large graphs very well and 85 | #> provides functions for generating random and 86 | #> regular graphs, graph visualization, centrality 87 | #> indices and much more. 88 | #> crayon 1.1.0 Crayon adds support for colored terminal output on 89 | #> terminals that support ANSI color and highlight 90 | #> codes. ANSI color support is automatically 91 | #> detected. Colors and highlighting can be combined 92 | #> and nested. New styles can also be created easily. 93 | #> This package was inspired by the chalk JavaScript 94 | #> project 95 | #> tab 1.0.0 Pretty Tables in the terminal. Pretty print a data 96 | #> frame in the R terminal. Supports word wrapping, 97 | #> cell truncation, ANSI colors, column names, 98 | #> alignments, padding 99 | ``` 100 | 101 | ### Maximum table width 102 | 103 | 104 | ```r 105 | tab(x, table_width = 50) 106 | ``` 107 | 108 | ``` 109 | #> PKG VERSION DESCRIPTION 110 | #> igraph 0.7.1 Routines for simple graphs and netw 111 | #> crayon 1.1.0 Crayon adds support for colored ter 112 | #> tab 1.0.0 Pretty Tables in the terminal. Pret 113 | ``` 114 | 115 | ### Align right or center 116 | 117 | 118 | ```r 119 | tab(data, align = "right") 120 | ``` 121 | 122 | ``` 123 | #> PKG VERSION 124 | #> igraph 0.7.1 125 | #> crayon 1.1.0 126 | #> tab 1.0.0 127 | ``` 128 | 129 | ```r 130 | tab(data, align = "center") 131 | ``` 132 | 133 | ``` 134 | #> PKG VERSION 135 | #> igraph 0.7.1 136 | #> crayon 1.1.0 137 | #> tab 1.0.0 138 | ``` 139 | 140 | ### Padding character 141 | 142 | 143 | ```r 144 | tab(data, padding_chr = ".") 145 | ``` 146 | 147 | ``` 148 | #> PKG... VERSION 149 | #> igraph 0.7.1.. 150 | #> crayon 1.1.0.. 151 | #> tab... 1.0.0.. 152 | ``` 153 | 154 | ### Preserve existing newlines 155 | 156 | 157 | ```r 158 | x2 <- data.frame(V1 = c("This is a line with \n newlines \nin it", 159 | "And another line"), 160 | V2 = c("and this is just a long line", 161 | "and a short one") 162 | ) 163 | tab(x2, preserve_newlines = TRUE, max_width = 20) 164 | ``` 165 | 166 | ``` 167 | #> V1 V2 168 | #> This is a line with and this is just a 169 | #> newlines long line 170 | #> in it 171 | #> And another line and a short one 172 | ``` 173 | 174 | ### Custom column splitter 175 | 176 | 177 | 178 | ```r 179 | tab(data, column_splitter = " | ") 180 | ``` 181 | 182 | ``` 183 | #> PKG | VERSION 184 | #> igraph | 0.7.1 185 | #> crayon | 1.1.0 186 | #> tab | 1.0.0 187 | ``` 188 | 189 | ## License 190 | 191 | MIT © Gabor Csardi 192 | -------------------------------------------------------------------------------- /R/tab.R: -------------------------------------------------------------------------------- 1 | 2 | tab_options <- list( 3 | max_width = Inf, 4 | min_width = 0, 5 | column_splitter = " ", 6 | truncate = FALSE, 7 | truncate_marker = "...", 8 | preserve_newlines = FALSE, 9 | padding_chr = " ", 10 | show_headers = TRUE, 11 | heading_transform = "toupper", 12 | data_transform = "format", 13 | table_width = "auto", 14 | align = "left" 15 | ) 16 | 17 | #' @importFrom magrittr %>% 18 | NULL 19 | 20 | #' Pretty tables in the R terminal 21 | #' 22 | #' @section Features: 23 | #' \itemize{ 24 | #' \item ANSI colored text via the crayon package. 25 | #' \item Cell wrapping. 26 | #' \item Minimum and maximum cell width. 27 | #' \item Custom column spliter string. 28 | #' \item Custom padding string. 29 | #' \item Preservation of newlines in cells. 30 | #' \item Headers. 31 | #' \item Transformation functions for headers and data. By 32 | #' defaults headers are in all caps, and data is formatted 33 | #' with \code{base::format}. 34 | #' \item Maximum table width (truncated here), defaults to 35 | #' terminal width. 36 | #' \item Align left, right or center. 37 | #' } 38 | #' 39 | #' It prints the table to the console via \code{base::cat}, and 40 | #' also returns the lines of the table invisibly. 41 | #' 42 | #' @param x The object to print. It will be coerced to a data frame. 43 | #' @param ... Extra configuration obtions, see them below. 44 | #' @return The lines of the formatted table, invisibly. 45 | #' 46 | #' @export 47 | #' @importFrom methods getFunction 48 | #' @examples 49 | #' x <- data.frame( 50 | #' stringsAsFactors = FALSE, 51 | #' pkg = c("igraph", "crayon", "tab"), 52 | #' version = c("0.7.1", "1.1.0", "1.0.0"), 53 | #' description = c( 54 | #' paste("Routines for simple graphs and network analysis.", 55 | #' "igraph can handle large graphs very well and provides", 56 | #' "functions for generating random and regular graphs,", 57 | #' "graph visualization, centrality indices and much more."), 58 | #' paste("Crayon adds support for colored terminal output on", 59 | #' "terminals that support ANSI color and highlight codes.", 60 | #' "ANSI color support is automatically detected.", 61 | #' "Colors and highlighting can be combined and nested. New styles", 62 | #' "can also be created easily. This package was inspired by", 63 | #' "the chalk JavaScript project"), 64 | #' paste("Pretty Tables in the terminal. Pretty print a data frame", 65 | #' "in the R terminal. Supports word wrapping,\ncell truncation,", 66 | #' "ANSI colors, column names, alignments, padding") 67 | #' ) 68 | #' ) 69 | #' tab(x, max_width = 40) 70 | #' tab(x, min_width = 10) 71 | #' tab(x, max_width = 50, column_splitter = " | ") 72 | #' 73 | #' x$pkg <- crayon::red(x$pkg) 74 | #' tab(x, max_width = 50) 75 | #' tab(x, align = "right", max_width = 50, column_splitter = " | ") 76 | #' tab(x, max_width = 50, padding_chr = ".") 77 | #' 78 | #' pkgs <- installed.packages()[,c("Package", "Version", "Depends", 79 | #' "Imports", "Suggests", "LibPath")][1:30,] 80 | #' pkgs <- as.data.frame(pkgs, stringsAsFactors = FALSE) 81 | #' pkgs$Package <- crayon::green(pkgs$Package) 82 | #' tab(pkgs, heading_transform = crayon::red) 83 | 84 | tab <- function(x, ...) { 85 | 86 | x <- as.data.frame(x, stringsAsFactors = FALSE) %>% 87 | as.list() 88 | 89 | opts <- list(...) 90 | if (length(opts) && ( 91 | is.null(names(opts)) || any(names(opts) == ""))) { 92 | stop("Unnamed arguments are not allowed") 93 | } 94 | 95 | unknown_opts <- setdiff(names(opts), names(tab_options)) 96 | if (length(unknown_opts)) { 97 | stop("Unknown option", if (length(unknown_opts) - 1) "s" else "", 98 | paste(unknown_opts, collapse = ", ")) 99 | } 100 | 101 | opts <- modifyList(tab_options, opts) 102 | 103 | if (opts$table_width == "auto") opts$table_width <- getOption("width") 104 | 105 | ## transform data cells 106 | if (is.character(opts$data_transform)) { 107 | opts$data_transform <- getFunction(opts$data_transform) 108 | } 109 | x <- lapply(x, opts$data_transform) 110 | 111 | ## sanitize data, remove newlines if requested 112 | x <- if (opts$preserve_newlines) { 113 | lapply(x, gsub, pattern = "\\h+", replacement = " ", perl = TRUE) 114 | } else { 115 | lapply(x, gsub, pattern = "\\s+", replacement = " ") 116 | } 117 | 118 | ## Add headers 119 | if (opts$show_headers) { 120 | if (is.character(opts$heading_transform)) { 121 | opts$heading_transform <- getFunction(opts$heading_transform) 122 | } 123 | headers <- names(x) %>% 124 | opts$heading_transform() 125 | x <- mapply(headers, x, FUN = c, SIMPLIFY = FALSE) 126 | } 127 | 128 | ## get actual max-width between min & max 129 | ## based on length of data in columns 130 | width <- vapply(x, FUN.VALUE = 1, function(cur) { 131 | ansi_nchar(cur) %>% 132 | max(opts$min_width) %>% 133 | min(opts$max_width) 134 | }) 135 | 136 | ## split long words so they can break onto multiple lines 137 | x <- mapply(x, width, opts$truncate_marker, FUN = split_long_words, 138 | SIMPLIFY = FALSE) 139 | 140 | ## wrap long lines. each item is now an array of lines. 141 | x <- mapply(x, width, FUN = split_into_lines, SIMPLIFY = FALSE) 142 | 143 | ## if truncating required, only include first line + add truncation char 144 | ## TODO 145 | 146 | ## recalculate column widths from truncated output/lines 147 | width <- vapply(x, FUN.VALUE = 1, function(cur) { 148 | ansi_nchar(cur) %>% 149 | max(opts$min_width) %>% 150 | min(opts$max_width) 151 | }) 152 | 153 | ## merge lines into rows 154 | rows <- create_rows(x, width, opts) 155 | 156 | cat(rows, sep = "\n") 157 | 158 | invisible(rows) 159 | } 160 | 161 | create_rows <- function(x, width, opts) { 162 | 163 | num_lines <- vapply(x, function(xx) vapply(xx, length, 1L), 164 | integer(length(x[[1]]))) %>% 165 | apply(1, max) 166 | 167 | ## combine matching lines of each rows 168 | lapply(seq_along(num_lines), FUN = function(i) { 169 | max_lines <- num_lines[i] 170 | lapply(seq_len(length(x)), FUN = function(c) { 171 | c(x[[c]][[i]], rep("", max_lines - length(x[[c]][[i]]))) %>% 172 | pad(width[c], opts$padding_chr, opts$align) 173 | }) %>% 174 | do_call(f = paste, .args = list(sep = opts$column_splitter)) 175 | }) %>% 176 | unlist() %>% 177 | truncate_string(opts$table_width) 178 | } 179 | --------------------------------------------------------------------------------