Skip to content

Commit

Permalink
wrap lines containing magrittr operators such as %>% (#68)
Browse files Browse the repository at this point in the history
first, change `%>%` to `%>       %` in mask_comments(), i.e., add a number of spaces before the closing %, so that deparse() can break the line after %; then remove the extra spaces in unmask_source()

close #54 and close #62

Co-authored-by: Yihui Xie <[email protected]>
  • Loading branch information
edlee123 and yihui authored Mar 15, 2021
1 parent ff21861 commit 5de39f1
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 4 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
CHANGES IN formatR VERSION 1.9

NEW FEATURES

o Lines will be wrapped after operators `%>%`, `%T%`, `%$%`, and `%<>%` now
(thanks, @g4challenge #54, @jzelner #62, @edlee123 #68).

CHANGES IN formatR VERSION 1.8

Expand Down
11 changes: 8 additions & 3 deletions R/tidy.R
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ tidy_source = function(
n2 = attr(regexpr('\n*$', one), 'match.length')
}
on.exit(.env$line_break <- NULL, add = TRUE)
if (comment) text = mask_comments(text, width.cutoff, blank, wrap)
# insert enough spaces into infix operators such as %>% so the lines can be
# broken after the operators
spaces = paste(rep(' ', max(10, width.cutoff)), collapse = '')
if (comment) text = mask_comments(text, width.cutoff, blank, wrap, spaces)
text.mask = tidy_block(text, width.cutoff, arrow && length(grep('=', text)))
text.tidy = if (comment) unmask_source(text.mask) else text.mask
text.tidy = if (comment) unmask_source(text.mask, spaces) else text.mask
text.tidy = reindent_lines(text.tidy, indent)
if (brace.newline) text.tidy = move_leftbrace(text.tidy)
# restore new lines in the beginning and end
Expand Down Expand Up @@ -104,7 +107,7 @@ tidy_block = function(text, width = getOption('width'), arrow = FALSE) {
}

# Restore the real source code from the masked text
unmask_source = function(text.mask) {
unmask_source = function(text.mask, spaces) {
if (length(text.mask) == 0) return(text.mask)
m = .env$line_break
if (!is.null(m)) text.mask = gsub(m, '\n', text.mask)
Expand All @@ -123,6 +126,8 @@ unmask_source = function(text.mask) {
# remove white spaces on blank lines
text.mask = gsub(blank.comment2, '\\1\\2', text.mask)
text.tidy = gsub(pat.comment, '', text.mask)
# restore infix operators such as %>%
text.tidy = gsub(paste0('(%)(', infix_ops, ')', spaces, '(%)\\s*(\n)'), '\\1\\2\\3\\4', text.tidy)
# inline comments should be terminated by $ or \n
text.tidy = gsub(paste(inline.comment, '(\n|$)', sep = ''), ' \\1\\2', text.tidy)
# the rest of inline comments should be appended by \n
Expand Down
6 changes: 5 additions & 1 deletion R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ replace_assignment = function(exp) {
}

## mask comments to cheat R
mask_comments = function(x, width, keep.blank.line, wrap = TRUE) {
mask_comments = function(x, width, keep.blank.line, wrap = TRUE, spaces) {
d = utils::getParseData(parse_source(x))
if (nrow(d) == 0 || (n <- sum(d$terminal)) == 0) return(x)
d = d[d$terminal, ]
Expand Down Expand Up @@ -69,10 +69,14 @@ mask_comments = function(x, width, keep.blank.line, wrap = TRUE) {
if (blank[i] > 0)
d.text[i] = paste(c(d.text[i], rep(blank.comment, blank[i])), collapse = '\n')
}
# break lines after some infix operators such as %>%
d.text = gsub(paste0('^(%)(', infix_ops, ')(%)$'), paste0('\\1\\2', spaces, '\\3'), d.text)

unlist(lapply(split(d.text, d.line), paste, collapse = ' '), use.names = FALSE)
}

infix_ops = '[>$]|T>|<>'

# no blank lines before an 'else' statement!
move_else = function(x) {
blank = grepl('^\\s*$', x)
Expand Down
19 changes: 19 additions & 0 deletions tests/testit/test-tidy.R
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,22 @@ assert(
'line breaks in strings are preserved instead of being replaced by \\n',
tidy.res(x1) %==% x1
)

# tests for magrittr newlines

x1 = '
iris %>% group_by(Species) %>%
summarize(meanlen = mean(Sepal.Length)) %$% arrange(meanlen) %>%meanlen
'

x2 = '
iris %>%
group_by(Species) %>%
summarize(meanlen = mean(Sepal.Length)) %$%
arrange(meanlen) %>%
meanlen
'

assert('magrittr lines are wrapped after the pipes', {
(paste(tidy.res(x1, indent = 2), collapse = '\n') %==% x2)
})

0 comments on commit 5de39f1

Please sign in to comment.