├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── R ├── boxview.R ├── boxview_rec.R ├── helpers.R ├── print.R └── swap_calls.R ├── README.Rmd ├── README.md ├── boxview.Rproj ├── inst ├── ave.png ├── interaction1.png └── interaction2.png ├── man ├── boxview.Rd └── figures │ └── logo.png └── tests ├── testthat.R └── testthat ├── _snaps └── boxview.md └── test-boxview.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^boxview\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^LICENSE\.md$ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | .quarto 7 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: boxview 2 | Title: Display the Code of a Function in Nested Boxes 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person("Antoine", "Fabri", , "antoine.fabri@gmail.com", role = c("aut", "cre")) 6 | Description: Functions containing a lot of control flow constructs can be hard 7 | to decipher. By displaying them into boxes and presenting the 'yes' and 'no' 8 | clauses of 'if' calls side by side we make their logic easier to grasp. 9 | License: MIT + file LICENSE 10 | Encoding: UTF-8 11 | Roxygen: list(markdown = TRUE) 12 | RoxygenNote: 7.3.2.9000 13 | Imports: 14 | cli, 15 | rlang, 16 | styler 17 | Suggests: 18 | testthat (>= 3.0.0) 19 | Config/testthat/edition: 3 20 | URL: https://github.com/cynkra/boxview 21 | BugReports: https://github.com/cynkra/boxview/issues 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2025 2 | COPYRIGHT HOLDER: boxview authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2025 boxview authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,boxview) 4 | export(boxview) 5 | -------------------------------------------------------------------------------- /R/boxview.R: -------------------------------------------------------------------------------- 1 | #' Display code logic in nested boxes 2 | #' 3 | #' `boxview()` displays the code of a function in nested boxes, showing the 4 | #' 'yes' and 'no' clauses of `if` calls side by side. It often makes code easier to 5 | #' skim than the traditional way. 6 | #' 7 | #' @param fun A function 8 | #' @param width The desired width, the output will be wider if the we can't 9 | #' make it narrow enough by wrapping code, and will be narrower if we don't 10 | #' need the width when not wrapping at all 11 | #' @param optimization The level of space optimization, to wrap calls only 12 | #' when needed. `boxview()` is quite slow, and strong optimization can take 13 | #' a long time for big functions. You might not need higher levels most of 14 | #' the time, especially with sufficient width where the difference is often 15 | #' barely noticeable. 16 | #' 17 | #' @return a 'boxview' object that prints the boxed code to the console. 18 | #' @export 19 | #' 20 | #' @examples 21 | #' \dontrun{ 22 | #' boxview(ave) 23 | #' boxview(ave, width = 10) 24 | #' boxview(ave, width = 10, optimization = "medium") 25 | #' boxview(interaction) 26 | #' boxview(data.frame) 27 | #' } 28 | boxview <- function(fun, width = 200, optimization = c("weak", "medium", "strong")) { 29 | optimization <- rlang::arg_match(optimization) 30 | header_chr <- cli::code_highlight(deparse(args(fun))) 31 | header_chr <- header_chr[-length(header_chr)] 32 | code <- boxview_rec(swap_calls(body(fun)), width = width, optimization = optimization) 33 | code <- c(header_chr, code) 34 | structure(code, class = c("boxview", "character")) 35 | } 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /R/boxview_rec.R: -------------------------------------------------------------------------------- 1 | 2 | boxview_rec <- function(call, col = identity, width = 200, optimization = "weak") { 3 | 4 | # A box of 40 # width 5 | # ┌──────────────────────────────────────┐ 6 | # | 36 for a regular call | # width - 4 7 | # | ┌───────────────┐ ┌───────────────┐ | 8 | # | | 17 outside | | 13 inside | | # floor((width - 5) / 2) 9 | # | └───────────────┘ └───────────────┘ | # floor((width - 5) / 2) - 4 10 | # | ┌──────────────────────────────────┐ | 11 | # | | 36 outside, 32 inside | | # width - 4 12 | # | └──────────────────────────────────┘ | # width - 8 13 | # └──────────────────────────────────────┘ 14 | # 15 | # * When a call doesn't fit the provided width, its box is extended 16 | # * As a consequence the parent box is extended too and we take advantage of 17 | # the new space there 18 | # * To take advantage of the extra space in this current box, we need 19 | # to rerun with the new width specs, and since it's recursive, that's a 20 | # lot of work and is very slow (though might be optimizationd with clever tricks). 21 | # So we make it optional. 22 | 23 | # `width` is the width of the box 24 | # The call content `with - 4` : regular calls, or nested boxes 25 | # if/else boxes are (width- 4) / 2, or width/2 - 2, rounded 26 | 27 | # if we don't run twice, some calls will stretch the boxes and calls above won't benefit from it 28 | # but it'll be much faster 29 | 30 | width_content <- width - 4 31 | 32 | if (rlang::is_call(call, "{")) { 33 | width_content <- optimal_content_width(call, width_content, optimization) 34 | code <- unlist(lapply(call[-1], boxview_rec, width = width_content, optimization = optimization)) 35 | box <- code_to_box(code, col) 36 | return(box) 37 | } 38 | 39 | 40 | # if ------------------------------------------------------------------------- 41 | if (rlang::is_call(call, "if")) { 42 | if_has_no <- length(call) == 4 43 | 44 | yes <- call[[3]] 45 | # wrap yes in {} 46 | if (!rlang::is_call(yes, "{")) yes <- bquote({.(yes)}) 47 | 48 | if (if_has_no) { 49 | width_nested_box <- floor((width_content - 1) / 2) 50 | 51 | no <- call[[4]] 52 | # wrap no in {} 53 | if (!rlang::is_call(no, "{")) no <- bquote({.(no)}) 54 | 55 | # boxes 56 | yes <- boxview_rec(yes, cli::col_green, width = width_nested_box, optimization = optimization) 57 | no <- boxview_rec(no, cli::col_red, width = width_nested_box, optimization = optimization) 58 | 59 | # add empty space at the bottom of the shortest box 60 | if (length(yes) > length(no)) { 61 | length(no) <- length(yes) 62 | } else { 63 | length(yes) <- length(no) 64 | } 65 | yes[is.na(yes)] <- strrep(" ", cli::ansi_nchar(yes[[1]])) 66 | no[is.na(no)] <- strrep(" ", cli::ansi_nchar(no[[1]])) 67 | box <- paste(yes, no) 68 | } else { 69 | yes <- boxview_rec(yes, cli::col_green, width = width_content, optimization = optimization) 70 | yes[is.na(yes)] <- strrep(" ", cli::ansi_nchar(yes[[1]])) 71 | box <- yes 72 | } 73 | 74 | header_chr <- header_if(call, width_content) 75 | box <- c(header_chr, box) 76 | return(box) 77 | } 78 | 79 | # for ------------------------------------------------------------------------ 80 | if (rlang::is_call(call, "for")) { 81 | body <- call[[4]] 82 | if (!rlang::is_call(body, "{")) body <- bquote({.(body)}) 83 | box <- boxview_rec(body, cli::col_blue, width = width_content, optimization = optimization) 84 | header_chr <- header_for(call, width_content) 85 | box <- c(header_chr, box) 86 | return(box) 87 | } 88 | 89 | # while ------------------------------------------------------------------------ 90 | if (rlang::is_call(call, "while")) { 91 | body <- call[[3]] 92 | if (!rlang::is_call(body, "{")) body <- bquote({.(body)}) 93 | box <- boxview_rec(body, cli::col_blue, width = width_content, optimization = optimization) 94 | header_chr <- header_while(call, width_content) 95 | box <- c(header_chr, box) 96 | return(box) 97 | } 98 | 99 | # repeat ------------------------------------------------------------------------ 100 | if (rlang::is_call(call, "repeat")) { 101 | body <- call[[2]] 102 | if (!rlang::is_call(body, "{")) body <- bquote({.(body)}) 103 | box <- boxview_rec(body, cli::col_blue, width = width_content, optimization = optimization) 104 | header_chr <- header_repeat() 105 | box <- c(header_chr, box) 106 | return(box) 107 | } 108 | 109 | if (is_function_definition(call)) { 110 | header_chr <- header_fun(call, width_content) 111 | body <- call[[3]][[3]] 112 | box <- boxview_rec(body, width = width_content, optimization = optimization) 113 | box <- c(header_chr, box) 114 | return(box) 115 | } 116 | 117 | # regular calls or symbols 118 | repeat { 119 | code <- try(cli::code_highlight(styler::style_text(rlang::expr_deparse(call, width = width_content))), silent = TRUE) 120 | stripped <- cli::ansi_strip(code) 121 | if (!inherits(code, "try-error")) { 122 | # FIXME: we should just color the function 123 | if (any(startsWith(stripped[[1]], c("stop(", "abort(", "rlang::abort(", "stopifnot(")))) { 124 | fun <- c("stop", "abort", "rlang::abort", "stopifnot")[startsWith(stripped[[1]], c("stop(", "abort(", "rlang::abort(", "stopifnot("))] 125 | code[[1]] <- paste0(cli::bg_red(cli::col_black(fun)), cli::ansi_substring(code[[1]], cli::ansi_nchar(fun) + 1)) 126 | } else if (any(startsWith(stripped[[1]], "return("))) { 127 | code[[1]] <- paste0(cli::bg_green(cli::col_black("return")), cli::ansi_substring(code[[1]], 7)) 128 | } else if (any(startsWith(stripped[[1]], c("warning(", "warn(", "rlang::warn(")))) { 129 | fun <- c("warning", "warn", "rlang::warn")[startsWith(stripped[[1]], c("warning(", "warn(", "rlang::warn("))] 130 | code[[1]] <- paste0(cli::bg_yellow(cli::col_black(fun)), cli::ansi_substring(code[[1]], cli::ansi_nchar(fun) + 1)) 131 | } 132 | return(code) 133 | } 134 | width <- width + 1 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /R/helpers.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ansi_if <- function() cli::code_highlight("if (x) x") |> cli::ansi_substring(1, 3) 5 | ansi_for <- function() cli::code_highlight("for (x in x) x") |> cli::ansi_substring(1, 4) 6 | ansi_in <- function() cli::code_highlight("for (x in x) x") |> cli::ansi_substring(8, 9) 7 | ansi_while <- function() cli::code_highlight("while (x) x") |> cli::ansi_substring(1, 6) 8 | ansi_repeat <- function() cli::code_highlight("repeat x") |> cli::ansi_substring(1, 7) 9 | 10 | header_if <- function(call, width) { 11 | header <- call 12 | header <- header[1:2] 13 | header[[1]] <- quote(.if) 14 | header_chr <- cli::code_highlight(rlang::expr_deparse(header, width = width)) 15 | header_chr <- paste(header_chr, collapse = "\n") 16 | header_chr <- paste0(ansi_if(), cli::ansi_substring(header_chr, 4)) 17 | header_chr <- strsplit(header_chr, "\n")[[1]] 18 | } 19 | 20 | header_for <- function(call, width) { 21 | header <- call 22 | header <- header[1:2] 23 | header[[2]] <- call("?", call[[2]], call[[3]]) 24 | header[[1]] <- quote(.for) 25 | n_sym_chr <- nchar(as.character(call[[2]])) 26 | header_chr <- cli::code_highlight(rlang::expr_deparse(header, width = width)) 27 | header_chr <- paste(header_chr, collapse = "\n") 28 | header_chr <- paste0( 29 | ansi_for(), 30 | cli::ansi_substring(header_chr, 5, 5 + n_sym_chr + 1), 31 | ansi_in(), 32 | cli::ansi_substring(header_chr, 5 + n_sym_chr + 3) 33 | ) 34 | header_chr <- strsplit(header_chr, "\n")[[1]] 35 | } 36 | 37 | header_while <- function(call, width) { 38 | header <- call 39 | header <- header[1:2] 40 | header[[1]] <- quote(.while) 41 | header_chr <- cli::code_highlight(rlang::expr_deparse(header, width = width)) 42 | header_chr <- paste(header_chr, collapse = "\n") 43 | header_chr <- paste0(ansi_while(), cli::ansi_substring(header_chr, 7)) 44 | header_chr <- strsplit(header_chr, "\n")[[1]] 45 | } 46 | 47 | header_repeat <- function() { 48 | ansi_repeat() 49 | } 50 | 51 | header_fun <- function(call, width) { 52 | # not great, hacky and width is ignored 53 | call[[3]][[3]] <- quote(expr=...) 54 | header_chr <- rlang::expr_deparse(call, width = width) 55 | header_chr <- cli::code_highlight(header_chr) 56 | header_chr 57 | } 58 | 59 | code_to_box <- function(code, col) { 60 | width_nested_content <- max(cli::ansi_nchar(code)) 61 | width_content <- width_nested_content + 4 62 | paddings <- strrep(" ", width_nested_content - cli::ansi_nchar(code)) 63 | box <- paste0(col("| "), code, paddings, col(" |")) 64 | top_line <- col(sprintf("\U{250C}%s\U{2510}", strrep("\U{2500}", width_content - 2))) 65 | bottom_line <- col(sprintf("\U{2514}%s\U{2518}", strrep("\U{2500}", width_content - 2))) 66 | box <- c(top_line, box, bottom_line) 67 | } 68 | 69 | optimal_content_width <- function(call, width, optimization) { 70 | if (optimization == "weak") return(width) 71 | if (optimization == "medium") optimization <- "weak" 72 | code_first_pass <- unlist(lapply(call[-1], boxview_rec, width = width, optimization = optimization)) 73 | width_nested_content <- max(cli::ansi_nchar(code_first_pass)) 74 | width_content <- width_nested_content + 4 75 | max(width_content, width) 76 | } 77 | 78 | is_function_definition <- function(call) { 79 | if (!rlang::is_call(call, "<-") && !rlang::is_call(call, "=")) return(FALSE) 80 | if (length(call) != 3) return(FALSE) 81 | rlang::is_call(call[[3]], "function") 82 | } 83 | -------------------------------------------------------------------------------- /R/print.R: -------------------------------------------------------------------------------- 1 | 2 | #' @export 3 | print.boxview <- function(x, ...) { 4 | writeLines(x) 5 | invisible(x) 6 | } 7 | -------------------------------------------------------------------------------- /R/swap_calls.R: -------------------------------------------------------------------------------- 1 | swap_calls <- function (expr) { 2 | if (!is.call(expr)) 3 | return(expr) 4 | 5 | expr_is_if_assignment <- 6 | identical(expr[[1]], quote(`<-`)) && 7 | is.call(expr[[3]]) && 8 | identical(expr[[3]][[1]], quote(`if`)) 9 | 10 | if (expr_is_if_assignment) { 11 | target <- expr[[2]] 12 | if_expr <- expr[[3]] 13 | yes <- if_expr[[3]] 14 | yes_has_braces <- is.call(yes) && identical(yes[[1]], quote(`{`)) 15 | if (yes_has_braces) { 16 | # replace last expr of yes clause 17 | yes[[length(yes)]] <- bquote(.(target) <- .(yes[[length(yes)]])) 18 | # the new last expr and other calls in the clause must be checked too 19 | yes <- swap_calls(yes) 20 | } else { 21 | yes <- bquote(.(target) <- .(yes)) 22 | yes <- swap_calls(yes) 23 | } 24 | if_expr[[3]] <- yes 25 | 26 | if_has_else <- length(if_expr) == 4 27 | 28 | if (if_has_else) { 29 | no <- if_expr[[4]] 30 | no_has_braces <- is.call(no) && identical(no[[1]], quote(`{`)) 31 | if (no_has_braces) { 32 | # replace last expr of yes clause 33 | no[[length(no)]] <- bquote(.(target) <- .(no[[length(no)]])) 34 | # the new last expr and other calls in the clause must be checked too 35 | no <- swap_calls(no) 36 | } else { 37 | no <- bquote(.(target) <- .(no)) 38 | no <- swap_calls(no) 39 | } 40 | if_expr[[4]] <- no 41 | } 42 | return(if_expr) 43 | } 44 | expr[] <- lapply(expr, swap_calls) 45 | expr 46 | } 47 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | 16 | # boxview 17 | 18 | `boxview::boxview()` displays the code of a function in nested boxes, showing the 'yes' and 'no' clauses of `if` calls side by side. It often makes code easier to skim than the traditional way. 19 | 20 | ## Installation 21 | 22 | You can install the development version of boxview like so: 23 | 24 | ``` r 25 | pak::pak("cynkra/boxview") 26 | ``` 27 | 28 | ## Example 29 | 30 | Here's `base::interaction()` shown with boxview and in the console. 31 | 32 | ![](inst/interaction1.png) 33 | 34 | ![](inst/interaction2.png) 35 | 36 | There's also a `width` argument to adjust, and an `optimization` argument that 37 | you might use to get a nicer output, at the cost of taking more time. 38 | 39 | ![](inst/ave.png) 40 | 41 | ## See also 42 | 43 | The [flow](https://github.com/moodymudskipper/flow) package and in particular the 44 | function `flow::flow_view()` for a somewhat comparable way to represent functions. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # boxview 5 | 6 | `boxview::boxview()` displays the code of a function in nested boxes, 7 | showing the ‘yes’ and ‘no’ clauses of `if` calls side by side. It often 8 | makes code easier to skim than the traditional way. 9 | 10 | ## Installation 11 | 12 | You can install the development version of boxview like so: 13 | 14 | ``` r 15 | pak::pak("cynkra/boxview") 16 | ``` 17 | 18 | ## Example 19 | 20 | Here’s `base::interaction()` shown with boxview and in the console. 21 | 22 | ![](inst/interaction1.png) 23 | 24 | ![](inst/interaction2.png) 25 | 26 | There’s also a `width` argument to adjust, and an `optimization` 27 | argument that you might use to get a nicer output, at the cost of taking 28 | more time. 29 | 30 | ![](inst/ave.png) 31 | 32 | ## See also 33 | 34 | The [flow](https://github.com/moodymudskipper/flow) package and in 35 | particular the function `flow::flow_view()` for a somewhat comparable 36 | way to represent functions. 37 | -------------------------------------------------------------------------------- /boxview.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /inst/ave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/boxview/00033beeb716b57330d358921ebb75acd340fac7/inst/ave.png -------------------------------------------------------------------------------- /inst/interaction1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/boxview/00033beeb716b57330d358921ebb75acd340fac7/inst/interaction1.png -------------------------------------------------------------------------------- /inst/interaction2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/boxview/00033beeb716b57330d358921ebb75acd340fac7/inst/interaction2.png -------------------------------------------------------------------------------- /man/boxview.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/boxview.R 3 | \name{boxview} 4 | \alias{boxview} 5 | \title{Display code logic in nested boxes} 6 | \usage{ 7 | boxview(fun, width = 200, optimization = c("weak", "medium", "strong")) 8 | } 9 | \arguments{ 10 | \item{fun}{A function} 11 | 12 | \item{width}{The desired width, the output will be wider if the we can't 13 | make it narrow enough by wrapping code, and will be narrower if we don't 14 | need the width when not wrapping at all} 15 | 16 | \item{optimization}{The level of space optimization, to wrap calls only 17 | when needed. \code{boxview()} is quite slow, and strong optimization can take 18 | a long time for big functions. You might not need higher levels most of 19 | the time, especially with sufficient width where the difference is often 20 | barely noticeable.} 21 | } 22 | \value{ 23 | a 'boxview' object that prints the boxed code to the console. 24 | } 25 | \description{ 26 | \code{boxview()} displays the code of a function in nested boxes, showing the 27 | 'yes' and 'no' clauses of \code{if} calls side by side. It often makes code easier to 28 | skim than the traditional way. 29 | } 30 | \examples{ 31 | \dontrun{ 32 | boxview(ave) 33 | boxview(ave, width = 10) 34 | boxview(ave, width = 10, optimization = "medium") 35 | boxview(interaction) 36 | boxview(data.frame) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cynkra/boxview/00033beeb716b57330d358921ebb75acd340fac7/man/figures/logo.png -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview 7 | # * https://testthat.r-lib.org/articles/special-files.html 8 | 9 | library(testthat) 10 | library(boxview) 11 | 12 | test_check("boxview") 13 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/boxview.md: -------------------------------------------------------------------------------- 1 | # boxview() snapshots 2 | 3 | Code 4 | boxview(ave) 5 | Output 6 | function (x, ..., FUN = mean) 7 | ┌───────────────────────────────────────────────────────────────┐ 8 | | if (missing(...)) | 9 | | ┌───────────────┐ ┌─────────────────────────────────────────┐ | 10 | | | x[] <- FUN(x) | | g <- interaction(...) | | 11 | | └───────────────┘ | split(x, g) <- lapply(split(x, g), FUN) | | 12 | | └─────────────────────────────────────────┘ | 13 | | x | 14 | └───────────────────────────────────────────────────────────────┘ 15 | 16 | --- 17 | 18 | Code 19 | boxview(ave, width = 40) 20 | Output 21 | function (x, ..., FUN = mean) 22 | ┌───────────────────────────────┐ 23 | | if (missing(...)) | 24 | | ┌────────┐ ┌────────────────┐ | 25 | | | x[] <- | | g <- | | 26 | | | FUN( | | interaction( | | 27 | | | x | | ... | | 28 | | | ) | | ) | | 29 | | └────────┘ | split( | | 30 | | | x, g | | 31 | | | ) <- | | 32 | | | lapply( | | 33 | | | split( | | 34 | | | x, | | 35 | | | g | | 36 | | | ), | | 37 | | | FUN | | 38 | | | ) | | 39 | | └────────────────┘ | 40 | | x | 41 | └───────────────────────────────┘ 42 | 43 | --- 44 | 45 | Code 46 | boxview(ave, width = 40, optimization = "strong") 47 | Output 48 | function (x, ..., FUN = mean) 49 | ┌───────────────────────────────┐ 50 | | if (missing(...)) | 51 | | ┌────────┐ ┌────────────────┐ | 52 | | | x[] <- | | g <- | | 53 | | | FUN( | | interaction( | | 54 | | | x | | ... | | 55 | | | ) | | ) | | 56 | | └────────┘ | split(x, g) <- | | 57 | | | lapply( | | 58 | | | split( | | 59 | | | x, | | 60 | | | g | | 61 | | | ), FUN | | 62 | | | ) | | 63 | | └────────────────┘ | 64 | | x | 65 | └───────────────────────────────┘ 66 | 67 | # swapcall() snapshots 68 | 69 | Code 70 | swap_calls(quote({ 71 | a <- if (this) { 72 | if (this) a else b 73 | } else b 74 | })) 75 | Output 76 | { 77 | if (this) { 78 | if (this) 79 | a <- a 80 | else a <- b 81 | } 82 | else a <- b 83 | } 84 | 85 | -------------------------------------------------------------------------------- /tests/testthat/test-boxview.R: -------------------------------------------------------------------------------- 1 | test_that("boxview() snapshots", { 2 | expect_snapshot(boxview(ave)) 3 | expect_snapshot(boxview(ave, width = 40)) 4 | expect_snapshot(boxview(ave, width = 40, optimization = "strong")) 5 | }) 6 | 7 | test_that("swapcall() snapshots", { 8 | expect_snapshot( 9 | swap_calls(quote({a <- if (this) {if (this) a else b} else b})) 10 | ) 11 | }) 12 | --------------------------------------------------------------------------------