Customizing styler – the quick way

July 15, 2018
By

(This article was first published on Lorenz Walthert, and kindly contributed to R-bloggers)

I am currently experiencing problems with getting my posts in full length on
r-bloggers. You can continue here with
reading in case only the first paragraph is rendered.

One cool thing that happens if you work resonates in the community is that you
see other people using it. In this blog post I am going to address a typical
question people have when they want to use a source code formatter – in
particular styler:

I don’t like rule xyz of the tidyverse style guide, which is the default
style guide implemented in styler. How can I tell styler not to apply it?

Theory

First, I think reading the docs would be a good approach. There are two
resources:

  • The help file for
    the function tidyverse_style(), which returns the transformer functions that
    prettify your code. It has a few interesting arguments, some of which are
    rather complex.1

  • If you can’t get styler behaving the way you want using the arguments of
    tidyverse_style(), you have another option, which is described in a
    vignette: Creating
    your own style guide. Yes, I admit, it’s pretty long and if you don’t want to
    become a styler expert, it may be a little bit overwhelming.

If you don’t care about how to create new rules but you simply want to remove
a rule, I have good news for you: There is a quick way to do it. These are the
steps you need to complete in order to do it:

  • Figure out which transformer function in the transformers returned by
    tidyerse_style() corresponds to the rule you want to remove.

  • Set that element in the list to NULL, which is equivalent to removing it.

  • Pass the list to style_text as a transformer.

Practice

Lets assume you want to remove the rule that turns = into <- for assignment.
That means you want

string = "hi there" 

to remain unchanged after applying styler. This is not the case if you use the
default style guide of styler:

library(styler)
style_text("string = 'hi there'")
string <- "hi there"

So you need to figure out which rule is responsible for this. Let’s check the
transformer categories used with the tidyverse style guide.

transformers <- tidyverse_style()
names(transformers)
## [1] "initialize"        "line_break"        "space"            
## [4] "token"             "indention"         "use_raw_indention"
## [7] "reindention"

From the aforementioned
vignette:

We note that there are different types of transformer functions. initialize
initializes some variables in the nested parse table (so it is not actually a
transformer), and the other elements modify either spacing, line breaks or
tokens. use_raw_indention is not a function, it is just an option.

Now, we can look at the names of the rules that are sub-elements of the
transformer categories.

transformers
## $initialize
## $initialize$initialize
## function (pd_flat) 
## {
##     init_pd <- initialize_newlines(pd_flat) %>% initialize_spaces() %>% 
##         remove_attributes(c("line1", "line2", "col1", "col2", 
##             "parent", "id")) %>% initialize_multi_line() %>% 
##         initialize_indention_ref_pos_id() %>% initialize_indent() %>% 
##         validate_parse_data()
##     init_pd
## }
## 
## 
## 
## 
## $line_break
## $line_break$set_line_break_around_comma
## function (pd) 
## {
##     comma_with_line_break_before <- (pd$token == "','") & (pd$lag_newlines > 
##         0) & (pd$token_before != "COMMENT")
##     pd$lag_newlines[comma_with_line_break_before] <- 0L
##     pd$lag_newlines[lead(comma_with_line_break_before)] <- 1L
##     pd
## }
## 
## 
## 
## $line_break$remove_line_break_before_curly_opening
## function (pd) 
## {
##     rm_break <- (pd$token_after == "'{'") & (pd$token != "COMMENT")
##     pd$lag_newlines[lag(rm_break)] <- 0L
##     pd
## }
## 
## 
## 
## $line_break$remove_line_break_before_round_closing_after_curly
## function (pd) 
## {
##     round_after_curly <- pd$token == "')'" & (pd$token_before == 
##         "'}'")
##     pd$lag_newlines[round_after_curly] <- 0L
##     pd
## }
## 
## 
## 
## $line_break$remove_line_break_before_round_closing_fun_dec
## function (pd) 
## {
##     if (is_function_dec(pd)) {
##         round_after <- pd$token == "')'" & pd$token_before != 
##             "COMMENT"
##         pd$lag_newlines[round_after] <- 0L
##     }
##     pd
## }
## 
## 
## 
## $line_break$style_line_break_around_curly
## 
## function (...) 
## style_line_break_around_curly(strict, ...)
## 
## $line_break$set_line_break_after_opening_if_call_is_multi_line
## 
## function (...) 
## set_line_break_after_opening_if_call_is_multi_line(except_token_after = "COMMENT", 
##     except_text_before = c("switch", "ifelse", "if_else"), ...)
## 
## $line_break$set_line_break_before_closing_call
## 
## function (...) 
## set_line_break_before_closing_call(except_token_before = "COMMENT", 
##     ...)
## 
## $line_break$remove_line_break_in_empty_fun_call
## function (pd) 
## {
##     if (is_function_call(pd) && nrow(pd) == 3) {
##         pd$lag_newlines[3] <- 0L
##     }
##     pd
## }
## 
## 
## 
## $line_break$add_line_break_after_pipe
## function (pd) 
## {
##     is_special <- pd$token == c("SPECIAL-PIPE") & pd$token_after != 
##         "COMMENT"
##     if (any(pd$lag_newlines != 0L)) {
##         pd$lag_newlines[lag(is_special)] <- 1L
##     }
##     pd
## }
## 
## 
## 
## 
## $space
## $space$indent_braces
## 
## function (...) 
## indent_braces(indent_by = indent_by, ...)
## 
## $space$unindent_fun_dec
## function (pd) 
## {
##     if (is_function_dec(pd)) {
##         idx_closing_brace <- which(pd$token %in% "')'")
##         fun_dec_head <- seq2(2L, idx_closing_brace)
##         pd$indent[fun_dec_head] <- 0L
##     }
##     pd
## }
## 
## 
## 
## $space$indent_op
## 
## function (...) 
## indent_op(indent_by = indent_by, ...)
## 
## $space$indent_eq_sub
## 
## function (...) 
## indent_eq_sub(indent_by = indent_by, ...)
## 
## $space$indent_without_paren
## 
## function (...) 
## indent_without_paren(indent_by = indent_by, ...)
## 
## $space$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
## }
## 
## 
## 
## $space$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
## }
## 
## 
## 
## $space$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
## }
## 
## 
## 
## $space$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)
##     pd_flat
## }
## 
## 
## 
## $space$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
##     pd_flat
## }
## 
## 
## 
## $space$style_space_around_math_token
## 
## function (...) 
## style_space_around_math_token(strict, math_token_spacing$zero, 
##     math_token_spacing$one, ...)
## 
## $space$style_space_around_tilde
## 
## function (...) 
## style_space_around_tilde(strict = strict, ...)
## 
## $space$spacing_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
##     pd_flat
## }
## 
## 
## 
## $space$spacing_around_comma
## function (pd_flat) 
## {
##     comma_after <- (pd_flat$token == "','") & (pd_flat$newlines == 
##         0L)
##     pd_flat$spaces[comma_after] <- 1L
##     pd_flat
## }
## 
## 
## 
## $space$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
## }
## 
## 
## 
## $space$remove_space_after_excl
## function (pd_flat) 
## {
##     excl <- (pd_flat$token == "'!'") & (pd_flat$token_after != 
##         "'!'") & (pd_flat$newlines == 0L)
##     pd_flat$spaces[excl] <- 0L
##     pd_flat
## }
## 
## 
## 
## $space$set_space_after_bang_bang
## function (pd_flat) 
## {
##     last_bang <- (pd_flat$token == "'!'") & (pd_flat$token_after != 
##         "'!'") & (pd_flat$newlines == 0L) & (pd_flat$token_before == 
##         "'!'")
##     pd_flat$spaces[last_bang] <- 0L
##     pd_flat
## }
## 
## 
## 
## $space$remove_space_before_dollar
## function (pd_flat) 
## {
##     dollar_after <- (pd_flat$token == "'$'") & (pd_flat$lag_newlines == 
##         0L)
##     dollar_before <- lead(dollar_after, default = FALSE)
##     pd_flat$spaces[dollar_before] <- 0L
##     pd_flat
## }
## 
## 
## 
## $space$remove_space_after_fun_dec
## function (pd_flat) 
## {
##     fun_after <- (pd_flat$token == "FUNCTION") & (pd_flat$lag_newlines == 
##         0L)
##     pd_flat$spaces[fun_after] <- 0L
##     pd_flat
## }
## 
## 
## 
## $space$remove_space_around_colons
## function (pd_flat) 
## {
##     one_two_or_three_col_after <- pd_flat$token %in% c("':'", 
##         "NS_GET_INT", "NS_GET")
##     one_two_or_three_col_before <- lead(one_two_or_three_col_after, 
##         default = FALSE)
##     col_around <- one_two_or_three_col_before | one_two_or_three_col_after
##     pd_flat$spaces[col_around & (pd_flat$newlines == 0L)] <- 0L
##     pd_flat
## }
## 
## 
## 
## $space$start_comments_with_space
## 
## function (...) 
## start_comments_with_space(force_one = start_comments_with_one_space, 
##     ...)
## 
## $space$remove_space_after_unary_pm_nested
## function (pd) 
## {
##     if (any(pd$token[1] %in% c("'+'", "'-'"))) {
##         pd$spaces[1] <- 0L
##     }
##     pd
## }
## 
## 
## 
## $space$spacing_before_comments
## function (pd_flat) 
## {
##     comment_after <- (pd_flat$token == "COMMENT") & (pd_flat$lag_newlines == 
##         0L)
##     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
## }
## 
## 
## 
## $space$set_space_between_levels
## function (pd_flat) 
## {
##     if (pd_flat$token[1] %in% c("FUNCTION", "IF", "WHILE")) {
##         index <- pd_flat$token == "')'" & pd_flat$newlines == 
##             0L
##         pd_flat$spaces[index] <- 1L
##     }
##     else if (pd_flat$token[1] == "FOR") {
##         index <- 2
##         pd_flat$spaces[index] <- 1L
##     }
##     pd_flat
## }
## 
## 
## 
## $space$set_space_between_eq_sub_and_comma
## function (pd) 
## {
##     op_before <- which(pd$token == "EQ_SUB" & lead(pd$token == 
##         "','"))
##     pd$spaces[op_before] <- 1L
##     pd
## }
## 
## 
## 
## 
## $token
## $token$fix_quotes
## function (pd_flat) 
## {
##     str_const <- pd_flat$token == "STR_CONST"
##     str_const_change <- grepl("^'([^\"]*)'$", pd_flat$text[str_const])
##     pd_flat$text[str_const][str_const_change] <- vapply(lapply(pd_flat$text[str_const][str_const_change], 
##         parse_text), deparse, character(1L))
##     pd_flat
## }
## 
## 
## 
## $token$force_assignment_op
## function (pd) 
## {
##     to_replace <- pd$token == "EQ_ASSIGN"
##     pd$token[to_replace] <- "LEFT_ASSIGN"
##     pd$text[to_replace] <- "<-"
##     pd
## }
## 
## 
## 
## $token$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
## }
## 
## 
## 
## $token$add_brackets_in_pipe
## function (pd) 
## {
##     is_pipe <- pd$token == "SPECIAL-PIPE"
##     reduce(which(is_pipe), add_brackets_in_pipe_one, .init = pd)
## }
## 
## 
## 
## $token$remove_terminal_token_before_and_after
## function (pd_flat) 
## {
##     pd_flat$token_before <- NULL
##     pd_flat$token_after <- NULL
##     pd_flat
## }
## 
## 
## 
## $token$wrap_if_else_multi_line_in_curly
## function (pd, indent_by = 2) 
## {
##     if (is_cond_expr(pd)) {
##         pd <- pd %>% wrap_if_multiline_curly(indent_by, space_after = ifelse(contains_else_expr(pd), 
##             1, 0)) %>% wrap_else_multiline_curly(indent_by, space_after = 0)
##     }
##     pd
## }
## 
## 
## 
## 
## $indention
## $indention$update_indention_ref_fun_dec
## function (pd_nested) 
## {
##     if (pd_nested$token[1] == "FUNCTION") {
##         seq <- seq2(3, nrow(pd_nested) - 2)
##         pd_nested$indention_ref_pos_id[seq] <- pd_nested$pos_id[2]
##     }
##     pd_nested
## }
## 
## 
## 
## 
## $use_raw_indention
## [1] FALSE
## 
## $reindention
## $reindention$indention
## [1] 0
## 
## $reindention$comments_only
## [1] TRUE
purrr::modify_depth(transformers, 0, names)
## $initialize
## [1] "initialize"
## 
## $line_break
## [1] "line_break"
## 
## $space
## [1] "space"
## 
## $token
## [1] "token"
## 
## $indention
## [1] "indention"
## 
## $use_raw_indention
## [1] "use_raw_indention"
## 
## $reindention
## [1] "reindention"

Spotted the rule we want to get rid of? It’s under token and it’s called
force_assignment_op. I agree, we could have chosen a better name. If you are
not sure if you can guess from the name of the rule what it does you can also
have a look at the function declaration of this (unexported) function.

styler:::force_assignment_op
## function (pd) 
## {
##     to_replace <- pd$token == "EQ_ASSIGN"
##     pd$token[to_replace] <- "LEFT_ASSIGN"
##     pd$text[to_replace] <- "<-"
##     pd
## }
## 
## 

Next, you simply set that element to NULL.

transformers$token$force_assignment_op <- NULL

And you can use the modified transformer list as input to style_text()

style_text("string = 'hi there'", transformers = transformers)
string = "hi there"

That’s it. Note that the transformer functions and how they are returned by
tidyverse_style() is not part of the exposed API. This means that the order,
the naming etc. may change. For example, I only recently spotted that the rule
to remove quotes (fix_quotes)is in the category space, which is clearly
wrong and I think I will move it over to token in a future release of styler.

Some other rules and their tranformers

  • You don’t like multi-line ifelse statements getting wrapped around curly
    braces: transformers$token$wrap_if_else_multi_line_in_curly.

  • You don’t like mutli-line calls to be broken before the first named argument:
    transformers$line_break$set_line_break_after_opening_if_call_is_multi_line
    (interacting with
    transformers$line_break$set_line_break_before_closing_call).

  • You don’t like the line being broken after the pipe:
    transformers$line_break$add_line_break_after_pipe

  • You don’t like single quotes to be replaced by double quotes:
    transformers$space$fix_quotes.

  • You don’t like comments to start with one space:
    transformers$space$start_comments_with_space

I think you get the idea. I nevertheless recommend using the tidyverse style
guide
as is since

  • it is a well-established, thought-through style.

  • using a consistent style (no matter which) reduces fraction in the community.

In case you want to add a custom rule, the vignette Customizing
styler
is still the
way to go. If you have questions, don’t hesitate to post on Stackoverflow or
leave a comment below.


  1. One example is math_token_spacing. It requires an input that is typically easiest created with another function, e.g. specify_math_token_spacing() [return]

To leave a comment for the author, please follow the link and comment on their blog: Lorenz Walthert.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: Data science, Big Data, R jobs, visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.

Search R-bloggers

Sponsors

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)