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

Expression-level on-type-formatting #209

Merged
merged 10 commits into from
Feb 13, 2020

Conversation

renkun-ken
Copy link
Member

@renkun-ken renkun-ken commented Feb 13, 2020

Closes #208

This PR enhances the current implementation of on-type-formatting handler which only tries to format the single-line under editing.

Parsing a short chunk of code without generating source reference is super fast, which allows us to use parse(text = ., keep.source = FALSE) repeatedly to find the parse-able expression before current line to perform formatting.

Currently, only ), ], } and \n are used to trigger on type formatting. The closing brackets are usually with complete expressions before cursor, while the line break can be in the end of either a complete expression (a single line call like foo()) or an incomplete expression to be continued in the next line (typical use case is pipelines ending with ggplot's + or magrittr's %>%)

To handle the case of \n in the end of an incomplete expression, we use a zero strategy, i.e. if the end line is empty or only whitespace, then we replace that line with 0 to make the expression a complete one.

mtcars %>% # hit enter

internally becomes

mtcars %>%
  0

Then we use a backward parsing strategy, i.e., we expand the code chunk backward line by line from the end line on request and try to parse the code. We stop backward parsing when

  • We have at least one expression parsed and
  • We are entering the previous expression.
    • If it is one-line expression, then we got 1 more expression.
    • If it is multi-line expression, then we got no expression

When we find the start line of the expression, and we format the code in the range and cut the zero if used.

Both backward parsing and formatting are called in tryCatchTimeout() which uses setTimeout() to limit the number of seconds to evaluate an expression. We set the timeout of backward parsing to 0.1s and formatting to 1s at the moment.

Now the on-type-formatting works nicely with the following use cases:

1+1 # hit enter to format this line

f(a,b,c) # hit enter to format this line

mtcars %>% # hit enter and we get an indentation

mtcars %>%
  select(a,b, c) %>%
  mutate(x=a + b, y = b - c) %>%
  select(a, b,c) # close the bracket or hit enter

mtcars %>% select(a, b, c) # hit enter anywhere in the middle of the expression to get correct indention

if(x+1){
  y+1
}else{
  z+1
} # hit enter or close this bracket to format the whole if else expression

fun2 <- function(x,y, z) {
  x+y + z # hit enter to format this line
  x+ y +z
  local({
  local({
    x+y+z
    }) # hit enter or close bracket to format this inner local call
  }) # hit enter or close bracket to format both inner and outer local calls
} # hit enter or close bracket to format the entire function

Kapture 2020-02-13 at 21 02 46

R/document.R Show resolved Hide resolved
@renkun-ken
Copy link
Member Author

The dot strategy requires some rework because if under default styler settings,

mtcars %>%
.

becomes

mtcars %>%
  .()

@renkun-ken
Copy link
Member Author

Another thing needs to be fixed is \n before comment, which also needs dot strategy but with an non-whitespace line.

Hit enter before #:

mtcars %>% # a comment

R/utils.R Show resolved Hide resolved
@renkun-ken
Copy link
Member Author

Since . in the end of a pipeline will be replaced with .() so I change it to 0 which won't be replaced and thus easier to work with.

And inserting \n before # in mtcars %>% # a comment is fixed too.

@renkun-ken renkun-ken merged commit 43b38e4 into REditorSupport:master Feb 13, 2020
@paulofelipe
Copy link

It worked perfectly on ubuntu, but I got an error on windows:

  Message: Error: unused argument (whitespace = "\\s")
Call: trimws(last_line, whitespace = "\\s")
Stack trace:
1: paste0(new_text, trimws(last_line, whitespace = "\\s"))
2: on_type_formatting_reply(id, uri, document, point, ch, options)
3: self$deliver(on_type_formatting_reply(id, uri, document, point, 
    ch, options))
4: dispatch(self, id, params)

  Code: -32603 ```

Thanks!

@randy3k
Copy link
Member

randy3k commented Feb 14, 2020

What version of R do you have?

@renkun-ken
Copy link
Member Author

renkun-ken commented Feb 14, 2020

@paulofelipe Didn't notice that trimws got whitespace since R 3.6.0. I'll use alternative ways that is compatible with older versions of R.

See https://cran.r-project.org/doc/manuals/r-devel/NEWS.html.

CHANGES IN R 3.6.0
trimws() gets new optional whitespace argument, allowing more extensive definitions of “space”, such as including Unicode spaces (as wished in PR#17431).

@renkun-ken
Copy link
Member Author

@paulofelipe c3290f0 should fix it.

@paulofelipe
Copy link

It worked! Indeed I'm using and older R version (3.5.1). Thanks!

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

Successfully merging this pull request may close these issues.

Support expression-level on-type-formatting
3 participants