diff --git a/R/modify_pd.R b/R/modify_pd.R index f8ec2128a..7d0b7e295 100644 --- a/R/modify_pd.R +++ b/R/modify_pd.R @@ -9,67 +9,23 @@ NULL #' @describeIn update_indention Inserts indetion based on round brackets. indent_round <- function(pd, indent_by) { - indention_needed <- needs_indention(pd, token = "'('") - if (indention_needed) { - opening <- which(pd$token == "'('") - start <- opening + 1 - stop <- nrow(pd) - 1 - if (start > stop) return(pd) - - pd <- pd %>% - mutate(indent = indent + ifelse(seq_len(nrow(pd)) %in% start:stop, - indent_by, 0)) - } - - pd %>% - set_unindention_child(token = "')'", unindent_by = indent_by) + indent_indices <- compute_indent_indices(pd, token = "'('") + pd$indent[indent_indices] <- pd$indent[indent_indices] + indent_by + set_unindention_child(pd, token = "')'", unindent_by = indent_by) } + #' @rdname update_indention indent_curly <- function(pd, indent_by) { - indention_needed <- needs_indention(pd, token = "'{'") - if (indention_needed) { - opening <- which(pd$token == "'{'") - start <- opening + 1 - stop <- nrow(pd) - 1 - if (start > stop) return(pd) - - pd <- pd %>% - mutate(indent = indent + ifelse(seq_len(nrow(pd)) %in% start:stop, - indent_by, 0)) - } - pd %>% - set_unindention_child(token = "'}'", unindent_by = indent_by) -} - -#' Check whether indention is needed -#' -#' @param pd A parse table. -#' @param token Which token the check should be based on. -#' @return returns `TRUE` if indention is needed, `FALSE` otherwise. Indention -#' is needed: -#' * if `token` occurs in `pd`. -#' * if there is no child that starts on the same line as `token` and -#' "opens" indention without closing it on this line. -#' @return `TRUE` if indention is needed, `FALSE` otherwise. -needs_indention <- function(pd, token = "'('") { - opening <- which(pd$token %in% token)[1] - if (is.na(opening)) return(FALSE) - before_first_break <- which(pd$lag_newlines > 0)[1] - 1 - if (is.na(before_first_break)) return(FALSE) - !any(pd$multi_line[opening:before_first_break]) + indent_indices <- compute_indent_indices(pd, token = "'{'") + pd$indent[indent_indices] <- pd$indent[indent_indices] + indent_by + set_unindention_child(pd, token = "'}'", unindent_by = indent_by) } #' @rdname update_indention indent_op <- function(pd, indent_by, token = c(math_token, "SPECIAL-PIPE")) { - if (needs_indention(pd, token)) { - opening <- which(pd$token %in% token) - start <- opening[1] + 1 - stop <- nrow(pd) - pd <- pd %>% - mutate(indent = indent + ifelse(seq_len(nrow(pd)) %in% start:stop, - indent_by, 0)) - } + indent_indices <- compute_indent_indices(pd, token, indent_last = TRUE) + pd$indent[indent_indices] <- pd$indent[indent_indices] + indent_by pd } @@ -77,14 +33,8 @@ indent_op <- function(pd, indent_by, token = c(math_token, #' after `token`, not all remaining. indent_assign <- function(pd, indent_by, token = c("LEFT_ASSIGN", " EQ_ASSIGN")) { - if (needs_indention(pd, token)) { - opening <- which(pd$token %in% token) - start <- opening + 1 - stop <- start + 1 - pd <- pd %>% - mutate(indent = indent + ifelse(seq_len(nrow(pd)) %in% start:stop, - indent_by, 0)) - } + indent_indices <- compute_indent_indices(pd, token, indent_last = TRUE) + pd$indent[indent_indices] <- pd$indent[indent_indices] + indent_by pd } @@ -98,6 +48,47 @@ indent_without_paren <- function(pd, indent_by = 2) { pd } +#' Compute the indices that need indention +#' +#' Based on `token`, find the rows in `pd` that need to be indented. +#' @param pd A parse table. +#' @param token A character vector with tokens. +#' @param indent_last Flag to indicate whether the last token in `pd` should +#' be indented or not. See 'Details'. +#' @details +#' For example when `token` is a parenthesis, the closing parenthesis does not +#' need indention, but if token is something else, for example a plus (+), the +#' last token in `pd` needs indention. +compute_indent_indices <- function(pd, token = "'('", indent_last = FALSE) { + npd <- nrow(pd) + opening <- which(pd$token %in% token)[1] + if (!needs_indention(pd, opening)) return(numeric(0)) + start <- opening + 1 + stop <- npd - ifelse(indent_last, 0, 1) + which(between(seq_len(npd), start, stop)) +} + + +#' Check whether indention is needed +#' +#' @param pd A parse table. +#' @param opening the index of the opening parse table. Since always computed +#' before this function is called, it is included as an argument so it does +#' not have to be recomputed. +#' @return returns `TRUE` if indention is needed, `FALSE` otherwise. Indention +#' is needed if and only if: +#' * the opening token is not `NA`. +#' * if there is a multi-line token before the first line break. +#' @return `TRUE` if indention is needed, `FALSE` otherwise. +needs_indention <- function(pd, opening) { + if (is.na(opening)) return(FALSE) + before_first_break <- which(pd$lag_newlines > 0)[1] - 1 + if (is.na(before_first_break)) return(FALSE) + !any(pd$multi_line[opening:before_first_break]) +} + + + #' Set the multi-line column #' #' Sets the column `multi_line` in `pd` by checking row-wise whether any child @@ -105,8 +96,8 @@ indent_without_paren <- function(pd, indent_by = 2) { #' @param pd A parse table. #' @importFrom purrr map_lgl set_multi_line <- function(pd) { - pd %>% - mutate(multi_line = map_lgl(child, token_is_multi_line)) + pd$multi_line <- map_lgl(pd$child, token_is_multi_line) + pd } #' Check whether a parse table is a multi-line token @@ -117,7 +108,7 @@ set_multi_line <- function(pd) { #' * it has at least one child that is a multi-line expression itself. #' @param pd A parse table. token_is_multi_line <- function(pd) { - any(pd$multi_line) | any(pd$lag_newlines) + any(pd$multi_line, pd$lag_newlines > 0) } @@ -127,6 +118,7 @@ token_is_multi_line <- function(pd) { #' @param pd_flat A flat parse table. #' @return A nested parse table. strip_eol_spaces <- function(pd_flat) { - pd_flat %>% - mutate(spaces = spaces * (lead(lag_newlines, default = 0) == 0)) + idx <- lead(pd_flat$lag_newlines, default = 0) != 0 + pd_flat$spaces[idx] <- 0 + pd_flat } diff --git a/R/nested.R b/R/nested.R index b6f88a5e3..3bfaf525b 100644 --- a/R/nested.R +++ b/R/nested.R @@ -29,10 +29,8 @@ compute_parse_data_nested <- function(text) { add_terminal_token_before() %>% add_terminal_token_after() + parse_data$child <- rep(list(NULL), length(parse_data$text)) pd_nested <- parse_data %>% - mutate_(child = ~rep(list(NULL), length(text))) %>% - mutate_(short = ~substr(text, 1, 5)) %>% - select_(~short, ~everything()) %>% nest_parse_data() %>% flatten_operators() @@ -48,6 +46,7 @@ tokenize <- function(text) { parsed <- parse(text = text, keep.source = TRUE) parse_data <- as_tibble(utils::getParseData(parsed, includeText = NA)) %>% enhance_mapping_special() + parse_data$short <- substr(parse_data$text, 1, 5) parse_data } @@ -57,13 +56,13 @@ tokenize <- function(text) { #' description. #' @param pd A parse table. enhance_mapping_special <- function(pd) { - pd %>% - mutate(token = case_when( + pd$token <- with(pd, case_when( token != "SPECIAL" ~ token, text == "%>%" ~ special_and("PIPE"), text == "%in%" ~ special_and("IN"), TRUE ~ special_and("OTHER") )) + pd } special_and <- function(text) { @@ -98,19 +97,23 @@ NULL #' @rdname add_token_terminal add_terminal_token_after <- function(pd_flat) { - pd_flat %>% + terminals <- pd_flat %>% filter(terminal) %>% - arrange(line1, col1) %>% - transmute(id = id, token_after = lead(token, default = "")) %>% + arrange(line1, col1) + + data_frame(id = terminals$id, + token_after = lead(terminals$token, default = "")) %>% left_join(pd_flat, ., by = "id") } #' @rdname add_token_terminal add_terminal_token_before <- function(pd_flat) { - pd_flat %>% + terminals <- pd_flat %>% filter(terminal) %>% - arrange(line1, col1) %>% - transmute(id = id, token_before = lag(token, default = "")) %>% + arrange(line1, col1) + + data_frame(id = terminals$id, + token_before = lag(terminals$token, default = "")) %>% left_join(pd_flat, ., by = "id") } @@ -146,24 +149,22 @@ set_spaces <- function(spaces_after_prefix, force_one) { #' @importFrom purrr map2 nest_parse_data <- function(pd_flat) { if (all(pd_flat$parent <= 0)) return(pd_flat) - split <- pd_flat %>% - mutate_(internal = ~ (id %in% parent) | (parent <= 0)) %>% - nest_("data", names(pd_flat)) + pd_flat$internal <- with(pd_flat, (id %in% parent) | (parent <= 0)) + split_data <- split(pd_flat, pd_flat$internal) - child <- split$data[!split$internal][[1L]] - internal <- split$data[split$internal][[1L]] + child <- split_data$`FALSE` + internal <- split_data$`TRUE` internal <- rename_(internal, internal_child = ~child) - nested <- + child$parent_ <- child$parent + joined <- child %>% - mutate_(parent_ = ~parent) %>% nest_(., "child", setdiff(names(.), "parent_")) %>% - left_join(internal, ., by = c("id" = "parent_")) %>% - mutate_(child = ~map2(child, internal_child, combine_children)) %>% - select_(~-internal_child) %>% - select_(~short, ~everything(), ~-text, ~text) - + left_join(internal, ., by = c("id" = "parent_")) + nested <- joined + nested$child <- map2(nested$child, nested$internal_child, combine_children) + nested <- nested[, setdiff(names(nested), "internal_child")] nest_parse_data(nested) } @@ -179,7 +180,8 @@ nest_parse_data <- function(pd_flat) { combine_children <- function(child, internal_child) { bound <- bind_rows(child, internal_child) if (nrow(bound) == 0) return(NULL) - arrange_(bound, ~line1, ~col1) + bound[order(bound$line1, bound$col1), ] + } #' Get the start right diff --git a/R/parsed.R b/R/parsed.R index 3c1c5bc21..935693495 100644 --- a/R/parsed.R +++ b/R/parsed.R @@ -44,9 +44,12 @@ enhance_parse_data <- function(parse_data) { parse_data_filtered %>% create_filler() - parse_data_comment_eol <- - parse_data_filled %>% - mutate_(text = ~if_else(token == "COMMENT", gsub(" +$", "", text), text)) + parse_data_comment_eol <- parse_data_filled + + parse_data_comment_eol$text <- + if_else(parse_data_comment_eol$token == "COMMENT", + gsub(" +$", "", parse_data_comment_eol$text), + parse_data_comment_eol$text) parse_data_comment_eol } @@ -81,18 +84,17 @@ verify_roundtrip <- function(pd_flat, text) { #' @return A parse table with two three columns: lag_newlines, newlines and #' spaces. create_filler <- function(pd_flat) { - ret <- - pd_flat %>% - mutate_( - line3 = ~lead(line1, default = tail(line2, 1)), - col3 = ~lead(col1, default = tail(col2, 1) + 1L), - newlines = ~line3 - line2, - lag_newlines = ~lag(newlines, default = 0), - col2_nl = ~if_else(newlines > 0L, 0L, col2), - spaces = ~col3 - col2_nl - 1L, - multi_line = ~ifelse(terminal, FALSE, NA) - ) %>% - select_(~-line3, ~-col3, ~-col2_nl) + + pd_flat$line3 <- lead(pd_flat$line1, default = tail(pd_flat$line2, 1)) + pd_flat$col3 <- lead(pd_flat$col1, default = tail(pd_flat$col2, 1) + 1L) + pd_flat$newlines <- pd_flat$line3 - pd_flat$line2 + pd_flat$lag_newlines <- lag(pd_flat$newlines, default = 0) + pd_flat$col2_nl <- if_else(pd_flat$newlines > 0L, 0L, pd_flat$col2) + pd_flat$spaces <- pd_flat$col3 - pd_flat$col2_nl - 1L + pd_flat$multi_line <- ifelse(pd_flat$terminal, FALSE, NA) + + ret <- pd_flat[, !(names(pd_flat) %in% c("line3", "col3", "col2_nl"))] + if (!("indent" %in% names(ret))) { ret$indent <- 0 diff --git a/R/rules-replacement.R b/R/rules-replacement.R index 3fa98e786..2066e6fdb 100644 --- a/R/rules-replacement.R +++ b/R/rules-replacement.R @@ -8,7 +8,8 @@ force_assignment_op <- function(pd) { resolve_semicolon <- function(pd) { is_semicolon <- pd$token == "';'" + if (!any(is_semicolon)) return(pd) pd$lag_newlines[lag(is_semicolon)] <- 1L - pd <- pd[!is_semicolon,] + pd <- pd[!is_semicolon, ] pd } diff --git a/R/rules-spacing.R b/R/rules-spacing.R index 30b833802..cfcd1e723 100644 --- a/R/rules-spacing.R +++ b/R/rules-spacing.R @@ -25,6 +25,7 @@ add_space_around_op <- function(pd_flat) { set_space_around_op <- function(pd_flat) { op_after <- pd_flat$token %in% op_token + if (!any(op_after)) return(pd_flat) op_before <- lead(op_after, default = FALSE) pd_flat$spaces[op_before & (pd_flat$newlines == 0L)] <- 1L pd_flat$spaces[op_after & (pd_flat$newlines == 0L)] <- 1L @@ -44,7 +45,7 @@ remove_space_after_unary_pm <- function(pd_flat) { remove_space_after_unary_pm_nested <- function(pd) { - if (any(c("'+'", "'-'") %in% pd$token[1])) { + if (any(pd$token[1] %in% c("'+'", "'-'"))) { pd$spaces[1] <- 0L } @@ -65,6 +66,7 @@ fix_quotes <- function(pd_flat) { remove_space_before_opening_paren <- function(pd_flat) { paren_after <- pd_flat$token == "'('" + if (!any(paren_after)) return(pd_flat) paren_before <- lead(paren_after, default = FALSE) pd_flat$spaces[paren_before & (pd_flat$newlines == 0L)] <- 0L pd_flat @@ -72,12 +74,14 @@ remove_space_before_opening_paren <- function(pd_flat) { remove_space_after_opening_paren <- function(pd_flat) { paren_after <- pd_flat$token == "'('" + if (!any(paren_after)) return(pd_flat) pd_flat$spaces[paren_after & (pd_flat$newlines == 0L)] <- 0L pd_flat } remove_space_before_closing_paren <- function(pd_flat) { paren_after <- pd_flat$token == "')'" + if (!any(paren_after)) return(pd_flat) paren_before <- lead(paren_after, default = FALSE) pd_flat$spaces[paren_before & (pd_flat$newlines == 0L)] <- 0L pd_flat @@ -85,6 +89,7 @@ remove_space_before_closing_paren <- function(pd_flat) { add_space_after_for_if_while <- function(pd_flat) { comma_after <- pd_flat$token %in% c("FOR", "IF", "WHILE") + if (!any(comma_after)) return(pd_flat) idx <- comma_after & (pd_flat$newlines == 0L) pd_flat$spaces[idx] <- pmax(pd_flat$spaces[idx], 1L) pd_flat @@ -92,6 +97,7 @@ add_space_after_for_if_while <- function(pd_flat) { add_space_before_brace <- function(pd_flat) { op_after <- pd_flat$token %in% "'{'" + if (!any(op_after)) return(pd_flat) op_before <- lead(op_after, default = FALSE) idx_before <- op_before & (pd_flat$newlines == 0L) & pd_flat$token != "'('" pd_flat$spaces[idx_before] <- pmax(pd_flat$spaces[idx_before], 1L) @@ -112,6 +118,7 @@ set_space_after_comma <- function(pd_flat) { remove_space_before_comma <- function(pd_flat) { comma_after <- pd_flat$token == "','" + if (!any(comma_after)) return(pd_flat) comma_before <- lead(comma_after, default = FALSE) idx <- comma_before & (pd_flat$newlines == 0L) pd_flat$spaces[idx] <- 0L @@ -143,30 +150,40 @@ set_space_between_levels <- function(pd_flat) { #' @param pd A parse table. #' @param force_one Wheter or not to force one space or allow multiple spaces #' after the regex "^#+'*". +#' @importFrom purrr map_chr start_comments_with_space <- function(pd, force_one = FALSE) { - comments <- pd %>% - filter(token == "COMMENT") - - if (nrow(comments) == 0) return(pd) - - non_comments <- pd %>% - filter(token != "COMMENT") - - comments <- comments %>% - extract(text, c("prefix", "space_after_prefix", "text"), - regex = "^(#+'*)( *)(.*)$") %>% - mutate(space_after_prefix = nchar(space_after_prefix), - space_after_prefix = set_spaces(space_after_prefix, force_one), - text = paste0(prefix, rep_char(" ", space_after_prefix), text), - short = substr(text, 1, 5)) %>% - select(-space_after_prefix, -prefix) - bind_rows(comments, non_comments) %>% + comment_pos <- pd$token == "COMMENT" + if (!any(comment_pos)) return(pd) + + comments <- pd[comment_pos, ] + + non_comments <-pd[pd$token != "COMMENT", ] + + comments <- extract(comments, text, + c("prefix", "space_after_prefix", "text"), + regex = "^(#+'*)( *)(.*)$") + comments$space_after_prefix <- nchar(comments$space_after_prefix) + comments$space_after_prefix <- set_spaces( + comments$space_after_prefix, + force_one + ) + + comments$text <- paste0( + comments$prefix, + map_chr(comments$space_after_prefix, rep_char, char = " "), + comments$text + ) + comments$short <- substr(comments$text, 1, 5) + + comments[, setdiff(names(comments), c("space_after_prefix", "prefix"))] %>% + bind_rows(non_comments) %>% arrange(line1, col1) } set_space_before_comments <- function(pd_flat) { comment_after <- pd_flat$token == "COMMENT" + if (!any(comment_after)) return(pd_flat) comment_before <- lead(comment_after, default = FALSE) pd_flat$spaces[comment_before & (pd_flat$newlines == 0L)] <- 1L pd_flat diff --git a/R/unindent.R b/R/unindent.R index e4fd35638..c2279ff93 100644 --- a/R/unindent.R +++ b/R/unindent.R @@ -5,7 +5,7 @@ #' @inheritParams unindent_child #' @importFrom purrr map set_unindention_child <- function(pd, token = "')'", unindent_by) { - if(all(pd$terminal) | all(pd$indent == 0)) return(pd) + if(all(pd$indent == 0) || all(pd$terminal) ) return(pd) closing <- which(pd$token %in% token) if (length(closing) == 0 || pd$lag_newlines[closing] > 0) return(pd) diff --git a/R/utils.R b/R/utils.R index 3ff65759c..0bf73e9e4 100644 --- a/R/utils.R +++ b/R/utils.R @@ -6,8 +6,7 @@ parse_text <- function(x) parse(text = x)[[1L]] #' @param times an integer giving the number of repetitions. #' @return A character vector. rep_char <- function(char, times) { - lapply(times, rep.int, x = char) %>% - vapply(paste, collapse = "", character(1L)) + paste(rep.int(char, times), collapse = "") } #' concentrate newlines an spaces in a string @@ -16,8 +15,9 @@ rep_char <- function(char, times) { #' @param spaces Scalar indicating how many spaces should be appended to the #' newlines. #' @return A string. +#' @importFrom purrr map2 newlines_and_spaces <- function(newlines, spaces) { - paste0(rep_char("\n", newlines), rep_char(" ", spaces)) + map2(newlines, spaces, ~paste0(rep_char("\n", .x), rep_char(" ", .y))) } add_newlines <- function(n) { diff --git a/R/visit.R b/R/visit.R index ab507b9f1..1d984d58d 100644 --- a/R/visit.R +++ b/R/visit.R @@ -16,19 +16,19 @@ NULL #' @rdname visit pre_visit <- function(pd_nested, funs) { if (is.null(pd_nested)) return() - pd_transformed <- pd_nested %>% - visit_one(funs) %>% - mutate(child = map(child, pre_visit, funs = funs)) + pd_transformed <- visit_one(pd_nested, funs) + + pd_transformed$child <- map(pd_transformed$child, pre_visit, funs = funs) pd_transformed } #' @rdname visit post_visit <- function(pd_nested, funs) { if (is.null(pd_nested)) return() - pd_transformed <- pd_nested %>% - mutate(child = map(child, post_visit, funs = funs)) %>% - visit_one(funs) - pd_transformed + pd_transformed <- pd_nested + + pd_transformed$child <- map(pd_transformed$child, post_visit, funs = funs) + visit_one(pd_transformed, funs) } #' Transform a flat parse table with a list of transformers diff --git a/man/compute_indent_indices.Rd b/man/compute_indent_indices.Rd new file mode 100644 index 000000000..aabab9ffd --- /dev/null +++ b/man/compute_indent_indices.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/modify_pd.R +\name{compute_indent_indices} +\alias{compute_indent_indices} +\title{Compute the indices that need indention} +\usage{ +compute_indent_indices(pd, token = "'('", indent_last = FALSE) +} +\arguments{ +\item{pd}{A parse table.} + +\item{token}{A character vector with tokens.} + +\item{indent_last}{Flag to indicate whether the last token in \code{pd} should +be indented or not. See 'Details'.} +} +\description{ +Based on \code{token}, find the rows in \code{pd} that need to be indented. +} +\details{ +For example when \code{token} is a parenthesis, the closing parenthesis does not +need indention, but if token is something else, for example a plus (+), the +last token in \code{pd} needs indention. +} diff --git a/man/needs_indention.Rd b/man/needs_indention.Rd index 850360a00..13fa00eea 100644 --- a/man/needs_indention.Rd +++ b/man/needs_indention.Rd @@ -4,19 +4,20 @@ \alias{needs_indention} \title{Check whether indention is needed} \usage{ -needs_indention(pd, token = "'('") +needs_indention(pd, opening) } \arguments{ \item{pd}{A parse table.} -\item{token}{Which token the check should be based on.} +\item{opening}{the index of the opening parse table. Since always computed +before this function is called, it is included as an argument so it does +not have to be recomputed.} } \value{ returns \code{TRUE} if indention is needed, \code{FALSE} otherwise. Indention -is needed: -* if \code{token} occurs in \code{pd}. -* if there is no child that starts on the same line as \code{token} and -"opens" indention without closing it on this line. +is needed if and only if: +* the opening token is not \code{NA}. +* if there is a multi-line token before the first line break. \code{TRUE} if indention is needed, \code{FALSE} otherwise. } diff --git a/vignettes/performance_improvements.Rmd b/vignettes/performance_improvements.Rmd new file mode 100644 index 000000000..35338100a --- /dev/null +++ b/vignettes/performance_improvements.Rmd @@ -0,0 +1,66 @@ +--- +title: "Performance Improvements" +author: "Lorenz Walthert" +date: "7/24/2017" +output: html_document +--- + +We want to make styler faster. + +```{r} +library(styler) +microbenchmark::microbenchmark( + base = style_file("tests/testthat/indention_multiple/overall-in.R"), + times = 2 +) +#> Unit: seconds +#> expr min lq mean median uq max neval +#> base 4.131253 4.131253 4.172017 4.172017 4.212781 4.212781 2 +``` + +Replacing mutate statments. +```{r} +microbenchmark::microbenchmark( + base = style_file("tests/testthat/indention_multiple/overall-in.R"), + times = 2 +) +#> Unit: seconds +#> expr min lq mean median uq max neval +#> base 2.13616 2.13616 2.223659 2.223659 2.311158 2.311158 2 +``` + +Move `opening` argument out of needs indention. +```{r} +microbenchmark::microbenchmark( + base = style_file("tests/testthat/indention_multiple/overall-in.R"), + times = 5 +) + +#> Unit: seconds +#> expr min lq mean median uq max neval +#> base 2.18097 2.184721 2.225294 2.200893 2.241799 2.318089 5 +``` + +Dropping unnecessary select and arrange stuffstatments +```{r} +microbenchmark::microbenchmark( + base = style_file("tests/testthat/indention_multiple/overall-in.R"), + times = 5 +) +#> Unit: seconds +#> expr min lq mean median uq max neval +#> base 2.109271 2.134377 2.147821 2.158567 2.165384 2.171505 5 +``` + + +Some more stuff (early return, purr) +```{r} +microbenchmark::microbenchmark( + base = style_file("tests/testthat/indention_multiple/overall-in.R"), + times = 5 +) +#> Unit: milliseconds +#> expr min lq mean median uq max neval +#> base 930.4391 944.9253 969.2838 951.4632 951.6571 1067.934 5 +``` +