Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format with {styler} after rpp::rpp_to_prod() #12

Open
lorenzwalthert opened this issue Oct 19, 2021 · 0 comments
Open

Format with {styler} after rpp::rpp_to_prod() #12

lorenzwalthert opened this issue Oct 19, 2021 · 0 comments

Comments

@lorenzwalthert
Copy link

lorenzwalthert commented Oct 19, 2021

In this blog post, you explain how to go to prod with rpp::rpp_to_prod():

foo <-              function(x               ) { # !q foo <- Character()? function(x = ?Character()) {
  out <- paste("foo:", x)                        # !q   Character()? out <- paste("foo:", x)
  out
}

I suggest to give the user the opportunity use {styler} with scope = I("spaces") (if they want) to style this text and since the line count should not change, replace all lines that have a comment with # q! with the styled code. Here is a quick implementation:

library(magrittr)

#' Style prod code 
style_prod <- function(text) {
  text <- ensure_last_empty(convert_newlines_to_linebreaks(text))
  text_new <- c(
    as.character(styler::style_text(text, scope = I("spaces"))),
    ""
  )
  if (length(text) != length(text_new)) {
    # TODO EOF
    rlang::warn("You hit a bug in {styler}, not formatting prod code. [more instrucitons]")
    return(text)
  }
  pd <- getParseData(parse(text = text))
  active_tokens <- grepl("^# !q", pd$text) & pd$token == 'COMMENT'
  active_lines <- unique(c(pd$line1[active_tokens], pd$line2[active_tokens]))
  text[active_lines] <- text_new[active_lines]
  text
}



# From {styler}, ensure input has a traling newline so formatting with scope = 'spaces'
# has same line count
ensure_last_empty <- function(x) {
  if (all(x == "")) {
    return("")
  }
  x <- c(x, "", "")
  x <- x[seq(1, length(x) - which(rev(x) != "")[1] + 1L)]
  c(x, rep("", 1))
}

# from {styler}, turn text into a character vector where every element is one
# line, i.e. convert_newlines_to_linebreaks('x\n2') -> c('x', '2')
convert_newlines_to_linebreaks <- function(text) {
  split <- strsplit(text, "\n", fixed = TRUE)
  purrr::map(split, ~ if (identical(.x, character(0))) {
    ""
  } else {
    .x
  }) %>%
    unlist()
}


text <- 'foo <-              function(x               ) { # !q foo <- Character()? function(x = ?Character()) {
  out <- paste("foo:", x)                        # !q   Character()? out <- paste("foo:", x)
  out
}
'
style_prod(text)
#> [1] "foo <- function(x) { # !q foo <- Character()? function(x = ?Character()) {"
#> [2] "  out <- paste(\"foo:\", x) # !q   Character()? out <- paste(\"foo:\", x)" 
#> [3] "  out"                                                                     
#> [4] "}"                                                                         
#> [5] ""

Created on 2021-10-19 by the reprex package (v2.0.1)

Only drawback is that {styler} is slow (but the cache helps in 2nd styling). You could also remove certain elements in the code, e.g. Character()? from the parse data and then collapse it together similar to how styler does it.

getParseData(parse(text = "Character()? x <- 'hi'"))

Anyways, not sure how often rpp::rpp_to_prod() is called, probably not often.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant