-
Notifications
You must be signed in to change notification settings - Fork 73
Fix token insertion / comment interaction #279
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
Fix token insertion / comment interaction #279
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. The examples look good, I'll try to devise a comprehensive set of examples.
R/indent.R
Outdated
has_if_without_curly <- | ||
pd$token[1] %in% c("IF", "WHILE") && pd$child[[5]]$token[1] != "'{'" | ||
pd$token[1] %in% c("IF", "WHILE") && pd$child[[expr_after_if_while]]$token[1] != "'{'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "WHILE"
here seems inconsistent with the function name.
R/rules-other.R
Outdated
} | ||
|
||
|
||
wrap_if_else_multi_line_in_curly_since_is_if_expr <- function(pd, indent_by = 2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand the function name when skimming through it. Maybe extract wrap_if_multi_line_in_curly()
and wrap_else_multi_line_in_curly()
and then write
wrap_if_else_multi_line_in_curly <- function(pd, indent_by = 2) {
if (is_cond_expr(pd)) {
pd <- wrap_if_multi_line_in_curly(pd, indent_by)
pd <- wrap_else_multi_line_in_curly(pd, indent_by)
}
pd
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that makes sense. It's just that I thought it was simple but it turned out to be quite complicated with all the edge cases, so I guess now that it works we can refactor a bit.
This generates a full set of test cases. Do we need newlines to make sure the transformer is triggered? gen <- function(x) {
if (length(x) == 0) ""
else {
c(
paste0(x[1], gen(x[-1])),
paste0(x[1], " # comment\n", paste(x[-1], collapse = ""))
)
}
}
collapse <- function(x) paste(x, collapse = "\n\n")
cat(collapse(gen(c("if", "(", "TRUE", ")", "NULL"))))
#> if(TRUE)NULL
#>
#> if(TRUE)NULL # comment
#>
#>
#> if(TRUE) # comment
#> NULL
#>
#> if(TRUE # comment
#> )NULL
#>
#> if( # comment
#> TRUE)NULL
#>
#> if # comment
#> (TRUE)NULL
cat(collapse(gen(c("if", "(", "TRUE", ")", "NULL", " else", " NULL"))))
#> if(TRUE)NULL else NULL
#>
#> if(TRUE)NULL else NULL # comment
#>
#>
#> if(TRUE)NULL else # comment
#> NULL
#>
#> if(TRUE)NULL # comment
#> else NULL
#>
#> if(TRUE) # comment
#> NULL else NULL
#>
#> if(TRUE # comment
#> )NULL else NULL
#>
#> if( # comment
#> TRUE)NULL else NULL
#>
#> if # comment
#> (TRUE)NULL else NULL
cat(collapse(gen(c("if", "(", "TRUE", ")", "NULL", " else", " if", "(", "FALSE", ")", "NULL", " else", " NULL"))))
#> if(TRUE)NULL else if(FALSE)NULL else NULL
#>
#> if(TRUE)NULL else if(FALSE)NULL else NULL # comment
#>
#>
#> if(TRUE)NULL else if(FALSE)NULL else # comment
#> NULL
#>
#> if(TRUE)NULL else if(FALSE)NULL # comment
#> else NULL
#>
#> if(TRUE)NULL else if(FALSE) # comment
#> NULL else NULL
#>
#> if(TRUE)NULL else if(FALSE # comment
#> )NULL else NULL
#>
#> if(TRUE)NULL else if( # comment
#> FALSE)NULL else NULL
#>
#> if(TRUE)NULL else if # comment
#> (FALSE)NULL else NULL
#>
#> if(TRUE)NULL else # comment
#> if(FALSE)NULL else NULL
#>
#> if(TRUE)NULL # comment
#> else if(FALSE)NULL else NULL
#>
#> if(TRUE) # comment
#> NULL else if(FALSE)NULL else NULL
#>
#> if(TRUE # comment
#> )NULL else if(FALSE)NULL else NULL
#>
#> if( # comment
#> TRUE)NULL else if(FALSE)NULL else NULL
#>
#> if # comment
#> (TRUE)NULL else if(FALSE)NULL else NULL |
Ok, thanks for generating the test cases. I will incorporate them in a new test and try to ensure they work. |
Codecov Report
@@ Coverage Diff @@
## master #279 +/- ##
==========================================
- Coverage 92.17% 90.49% -1.69%
==========================================
Files 28 28
Lines 1188 1262 +74
==========================================
+ Hits 1095 1142 +47
- Misses 93 120 +27
Continue to review full report at Codecov.
|
Ok, now all valid test cases seem to work. There were a few that are invalid, I commented them out. I just c/p your code above, probably should do some documentation at some point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, tests look good. Did you change much to cover all edge cases?
R/indent.R
Outdated
@@ -78,18 +78,21 @@ indent_without_paren_for_while_fun <- function(pd, indent_by) { | |||
#' @describeIn update_indention Is used to indent if and if-else statements. | |||
#' @importFrom rlang seq2 | |||
indent_without_paren_if_else <- function(pd, indent_by) { | |||
expr_after_if_while <- next_non_comment(pd, which(pd$token == "')'")[1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, why is the function called indent_without_paren_if_else()
, and the code is looking for "IF"
and "WHILE"
tokens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's inconsistent. I think it's no longer needed since the function indent_without_paren_for_while_fun()
handles the "WHILE"
case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related: Since we now change
if (TRUE)
return(call(a))
Into
if (TRUE) {
return(call(a))
}
We should probably also change
while (FALSE)
xyz(v)
Into
while (FALSE) {
xyz(v)
}
The same applies to for
. Should we open an issue and postpone it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New issue and postponing sounds great. Do we fix the inconsistency in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, just saw the commit. Thanks!
Maybe we also need to test for |
If you expect the output to be different, this sounds like a good idea. |
Yes it should be quite different since no braces should be added. |
add new trees
33fb7e2
to
b0e0bdd
Compare
Ok, tests added |
R/utils.R
Outdated
#' @param pos The position of the token to start the search from. | ||
extend_if_comment <- function(pd, pos) { | ||
if (pos == nrow(pd)) return(pos) | ||
if (pd[pos + 1,]$token == "COMMENT") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not important here, but in general pd$token[pos + 1]
requires a lot less work. Maybe we can search later for ,]
and , ]
to find this and other instances?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that's for speed I assume...
reprex::reprex_info()
#> Created by the reprex package v0.1.1.9000 on 2017-11-21
library("tidyverse")
#> Loading tidyverse: ggplot2
#> Loading tidyverse: tibble
#> Loading tidyverse: tidyr
#> Loading tidyverse: readr
#> Loading tidyverse: purrr
#> Loading tidyverse: dplyr
#> Conflicts with tidy packages ----------------------------------------------
#> filter(): dplyr, stats
#> lag(): dplyr, stats
tbl <- as_tibble(mtcars)
microbenchmark::microbenchmark(tbl$cyl[1], tbl[1, ]$cyl)
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> tbl$cyl[1] 47.232 50.143 62.46642 60.7610 63.6915 190.264 100
#> tbl[1, ]$cyl 254.001 298.076 569.41824 318.3655 338.8025 13161.350 100
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked and I found , ]$
and ,]$
only twice and I changed it as part of this PR to your suggested style.
Not sure whether you spotted that but because of the structure of the nested parse table, end-of-expression comments are located one level up in the nest, so it's impossible to include them within the braces I think. if (TRUE)
4 else
44 # comment Becomes if (TRUE) {
4
} else {
44
} # comment Instead of if (TRUE) {
4
} else {
44 # comment
} Here you see why: reprex::reprex_info()
#> Created by the reprex package v0.1.1.9000 on 2017-11-21
code <-
"if (TRUE)
4 else
44 # "
styler:::create_tree(code)
#> levelName
#> 1 ROOT (token: short_text [lag_newlines/spaces] {pos_id})
#> 2 ¦--expr: [0/1] {1}
#> 3 ¦ ¦--IF: if [0/1] {2}
#> 4 ¦ ¦--'(': ( [0/0] {3}
#> 5 ¦ ¦--expr: [0/0] {5}
#> 6 ¦ ¦ °--NUM_CONST: TRUE [0/0] {4}
#> 7 ¦ ¦--')': ) [0/4] {6}
#> 8 ¦ ¦--expr: [1/1] {8}
#> 9 ¦ ¦ °--NUM_CONST: 4 [0/0] {7}
#> 10 ¦ ¦--ELSE: else [0/0] {9}
#> 11 ¦ °--expr: [1/0] {11}
#> 12 ¦ °--NUM_CONST: 44 [0/0] {10}
#> 13 °--COMMENT: # [0/0] {12}
styler::style_text(code)
#> if (TRUE) {
#> 4
#> } else {
#> 44
#> } # |
I'm fine with putting the comment after the terminal brace. |
- Adapt documentation (#290). - Add roundtrip (#287). - Fix AppVeyor builds. - Fix token insertion / comment interaction (#279). - Clarify labelling strategy (#285). - Fixing and extending Rstudioaddins (#283). - Fix eq assign parsing (#276). - style_files -> vectorized style_file (#273). - Refactoring (#270). - Fix CI (#275). - Fix covr (#274). - Renaming files (#271). - Handle styling of an unsaved active file (#243). - Test R 3.1 and R 3.2 (#249). - Allow empty {} without line break (#261). - Wrap expr in expr before enclosing with curly braces (#263). - Avoid checking for hard-coded dot (#262). - Account for dependency renaming (utf8 changed to enc) (#264). - Indention of function declaration and closing braces (#260). - Only remove line break before closing with strict option (#252).
Intended to fix #278.
wrap_if_else_multi_line_in_curly()
now simply forwards towrap_if_else_multi_line_in_curly_since_is_if_expr()
if the expression contains an if-clause. This should decrease complexity for the programmer.wrap_if_else_multi_line_in_curly_since_is_if_expr()
callswrap_subexpr_in_curly()
to wrap the relevant sub-expression into curly braces for both the if-clause and the else or else-if clause.@krlmlr can you maybe add other test cases if I missed any?