-
Notifications
You must be signed in to change notification settings - Fork 73
Style roxygen code examples #381
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
Merged
lorenzwalthert
merged 63 commits into
r-lib:master
from
lorenzwalthert:style-roxygen-code-examples
Jul 27, 2018
Merged
Changes from all commits
Commits
Show all changes
63 commits
Select commit
Hold shift + click to select a range
4f7f128
basic code template for styling roxygen code examples
lorenzwalthert 1d48af4
setting up basic testing
lorenzwalthert 8867e2a
document
lorenzwalthert f00d5ee
explain and refined output so list structure of identify_start* is cl…
lorenzwalthert 548b757
Get started on identification of examples
jonmcalder 691d56d
Refine identification of start/stop
jonmcalder 0365418
remove unused function
lorenzwalthert e095fe5
split odd() and even() up into bare components for recycling.
lorenzwalthert 99629d1
return sequences instead of start / stop
lorenzwalthert 3296417
roxygen mask manipulation.
lorenzwalthert 7c075f2
remove unused top-level interface
lorenzwalthert 8a4377b
use parse_transform_serialize() structure for both r and roxygen
lorenzwalthert cdd4042
fix identify_roxygen example tests
lorenzwalthert 5143b20
document
lorenzwalthert 366b591
capsulate splitting into function
lorenzwalthert 058acbb
complete draft on styling rd, documentation
lorenzwalthert e394852
incorporate styling of roxygen in top-level API
lorenzwalthert fbc4bdb
renaming tests for consistency
lorenzwalthert baa32dc
more tests without dontrun
lorenzwalthert 5dbbe6f
# path renaming
lorenzwalthert c870c07
allow for multiple dashes in test name
lorenzwalthert 25fabf9
fixing serialization and allow for non-dontrun sequence.
lorenzwalthert f8a3b89
style / nicer formatting.
lorenzwalthert c2636ba
always start on 2nd line
lorenzwalthert c0d5330
cannot handle mutliple in files per out file anymore
lorenzwalthert 0a4fca3
# incorporate include_roxygen in top-level API
lorenzwalthert 904469c
document
lorenzwalthert 0994f11
# incorporate [...] top-level API
lorenzwalthert 5fe71d4
Add tests
lorenzwalthert 600e1a3
rename testing because path gets too long otherwise
lorenzwalthert bb12a98
make things work for multiple dontrun segments
lorenzwalthert f71c06c
document
lorenzwalthert 3a0157e
allow brace expression in dontrun
lorenzwalthert 14e6813
add post processing to circumvent parsing bug
lorenzwalthert 78bece2
allow for an example code section to start with dontrun
lorenzwalthert a8e946b
doc
lorenzwalthert 968550b
fixing function declarations
lorenzwalthert b715ed4
document
lorenzwalthert 9a04508
extend dontrun handling to dontshow and donttest
lorenzwalthert b5412a4
add tests
lorenzwalthert 404e7f4
# ns
lorenzwalthert 0cdcefd
superficial self-review
lorenzwalthert 188b405
renaming
lorenzwalthert 7063808
more renaming, section on hierarchy
lorenzwalthert 5184ff3
adapt document test_collection()
lorenzwalthert 688feff
another outsource
lorenzwalthert 79d5f48
WIP that breaks test
lorenzwalthert 1988fca
add roxygen mask at example basis, not segment basis
lorenzwalthert 8aadee0
splitting tests into multiple test_collection() calls
lorenzwalthert 6919175
encoding problems with trees
lorenzwalthert 7385c74
fix problem if example starts on same line as tag
lorenzwalthert 6002d24
closing means closing brace
lorenzwalthert addfe53
line break issues
lorenzwalthert 318df5a
all tests fixed. Two roxygen example sections following each other im…
lorenzwalthert 7bad8b8
tidy up description
lorenzwalthert 549ad34
add test for pipe
lorenzwalthert 1b04b98
gsub instead of sub / name space
lorenzwalthert d7b5fdd
add test that split() does not randomly break at \n
lorenzwalthert 3dab6c2
simplify post-processing
lorenzwalthert cadab0f
low-level exeption handling
lorenzwalthert a6508b7
roxygen
lorenzwalthert 702b96d
again remove \n
lorenzwalthert 47761db
style
lorenzwalthert File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,15 @@ | ||
Package: styler | ||
Title: Non-Invasive Pretty Printing of R Code | ||
Version: 1.0.2.9000 | ||
Date: 2018-06-21 | ||
Authors@R: c(person("Kirill", "Müller", role = c("aut"), email = "[email protected]"), | ||
person("Lorenz", "Walthert", role = c("cre", "aut"), email = "[email protected]")) | ||
Description: | ||
Pretty-prints R code without changing the user's formatting intent. | ||
Imports: | ||
License: GPL-3 | ||
URL: https://github.com/r-lib/styler | ||
BugReports: https://github.com/r-lib/styler/issues | ||
Imports: | ||
backports, | ||
cli, | ||
enc (>= 0.2), | ||
|
@@ -15,24 +19,21 @@ Imports: | |
rlang, | ||
rprojroot, | ||
tibble (>= 1.4.2), | ||
tools, | ||
withr | ||
Suggests: | ||
Suggests: | ||
data.tree, | ||
dplyr, | ||
here, | ||
knitr, | ||
rmarkdown, | ||
rstudioapi, | ||
testthat | ||
License: GPL-3 | ||
VignetteBuilder: knitr | ||
Encoding: UTF-8 | ||
LazyData: true | ||
Date: 2018-06-21 | ||
BugReports: https://github.com/r-lib/styler/issues | ||
URL: https://github.com/r-lib/styler | ||
Roxygen: list(markdown = TRUE, roclets = c("rd", "namespace", "collate", "pkgapi::api_roclet")) | ||
RoxygenNote: 6.0.1 | ||
VignetteBuilder: knitr | ||
Collate: | ||
'addins.R' | ||
'communicate.R' | ||
|
@@ -47,6 +48,10 @@ Collate: | |
'reindent.R' | ||
'token-define.R' | ||
'relevel.R' | ||
'roxygen-examples-add-remove.R' | ||
'roxygen-examples-find.R' | ||
'roxygen-examples-parse.R' | ||
'roxygen-examples.R' | ||
'rules-line-break.R' | ||
'rules-other.R' | ||
'rules-replacement.R' | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#' Remove dont* mask | ||
#' | ||
#' @param roxygen Roxygen code examples that contains a dont* segment only. | ||
#' @keywords internal | ||
#' @importFrom rlang seq2 | ||
remove_dont_mask <- function(roxygen) { | ||
mask <- c( | ||
1L, 2L, if (roxygen[3] == "\n") 3L, last(which(roxygen == "}")) | ||
) %>% sort() | ||
list( | ||
code = roxygen[-mask], mask = paste(roxygen[seq2(1, 2)], collapse = "") | ||
) | ||
} | ||
|
||
remove_blank_lines <- function(code) { | ||
code[code != "\n"] | ||
} | ||
|
||
remove_roxygen_mask <- function(text) { | ||
code_with_header <- gsub(pattern = "^#'\\s*", "", text) | ||
remove_roxygen_header(code_with_header) | ||
} | ||
|
||
#' Remove roxygen header | ||
#' | ||
#' Can't simply remove the element with the regex because it may happen that | ||
#' the roxygen tag is on the same line as its contents start. | ||
#' @examples | ||
#' #' @examples c(1, 2) | ||
#' @keywords internal | ||
remove_roxygen_header <- function(text) { | ||
text <- gsub("^\\s*@examples\\s*", "", text, perl = TRUE) | ||
starts_with_blank <- text[1] == "\n" | ||
c(text[1][!starts_with_blank], text[-1]) | ||
} | ||
|
||
#' @importFrom purrr map_chr | ||
add_roxygen_mask <- function(text) { | ||
c(paste0("#' @examples"), map_chr(text, ~paste0("#' ", .x))) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#' Figure out where code examples start and stop | ||
#' | ||
#' Finds the sequence from start to stop of the lines in `text` that are | ||
#' code examples in roxygen comments. | ||
#' @param text A text consisting of code and/or roxygen comments. | ||
#' @importFrom purrr map_int map2 | ||
#' @importFrom rlang seq2 | ||
#' @keywords internal | ||
identify_start_to_stop_of_roxygen_examples_from_text <- function(text) { | ||
starts <- grep("^#'\\s*@examples", text, perl = TRUE) | ||
stop_candidates <- grep("^[^#]|^#'\\s*@", text, perl = TRUE) | ||
stops <- map_int(starts, match_stop_to_start, stop_candidates) | ||
map2(starts, stops, seq2) | ||
} | ||
|
||
identify_start_to_stop_of_roxygen_examples <- function(path) { | ||
content <- enc::read_lines_enc(path) | ||
identify_start_to_stop_of_roxygen_examples_from_text(content) | ||
} | ||
|
||
#' Match a stop candidate to a start | ||
#' @param start An integer. | ||
#' @param stop_candidates Potential stop candidates. | ||
#' @examples | ||
#' styler:::match_stop_to_start(1, c(3, 4, 5)) | ||
#' @keywords internal | ||
match_stop_to_start <- function(start, stop_candidates) { | ||
min(stop_candidates[stop_candidates > start]) - 1L | ||
} | ||
|
||
#' Find dontrun and friend sequences | ||
#' | ||
#' Returns the indices of the lines that correspond to a `dontrun` or | ||
#' friends sequence. | ||
#' @param bare Bare code. | ||
#' @importFrom purrr map2 map_int | ||
#' @keywords internal | ||
find_dont_seqs <- function(bare) { | ||
dont_openings <- which(bare %in% dont_keywords()) | ||
dont_type <- bare[dont_openings] | ||
dont_closings <- map_int(dont_openings + 1L, find_dont_closings, bare = bare) | ||
map2(dont_openings, dont_closings, seq2) | ||
} | ||
|
||
#' @importFrom rlang seq2 | ||
find_dont_closings <- function(bare, dont_openings) { | ||
opening <- cumsum(bare == "{") | ||
closing <- cumsum(bare == "}") | ||
diff <- opening - closing | ||
level_dont <- diff[dont_openings] | ||
match_closing <- intersect( | ||
seq2(dont_openings + 1L, length(bare)), | ||
which(diff == level_dont - 1L) | ||
)[1] | ||
match_closing + 1L | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#' Parse roxygen comments into text | ||
#' | ||
#' Used to parse roxygen code examples. Removes line break before | ||
#' `\\dontrun{...}` and friends because it does not occurr for segments other | ||
#' than `\\dont{...}` and friends. | ||
#' @param roxygen Roxygen comments. | ||
#' @examples | ||
#' styler:::parse_roxygen(c( | ||
#' "#' @examples", | ||
#' "#' 1+ 1" | ||
#' )) | ||
#' styler:::parse_roxygen(c( | ||
#' "#' @examples 33", | ||
#' "#'1+ 1" | ||
#' )) | ||
#' @keywords internal | ||
parse_roxygen <- function(roxygen) { | ||
parsed <- remove_roxygen_mask(roxygen) %>% | ||
textConnection() %>% | ||
tools::parse_Rd(fragment = TRUE) %>% | ||
as.character() | ||
is_line_break <- parsed[1] == "\n" | ||
c(parsed[1][!is_line_break], parsed[-1]) | ||
} | ||
|
||
#' Changing the line definition | ||
#' | ||
#' Input: New line denoted with `\\n`. Lines can span accross elements. | ||
#' Output: Each element in the vector is one line. | ||
#' | ||
#' @param raw Raw code to post-process. | ||
#' @keywords internal | ||
post_parse_roxygen <- function(raw) { | ||
split <- raw %>% | ||
paste0(collapse = "") %>% | ||
strsplit("\n", fixed = TRUE) | ||
split[[1]] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#' Style a roxygen code example that may contain dontrun and friends | ||
#' | ||
#' Parses roxygen2 comments into code, breaks it into dont* (dontrun, dontest, | ||
#' dontshow) and run sections and processes each segment indicidually using | ||
#' [style_roxygen_example_snippet()]. | ||
#' @inheritParams parse_transform_serialize_r | ||
#' @param example Roxygen example code. | ||
#' @inheritSection parse_transform_serialize_roxygen Hierarchy | ||
#' @importFrom purrr map flatten_chr | ||
#' @keywords internal | ||
style_roxygen_code_example <- function(example, transformers) { | ||
bare <- parse_roxygen(example) | ||
one_dont <- split(bare, factor(cumsum(bare %in% dont_keywords()))) | ||
map(one_dont, style_roxygen_code_example_segment, transformers) %>% | ||
flatten_chr() %>% | ||
add_roxygen_mask() | ||
} | ||
|
||
#' Style a roxygen code example segment | ||
#' | ||
#' A roxygen code example segment corresponds to roxygen example code that | ||
#' contains at most one `\\dontrun{...}` or friends. | ||
#' We drop all newline characters first because otherwise the code segment | ||
#' passed to this function was previously parsed with [parse_roxygen()] and | ||
#' line-breaks in and after the `\\dontrun{...}` are expressed with `"\n"`, which | ||
#' contradicts to the definition used elsewhere in this package, where every | ||
#' element in a vector corresponds to a line. These line-breaks don't get | ||
#' eliminated because they move to the front of a `code_segment` and | ||
#' `style_text("\n1")` gives `"\n1"`, i.e. trailing newlines are not | ||
#' eliminated. | ||
#' @param one_dont Bare R code containing at most one `\\dontrun{...}` or | ||
#' friends. | ||
#' @inheritParams parse_transform_serialize_r | ||
#' @inheritSection parse_transform_serialize_roxygen Hierarchy | ||
#' @importFrom rlang seq2 | ||
#' @importFrom purrr map2 flatten_chr | ||
#' @keywords internal | ||
style_roxygen_code_example_segment <- function(one_dont, transformers) { | ||
if (length(one_dont) < 1L) return(character()) | ||
dont_seqs <- find_dont_seqs(one_dont) | ||
split_segments <- split_roxygen_segments(one_dont, unlist(dont_seqs)) | ||
is_dont <- | ||
seq2(1L, length(split_segments$separated)) %in% split_segments$selectors | ||
|
||
map2(split_segments$separated, is_dont, | ||
style_roxygen_example_snippet, | ||
transformers = transformers | ||
) %>% | ||
flatten_chr() | ||
} | ||
|
||
#' Given a code snippet is dont* or run, style it | ||
#' | ||
#' @param code_snippet A character vector with code to style. | ||
#' @param is_dont Whether the snippet to process is a dontrun, dontshow, | ||
#' donttest segemnt or not. | ||
#' @inheritParams parse_transform_serialize_r | ||
#' @inheritSection parse_transform_serialize_roxygen Hierarchy | ||
#' @keywords internal | ||
style_roxygen_example_snippet <- function(code_snippet, | ||
transformers, | ||
is_dont) { | ||
if (is_dont) { | ||
decomposed <- remove_dont_mask(code_snippet) | ||
code_snippet <- decomposed$code | ||
mask <- decomposed$mask | ||
} | ||
code_snippet <- post_parse_roxygen(code_snippet) %>% | ||
paste0(collapse = "\n") %>% | ||
parse_transform_serialize_r(transformers) | ||
|
||
if (is_dont) { | ||
code_snippet <- c(mask, code_snippet, "}") | ||
} | ||
code_snippet | ||
} | ||
|
||
dont_keywords <- function() { | ||
c("\\dontrun", "\\dontshow", "\\donttest") | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,17 +8,13 @@ | |
#' @param sub_test A regex pattern to further reduce the amount of test files | ||
#' to be tested in the test. `sub_test` must match the beginning of file | ||
#' names in tests/testthat. `NULL` matches all files. | ||
#' @details Each file name that matches `test` and `sub_test` and ends with | ||
#' "-in.R" is considered as an input to test. Its counterpart, | ||
#' the reference to compare it against is the *-out.R file. It is constructed | ||
#' by taking the substring of the *-in.R file before the | ||
#' first dash and adding -out.R. This allows for multiple in.R files to | ||
#' share one out.R file. You could have one_line-out.R as the reference to | ||
#' compare one_line-random-something-stuff-in.R and | ||
#' one_line-random-but-not-so-much-in.R. | ||
#' | ||
#' This also implies that -out.R files cannot have more than one dash in | ||
#' their name, i.e. just the one before out.R. | ||
#' @details | ||
#' Each file name that matches `test` and `sub_test` and ends with | ||
#' "-in.R" is considered as an input to test. Its counterpart, | ||
#' the reference to compare it against is the *-out.R file. It is constructed | ||
#' by taking the substring of the *-in.R file before the | ||
#' last dash and adding -out.R. In contrast to older versions of this | ||
#' function, every *-out.R file has just one in file. | ||
#' @inheritParams transform_and_check | ||
#' @importFrom purrr flatten_chr pwalk map | ||
#' @keywords internal | ||
|
@@ -68,7 +64,7 @@ test_collection <- function(test, sub_test = NULL, | |
#' "path/to/file/first-extended-in.R")) | ||
#' @keywords internal | ||
construct_out <- function(in_paths) { | ||
gsub("\\-.*([.]R(?:|md))$", "\\-out\\1", in_paths) | ||
gsub("\\-in([.]R(?:|md))$", "\\-out\\1", in_paths) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to adapt documentation for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
||
#' Construct paths of a tree object given the paths of *-in.R files | ||
|
@@ -125,10 +121,7 @@ transform_and_check <- function(in_item, out_item, | |
immediate. = TRUE, call. = FALSE | ||
) | ||
} else { | ||
message( | ||
in_name, " was identical to ", out_name, | ||
immediate. = TRUE, call. = FALSE | ||
) | ||
message(in_name, " was identical to ", out_name) | ||
} | ||
} | ||
|
||
|
@@ -165,7 +158,7 @@ style_empty <- function(text) { | |
reindention = specify_reindention(), | ||
NULL | ||
) | ||
transformed_text <- parse_transform_serialize(text, transformers) | ||
transformed_text <- parse_transform_serialize_r(text, transformers) | ||
transformed_text | ||
} | ||
|
||
|
@@ -184,7 +177,7 @@ style_op <- function(text) { | |
NULL | ||
) | ||
|
||
transformed_text <- parse_transform_serialize(text, transformers) | ||
transformed_text <- parse_transform_serialize_r(text, transformers) | ||
transformed_text | ||
} | ||
|
||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Are the terminal newlines part of the contents of a character vector? In
readLines()
(and many other functions), each vector element is a line, but the newlines aren't part of it. This design is surprising at least.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, they are at that stage, because the output of
tools::parse_Rd()
uses the following definition to represent line breaks. Using\n
. It's not the case that every element in the vector corresponds to one line:Created on 2018-07-15 by the reprex package (v0.2.0).
As noted above, how we work around line breaks is a bit of a hack. Need to figure out better post-processing to map this definition to the one we use in styler, i.e.
\n
.And
rd
does not match either of them, it's a mixture: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.
Does this help get rid of trailing newlines at a very early stage?
Uh oh!
There was an error while loading. Please reload this page.
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.
Not sure because we must keep the
dontrun
. We just want the following mapping:We can only post-process the code right before we style it, because we use the fact that closing lines are always the only element in the output vector of
tools::parse_Rd()
anddontrun
is always isolated etc. to perform the segmentation and create code snippets as described under@section Hierarchy:
inR/transform-files.R
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.
Not to confuse anywone, just a note for myself. A problem might be that
strsplit
does not split if the character is already the last in the string.Created on 2018-07-15 by the reprex
package (v0.2.0).