From 8db36861ce769a5b968a0c70b89f9cb72142b59e Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 15:10:10 -0400 Subject: [PATCH 01/71] Awesome progress on roxy parsing! --- playground/roxy-comment-parsing.R | 183 ++++++++++++++++++++++++++++++ playground/roxygen2-test.R | 29 ----- 2 files changed, 183 insertions(+), 29 deletions(-) create mode 100644 playground/roxy-comment-parsing.R delete mode 100644 playground/roxygen2-test.R diff --git a/playground/roxy-comment-parsing.R b/playground/roxy-comment-parsing.R new file mode 100644 index 0000000..ab7043c --- /dev/null +++ b/playground/roxy-comment-parsing.R @@ -0,0 +1,183 @@ +# pkgload::load_all() + +#' Extract roxygen tag +#' +#' Tell me what this does +#' +#' # Section to extract +#' +#' Well this is a section +#' +#' @md +#' @param file A file +#' @returns A named list with name = file:line, and element is the section title +extract_roxygen_tag_location <- function(file = testthat::test_path("_ref", "test-roxygen.R"), tag) { + suppressMessages(aa <- roxygen2::parse_file(file)) + # browser() + # don't parse noRd tags + aa <- purrr::discard(aa, \(x) roxygen2::block_has_tags(x, "noRd")) + # Return early if no roxy tags + if (length(aa) == 0) { + return(character(0L)) + } + # browser() + pos <- purrr::map(aa, \(x) roxygen2::block_get_tags(x, tags = tag)) + if (all(lengths(pos) == 0)) { + return(character(0)) + } + aa <- aa[lengths(pos) > 0] + pos <- purrr::list_flatten(pos) + objects <- purrr::map( + aa, + \(x) x$object$topic + ) + if (any(lengths(objects) == 0)) { + name_tag <- purrr::map( + aa, + \(x) roxygen2::block_get_tag_value(x, "name") + ) + for (i in seq_along(objects)) { + if (is.null(objects[[i]])) { + if (!is.null(name_tag[[i]])) { + objects[[i]] <- name_tag[[i]] + } else { + objects[[i]] <- "no-topic" + } + } + } + if (any(lengths(objects) == 0)) { + # should not happen. I chose "no-topic" instead. + cli::cli_abort("Could not resolve object or topic names.") + } + } + + + # browser() + pos <- purrr::set_names(pos, file) + + # browser() + val <- withCallingHandlers( + purrr::map2(pos, objects, \(x, obj_name) { + el <- x$val + el_has_names <- !is.null(names(el)) + + if (length(el) == 1 && !el_has_names) { + el <- paste0( + el, "____", obj_name + ) + names(el) <- x$line + return(el) + } + if (tag %in% c("description", "details") && !el_has_names) { + # TODO when stable delete + # print(x$val) + # print(el_has_names) + # cli::cli_inform("return early (no headings)") + return(NULL) + } + # use raw instead + lines <- stringr::str_split_1(x$raw, "\n") + # browser() + keep <- which(o_is_section_title(lines)) + + if (length(keep) == 0L) { + # TODO Delete when stable debugging + # cli::cli_inform(" No section title detected") + return(NULL) + } + # line position. + line_pos <- x$line + seq_along(lines) - 1L + final_lines_to_include <- lines[keep] + # Will not make this transformation and will consider roxygen comments to be + # final_lines_to_include <- stringr::str_remove(final_lines_to_include, "^#+\\s") + + final_lines_to_include <- paste0(final_lines_to_include, "____", obj_name) + names(final_lines_to_include) <- line_pos[keep] + # TODO Delete when stable for debugging + # if (length(final_lines_to_include) != 1) { + # cli::cli_warn("el resulted to {.val {final_lines_to_include}}", "using first element for now") + # } + final_lines_to_include + }), + error = function(e) { + cli::cli_abort( + "For tag = {tag}, obj_name = {objects}, wrong size, should be {length(pos)}" + ) + + }) + + # rlang::set_names(val, nam) + # merge line number and file name + # I wonder if purrr make it easy to do tidyverse/purrr#1064 + # list(x = c(el1 = 1), x = c(el2 = 2, el3 = 3)) + #> list(x = c(el1 = 1, el2 = 2, el3 = 3)) + val <- val |> purrr::compact() + + if (FALSE) { + val <- unlist(val) + names(val) <- stringr::str_replace(names(val), "\\.(\\d+)$", ":\\1") + } else { + # purrr::list_flatten( + # name_spec = "{outer}:{inner}" + # ) + val <- vctrs::list_unchop( + val, + name_spec = "{outer}:{inner}", + ptype = "character" + ) + } + + + # hack to keep tag + if (length(val) > 0) { + names(val) <- paste0(names(val), "____", tag) + } + val +} + +titles_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "title")) + +section_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "section")) +subsection_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "subsection")) + +desc_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "description")) + +details_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "details")) + +family_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "family")) +concept_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "concept")) +roxy_parsed <- vctrs::vec_c( + titles_list, + section_list, + desc_list, + details_list, + family_list, + concept_list, + .name_spec = "{outer}:{inner}", +) |> + vctrs::list_unchop( + name_spec = "{inner}" + ) |> + tibble::enframe() |> + tidyr::separate_wider_delim( + cols = name, + names = c("file", "tag"), + delim = "____" + ) +roxy_parsed |> + tidyr::separate_wider_delim( + cols = value, + delim = "____", + names = c("outline_el", "topic"), + ) |> + tidyr::separate_wider_delim( + file, + delim = ":", + names = c("file", "line") + ) |> + dplyr::relocate( + topic, outline_el, file, line, tag + ) |> + dplyr::mutate( + is_md = tag %in% c("subsection", "details", "description", "section") + ) diff --git a/playground/roxygen2-test.R b/playground/roxygen2-test.R deleted file mode 100644 index 71ffe07..0000000 --- a/playground/roxygen2-test.R +++ /dev/null @@ -1,29 +0,0 @@ -# pkgload::load_all() - -#' Extract roxygen tag -#' -#' Tell me what this does -#' -#' # Section to extract -#' -#' Well this is a section -#' -#' @md -#' @param file A file -#' @returns A named list with name = file:line, and element is the section title -extract_roxygen_tag_location <- function(file = c("R/proj-list.R"), tag) { - aa <- roxygen2::parse_file(file) - - pos <- purrr::map(aa, \(x) roxygen2::block_get_tags(x, tags = tag)) |> - purrr::list_flatten() - nam <- purrr::map(pos, \(x) paste0(x$file, ":", x$line)) - val <- purrr::map_chr(pos, "val") - rlang::set_names(val, nam) -} -titles_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "title")) |> - unlist() - -section_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "section")) |> - unlist() - -roxygen2::parse_file("R/rename-files.R") From cdd9c7076900ddaf0e13505f73ce4fe71da1ac02 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 15:29:55 -0400 Subject: [PATCH 02/71] Speed things up! --- playground/roxy-comment-parsing.R | 75 +++++++++++++++++++------------ 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/playground/roxy-comment-parsing.R b/playground/roxy-comment-parsing.R index ab7043c..cd70a19 100644 --- a/playground/roxy-comment-parsing.R +++ b/playground/roxy-comment-parsing.R @@ -11,17 +11,12 @@ #' @md #' @param file A file #' @returns A named list with name = file:line, and element is the section title -extract_roxygen_tag_location <- function(file = testthat::test_path("_ref", "test-roxygen.R"), tag) { - suppressMessages(aa <- roxygen2::parse_file(file)) - # browser() - # don't parse noRd tags - aa <- purrr::discard(aa, \(x) roxygen2::block_has_tags(x, "noRd")) - # Return early if no roxy tags - if (length(aa) == 0) { - return(character(0L)) - } +extract_roxygen_tag_location <- function(file = roxygen2::parse_file(testthat::test_path("_ref", "test-roxygen.R")), tag) { + # suppressMessages(aa <- roxygen2::parse_file(file)) # browser() + aa <- file pos <- purrr::map(aa, \(x) roxygen2::block_get_tags(x, tags = tag)) + # browser() if (all(lengths(pos) == 0)) { return(character(0)) } @@ -53,7 +48,7 @@ extract_roxygen_tag_location <- function(file = testthat::test_path("_ref", "tes # browser() - pos <- purrr::set_names(pos, file) + pos <- purrr::set_names(pos, pos$file) # browser() val <- withCallingHandlers( @@ -135,30 +130,52 @@ extract_roxygen_tag_location <- function(file = testthat::test_path("_ref", "tes val } -titles_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "title")) +extract_roxygen_tag_location(tag = "title") -section_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "section")) -subsection_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "subsection")) +if (!exists("parsed_files")) { + # This is what takes the longest. + # only need to run this once. + parsed_files <- purrr::map(fs::dir_ls("R"), roxygen2::parse_file) +} -desc_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "description")) -details_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "details")) +join_roxy_fun <- function(file) { + # don't parse noRd tags + parsed_files <- purrr::discard(file, \(x) roxygen2::block_has_tags(x, "noRd")) + # Return early if no roxy tags + if (length(parsed_files) == 0) { + return(character(0L)) + } + titles_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "title")) + + section_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "section")) + subsection_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "subsection")) + + desc_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "description")) -family_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "family")) -concept_list <- purrr::map(fs::dir_ls("R"), \(x) extract_roxygen_tag_location(x, tag = "concept")) -roxy_parsed <- vctrs::vec_c( - titles_list, - section_list, - desc_list, - details_list, - family_list, - concept_list, - .name_spec = "{outer}:{inner}", -) |> - vctrs::list_unchop( - name_spec = "{inner}" + details_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "details")) + + family_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "family")) + concept_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "concept")) + roxy_parsed <- vctrs::vec_c( + titles_list, + section_list, + desc_list, + details_list, + family_list, + concept_list, + .name_spec = "{outer}:{inner}", ) |> - tibble::enframe() |> + vctrs::list_unchop( + name_spec = "{outer}:{inner}" + ) |> + tibble::enframe() + roxy_parsed +} + + +roxy_parsed <- parsed_files |> + join_roxy_fun() |> tidyr::separate_wider_delim( cols = name, names = c("file", "tag"), From d2578be7d7f61b0f203224f985f9d9c241db19dc Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 16:18:54 -0400 Subject: [PATCH 03/71] Tiny modifications to ensure comparison between the other outline files. --- playground/roxy-comment-parsing.R | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/playground/roxy-comment-parsing.R b/playground/roxy-comment-parsing.R index cd70a19..7ca9e5d 100644 --- a/playground/roxy-comment-parsing.R +++ b/playground/roxy-comment-parsing.R @@ -178,7 +178,7 @@ roxy_parsed <- parsed_files |> join_roxy_fun() |> tidyr::separate_wider_delim( cols = name, - names = c("file", "tag"), + names = c("file_line", "tag"), delim = "____" ) roxy_parsed |> @@ -188,13 +188,28 @@ roxy_parsed |> names = c("outline_el", "topic"), ) |> tidyr::separate_wider_delim( - file, + file_line, delim = ":", names = c("file", "line") ) |> + dplyr::mutate( + file = fs::path_real(file) |> as.character(), + file_line = paste0(file, ":", line) + ) |> dplyr::relocate( - topic, outline_el, file, line, tag + file, topic, outline_el, , line, tag ) |> dplyr::mutate( - is_md = tag %in% c("subsection", "details", "description", "section") + is_md = tag %in% c("subsection", "details", "description", "section"), + is_object_title = tag == "title", + file_ext = "R", + tile_el = NA_character_, + title_el_line = NA_integer_, + is_news = FALSE, + is_test_file = FALSE, + is_snap_file = FALSE, + is_section_title = tag %in% c("section", "subsection") | tag %in% c("details", "description"), + is_saved_doc = TRUE, + has_inline_markup = FALSE # let's not mess with inline markup + ) From a887d761489fd21b630b534b176f0be8c9b9f586 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 16:19:35 -0400 Subject: [PATCH 04/71] Other fixes --- DESCRIPTION | 2 +- R/escape-inline-markup.R | 1 + R/outline.R | 18 ++++++++++++------ TODO.R | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5d9298c..0911389 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -35,8 +35,8 @@ Suggests: curl, gert, gt, - pillar, magick, + pillar, testthat (>= 3.2.1), withr Config/testthat/edition: 3 diff --git a/R/escape-inline-markup.R b/R/escape-inline-markup.R index 378e0f5..f33e140 100644 --- a/R/escape-inline-markup.R +++ b/R/escape-inline-markup.R @@ -122,3 +122,4 @@ replace_r_var <- function(x) { #' #> "{{gt_var}} in {{gt_var}} in gt_var in {.file {gt_var}}." #' escape_markup("{gt_var} in {{gt_var}} in gt_var in {.file {gt_var}}.") #' #> "{{gt_var}} in {{gt_var}} in gt_var in {.file gt_var}." +NULL diff --git a/R/outline.R b/R/outline.R index e23ebb8..4702cfc 100644 --- a/R/outline.R +++ b/R/outline.R @@ -517,7 +517,7 @@ display_outline_element <- function(.data) { (line_id == 1 & !is_todo_fixme & !is_test_name & !is_snap_file) | (is_doc_title & !is_subtitle & !is_snap_file & !is_second_level_heading_or_more) ][1], # take the first element to avoid problems (may be the reason why problems occur) - NA + NA_integer_ ), title_el = outline_el[line_id == title_el_line], .by = "file" @@ -535,9 +535,13 @@ display_outline_element <- function(.data) { y$outline_el <- ifelse(y$has_title_el, NA_character_, y$outline_el) - na_if0 <- function(x) { + na_if0 <- function(x, which) { if (length(x) == 0) { - x <- NA + if (which == "title") { + x <- NA_character_ + } else { + NA_integer_ + } } if (length(x) != 1) { cli::cli_inform("{x} are detected as document title. Internal error") @@ -547,8 +551,8 @@ display_outline_element <- function(.data) { if (!all(is.na(y$title_el))) { y <- dplyr::mutate( y, - title_el = na_if0(title_el[!is.na(title_el)]), - title_el_line = na_if0(title_el_line[!is.na(title_el_line)]), + title_el = na_if0(title_el[!is.na(title_el)], "title"), + title_el_line = na_if0(title_el_line[!is.na(title_el_line)], "line"), .by = "file" ) } @@ -658,7 +662,9 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { .default = paste0("{.run [", text_in_link, "](reuseme::open_rs_doc('", file_path, "'))}") ), rs_version = NULL, - outline_el2 = NULL + outline_el2 = NULL, + condition_to_truncate = NULL, + condition_to_truncate2 = NULL ) |> dplyr::filter(is.na(outline_el) | grepl(pattern, outline_el, ignore.case = TRUE)) } diff --git a/TODO.R b/TODO.R index ecb6042..eb7e79f 100644 --- a/TODO.R +++ b/TODO.R @@ -23,3 +23,4 @@ # TODO [proj_file] to accesss data (return the path in this case?) # TODO [check_referenced_files] doesn't check for {.file R/file.R} # TODO explain rationale behind `work_only`. Suggest to transform to TODO if this item is no longer critical. `work_only` goal is to show you exactly where you need to do work +# TODO [outline] Show function call if exported + not internal + bonus if has family tagf From 0e4f9fc9b46baf7576f71d89b912e4374171cb56 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 18:34:51 -0400 Subject: [PATCH 05/71] Remove cli info --- README.md | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 27aea95..54ea092 100644 --- a/README.md +++ b/README.md @@ -187,27 +187,10 @@ outline #> #> ── `playground/roxygen2-test.R` #> `i` Section to extract -#> -#> ── `R/browse-pkg.R` -#> `i` {package} -#> `i` Vignettes -#> #> ── `R/dplyr-plus.R` dplyr extra -#> `i` in the presence of ties. -#> `i` Use with_ties = FALSE to return exactly n matches -#> `i` Use each = FALSE to have n divided in each place #> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` -#> `i` with dplyr::filter -#> `i` extract the skin_color for C-3PO -#> `i` will return a named vector of mpg (as mtcars has rownames.) -#> `i` Extract hair color for all people #> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` #> `i` summarise with total -#> `i` works with `.by` -#> `i` works with `group_by()` -#> `i` NA all 2s -#> `i` You can actually use dplyr::na_if() in this case -#> `i` NA all 1 and 2 #> #> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- #> `i` Use cases / advantages @@ -219,10 +202,6 @@ outline #> `i` dplyr extensions identity #> `i` helpers #> -#> ── `R/escape-inline-markup.R` -#> `i` example code -#> `i` last instance taken care of with escape_markup with a different strategy -#> #> ── `R/files-conflicts.R` #> `i` TODO insert in either proj_outline, or rename_file- `Done✔?` #> `i` TODO probably needs a `detect_genuine_path()`- `Done✔?` @@ -244,7 +223,6 @@ outline #> `i` TODO structure and summarise information.- `Done✔?` #> #> ── `R/outdated-pkgs.R` -#> `i` All packages are up to date. #> `i` TODO figure out pad :)- `Done✔?` #> #> ── `R/outline-criteria.R` @@ -254,14 +232,11 @@ outline #> `i` it is 'R/outline.R' #> #> ── `R/outline.R` `proj_outline()` -#> `i` Remove todo items -#> `i` interact with data frame -#> `i` These all work on the active file / project or directory. -#> `i` Like proj_switch(), proj_outline() accepts a project #> `i` `file_outline()` #> `i` File outline #> `i` Print method #> `i` Step: tweak outline look as they show +#> `i` TODO reanable cli info- `Done✔?` #> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` #> #> ── `R/proj-list.R` @@ -290,9 +265,6 @@ outline #> ── `R/utils-proj.R` usethis adaptions utils #> `i` Active project / document #> -#> ── `R/utils-write.R` -#> `i` Creating -#> #> ── `R/utils.R` OS utils #> #> ── `tests/testthat/_ref/many-titles.md` The title is the only outline element @@ -374,7 +346,6 @@ outline #> `i` open_rs_doc() errors in non-interactive sessions #> #> ── `tests/testthat/test-outline-criteria.R` Test individual outline elements -#> `i` o_is_cli_info() works #> `i` No outline criteria are untested #> #> ── `tests/testthat/test-outline.R` @@ -394,6 +365,7 @@ outline #> #> ── `tests/testthat/test-use-todo.R` #> `i` Marking TODO as done detects tags +#> `i` todo items are correctly stripped #> #> ── `tests/testthat/test-utils.R` #> `i` Windows is recognized correctly. @@ -407,6 +379,7 @@ outline #> `i` TODO outline make ggtitle work- `Done✔?` #> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` #> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` +#> `i` TODO outline renable cli info.- `Done✔?` #> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` #> `i` TODO outline avoid evaluating in current env.- `Done✔?` #> `i` TODO wrap regexps in functions- `Done✔?` From 1ee6d49cbc88cda6fd949b25985cdf48201f250d Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 18:35:53 -0400 Subject: [PATCH 06/71] Add support for outline-roxy --- DESCRIPTION | 2 + R/outline-criteria.R | 23 +++- .../outline-roxy.R | 130 +++++++++--------- R/outline.R | 3 +- 4 files changed, 92 insertions(+), 66 deletions(-) rename playground/roxy-comment-parsing.R => R/outline-roxy.R (67%) diff --git a/DESCRIPTION b/DESCRIPTION index 0911389..7752d98 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -37,7 +37,9 @@ Suggests: gt, magick, pillar, + roxygen2, testthat (>= 3.2.1), + tidyr, withr Config/testthat/edition: 3 Encoding: UTF-8 diff --git a/R/outline-criteria.R b/R/outline-criteria.R index efadfc6..4b88305 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -124,12 +124,31 @@ define_outline_criteria <- function(.data, print_todo) { x$is_md <- x$is_md & !x$is_news # treating news and other md files differently. x$is_test_file <- grepl("tests/testthat", x$file, fixed = TRUE) x$is_snap_file <- grepl("_snaps", x$file, fixed = TRUE) + x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext) + + if (any(x$is_roxygen_comment)) { + rlang::check_installed(c("roxygen2", "tidyr"), "to create roxygen2 comments outline.") + files_with_roxy_comments <- unique(x[x$is_roxygen_comment, "file", drop = TRUE]) + files_with_roxy_comments <- rlang::set_names(files_with_roxy_comments, files_with_roxy_comments) + parsed_files <- suppressMessages( # roxygen2 messages + purrr::map(files_with_roxy_comments, purrr::safely(roxygen2::parse_file)) + ) + # if roxygen2 cannot parse a file, let's just forget about it. + unparsed_files <- files_with_roxy_comments[which(is.null(parsed_files))] + if (length(unparsed_files) > 0) { + cli::cli_inform("Could not parse roxygen comments in {.file {unparsed_files}}") + } + parsed_files <- purrr::compact(parsed_files) + outline_roxy <- join_roxy_fun(parsed_files) + } else { + outline_roxy <- NULL + } x <- dplyr::mutate( - x, + x |> dplyr::filter(!is_roxygen_comment) |> dplyr::bind_rows(outline_roxy), # Problematic when looking inside functions # maybe force no leading space. - # TODO strip is_cli_info in Package? only valid for EDA + # TODO strip is_cli_info in Package? only valid for EDA (currently not showcased..) is_cli_info = o_is_cli_info(content, is_snap_file, file), is_doc_title = stringr::str_detect(content, "(? 0] + aa <- aa[lengths(pos) > 0L] pos <- purrr::list_flatten(pos) objects <- purrr::map( aa, @@ -130,15 +131,6 @@ extract_roxygen_tag_location <- function(file = roxygen2::parse_file(testthat::t val } -extract_roxygen_tag_location(tag = "title") - -if (!exists("parsed_files")) { - # This is what takes the longest. - # only need to run this once. - parsed_files <- purrr::map(fs::dir_ls("R"), roxygen2::parse_file) -} - - join_roxy_fun <- function(file) { # don't parse noRd tags parsed_files <- purrr::discard(file, \(x) roxygen2::block_has_tags(x, "noRd")) @@ -146,6 +138,12 @@ join_roxy_fun <- function(file) { if (length(parsed_files) == 0) { return(character(0L)) } + if (is.null(names(parsed_files))) { + # browser() + parsed_files <- parsed_files |> purrr::set_names(purrr::map_chr(parsed_files, \(x) x$file)) + # cli::cli_abort("parsed files must be named at this point.") + } + # parsed_files <- set_names(parsed_files, \(x)) titles_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "title")) section_list <- purrr::map(parsed_files, \(x) extract_roxygen_tag_location(x, tag = "section")) @@ -160,56 +158,62 @@ join_roxy_fun <- function(file) { roxy_parsed <- vctrs::vec_c( titles_list, section_list, + subsection_list, desc_list, details_list, family_list, - concept_list, - .name_spec = "{outer}:{inner}", + concept_list#, + #.name_spec = "{outer}:::::{inner}", ) |> vctrs::list_unchop( - name_spec = "{outer}:{inner}" + name_spec = "{outer}.....{inner}" ) |> - tibble::enframe() - roxy_parsed -} - - -roxy_parsed <- parsed_files |> - join_roxy_fun() |> - tidyr::separate_wider_delim( - cols = name, - names = c("file_line", "tag"), - delim = "____" - ) -roxy_parsed |> - tidyr::separate_wider_delim( - cols = value, - delim = "____", - names = c("outline_el", "topic"), - ) |> - tidyr::separate_wider_delim( - file_line, - delim = ":", - names = c("file", "line") - ) |> - dplyr::mutate( - file = fs::path_real(file) |> as.character(), - file_line = paste0(file, ":", line) - ) |> - dplyr::relocate( - file, topic, outline_el, , line, tag - ) |> - dplyr::mutate( - is_md = tag %in% c("subsection", "details", "description", "section"), - is_object_title = tag == "title", - file_ext = "R", - tile_el = NA_character_, - title_el_line = NA_integer_, - is_news = FALSE, - is_test_file = FALSE, - is_snap_file = FALSE, - is_section_title = tag %in% c("section", "subsection") | tag %in% c("details", "description"), - is_saved_doc = TRUE, - has_inline_markup = FALSE # let's not mess with inline markup + tibble::enframe() |> + tidyr::separate_wider_delim( + cols = name, + names = c("file_line", "tag"), + delim = "____" + ) - ) + roxy_parsed <- roxy_parsed |> + tidyr::separate_wider_delim( + cols = value, + delim = "____", + names = c("content", "topic"), + ) + if (!all(grepl("\\.{5}", roxy_parsed$file_line, fixed = F))) { + problems <- which(!grepl("\\.{5}", roxy_parsed$file_line, fixed = F)) + #rowser() + # roxy_parsed + cli::cli_abort("Malformed file line at {problems}.") + } + roxy_parsed <- roxy_parsed |> + tidyr::separate_wider_delim( + file_line, + delim = ".....", + names = c("file", "line") + ) + roxy_parsed |> + dplyr::mutate( + file = fs::path_real(file) |> as.character(), + file_line = paste0(file, ":", line) + ) |> + dplyr::relocate( + file, topic, content, line, tag + ) |> + dplyr::mutate( + is_md = tag %in% c("subsection", "details", "description", "section"), + # content = paste0("#' ", outline_el), + is_object_title = tag == "title", + file_ext = "R", + tile_el = NA_character_, + title_el_line = NA_integer_, + is_news = FALSE, + is_roxygen_comment = TRUE, + is_test_file = FALSE, + is_snap_file = FALSE, + is_section_title = tag %in% c("section", "subsection") | tag %in% c("details", "description"), + is_saved_doc = TRUE, + has_inline_markup = FALSE # let's not mess with inline markup + ) +} diff --git a/R/outline.R b/R/outline.R index 4702cfc..fac4e2d 100644 --- a/R/outline.R +++ b/R/outline.R @@ -540,7 +540,7 @@ display_outline_element <- function(.data) { if (which == "title") { x <- NA_character_ } else { - NA_integer_ + x <- NA_integer_ } } if (length(x) != 1) { @@ -549,6 +549,7 @@ display_outline_element <- function(.data) { x } if (!all(is.na(y$title_el))) { + #browser() y <- dplyr::mutate( y, title_el = na_if0(title_el[!is.na(title_el)], "title"), From 88bb4448fb909fff8519f193a6a99e58507eb8b8 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 18:36:22 -0400 Subject: [PATCH 07/71] Fix color in message --- R/open.R | 3 ++- R/rename-files.R | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/R/open.R b/R/open.R index 2886940..67d3b42 100644 --- a/R/open.R +++ b/R/open.R @@ -255,7 +255,8 @@ active_rs_doc_delete <- function() { if (isTRUE(will_delete_decision)) { cli::cli_inform(c( "v" = "Deleted the active document {.val {elems$rel_path}} because {reasons_deleting}.", - "i" = cli::col_grey("The deleted file {.path {elems$full_path}} contents are returned invisibly in case you need them.") + # FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 + "i" = paste(cli::col_grey("The deleted file"), "{.path {elems$full_path}}", cli::col_grey("contents are returned invisibly in case you need them.")) )) contents <- readLines(elems$full_path, encoding = "UTF-8") fs::file_delete(elems$full_path) diff --git a/R/rename-files.R b/R/rename-files.R index 6bc799c..39ee6d1 100644 --- a/R/rename-files.R +++ b/R/rename-files.R @@ -92,6 +92,7 @@ rename_files2 <- function(old, related_files <- fs::path_filter(related_files, "_snaps/|_book/|_files|_freeze|renv/", invert = TRUE) related_files <- setdiff(related_files, old) if (length(related_files) > 0) { + # maybe would need to normalize path. cli::cli_warn(c( "Other files have a similar pattern", "See {.file {related_files}}", @@ -151,7 +152,7 @@ rename_files2 <- function(old, # check_referenced_files(path = ".", quiet = !verbose) if (interactive() && action != "test") { cli::cli_inform(c( - i = "Call {.run reuseme::check_referenced_files()} to see if dir contains non-existing files." + i = cli::col_grey("Call {.run reuseme::check_referenced_files()} to see if dir contains non-existing files.") )) } return(invisible(new)) From 2f6654071f75cc69ce7226fed086d2ca36cf36ff Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 18:36:54 -0400 Subject: [PATCH 08/71] Add tests to see if it works correctly. --- tests/testthat/_ref/test-roxygen-safeguard.R | 29 ++++++ tests/testthat/_ref/test-roxygen.R | 102 +++++++++++++++++++ tests/testthat/test-outline-roxy.R | 23 +++++ tests/testthat/test-use-todo.R | 2 +- 4 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 tests/testthat/_ref/test-roxygen-safeguard.R create mode 100644 tests/testthat/_ref/test-roxygen.R create mode 100644 tests/testthat/test-outline-roxy.R diff --git a/tests/testthat/_ref/test-roxygen-safeguard.R b/tests/testthat/_ref/test-roxygen-safeguard.R new file mode 100644 index 0000000..3d75371 --- /dev/null +++ b/tests/testthat/_ref/test-roxygen-safeguard.R @@ -0,0 +1,29 @@ +# Test for roxygen parsing for no error ---------------------------------------- +## Use {.file tests/testthat/_ref/test-roxygen.R} for output testing ----------- +# Dump of other things to test for expect_no_error (not necessary to verify) +# Mostly cases inspired by testing in the wild. + +#' Title +#' +#' @examples +#' # Commented code not included +#' +#' ggplot2::ggplot(mtcars) + +#' labs( +#' title = "A title not to be included" +#' ) +#' @export +#' @family a family to include +f_to_be_index_in_outline <- function() { + +} + +#' An S3 method not to be include +#' +#' content +#' @export +f_not_to_index.xml <- function() { + +} + +# Keep this line last: content to test for new roxygen output should be put in test-roxygen.R diff --git a/tests/testthat/_ref/test-roxygen.R b/tests/testthat/_ref/test-roxygen.R new file mode 100644 index 0000000..5e7a4bd --- /dev/null +++ b/tests/testthat/_ref/test-roxygen.R @@ -0,0 +1,102 @@ +# This file is for roxygen comments parsing +## Use {.file tests/testthat/_ref/test-roxygen-safeguard.R} for output testing ----------- + +## Complete block for exported function with headings -------------------------- +# Mix md and `@section` +#' A title to be included +#' +#' A description not to be included +#' +#' ## A second-level heading in description to be included? +#' +#' # A detail first level-heading to be included +#' +#' Content not to be included +#' +#' ## A detail second-level heading to be included +#' +#' Content not to be included2 +#' +#' @section First to be included: +#' +#' Content not to be included +#' +#' @examples +#' # Commented code not included +#' +#' ggplot2::ggplot(mtcars) + +#' labs( +#' title = "A title not to be included" +#' ) +#' @export +#' @family a family to include +f_to_be_index_in_outline <- function() { + +} + +# block not to index ----------------------------------------------------------- +#' A title not to be included (internal function) +#' +#' A description not to include +#' +#' # heading not to be included +#' +#' content +#' @keywords internal +#' @export +f_not_to_index <- function() { + +} +# Topic to index ----------------------------------- + +#' A title to be included +#' +#' A description not to be included +#' +#' ## A second-level heading in description to be included? +#' +#' # A detail first level-heading to be included +#' +#' Content not to be included +#' +#' ## A detail second-level heading to be included +#' +#' Content not to be included2 +#' +#' # First to be included +#' +#' Content not to be included +#' +#' @examples +#' # Commented code not included +#' +#' ggplot2::ggplot(mtcars) + +#' labs( +#' title = "A title not to be included" +#' ) +#' @name topic-name-to-include +#' @family a family to include +NULL + +#' Opens a RStudio project in a new session +#' @description +#' If not specified, will generate hyperlinks that call [usethis::proj_activate()]. +#' `proj_switch()` looks at `options(reuseme.reposdir)`. +#' +#' ## second-level heading in desc +#' +#' content +#' +#' # Details + 2nd level heading +#' content +#' +#' ## second heading +#' +#' Content +#' +# data to index ---------------------------------------------------------------- + +# TODO add data block example to index +# I think I'd want the outline to show as "outline": title (but ctrl + . does a good job for this) (maybe document this) + +# Keep this line last: content to test for edge cases should be put in test-roxygen-safeguard.R diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R new file mode 100644 index 0000000..bbe1f6f --- /dev/null +++ b/tests/testthat/test-outline-roxy.R @@ -0,0 +1,23 @@ +test_that("roxy tags are parsed properly", { + skip_if_not_installed("roxygen2") + skip_if_not_installed("tidyr") + file_to_map <- testthat::test_path("_ref", "test-roxygen.R") + names(file_to_map) <- file_to_map + example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) + + example_parsed |> + extract_roxygen_tag_location("details") |> + expect_no_error() + + expect_no_error(res <- join_roxy_fun(example_parsed)) + + expect_s3_class(res, "tbl_df") +}) + +test_that("roxy tags don't error", { + file_to_map <- testthat::test_path("_ref", "test-roxygen-safeguard.R") + names(file_to_map) <- file_to_map + example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) + expect_no_error(join_roxy_fun(example_parsed)) + +}) diff --git a/tests/testthat/test-use-todo.R b/tests/testthat/test-use-todo.R index 7024b2f..e678b80 100644 --- a/tests/testthat/test-use-todo.R +++ b/tests/testthat/test-use-todo.R @@ -17,7 +17,7 @@ test_that("Marking TODO as done detects tags", { ) }) -test_that("todo items are correctly stripped", { +test_that("TODO items are correctly stripped", { expect_equal( strip_todo_line("2^2 # TODO this"), "2^2" From 401997cecfacfa57be56dd8a68e353a4bf30943c Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 19:27:39 -0400 Subject: [PATCH 09/71] Use `line` instead of line_id --- R/escape-inline-markup.R | 3 ++- R/use-todo.R | 26 +++++++++++++------------- TODO.R | 1 + man/complete_todo.Rd | 4 ++-- tests/testthat/_ref/my-analysis.R | 8 +------- tests/testthat/_snaps/use-todo.md | 4 ++-- tests/testthat/test-use-todo.R | 12 ++++++------ 7 files changed, 27 insertions(+), 31 deletions(-) diff --git a/R/escape-inline-markup.R b/R/escape-inline-markup.R index f33e140..ef2b883 100644 --- a/R/escape-inline-markup.R +++ b/R/escape-inline-markup.R @@ -118,8 +118,9 @@ replace_r_var <- function(x) { #' #' @examples #' replace_r_var("i{gt_var} in {{gt_var}} in gt_var in {.file {gt_var}}.") -#' # last instance taken care of with escape_markup with a different strategy #' #> "{{gt_var}} in {{gt_var}} in gt_var in {.file {gt_var}}." +#' # last instance taken care of with escape_markup with a different strategy #' escape_markup("{gt_var} in {{gt_var}} in gt_var in {.file {gt_var}}.") #' #> "{{gt_var}} in {{gt_var}} in gt_var in {.file gt_var}." +#' NULL diff --git a/R/use-todo.R b/R/use-todo.R index 017dbe7..edf5c93 100644 --- a/R/use-todo.R +++ b/R/use-todo.R @@ -63,10 +63,10 @@ use_todo <- function(todo, proj = proj_get2(), code = FALSE) { #' Remove a TODO/WORK/FIXME item from a file #' -#' Function meant to be wrapped as `{.run}` hyperlinks with [file_outline()]. +#' Function meant to be wrapped as `{.run }` hyperlinks with [file_outline()]. #' It basically removes a line from a file. #' -#' @param line_id The line number (a single integer) +#' @param line The line number (a single integer) #' @param file Path to a file #' @param regexp A regexp to assess that the file content has not changed. #' @param rm_line A logical If `NULL` will remove the full line in the file @@ -77,10 +77,10 @@ use_todo <- function(todo, proj = proj_get2(), code = FALSE) { #' content invisibly. #' @export #' @keywords internal -complete_todo <- function(line_id, file, regexp, rm_line = NULL) { +complete_todo <- function(line, file, regexp, rm_line = NULL) { check_string(regexp) - check_number_whole(line_id) - line_id_original <- line_id + check_number_whole(line) + line_original <- line # to defer warning. if (interactive() && rstudioapi::isAvailable()) { rstudioapi::documentSaveAll() @@ -88,7 +88,7 @@ complete_todo <- function(line_id, file, regexp, rm_line = NULL) { warn_change_of_line <- FALSE file_content <- readLines(file, encoding = "UTF-8") - line_content <- file_content[line_id] + line_content <- file_content[line] # Special case for issues (probably need to opt out at some point) # patch that will likely not work for many cases. @@ -105,8 +105,8 @@ complete_todo <- function(line_id, file, regexp, rm_line = NULL) { warn_change_of_line <- TRUE if (length(regexp_detection) == 1) { - line_id <- regexp_detection - line_content <- line_content <- file_content[line_id] + line <- regexp_detection + line_content <- line_content <- file_content[line] detect_regexp_in_line <- grepl(pattern = regexp, x = line_content) } else if (length(regexp_detection) > 1) { cli::cli_abort(c( @@ -121,12 +121,12 @@ complete_todo <- function(line_id, file, regexp, rm_line = NULL) { } } - tag_type <- extract_tag_in_text(text = line_content, line_id) + tag_type <- extract_tag_in_text(text = line_content, line) if (warn_change_of_line) { cli::cli_warn(c( x = "Could not find {.arg regexp} as expected", - "Could not find {.val {regexp}} at line {line_id_original}.", + "Could not find {.val {regexp}} at line {line_original}.", i = "Has the file content changed since you ran this code?", # needs qty for cli pluralization, but no printing "`regexp` was detected in {cli::qty(length(regexp_detection))} line{?s} {regexp_detection}." @@ -152,7 +152,7 @@ complete_todo <- function(line_id, file, regexp, rm_line = NULL) { regex_new <- cli::style_strikethrough(regex) line_content_show <- stringr::str_replace(line_content_show, regex, regex_new) } - file_line <- paste0(file, ":", line_id) + file_line <- paste0(file, ":", line) cli::cli_alert_success( "Removed {.code {line_content_show}} from {.file {file_line}}!" ) @@ -160,10 +160,10 @@ complete_todo <- function(line_id, file, regexp, rm_line = NULL) { line_content_new <- strip_todo_line(line_content, only_rm_tag = !rm_line) if (nzchar(line_content_new)) { - file_content[line_id] <- line_content_new + file_content[line] <- line_content_new file_content_new <- file_content } else { - file_content_new <- file_content[-line_id] + file_content_new <- file_content[-line] if (!rm_line) { # WIll remove this line eventually # remove line if it ends up empty. not supposed to happen diff --git a/TODO.R b/TODO.R index eb7e79f..699f01c 100644 --- a/TODO.R +++ b/TODO.R @@ -24,3 +24,4 @@ # TODO [check_referenced_files] doesn't check for {.file R/file.R} # TODO explain rationale behind `work_only`. Suggest to transform to TODO if this item is no longer critical. `work_only` goal is to show you exactly where you need to do work # TODO [outline] Show function call if exported + not internal + bonus if has family tagf +# TODO title of file could be function title if it is first element [proj_outline] diff --git a/man/complete_todo.Rd b/man/complete_todo.Rd index 7ab62ad..00b7ad8 100644 --- a/man/complete_todo.Rd +++ b/man/complete_todo.Rd @@ -4,10 +4,10 @@ \alias{complete_todo} \title{Remove a TODO/WORK/FIXME item from a file} \usage{ -complete_todo(line_id, file, regexp, rm_line = NULL) +complete_todo(line, file, regexp, rm_line = NULL) } \arguments{ -\item{line_id}{The line number (a single integer)} +\item{line}{The line number (a single integer)} \item{file}{Path to a file} diff --git a/tests/testthat/_ref/my-analysis.R b/tests/testthat/_ref/my-analysis.R index 8e72086..86dfcbc 100644 --- a/tests/testthat/_ref/my-analysis.R +++ b/tests/testthat/_ref/my-analysis.R @@ -1,6 +1,6 @@ # Analyse my streets --------------- ## Read my [streets](https://https://en.wikipedia.org/wiki/Street_art) data ------- -my_streets <- read.csv("data/my-streets.csv") +my_streets <- read.csv("data/my-streets.csv"), silent = TRUE) new_dat <- my_streets |> dplyr::mutate( title = "data wrangling", # "problem" @@ -17,12 +17,6 @@ fs::path("data", "my-streets", ext = "csv") # system.file("file.R", package = "fs") # should not be listed. # TODO Create a new version -#' ## Roxygen section -#' \url{https://github.com} -#' -#' gt::tab_header(title = "A real one") |> -#' gt::tab_header(subtitle = "A true one") - # {.file R/my-file.R} --- cli::cli_ul("Refer to {.href [google](https://google.com)}") diff --git a/tests/testthat/_snaps/use-todo.md b/tests/testthat/_snaps/use-todo.md index 5607b20..ef75fa2 100644 --- a/tests/testthat/_snaps/use-todo.md +++ b/tests/testthat/_snaps/use-todo.md @@ -1,13 +1,13 @@ # Marking a TODO item as done works Code - complete_todo(line_id = 1, file = tmp, regexp = "I Want this done") + complete_todo(line = 1, file = tmp, regexp = "I Want this done") Condition Error in `complete_todo()`: x Could not delete the TODO item. i Line 1 does not contain any "WORK", "FIXME", or "TODO" tags. Code - complete_todo(line_id = 2, file = tmp) + complete_todo(line = 2, file = tmp) Condition Error in `complete_todo()`: ! `regexp` must be a single string, not absent. diff --git a/tests/testthat/test-use-todo.R b/tests/testthat/test-use-todo.R index e678b80..601f31b 100644 --- a/tests/testthat/test-use-todo.R +++ b/tests/testthat/test-use-todo.R @@ -46,20 +46,20 @@ test_that("Marking a TODO item as done works", { # tmp still has 7 lines expect_snapshot(error = TRUE, { # Can't delete the first line as it doesn't contain a TODO item - complete_todo(line_id = 1, file = tmp, regexp = "I Want this done") + complete_todo(line = 1, file = tmp, regexp = "I Want this done") # Try to delete TODO item at line 2 without providing the regexp for safeguard - complete_todo(line_id = 2, file = tmp) + complete_todo(line = 2, file = tmp) }) # Deleting the TODO item line completely (tmp now has 4 lines) expect_complete_todo( - complete_todo(line_id = 2, file = tmp, regexp = "item to delete") + complete_todo(line = 2, file = tmp, regexp = "item to delete") ) # Deleting the work tag (on new line 2), but keeping the comment. # Will throw a warning for now. expect_complete_todo( out <- complete_todo( - line_id = 4, + line = 4, file = tmp, regexp = "Explain what the next code does" ), @@ -71,7 +71,7 @@ test_that("Marking a TODO item as done works", { ) expect_complete_todo( out <- complete_todo( - line_id = 4, + line = 4, file = tmp, regexp = "ethisissues1890" ), @@ -101,5 +101,5 @@ test_that("use_todo() global works", { suppressMessages(path <- use_todo("global::it is time")) ) line_to_delete <- length(readLines(path, encoding = "UTF-8")) - suppressMessages(complete_todo(file = path, regexp = "it is time", line_id = line_to_delete, rm_line = TRUE)) + suppressMessages(complete_todo(file = path, regexp = "it is time", line = line_to_delete, rm_line = TRUE)) }) From 0e74f37f672a563f144fa2132e08daeca9538d8e Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 17 May 2024 19:28:01 -0400 Subject: [PATCH 10/71] Commit remaining breakage --- R/outline-criteria.R | 21 +- R/outline-roxy.R | 21 +- R/outline.R | 36 +- README.md | 739 +++++++++++++++++++++++----------- tests/testthat/test-outline.R | 1 + 5 files changed, 568 insertions(+), 250 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 4b88305..1ada012 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -29,7 +29,7 @@ o_is_roxygen_comment <- function(x, file_ext = NULL) { return(FALSE) } - ifelse(rep(is_r_file, length.out = length(x)), stringr::str_starts(x, "#'\\s"), FALSE) + ifelse(rep(is_r_file, length.out = length(x)), stringr::str_detect(x, "^#'\\s|^#'$"), FALSE) } o_is_todo_fixme <- function(x) { @@ -121,20 +121,21 @@ define_outline_criteria <- function(.data, print_todo) { x$file_ext <- s_file_ext(x$file) x$is_md <- x$file_ext %in% c("qmd", "md", "Rmd", "Rmarkdown") x$is_news <- x$is_md & grepl("NEWS.md", x$file, fixed = TRUE) - x$is_md <- x$is_md & !x$is_news # treating news and other md files differently. + x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext) + x$is_md <- (x$is_md | x$is_roxygen_comment) & !x$is_news # treating news and other md files differently. x$is_test_file <- grepl("tests/testthat", x$file, fixed = TRUE) x$is_snap_file <- grepl("_snaps", x$file, fixed = TRUE) - x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext) - if (any(x$is_roxygen_comment)) { rlang::check_installed(c("roxygen2", "tidyr"), "to create roxygen2 comments outline.") files_with_roxy_comments <- unique(x[x$is_roxygen_comment, "file", drop = TRUE]) files_with_roxy_comments <- rlang::set_names(files_with_roxy_comments, files_with_roxy_comments) parsed_files <- suppressMessages( # roxygen2 messages - purrr::map(files_with_roxy_comments, purrr::safely(roxygen2::parse_file)) + # TRICK purrr::safely creates an error object, while possible is better. + purrr::map(files_with_roxy_comments, purrr::possibly(roxygen2::parse_file)) ) # if roxygen2 cannot parse a file, let's just forget about it. unparsed_files <- files_with_roxy_comments[which(is.null(parsed_files))] + # browser() if (length(unparsed_files) > 0) { cli::cli_inform("Could not parse roxygen comments in {.file {unparsed_files}}") } @@ -175,11 +176,19 @@ define_outline_criteria <- function(.data, print_todo) { is_cross_ref = stringr::str_detect(content, "docs_links?\\(") & !stringr::str_detect(content, "@param|\\{\\."), is_function_def = grepl("<- function(", content, fixed = TRUE) & !stringr::str_starts(content, "\\s*#") ) + if (!"before_and_after_empty" %in% names(x)) { + x$before_and_after_empty <- NA + } x <- dplyr::mutate( x, - before_and_after_empty = line_id == 1 | !nzchar(dplyr::lead(content, default = "")) & !nzchar(dplyr::lag(content)), + before_and_after_empty = ifelse( + !is.na(before_and_after_empty), + line == 1 | !nzchar(dplyr::lead(content, default = "")) & !nzchar(dplyr::lag(content)), + before_and_after_empty + ), .by = file ) + #browser() x } diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 92e937a..ab9bbbd 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -195,8 +195,8 @@ join_roxy_fun <- function(file) { ) roxy_parsed |> dplyr::mutate( - file = fs::path_real(file) |> as.character(), - file_line = paste0(file, ":", line) + #file = fs::path_real(file) |> as.character(), + #file_line = paste0(file, ":", line) ) |> dplyr::relocate( file, topic, content, line, tag @@ -205,6 +205,7 @@ join_roxy_fun <- function(file) { is_md = tag %in% c("subsection", "details", "description", "section"), # content = paste0("#' ", outline_el), is_object_title = tag == "title", + line = as.integer(line), file_ext = "R", tile_el = NA_character_, title_el_line = NA_integer_, @@ -212,8 +213,22 @@ join_roxy_fun <- function(file) { is_roxygen_comment = TRUE, is_test_file = FALSE, is_snap_file = FALSE, - is_section_title = tag %in% c("section", "subsection") | tag %in% c("details", "description"), + before_and_after_empty = TRUE, + is_section_title = TRUE, + is_section_title_source = TRUE, is_saved_doc = TRUE, has_inline_markup = FALSE # let's not mess with inline markup + ) |> + dplyr::filter( + content != "NULL" ) } + +# helper for interactive checking ----------- + + +active_doc_parse <- function(doc = active_rs_doc()) { + doc <- purrr::set_names(doc) + parsed <- purrr::map(doc, roxygen2::parse_file) + parsed |> join_roxy_fun() +} diff --git a/R/outline.R b/R/outline.R index fac4e2d..557b4df 100644 --- a/R/outline.R +++ b/R/outline.R @@ -122,14 +122,14 @@ file_outline <- function(pattern = NULL, file_content <- rlang::set_names(path) # Not warn file_content <- purrr::map(file_content, function(x) readLines(fs::path_real(x), encoding = "UTF-8", warn = FALSE)) - # Combine everything into a tibble that contains file, line_id, content - file_content <- purrr::map(file_content, function(x) tibble::enframe(x, name = "line_id", value = "content")) + # Combine everything into a tibble that contains file, line, content + file_content <- purrr::map(file_content, function(x) tibble::enframe(x, name = "line", value = "content")) file_content <- dplyr::bind_rows(file_content, .id = "file") } else { file_content <- purrr::map( .x = list("unsaved-doc.R" = rstudioapi::getSourceEditorContext()$contents), - .f = function(x) tibble::enframe(x, name = "line_id", value = "content") + .f = function(x) tibble::enframe(x, name = "line", value = "content") ) file_content <- dplyr::bind_rows(file_content, .id = "file") } @@ -365,7 +365,7 @@ print.outline_report <- function(x, ...) { link = list(rlang::set_names( link_rs_api, purrr::map_chr( - paste0("{.file ", file, ":", line_id, "}"), + paste0("{.file ", file, ":", line, "}"), cli::format_inline ) )), @@ -451,6 +451,7 @@ keep_outline_element <- function(.data) { } else { versions_to_drop <- character(0L) } + # browser() dat <- dplyr::filter( .data, (is_news & ( @@ -465,8 +466,9 @@ keep_outline_element <- function(.data) { # What to keep in .R files (!is_md & is_section_title_source) | # What to keep anywhere - is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def # | is_cli_info # TODO reanable cli info + is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def | is_roxygen_comment # | is_cli_info # TODO reanable cli info ) + dat$simplify_news <- NULL dat } @@ -507,19 +509,19 @@ display_outline_element <- function(.data) { y <- dplyr::mutate( x, has_title_el = - ((line_id == 1 & !is_todo_fixme & !is_test_name & !is_snap_file) | + ((line == 1 & !is_todo_fixme & !is_test_name & !is_snap_file) | (is_doc_title & !is_subtitle & !is_snap_file & !is_second_level_heading_or_more)) & !is_news, .by = "file" ) y <- withCallingHandlers( dplyr::mutate(y, - title_el_line = ifelse(has_title_el, line_id[ - (line_id == 1 & !is_todo_fixme & !is_test_name & !is_snap_file) | + title_el_line = ifelse(has_title_el, line[ + (line == 1 & !is_todo_fixme & !is_test_name & !is_snap_file) | (is_doc_title & !is_subtitle & !is_snap_file & !is_second_level_heading_or_more) ][1], # take the first element to avoid problems (may be the reason why problems occur) NA_integer_ ), - title_el = outline_el[line_id == title_el_line], + title_el = outline_el[line == title_el_line], .by = "file" ), error = function(e) { @@ -529,7 +531,7 @@ display_outline_element <- function(.data) { ) y <- dplyr::relocate( y, - "outline_el", "line_id", "title_el", "title_el_line", "has_title_el", + "outline_el", "line", "title_el", "title_el_line", "has_title_el", .after = "content" ) @@ -596,7 +598,7 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { as.character(trim_outline(.data$outline_el[cn], width - 8L)), "- {.run [Done{cli::symbol$tick}?](reuseme::complete_todo(", # Removed ending dot. (possibly will fail with older versions) - .data$line_id[cn], ", '", .data$file[cn], "', '", + .data$line[cn], ", '", .data$file[cn], "', '", # modify regex twice if needed (see below) stringr::str_sub(stringr::str_replace_all(.data$content[cn], "\\^|\\$|'|\\{|\\}|\\)|\\(|\\[\\]|\\+", "."), start = -15L), "'))}", .data$rs_version[cn] @@ -618,7 +620,7 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { # Removed ending dot. (possibly will fail with older versions) # modify regex twice if needed (see above) - line_id, ", '", file, "', '", stringr::str_sub(stringr::str_replace_all(content, "\\^|\\$|'|\\{|\\}|\\)|\\(|\\[\\]|\\+", "."), start = -15L), "'))}", + line, ", '", file, "', '", stringr::str_sub(stringr::str_replace_all(content, "\\^|\\$|'|\\{|\\}|\\)|\\(|\\[\\]|\\+", "."), start = -15L), "'))}", rs_version ), outline_el2 @@ -627,7 +629,7 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { ) .data <- dplyr::mutate(.data, - link = paste0(outline_el2, " {.path ", file, ":", line_id, "}"), + link = paste0(outline_el2, " {.path ", file, ":", line, "}"), # rstudioapi::documentOpen works in the visual mode!! but not fully. file_path = .data$file, is_saved_doc = .env$is_saved_doc, @@ -647,15 +649,15 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { } dplyr::mutate(.data, - # link_rs_api = paste0("{.run [", outline_el, "](reuseme::open_rs_doc('", file_path, "', line = ", line_id, "))}"), + # link_rs_api = paste0("{.run [", outline_el, "](reuseme::open_rs_doc('", file_path, "', line = ", line, "))}"), link_rs_api = dplyr::case_when( is.na(outline_el2) ~ NA_character_, - !is_saved_doc ~ paste0("line ", line_id, " -", outline_el2), + !is_saved_doc ~ paste0("line ", line, " -", outline_el2), rs_avail_file_link ~ paste0( "{cli::style_hyperlink(", style_fun, ', "', - paste0("file://", file_path), '", params = list(line = ', line_id, ", col = 1))} ", outline_el2 + paste0("file://", file_path), '", params = list(line = ', line, ", col = 1))} ", outline_el2 ), - .default = paste0(rs_version, "{.run [i](reuseme::open_rs_doc('", file_path, "', line = ", line_id, "))} ", outline_el2) + .default = paste0(rs_version, "{.run [i](reuseme::open_rs_doc('", file_path, "', line = ", line, "))} ", outline_el2) ), file_hl = dplyr::case_when( !is_saved_doc ~ file_path, diff --git a/README.md b/README.md index 54ea092..6b15bdb 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 481ms 483ms 2.07 18MB 4.14 +#> 1 outline <- proj_outline() 1.31s 1.31s 0.762 66.2MB 6.10 ```
@@ -183,231 +183,522 @@ outline #> `i` A great title #> `i` TODO improve this Viz!- `Done✔?` #> -#> ── `LICENSE.md` MIT License -#> -#> ── `playground/roxygen2-test.R` -#> `i` Section to extract #> ── `R/dplyr-plus.R` dplyr extra -#> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` -#> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` -#> `i` summarise with total -#> -#> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- -#> `i` Use cases / advantages -#> `i` Caution -#> `i` Workflow to explore mtcars -#> `i` base identity functions -#> `i` dplyr identity functions with small tweaks -#> `i` dplyr identity without tweaks -#> `i` dplyr extensions identity -#> `i` helpers -#> -#> ── `R/files-conflicts.R` -#> `i` TODO insert in either proj_outline, or rename_file- `Done✔?` -#> `i` TODO probably needs a `detect_genuine_path()`- `Done✔?` -#> `i` Helpers -#> `i` TODO Add false positive references- `Done✔?` -#> `i` TODO fs::path and file.path should be handled differently- `Done✔?` -#> -#> ── `R/import-standalone-types-check.R` -#> `i` Scalars -#> `i` Vectors -#> -#> ── `R/named.R` -#> `i` returns the same as base R for unnamed input -#> `i` returns all values -#> `i` TODO is usable with `extract_cell_value()` -#> -#> ── `R/open.R` -#> `i` FIXME why is this code like this?- `Done✔?` -#> `i` TODO structure and summarise information.- `Done✔?` -#> -#> ── `R/outdated-pkgs.R` -#> `i` TODO figure out pad :)- `Done✔?` -#> -#> ── `R/outline-criteria.R` -#> `i` Add variable to outline data frame -#> `i` TODO strip is_cli_info in Package? only valid for EDA- `Done✔?` -#> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` -#> `i` it is 'R/outline.R' -#> -#> ── `R/outline.R` `proj_outline()` -#> `i` `file_outline()` -#> `i` File outline -#> `i` Print method -#> `i` Step: tweak outline look as they show -#> `i` TODO reanable cli info- `Done✔?` -#> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` -#> -#> ── `R/proj-list.R` -#> `i` TODO maybe add a max?- `Done✔?` -#> `i` TODO improve on this message- `Done✔?` -#> -#> ── `R/proj-reuseme.R` -#> `i` Setup -#> `i` Capabilities. -#> -#> ── `R/rename-files.R` -#> `i` Use case -#> `i` After here, we start doing some renaming real situations -#> `i` Helpers -#> `i` helpers for computing scope of renaming -#> `i` TODO measure of string proximity- `Done✔?` -#> `i` Prevent renaming if something is going on -#> `i` FIXME maybe not fail while testing- `Done✔?` -#> `i` TODO Check that old file is more recent- `Done✔?` -#> -#> ── `R/use-todo.R` -#> `i` TODO think about maybe using todo = clipr::read_clip()- `Done✔?` -#> `i` TODO nice to have, but would need to extract duplicates- `Done✔?` -#> `i` Helpers -#> -#> ── `R/utils-proj.R` usethis adaptions utils -#> `i` Active project / document -#> -#> ── `R/utils.R` OS utils -#> -#> ── `tests/testthat/_ref/many-titles.md` The title is the only outline element -#> `i` Another title -#> `i` Second level -#> `i` TODO this is an item- `Done✔?` -#> `i` Last title -#> -#> ── `tests/testthat/_ref/my-analysis.md` My doc title -#> `i` A section -#> `i` Dashboard card -#> `i` A code section -#> `i` A subsection -#> `i` A section2 -#> `i` A long ggplot2 title -#> `i` A code section -#> -#> ── `tests/testthat/_ref/my-analysis.R` Analyse my streets -#> `i` Read my streets () data -#> `i` data wrangling -#> `i` Write my streets -#> `i` TODO Create a new version- `Done✔?` -#> `i` Roxygen section -#> `i` A real one -#> `i` A true one -#> `i` 'R/my-file.R' -#> `i` Refer to google () -#> `i` Section title -#> -#> ── `tests/testthat/_ref/single-title.md` The title is the only outline element -#> -#> ── `tests/testthat/_snaps/case-if-any.md` -#> `i` wrong cases error -#> -#> ── `tests/testthat/_snaps/dplyr-plus.md` -#> `i` adds rows in front, but warns the user -#> -#> ── `tests/testthat/_snaps/eda-identity.md` -#> `i` Side effects are what's intended in interactive sessions -#> -#> ── `tests/testthat/_snaps/outline-criteria.md` -#> `i` No outline criteria are untested -#> -#> ── `tests/testthat/_snaps/outline.md` -#> `i` alpha and work_only arguments work -#> `i` pattern works as expected -#> -#> ── `tests/testthat/_snaps/rename-files.md` -#> `i` Helper files returns the expected input -#> -#> ── `tests/testthat/_snaps/use-todo.md` -#> `i` Marking a TODO item as done works -#> -#> ── `tests/testthat/test-case-if-any.R` -#> `i` case_if_any() basic work -#> `i` wrong cases error -#> `i` case_if_any() can use a newly created variable (#8) -#> -#> ── `tests/testthat/test-dplyr-plus.R` -#> `i` filter_if_any() errors correctly when using `by` instead of `.by` -#> `i` filter_if_any() errors with across() -#> `i` TODO improve this error- `Done✔?` -#> `i` adds rows in front, but warns the user -#> `i` summarise_with_total() keeps factors -#> `i` na_if2() works with expr and values -#> -#> ── `tests/testthat/test-eda-identity.R` -#> `i` Returns identity -#> `i` Side effects are what's intended in interactive sessions -#> -#> ── `tests/testthat/test-link-elements.R` -#> `i` link_gh_issue() + markup_href() work -#> -#> ── `tests/testthat/test-named.R` -#> `i` min/max/unique_named() return named output -#> `i` max_named() and unique_named() work with unnamed vectors -#> -#> ── `tests/testthat/test-open.R` -#> `i` open_rs_doc() errors in non-interactive sessions -#> -#> ── `tests/testthat/test-outline-criteria.R` Test individual outline elements -#> `i` No outline criteria are untested -#> -#> ── `tests/testthat/test-outline.R` -#> `i` alpha and work_only arguments work -#> `i` file_outline() is a data frame -#> `i` TODO change tests for data frame size when stable (efficiency). As stil…- `Done✔?` -#> `i` file_outline() with only title doesn't error -#> `i` file_outline() contains function calls -#> `i` dir_outline() works with no error -#> -#> ── `tests/testthat/test-rename-files.R` -#> `i` Helper files returns the expected input -#> `i` force and action are deprecated -#> -#> ── `tests/testthat/test-screenshot.R` -#> `i` screenshot() does nothing in non-interactive sessions -#> -#> ── `tests/testthat/test-use-todo.R` -#> `i` Marking TODO as done detects tags -#> `i` todo items are correctly stripped -#> -#> ── `tests/testthat/test-utils.R` -#> `i` Windows is recognized correctly. -#> -#> ── `TODO.R` -#> `i` TODO screenshot make the behaviour different when vignettes vs articl…- `Done✔?` -#> `i` TODO screenshot RStudio addin to insert the code directly in the qmd …- `Done✔?` -#> `i` TODO use_family() to edit .R file to add @family data frames tags to ro…- `Done✔?` -#> `i` TODO mutate_identity redundant if the focus pillar PR was merged. r-lib/pillar#585 ()- `Done✔?` -#> `i` TODO rename if many matches, separate those with the exact path.- `Done✔?` -#> `i` TODO outline make ggtitle work- `Done✔?` -#> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` -#> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` -#> `i` TODO outline renable cli info.- `Done✔?` -#> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` -#> `i` TODO outline avoid evaluating in current env.- `Done✔?` -#> `i` TODO wrap regexps in functions- `Done✔?` -#> `i` TODO outline remove examples from outline. Sometimes commented code i…- `Done✔?` -#> `i` TODO outline roxygen comments processing should be left to `roxygen2::parse_file()`- `Done✔?` -#> `i` TODO outline show key like `pak::pkg_deps_tree()` does.- `Done✔?` -#> `i` TODO outline roxygen function title- `Done✔?` -#> `i` TODO outline remove ggtext markup from plot title.- `Done✔?` -#> `i` FIXME outline comments are now interpreted as section- `Done✔?` -#> `i` TODO outline todos in qmd file inside html comment- `Done✔?` -#> `i` TODO reframe more than one issue. nw drive- `Done✔?` -#> `i` TODO delete generated files- `Done✔?` -#> `i` TODO [proj_file] to accesss data (return the path in this case?)- `Done✔?` -#> -#> ── `NEWS.md` -#> `i` reuseme (development version) -#> `i` reuseme 0.0.1 -#> -#> ── `README.Rmd` -#> `i` reuseme -#> `i` Installation -#> `i` Getting started -#> `i` Example -#> `i` hello -#> `i` Extend usethis functionality -#> `i` Proposing a data analysis workflow -#> `i` Outline speed ``` + #> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` + #> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` + #> `i` summarise with total + #> `i` Count observations by group and compute percentage + #> `i` Subset rows using their positions + #> `i` Explore all rows in a random group + #> `i` Keep rows that match one of the conditions + #> `i` Elegant wrapper around filter and pull + #> `i` Compute a summary for one group with the total included. + #> `i` Transform to NA any of the condition + #> `i` `count_pct()` lets you quickly count the unique values of one or more + #> variables: `df |> count_pct(a, b)` It calculates the percentage by group + #> afterwards + #> `i` A wrapper around `dplyr::bind_rows()`, `dplyr::slice_min()`, `dplyr::slice_max()` + #> `i` Compared to `slice_sample()` `slice_group_sample` will return all rows corresponding to a group. + #> `i` The `filter_if_any()` function is used to subset a data frame, retaining all + #> rows that satisfy **at least one of** your conditions. + #> To be retained, the row must produce a value of `TRUE` for + #> **one of the conditions**. Note that when a condition evaluates to `NA` the + #> row will be dropped, (hence this function) unlike base subsetting with `[`. + #> `i` It can be very useful when trying to extract a value from somewhere, and you + #> have one col that represents the unique id. + #> `i` This function is useful to create end tables, apply the same formula to a group and to its overall. + #> You can specify a personalized `Total` value with the `.label` argument. You + #> You should only use the output from `summarise_with_total()` with `tidyr::pivot_wider()`, + #> write data to a spreadsheet, `gt::gt()` after that. Don't try to do more computing afterwards. + #> It can also be used for plotting + #> Changes the `.by` variable to a factor. + #> `i` This function is similar to `dplyr::na_if()`, but it has 2 differences. the + #> values of `y` are never recycled. There are two ways to provide the condition. + #> As values or as a logical vector. + #> `i` Wrapper function around [dplyr::count()] + #> `i` The reason to be of this function is to simplify a call like + #> + #> ```r + #> # with dplyr::filter + #> dat |> dplyr::filter(vs == 1 | is.na(vs)) + #> data |> + #> dplyr::mutate(cond1 = vs == 1, cond2 = is.na(vs)) |> + #> dplyr::filter(dplyr::if_any(starts_with("cond"))) + #> dat |> filter_if_any(vs == 1, is.na(vs)) + #> ``` + #> + #> Basically, this is just a shortcut to + #> `mutate(.data, new_lgl_vars)` + `filter(if_any(new_lgl_vars))` + `select(-new_lgl_vars)`. + #> It allows mutate_like syntax in `filter(if_any(...))`. + #> + #> Caution: still doesn't work with [dplyr::across()], use the regular + #> `filter(if_any())` syntax. + #> `i` dplyr extensions + #> `i` dplyr extensions + #> `i` family dplyr extensions + + #> + #> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- + #> `i` base identity functions + #> `i` dplyr identity functions with small tweaks + #> `i` dplyr identity without tweaks + #> `i` dplyr extensions identity + #> `i` helpers + #> `i` Helpers that return the same value + #> `i` They all share the `*_identity` suffix, they are silent in non-interactive + #> sessions. They are very handy to create clickable hyperlinks that do not + #> modify the current state of the analysis. + #> + #> They are inspired by [pillar::glimpse()], [tibble::view()]. + #> + #> Look at the original functions for the other parameters. + #> + #> # Use cases / advantages + #> + #> * Like many other reuseme functions, they are most useful in interactive sessions + #> * print the result in interactive sessions (quiet in non-interactive.) + #> * Create runnable hyperlinks (In August 2023, RStudio forbids runnable + #> hyperlinks of base functions, or non-package functions. (i.e. that don't have `::`)) + #> * Use in pipelines to explore the data + #> * Use [rlang::is_interactive()] over [base::interactive()] as it's easier to + #> control and test with `options(rlang_interactive)` + #> * Use the original functions for your final results. + #> * `count_identity()` also prints percentages. + #> * `slice_identity()` can be useful to resolve many-to-many warnings from + #> dplyr join functions. + #> + #> # Caution + #> + #> * Don't put those at the end of a pipeline + #> * Don't name the first argument, to avoid conflicts in case a column in the + #> data is named `x`. + #> * Some functions have small tweaks + #> * `mutate_identity()` only prints the distinct values, and uses + #> `.keep = "used"`, `.before = 0`, unless specified to improve the display. + #> * `count_identity()` is a wrapper of [count_pct()] + #> (itself a wrapper of `dplyr::count()`), + #> * `count_identity()` may fail if there is already a variable named `n`. + #> * `slice_min/max_identity()` relocates the target column at the beginning. + #> * `filter_identity()` prints a short message if no rows are returned. + #> + #> ── `R/files-conflicts.R` Check if files referenced in source files exist in a current dir + #> `i` TODO insert in either proj_outline, or rename_file- `Done✔?` + #> `i` TODO probably needs a `detect_genuine_path()`- `Done✔?` + #> `i` Helpers + #> `i` TODO Add false positive references- `Done✔?` + #> `i` TODO fs::path and file.path should be handled differently- `Done✔?` + #> `i` Check if outdated or non-existent file is. + #> `i` 1. It goes through the source files (.R/.qmd etc.), + #> 2. It identifies data files (.csv, .xlsx) read or written + #> 3. Search on the system if these files exist. + #> `i` If `quiet = FALSE` (default) will give a hint of where the file is referenced. + #> `i` Still WIP, so you can add code for false positive as needed. + #> + #> + #> To find genuine referenced files, we exclude different paths + #> + #> 1. Those created with `fs::path()` or `file.path()` or `glue::glue()` + #> 2. Those that are checked for `fs::file_exists()`, `file.exists()` + #> 3. Deleted with `fs::file_delete()`, `unlink()` + #> + #> ── `R/import-standalone-types-check.R` + #> `i` Scalars + #> `i` Vectors + #> + #> ── `R/open.R` Open a Document in RStudio + #> `i` FIXME why is this code like this?- `Done✔?` + #> `i` TODO structure and summarise information.- `Done✔?` + #> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` + #> `i` Copy the active document to the same location + #> `i` Delete the active RStudio document safely + #> `i` Wrapper around [rstudioapi::documentOpen()], but with `fs paths`, for consistency. + #> If the file could not be opened, a clickable hyperlink is displayed. + #> `i` The goal is to provide things that RStudio or usethis doesn't provide natively. + #> `i` `r lifecycle::badge('experimental')` + #> + #> Gathers informative summary about the document you are about to delete. + #> + #> + #> Will delete more easily if file name starts with `temp-`, if file is untracked and recent. + #> `i` * `active_rs_doc()` is a wrapper around [rstudioapi::documentPath()] that handles + #> unsaved files gracefully + #> `i` For example, `active_rs_doc_rename()` will not happen, because it is already easy + #> to do so via the RStudio IDE. + #> `i` document manipulation helpers + #> `i` document manipulation helpers + #> + #> ── `R/outdated-pkgs.R` Looks for outdated packages + #> `i` TODO figure out pad :)- `Done✔?` + #> `i` Only checks for binaries, which has the advantage of not notifying you when + #> packages are newly available on CRAN, but without the binaries available. + #> Installing packages from source can be longer, hence the advantage of + #> waiting a few days for binaries. + #> + #> It takes advantage of pak's capacities to allow you to install packages on + #> Windows without restarting session. + #> + #> ── `R/outline-criteria.R` + #> `i` Add variable to outline data frame + #> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` + #> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` + #> `i` it is 'R/outline.R' + #> `i` outline_criteria + #> `i` * is test title + #> * is a todo item + #> * is_roxygen_line + #> * is_tab_title + #> + #> ── `R/outline-roxy.R` Extract roxygen tag + #> `i` TODO when stable delete- `Done✔?` + #> `i` TODO Delete when stable debugging- `Done✔?` + #> `i` TODO Delete when stable for debugging- `Done✔?` + #> `i` helper for interactive checking + #> `i` Tell me what this does + #> `i` Section to extract + #> + #> Well this is a section + #> + #> ── `R/outline.R` `proj_outline()` + #> `i` `file_outline()` + #> `i` File outline + #> `i` Print method + #> `i` Step: tweak outline look as they show + #> `i` TODO reanable cli info- `Done✔?` + #> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` + #> `i` Print interactive outline of file sections + #> `i` TODO` items- `Done✔?` + #> `i` `proj_outline()` and `dir_outline()` are wrapper of `file_outline()`. + #> + #> The parser is very opinioneted and is not very robust as it is based on regexps. + #> For a better file parser, explore other options, like lightparser () for Quarto, `{roxygen2}` + #> + #> Will show TODO items and will offer a link to [mark them as + #> complete][complete_todo()]. + #> + #> ── `R/proj-list.R` Opens a RStudio project in a new session + #> `i` TODO maybe add a max?- `Done✔?` + #> `i` TODO improve on this message- `Done✔?` + #> `i` Access the file outline within other project + #> `i` Returns a named project list options + #> `i` If not specified, will generate hyperlinks that call [usethis::proj_activate()]. + #> `proj_switch()` looks at `options(reuseme.reposdir)`. + #> `i` It can be used as [file_outline()] + `proj`. + #> `i` It peeks `options(reuseme.reposdir)` to find projects. + #> `i` project management helpers + #> `i` project management helpers + #> `i` project management helpers + #> + #> ── `R/rename-files.R` Rename an output or a data file and watch for references + #> `i` After here, we start doing some renaming real situations + #> `i` Helpers + #> `i` helpers for computing scope of renaming + #> `i` TODO measure of string proximity- `Done✔?` + #> `i` Prevent renaming if something is going on + #> `i` FIXME maybe not fail while testing- `Done✔?` + #> `i` TODO Check that old file is more recent- `Done✔?` + #> `i` `r lifecycle::badge("experimental")` + #> + #> This function can improve your workflow. + #> It is inspired by [usethis::rename_files()], but its scope + #> is more oriented towards analysis script. + #> + #> # Use case + #> + #> Let's say you have an analysis and work on a certain subject. + #> You want to rename a figure for clarity. + #> For example, you had an input file named `data/my-streets.csv` and you now want to + #> rename it to + #> + #> Here is what `rename_files2()` does for you, before it renames files. + #> + #> 1. Look for potential name conflict + #> 2. Look for data frame name conflicts + #> 3. Sends information to clipboard + #> + #> Will work well for you if you tend to name your objects using snake case and + #> naming objects with snake case or kebab-case. + #> + #> The philosophy is to inform you of manual steps required before actually + #> performing file renaming. + #> + #> A way to be less strict is to us + #> + #> ── `R/use-todo.R` Add a TODO list by project to a TODO.R file in the base directory + + #> `i` TODO think about maybe using todo = clipr::read_clip()- `Done✔?` + #> `i` TODO nice to have, but would need to extract duplicates- `Done✔?` + #> `i` Helpers + #> `i` Remove a TODO/WORK/FIXME item from a file + #> `i` Creates or edits a `TODO.R` file to store your TODOs. + #> By default it will write in the current RStudio project. + #> `i` Function meant to be wrapped as ```` hyperlinks with [file_outline()]. + #> It basically removes a line from a file. + #> `i` If you use `use_todo()` with a version-control repository, you may want to + #> use `usethis::use_git_ignore("TODO.R")` if you don't want your `TODO.R` file + #> + #> to be included in git. If using in a package directory, use + #> `usethis::use_build_ignore("TODO.R")` to prevent a note in `R CMD CHECK` + + #> + #> ── `R/utils-proj.R` usethis adaptions utils + #> `i` Active project / document + #> `i` copy of usethis::is_package + #> + #> ── `R/utils.R` OS utils + #> + #> ── `tests/testthat/_ref/many-titles.md` + #> `i` TODO this is an item- `Done✔?` + #> + #> ── `tests/testthat/_ref/my-analysis.md` My doc title + #> `i` Dashboard card + #> `i` A long ggplot2 title + #> + #> ── `tests/testthat/_ref/my-analysis.R` Analyse my streets + #> `i` Read my streets () data + #> `i` data wrangling + #> `i` Write my streets + #> `i` TODO Create a new version- `Done✔?` + #> `i` 'R/my-file.R' + #> `i` Section title + #> + #> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error + #> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing + #> `i` Title + #> `i` An S3 method not to be include + #> `i` content + #> `i` a family to include + #> + #> ── `tests/testthat/_ref/test-roxygen.R` + #> `i` Use 'tests/testthat/_ref/test-roxygen-safeguard.R' for output testing + #> `i` Complete block for exported function with headings + #> `i` block not to index + #> `i` Topic to index + #> `i` data to index + #> `i` TODO add data block example to index- `Done✔?` + #> `i` A title to be included + #> `i` A title not to be included (internal function) + #> `i` A title to be included + #> `i` First to be included: + #> + #> Content not to be included + #> `i` A description not to be included + #> `i` A description not to include + #> `i` A description not to be included + #> `i` A second-level heading in description to be included? + #> + #> # A detail first level-… + #> `i` heading not to be included + #> + #> content + #> `i` A second-level heading in description to be included? + #> + #> # A detail first level-… + #> `i` a family to include + #> `i` a family to include + #> + #> ── `tests/testthat/test-case-if-any.R` + #> `i` case_if_any() basic work + #> `i` wrong cases error + #> `i` case_if_any() can use a newly created variable (#8) + #> + #> ── `tests/testthat/test-dplyr-plus.R` + #> `i` filter_if_any() errors correctly when using `by` instead of `.by` + #> `i` filter_if_any() errors with across() + #> `i` TODO improve this error- `Done✔?` + #> `i` adds rows in front, but warns the user + #> `i` summarise_with_total() keeps factors + #> `i` na_if2() works with expr and values + #> + #> ── `tests/testthat/test-eda-identity.R` + #> `i` Returns identity + #> `i` Side effects are what's intended in interactive sessions + #> + #> ── `tests/testthat/test-link-elements.R` + #> `i` link_gh_issue() + markup_href() work + #> + #> ── `tests/testthat/test-named.R` + #> `i` min/max/unique_named() return named output + #> `i` max_named() and unique_named() work with unnamed vectors + #> + #> ── `tests/testthat/test-open.R` + #> `i` open_rs_doc() errors in non-interactive sessions + #> + #> ── `tests/testthat/test-outline-criteria.R` Test individual outline elements + #> `i` No outline criteria are untested + #> + #> ── `tests/testthat/test-outline-roxy.R` + #> `i` roxy tags don't error + #> + #> ── `tests/testthat/test-outline.R` + #> `i` alpha and work_only arguments work + #> `i` file_outline() is a data frame + #> `i` TODO change tests for data frame size when stable (efficiency). As stil…- `Done✔?` + #> `i` file_outline() with only title doesn't error + #> `i` file_outline() contains function calls + #> `i` dir_outline() works with no error + #> + #> ── `tests/testthat/test-rename-files.R` + #> `i` Helper files returns the expected input + #> `i` force and action are deprecated + #> + #> ── `tests/testthat/test-screenshot.R` + #> `i` screenshot() does nothing in non-interactive sessions + #> + #> ── `tests/testthat/test-use-todo.R` + #> `i` Marking TODO as done detects tags + #> `i` TODO items are correctly stripped + #> + #> ── `tests/testthat/test-utils.R` + #> `i` Windows is recognized correctly. + #> + #> ── `TODO.R` + #> `i` TODO screenshot make the behaviour different when vignettes vs articl…- `Done✔?` + #> `i` TODO screenshot RStudio addin to insert the code directly in the qmd …- `Done✔?` + #> `i` TODO use_family() to edit .R file to add @family data frames tags to ro…- `Done✔?` + #> `i` TODO mutate_identity redundant if the focus pillar PR was merged. r-lib/pillar#585 ()- `Done✔?` + #> `i` TODO rename if many matches, separate those with the exact path.- `Done✔?` + #> `i` TODO outline make ggtitle work- `Done✔?` + #> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` + #> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` + #> `i` TODO outline renable cli info.- `Done✔?` + #> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` + #> `i` TODO outline avoid evaluating in current env.- `Done✔?` + #> `i` TODO wrap regexps in functions- `Done✔?` + #> `i` TODO outline remove examples from outline. Sometimes commented code i…- `Done✔?` + #> `i` TODO outline roxygen comments processing should be left to `roxygen2::parse_file()`- `Done✔?` + #> `i` TODO outline show key like `pak::pkg_deps_tree()` does.- `Done✔?` + #> `i` TODO outline roxygen function title- `Done✔?` + #> `i` TODO outline remove ggtext markup from plot title.- `Done✔?` + #> `i` FIXME outline comments are now interpreted as section- `Done✔?` + #> `i` TODO outline todos in qmd file inside html comment- `Done✔?` + #> `i` TODO reframe more than one issue. nw drive- `Done✔?` + #> `i` TODO delete generated files- `Done✔?` + #> `i` TODO [proj_file] to accesss data (return the path in this case?)- `Done✔?` + #> `i` TODO [check_referenced_files] doesn't check for 'R/file.R'- `Done✔?` + #> `i` TODO explain rationale behind `work_only`. Suggest to transform to TODO…- `Done✔?` + #> `i` TODO outline Show function call if exported + not internal + bonus if…- `Done✔?` + #> `i` TODO title of file could be function title if it is first element [proj…- `Done✔?` + #> + #> ── `R/browse-pkg.R` Browse pkgdown site if it exists + #> `i` A wrapper around [usethis::browse_package()] that aims at identifying the + #> package website. It looks up for a link in DESCRIPTION. + #> + #> ── `R/case-if-any.R` case-when, but checks for all matches, returns a character + #> `i` Each case is evaluated for **all** cases and a character vector match + #> for each element determines the corresponding value in the output vector. + #> If no cases match, the `.default` is used. + #> The function allows you to assign multiple values to a character value, which + #> can be very handy for EDA. + #> + #> ── `R/escape-inline-markup.R` + #> `i` escape inline markup in case of problems + #> `i` Is inline markup valid? + #> `i` inline markup internal helpers + #> + #> ── `R/import-standalone-obj-type.R` + #> `i` Return English-friendly type + #> `i` Return OO type + #> + #> ── `R/link-elements.R` Create a markdown link to a GitHub issue + #> `i` Create a cli href with a markdown link + #> `i` In RStudio, links to issues are automatically recognized. + #> This function creates intermediate markdown links to entries of the form rstudio/rstudio#1100 () + #> `i` Transforms `[text](url)` -> `text ()` + #> `i` Note: doesn't (yet) support without / + #> + #> Basically trransform repo/org#xx -> repo/org#xx (). + #> + #> Afterwards, we use [markup_href()] to create a cli link + #> `i` inline markup internal helpers + #> `i` inline markup internal helpers + #> + #> ── `R/named.R` Helpers that can return a named vector + #> `i` Base R keeps names in various places, but drops them elsewhere + #> These functions are some that I use frequently, like `max`, or `unique` + #> + #> ── `R/proj-reuseme.R` Interact with different RStudio projects + + #> `i` The package offers many ways to interact with different local RStudio projects. + #> `i` Setup + #> + #> To take advantage of this functionality, you first have to set `options(reuseme.reposdir)` in + #> your .Rprofile file. Access it with [usethis::edit_r_profile()]. + #> + #> I would recommend you add the following. It works better if you store your RStudio + #> projects in common directories. + #> + #> Inspired by [usethis options][usethis::usethis_options] + #> + #> ``` + #> if (interactive()) NULL + #> ``` + #> + #> # Capabilities. + #> + #> Assumes that you have a project named `"learning"` + #> A project outline + #> + #> ``` + #> proj_outline(proj = "learning) + #> ``` + #> + #> Add a TODO item to the `learning` project + #> + #> ``` + #> use_todo("learning::Learn this") + #> ``` + #> + #> Get file [outline][proj_outline()] of the `file.R` in "learning" + #> + #> ``` + #> proj_file("file", "learning") + #> ``` + #> + #> Move to a new project in the same session + #> + #> ``` + #> proj_switch("learning") + #> ``` + #> + #> A lot of these features are already present in RStudio and with usethis. + #> However, when managing many projects, the recent projects list can be more difficult + #> to handle. + #> Passing the full project name to `usethis::proj_activate()` was too long. + #> `i` project management helpers + + #> + #> ── `R/quarto-help.R` Show links to Quarto documentation of interest + #> `i` Very opinionated of links I need to access periodically. Easily + #> accessible from R console. + #> + #> ── `R/reuseme-package.R` + #> `i` reuseme: Collections of Utility Functions to Work Across Projects + #> `i` Allows you to browse current projects, rename files safely, add screenshots to project on Windows. It is also my personal library and contains wrapper around common functions, from dplyr and readxl. It takes advantage of cli hyperlinks. Finally, it provides a custom print method for tibbles, inspired by janitor, and readr. + #> + #> ── `R/screenshot.R` Save the current image in clipboard to png in your active directory + #> `i` The screenshot will be saved as `.png` to a directory following these rules + #> 1. In a regular RStudio project (or a Quarto book), it will be saved to a `images/` directory + #> 2. In a package project, it will be saved in a `man/figures` directory + #> 3. In a Quarto Blog project, it will save in the current post's folder. + #> 4. You can always override these defaults by setting `dir` + #> + #> After using the shortcut Win + Shift + S, you can call this function! + #> `i` If no file name is supplied, a file named `image0*.png` will be created. + #> The function then prompts you to rename the file with a more expressive name. + #> It will continue the numbering if a file named image exists. + #> + #> Still have to validate if it works on macOS, as it is not clear whether the + #> image goes to the clipboard by default + #> + #> The maximum number of images in a folder is 99. (only padding 2), should be enough. + #> + #> You should not be able to overwrite a screenshot with a generic name, only a + #> named one as it is possible you may require to retake your screenshot. + #> + #> ── `README.Rmd` + #> `i` hello +

diff --git a/tests/testthat/test-outline.R b/tests/testthat/test-outline.R index c09e0ae..b1a2c22 100644 --- a/tests/testthat/test-outline.R +++ b/tests/testthat/test-outline.R @@ -51,6 +51,7 @@ test_that("pattern works as expected", { }) test_that("file_outline() with only title doesn't error", { + # broken by change to before_and_after_empty expect_no_error({ file <- file_outline(path = test_path("_ref", "single-title.md")) }) From 145809caf4f063f81fc55ac909aa28e336941225 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 12:18:36 -0400 Subject: [PATCH 11/71] rename `o_is_object_title()` to `o_is_tab_plot_title()` --- R/outline-criteria.R | 6 +++--- R/outline.R | 26 +++++++++++++++++++++-- tests/testthat/_snaps/outline-criteria.md | 2 +- tests/testthat/test-outline-criteria.R | 4 ++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 7344ab3..cae0de5 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -15,7 +15,7 @@ extract_pkg_version <- function(x, is_news, is_heading) { #' * is test title #' * is a todo item #' * is_roxygen_line -#' * is_tab_title +#' * is_tab_plot_title #' #' @noRd o_is_roxygen_comment <- function(x, file_ext = NULL) { @@ -72,7 +72,7 @@ o_is_generic_test <- function(x) { } # Returns table or plot titles. -o_is_object_title <- function(x) { +o_is_tab_plot_title <- function(x) { stringr::str_detect(x, "(?|\\(\\.*\\)"), is_todo_fixme = print_todo & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext) & !is_snap_file, n_leading_hash = nchar(stringr::str_extract(content, "\\#+")), diff --git a/R/outline.R b/R/outline.R index 557b4df..4e20d10 100644 --- a/R/outline.R +++ b/R/outline.R @@ -466,7 +466,7 @@ keep_outline_element <- function(.data) { # What to keep in .R files (!is_md & is_section_title_source) | # What to keep anywhere - is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def | is_roxygen_comment # | is_cli_info # TODO reanable cli info + is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def # | is_cli_info # TODO reanable cli info ) dat$simplify_news <- NULL @@ -487,6 +487,8 @@ display_outline_element <- function(.data) { is_todo_fixme ~ stringr::str_extract(outline_el, "(TODO.+)|(FIXME.+)|(WORK.+)"), is_test_name ~ stringr::str_extract(outline_el, "test_that\\(['\"](.+)['\"]", group = 1), is_cli_info ~ stringr::str_extract(outline_el, "[\"'](.{5,})[\"']") |> stringr::str_remove_all("\""), + # family or concept! + is_tab_or_plot_title & !is.na(tag) ~ outline_el, is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title = [\"']([^\"]{5,})[\"']", group = 1), is_chunk_cap_next & !is_chunk_cap ~ stringr::str_remove_all(outline_el, "\\s?\\#\\|\\s+"), is_chunk_cap ~ stringr::str_remove_all(stringr::str_extract(outline_el, "(cap|title)\\:\\s*(.+)", group = 2), "\"|'"), @@ -503,9 +505,29 @@ display_outline_element <- function(.data) { .default = outline_el ), outline_el = stringr::str_remove(outline_el, "[-\\=]{3,}") |> stringr::str_trim(), # remove trailing bars - is_subtitle = (is_tab_or_plot_title | is_doc_title) & grepl("subt", content, fixed = TRUE), + is_subtitle = (is_tab_or_plot_title | is_doc_title) & ( + grepl("subt", content, fixed = TRUE) | + tag %in% c("family", "concept")) ) + if (anyNA(x$outline_el)) { + indices <- which(is.na(x$outline_el)) + all_na <- x |> dplyr::select(!dplyr::where(\(x) !is.logical(x) & all(is.na(x)))) |> dplyr::slice(dplyr::all_of(indices)) |> dplyr::select(dplyr::where(\(x) all(is.na(x)))) |> names() + all_true_or_single_value <- x |> dplyr::slice(dplyr::all_of(indices)) |> dplyr::select(dplyr::where(\(x)dplyr::n_distinct(x) == 1)) |> dplyr::select(!dplyr::where(\(x) all(is.na(x)))) |> + dplyr::select(!dplyr::where(\(x) is.logical(x) & !suppressWarnings(any(x, na.rm = FALSE)))) |> names() + if (length(all_na) > 0) { + msg <- c("The following places have all NAs {.var {all_na}}") + } else { + msg <- NULL + } + if (length(all_true_or_single_value) > 0) { + msg <- c(msg, "Likely problems in creating or displaying {.var {all_true_or_single_value}}.") + } + cli::cli_abort(c("Internal error, outline elements can't be NA. Please review.", msg, + "Criteria are created in {.fn define_outline_criteria} and {.fn define_outline_criteria_roxy}. + `outline_el` is defined in {.fn display_outline_element}.")) + } + y <- dplyr::mutate( x, has_title_el = diff --git a/tests/testthat/_snaps/outline-criteria.md b/tests/testthat/_snaps/outline-criteria.md index 773e52b..a97cef0 100644 --- a/tests/testthat/_snaps/outline-criteria.md +++ b/tests/testthat/_snaps/outline-criteria.md @@ -6,9 +6,9 @@ o_is_cli_info o_is_commented_code o_is_generic_test - o_is_object_title o_is_roxygen_comment o_is_section_title + o_is_tab_plot_title o_is_test_that o_is_todo_fixme o_is_work_item diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index 98ef4a0..ef7ca3f 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -32,8 +32,8 @@ test_that("o_is_generic_test() works", { expect_true(o_is_generic_test('test_that("Serious things work properly"')) }) -test_that("o_is_object_title() works", { - expect_true(o_is_object_title("title = 'A great'")) +test_that("o_is_tab_plot_title() works", { + expect_true(o_is_tab_plot_title("title = 'A great'")) }) test_that("o_is_section_title() works", { From 3a875df4bdf740928c62db752890c374c89285fc Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 12:21:02 -0400 Subject: [PATCH 12/71] Exclude `@keywords` and `@noRd` (will only need to exclude undocumented S3 methods?) --- R/outline-roxy.R | 11 +++++++++-- tests/testthat/_ref/test-roxygen.R | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index ab9bbbd..da357d5 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -132,8 +132,15 @@ extract_roxygen_tag_location <- function(file, tag) { } join_roxy_fun <- function(file) { - # don't parse noRd tags - parsed_files <- purrr::discard(file, \(x) roxygen2::block_has_tags(x, "noRd")) + # Assuming that only @keywords internal is used, when keywords is specified. + # Will probably have to handle other cases, but this is not recommended. + # https://roxygen2.r-lib.org/reference/tags-index-crossref.html + parsed_files <- purrr::map( + file, + # discard if noRd or has keywords. + \(x) purrr::discard(x, \(y) roxygen2::block_has_tags(y, c("keywords", "noRd"))) + ) + # TODO exclude S3 methods # Return early if no roxy tags if (length(parsed_files) == 0) { return(character(0L)) diff --git a/tests/testthat/_ref/test-roxygen.R b/tests/testthat/_ref/test-roxygen.R index 5e7a4bd..342830a 100644 --- a/tests/testthat/_ref/test-roxygen.R +++ b/tests/testthat/_ref/test-roxygen.R @@ -37,9 +37,9 @@ f_to_be_index_in_outline <- function() { # block not to index ----------------------------------------------------------- #' A title not to be included (internal function) #' -#' A description not to include +#' An internal description not to include #' -#' # heading not to be included +#' # Internal heading not to be included #' #' content #' @keywords internal From f8fe284d0c3e8631a52103ec2fc096e2e2635598 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 12:25:16 -0400 Subject: [PATCH 13/71] Create `define_criteria_roxy()` to define criteria independently for roxygen comments for now (will probably merge in the future PR.) --- R/outline-criteria.R | 73 +++++++++++++++++++++++++++++++++++++------- R/outline-roxy.R | 28 ++++------------- 2 files changed, 68 insertions(+), 33 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index cae0de5..d40f5ec 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -140,13 +140,14 @@ define_outline_criteria <- function(.data, print_todo) { cli::cli_inform("Could not parse roxygen comments in {.file {unparsed_files}}") } parsed_files <- purrr::compact(parsed_files) - outline_roxy <- join_roxy_fun(parsed_files) + processed_roxy <- join_roxy_fun(parsed_files) + outline_roxy <- define_outline_criteria_roxy(processed_roxy) } else { outline_roxy <- NULL } x <- dplyr::mutate( - x |> dplyr::filter(!is_roxygen_comment) |> dplyr::bind_rows(outline_roxy), + x |> dplyr::filter(!is_roxygen_comment), # Problematic when looking inside functions # maybe force no leading space. # TODO strip is_cli_info in Package? only valid for EDA (currently not showcased..) @@ -168,6 +169,10 @@ define_outline_criteria <- function(.data, print_todo) { pkg_version = extract_pkg_version(content, is_news, is_section_title), is_section_title_source = o_is_section_title(content) & stringr::str_detect(content, "[-\\=]{3,}|^\\#'") & !stringr::str_detect(content, "\\@param"), is_tab_or_plot_title = o_is_tab_plot_title(content) & !is_section_title, + # roxygen2 title block + is_object_title = FALSE, + tag = NA_character_, + topic = NA_character_, is_a_comment_or_code = stringr::str_detect(content, "!=|\\|\\>|\\(\\.*\\)"), is_todo_fixme = print_todo & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext) & !is_snap_file, n_leading_hash = nchar(stringr::str_extract(content, "\\#+")), @@ -176,20 +181,66 @@ define_outline_criteria <- function(.data, print_todo) { is_cross_ref = stringr::str_detect(content, "docs_links?\\(") & !stringr::str_detect(content, "@param|\\{\\."), is_function_def = grepl("<- function(", content, fixed = TRUE) & !stringr::str_starts(content, "\\s*#") ) - if (!"before_and_after_empty" %in% names(x)) { - x$before_and_after_empty <- NA - } x <- dplyr::mutate( x, - before_and_after_empty = ifelse( - !is.na(before_and_after_empty), + before_and_after_empty = line == 1 | !nzchar(dplyr::lead(content, default = "")) & !nzchar(dplyr::lag(content)), - before_and_after_empty - ), .by = "file" ) #browser() - x + x |> dplyr::bind_rows(outline_roxy) +} + + +define_outline_criteria_roxy <- function(x) { + # TODO merge with define_outline_criteria + x$is_md <- x$tag %in% c("subsection", "details", "description", "section") + x$is_object_title <- x$tag == "title" + x$line <- as.integer(x$line) + x$file_ext <- "R" + #x$content <- paste0("#' ", x$content) # maybe not? + x$title_el <- NA_character_ + x$title_el_line <- NA_integer_ + x$is_news <- FALSE + x$is_roxygen_comment <- TRUE + x$is_test_file <- FALSE + x$is_snap_file <- FALSE + x$before_and_after_empty <- TRUE + x$is_section_title <- (x$tag %in% c("section", "subsection") & stringr::str_ends(x$content, ":")) | + (x$tag %in% c("details", "description") & stringr::str_detect(x$content, "#\\s")) + x$is_section_title_source <- x$is_section_title + x$is_saved_doc <- TRUE + x$is_chunk_cap <- FALSE + x$is_chunk_cap_next = FALSE + x$is_test_name = FALSE + x$pkg_version = NA_character_ + # a family or concept can be seen as a plot subtitle? + x$is_tab_or_plot_title <- x$tag %in% c("family", "concept") + x$is_cli_info <- FALSE + x$is_cross_ref = FALSE + x$is_function_def = FALSE + x$is_todo_fixme <- FALSE + x$is_a_comment_or_code <- FALSE + x$is_doc_title <- x$line == 1 & x$tag == "title" + x$n_leading_hash = nchar(stringr::str_extract(x$content, "\\#+")) + x$n_leading_hash <- dplyr::case_when( + x$n_leading_hash > 0 ~ x$n_leading_hash, + x$tag == "section" & x$is_section_title_source ~ 1, + x$tag == "subsection" & x$is_section_title_source ~ 2, + .default = 0 + ) + x$content <- dplyr::case_when( + !x$is_section_title ~ x$content, + x$tag == "section" ~ paste0("# ", stringr::str_remove(x$content, ":")), + x$tag == "subsection" ~ paste0("## ", stringr::str_remove(x$content, ":")), + .default = x$content + ) + x$is_second_level_heading_or_more <- ((x$is_section_title_source | x$is_section_title) & x$n_leading_hash > 1) + x$has_inline_markup = FALSE # let's not mess with inline markup + dplyr::filter( + x, + content != "NULL" + ) } -# it is {.file R/outline.R} ------ +# it is {.file R/outline.R} or {.file R/outline-roxy.R} ------ diff --git a/R/outline-roxy.R b/R/outline-roxy.R index da357d5..1cbc076 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -207,28 +207,12 @@ join_roxy_fun <- function(file) { ) |> dplyr::relocate( file, topic, content, line, tag - ) |> - dplyr::mutate( - is_md = tag %in% c("subsection", "details", "description", "section"), - # content = paste0("#' ", outline_el), - is_object_title = tag == "title", - line = as.integer(line), - file_ext = "R", - tile_el = NA_character_, - title_el_line = NA_integer_, - is_news = FALSE, - is_roxygen_comment = TRUE, - is_test_file = FALSE, - is_snap_file = FALSE, - before_and_after_empty = TRUE, - is_section_title = TRUE, - is_section_title_source = TRUE, - is_saved_doc = TRUE, - has_inline_markup = FALSE # let's not mess with inline markup - ) |> - dplyr::filter( - content != "NULL" - ) + ) |> dplyr::mutate(id = dplyr::row_number()) |> + tidyr::separate_longer_delim(content, delim = "\n") |> + dplyr::mutate(n = dplyr::n() , + line = seq(from = line[1], length.out = n[1], by = 1), + .by = id) |> + dplyr::filter(nzchar(content)) |> dplyr::select(-id, -n) } # helper for interactive checking ----------- From 92335a4b1873511ea3b4fe50ab28d43575e26fec Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 12:25:24 -0400 Subject: [PATCH 14/71] Update snapshots --- README.md | 777 +++++++++++-------------------- tests/testthat/_snaps/outline.md | 19 +- 2 files changed, 270 insertions(+), 526 deletions(-) diff --git a/README.md b/README.md index e150f31..54942ef 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 1.55s 1.55s 0.647 65.9MB 5.17 +#> 1 outline <- proj_outline() 1.56s 1.56s 0.642 66.8MB 5.78 ```
@@ -183,522 +183,269 @@ outline #> `i` A great title #> `i` TODO improve this Viz!- `Done✔?` #> +#> ── `LICENSE.md` MIT License +#> #> ── `R/dplyr-plus.R` dplyr extra +#> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` +#> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` +#> `i` summarise with total +#> `i` with dplyr::filter +#> `i` dplyr extensions +#> `i` dplyr extensions +#> `i` family dplyr extensions +#> +#> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- +#> `i` base identity functions +#> `i` dplyr identity functions with small tweaks +#> `i` dplyr identity without tweaks +#> `i` dplyr extensions identity +#> `i` helpers +#> `i` Use cases / advantages +#> `i` Caution +#> +#> ── `R/files-conflicts.R` +#> `i` TODO insert in either proj_outline, or rename_file- `Done✔?` +#> `i` TODO probably needs a `detect_genuine_path()`- `Done✔?` +#> `i` Helpers +#> `i` TODO Add false positive references- `Done✔?` +#> `i` TODO fs::path and file.path should be handled differently- `Done✔?` +#> +#> ── `R/import-standalone-types-check.R` +#> `i` Scalars +#> `i` Vectors +#> +#> ── `R/open.R` +#> `i` FIXME why is this code like this?- `Done✔?` +#> `i` TODO structure and summarise information.- `Done✔?` +#> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` +#> `i` document manipulation helpers +#> `i` document manipulation helpers +#> +#> ── `R/outdated-pkgs.R` +#> `i` TODO figure out pad :)- `Done✔?` +#> +#> ── `R/outline-criteria.R` +#> `i` Add variable to outline data frame +#> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` +#> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` +#> `i` it is 'R/outline.R' +#> +#> ── `R/outline-roxy.R` +#> `i` TODO when stable delete- `Done✔?` +#> `i` TODO Delete when stable debugging- `Done✔?` +#> `i` TODO Delete when stable for debugging- `Done✔?` +#> `i` TODO exclude S3 methods- `Done✔?` +#> `i` helper for interactive checking +#> +#> ── `R/outline.R` `proj_outline()` +#> `i` `file_outline()` +#> `i` File outline +#> `i` Print method +#> `i` Step: tweak outline look as they show +#> `i` TODO reanable cli info- `Done✔?` +#> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` +#> `i` If `work_only` is set to `TRUE`, the function will only return outline of the `# WORK` comment +#> +#> ── `R/proj-list.R` +#> `i` TODO maybe add a max?- `Done✔?` +#> `i` TODO improve on this message- `Done✔?` +#> `i` project management helpers +#> `i` project management helpers +#> `i` project management helpers +#> +#> ── `R/rename.R` +#> `i` After here, we start doing some renaming real situations +#> `i` Helpers +#> `i` helpers for computing scope of renaming +#> `i` TODO measure of string proximity- `Done✔?` +#> `i` Prevent renaming if something is going on +#> `i` FIXME maybe not fail while testing- `Done✔?` +#> `i` TODO Check that old file is more recent- `Done✔?` +#> `i` Use case +#> +#> ── `R/todo.R` +#> `i` TODO think about maybe using todo = clipr::read_clip()- `Done✔?` +#> `i` TODO nice to have, but would need to extract duplicates- `Done✔?` +#> `i` Helpers +#> +#> ── `R/utils-proj.R` usethis adaptions utils +#> `i` Active project / document +#> +#> ── `R/utils.R` OS utils +#> +#> ── `tests/testthat/_ref/many-titles.md` The title is the only outline element +#> `i` Another title +#> `i` Second level +#> `i` TODO this is an item- `Done✔?` +#> `i` Last title +#> +#> ── `tests/testthat/_ref/my-analysis.md` My doc title +#> `i` A section +#> `i` Dashboard card +#> `i` A code section +#> `i` A subsection +#> `i` A section2 +#> `i` A long ggplot2 title +#> `i` A code section +#> +#> ── `tests/testthat/_ref/my-analysis.R` Analyse my streets +#> `i` Read my streets () data +#> `i` data wrangling +#> `i` Write my streets +#> `i` TODO Create a new version- `Done✔?` +#> `i` 'R/my-file.R' +#> `i` Section title +#> +#> ── `tests/testthat/_ref/single-title.md` The title is the only outline element +#> +#> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error +#> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing +#> `i` a family to include +#> +#> ── `tests/testthat/_ref/test-roxygen.R` +#> `i` Use 'tests/testthat/_ref/test-roxygen-safeguard.R' for output testing +#> `i` Complete block for exported function with headings +#> `i` block not to index +#> `i` Topic to index +#> `i` data to index +#> `i` TODO add data block example to index- `Done✔?` +#> `i` First to be included +#> `i` A second-level heading in description to be included? +#> `i` A detail first level-heading to be included +#> `i` A detail second-level heading to be included +#> `i` A second-level heading in description to be included? +#> `i` A detail first level-heading to be included +#> `i` A detail second-level heading to be included +#> `i` First to be included +#> `i` a family to include +#> `i` a family to include +#> +#> ── `tests/testthat/_snaps/case-if-any.md` +#> `i` wrong cases error +#> +#> ── `tests/testthat/_snaps/dplyr-plus.md` +#> `i` adds rows in front, but warns the user +#> +#> ── `tests/testthat/_snaps/eda-identity.md` +#> `i` Side effects are what's intended in interactive sessions +#> +#> ── `tests/testthat/_snaps/outline-criteria.md` +#> `i` No outline criteria are untested +#> +#> ── `tests/testthat/_snaps/outline.md` +#> `i` alpha and work_only arguments work +#> `i` pattern works as expected +#> +#> ── `tests/testthat/_snaps/rename.md` +#> `i` Helper files returns the expected input +#> +#> ── `tests/testthat/_snaps/todo.md` +#> `i` Marking a TODO item as done works +#> +#> ── `tests/testthat/test-case-if-any.R` +#> `i` case_if_any() basic work +#> `i` wrong cases error +#> `i` case_if_any() can use a newly created variable (#8) +#> +#> ── `tests/testthat/test-dplyr-plus.R` +#> `i` filter_if_any() errors correctly when using `by` instead of `.by` +#> `i` filter_if_any() errors with across() +#> `i` TODO improve this error- `Done✔?` +#> `i` adds rows in front, but warns the user +#> `i` summarise_with_total() keeps factors +#> `i` na_if2() works with expr and values +#> +#> ── `tests/testthat/test-eda-identity.R` +#> `i` Returns identity +#> `i` Side effects are what's intended in interactive sessions +#> +#> ── `tests/testthat/test-markup.R` +#> `i` link_gh_issue() + markup_href() work +#> +#> ── `tests/testthat/test-named.R` +#> `i` min/max/unique_named() return named output +#> `i` max_named() and unique_named() work with unnamed vectors +#> +#> ── `tests/testthat/test-open.R` +#> `i` open_rs_doc() errors in non-interactive sessions +#> +#> ── `tests/testthat/test-outline-criteria.R` Test individual outline elements +#> `i` No outline criteria are untested +#> +#> ── `tests/testthat/test-outline-roxy.R` +#> `i` roxy tags don't error +#> +#> ── `tests/testthat/test-outline.R` +#> `i` alpha and work_only arguments work +#> `i` file_outline() is a data frame +#> `i` TODO change tests for data frame size when stable (efficiency). As stil…- `Done✔?` +#> `i` file_outline() with only title doesn't error +#> `i` file_outline() contains function calls +#> `i` dir_outline() works with no error +#> +#> ── `tests/testthat/test-rename.R` +#> `i` Helper files returns the expected input +#> `i` force and action are deprecated +#> +#> ── `tests/testthat/test-screenshot.R` +#> `i` screenshot() does nothing in non-interactive sessions +#> +#> ── `tests/testthat/test-todo.R` +#> `i` Marking TODO as done detects tags +#> `i` TODO items are correctly stripped +#> +#> ── `tests/testthat/test-utils.R` +#> `i` Windows is recognized correctly. +#> +#> ── `TODO.R` +#> `i` TODO screenshot make the behaviour different when vignettes vs articl…- `Done✔?` +#> `i` TODO screenshot RStudio addin to insert the code directly in the qmd …- `Done✔?` +#> `i` TODO use_family() to edit .R file to add @family data frames tags to ro…- `Done✔?` +#> `i` TODO mutate_identity redundant if the focus pillar PR was merged. r-lib/pillar#585 ()- `Done✔?` +#> `i` TODO rename if many matches, separate those with the exact path.- `Done✔?` +#> `i` TODO outline make ggtitle work- `Done✔?` +#> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` +#> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` +#> `i` TODO outline renable cli info.- `Done✔?` +#> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` +#> `i` TODO outline avoid evaluating in current env.- `Done✔?` +#> `i` TODO wrap regexps in functions- `Done✔?` +#> `i` TODO outline remove examples from outline. Sometimes commented code i…- `Done✔?` +#> `i` TODO outline roxygen comments processing should be left to `roxygen2::parse_file()`- `Done✔?` +#> `i` TODO outline show key like `pak::pkg_deps_tree()` does.- `Done✔?` +#> `i` TODO outline roxygen function title- `Done✔?` +#> `i` TODO outline remove ggtext markup from plot title.- `Done✔?` +#> `i` FIXME outline comments are now interpreted as section- `Done✔?` +#> `i` TODO outline todos in qmd file inside html comment- `Done✔?` +#> `i` TODO reframe more than one issue. nw drive- `Done✔?` +#> `i` TODO delete generated files- `Done✔?` +#> `i` TODO [proj_file] to accesss data (return the path in this case?)- `Done✔?` +#> `i` TODO [check_referenced_files] doesn't check for 'R/file.R'- `Done✔?` +#> `i` TODO explain rationale behind `work_only`. Suggest to transform to TODO…- `Done✔?` +#> `i` TODO outline Show function call if exported + not internal + bonus if…- `Done✔?` +#> `i` TODO title of file could be function title if it is first element [proj…- `Done✔?` +#> `i` TODO rename_files should be less noisy about project name file- `Done✔?` +#> +#> ── `R/proj-reuseme.R` +#> `i` Setup +#> `i` Capabilities. +#> `i` project management helpers +#> +#> ── `NEWS.md` +#> `i` reuseme (development version) +#> `i` reuseme 0.0.2 +#> `i` reuseme 0.0.1 +#> +#> ── `README.Rmd` +#> `i` reuseme +#> `i` Installation +#> `i` Getting started +#> `i` Example +#> `i` hello +#> `i` Extend usethis functionality +#> `i` Proposing a data analysis workflow +#> `i` Outline speed ``` - #> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` - #> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` - #> `i` summarise with total - #> `i` Count observations by group and compute percentage - #> `i` Subset rows using their positions - #> `i` Explore all rows in a random group - #> `i` Keep rows that match one of the conditions - #> `i` Elegant wrapper around filter and pull - #> `i` Compute a summary for one group with the total included. - #> `i` Transform to NA any of the condition - #> `i` `count_pct()` lets you quickly count the unique values of one or more - #> variables: `df |> count_pct(a, b)` It calculates the percentage by group - #> afterwards - #> `i` A wrapper around `dplyr::bind_rows()`, `dplyr::slice_min()`, `dplyr::slice_max()` - #> `i` Compared to `slice_sample()` `slice_group_sample` will return all rows corresponding to a group. - #> `i` The `filter_if_any()` function is used to subset a data frame, retaining all - #> rows that satisfy **at least one of** your conditions. - #> To be retained, the row must produce a value of `TRUE` for - #> **one of the conditions**. Note that when a condition evaluates to `NA` the - #> row will be dropped, (hence this function) unlike base subsetting with `[`. - #> `i` It can be very useful when trying to extract a value from somewhere, and you - #> have one col that represents the unique id. - #> `i` This function is useful to create end tables, apply the same formula to a group and to its overall. - #> You can specify a personalized `Total` value with the `.label` argument. You - #> You should only use the output from `summarise_with_total()` with `tidyr::pivot_wider()`, - #> write data to a spreadsheet, `gt::gt()` after that. Don't try to do more computing afterwards. - #> It can also be used for plotting - #> Changes the `.by` variable to a factor. - #> `i` This function is similar to `dplyr::na_if()`, but it has 2 differences. the - #> values of `y` are never recycled. There are two ways to provide the condition. - #> As values or as a logical vector. - #> `i` Wrapper function around [dplyr::count()] - #> `i` The reason to be of this function is to simplify a call like - #> - #> ```r - #> # with dplyr::filter - #> dat |> dplyr::filter(vs == 1 | is.na(vs)) - #> data |> - #> dplyr::mutate(cond1 = vs == 1, cond2 = is.na(vs)) |> - #> dplyr::filter(dplyr::if_any(starts_with("cond"))) - #> dat |> filter_if_any(vs == 1, is.na(vs)) - #> ``` - #> - #> Basically, this is just a shortcut to - #> `mutate(.data, new_lgl_vars)` + `filter(if_any(new_lgl_vars))` + `select(-new_lgl_vars)`. - #> It allows mutate_like syntax in `filter(if_any(...))`. - #> - #> Caution: still doesn't work with [dplyr::across()], use the regular - #> `filter(if_any())` syntax. - #> `i` dplyr extensions - #> `i` dplyr extensions - #> `i` family dplyr extensions - - #> - #> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- - #> `i` base identity functions - #> `i` dplyr identity functions with small tweaks - #> `i` dplyr identity without tweaks - #> `i` dplyr extensions identity - #> `i` helpers - #> `i` Helpers that return the same value - #> `i` They all share the `*_identity` suffix, they are silent in non-interactive - #> sessions. They are very handy to create clickable hyperlinks that do not - #> modify the current state of the analysis. - #> - #> They are inspired by [pillar::glimpse()], [tibble::view()]. - #> - #> Look at the original functions for the other parameters. - #> - #> # Use cases / advantages - #> - #> * Like many other reuseme functions, they are most useful in interactive sessions - #> * print the result in interactive sessions (quiet in non-interactive.) - #> * Create runnable hyperlinks (In August 2023, RStudio forbids runnable - #> hyperlinks of base functions, or non-package functions. (i.e. that don't have `::`)) - #> * Use in pipelines to explore the data - #> * Use [rlang::is_interactive()] over [base::interactive()] as it's easier to - #> control and test with `options(rlang_interactive)` - #> * Use the original functions for your final results. - #> * `count_identity()` also prints percentages. - #> * `slice_identity()` can be useful to resolve many-to-many warnings from - #> dplyr join functions. - #> - #> # Caution - #> - #> * Don't put those at the end of a pipeline - #> * Don't name the first argument, to avoid conflicts in case a column in the - #> data is named `x`. - #> * Some functions have small tweaks - #> * `mutate_identity()` only prints the distinct values, and uses - #> `.keep = "used"`, `.before = 0`, unless specified to improve the display. - #> * `count_identity()` is a wrapper of [count_pct()] - #> (itself a wrapper of `dplyr::count()`), - #> * `count_identity()` may fail if there is already a variable named `n`. - #> * `slice_min/max_identity()` relocates the target column at the beginning. - #> * `filter_identity()` prints a short message if no rows are returned. - #> - #> ── `R/files-conflicts.R` Check if files referenced in source files exist in a current dir - #> `i` TODO insert in either proj_outline, or rename_file- `Done✔?` - #> `i` TODO probably needs a `detect_genuine_path()`- `Done✔?` - #> `i` Helpers - #> `i` TODO Add false positive references- `Done✔?` - #> `i` TODO fs::path and file.path should be handled differently- `Done✔?` - #> `i` Check if outdated or non-existent file is. - #> `i` 1. It goes through the source files (.R/.qmd etc.), - #> 2. It identifies data files (.csv, .xlsx) read or written - #> 3. Search on the system if these files exist. - #> `i` If `quiet = FALSE` (default) will give a hint of where the file is referenced. - #> `i` Still WIP, so you can add code for false positive as needed. - #> - #> - #> To find genuine referenced files, we exclude different paths - #> - #> 1. Those created with `fs::path()` or `file.path()` or `glue::glue()` - #> 2. Those that are checked for `fs::file_exists()`, `file.exists()` - #> 3. Deleted with `fs::file_delete()`, `unlink()` - #> - #> ── `R/import-standalone-types-check.R` - #> `i` Scalars - #> `i` Vectors - #> - #> ── `R/open.R` Open a Document in RStudio - #> `i` FIXME why is this code like this?- `Done✔?` - #> `i` TODO structure and summarise information.- `Done✔?` - #> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` - #> `i` Copy the active document to the same location - #> `i` Delete the active RStudio document safely - #> `i` Wrapper around [rstudioapi::documentOpen()], but with `fs paths`, for consistency. - #> If the file could not be opened, a clickable hyperlink is displayed. - #> `i` The goal is to provide things that RStudio or usethis doesn't provide natively. - #> `i` `r lifecycle::badge('experimental')` - #> - #> Gathers informative summary about the document you are about to delete. - #> - #> - #> Will delete more easily if file name starts with `temp-`, if file is untracked and recent. - #> `i` * `active_rs_doc()` is a wrapper around [rstudioapi::documentPath()] that handles - #> unsaved files gracefully - #> `i` For example, `active_rs_doc_rename()` will not happen, because it is already easy - #> to do so via the RStudio IDE. - #> `i` document manipulation helpers - #> `i` document manipulation helpers - #> - #> ── `R/outdated-pkgs.R` Looks for outdated packages - #> `i` TODO figure out pad :)- `Done✔?` - #> `i` Only checks for binaries, which has the advantage of not notifying you when - #> packages are newly available on CRAN, but without the binaries available. - #> Installing packages from source can be longer, hence the advantage of - #> waiting a few days for binaries. - #> - #> It takes advantage of pak's capacities to allow you to install packages on - #> Windows without restarting session. - #> - #> ── `R/outline-criteria.R` - #> `i` Add variable to outline data frame - #> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` - #> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` - #> `i` it is 'R/outline.R' - #> `i` outline_criteria - #> `i` * is test title - #> * is a todo item - #> * is_roxygen_line - #> * is_tab_title - #> - #> ── `R/outline-roxy.R` Extract roxygen tag - #> `i` TODO when stable delete- `Done✔?` - #> `i` TODO Delete when stable debugging- `Done✔?` - #> `i` TODO Delete when stable for debugging- `Done✔?` - #> `i` helper for interactive checking - #> `i` Tell me what this does - #> `i` Section to extract - #> - #> Well this is a section - #> - #> ── `R/outline.R` `proj_outline()` - #> `i` `file_outline()` - #> `i` File outline - #> `i` Print method - #> `i` Step: tweak outline look as they show - #> `i` TODO reanable cli info- `Done✔?` - #> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` - #> `i` Print interactive outline of file sections - #> `i` TODO` items- `Done✔?` - #> `i` `proj_outline()` and `dir_outline()` are wrapper of `file_outline()`. - #> - #> The parser is very opinioneted and is not very robust as it is based on regexps. - #> For a better file parser, explore other options, like lightparser () for Quarto, `{roxygen2}` - #> - #> Will show TODO items and will offer a link to [mark them as - #> complete][complete_todo()]. - #> - #> ── `R/proj-list.R` Opens a RStudio project in a new session - #> `i` TODO maybe add a max?- `Done✔?` - #> `i` TODO improve on this message- `Done✔?` - #> `i` Access the file outline within other project - #> `i` Returns a named project list options - #> `i` If not specified, will generate hyperlinks that call [usethis::proj_activate()]. - #> `proj_switch()` looks at `options(reuseme.reposdir)`. - #> `i` It can be used as [file_outline()] + `proj`. - #> `i` It peeks `options(reuseme.reposdir)` to find projects. - #> `i` project management helpers - #> `i` project management helpers - #> `i` project management helpers - #> - #> ── `R/rename-files.R` Rename an output or a data file and watch for references - #> `i` After here, we start doing some renaming real situations - #> `i` Helpers - #> `i` helpers for computing scope of renaming - #> `i` TODO measure of string proximity- `Done✔?` - #> `i` Prevent renaming if something is going on - #> `i` FIXME maybe not fail while testing- `Done✔?` - #> `i` TODO Check that old file is more recent- `Done✔?` - #> `i` `r lifecycle::badge("experimental")` - #> - #> This function can improve your workflow. - #> It is inspired by [usethis::rename_files()], but its scope - #> is more oriented towards analysis script. - #> - #> # Use case - #> - #> Let's say you have an analysis and work on a certain subject. - #> You want to rename a figure for clarity. - #> For example, you had an input file named `data/my-streets.csv` and you now want to - #> rename it to - #> - #> Here is what `rename_files2()` does for you, before it renames files. - #> - #> 1. Look for potential name conflict - #> 2. Look for data frame name conflicts - #> 3. Sends information to clipboard - #> - #> Will work well for you if you tend to name your objects using snake case and - #> naming objects with snake case or kebab-case. - #> - #> The philosophy is to inform you of manual steps required before actually - #> performing file renaming. - #> - #> A way to be less strict is to us - #> - #> ── `R/use-todo.R` Add a TODO list by project to a TODO.R file in the base directory - - #> `i` TODO think about maybe using todo = clipr::read_clip()- `Done✔?` - #> `i` TODO nice to have, but would need to extract duplicates- `Done✔?` - #> `i` Helpers - #> `i` Remove a TODO/WORK/FIXME item from a file - #> `i` Creates or edits a `TODO.R` file to store your TODOs. - #> By default it will write in the current RStudio project. - #> `i` Function meant to be wrapped as ```` hyperlinks with [file_outline()]. - #> It basically removes a line from a file. - #> `i` If you use `use_todo()` with a version-control repository, you may want to - #> use `usethis::use_git_ignore("TODO.R")` if you don't want your `TODO.R` file - #> - #> to be included in git. If using in a package directory, use - #> `usethis::use_build_ignore("TODO.R")` to prevent a note in `R CMD CHECK` - - #> - #> ── `R/utils-proj.R` usethis adaptions utils - #> `i` Active project / document - #> `i` copy of usethis::is_package - #> - #> ── `R/utils.R` OS utils - #> - #> ── `tests/testthat/_ref/many-titles.md` - #> `i` TODO this is an item- `Done✔?` - #> - #> ── `tests/testthat/_ref/my-analysis.md` My doc title - #> `i` Dashboard card - #> `i` A long ggplot2 title - #> - #> ── `tests/testthat/_ref/my-analysis.R` Analyse my streets - #> `i` Read my streets () data - #> `i` data wrangling - #> `i` Write my streets - #> `i` TODO Create a new version- `Done✔?` - #> `i` 'R/my-file.R' - #> `i` Section title - #> - #> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error - #> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing - #> `i` Title - #> `i` An S3 method not to be include - #> `i` content - #> `i` a family to include - #> - #> ── `tests/testthat/_ref/test-roxygen.R` - #> `i` Use 'tests/testthat/_ref/test-roxygen-safeguard.R' for output testing - #> `i` Complete block for exported function with headings - #> `i` block not to index - #> `i` Topic to index - #> `i` data to index - #> `i` TODO add data block example to index- `Done✔?` - #> `i` A title to be included - #> `i` A title not to be included (internal function) - #> `i` A title to be included - #> `i` First to be included: - #> - #> Content not to be included - #> `i` A description not to be included - #> `i` A description not to include - #> `i` A description not to be included - #> `i` A second-level heading in description to be included? - #> - #> # A detail first level-… - #> `i` heading not to be included - #> - #> content - #> `i` A second-level heading in description to be included? - #> - #> # A detail first level-… - #> `i` a family to include - #> `i` a family to include - #> - #> ── `tests/testthat/test-case-if-any.R` - #> `i` case_if_any() basic work - #> `i` wrong cases error - #> `i` case_if_any() can use a newly created variable (#8) - #> - #> ── `tests/testthat/test-dplyr-plus.R` - #> `i` filter_if_any() errors correctly when using `by` instead of `.by` - #> `i` filter_if_any() errors with across() - #> `i` TODO improve this error- `Done✔?` - #> `i` adds rows in front, but warns the user - #> `i` summarise_with_total() keeps factors - #> `i` na_if2() works with expr and values - #> - #> ── `tests/testthat/test-eda-identity.R` - #> `i` Returns identity - #> `i` Side effects are what's intended in interactive sessions - #> - #> ── `tests/testthat/test-link-elements.R` - #> `i` link_gh_issue() + markup_href() work - #> - #> ── `tests/testthat/test-named.R` - #> `i` min/max/unique_named() return named output - #> `i` max_named() and unique_named() work with unnamed vectors - #> - #> ── `tests/testthat/test-open.R` - #> `i` open_rs_doc() errors in non-interactive sessions - #> - #> ── `tests/testthat/test-outline-criteria.R` Test individual outline elements - #> `i` No outline criteria are untested - #> - #> ── `tests/testthat/test-outline-roxy.R` - #> `i` roxy tags don't error - #> - #> ── `tests/testthat/test-outline.R` - #> `i` alpha and work_only arguments work - #> `i` file_outline() is a data frame - #> `i` TODO change tests for data frame size when stable (efficiency). As stil…- `Done✔?` - #> `i` file_outline() with only title doesn't error - #> `i` file_outline() contains function calls - #> `i` dir_outline() works with no error - #> - #> ── `tests/testthat/test-rename-files.R` - #> `i` Helper files returns the expected input - #> `i` force and action are deprecated - #> - #> ── `tests/testthat/test-screenshot.R` - #> `i` screenshot() does nothing in non-interactive sessions - #> - #> ── `tests/testthat/test-use-todo.R` - #> `i` Marking TODO as done detects tags - #> `i` TODO items are correctly stripped - #> - #> ── `tests/testthat/test-utils.R` - #> `i` Windows is recognized correctly. - #> - #> ── `TODO.R` - #> `i` TODO screenshot make the behaviour different when vignettes vs articl…- `Done✔?` - #> `i` TODO screenshot RStudio addin to insert the code directly in the qmd …- `Done✔?` - #> `i` TODO use_family() to edit .R file to add @family data frames tags to ro…- `Done✔?` - #> `i` TODO mutate_identity redundant if the focus pillar PR was merged. r-lib/pillar#585 ()- `Done✔?` - #> `i` TODO rename if many matches, separate those with the exact path.- `Done✔?` - #> `i` TODO outline make ggtitle work- `Done✔?` - #> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` - #> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` - #> `i` TODO outline renable cli info.- `Done✔?` - #> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` - #> `i` TODO outline avoid evaluating in current env.- `Done✔?` - #> `i` TODO wrap regexps in functions- `Done✔?` - #> `i` TODO outline remove examples from outline. Sometimes commented code i…- `Done✔?` - #> `i` TODO outline roxygen comments processing should be left to `roxygen2::parse_file()`- `Done✔?` - #> `i` TODO outline show key like `pak::pkg_deps_tree()` does.- `Done✔?` - #> `i` TODO outline roxygen function title- `Done✔?` - #> `i` TODO outline remove ggtext markup from plot title.- `Done✔?` - #> `i` FIXME outline comments are now interpreted as section- `Done✔?` - #> `i` TODO outline todos in qmd file inside html comment- `Done✔?` - #> `i` TODO reframe more than one issue. nw drive- `Done✔?` - #> `i` TODO delete generated files- `Done✔?` - #> `i` TODO [proj_file] to accesss data (return the path in this case?)- `Done✔?` - #> `i` TODO [check_referenced_files] doesn't check for 'R/file.R'- `Done✔?` - #> `i` TODO explain rationale behind `work_only`. Suggest to transform to TODO…- `Done✔?` - #> `i` TODO outline Show function call if exported + not internal + bonus if…- `Done✔?` - #> `i` TODO title of file could be function title if it is first element [proj…- `Done✔?` - #> - #> ── `R/browse-pkg.R` Browse pkgdown site if it exists - #> `i` A wrapper around [usethis::browse_package()] that aims at identifying the - #> package website. It looks up for a link in DESCRIPTION. - #> - #> ── `R/case-if-any.R` case-when, but checks for all matches, returns a character - #> `i` Each case is evaluated for **all** cases and a character vector match - #> for each element determines the corresponding value in the output vector. - #> If no cases match, the `.default` is used. - #> The function allows you to assign multiple values to a character value, which - #> can be very handy for EDA. - #> - #> ── `R/escape-inline-markup.R` - #> `i` escape inline markup in case of problems - #> `i` Is inline markup valid? - #> `i` inline markup internal helpers - #> - #> ── `R/import-standalone-obj-type.R` - #> `i` Return English-friendly type - #> `i` Return OO type - #> - #> ── `R/link-elements.R` Create a markdown link to a GitHub issue - #> `i` Create a cli href with a markdown link - #> `i` In RStudio, links to issues are automatically recognized. - #> This function creates intermediate markdown links to entries of the form rstudio/rstudio#1100 () - #> `i` Transforms `[text](url)` -> `text ()` - #> `i` Note: doesn't (yet) support without / - #> - #> Basically trransform repo/org#xx -> repo/org#xx (). - #> - #> Afterwards, we use [markup_href()] to create a cli link - #> `i` inline markup internal helpers - #> `i` inline markup internal helpers - #> - #> ── `R/named.R` Helpers that can return a named vector - #> `i` Base R keeps names in various places, but drops them elsewhere - #> These functions are some that I use frequently, like `max`, or `unique` - #> - #> ── `R/proj-reuseme.R` Interact with different RStudio projects - - #> `i` The package offers many ways to interact with different local RStudio projects. - #> `i` Setup - #> - #> To take advantage of this functionality, you first have to set `options(reuseme.reposdir)` in - #> your .Rprofile file. Access it with [usethis::edit_r_profile()]. - #> - #> I would recommend you add the following. It works better if you store your RStudio - #> projects in common directories. - #> - #> Inspired by [usethis options][usethis::usethis_options] - #> - #> ``` - #> if (interactive()) NULL - #> ``` - #> - #> # Capabilities. - #> - #> Assumes that you have a project named `"learning"` - #> A project outline - #> - #> ``` - #> proj_outline(proj = "learning) - #> ``` - #> - #> Add a TODO item to the `learning` project - #> - #> ``` - #> use_todo("learning::Learn this") - #> ``` - #> - #> Get file [outline][proj_outline()] of the `file.R` in "learning" - #> - #> ``` - #> proj_file("file", "learning") - #> ``` - #> - #> Move to a new project in the same session - #> - #> ``` - #> proj_switch("learning") - #> ``` - #> - #> A lot of these features are already present in RStudio and with usethis. - #> However, when managing many projects, the recent projects list can be more difficult - #> to handle. - #> Passing the full project name to `usethis::proj_activate()` was too long. - #> `i` project management helpers - - #> - #> ── `R/quarto-help.R` Show links to Quarto documentation of interest - #> `i` Very opinionated of links I need to access periodically. Easily - #> accessible from R console. - #> - #> ── `R/reuseme-package.R` - #> `i` reuseme: Collections of Utility Functions to Work Across Projects - #> `i` Allows you to browse current projects, rename files safely, add screenshots to project on Windows. It is also my personal library and contains wrapper around common functions, from dplyr and readxl. It takes advantage of cli hyperlinks. Finally, it provides a custom print method for tibbles, inspired by janitor, and readr. - #> - #> ── `R/screenshot.R` Save the current image in clipboard to png in your active directory - #> `i` The screenshot will be saved as `.png` to a directory following these rules - #> 1. In a regular RStudio project (or a Quarto book), it will be saved to a `images/` directory - #> 2. In a package project, it will be saved in a `man/figures` directory - #> 3. In a Quarto Blog project, it will save in the current post's folder. - #> 4. You can always override these defaults by setting `dir` - #> - #> After using the shortcut Win + Shift + S, you can call this function! - #> `i` If no file name is supplied, a file named `image0*.png` will be created. - #> The function then prompts you to rename the file with a more expressive name. - #> It will continue the numbering if a file named image exists. - #> - #> Still have to validate if it works on macOS, as it is not clear whether the - #> image goes to the clipboard by default - #> - #> The maximum number of images in a folder is 99. (only padding 2), should be enough. - #> - #> You should not be able to overwrite a screenshot with a generic name, only a - #> named one as it is possible you may require to retake your screenshot. - #> - #> ── `README.Rmd` - #> `i` hello -

diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index 4ff5dd4..c372f55 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -15,27 +15,24 @@ `i` Dashboard card Message + -- `many-titles.md` The title is the only outline element + Output + `i` Another title + `i` Last title + `i` Second level + `i` TODO this is an item- `Donev?` + Message + -- `my-analysis.R` Analyse my streets Output - `i` A real one - `i` A true one `i` TODO Create a new version- `Donev?` `i` Read my streets () data - `i` Roxygen section `i` Section title `i` Write my streets `i` data wrangling `i` 'R/my-file.R' Message - -- `many-titles.md` The title is the only outline element - Output - `i` Another title - `i` Last title - `i` Second level - `i` TODO this is an item- `Donev?` - Message - -- `single-title.md` The title is the only outline element # alpha and work_only arguments work From 38a71a0e61a008bed0e0270336fbeedee3872e01 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 13:02:44 -0400 Subject: [PATCH 15/71] Handle empty case better --- R/outline-criteria.R | 3 +- R/outline-roxy.R | 132 ++++++++++++++++++++++--------------------- README.md | 120 +++++++++++++++------------------------ man/complete_todo.Rd | 4 +- man/link_gh_issue.Rd | 2 +- man/markup_href.Rd | 2 +- man/rename_files2.Rd | 2 +- man/use_todo.Rd | 2 +- 8 files changed, 123 insertions(+), 144 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index d40f5ec..ca09774 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -188,7 +188,8 @@ define_outline_criteria <- function(.data, print_todo) { .by = "file" ) #browser() - x |> dplyr::bind_rows(outline_roxy) + res <- dplyr::bind_rows(x, outline_roxy) + res <- dplyr::arrange(x, .data$file, .data$line) } diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 1cbc076..575ff71 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -11,7 +11,6 @@ #' @returns A named list with name = file:line, and element is the section title #' @examples #' extract_roxygen_tag_location(tag = "title") - extract_roxygen_tag_location <- function(file, tag) { # suppressMessages(aa <- roxygen2::parse_file(file)) # browser() @@ -54,53 +53,53 @@ extract_roxygen_tag_location <- function(file, tag) { # browser() val <- withCallingHandlers( purrr::map2(pos, objects, \(x, obj_name) { - el <- x$val - el_has_names <- !is.null(names(el)) - - if (length(el) == 1 && !el_has_names) { - el <- paste0( - el, "____", obj_name + el <- x$val + el_has_names <- !is.null(names(el)) + + if (length(el) == 1 && !el_has_names) { + el <- paste0( + el, "____", obj_name + ) + names(el) <- x$line + return(el) + } + if (tag %in% c("description", "details") && !el_has_names) { + # TODO when stable delete + # print(x$val) + # print(el_has_names) + # cli::cli_inform("return early (no headings)") + return(NULL) + } + # use raw instead + lines <- stringr::str_split_1(x$raw, "\n") + # browser() + keep <- which(o_is_section_title(lines)) + + if (length(keep) == 0L) { + # TODO Delete when stable debugging + # cli::cli_inform(" No section title detected") + return(NULL) + } + # line position. + line_pos <- x$line + seq_along(lines) - 1L + final_lines_to_include <- lines[keep] + # Will not make this transformation and will consider roxygen comments to be + # final_lines_to_include <- stringr::str_remove(final_lines_to_include, "^#+\\s") + + final_lines_to_include <- paste0(final_lines_to_include, "____", obj_name) + names(final_lines_to_include) <- line_pos[keep] + # TODO Delete when stable for debugging + # if (length(final_lines_to_include) != 1) { + # cli::cli_warn("el resulted to {.val {final_lines_to_include}}", "using first element for now") + # } + final_lines_to_include + }), + error = function(e) { + cli::cli_abort( + "For tag = {tag}, obj_name = {objects}, wrong size, should be {length(pos)}" ) - names(el) <- x$line - return(el) - } - if (tag %in% c("description", "details") && !el_has_names) { - # TODO when stable delete - # print(x$val) - # print(el_has_names) - # cli::cli_inform("return early (no headings)") - return(NULL) } - # use raw instead - lines <- stringr::str_split_1(x$raw, "\n") - # browser() - keep <- which(o_is_section_title(lines)) - - if (length(keep) == 0L) { - # TODO Delete when stable debugging - # cli::cli_inform(" No section title detected") - return(NULL) - } - # line position. - line_pos <- x$line + seq_along(lines) - 1L - final_lines_to_include <- lines[keep] - # Will not make this transformation and will consider roxygen comments to be - # final_lines_to_include <- stringr::str_remove(final_lines_to_include, "^#+\\s") - - final_lines_to_include <- paste0(final_lines_to_include, "____", obj_name) - names(final_lines_to_include) <- line_pos[keep] - # TODO Delete when stable for debugging - # if (length(final_lines_to_include) != 1) { - # cli::cli_warn("el resulted to {.val {final_lines_to_include}}", "using first element for now") - # } - final_lines_to_include - }), - error = function(e) { - cli::cli_abort( - "For tag = {tag}, obj_name = {objects}, wrong size, should be {length(pos)}" - ) - - }) + ) # rlang::set_names(val, nam) # merge line number and file name @@ -146,7 +145,7 @@ join_roxy_fun <- function(file) { return(character(0L)) } if (is.null(names(parsed_files))) { - # browser() + # browser() parsed_files <- parsed_files |> purrr::set_names(purrr::map_chr(parsed_files, \(x) x$file)) # cli::cli_abort("parsed files must be named at this point.") } @@ -169,8 +168,8 @@ join_roxy_fun <- function(file) { desc_list, details_list, family_list, - concept_list#, - #.name_spec = "{outer}:::::{inner}", + concept_list # , + # .name_spec = "{outer}:::::{inner}", ) |> vctrs::list_unchop( name_spec = "{outer}.....{inner}" @@ -188,9 +187,9 @@ join_roxy_fun <- function(file) { delim = "____", names = c("content", "topic"), ) - if (!all(grepl("\\.{5}", roxy_parsed$file_line, fixed = F))) { - problems <- which(!grepl("\\.{5}", roxy_parsed$file_line, fixed = F)) - #rowser() + if (!all(grepl("\\.{5}", roxy_parsed$file_line, fixed = FALSE))) { + problems <- which(!grepl("\\.{5}", roxy_parsed$file_line, fixed = FALSE)) + # rowser() # roxy_parsed cli::cli_abort("Malformed file line at {problems}.") } @@ -200,19 +199,26 @@ join_roxy_fun <- function(file) { delim = ".....", names = c("file", "line") ) - roxy_parsed |> - dplyr::mutate( - #file = fs::path_real(file) |> as.character(), - #file_line = paste0(file, ":", line) - ) |> + + if (nrow(roxy_parsed) == 0) { + return(roxy_parsed) + } + roxy_parsed1 <- roxy_parsed |> dplyr::relocate( file, topic, content, line, tag - ) |> dplyr::mutate(id = dplyr::row_number()) |> - tidyr::separate_longer_delim(content, delim = "\n") |> - dplyr::mutate(n = dplyr::n() , - line = seq(from = line[1], length.out = n[1], by = 1), - .by = id) |> - dplyr::filter(nzchar(content)) |> dplyr::select(-id, -n) + ) |> + dplyr::mutate(id = dplyr::row_number()) |> + tidyr::separate_longer_delim(content, delim = "\n") + + roxy_parsed1 |> + dplyr::mutate( + n = dplyr::n(), + # error if something is length 0. + line = seq(from = line[1], length.out = n[1], by = 1), + .by = id + ) |> + dplyr::filter(nzchar(content)) |> + dplyr::select(-id, -n) } # helper for interactive checking ----------- diff --git a/README.md b/README.md index 54942ef..840417c 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 1.56s 1.56s 0.642 66.8MB 5.78 +#> 1 outline <- proj_outline() 1.52s 1.52s 0.659 67.7MB 5.93 ```
@@ -177,22 +177,12 @@ Example outline ``` r outline #> -#> ── `inst/example-file/outline-script.R` Example for `file_outline()` -#> `i` Load packages -#> `i` Wrangle + visualize data -#> `i` A great title -#> `i` TODO improve this Viz!- `Done✔?` -#> #> ── `LICENSE.md` MIT License #> #> ── `R/dplyr-plus.R` dplyr extra #> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` #> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` #> `i` summarise with total -#> `i` with dplyr::filter -#> `i` dplyr extensions -#> `i` dplyr extensions -#> `i` family dplyr extensions #> #> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- #> `i` base identity functions @@ -200,8 +190,6 @@ outline #> `i` dplyr identity without tweaks #> `i` dplyr extensions identity #> `i` helpers -#> `i` Use cases / advantages -#> `i` Caution #> #> ── `R/files-conflicts.R` #> `i` TODO insert in either proj_outline, or rename_file- `Done✔?` @@ -218,8 +206,6 @@ outline #> `i` FIXME why is this code like this?- `Done✔?` #> `i` TODO structure and summarise information.- `Done✔?` #> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` -#> `i` document manipulation helpers -#> `i` document manipulation helpers #> #> ── `R/outdated-pkgs.R` #> `i` TODO figure out pad :)- `Done✔?` @@ -228,7 +214,8 @@ outline #> `i` Add variable to outline data frame #> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` #> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` -#> `i` it is 'R/outline.R' +#> `i` TODO merge with define_outline_criteria- `Done✔?` +#> `i` it is 'R/outline.R' or 'R/outline-roxy.R' #> #> ── `R/outline-roxy.R` #> `i` TODO when stable delete- `Done✔?` @@ -244,14 +231,10 @@ outline #> `i` Step: tweak outline look as they show #> `i` TODO reanable cli info- `Done✔?` #> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` -#> `i` If `work_only` is set to `TRUE`, the function will only return outline of the `# WORK` comment #> #> ── `R/proj-list.R` #> `i` TODO maybe add a max?- `Done✔?` #> `i` TODO improve on this message- `Done✔?` -#> `i` project management helpers -#> `i` project management helpers -#> `i` project management helpers #> #> ── `R/rename.R` #> `i` After here, we start doing some renaming real situations @@ -261,7 +244,6 @@ outline #> `i` Prevent renaming if something is going on #> `i` FIXME maybe not fail while testing- `Done✔?` #> `i` TODO Check that old file is more recent- `Done✔?` -#> `i` Use case #> #> ── `R/todo.R` #> `i` TODO think about maybe using todo = clipr::read_clip()- `Done✔?` @@ -273,12 +255,55 @@ outline #> #> ── `R/utils.R` OS utils #> +#> ── `TODO.R` +#> `i` TODO screenshot make the behaviour different when vignettes vs articl…- `Done✔?` +#> `i` TODO screenshot RStudio addin to insert the code directly in the qmd …- `Done✔?` +#> `i` TODO use_family() to edit .R file to add @family data frames tags to ro…- `Done✔?` +#> `i` TODO mutate_identity redundant if the focus pillar PR was merged. r-lib/pillar#585 ()- `Done✔?` +#> `i` TODO rename if many matches, separate those with the exact path.- `Done✔?` +#> `i` TODO outline make ggtitle work- `Done✔?` +#> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` +#> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` +#> `i` TODO outline renable cli info.- `Done✔?` +#> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` +#> `i` TODO outline avoid evaluating in current env.- `Done✔?` +#> `i` TODO wrap regexps in functions- `Done✔?` +#> `i` TODO outline remove examples from outline. Sometimes commented code i…- `Done✔?` +#> `i` TODO outline roxygen comments processing should be left to `roxygen2::parse_file()`- `Done✔?` +#> `i` TODO outline show key like `pak::pkg_deps_tree()` does.- `Done✔?` +#> `i` TODO outline roxygen function title- `Done✔?` +#> `i` TODO outline remove ggtext markup from plot title.- `Done✔?` +#> `i` FIXME outline comments are now interpreted as section- `Done✔?` +#> `i` TODO outline todos in qmd file inside html comment- `Done✔?` +#> `i` TODO reframe more than one issue. nw drive- `Done✔?` +#> `i` TODO delete generated files- `Done✔?` +#> `i` TODO [proj_file] to accesss data (return the path in this case?)- `Done✔?` +#> `i` TODO [check_referenced_files] doesn't check for 'R/file.R'- `Done✔?` +#> `i` TODO explain rationale behind `work_only`. Suggest to transform to TODO…- `Done✔?` +#> `i` TODO outline Show function call if exported + not internal + bonus if…- `Done✔?` +#> `i` TODO title of file could be function title if it is first element [proj…- `Done✔?` +#> `i` TODO rename_files should be less noisy about project name file- `Done✔?` +#> +#> ── `inst/example-file/outline-script.R` Example for `file_outline()` +#> `i` Load packages +#> `i` Wrangle + visualize data +#> `i` A great title +#> `i` TODO improve this Viz!- `Done✔?` +#> #> ── `tests/testthat/_ref/many-titles.md` The title is the only outline element #> `i` Another title #> `i` Second level #> `i` TODO this is an item- `Done✔?` #> `i` Last title #> +#> ── `tests/testthat/_ref/my-analysis.R` Analyse my streets +#> `i` Read my streets () data +#> `i` data wrangling +#> `i` Write my streets +#> `i` TODO Create a new version- `Done✔?` +#> `i` 'R/my-file.R' +#> `i` Section title +#> #> ── `tests/testthat/_ref/my-analysis.md` My doc title #> `i` A section #> `i` Dashboard card @@ -288,19 +313,10 @@ outline #> `i` A long ggplot2 title #> `i` A code section #> -#> ── `tests/testthat/_ref/my-analysis.R` Analyse my streets -#> `i` Read my streets () data -#> `i` data wrangling -#> `i` Write my streets -#> `i` TODO Create a new version- `Done✔?` -#> `i` 'R/my-file.R' -#> `i` Section title -#> #> ── `tests/testthat/_ref/single-title.md` The title is the only outline element #> #> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error #> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing -#> `i` a family to include #> #> ── `tests/testthat/_ref/test-roxygen.R` #> `i` Use 'tests/testthat/_ref/test-roxygen-safeguard.R' for output testing @@ -309,16 +325,6 @@ outline #> `i` Topic to index #> `i` data to index #> `i` TODO add data block example to index- `Done✔?` -#> `i` First to be included -#> `i` A second-level heading in description to be included? -#> `i` A detail first level-heading to be included -#> `i` A detail second-level heading to be included -#> `i` A second-level heading in description to be included? -#> `i` A detail first level-heading to be included -#> `i` A detail second-level heading to be included -#> `i` First to be included -#> `i` a family to include -#> `i` a family to include #> #> ── `tests/testthat/_snaps/case-if-any.md` #> `i` wrong cases error @@ -397,40 +403,6 @@ outline #> ── `tests/testthat/test-utils.R` #> `i` Windows is recognized correctly. #> -#> ── `TODO.R` -#> `i` TODO screenshot make the behaviour different when vignettes vs articl…- `Done✔?` -#> `i` TODO screenshot RStudio addin to insert the code directly in the qmd …- `Done✔?` -#> `i` TODO use_family() to edit .R file to add @family data frames tags to ro…- `Done✔?` -#> `i` TODO mutate_identity redundant if the focus pillar PR was merged. r-lib/pillar#585 ()- `Done✔?` -#> `i` TODO rename if many matches, separate those with the exact path.- `Done✔?` -#> `i` TODO outline make ggtitle work- `Done✔?` -#> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` -#> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` -#> `i` TODO outline renable cli info.- `Done✔?` -#> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` -#> `i` TODO outline avoid evaluating in current env.- `Done✔?` -#> `i` TODO wrap regexps in functions- `Done✔?` -#> `i` TODO outline remove examples from outline. Sometimes commented code i…- `Done✔?` -#> `i` TODO outline roxygen comments processing should be left to `roxygen2::parse_file()`- `Done✔?` -#> `i` TODO outline show key like `pak::pkg_deps_tree()` does.- `Done✔?` -#> `i` TODO outline roxygen function title- `Done✔?` -#> `i` TODO outline remove ggtext markup from plot title.- `Done✔?` -#> `i` FIXME outline comments are now interpreted as section- `Done✔?` -#> `i` TODO outline todos in qmd file inside html comment- `Done✔?` -#> `i` TODO reframe more than one issue. nw drive- `Done✔?` -#> `i` TODO delete generated files- `Done✔?` -#> `i` TODO [proj_file] to accesss data (return the path in this case?)- `Done✔?` -#> `i` TODO [check_referenced_files] doesn't check for 'R/file.R'- `Done✔?` -#> `i` TODO explain rationale behind `work_only`. Suggest to transform to TODO…- `Done✔?` -#> `i` TODO outline Show function call if exported + not internal + bonus if…- `Done✔?` -#> `i` TODO title of file could be function title if it is first element [proj…- `Done✔?` -#> `i` TODO rename_files should be less noisy about project name file- `Done✔?` -#> -#> ── `R/proj-reuseme.R` -#> `i` Setup -#> `i` Capabilities. -#> `i` project management helpers -#> #> ── `NEWS.md` #> `i` reuseme (development version) #> `i` reuseme 0.0.2 diff --git a/man/complete_todo.Rd b/man/complete_todo.Rd index 00b7ad8..a00beef 100644 --- a/man/complete_todo.Rd +++ b/man/complete_todo.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/use-todo.R +% Please edit documentation in R/todo.R \name{complete_todo} \alias{complete_todo} \title{Remove a TODO/WORK/FIXME item from a file} @@ -22,7 +22,7 @@ Writes a file with corrections, and returns the new line content invisibly. } \description{ -Function meant to be wrapped as \code{{.run}} hyperlinks with \code{\link[=file_outline]{file_outline()}}. +Function meant to be wrapped as \code{{.run }} hyperlinks with \code{\link[=file_outline]{file_outline()}}. It basically removes a line from a file. } \keyword{internal} diff --git a/man/link_gh_issue.Rd b/man/link_gh_issue.Rd index 5f43ef4..521935d 100644 --- a/man/link_gh_issue.Rd +++ b/man/link_gh_issue.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/link-elements.R +% Please edit documentation in R/markup.R \name{link_gh_issue} \alias{link_gh_issue} \title{Create a markdown link to a GitHub issue} diff --git a/man/markup_href.Rd b/man/markup_href.Rd index 6204d72..3380241 100644 --- a/man/markup_href.Rd +++ b/man/markup_href.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/link-elements.R +% Please edit documentation in R/markup.R \name{markup_href} \alias{markup_href} \title{Create a cli href with a markdown link} diff --git a/man/rename_files2.Rd b/man/rename_files2.Rd index 8b3d1b6..4d251e6 100644 --- a/man/rename_files2.Rd +++ b/man/rename_files2.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/rename-files.R +% Please edit documentation in R/rename.R \name{rename_files2} \alias{rename_files2} \title{Rename an output or a data file and watch for references} diff --git a/man/use_todo.Rd b/man/use_todo.Rd index ff42a5f..9da58c0 100644 --- a/man/use_todo.Rd +++ b/man/use_todo.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/use-todo.R +% Please edit documentation in R/todo.R \name{use_todo} \alias{use_todo} \title{Add a TODO list by project to a TODO.R file in the base directory} From 97da59739f06d676e0af7f50cda557fa8c4823ab Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 13:17:25 -0400 Subject: [PATCH 16/71] Fix logic to include object titles in outline --- R/outline-criteria.R | 15 +++++++-------- R/outline.R | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index ca09774..d46f031 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -189,7 +189,11 @@ define_outline_criteria <- function(.data, print_todo) { ) #browser() res <- dplyr::bind_rows(x, outline_roxy) - res <- dplyr::arrange(x, .data$file, .data$line) + res <- dplyr::filter( + res, + content != "NULL" + ) + res <- dplyr::arrange(res, .data$file, .data$line) } @@ -200,8 +204,6 @@ define_outline_criteria_roxy <- function(x) { x$line <- as.integer(x$line) x$file_ext <- "R" #x$content <- paste0("#' ", x$content) # maybe not? - x$title_el <- NA_character_ - x$title_el_line <- NA_integer_ x$is_news <- FALSE x$is_roxygen_comment <- TRUE x$is_test_file <- FALSE @@ -237,11 +239,8 @@ define_outline_criteria_roxy <- function(x) { .default = x$content ) x$is_second_level_heading_or_more <- ((x$is_section_title_source | x$is_section_title) & x$n_leading_hash > 1) - x$has_inline_markup = FALSE # let's not mess with inline markup - dplyr::filter( - x, - content != "NULL" - ) + x$has_inline_markup <- FALSE # let's not mess with inline markup + x } # it is {.file R/outline.R} or {.file R/outline-roxy.R} ------ diff --git a/R/outline.R b/R/outline.R index 4e20d10..832136b 100644 --- a/R/outline.R +++ b/R/outline.R @@ -461,12 +461,12 @@ keep_outline_element <- function(.data) { # still regular comments in .md files # what to keep in .md docs - (is_md & (is_chunk_cap | is_doc_title)) | + (is_md & (is_chunk_cap | is_doc_title | is_object_title)) | (is_md & (is_section_title & before_and_after_empty & !is_a_comment_or_code)) | # What to keep in .R files (!is_md & is_section_title_source) | # What to keep anywhere - is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def # | is_cli_info # TODO reanable cli info + is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def | is_object_title # | is_cli_info # TODO reanable cli info ) dat$simplify_news <- NULL From ebc4355ac4d95fc24b682c0b4d46db615c5c49c3 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 13:17:48 -0400 Subject: [PATCH 17/71] Commit changes to README --- README.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 840417c..e0786ca 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 1.52s 1.52s 0.659 67.7MB 5.93 +#> 1 outline <- proj_outline() 1.78s 1.78s 0.561 69MB 4.49 ```
@@ -179,12 +179,30 @@ outline #> #> ── `LICENSE.md` MIT License #> +#> ── `R/browse-pkg.R` Browse pkgdown site if it exists +#> +#> ── `R/case-if-any.R` case-when, but checks for all matches, returns a character +#> #> ── `R/dplyr-plus.R` dplyr extra +#> `i` Count observations by group and compute percentage +#> `i` dplyr extensions +#> `i` Subset rows using their positions +#> `i` dplyr extensions +#> `i` Explore all rows in a random group +#> `i` family dplyr extensions #> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` +#> `i` Keep rows that match one of the conditions +#> `i` with dplyr::filter +#> `i` Elegant wrapper around filter and pull #> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` #> `i` summarise with total +#> `i` Compute a summary for one group with the total included. +#> `i` Transform to NA any of the condition #> #> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- +#> `i` Helpers that return the same value +#> `i` Use cases / advantages +#> `i` Caution #> `i` base identity functions #> `i` dplyr identity functions with small tweaks #> `i` dplyr identity without tweaks @@ -202,12 +220,18 @@ outline #> `i` Scalars #> `i` Vectors #> -#> ── `R/open.R` +#> ── `R/named.R` Helpers that can return a named vector +#> +#> ── `R/open.R` Open a Document in RStudio #> `i` FIXME why is this code like this?- `Done✔?` +#> `i` Copy the active document to the same location +#> `i` document manipulation helpers +#> `i` Delete the active RStudio document safely +#> `i` document manipulation helpers #> `i` TODO structure and summarise information.- `Done✔?` #> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` #> -#> ── `R/outdated-pkgs.R` +#> ── `R/outdated-pkgs.R` Looks for outdated packages #> `i` TODO figure out pad :)- `Done✔?` #> #> ── `R/outline-criteria.R` @@ -225,6 +249,8 @@ outline #> `i` helper for interactive checking #> #> ── `R/outline.R` `proj_outline()` +#> `i` Print interactive outline of file sections +#> `i` If `work_only` is set to `TRUE`, the function will only return outline of the `# WORK` comment #> `i` `file_outline()` #> `i` File outline #> `i` Print method @@ -232,11 +258,24 @@ outline #> `i` TODO reanable cli info- `Done✔?` #> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` #> -#> ── `R/proj-list.R` +#> ── `R/proj-list.R` Opens a RStudio project in a new session +#> `i` project management helpers #> `i` TODO maybe add a max?- `Done✔?` +#> `i` Access the file outline within other project +#> `i` project management helpers #> `i` TODO improve on this message- `Done✔?` +#> `i` Returns a named project list options +#> `i` project management helpers #> -#> ── `R/rename.R` +#> ── `R/proj-reuseme.R` Interact with different RStudio projects +#> `i` Setup +#> `i` Capabilities. +#> `i` project management helpers +#> +#> ── `R/quarto-help.R` Show links to Quarto documentation of interest +#> +#> ── `R/rename.R` Rename an output or a data file and watch for references +#> `i` Use case #> `i` After here, we start doing some renaming real situations #> `i` Helpers #> `i` helpers for computing scope of renaming @@ -245,7 +284,9 @@ outline #> `i` FIXME maybe not fail while testing- `Done✔?` #> `i` TODO Check that old file is more recent- `Done✔?` #> -#> ── `R/todo.R` +#> ── `R/screenshot.R` Save the current image in clipboard to png in your active directory +#> +#> ── `R/todo.R` Add a TODO list by project to a TODO.R file in the base directory #> `i` TODO think about maybe using todo = clipr::read_clip()- `Done✔?` #> `i` TODO nice to have, but would need to extract duplicates- `Done✔?` #> `i` Helpers @@ -317,12 +358,27 @@ outline #> #> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error #> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing +#> `i` Title +#> `i` a family to include +#> `i` An S3 method not to be include #> #> ── `tests/testthat/_ref/test-roxygen.R` #> `i` Use 'tests/testthat/_ref/test-roxygen-safeguard.R' for output testing #> `i` Complete block for exported function with headings +#> `i` A title to be included +#> `i` A second-level heading in description to be included? +#> `i` A detail first level-heading to be included +#> `i` A detail second-level heading to be included +#> `i` First to be included +#> `i` a family to include #> `i` block not to index #> `i` Topic to index +#> `i` A title to be included +#> `i` A second-level heading in description to be included? +#> `i` A detail first level-heading to be included +#> `i` A detail second-level heading to be included +#> `i` First to be included +#> `i` a family to include #> `i` data to index #> `i` TODO add data block example to index- `Done✔?` #> From d3bf7243adec9f2908f2eabc84d8eab4304fec38 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 14:07:24 -0400 Subject: [PATCH 18/71] Refine some criteria to exclude some contents or files. Remove markup for tag = title. Add some tryCatch to facilitate error discovery. --- R/outline-criteria.R | 13 +++++++------ R/outline-roxy.R | 7 ++++++- R/outline.R | 18 +++++++++++++++--- README.md | 5 +++-- tests/testthat/_ref/test-roxygen-safeguard.R | 10 +++++++++- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index d46f031..82e413e 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -75,7 +75,7 @@ o_is_generic_test <- function(x) { o_is_tab_plot_title <- function(x) { stringr::str_detect(x, "(? 4 x$line <- as.integer(x$line) x$file_ext <- "R" #x$content <- paste0("#' ", x$content) # maybe not? @@ -212,7 +214,6 @@ define_outline_criteria_roxy <- function(x) { x$is_section_title <- (x$tag %in% c("section", "subsection") & stringr::str_ends(x$content, ":")) | (x$tag %in% c("details", "description") & stringr::str_detect(x$content, "#\\s")) x$is_section_title_source <- x$is_section_title - x$is_saved_doc <- TRUE x$is_chunk_cap <- FALSE x$is_chunk_cap_next = FALSE x$is_test_name = FALSE @@ -239,7 +240,7 @@ define_outline_criteria_roxy <- function(x) { .default = x$content ) x$is_second_level_heading_or_more <- ((x$is_section_title_source | x$is_section_title) & x$n_leading_hash > 1) - x$has_inline_markup <- FALSE # let's not mess with inline markup + #x$has_inline_markup <- FALSE # let's not mess with inline markup x } diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 575ff71..fb106e3 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -215,7 +215,12 @@ join_roxy_fun <- function(file) { n = dplyr::n(), # error if something is length 0. line = seq(from = line[1], length.out = n[1], by = 1), - .by = id + .by = id, + content = dplyr::case_when( + # remove markup. + tag == "title" ~ stringr::str_remove_all(content, "\\}+|\\\\+[:alpha:]+\\{+"), + .default = content + ) ) |> dplyr::filter(nzchar(content)) |> dplyr::select(-id, -n) diff --git a/R/outline.R b/R/outline.R index 832136b..f5a1f63 100644 --- a/R/outline.R +++ b/R/outline.R @@ -312,7 +312,7 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, dir_tree = # examples don't help understand a project. file_list_to_outline <- fs::path_filter( file_list_to_outline, - regexp = "testthat/_ref/|example-file", + regexp = "testthat/_ref/|testthat/assets|example-file|vignettes/test/", invert = TRUE ) } @@ -415,11 +415,23 @@ print.outline_report <- function(x, ...) { # add first line to title and remove has_title <- !is.na(summary_links_files$first_line[[i]]) if (has_title) { - title_el <- cli::format_inline(escape_markup(summary_links_files$first_line_el[[i]])) + title_el <- withCallingHandlers( + cli::format_inline(escape_markup(summary_links_files$first_line_el[[i]])), + error = function(e) { + thing <- summary_links_files$first_line_el[[i]] + print(thing) + print(escape_markup(thing)) + cli::cli_abort("failed to parse in first line of file {.file {summary_links_files$file[[i]]}}.", parent = e) + }) base_name <- c(base_name, " ", title_el) } - cli::cli_h3(base_name) + withCallingHandlers( + cli::cli_h3(base_name), + error = function(e) { + print(base_name) + rlang::abort("Could not parse by cli") + }) if (recent_only) { if (i %in% is_recently_modified) { diff --git a/README.md b/README.md index e0786ca..baebedd 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 1.78s 1.78s 0.561 69MB 4.49 +#> 1 outline <- proj_outline() 2.01s 2.01s 0.497 69.2MB 2.48 ```
@@ -237,6 +237,7 @@ outline #> ── `R/outline-criteria.R` #> `i` Add variable to outline data frame #> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` +#> `i` TODO long enough to be meanignful?- `Done✔?` #> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` #> `i` TODO merge with define_outline_criteria- `Done✔?` #> `i` it is 'R/outline.R' or 'R/outline-roxy.R' @@ -358,7 +359,7 @@ outline #> #> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error #> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing -#> `i` Title +#> `i` Title with `_things` #> `i` a family to include #> `i` An S3 method not to be include #> diff --git a/tests/testthat/_ref/test-roxygen-safeguard.R b/tests/testthat/_ref/test-roxygen-safeguard.R index 3d75371..fbfe714 100644 --- a/tests/testthat/_ref/test-roxygen-safeguard.R +++ b/tests/testthat/_ref/test-roxygen-safeguard.R @@ -3,7 +3,7 @@ # Dump of other things to test for expect_no_error (not necessary to verify) # Mostly cases inspired by testing in the wild. -#' Title +#' Title with `_things` #' #' @examples #' # Commented code not included @@ -26,4 +26,12 @@ f_not_to_index.xml <- function() { } +#' A +#' +#' Very short title. +#' @export +f_not_to_index <- function() { + +} + # Keep this line last: content to test for new roxygen output should be put in test-roxygen.R From 557209a2c373ac1dad15212d9e3cd6ddbeae0933 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 14:16:34 -0400 Subject: [PATCH 19/71] Avoid index roxygen comments in tests --- R/outline-criteria.R | 10 +++++----- R/outline-roxy.R | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 82e413e..9366b38 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -18,11 +18,11 @@ extract_pkg_version <- function(x, is_news, is_heading) { #' * is_tab_plot_title #' #' @noRd -o_is_roxygen_comment <- function(x, file_ext = NULL) { +o_is_roxygen_comment <- function(x, file_ext = NULL, is_test_file = FALSE) { if (!is.null(file_ext)) { - is_r_file <- tolower(file_ext) == "r" + is_r_file <- tolower(file_ext) == "r" & !is_test_file } else { - is_r_file <- TRUE + is_r_file <- !is_test_file } if (!any(is_r_file)) { @@ -121,9 +121,9 @@ define_outline_criteria <- function(.data, print_todo) { x$file_ext <- s_file_ext(x$file) x$is_md <- x$file_ext %in% c("qmd", "md", "Rmd", "Rmarkdown") x$is_news <- x$is_md & grepl("NEWS.md", x$file, fixed = TRUE) - x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext) - x$is_md <- (x$is_md | x$is_roxygen_comment) & !x$is_news # treating news and other md files differently. x$is_test_file <- grepl("tests/testthat", x$file, fixed = TRUE) + x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext, x$is_test_file) + x$is_md <- (x$is_md | x$is_roxygen_comment) & !x$is_news # treating news and other md files differently. x$is_snap_file <- grepl("_snaps", x$file, fixed = TRUE) if (any(x$is_roxygen_comment)) { rlang::check_installed(c("roxygen2", "tidyr"), "to create roxygen2 comments outline.") diff --git a/R/outline-roxy.R b/R/outline-roxy.R index fb106e3..859c37d 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -96,7 +96,7 @@ extract_roxygen_tag_location <- function(file, tag) { }), error = function(e) { cli::cli_abort( - "For tag = {tag}, obj_name = {objects}, wrong size, should be {length(pos)}" + "For tag = {tag}, obj_name = {objects}, wrong size, should be {length(pos)}, not {length(objects)}." ) } ) From fa9d9357b1b5d820a0c2cfc3f9795ead07e2ee6e Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 19:48:22 -0400 Subject: [PATCH 20/71] Improve table detection. Improve package version detection in news. --- DESCRIPTION | 2 +- R/markup.R | 60 +++++++++++++++++++++++++- R/outline-criteria.R | 4 +- R/outline.R | 11 ++--- TODO.R | 16 ------- man/link_gh_issue.Rd | 4 +- tests/testthat/test-outline-criteria.R | 2 + 7 files changed, 73 insertions(+), 26 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7752d98..d34af0d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -44,4 +44,4 @@ Suggests: Config/testthat/edition: 3 Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.1.9000 diff --git a/R/markup.R b/R/markup.R index 1a4f90d..c324615 100644 --- a/R/markup.R +++ b/R/markup.R @@ -9,14 +9,14 @@ #' #' Afterwards, we use [markup_href()] to create a cli link #' @param x A string, usually lines of files that contains issue numbers. -#' +#' @param home_repo Optional, but if supplied, will be stripped. #' @return A markdown link linked issue to GitHub issue #' @export #' @keywords internal #' @family inline markup internal helpers #' @examples #' link_gh_issue(c("We really need rstudio/gt#1469 to be fixed.")) -link_gh_issue <- function(x) { +link_gh_issue <- function(x, home_repo = NULL) { # Return early if no issue pattern is detected. regex_gh_issue <- common_regex("gh_issue") @@ -37,11 +37,67 @@ link_gh_issue <- function(x) { regex_gh_issue, paste0("[\\1#\\2](https://github.com/\\1/issues/\\2)") ) + if (!is.null(home_repo)) + x_changed <- gsub( + paste0(home_repo,"#"), + "#", + x_changed + ) x[has_gh_issue] <- x_changed x } +# transforms (#xx) to (org/repo#xx) +link_local_gh_issue <- function(x, repo_home) { + gsub( + # max 99999 issues. + pattern = "\\((#\\d{1,5})\\)", + paste0("(", repo_home, "\\1)"), + x + ) +} +find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { + rlang::local_interactive(FALSE) + withr::local_options("usethis.quiet" = TRUE) + if (!is.null(dir_common)) { + pkg_path <- withCallingHandlers( + rprojroot::find_package_root_file(path = dir_common), + error = function(e) { + cli::cli_inform("Could not detect path.") + NULL + }) + if (is.null(pkg_path)) { + return(NULL) + } + gh_url <- withCallingHandlers( + usethis::browse_github(basename(pkg_path)), + error = function(e) { + cli::cli_abort("didn't find a way to do what is required.") + } + ) + org_repo_found <- stringr::str_remove(gh_url, ".+github.com/|.+gitlab.com/") + return(org_repo_found) + } + if (!is.null(file)) { + pkg_path <- withCallingHandlers( + rprojroot::find_package_root_file(path = file), + error = function(e) { + cli::cli_inform("Could not detect path.") + NULL + } + ) + + gh_url <- usethis::browse_github(basename(pkg_path)) + org_repo_found <- stringr::str_remove(gh_url, ".+github.com/|.+gitlab.com/") + } else { + org_repo_found <- NULL + } + if (is.null(org_repo) && is.null(org_repo_found)) { + cli::cli_abort("No way to discover URL.") + } + +} #' Create a cli href with a markdown link #' #' Transforms `[text](url)` -> `{.href [text](url)}` diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 9366b38..9a372c4 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -75,7 +75,9 @@ o_is_generic_test <- function(x) { o_is_tab_plot_title <- function(x) { stringr::str_detect(x, "(? stringr::str_remove_all("\""), # family or concept! is_tab_or_plot_title & !is.na(tag) ~ outline_el, - is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title = [\"']([^\"]{5,})[\"']", group = 1), + is_tab_or_plot_title ~ stringr::str_extract(outline_el, "(title = )?[\"']([^\"]{5,})[\"']", group = 2), is_chunk_cap_next & !is_chunk_cap ~ stringr::str_remove_all(outline_el, "\\s?\\#\\|\\s+"), is_chunk_cap ~ stringr::str_remove_all(stringr::str_extract(outline_el, "(cap|title)\\:\\s*(.+)", group = 2), "\"|'"), is_cross_ref ~ stringr::str_remove_all(outline_el, "^(i.stat\\:\\:)?.cdocs_lin.s\\(|[\"']\\)$|\""), diff --git a/TODO.R b/TODO.R index f462619..079b48b 100644 --- a/TODO.R +++ b/TODO.R @@ -25,22 +25,6 @@ # TODO explain rationale behind `work_only`. Suggest to transform to TODO if this item is no longer critical. `work_only` goal is to show you exactly where you need to do work # TODO browse_pkg should open by default if no vignettes are found, because there is not much to do in the R-session. # TODO exclude _files from `proj_list()` -# TODO escape_markup doesn't work with complex operation {{x^2}} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this. -# TODO [outline] avoid evaluating in current env. -# TODO wrap regexps in functions -# TODO items should not truncate leading code when marking as complte trunc-todo-code -# TODO [outline] remove examples from outline. Sometimes commented code is caught. -# TODO [outline] roxygen comments processing should be left to {.fn roxygen2::parse_file} -# TODO [outline] show key like {.fn pak::pkg_deps_tree} does. -# TODO [outline] roxygen function title -# TODO [outline] remove ggtext markup from plot title. -# FIXME [outline] comments are now interpreted as section -# TODO [outline] todos in qmd file inside html comment -# TODO reframe more than one issue. nw drive -# TODO [delete] generated files -# TODO [proj_file] to accesss data (return the path in this case?) -# TODO [check_referenced_files] doesn't check for {.file R/file.R} -# TODO explain rationale behind `work_only`. Suggest to transform to TODO if this item is no longer critical. `work_only` goal is to show you exactly where you need to do work # TODO [outline] Show function call if exported + not internal + bonus if has family tagf # TODO title of file could be function title if it is first element [proj_outline] # TODO rename_files should be less noisy about project name file diff --git a/man/link_gh_issue.Rd b/man/link_gh_issue.Rd index 521935d..df683f6 100644 --- a/man/link_gh_issue.Rd +++ b/man/link_gh_issue.Rd @@ -4,10 +4,12 @@ \alias{link_gh_issue} \title{Create a markdown link to a GitHub issue} \usage{ -link_gh_issue(x) +link_gh_issue(x, home_repo = NULL) } \arguments{ \item{x}{A string, usually lines of files that contains issue numbers.} + +\item{home_repo}{Optional, but if supplied, will be stripped.} } \value{ A markdown link linked issue to GitHub issue diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index ef7ca3f..1c743ec 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -34,6 +34,8 @@ test_that("o_is_generic_test() works", { test_that("o_is_tab_plot_title() works", { expect_true(o_is_tab_plot_title("title = 'A great'")) + expect_false(o_is_tab_plot_title("tab_header()")) + expect_false(o_is_tab_plot_title("```{r tab_header}")) }) test_that("o_is_section_title() works", { From 1db1418b4ab8bcd3781f5ce33bd419e940d5d272 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 19:50:37 -0400 Subject: [PATCH 21/71] Add markup for linking local issues --- R/markup.R | 6 +++--- R/outline-criteria.R | 6 +++++- R/outline.R | 14 ++++++++++---- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/R/markup.R b/R/markup.R index c324615..1b2a9d8 100644 --- a/R/markup.R +++ b/R/markup.R @@ -60,10 +60,10 @@ find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { rlang::local_interactive(FALSE) withr::local_options("usethis.quiet" = TRUE) if (!is.null(dir_common)) { - pkg_path <- withCallingHandlers( + pkg_path <- tryCatch( rprojroot::find_package_root_file(path = dir_common), error = function(e) { - cli::cli_inform("Could not detect path.") + # cli::cli_inform("Could not detect path.") NULL }) if (is.null(pkg_path)) { @@ -83,7 +83,7 @@ find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { pkg_path <- withCallingHandlers( rprojroot::find_package_root_file(path = file), error = function(e) { - cli::cli_inform("Could not detect path.") + # cli::cli_inform("Could not detect path.") NULL } ) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 9a372c4..463631b 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -124,7 +124,11 @@ define_outline_criteria <- function(.data, print_todo) { x$is_md <- x$file_ext %in% c("qmd", "md", "Rmd", "Rmarkdown") x$is_news <- x$is_md & grepl("NEWS.md", x$file, fixed = TRUE) x$is_test_file <- grepl("tests/testthat", x$file, fixed = TRUE) - x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext, x$is_test_file) + # TODO Would have to look for notebooks that don't contain notebook in their name + # Like roxy comments and first line = --, 2nd title. + x$is_notebook <- grepl("notebook.*\\.R", x$file) + x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext, x$is_test_file | x$is_notebook) + x$content[x$is_notebook] <- sub("^#' ", "", x$content[x$is_notebook]) x$is_md <- (x$is_md | x$is_roxygen_comment) & !x$is_news # treating news and other md files differently. x$is_snap_file <- grepl("_snaps", x$file, fixed = TRUE) if (any(x$is_roxygen_comment)) { diff --git a/R/outline.R b/R/outline.R index f9b3ee2..f1cdc58 100644 --- a/R/outline.R +++ b/R/outline.R @@ -162,7 +162,7 @@ file_outline <- function(pattern = NULL, check_string(pattern, arg = "You may have specified path Internal error") - file_sections00 <- define_outline_criteria(file_content, print_todo = print_todo) + file_sections00 <- define_outline_criteria(file_content, print_todo = print_todo, roxy) # filter for interesting items. file_sections0 <- keep_outline_element(file_sections00) @@ -197,7 +197,7 @@ file_outline <- function(pattern = NULL, } # File outline =================== # strip outline element .data$outline = `# Section 1` becomes `Section 1` - file_sections1 <- display_outline_element(file_sections0) + file_sections1 <- display_outline_element(file_sections0, dir_common) # Create hyperlink in console file_sections <- construct_outline_link(file_sections1, is_saved_doc, dir_common, pattern) @@ -490,9 +490,15 @@ keep_outline_element <- function(.data) { # Includes removing headings comments # Remove title = # Removing quotes, etc. -display_outline_element <- function(.data) { +display_outline_element <- function(.data, dir_common) { x <- .data - x$outline_el <- purrr::map_chr(x$content, link_gh_issue) # to add link to GitHub. + org_repo <- find_pkg_org_repo(dir_common, unique(x$file)) + if (!is.null(org_repo)) { + x$outline_el <- link_local_gh_issue(x$content, org_repo) + } else { + x$outline_el <- x$content + } + x$outline_el <- purrr::map_chr(x$outline_el, \(x) link_gh_issue(x, org_repo)) # to add link to GitHub. x$outline_el <- purrr::map_chr(x$outline_el, markup_href) x <- dplyr::mutate( x, From d172b30d25652730b476331016e477e1b26733a6 Mon Sep 17 00:00:00 2001 From: olivroy <52606734+olivroy@users.noreply.github.com> Date: Fri, 24 May 2024 19:53:49 -0400 Subject: [PATCH 22/71] Update R/outline.R --- R/outline.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/outline.R b/R/outline.R index f1cdc58..f84b23a 100644 --- a/R/outline.R +++ b/R/outline.R @@ -162,7 +162,7 @@ file_outline <- function(pattern = NULL, check_string(pattern, arg = "You may have specified path Internal error") - file_sections00 <- define_outline_criteria(file_content, print_todo = print_todo, roxy) + file_sections00 <- define_outline_criteria(file_content, print_todo = print_todo) # filter for interesting items. file_sections0 <- keep_outline_element(file_sections00) From d3506b4f97705bb67c3984969636700f8341f125 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 20:09:31 -0400 Subject: [PATCH 23/71] Last update --- R/outline-roxy.R | 2 +- R/outline.R | 2 +- README.md | 26 ++++++++++++++++---------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 859c37d..24d91ba 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -218,7 +218,7 @@ join_roxy_fun <- function(file) { .by = id, content = dplyr::case_when( # remove markup. - tag == "title" ~ stringr::str_remove_all(content, "\\}+|\\\\+[:alpha:]+\\{+"), + tag == "title" ~ stringr::str_remove_all(content, "\\}+|\\\\+[:alpha:]+\\{+|\\{$"), .default = content ) ) |> diff --git a/R/outline.R b/R/outline.R index f84b23a..c0a8a41 100644 --- a/R/outline.R +++ b/R/outline.R @@ -508,7 +508,7 @@ display_outline_element <- function(.data, dir_common) { is_cli_info ~ stringr::str_extract(outline_el, "[\"'](.{5,})[\"']") |> stringr::str_remove_all("\""), # family or concept! is_tab_or_plot_title & !is.na(tag) ~ outline_el, - is_tab_or_plot_title ~ stringr::str_extract(outline_el, "(title = )?[\"']([^\"]{5,})[\"']", group = 2), + is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title = [\"']([^\"]{5,})[\"']", group = 1), is_chunk_cap_next & !is_chunk_cap ~ stringr::str_remove_all(outline_el, "\\s?\\#\\|\\s+"), is_chunk_cap ~ stringr::str_remove_all(stringr::str_extract(outline_el, "(cap|title)\\:\\s*(.+)", group = 2), "\"|'"), is_cross_ref ~ stringr::str_remove_all(outline_el, "^(i.stat\\:\\:)?.cdocs_lin.s\\(|[\"']\\)$|\""), diff --git a/README.md b/README.md index baebedd..8574ea0 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 2.01s 2.01s 0.497 69.2MB 2.48 +#> 1 outline <- proj_outline() 1.55s 1.55s 0.647 68.6MB 5.17 ```
@@ -236,6 +236,7 @@ outline #> #> ── `R/outline-criteria.R` #> `i` Add variable to outline data frame +#> `i` TODO Would have to look for notebooks that don't contain notebook in th…- `Done✔?` #> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` #> `i` TODO long enough to be meanignful?- `Done✔?` #> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` @@ -322,9 +323,12 @@ outline #> `i` TODO [proj_file] to accesss data (return the path in this case?)- `Done✔?` #> `i` TODO [check_referenced_files] doesn't check for 'R/file.R'- `Done✔?` #> `i` TODO explain rationale behind `work_only`. Suggest to transform to TODO…- `Done✔?` +#> `i` TODO browse_pkg should open by default if no vignettes are found, becau…- `Done✔?` +#> `i` TODO exclude _files from `proj_list()`- `Done✔?` #> `i` TODO outline Show function call if exported + not internal + bonus if…- `Done✔?` #> `i` TODO title of file could be function title if it is first element [proj…- `Done✔?` #> `i` TODO rename_files should be less noisy about project name file- `Done✔?` +#> `i` TODO add_to_tricks(). when detecting TRICK like complete todo, but not …- `Done✔?` #> #> ── `inst/example-file/outline-script.R` Example for `file_outline()` #> `i` Load packages @@ -359,27 +363,29 @@ outline #> #> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error #> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing -#> `i` Title with `_things` -#> `i` a family to include -#> `i` An S3 method not to be include +#> `i` Commented code not included +#> `i` A title not to be included #> #> ── `tests/testthat/_ref/test-roxygen.R` #> `i` Use 'tests/testthat/_ref/test-roxygen-safeguard.R' for output testing #> `i` Complete block for exported function with headings -#> `i` A title to be included #> `i` A second-level heading in description to be included? #> `i` A detail first level-heading to be included #> `i` A detail second-level heading to be included -#> `i` First to be included -#> `i` a family to include +#> `i` Commented code not included +#> `i` A title not to be included #> `i` block not to index +#> `i` Internal heading not to be included #> `i` Topic to index -#> `i` A title to be included #> `i` A second-level heading in description to be included? #> `i` A detail first level-heading to be included #> `i` A detail second-level heading to be included #> `i` First to be included -#> `i` a family to include +#> `i` Commented code not included +#> `i` A title not to be included +#> `i` second-level heading in desc +#> `i` Details + 2nd level heading +#> `i` second heading #> `i` data to index #> `i` TODO add data block example to index- `Done✔?` #> @@ -408,7 +414,7 @@ outline #> ── `tests/testthat/test-case-if-any.R` #> `i` case_if_any() basic work #> `i` wrong cases error -#> `i` case_if_any() can use a newly created variable (#8) +#> `i` case_if_any() can use a newly created variable (#8 ()) #> #> ── `tests/testthat/test-dplyr-plus.R` #> `i` filter_if_any() errors correctly when using `by` instead of `.by` From 646fe4b9e39b52515e1a6e4af831862e3101f043 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 20:14:32 -0400 Subject: [PATCH 24/71] Add parent error for debugging. --- R/outline-roxy.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 24d91ba..a07e4e9 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -96,7 +96,8 @@ extract_roxygen_tag_location <- function(file, tag) { }), error = function(e) { cli::cli_abort( - "For tag = {tag}, obj_name = {objects}, wrong size, should be {length(pos)}, not {length(objects)}." + "For tag = {tag}, obj_name = {objects}, wrong size, should be {length(pos)}, not {length(objects)}.", + parent = e ) } ) From e481ac3dba594bc8ff0994ebaf5c9501a29e0073 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 24 May 2024 20:29:45 -0400 Subject: [PATCH 25/71] I identified the issue. Will try to fix. --- R/outline-criteria.R | 2 +- R/outline-roxy.R | 4 ++-- tests/testthat/_ref/roxy-section.R | 24 ++++++++++++++++++++++++ tests/testthat/test-outline-roxy.R | 10 ++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/testthat/_ref/roxy-section.R diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 463631b..aefe3d3 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -137,7 +137,7 @@ define_outline_criteria <- function(.data, print_todo) { files_with_roxy_comments <- rlang::set_names(files_with_roxy_comments, files_with_roxy_comments) parsed_files <- suppressMessages( # roxygen2 messages # TRICK purrr::safely creates an error object, while possible is better. - purrr::map(files_with_roxy_comments, purrr::possibly(roxygen2::parse_file)) + purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))) ) # if roxygen2 cannot parse a file, let's just forget about it. unparsed_files <- files_with_roxy_comments[which(is.null(parsed_files))] diff --git a/R/outline-roxy.R b/R/outline-roxy.R index a07e4e9..a7439cf 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -232,6 +232,6 @@ join_roxy_fun <- function(file) { active_doc_parse <- function(doc = active_rs_doc()) { doc <- purrr::set_names(doc) - parsed <- purrr::map(doc, roxygen2::parse_file) - parsed |> join_roxy_fun() + parsed <- purrr::map(doc, \(x) roxygen2::parse_file(x, env = NULL)) + parsed |> join_roxy_fun() |> define_outline_criteria_roxy() } diff --git a/tests/testthat/_ref/roxy-section.R b/tests/testthat/_ref/roxy-section.R new file mode 100644 index 0000000..b1847f9 --- /dev/null +++ b/tests/testthat/_ref/roxy-section.R @@ -0,0 +1,24 @@ +## multiple tags + name parsing issue -------------------------- +# Mix md and `@section` +#' A title to be included +#' +#' A description not to be included +#' +#' @section a section: +#' +#' +#' @section another section: +#' +#' A second-level heading in description to be included? +#' +#' # A detail first level-heading to be included +#' @name xxx +NULL +#' @section another sectio2n: +#' +#' A second-level heading in description to be included? +#' +#' # A detail first level-heading to be included +#' @name yyy +NULL + diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index bbe1f6f..0a618f9 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -21,3 +21,13 @@ test_that("roxy tags don't error", { expect_no_error(join_roxy_fun(example_parsed)) }) + + +test_that("multiple roxy tags don't error.", { + skip("not yet working.") + file_to_map <- testthat::test_path("_ref", "roxy-section.R") + names(file_to_map) <- file_to_map + example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) + expect_no_error(join_roxy_fun(example_parsed)) + +}) From 323a121309f010e2c2a873a1a8a20d6147733e25 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 08:38:27 -0400 Subject: [PATCH 26/71] Fix `pos` and `objects` to make sure they have a common length. --- R/outline-roxy.R | 24 +++++++++++++++++++++--- README.md | 8 +++++++- tests/testthat/test-outline-roxy.R | 3 --- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index a7439cf..04b49c6 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -21,7 +21,6 @@ extract_roxygen_tag_location <- function(file, tag) { return(character(0L)) } aa <- aa[lengths(pos) > 0L] - pos <- purrr::list_flatten(pos) objects <- purrr::map( aa, \(x) x$object$topic @@ -46,11 +45,30 @@ extract_roxygen_tag_location <- function(file, tag) { } } + # double object name + for (i in seq_along(pos)) { + l <- length(pos[[i]]) + if (l > 1) { + # to repeat object name to be same length as `pos` + objects[[i]] <- as.list(rep(objects[[i]][1], length.out = l)) + } + } + # Unnest to make it easier. + pos <- purrr::list_flatten(pos) + objects <- purrr::list_flatten(objects) + if (length(objects) != length(pos)) { + print(objects) + print(pos) + cli::cli_abort(c( + "Could not resolve pos and objects to be the same length.", + "pos = {length(pos)}, objects = {length(objects)}." + ), + .internal = TRUE + ) + } - # browser() pos <- purrr::set_names(pos, pos$file) - # browser() val <- withCallingHandlers( purrr::map2(pos, objects, \(x, obj_name) { el <- x$val diff --git a/README.md b/README.md index 8574ea0..18a31a5 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 1.55s 1.55s 0.647 68.6MB 5.17 +#> 1 outline <- proj_outline() 1.57s 1.57s 0.636 68.8MB 4.45 ```
@@ -230,6 +230,7 @@ outline #> `i` document manipulation helpers #> `i` TODO structure and summarise information.- `Done✔?` #> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` +#> `i` Navigate to Files Pane #> #> ── `R/outdated-pkgs.R` Looks for outdated packages #> `i` TODO figure out pad :)- `Done✔?` @@ -359,6 +360,10 @@ outline #> `i` A long ggplot2 title #> `i` A code section #> +#> ── `tests/testthat/_ref/roxy-section.R` multiple tags + name parsing issue +#> `i` A detail first level-heading to be included +#> `i` A detail first level-heading to be included +#> #> ── `tests/testthat/_ref/single-title.md` The title is the only outline element #> #> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error @@ -443,6 +448,7 @@ outline #> #> ── `tests/testthat/test-outline-roxy.R` #> `i` roxy tags don't error +#> `i` multiple roxy tags don't error. #> #> ── `tests/testthat/test-outline.R` #> `i` alpha and work_only arguments work diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 0a618f9..1c21066 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -22,12 +22,9 @@ test_that("roxy tags don't error", { }) - test_that("multiple roxy tags don't error.", { - skip("not yet working.") file_to_map <- testthat::test_path("_ref", "roxy-section.R") names(file_to_map) <- file_to_map example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) expect_no_error(join_roxy_fun(example_parsed)) - }) From 910a202c39f4f2def91bae8fd27bf8d442a5f4a6 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 08:41:34 -0400 Subject: [PATCH 27/71] Add `active_rs_doc_nav()` to navigate to Files Pane at location. --- NAMESPACE | 1 + R/open.R | 33 +++++++++++++++++++++++++++++++++ man/active_rs_doc_nav.Rd | 21 +++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 man/active_rs_doc_nav.Rd diff --git a/NAMESPACE b/NAMESPACE index 175ae06..1455f4e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,6 +4,7 @@ S3method(print,outline_report) export(active_rs_doc) export(active_rs_doc_copy) export(active_rs_doc_delete) +export(active_rs_doc_nav) export(arrange_identity) export(browse_pkg) export(case_if_any) diff --git a/R/open.R b/R/open.R index 67d3b42..e2422d7 100644 --- a/R/open.R +++ b/R/open.R @@ -361,3 +361,36 @@ normalize_proj_and_path <- function(path, call = caller_env()) { full_path = full_path ) } + +#' Open Files Pane at current document location +#' +#' Easily navigate to active file document. +#' +#' Wrapper around [executeCommand("activateFiles")][rstudioapi::executeCommand()] + +#' [rstudioapi::filesPaneNavigate()] + [rstudioapi::getActiveDocumentContext()] +#' +#' @param path A path to file to navigate to (default active document). +#' +#' @returns NULL, called for its side effects. +#' @export +active_rs_doc_nav <- function(path = active_rs_doc()) { + if (!rstudioapi::isAvailable() || !interactive()) { + cli::cli_abort("Must use in RStudio interactive sessions.") + } + if (is.null(path)) { + cli::cli_abort("Can't navigate to an unsaved file!") + } + if (fs::is_file(path)) { + dir <- fs::path_dir(path) + } else if (fs::is_dir(path)) { + dir <- path + } else { + cli::cli_abort("{.arg path} must be an existing file or directory.") + } + rstudioapi::executeCommand("activateFiles") + rstudioapi::filesPaneNavigate(dir) + cli::cli_inform(c( + "v" = "Navigated to {.path {dir}} in RStudio Files Pane." + )) + invisible() +} diff --git a/man/active_rs_doc_nav.Rd b/man/active_rs_doc_nav.Rd new file mode 100644 index 0000000..21d6159 --- /dev/null +++ b/man/active_rs_doc_nav.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/open.R +\name{active_rs_doc_nav} +\alias{active_rs_doc_nav} +\title{Open Files Pane at current document location} +\usage{ +active_rs_doc_nav(path = active_rs_doc()) +} +\arguments{ +\item{path}{A path to file to navigate to (default active document).} +} +\value{ +NULL, called for its side effects. +} +\description{ +Easily navigate to active file document. +} +\details{ +Wrapper around \link[rstudioapi:executeCommand]{executeCommand("activateFiles")} + +\code{\link[rstudioapi:filesPaneNavigate]{rstudioapi::filesPaneNavigate()}} + \code{\link[rstudioapi:rstudio-editors]{rstudioapi::getActiveDocumentContext()}} +} From 8cec6ceee46db661424d209389ac3ca048d44fb8 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 09:02:00 -0400 Subject: [PATCH 28/71] Rename to `_outline` for consistency. --- .github/CONTRIBUTING.md | 2 +- R/outline.R | 2 +- README.md | 50 ++++++++++--------- .../testthat/{_ref => _outline}/my-analysis.R | 0 .../{_ref => _outline}/my-analysis.md | 0 .../roxy-general.R} | 6 +-- .../roxy-general2.R} | 4 +- .../{_ref => _outline}/roxy-section.R | 0 .../single-title.md => _outline/title.md} | 0 .../many-titles.md => _outline/titles.md} | 0 tests/testthat/_snaps/outline.md | 6 +-- tests/testthat/test-outline-roxy.R | 6 +-- tests/testthat/test-outline.R | 12 ++--- tests/testthat/test-rename.R | 2 +- 14 files changed, 47 insertions(+), 43 deletions(-) rename tests/testthat/{_ref => _outline}/my-analysis.R (100%) rename tests/testthat/{_ref => _outline}/my-analysis.md (100%) rename tests/testthat/{_ref/test-roxygen.R => _outline/roxy-general.R} (91%) rename tests/testthat/{_ref/test-roxygen-safeguard.R => _outline/roxy-general2.R} (85%) rename tests/testthat/{_ref => _outline}/roxy-section.R (100%) rename tests/testthat/{_ref/single-title.md => _outline/title.md} (100%) rename tests/testthat/{_ref/many-titles.md => _outline/titles.md} (100%) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e68d19e..951ea85 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -112,6 +112,6 @@ Be careful for markdown vs non-markdown (i.e. section title not the same) 5. If important, add to criteria in `define_important_element()` -6. Look at the result. Ideally, add to _ref/my-analysis.R, so it shows somehow in snapshots. +6. Look at the result. Ideally, add to _outline/my-analysis.R, so it shows somehow in snapshots. NEWS.md is handled differently than other files. diff --git a/R/outline.R b/R/outline.R index c0a8a41..9f1d88a 100644 --- a/R/outline.R +++ b/R/outline.R @@ -312,7 +312,7 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, dir_tree = # examples don't help understand a project. file_list_to_outline <- fs::path_filter( file_list_to_outline, - regexp = "testthat/_ref/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples", + regexp = "testthat/_outline/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples", invert = TRUE ) } diff --git a/README.md b/README.md index 18a31a5..cbb43ea 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 1.57s 1.57s 0.636 68.8MB 4.45 +#> 1 outline <- proj_outline() 2.32s 2.32s 0.431 69MB 3.02 ```
@@ -230,7 +230,7 @@ outline #> `i` document manipulation helpers #> `i` TODO structure and summarise information.- `Done✔?` #> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` -#> `i` Navigate to Files Pane +#> `i` Open Files Pane at current document location #> #> ── `R/outdated-pkgs.R` Looks for outdated packages #> `i` TODO figure out pad :)- `Done✔?` @@ -337,13 +337,7 @@ outline #> `i` A great title #> `i` TODO improve this Viz!- `Done✔?` #> -#> ── `tests/testthat/_ref/many-titles.md` The title is the only outline element -#> `i` Another title -#> `i` Second level -#> `i` TODO this is an item- `Done✔?` -#> `i` Last title -#> -#> ── `tests/testthat/_ref/my-analysis.R` Analyse my streets +#> ── `tests/testthat/_outline/my-analysis.R` Analyse my streets #> `i` Read my streets () data #> `i` data wrangling #> `i` Write my streets @@ -351,7 +345,7 @@ outline #> `i` 'R/my-file.R' #> `i` Section title #> -#> ── `tests/testthat/_ref/my-analysis.md` My doc title +#> ── `tests/testthat/_outline/my-analysis.md` My doc title #> `i` A section #> `i` Dashboard card #> `i` A code section @@ -360,19 +354,8 @@ outline #> `i` A long ggplot2 title #> `i` A code section #> -#> ── `tests/testthat/_ref/roxy-section.R` multiple tags + name parsing issue -#> `i` A detail first level-heading to be included -#> `i` A detail first level-heading to be included -#> -#> ── `tests/testthat/_ref/single-title.md` The title is the only outline element -#> -#> ── `tests/testthat/_ref/test-roxygen-safeguard.R` Test for roxygen parsing for no error -#> `i` Use 'tests/testthat/_ref/test-roxygen.R' for output testing -#> `i` Commented code not included -#> `i` A title not to be included -#> -#> ── `tests/testthat/_ref/test-roxygen.R` -#> `i` Use 'tests/testthat/_ref/test-roxygen-safeguard.R' for output testing +#> ── `tests/testthat/_outline/roxy-general.R` +#> `i` Use 'tests/testthat/_outline/roxy-general2.R' for output testing #> `i` Complete block for exported function with headings #> `i` A second-level heading in description to be included? #> `i` A detail first level-heading to be included @@ -394,6 +377,23 @@ outline #> `i` data to index #> `i` TODO add data block example to index- `Done✔?` #> +#> ── `tests/testthat/_outline/roxy-general2.R` Test for roxygen parsing for no error +#> `i` Use 'tests/testthat/_outline/ex-outline-roxy.R' for output testing +#> `i` Commented code not included +#> `i` A title not to be included +#> +#> ── `tests/testthat/_outline/roxy-section.R` multiple tags + name parsing issue +#> `i` A detail first level-heading to be included +#> `i` A detail first level-heading to be included +#> +#> ── `tests/testthat/_outline/title.md` The title is the only outline element +#> +#> ── `tests/testthat/_outline/titles.md` The title is the only outline element +#> `i` Another title +#> `i` Second level +#> `i` TODO this is an item- `Done✔?` +#> `i` Last title +#> #> ── `tests/testthat/_snaps/case-if-any.md` #> `i` wrong cases error #> @@ -410,6 +410,10 @@ outline #> `i` alpha and work_only arguments work #> `i` pattern works as expected #> +#> ── `tests/testthat/_snaps/outline.new.md` +#> `i` alpha and work_only arguments work +#> `i` pattern works as expected +#> #> ── `tests/testthat/_snaps/rename.md` #> `i` Helper files returns the expected input #> diff --git a/tests/testthat/_ref/my-analysis.R b/tests/testthat/_outline/my-analysis.R similarity index 100% rename from tests/testthat/_ref/my-analysis.R rename to tests/testthat/_outline/my-analysis.R diff --git a/tests/testthat/_ref/my-analysis.md b/tests/testthat/_outline/my-analysis.md similarity index 100% rename from tests/testthat/_ref/my-analysis.md rename to tests/testthat/_outline/my-analysis.md diff --git a/tests/testthat/_ref/test-roxygen.R b/tests/testthat/_outline/roxy-general.R similarity index 91% rename from tests/testthat/_ref/test-roxygen.R rename to tests/testthat/_outline/roxy-general.R index 342830a..a7b340e 100644 --- a/tests/testthat/_ref/test-roxygen.R +++ b/tests/testthat/_outline/roxy-general.R @@ -1,5 +1,5 @@ # This file is for roxygen comments parsing -## Use {.file tests/testthat/_ref/test-roxygen-safeguard.R} for output testing ----------- +## Use {.file tests/testthat/_outline/roxy-general2.R} for output testing ----------- ## Complete block for exported function with headings -------------------------- # Mix md and `@section` @@ -97,6 +97,6 @@ NULL # data to index ---------------------------------------------------------------- # TODO add data block example to index -# I think I'd want the outline to show as "outline": title (but ctrl + . does a good job for this) (maybe document this) +# I think I'd want the outline to show as "outline": title (but Ctrl + . does a good job for this) (maybe document this) -# Keep this line last: content to test for edge cases should be put in test-roxygen-safeguard.R +# Keep this line last: content to test for edge cases should be put in {.file tests/testthat/_outline/roxy-general2.R} diff --git a/tests/testthat/_ref/test-roxygen-safeguard.R b/tests/testthat/_outline/roxy-general2.R similarity index 85% rename from tests/testthat/_ref/test-roxygen-safeguard.R rename to tests/testthat/_outline/roxy-general2.R index fbfe714..f6026ec 100644 --- a/tests/testthat/_ref/test-roxygen-safeguard.R +++ b/tests/testthat/_outline/roxy-general2.R @@ -1,5 +1,5 @@ # Test for roxygen parsing for no error ---------------------------------------- -## Use {.file tests/testthat/_ref/test-roxygen.R} for output testing ----------- +## Use {.file tests/testthat/_outline/ex-outline-roxy.R} for output testing ----------- # Dump of other things to test for expect_no_error (not necessary to verify) # Mostly cases inspired by testing in the wild. @@ -34,4 +34,4 @@ f_not_to_index <- function() { } -# Keep this line last: content to test for new roxygen output should be put in test-roxygen.R +# Keep this line last: content to test for new roxygen output should be put in ex-outline-roxy.R diff --git a/tests/testthat/_ref/roxy-section.R b/tests/testthat/_outline/roxy-section.R similarity index 100% rename from tests/testthat/_ref/roxy-section.R rename to tests/testthat/_outline/roxy-section.R diff --git a/tests/testthat/_ref/single-title.md b/tests/testthat/_outline/title.md similarity index 100% rename from tests/testthat/_ref/single-title.md rename to tests/testthat/_outline/title.md diff --git a/tests/testthat/_ref/many-titles.md b/tests/testthat/_outline/titles.md similarity index 100% rename from tests/testthat/_ref/many-titles.md rename to tests/testthat/_outline/titles.md diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index c372f55..5d21f14 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -15,7 +15,7 @@ `i` Dashboard card Message - -- `many-titles.md` The title is the only outline element + -- `titles.md` The title is the only outline element Output `i` Another title `i` Last title @@ -33,7 +33,7 @@ `i` 'R/my-file.R' Message - -- `single-title.md` The title is the only outline element + -- `title.md` The title is the only outline element # alpha and work_only arguments work @@ -41,7 +41,7 @@ file_outline("street", my_test_file, alpha = TRUE, work_only = FALSE) Message - -- `ref/my-analysis.R` Analyse my streets + -- `outline/my-analysis.R` Analyse my streets Output `i` Read my streets () data `i` Write my streets diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 1c21066..1e888e0 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -1,7 +1,7 @@ test_that("roxy tags are parsed properly", { skip_if_not_installed("roxygen2") skip_if_not_installed("tidyr") - file_to_map <- testthat::test_path("_ref", "test-roxygen.R") + file_to_map <- testthat::test_path("_outline", "roxy-general.R") names(file_to_map) <- file_to_map example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) @@ -15,7 +15,7 @@ test_that("roxy tags are parsed properly", { }) test_that("roxy tags don't error", { - file_to_map <- testthat::test_path("_ref", "test-roxygen-safeguard.R") + file_to_map <- testthat::test_path("_outline", "roxy-general2.R") names(file_to_map) <- file_to_map example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) expect_no_error(join_roxy_fun(example_parsed)) @@ -23,7 +23,7 @@ test_that("roxy tags don't error", { }) test_that("multiple roxy tags don't error.", { - file_to_map <- testthat::test_path("_ref", "roxy-section.R") + file_to_map <- testthat::test_path("_outline", "roxy-section.R") names(file_to_map) <- file_to_map example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) expect_no_error(join_roxy_fun(example_parsed)) diff --git a/tests/testthat/test-outline.R b/tests/testthat/test-outline.R index b1a2c22..c5f1a19 100644 --- a/tests/testthat/test-outline.R +++ b/tests/testthat/test-outline.R @@ -1,5 +1,5 @@ test_that("file_outline() works", { - my_test_files <- test_path("_ref", c("my-analysis.R", "my-analysis.md", "single-title.md", "many-titles.md")) + my_test_files <- test_path("_outline", c("my-analysis.R", "my-analysis.md", "title.md", "titles.md")) rlang::local_interactive(TRUE) expect_snapshot( file_outline(path = my_test_files, alpha = TRUE), @@ -8,7 +8,7 @@ test_that("file_outline() works", { }) test_that("alpha and work_only arguments work", { - my_test_file <- test_path("_ref/my-analysis.R") + my_test_file <- test_path("_outline/my-analysis.R") rlang::local_interactive(TRUE) # Somehow on r cmd check, strips _ref -> ref? # it is just RStudio vs non-Rstudio @@ -53,13 +53,13 @@ test_that("pattern works as expected", { test_that("file_outline() with only title doesn't error", { # broken by change to before_and_after_empty expect_no_error({ - file <- file_outline(path = test_path("_ref", "single-title.md")) + file <- file_outline(path = test_path("_outline", "title.md")) }) expect_equal(nrow(file), 1L) expect_no_error({ - file <- file_outline(path = test_path("_ref", "many-titles.md")) + file <- file_outline(path = test_path("_outline", "titles.md")) }) - # Number of items in many-titles.md + # Number of items in titles.md expect_equal(nrow(file), 5L) }) @@ -72,5 +72,5 @@ test_that("file_outline() contains function calls", { }) test_that("dir_outline() works with no error", { - expect_no_error(dir_outline(pattern = ".+", path = test_path("_ref"))) + expect_no_error(dir_outline(pattern = ".+", path = test_path("_outline"))) }) diff --git a/tests/testthat/test-rename.R b/tests/testthat/test-rename.R index f8bdabc..2b9bc38 100644 --- a/tests/testthat/test-rename.R +++ b/tests/testthat/test-rename.R @@ -1,5 +1,5 @@ describe("rename_files2()", { - og_file <- fs::path_real(test_path("_ref", "my-analysis.R")) + og_file <- fs::path_real(test_path("_outline", "my-analysis.R")) # temp dir + change working directory tmp_dir <- withr::local_tempdir() fs::dir_create( From 406a0c4b2f9a400ff257b97efc7f8107445dcf1d Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 11:51:16 -0400 Subject: [PATCH 29/71] Better topic name detection --- R/outline-roxy.R | 14 +++++++++++++- tests/testthat/_outline/roxy-general.R | 7 ++++++- tests/testthat/_outline/roxy-section.R | 1 - tests/testthat/test-outline-roxy.R | 14 ++++++++++---- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 04b49c6..6e0654c 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -23,7 +23,19 @@ extract_roxygen_tag_location <- function(file, tag) { aa <- aa[lengths(pos) > 0L] objects <- purrr::map( aa, - \(x) x$object$topic + \(x) { + if (!is.null(x$object$topic)) { + return(x$object$topic) + } + object_call <- as.character(x$call) + if (length(object_call) == 1) { + return(object_call) + } + if (length(object_call) > 1) { + return(object_call[2]) + } + NULL + } ) if (any(lengths(objects) == 0)) { name_tag <- purrr::map( diff --git a/tests/testthat/_outline/roxy-general.R b/tests/testthat/_outline/roxy-general.R index a7b340e..5c1f64b 100644 --- a/tests/testthat/_outline/roxy-general.R +++ b/tests/testthat/_outline/roxy-general.R @@ -94,9 +94,14 @@ NULL #' #' Content #' +NULL # data to index ---------------------------------------------------------------- -# TODO add data block example to index # I think I'd want the outline to show as "outline": title (but Ctrl + . does a good job for this) (maybe document this) +#' My data +#' +#' Another dataset +"dataset" + # Keep this line last: content to test for edge cases should be put in {.file tests/testthat/_outline/roxy-general2.R} diff --git a/tests/testthat/_outline/roxy-section.R b/tests/testthat/_outline/roxy-section.R index b1847f9..551ee2f 100644 --- a/tests/testthat/_outline/roxy-section.R +++ b/tests/testthat/_outline/roxy-section.R @@ -21,4 +21,3 @@ NULL #' # A detail first level-heading to be included #' @name yyy NULL - diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 1e888e0..389cf69 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -1,9 +1,9 @@ -test_that("roxy tags are parsed properly", { +test_that("roxy tags are parsed properly + object names are correct", { skip_if_not_installed("roxygen2") skip_if_not_installed("tidyr") file_to_map <- testthat::test_path("_outline", "roxy-general.R") names(file_to_map) <- file_to_map - example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) + example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) example_parsed |> extract_roxygen_tag_location("details") |> @@ -12,12 +12,18 @@ test_that("roxy tags are parsed properly", { expect_no_error(res <- join_roxy_fun(example_parsed)) expect_s3_class(res, "tbl_df") + # verify if topic name is well done. + res_order <- dplyr::arrange(res, line) + expect_setequal( + res$topic, + c("f_to_be_index_in_outline", "topic-name-to-include", "no-topic", "dataset") + ) }) test_that("roxy tags don't error", { file_to_map <- testthat::test_path("_outline", "roxy-general2.R") names(file_to_map) <- file_to_map - example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) + example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) expect_no_error(join_roxy_fun(example_parsed)) }) @@ -25,6 +31,6 @@ test_that("roxy tags don't error", { test_that("multiple roxy tags don't error.", { file_to_map <- testthat::test_path("_outline", "roxy-section.R") names(file_to_map) <- file_to_map - example_parsed <- purrr::map(file_to_map, roxygen2::parse_file) + example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) expect_no_error(join_roxy_fun(example_parsed)) }) From c0e9063c4605a4e15e6ceff9ac87f0cb77ca1dec Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 11:53:38 -0400 Subject: [PATCH 30/71] Improve regex to allow for title to be wrapped in function + family to parse the first line only. --- R/outline-roxy.R | 7 ++++++- R/outline.R | 5 +++-- tests/testthat/_outline/my-analysis.R | 4 ++-- tests/testthat/_outline/roxy-general2.R | 2 ++ tests/testthat/_snaps/outline.md | 21 +++++++++++---------- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 6e0654c..99a31aa 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -239,6 +239,11 @@ join_roxy_fun <- function(file) { file, topic, content, line, tag ) |> dplyr::mutate(id = dplyr::row_number()) |> + dplyr::mutate(content = dplyr::case_when( + # only keep the first line of family and concept tags. + tag %in% c("family", "concept") ~ stringr::str_extract(content, "^(.+)(\n)?"), + .default = content + )) |> tidyr::separate_longer_delim(content, delim = "\n") roxy_parsed1 |> @@ -253,7 +258,7 @@ join_roxy_fun <- function(file) { .default = content ) ) |> - dplyr::filter(nzchar(content)) |> + dplyr::filter(nzchar(content), !stringr::str_detect(content, "`r\\s")) |> dplyr::select(-id, -n) } diff --git a/R/outline.R b/R/outline.R index 9f1d88a..9a6407c 100644 --- a/R/outline.R +++ b/R/outline.R @@ -508,7 +508,7 @@ display_outline_element <- function(.data, dir_common) { is_cli_info ~ stringr::str_extract(outline_el, "[\"'](.{5,})[\"']") |> stringr::str_remove_all("\""), # family or concept! is_tab_or_plot_title & !is.na(tag) ~ outline_el, - is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title = [\"']([^\"]{5,})[\"']", group = 1), + is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title =.*[\"']([^\"]{5,})[\"']", group = 1), is_chunk_cap_next & !is_chunk_cap ~ stringr::str_remove_all(outline_el, "\\s?\\#\\|\\s+"), is_chunk_cap ~ stringr::str_remove_all(stringr::str_extract(outline_el, "(cap|title)\\:\\s*(.+)", group = 2), "\"|'"), is_cross_ref ~ stringr::str_remove_all(outline_el, "^(i.stat\\:\\:)?.cdocs_lin.s\\(|[\"']\\)$|\""), @@ -530,6 +530,7 @@ display_outline_element <- function(.data, dir_common) { ) if (anyNA(x$outline_el)) { + zz <<- x |> dplyr::filter(is.na(outline_el)) indices <- which(is.na(x$outline_el)) all_na <- x |> dplyr::select(!dplyr::where(\(x) !is.logical(x) & all(is.na(x)))) |> dplyr::slice(dplyr::all_of(indices)) |> dplyr::select(dplyr::where(\(x) all(is.na(x)))) |> names() all_true_or_single_value <- x |> dplyr::slice(dplyr::all_of(indices)) |> dplyr::select(dplyr::where(\(x)dplyr::n_distinct(x) == 1)) |> dplyr::select(!dplyr::where(\(x) all(is.na(x)))) |> @@ -544,7 +545,7 @@ display_outline_element <- function(.data, dir_common) { } cli::cli_abort(c("Internal error, outline elements can't be NA. Please review.", msg, "Criteria are created in {.fn define_outline_criteria} and {.fn define_outline_criteria_roxy}. - `outline_el` is defined in {.fn display_outline_element}.")) + `outline_el` is defined in {.fn display_outline_element}. Investigate `zz` for debugging.")) } y <- dplyr::mutate( diff --git a/tests/testthat/_outline/my-analysis.R b/tests/testthat/_outline/my-analysis.R index 86dfcbc..ae07114 100644 --- a/tests/testthat/_outline/my-analysis.R +++ b/tests/testthat/_outline/my-analysis.R @@ -1,6 +1,6 @@ # Analyse my streets --------------- ## Read my [streets](https://https://en.wikipedia.org/wiki/Street_art) data ------- -my_streets <- read.csv("data/my-streets.csv"), silent = TRUE) +my_streets <- read.csv("data/my-streets.csv", silent = TRUE) new_dat <- my_streets |> dplyr::mutate( title = "data wrangling", # "problem" @@ -18,7 +18,7 @@ fs::path("data", "my-streets", ext = "csv") # TODO Create a new version # {.file R/my-file.R} --- - +tab_header(title = md("**A table title**")) cli::cli_ul("Refer to {.href [google](https://google.com)}") # ## a commented section title ----- diff --git a/tests/testthat/_outline/roxy-general2.R b/tests/testthat/_outline/roxy-general2.R index f6026ec..5352047 100644 --- a/tests/testthat/_outline/roxy-general2.R +++ b/tests/testthat/_outline/roxy-general2.R @@ -14,6 +14,8 @@ #' ) #' @export #' @family a family to include +#' if { +#' } f_to_be_index_in_outline <- function() { } diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index 5d21f14..92b7c99 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -4,6 +4,17 @@ file_outline(path = my_test_files, alpha = TRUE) Message + -- `my-analysis.R` Analyse my streets + Output + `i` **A table title** + `i` TODO Create a new version- `Donev?` + `i` Read my streets () data + `i` Section title + `i` Write my streets + `i` 'R/my-file.R' + `i` problem + Message + -- `my-analysis.md` My doc title Output `i` A section @@ -23,16 +34,6 @@ `i` TODO this is an item- `Donev?` Message - -- `my-analysis.R` Analyse my streets - Output - `i` TODO Create a new version- `Donev?` - `i` Read my streets () data - `i` Section title - `i` Write my streets - `i` data wrangling - `i` 'R/my-file.R' - Message - -- `title.md` The title is the only outline element # alpha and work_only arguments work From 8badd76f0e2ef5a06552a07b46144ece2c20dc83 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 11:55:33 -0400 Subject: [PATCH 31/71] Improve `proj_file()` if exact match in `proj`. --- R/proj-list.R | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/R/proj-list.R b/R/proj-list.R index ab2a6bb..acf3168 100644 --- a/R/proj-list.R +++ b/R/proj-list.R @@ -58,10 +58,16 @@ proj_file <- function(file = NULL, proj = NULL, pattern = NULL) { if (fs::is_file(file)) { file_outline(path = file) open_rs_doc(file) + return(invisible(file)) } proj <- proj %||% proj_get2() proj_path <- proj_list(proj) - + file_path <- fs::path(proj_path, file) + if (fs::is_file(file_path)) { + file_outline(path = file_path) + open_rs_doc(file_path) + return(invisible(file_path)) + } file_exts <- c("R", "qmd", "Rmd", "md", "Rmarkdown") file_exts_regex <- paste0("*.", file_exts, "$", collapse = "|") possible_files <- fs::dir_ls(proj_path, regexp = file_exts_regex, recurse = TRUE) From 83f0c4a5e8f14fd8ea1ae8fdb18a4d1a85bf340b Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 12:35:02 -0400 Subject: [PATCH 32/71] Make sure pos and objects have the same length. --- R/outline-roxy.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 99a31aa..8065129 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -21,6 +21,7 @@ extract_roxygen_tag_location <- function(file, tag) { return(character(0L)) } aa <- aa[lengths(pos) > 0L] + pos <- pos[lengths(pos) > 0L] objects <- purrr::map( aa, \(x) { From 594b627bf3674ecebbef1132d04a42199a9685bb Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 12:39:42 -0400 Subject: [PATCH 33/71] fix regex for plot title. --- R/outline.R | 2 +- README.md | 9 +++------ tests/testthat/_snaps/outline.md | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/R/outline.R b/R/outline.R index 9a6407c..b5a84bd 100644 --- a/R/outline.R +++ b/R/outline.R @@ -508,7 +508,7 @@ display_outline_element <- function(.data, dir_common) { is_cli_info ~ stringr::str_extract(outline_el, "[\"'](.{5,})[\"']") |> stringr::str_remove_all("\""), # family or concept! is_tab_or_plot_title & !is.na(tag) ~ outline_el, - is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title =.*[\"']([^\"]{5,})[\"']", group = 1), + is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title =[^\"']*[\"']([^\"]{5,})[\"']", group = 1), is_chunk_cap_next & !is_chunk_cap ~ stringr::str_remove_all(outline_el, "\\s?\\#\\|\\s+"), is_chunk_cap ~ stringr::str_remove_all(stringr::str_extract(outline_el, "(cap|title)\\:\\s*(.+)", group = 2), "\"|'"), is_cross_ref ~ stringr::str_remove_all(outline_el, "^(i.stat\\:\\:)?.cdocs_lin.s\\(|[\"']\\)$|\""), diff --git a/README.md b/README.md index cbb43ea..c7d45cd 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 2.32s 2.32s 0.431 69MB 3.02 +#> 1 outline <- proj_outline() 1.45s 1.45s 0.688 71.1MB 4.82 ```
@@ -343,6 +343,7 @@ outline #> `i` Write my streets #> `i` TODO Create a new version- `Done✔?` #> `i` 'R/my-file.R' +#> `i` **A table title** #> `i` Section title #> #> ── `tests/testthat/_outline/my-analysis.md` My doc title @@ -375,7 +376,6 @@ outline #> `i` Details + 2nd level heading #> `i` second heading #> `i` data to index -#> `i` TODO add data block example to index- `Done✔?` #> #> ── `tests/testthat/_outline/roxy-general2.R` Test for roxygen parsing for no error #> `i` Use 'tests/testthat/_outline/ex-outline-roxy.R' for output testing @@ -410,10 +410,6 @@ outline #> `i` alpha and work_only arguments work #> `i` pattern works as expected #> -#> ── `tests/testthat/_snaps/outline.new.md` -#> `i` alpha and work_only arguments work -#> `i` pattern works as expected -#> #> ── `tests/testthat/_snaps/rename.md` #> `i` Helper files returns the expected input #> @@ -451,6 +447,7 @@ outline #> `i` No outline criteria are untested #> #> ── `tests/testthat/test-outline-roxy.R` +#> `i` roxy tags are parsed properly + object names are correct #> `i` roxy tags don't error #> `i` multiple roxy tags don't error. #> diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index 92b7c99..e015503 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -11,8 +11,8 @@ `i` Read my streets () data `i` Section title `i` Write my streets + `i` data wrangling `i` 'R/my-file.R' - `i` problem Message -- `my-analysis.md` My doc title From 35ba23dedaaf5f9c2661e65ec8982418bbde3c0d Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 12:41:29 -0400 Subject: [PATCH 34/71] Avoid uninteresting roxy headings. --- R/outline-criteria.R | 12 +++++++----- tests/testthat/test-outline-criteria.R | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index aefe3d3..14e1c65 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -80,13 +80,15 @@ o_is_tab_plot_title <- function(x) { !stringr::str_detect(x, "expect_error|header\\(\\)|```\\{") } -o_is_section_title <- function(x) { - is_section_title <- stringr::str_detect(x, "^\\s{0,4}\\#+\\s+(?!\\#)|^\\#'\\s\\#+\\s") # remove commented add roxygen +o_is_section_title <- function(x, roxy_section = FALSE) { + is_section_title <- stringr::str_detect(x, "^\\s{0,4}\\#+\\s+(?!\\#)|^\\#'\\s\\#+\\s") | roxy_section # remove commented add roxygen if (!any(is_section_title)) { return(is_section_title) } - - uninteresting_headings <- "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE|url\\{|Error before installation" + if (roxy_section) { + x <- stringr::str_remove(x, ":$") + } + uninteresting_headings <- "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE|url\\{|Error before installation|unreleased|^Function ID$|^Function Introduced$|^Examples$" # potential section titles p_s_title <- which(is_section_title) is_section_title[p_s_title] <- !stringr::str_detect(x[p_s_title], uninteresting_headings) & !o_is_todo_fixme(x[p_s_title]) & !o_is_commented_code(x[p_s_title]) @@ -217,7 +219,7 @@ define_outline_criteria_roxy <- function(x) { x$is_test_file <- FALSE x$is_snap_file <- FALSE x$before_and_after_empty <- TRUE - x$is_section_title <- (x$tag %in% c("section", "subsection") & stringr::str_ends(x$content, ":")) | + x$is_section_title <- (x$tag %in% c("section", "subsection") & stringr::str_ends(x$content, ":") & o_is_section_title(x$content, roxy_section = TRUE)) | (x$tag %in% c("details", "description") & stringr::str_detect(x$content, "#\\s")) x$is_section_title_source <- x$is_section_title x$is_chunk_cap <- FALSE diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index 1c743ec..7db4222 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -42,8 +42,10 @@ test_that("o_is_section_title() works", { expect_true(o_is_section_title("# Analysis of this")) expect_true(o_is_section_title(" # section 1 ----")) expect_false(o_is_section_title("# TidyTuesday")) + expect_false(o_is_section_title("Function ID:",roxy_section = TRUE)) }) +# TODO figure out if this is still needed? test_that("o_is_commented_code() works", { expect_true(o_is_commented_code("# DiagrammeR(x = 1,")) expect_true(o_is_commented_code("# DiagrammeR(x = 1)")) From 15be047453f4ba97ca6646e4a4b23b062610605f Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 12:43:43 -0400 Subject: [PATCH 35/71] Don't parse roxy comments in `proj_file()` + add `options("reuseme.roxy_parse")` to allow not parsing roxy comments + make parse_file even more quiet. --- R/outline-criteria.R | 12 ++++++++---- R/proj-list.R | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 14e1c65..de10c26 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -133,14 +133,18 @@ define_outline_criteria <- function(.data, print_todo) { x$content[x$is_notebook] <- sub("^#' ", "", x$content[x$is_notebook]) x$is_md <- (x$is_md | x$is_roxygen_comment) & !x$is_news # treating news and other md files differently. x$is_snap_file <- grepl("_snaps", x$file, fixed = TRUE) - if (any(x$is_roxygen_comment)) { + + should_parse_roxy_comments <- + !isFALSE(getOption("reuseme.roxy_parse", default = TRUE)) && # will not parse if option is set to FALSE + any(x$is_roxygen_comment) + if (should_parse_roxy_comments) { rlang::check_installed(c("roxygen2", "tidyr"), "to create roxygen2 comments outline.") files_with_roxy_comments <- unique(x[x$is_roxygen_comment, "file", drop = TRUE]) files_with_roxy_comments <- rlang::set_names(files_with_roxy_comments, files_with_roxy_comments) - parsed_files <- suppressMessages( # roxygen2 messages + # roxygen2 messages # TRICK purrr::safely creates an error object, while possible is better. - purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))) - ) + invisible(capture.output(parsed_files <- purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))) + )) |> suppressMessages() # if roxygen2 cannot parse a file, let's just forget about it. unparsed_files <- files_with_roxy_comments[which(is.null(parsed_files))] # browser() diff --git a/R/proj-list.R b/R/proj-list.R index acf3168..5ccaaa9 100644 --- a/R/proj-list.R +++ b/R/proj-list.R @@ -48,6 +48,10 @@ proj_switch <- function(proj = NULL, new_session = TRUE) { #' @family project management helpers proj_file <- function(file = NULL, proj = NULL, pattern = NULL) { rlang::check_required(file) + # avoid indexing roxy comments. + withr::local_options( + "reuseme.roxy_parse" = FALSE + ) # search will only be conducted with pattern if (is.null(pattern) && is.null(file)) { cli::cli_abort( From a742214be42875a196fe61ad040032cba23daace Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 12:48:36 -0400 Subject: [PATCH 36/71] Avoid recognizing test_that("a", expect_true(TRUE)) --- R/outline-criteria.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index de10c26..e432357 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -64,7 +64,11 @@ o_is_work_item <- function(x) { o_is_test_that <- function(x) { # avoid generic like f works. - stringr::str_detect(x, "(? Date: Thu, 30 May 2024 13:17:34 -0400 Subject: [PATCH 37/71] Temporarily change directory when parsing roxy comments as it may help? Add `exclude_tests` to avoid printing tests for big repos. --- R/markup.R | 19 ++++----- R/open.R | 2 +- R/outline-criteria.R | 34 ++++++++-------- R/outline-roxy.R | 11 +++-- R/outline.R | 53 +++++++++++++++++-------- man/outline.Rd | 6 ++- tests/testthat/_outline/roxy-general.R | 8 ++-- tests/testthat/_outline/roxy-general2.R | 4 +- tests/testthat/test-outline-criteria.R | 4 +- tests/testthat/test-outline-roxy.R | 19 +++++---- 10 files changed, 95 insertions(+), 65 deletions(-) diff --git a/R/markup.R b/R/markup.R index 1b2a9d8..481f4ea 100644 --- a/R/markup.R +++ b/R/markup.R @@ -37,12 +37,13 @@ link_gh_issue <- function(x, home_repo = NULL) { regex_gh_issue, paste0("[\\1#\\2](https://github.com/\\1/issues/\\2)") ) - if (!is.null(home_repo)) - x_changed <- gsub( - paste0(home_repo,"#"), - "#", - x_changed - ) + if (!is.null(home_repo)) { + x_changed <- gsub( + paste0(home_repo, "#"), + "#", + x_changed + ) + } x[has_gh_issue] <- x_changed x @@ -51,7 +52,7 @@ link_gh_issue <- function(x, home_repo = NULL) { link_local_gh_issue <- function(x, repo_home) { gsub( # max 99999 issues. - pattern = "\\((#\\d{1,5})\\)", + pattern = "\\((#\\d{1,5})\\)", paste0("(", repo_home, "\\1)"), x ) @@ -65,7 +66,8 @@ find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { error = function(e) { # cli::cli_inform("Could not detect path.") NULL - }) + } + ) if (is.null(pkg_path)) { return(NULL) } @@ -96,7 +98,6 @@ find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { if (is.null(org_repo) && is.null(org_repo_found)) { cli::cli_abort("No way to discover URL.") } - } #' Create a cli href with a markdown link #' diff --git a/R/open.R b/R/open.R index e2422d7..62bdbd8 100644 --- a/R/open.R +++ b/R/open.R @@ -256,7 +256,7 @@ active_rs_doc_delete <- function() { cli::cli_inform(c( "v" = "Deleted the active document {.val {elems$rel_path}} because {reasons_deleting}.", # FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 - "i" = paste(cli::col_grey("The deleted file"), "{.path {elems$full_path}}", cli::col_grey("contents are returned invisibly in case you need them.")) + "i" = paste(cli::col_grey("The deleted file"), "{.path {elems$full_path}}", cli::col_grey("contents are returned invisibly in case you need them.")) )) contents <- readLines(elems$full_path, encoding = "UTF-8") fs::file_delete(elems$full_path) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index e432357..2a883bc 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -92,7 +92,7 @@ o_is_section_title <- function(x, roxy_section = FALSE) { if (roxy_section) { x <- stringr::str_remove(x, ":$") } - uninteresting_headings <- "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE|url\\{|Error before installation|unreleased|^Function ID$|^Function Introduced$|^Examples$" + uninteresting_headings <- "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE|url\\{|Error before installation|unreleased|^Function ID$|^Function Introduced$|^Examples$|^Newly broken|^In both|^Installation$|MIT License|nocov" # potential section titles p_s_title <- which(is_section_title) is_section_title[p_s_title] <- !stringr::str_detect(x[p_s_title], uninteresting_headings) & !o_is_todo_fixme(x[p_s_title]) & !o_is_commented_code(x[p_s_title]) @@ -124,7 +124,7 @@ o_is_cli_info <- function(x, is_snap_file = FALSE, file = "file") { # Add variable to outline data frame -------------------- -define_outline_criteria <- function(.data, print_todo) { +define_outline_criteria <- function(.data, print_todo, dir_common) { x <- .data x$file_ext <- s_file_ext(x$file) x$is_md <- x$file_ext %in% c("qmd", "md", "Rmd", "Rmarkdown") @@ -140,15 +140,17 @@ define_outline_criteria <- function(.data, print_todo) { should_parse_roxy_comments <- !isFALSE(getOption("reuseme.roxy_parse", default = TRUE)) && # will not parse if option is set to FALSE - any(x$is_roxygen_comment) + any(x$is_roxygen_comment) if (should_parse_roxy_comments) { + if (!is.null(dir_common)) { + withr::local_dir(dir_common) + } rlang::check_installed(c("roxygen2", "tidyr"), "to create roxygen2 comments outline.") files_with_roxy_comments <- unique(x[x$is_roxygen_comment, "file", drop = TRUE]) files_with_roxy_comments <- rlang::set_names(files_with_roxy_comments, files_with_roxy_comments) - # roxygen2 messages - # TRICK purrr::safely creates an error object, while possible is better. - invisible(capture.output(parsed_files <- purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))) - )) |> suppressMessages() + # roxygen2 messages + # TRICK purrr::safely creates an error object, while possible is better. + invisible(capture.output(parsed_files <- purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))))) |> suppressMessages() # if roxygen2 cannot parse a file, let's just forget about it. unparsed_files <- files_with_roxy_comments[which(is.null(parsed_files))] # browser() @@ -204,7 +206,7 @@ define_outline_criteria <- function(.data, print_todo) { line == 1 | !nzchar(dplyr::lead(content, default = "")) & !nzchar(dplyr::lag(content)), .by = "file" ) - #browser() + # browser() res <- dplyr::bind_rows(x, outline_roxy) res <- dplyr::filter( res, @@ -221,7 +223,7 @@ define_outline_criteria_roxy <- function(x) { x$is_object_title <- x$tag == "title" & nchar(x$content) > 4 x$line <- as.integer(x$line) x$file_ext <- "R" - #x$content <- paste0("#' ", x$content) # maybe not? + # x$content <- paste0("#' ", x$content) # maybe not? x$is_news <- FALSE x$is_roxygen_comment <- TRUE x$is_test_file <- FALSE @@ -231,18 +233,18 @@ define_outline_criteria_roxy <- function(x) { (x$tag %in% c("details", "description") & stringr::str_detect(x$content, "#\\s")) x$is_section_title_source <- x$is_section_title x$is_chunk_cap <- FALSE - x$is_chunk_cap_next = FALSE - x$is_test_name = FALSE - x$pkg_version = NA_character_ + x$is_chunk_cap_next <- FALSE + x$is_test_name <- FALSE + x$pkg_version <- NA_character_ # a family or concept can be seen as a plot subtitle? x$is_tab_or_plot_title <- x$tag %in% c("family", "concept") x$is_cli_info <- FALSE - x$is_cross_ref = FALSE - x$is_function_def = FALSE + x$is_cross_ref <- FALSE + x$is_function_def <- FALSE x$is_todo_fixme <- FALSE x$is_a_comment_or_code <- FALSE x$is_doc_title <- x$line == 1 & x$tag == "title" - x$n_leading_hash = nchar(stringr::str_extract(x$content, "\\#+")) + x$n_leading_hash <- nchar(stringr::str_extract(x$content, "\\#+")) x$n_leading_hash <- dplyr::case_when( x$n_leading_hash > 0 ~ x$n_leading_hash, x$tag == "section" & x$is_section_title_source ~ 1, @@ -256,7 +258,7 @@ define_outline_criteria_roxy <- function(x) { .default = x$content ) x$is_second_level_heading_or_more <- ((x$is_section_title_source | x$is_section_title) & x$n_leading_hash > 1) - #x$has_inline_markup <- FALSE # let's not mess with inline markup + # x$has_inline_markup <- FALSE # let's not mess with inline markup x } diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 8065129..ed3adb5 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -72,9 +72,10 @@ extract_roxygen_tag_location <- function(file, tag) { if (length(objects) != length(pos)) { print(objects) print(pos) - cli::cli_abort(c( - "Could not resolve pos and objects to be the same length.", - "pos = {length(pos)}, objects = {length(objects)}." + cli::cli_abort( + c( + "Could not resolve pos and objects to be the same length.", + "pos = {length(pos)}, objects = {length(objects)}." ), .internal = TRUE ) @@ -269,5 +270,7 @@ join_roxy_fun <- function(file) { active_doc_parse <- function(doc = active_rs_doc()) { doc <- purrr::set_names(doc) parsed <- purrr::map(doc, \(x) roxygen2::parse_file(x, env = NULL)) - parsed |> join_roxy_fun() |> define_outline_criteria_roxy() + parsed |> + join_roxy_fun() |> + define_outline_criteria_roxy() } diff --git a/R/outline.R b/R/outline.R index b5a84bd..d08832e 100644 --- a/R/outline.R +++ b/R/outline.R @@ -42,7 +42,8 @@ #' Defaults to active file, project or directory. `rstudioapi::documentPath()` #' @param pattern A string or regex to search for in the outline. If #' specified, will search only for elements matching this regular expression. -#' The print method will show the document title for context. Previously `regex_outline` +#' The print method will show the document title for context. Previously `regex_outline`. +#' @param exclude_tests Should tests be displayed? (Not sure if this argument will stay, Will have to think) #' @param work_only If `TRUE`, (the default), will only show you work items #' first. Set to `FALSE` if you want to see the full outline. `WORK` will #' combine with `pattern` @@ -162,7 +163,7 @@ file_outline <- function(pattern = NULL, check_string(pattern, arg = "You may have specified path Internal error") - file_sections00 <- define_outline_criteria(file_content, print_todo = print_todo) + file_sections00 <- define_outline_criteria(file_content, print_todo = print_todo, dir_common) # filter for interesting items. file_sections0 <- keep_outline_element(file_sections00) @@ -229,7 +230,7 @@ file_outline <- function(pattern = NULL, } #' @rdname outline #' @export -proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE) { +proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, exclude_tests = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE) { is_active_proj <- identical(proj, proj_get2()) if (is_active_proj && !is.null(pattern) && pattern %in% names(proj_list())) { @@ -244,6 +245,7 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, d return(dir_outline( pattern = pattern, work_only = work_only, + exclude_tests = exclude_tests, dir_tree = dir_tree, alpha = alpha, recent_only = recent_only, @@ -283,6 +285,7 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, d pattern = pattern, path = proj_dir, work_only = work_only, + exclude_tests = exclude_tests, dir_tree = dir_tree, alpha = alpha, recurse = TRUE @@ -290,7 +293,7 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, d } #' @rdname outline #' @export -dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, recurse = FALSE) { +dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_tests = TRUE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, recurse = FALSE) { dir <- fs::path_real(path) file_exts <- c("R", "qmd", "Rmd", "md", "Rmarkdown") file_exts_regex <- paste0("*.", file_exts, "$", collapse = "|") @@ -316,7 +319,13 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, dir_tree = invert = TRUE ) } - + if (exclude_tests) { + file_list_to_outline <- fs::path_filter( + file_list_to_outline, + regexp = "tests/", + invert = TRUE + ) + } if (any(grepl("README.Rmd", file_list_to_outline))) { file_list_to_outline <- stringr::str_subset(file_list_to_outline, "README.md", negate = TRUE) } @@ -422,7 +431,8 @@ print.outline_report <- function(x, ...) { print(thing) print(escape_markup(thing)) cli::cli_abort("failed to parse in first line of file {.file {summary_links_files$file[[i]]}}.", parent = e) - }) + } + ) base_name <- c(base_name, " ", title_el) } @@ -431,7 +441,8 @@ print.outline_report <- function(x, ...) { error = function(e) { print(base_name) rlang::abort("Could not parse by cli") - }) + } + ) if (recent_only) { if (i %in% is_recently_modified) { @@ -479,7 +490,7 @@ keep_outline_element <- function(.data) { # What to keep in .R files (!is_md & is_section_title_source) | # What to keep anywhere - is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def | is_object_title # | is_cli_info # TODO reanable cli info + is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def | is_object_title # | is_cli_info # TODO reanable cli info ) dat$simplify_news <- NULL @@ -514,7 +525,7 @@ display_outline_element <- function(.data, dir_common) { is_cross_ref ~ stringr::str_remove_all(outline_el, "^(i.stat\\:\\:)?.cdocs_lin.s\\(|[\"']\\)$|\""), is_doc_title ~ stringr::str_remove_all(outline_el, "subtitle\\:\\s?|title\\:\\s?|\"|\\#\\|\\s?"), is_section_title & !is_md ~ stringr::str_remove(outline_el, "^\\s{0,4}\\#+\\s+|^\\#'\\s\\#+\\s+"), # Keep inline markup - is_section_title & is_md ~ stringr::str_remove_all(outline_el, "^\\#+\\s+|\\{.+\\}"), # strip cross-refs. + is_section_title & is_md ~ stringr::str_remove_all(outline_el, "^\\#+\\s+|\\{.+\\}| stringr::str_trim(), .default = stringr::str_remove_all(outline_el, "^\\s*\\#+\\|?\\s?(label:\\s)?|\\s?[-\\=]{4,}") ), @@ -532,9 +543,17 @@ display_outline_element <- function(.data, dir_common) { if (anyNA(x$outline_el)) { zz <<- x |> dplyr::filter(is.na(outline_el)) indices <- which(is.na(x$outline_el)) - all_na <- x |> dplyr::select(!dplyr::where(\(x) !is.logical(x) & all(is.na(x)))) |> dplyr::slice(dplyr::all_of(indices)) |> dplyr::select(dplyr::where(\(x) all(is.na(x)))) |> names() - all_true_or_single_value <- x |> dplyr::slice(dplyr::all_of(indices)) |> dplyr::select(dplyr::where(\(x)dplyr::n_distinct(x) == 1)) |> dplyr::select(!dplyr::where(\(x) all(is.na(x)))) |> - dplyr::select(!dplyr::where(\(x) is.logical(x) & !suppressWarnings(any(x, na.rm = FALSE)))) |> names() + all_na <- x |> + dplyr::select(!dplyr::where(\(x) !is.logical(x) & all(is.na(x)))) |> + dplyr::slice(dplyr::all_of(indices)) |> + dplyr::select(dplyr::where(\(x) all(is.na(x)))) |> + names() + all_true_or_single_value <- x |> + dplyr::slice(dplyr::all_of(indices)) |> + dplyr::select(dplyr::where(\(x)dplyr::n_distinct(x) == 1)) |> + dplyr::select(!dplyr::where(\(x) all(is.na(x)))) |> + dplyr::select(!dplyr::where(\(x) is.logical(x) & !suppressWarnings(any(x, na.rm = FALSE)))) |> + names() if (length(all_na) > 0) { msg <- c("The following places have all NAs {.var {all_na}}") } else { @@ -543,9 +562,11 @@ display_outline_element <- function(.data, dir_common) { if (length(all_true_or_single_value) > 0) { msg <- c(msg, "Likely problems in creating or displaying {.var {all_true_or_single_value}}.") } - cli::cli_abort(c("Internal error, outline elements can't be NA. Please review.", msg, - "Criteria are created in {.fn define_outline_criteria} and {.fn define_outline_criteria_roxy}. - `outline_el` is defined in {.fn display_outline_element}. Investigate `zz` for debugging.")) + cli::cli_abort(c( + "Internal error, outline elements can't be NA. Please review.", msg, + "Criteria are created in {.fn define_outline_criteria} and {.fn define_outline_criteria_roxy}. + `outline_el` is defined in {.fn display_outline_element}. Investigate `zz` for debugging." + )) } y <- dplyr::mutate( @@ -593,7 +614,7 @@ display_outline_element <- function(.data, dir_common) { x } if (!all(is.na(y$title_el))) { - #browser() + # browser() y <- dplyr::mutate( y, title_el = na_if0(title_el[!is.na(title_el)], "title"), diff --git a/man/outline.Rd b/man/outline.Rd index b893596..ba4ef5e 100644 --- a/man/outline.Rd +++ b/man/outline.Rd @@ -21,6 +21,7 @@ proj_outline( pattern = NULL, proj = proj_get2(), work_only = TRUE, + exclude_tests = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE @@ -30,6 +31,7 @@ dir_outline( pattern = NULL, path = ".", work_only = TRUE, + exclude_tests = TRUE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, @@ -39,7 +41,7 @@ dir_outline( \arguments{ \item{pattern}{A string or regex to search for in the outline. If specified, will search only for elements matching this regular expression. -The print method will show the document title for context. Previously \code{regex_outline}} +The print method will show the document title for context. Previously \code{regex_outline}.} \item{path, proj}{A character vector of file paths, a \link[=proj_list]{project}. Defaults to active file, project or directory. \code{rstudioapi::documentPath()}} @@ -57,6 +59,8 @@ print a less verbose output with sections.} \item{recent_only}{Show outline for recent files} +\item{exclude_tests}{Should tests be displayed? (Not sure if this argument will stay, Will have to think)} + \item{dir_tree}{If \code{TRUE}, will print the \code{\link[fs:dir_tree]{fs::dir_tree()}} or non-R files in the directory} diff --git a/tests/testthat/_outline/roxy-general.R b/tests/testthat/_outline/roxy-general.R index 5c1f64b..8b6a1c3 100644 --- a/tests/testthat/_outline/roxy-general.R +++ b/tests/testthat/_outline/roxy-general.R @@ -26,8 +26,8 @@ #' #' ggplot2::ggplot(mtcars) + #' labs( -#' title = "A title not to be included" -#' ) +#' title = "A title not to be included" +#' ) #' @export #' @family a family to include f_to_be_index_in_outline <- function() { @@ -72,8 +72,8 @@ f_not_to_index <- function() { #' #' ggplot2::ggplot(mtcars) + #' labs( -#' title = "A title not to be included" -#' ) +#' title = "A title not to be included" +#' ) #' @name topic-name-to-include #' @family a family to include NULL diff --git a/tests/testthat/_outline/roxy-general2.R b/tests/testthat/_outline/roxy-general2.R index 5352047..74349da 100644 --- a/tests/testthat/_outline/roxy-general2.R +++ b/tests/testthat/_outline/roxy-general2.R @@ -10,8 +10,8 @@ #' #' ggplot2::ggplot(mtcars) + #' labs( -#' title = "A title not to be included" -#' ) +#' title = "A title not to be included" +#' ) #' @export #' @family a family to include #' if { diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index 7db4222..8337ba7 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -25,7 +25,7 @@ test_that("o_is_work_item() works", { }) test_that("o_is_test_that() works", { - expect_true(o_is_test_that('test_that("Serious things are happening"')) + expect_true(o_is_test_that('test_that("Serious things are happening", {')) }) test_that("o_is_generic_test() works", { @@ -42,7 +42,7 @@ test_that("o_is_section_title() works", { expect_true(o_is_section_title("# Analysis of this")) expect_true(o_is_section_title(" # section 1 ----")) expect_false(o_is_section_title("# TidyTuesday")) - expect_false(o_is_section_title("Function ID:",roxy_section = TRUE)) + expect_false(o_is_section_title("Function ID:", roxy_section = TRUE)) }) # TODO figure out if this is still needed? diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 389cf69..71ea88b 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -3,21 +3,21 @@ test_that("roxy tags are parsed properly + object names are correct", { skip_if_not_installed("tidyr") file_to_map <- testthat::test_path("_outline", "roxy-general.R") names(file_to_map) <- file_to_map - example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) + example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) example_parsed |> extract_roxygen_tag_location("details") |> expect_no_error() - expect_no_error(res <- join_roxy_fun(example_parsed)) + expect_no_error(res <- join_roxy_fun(example_parsed)) - expect_s3_class(res, "tbl_df") - # verify if topic name is well done. - res_order <- dplyr::arrange(res, line) - expect_setequal( - res$topic, - c("f_to_be_index_in_outline", "topic-name-to-include", "no-topic", "dataset") - ) + expect_s3_class(res, "tbl_df") + # verify if topic name is well done. + res_order <- dplyr::arrange(res, line) + expect_setequal( + res$topic, + c("f_to_be_index_in_outline", "topic-name-to-include", "no-topic", "dataset") + ) }) test_that("roxy tags don't error", { @@ -25,7 +25,6 @@ test_that("roxy tags don't error", { names(file_to_map) <- file_to_map example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) expect_no_error(join_roxy_fun(example_parsed)) - }) test_that("multiple roxy tags don't error.", { From b5f1896248c0a13cdc245f9031ee5faca6c0003d Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 13:28:27 -0400 Subject: [PATCH 38/71] Fix mistake --- R/outline.R | 2 +- man/outline.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/outline.R b/R/outline.R index d08832e..fa25a1f 100644 --- a/R/outline.R +++ b/R/outline.R @@ -293,7 +293,7 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, e } #' @rdname outline #' @export -dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_tests = TRUE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, recurse = FALSE) { +dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_tests = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, recurse = FALSE) { dir <- fs::path_real(path) file_exts <- c("R", "qmd", "Rmd", "md", "Rmarkdown") file_exts_regex <- paste0("*.", file_exts, "$", collapse = "|") diff --git a/man/outline.Rd b/man/outline.Rd index ba4ef5e..f1766c9 100644 --- a/man/outline.Rd +++ b/man/outline.Rd @@ -31,7 +31,7 @@ dir_outline( pattern = NULL, path = ".", work_only = TRUE, - exclude_tests = TRUE, + exclude_tests = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, From decdd06b082ccdefea65d856438748e4c4908dcb Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 19:34:52 -0400 Subject: [PATCH 39/71] Use lightparser for caption parsing! --- DESCRIPTION | 1 + R/open.R | 6 +-- R/outline-criteria.R | 16 ++------ R/outline.R | 42 ++++++++++++++++--- README.Rmd | 1 + README.md | 18 ++++++--- man/outline.Rd | 5 +-- reuseme.Rproj | 56 +++++++++++++------------- tests/testthat/_outline/my-analysis.md | 10 +++-- tests/testthat/_outline/quarto-caps.md | 28 +++++++++++++ tests/testthat/_snaps/outline.md | 38 +++++++++++------ tests/testthat/test-outline.R | 8 ++++ 12 files changed, 155 insertions(+), 74 deletions(-) create mode 100644 tests/testthat/_outline/quarto-caps.md diff --git a/DESCRIPTION b/DESCRIPTION index d34af0d..6203f10 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -35,6 +35,7 @@ Suggests: curl, gert, gt, + lightparser, magick, pillar, roxygen2, diff --git a/R/open.R b/R/open.R index 62bdbd8..1ec1e7d 100644 --- a/R/open.R +++ b/R/open.R @@ -79,8 +79,8 @@ active_rs_doc_copy <- function(new = NULL, ..., old = NULL) { cli::cli_abort("Unsaved document, focus on the saved doc you want to save.") } - if (!fs::path_ext(old) %in% c("R", "qmd", "Rmd")) { - cli::cli_abort("Only R docs for now") + if (!fs::path_ext(old) %in% c("md","R", "qmd", "Rmd")) { + cli::cli_abort("Only R and md docs for now") } old_path_file <- fs::path_ext_remove(fs::path_file(old)) if (stringr::str_detect(old, "r-profile|Rprofile")) { @@ -89,7 +89,7 @@ active_rs_doc_copy <- function(new = NULL, ..., old = NULL) { if (is.null(new)) { new_name <- paste0(old_path_file, "-new") } else { - new_name <- stringr::str_remove(new, "\\.R|\\.qmd|\\.Rmd$") + new_name <- stringr::str_remove(new, "\\.R|\\.[Rq]?md$") } # Hack to ensure file/file.R will be correctly renamed. new_path <- stringr::str_replace(old, paste0(old_path_file, "\\."), paste0(new_name, ".")) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 2a883bc..4509175 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -77,7 +77,7 @@ o_is_generic_test <- function(x) { # Returns table or plot titles. o_is_tab_plot_title <- function(x) { - stringr::str_detect(x, "(?\\|]$"), - is_chunk_cap = dplyr::case_when( - is_chunk_cap & is_chunk_cap_next ~ FALSE, - dplyr::lag(is_chunk_cap_next, default = FALSE) ~ TRUE, - .default = is_chunk_cap - ), - is_chunk_cap_next = is_chunk_cap, + is_chunk_cap = stringr::str_detect(content, "\\#\\|.*(cap|title):|```\\{r.*cap\\s?\\="), is_test_name = is_test_file & o_is_test_that(content) & !o_is_generic_test(content), is_section_title = o_is_section_title(content), pkg_version = extract_pkg_version(content, is_news, is_section_title), @@ -194,7 +185,7 @@ define_outline_criteria <- function(.data, print_todo, dir_common) { topic = NA_character_, is_a_comment_or_code = stringr::str_detect(content, "!=|\\|\\>|\\(\\.*\\)"), is_todo_fixme = print_todo & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext) & !is_snap_file, - n_leading_hash = nchar(stringr::str_extract(content, "\\#+")), + n_leading_hash = nchar(stringr::str_extract(content, "\\#+(?!\\|)")), # don't count hashpipe n_leading_hash = dplyr::coalesce(n_leading_hash, 0), is_second_level_heading_or_more = (is_section_title_source | is_section_title) & n_leading_hash > 1, is_cross_ref = stringr::str_detect(content, "docs_links?\\(") & !stringr::str_detect(content, "@param|\\{\\."), @@ -233,7 +224,6 @@ define_outline_criteria_roxy <- function(x) { (x$tag %in% c("details", "description") & stringr::str_detect(x$content, "#\\s")) x$is_section_title_source <- x$is_section_title x$is_chunk_cap <- FALSE - x$is_chunk_cap_next <- FALSE x$is_test_name <- FALSE x$pkg_version <- NA_character_ # a family or concept can be seen as a plot subtitle? diff --git a/R/outline.R b/R/outline.R index fa25a1f..2f2d6f3 100644 --- a/R/outline.R +++ b/R/outline.R @@ -13,7 +13,7 @@ #' * `TODO` items #' * Parse cli hyperlinks #' * Plot or table titles -#' * FIgures caption in Quarto documents (limited support for multiline caption currently) +#' * FIgures caption in Quarto documents #' * test names #' * Indicator of recent modification #' * Colored output for @@ -31,8 +31,7 @@ #' @details #' `proj_outline()` and `dir_outline()` are wrapper of `file_outline()`. #' -#' The parser is very opinioneted and is not very robust as it is based on regexps. -#' For a better file parser, explore other options, like [lightparser](https://thinkr-open.github.io/lightparser/) for Quarto, `{roxygen2}` +#' The parser is very opinionated and based on lightparser, roxygen2 and regexps. #' #' Will show TODO items and will offer a link to [mark them as #' complete][complete_todo()]. @@ -511,6 +510,9 @@ display_outline_element <- function(.data, dir_common) { } x$outline_el <- purrr::map_chr(x$outline_el, \(x) link_gh_issue(x, org_repo)) # to add link to GitHub. x$outline_el <- purrr::map_chr(x$outline_el, markup_href) + if (any(x$is_chunk_cap)) { + x$outline_el[x$is_chunk_cap] <- get_chunk_cap(x$file[x$is_chunk_cap]) + } x <- dplyr::mutate( x, outline_el = dplyr::case_when( @@ -520,8 +522,6 @@ display_outline_element <- function(.data, dir_common) { # family or concept! is_tab_or_plot_title & !is.na(tag) ~ outline_el, is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title =[^\"']*[\"']([^\"]{5,})[\"']", group = 1), - is_chunk_cap_next & !is_chunk_cap ~ stringr::str_remove_all(outline_el, "\\s?\\#\\|\\s+"), - is_chunk_cap ~ stringr::str_remove_all(stringr::str_extract(outline_el, "(cap|title)\\:\\s*(.+)", group = 2), "\"|'"), is_cross_ref ~ stringr::str_remove_all(outline_el, "^(i.stat\\:\\:)?.cdocs_lin.s\\(|[\"']\\)$|\""), is_doc_title ~ stringr::str_remove_all(outline_el, "subtitle\\:\\s?|title\\:\\s?|\"|\\#\\|\\s?"), is_section_title & !is_md ~ stringr::str_remove(outline_el, "^\\s{0,4}\\#+\\s+|^\\#'\\s\\#+\\s+"), # Keep inline markup @@ -564,6 +564,7 @@ display_outline_element <- function(.data, dir_common) { } cli::cli_abort(c( "Internal error, outline elements can't be NA. Please review.", msg, + paste0("{.file ",zz$file, ":",zz$line, "}"), "Criteria are created in {.fn define_outline_criteria} and {.fn define_outline_criteria_roxy}. `outline_el` is defined in {.fn display_outline_element}. Investigate `zz` for debugging." )) @@ -625,6 +626,26 @@ display_outline_element <- function(.data, dir_common) { y } +get_chunk_cap <- function(file) { + rlang::check_installed("lightparser", "to parse qmd files add chunk caption to outline.") + # we want fig-cap, tbl-cap and title + caps <- NULL + unique_file <- unique(file) + # ThinkR-open/lightparser#8 + for (i in seq_along(unique_file)) { + # FIXME find a way to be as consistent as lightparser, but faster. + dat <- (lightparser::split_to_tbl(unique_file[i]) |> dplyr::filter(type == "block"))$params + # tidyverse/purrr#1081 + caps <- c(caps, dat |> purrr::map_chr( \(x) x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% "USELESS THING")) + } + caps <- caps[caps != "USELESS THING"] + if (length(caps) != length(file)) { + cli::cli_abort("error! :(, caps = {length(caps)}, file = {length(file)} in file {.file {unique_file}}") + } + # used to make sure purrr doesn't complain + caps |> stringr::str_replace_all("\n", " ") +} + define_important_element <- function(.data) { dplyr::mutate( .data, @@ -730,7 +751,16 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { rs_version = NULL, outline_el2 = NULL, condition_to_truncate = NULL, - condition_to_truncate2 = NULL + condition_to_truncate2 = NULL, + style_fun = NULL, + is_saved_doc = NULL, + # I may put it back... + importance = NULL, + # may be useful for debugging. + before_and_after_empty = NULL, + # may be useful for debugging + has_inline_markup = NULL, + n_leading_hash = NULL ) |> dplyr::filter(is.na(outline_el) | grepl(pattern, outline_el, ignore.case = TRUE)) } diff --git a/README.Rmd b/README.Rmd index bd1bcd0..87b812b 100644 --- a/README.Rmd +++ b/README.Rmd @@ -128,6 +128,7 @@ Due to the growing number of criteria, regex, `file_outline()` is slowing down a ```{r} #| warning: false +#| message: false bench::mark( outline <- proj_outline() ) diff --git a/README.md b/README.md index c7d45cd..d6e1bd9 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 1.45s 1.45s 0.688 71.1MB 4.82 +#> 1 outline <- proj_outline() 8.76s 8.76s 0.114 76.1MB 1.03 ```
@@ -177,8 +177,6 @@ Example outline ``` r outline #> -#> ── `LICENSE.md` MIT License -#> #> ── `R/browse-pkg.R` Browse pkgdown site if it exists #> #> ── `R/case-if-any.R` case-when, but checks for all matches, returns a character @@ -240,7 +238,6 @@ outline #> `i` TODO Would have to look for notebooks that don't contain notebook in th…- `Done✔?` #> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` #> `i` TODO long enough to be meanignful?- `Done✔?` -#> `i` FIXME try to detect all the chunk caption, but would have to figure out the end of it maybe lightparser.- `Done✔?` #> `i` TODO merge with define_outline_criteria- `Done✔?` #> `i` it is 'R/outline.R' or 'R/outline-roxy.R' #> @@ -343,17 +340,24 @@ outline #> `i` Write my streets #> `i` TODO Create a new version- `Done✔?` #> `i` 'R/my-file.R' -#> `i` **A table title** #> `i` Section title #> #> ── `tests/testthat/_outline/my-analysis.md` My doc title #> `i` A section #> `i` Dashboard card -#> `i` A code section #> `i` A subsection #> `i` A section2 #> `i` A long ggplot2 title #> `i` A code section +#> `i` A long ggplot2 title with more details +#> +#> ── `tests/testthat/_outline/quarto-caps.md` title +#> `i` A long ggplot2 title with more details +#> `i` Heading +#> `i` A long ggplot2 title with more details +#> `i` Heading2 +#> `i` Dashboard link +#> `i` Dashboard link #> #> ── `tests/testthat/_outline/roxy-general.R` #> `i` Use 'tests/testthat/_outline/roxy-general2.R' for output testing @@ -444,6 +448,7 @@ outline #> `i` open_rs_doc() errors in non-interactive sessions #> #> ── `tests/testthat/test-outline-criteria.R` Test individual outline elements +#> `i` TODO figure out if this is still needed?- `Done✔?` #> `i` No outline criteria are untested #> #> ── `tests/testthat/test-outline-roxy.R` @@ -458,6 +463,7 @@ outline #> `i` file_outline() with only title doesn't error #> `i` file_outline() contains function calls #> `i` dir_outline() works with no error +#> `i` file_outline() works well with figure captions #> #> ── `tests/testthat/test-rename.R` #> `i` Helper files returns the expected input diff --git a/man/outline.Rd b/man/outline.Rd index f1766c9..0a9a947 100644 --- a/man/outline.Rd +++ b/man/outline.Rd @@ -86,7 +86,7 @@ Outline elements include \item \code{TODO} items \item Parse cli hyperlinks \item Plot or table titles -\item FIgures caption in Quarto documents (limited support for multiline caption currently) +\item FIgures caption in Quarto documents \item test names \item Indicator of recent modification \item Colored output for @@ -106,8 +106,7 @@ By default \details{ \code{proj_outline()} and \code{dir_outline()} are wrapper of \code{file_outline()}. -The parser is very opinioneted and is not very robust as it is based on regexps. -For a better file parser, explore other options, like \href{https://thinkr-open.github.io/lightparser/}{lightparser} for Quarto, \code{{roxygen2}} +The parser is very opinionated and based on lightparser, roxygen2 and regexps. Will show TODO items and will offer a link to \link[=complete_todo]{mark them as complete}. } diff --git a/reuseme.Rproj b/reuseme.Rproj index d230585..59699e9 100644 --- a/reuseme.Rproj +++ b/reuseme.Rproj @@ -1,28 +1,28 @@ -Version: 1.0 - -RestoreWorkspace: No -SaveWorkspace: No -AlwaysSaveHistory: Default - -EnableCodeIndexing: Yes -UseSpacesForTab: Yes -NumSpacesForTab: 2 -Encoding: UTF-8 - -RnwWeave: Sweave -LaTeX: pdfLaTeX - -AutoAppendNewline: Yes -StripTrailingWhitespace: Yes -LineEndingConversion: Posix - -BuildType: Package -PackageUseDevtools: Yes -PackageInstallArgs: --no-multiarch --with-keep.source -PackageRoxygenize: rd,collate,namespace - -UseNativePipeOperator: Yes - -MarkdownWrap: Sentence - -SpellingDictionary: en_CA +Version: 1.0 + +RestoreWorkspace: No +SaveWorkspace: No +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes +LineEndingConversion: Posix + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd,collate,namespace + +UseNativePipeOperator: Yes + +MarkdownWrap: Sentence + +SpellingDictionary: en_CA diff --git a/tests/testthat/_outline/my-analysis.md b/tests/testthat/_outline/my-analysis.md index 15a2933..2017f69 100644 --- a/tests/testthat/_outline/my-analysis.md +++ b/tests/testthat/_outline/my-analysis.md @@ -6,9 +6,6 @@ title: My doc title ```{r} #| title: Dashboard card - -# A code section ---- - ``` ## A subsection @@ -25,3 +22,10 @@ Some text with TODO. # A code section ---- ``` + +```{r} +#| fig-cap: > +#| A long ggplot2 title +#| with more details +``` + diff --git a/tests/testthat/_outline/quarto-caps.md b/tests/testthat/_outline/quarto-caps.md new file mode 100644 index 0000000..cddf912 --- /dev/null +++ b/tests/testthat/_outline/quarto-caps.md @@ -0,0 +1,28 @@ +--- +title: title +--- + +```{r} +#| fig-cap: > +#| A long ggplot2 title +#| with more details +``` + +## Heading + +```{r} +#| label: another-lab +#| tbl-cap: | +#| A long ggplot2 title +#| with more details +``` + +## Heading2 + +```{r} +#| title: Dashboard link +``` + +```{r, fig.cap ="Dashboard link"} +caption +``` diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index e015503..2ae054f 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -4,25 +4,14 @@ file_outline(path = my_test_files, alpha = TRUE) Message - -- `my-analysis.R` Analyse my streets - Output - `i` **A table title** - `i` TODO Create a new version- `Donev?` - `i` Read my streets () data - `i` Section title - `i` Write my streets - `i` data wrangling - `i` 'R/my-file.R' - Message - -- `my-analysis.md` My doc title Output `i` A section - `i` A code section `i` A subsection `i` A section2 `i` A long ggplot2 title `i` A code section + `i` A long ggplot2 title with more details `i` Dashboard card Message @@ -34,6 +23,16 @@ `i` TODO this is an item- `Donev?` Message + -- `my-analysis.R` Analyse my streets + Output + `i` TODO Create a new version- `Donev?` + `i` Read my streets () data + `i` Section title + `i` Write my streets + `i` data wrangling + `i` 'R/my-file.R' + Message + -- `title.md` The title is the only outline element # alpha and work_only arguments work @@ -86,3 +85,18 @@ -- `outline-script.R` Example for `file_outline()` +# file_outline() works well with figure captions + + Code + file_outline(path = test_path("_outline", "quarto-caps.md")) + Message + + -- `quarto-caps.md` title + Output + `i` A long ggplot2 title with more details + `i` Heading + `i` A long ggplot2 title with more details + `i` Heading2 + `i` Dashboard link + `i` Dashboard link + diff --git a/tests/testthat/test-outline.R b/tests/testthat/test-outline.R index c5f1a19..40698a4 100644 --- a/tests/testthat/test-outline.R +++ b/tests/testthat/test-outline.R @@ -74,3 +74,11 @@ test_that("file_outline() contains function calls", { test_that("dir_outline() works with no error", { expect_no_error(dir_outline(pattern = ".+", path = test_path("_outline"))) }) + +test_that("file_outline() works well with figure captions", { + skip_if_not_installed("lightparser") + expect_snapshot( + file_outline(path = test_path("_outline", "quarto-caps.md")), + transform = ~ sub(" `[^`]+` ", " `quarto-caps.md` ", .x) + ) +}) From d5b6ad305746322c84ae3d26c336aae60bea29de Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 20:46:24 -0400 Subject: [PATCH 40/71] Some fixups for revdeps and plot titles to remove some false positives + handle --- R/outline-criteria.R | 19 +++++++++-------- R/outline.R | 28 ++++++++++++++++++++++---- README.md | 3 ++- tests/testthat/test-outline-criteria.R | 4 ++++ 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 4509175..a96871d 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -77,11 +77,12 @@ o_is_generic_test <- function(x) { # Returns table or plot titles. o_is_tab_plot_title <- function(x) { - stringr::str_detect(x, "(? 1, is_cross_ref = stringr::str_detect(content, "docs_links?\\(") & !stringr::str_detect(content, "@param|\\{\\."), - is_function_def = grepl("<- function(", content, fixed = TRUE) & !stringr::str_starts(content, "\\s*#") ) x <- dplyr::mutate( x, diff --git a/R/outline.R b/R/outline.R index 2f2d6f3..3a9ebc6 100644 --- a/R/outline.R +++ b/R/outline.R @@ -314,7 +314,7 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te # examples don't help understand a project. file_list_to_outline <- fs::path_filter( file_list_to_outline, - regexp = "testthat/_outline/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples", + regexp = "testthat/_outline/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples|revdep/", invert = TRUE ) } @@ -525,7 +525,7 @@ display_outline_element <- function(.data, dir_common) { is_cross_ref ~ stringr::str_remove_all(outline_el, "^(i.stat\\:\\:)?.cdocs_lin.s\\(|[\"']\\)$|\""), is_doc_title ~ stringr::str_remove_all(outline_el, "subtitle\\:\\s?|title\\:\\s?|\"|\\#\\|\\s?"), is_section_title & !is_md ~ stringr::str_remove(outline_el, "^\\s{0,4}\\#+\\s+|^\\#'\\s\\#+\\s+"), # Keep inline markup - is_section_title & is_md ~ stringr::str_remove_all(outline_el, "^\\#+\\s+|\\{.+\\}| stringr::str_trim(), .default = stringr::str_remove_all(outline_el, "^\\s*\\#+\\|?\\s?(label:\\s)?|\\s?[-\\=]{4,}") ), @@ -634,9 +634,29 @@ get_chunk_cap <- function(file) { # ThinkR-open/lightparser#8 for (i in seq_along(unique_file)) { # FIXME find a way to be as consistent as lightparser, but faster. - dat <- (lightparser::split_to_tbl(unique_file[i]) |> dplyr::filter(type == "block"))$params + # If ThinkR-open/lightparser#11 gets fixed, no more fiddling? + dat <- tryCatch( (lightparser::split_to_tbl(unique_file[i]) |> dplyr::filter(type == "block"))$params, + error = function(e) { + # workaround https://github.com/ThinkR-open/lightparser/issues/11 + tmp <- withr::local_tempfile( + lines = c( + "---", + "title: dummy", + "---", + readLines(unique_file[i]) + )) + (lightparser::split_to_tbl(tmp) |> dplyr::filter(type == "block"))$params + # list(`1` = list(`fig-cap` = "Wrong caption")) + # cli::cli_abort( + # c("{.file {unique_file[i]}} is incorrect."), + # parent = e + # ) + }) + # tidyverse/purrr#1081 + if (length(dat) > 0) { caps <- c(caps, dat |> purrr::map_chr( \(x) x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% "USELESS THING")) + } } caps <- caps[caps != "USELESS THING"] if (length(caps) != length(file)) { @@ -669,7 +689,7 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { .data <- dplyr::mutate( .data, condition_to_truncate = !is.na(outline_el) & !has_title_el & (is_todo_fixme) & is_saved_doc & !has_inline_markup, - condition_to_truncate2 = !is.na(outline_el) & !has_title_el & !is_todo_fixme & (is_second_level_heading_or_more | is_subtitle) & is_saved_doc & !has_inline_markup + condition_to_truncate2 = !is.na(outline_el) & !has_title_el & !is_todo_fixme & (is_second_level_heading_or_more | is_subtitle | is_chunk_cap) & is_saved_doc & !has_inline_markup ) # r-lib/cli#627, add a dot before and at the end (Only in RStudio before 2023.12) .data$outline_el2 <- NA_character_ diff --git a/README.md b/README.md index d6e1bd9..04afc34 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 8.76s 8.76s 0.114 76.1MB 1.03 +#> 1 outline <- proj_outline() 5.2s 5.2s 0.192 76.3MB 1.73 ```
@@ -256,6 +256,7 @@ outline #> `i` Print method #> `i` Step: tweak outline look as they show #> `i` TODO reanable cli info- `Done✔?` +#> `i` FIXME find a way to be as consistent as lightparser, but faster.- `Done✔?` #> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` #> #> ── `R/proj-list.R` Opens a RStudio project in a new session diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index 8337ba7..7ce4003 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -36,6 +36,10 @@ test_that("o_is_tab_plot_title() works", { expect_true(o_is_tab_plot_title("title = 'A great'")) expect_false(o_is_tab_plot_title("tab_header()")) expect_false(o_is_tab_plot_title("```{r tab_header}")) + expect_false(o_is_tab_plot_title("fwd_title = 'Family'")) + expect_false(o_is_tab_plot_title("guide_legend(title = 'Family'")) + expect_false(o_is_tab_plot_title("title = ''")) + expect_false(o_is_tab_plot_title('title = ".+", " +\\(",")')) }) test_that("o_is_section_title() works", { From 6af2548a0a08c10d110b1c4b2da05be457a15121 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 20:46:53 -0400 Subject: [PATCH 41/71] Don't error if you couldn't find gh URL. --- R/markup.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/R/markup.R b/R/markup.R index 481f4ea..86a0a30 100644 --- a/R/markup.R +++ b/R/markup.R @@ -71,12 +71,17 @@ find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { if (is.null(pkg_path)) { return(NULL) } - gh_url <- withCallingHandlers( + gh_url <- tryCatch( usethis::browse_github(basename(pkg_path)), error = function(e) { - cli::cli_abort("didn't find a way to do what is required.") + # TODO possibly look into checking desc::desc_get("BugReports", "~/path/to/DESCRIPTION") + # cli::cli_abort("didn't find a way to do what is required.", parent = e) + NULL } ) + if (is.null(gh_url)) { + return(NULL) + } org_repo_found <- stringr::str_remove(gh_url, ".+github.com/|.+gitlab.com/") return(org_repo_found) } From 27cd5cbd2877d75414cf0a2f86f62c991f2bd063 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 20:51:40 -0400 Subject: [PATCH 42/71] Clean workaround --- R/outline.R | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/R/outline.R b/R/outline.R index 3a9ebc6..882ac36 100644 --- a/R/outline.R +++ b/R/outline.R @@ -564,7 +564,7 @@ display_outline_element <- function(.data, dir_common) { } cli::cli_abort(c( "Internal error, outline elements can't be NA. Please review.", msg, - paste0("{.file ",zz$file, ":",zz$line, "}"), + paste0("{.file ", zz$file, ":", zz$line, "}"), "Criteria are created in {.fn define_outline_criteria} and {.fn define_outline_criteria_roxy}. `outline_el` is defined in {.fn display_outline_element}. Investigate `zz` for debugging." )) @@ -634,28 +634,26 @@ get_chunk_cap <- function(file) { # ThinkR-open/lightparser#8 for (i in seq_along(unique_file)) { # FIXME find a way to be as consistent as lightparser, but faster. - # If ThinkR-open/lightparser#11 gets fixed, no more fiddling? - dat <- tryCatch( (lightparser::split_to_tbl(unique_file[i]) |> dplyr::filter(type == "block"))$params, - error = function(e) { - # workaround https://github.com/ThinkR-open/lightparser/issues/11 - tmp <- withr::local_tempfile( - lines = c( - "---", - "title: dummy", - "---", - readLines(unique_file[i]) - )) - (lightparser::split_to_tbl(tmp) |> dplyr::filter(type == "block"))$params - # list(`1` = list(`fig-cap` = "Wrong caption")) - # cli::cli_abort( - # c("{.file {unique_file[i]}} is incorrect."), - # parent = e - # ) - }) + dat <- tryCatch( + lightparser::split_to_tbl(unique_file[i]), + error = function(e) { + # workaround ThinkR-open/lightparser#11 + tmp <- withr::local_tempfile( + lines = c( + "---", + "title: dummy", + "---", + readLines(unique_file[i], warn = FALSE) + ) + ) + lightparser::split_to_tbl(tmp) + } + ) + dat <- dplyr::filter(dat, type == "block")$params # tidyverse/purrr#1081 if (length(dat) > 0) { - caps <- c(caps, dat |> purrr::map_chr( \(x) x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% "USELESS THING")) + caps <- c(caps, dat |> purrr::map_chr(\(x) x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% "USELESS THING")) } } caps <- caps[caps != "USELESS THING"] From a4972186dcb326aae9f09e1fcb45571bfd58fe08 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 21:36:26 -0400 Subject: [PATCH 43/71] Add to NEWS + minor adjustments to make outline and usethis to keep working. Exclude inst/templates/license- from results --- NEWS.md | 10 ++++++++++ R/escape-inline-markup.R | 3 +++ R/outline-criteria.R | 6 +++--- R/outline-roxy.R | 3 ++- R/outline.R | 5 +++-- tests/testthat/_outline/roxy-general.R | 2 +- tests/testthat/test-outline-criteria.R | 3 +++ tests/testthat/test-outline-roxy.R | 2 ++ 8 files changed, 27 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 830a931..1609434 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,16 @@ * `proj_list()` / `proj_switch()` no longer opens a nested project if looking for `"pkgdown"`, `"testthat"`, etc. +* `proj_outline()` was improved to work with roxygen2 and lightparser to parse file contents more consistenly. This means a slowdown, but the increased accuracy is worth it! Parsing a single file should still be pretty fast! + +* `proj_outline()` gains `exclude_tests` to exclude tests from outline + +* `doc_title`, `is_plot_or_tab_title` is better detected now. + +* `proj_outline()` now detects legacy `fig.cap` in the chunk header. See `knitr::convert_chunk_headers()` for the newer approach. + +* `proj_outline()` now autolinks GitHub issuea inside the repo. + # reuseme 0.0.2 * `complete_todo()` no longer deletes the full line. It only deletes what it says it deletes (#27). diff --git a/R/escape-inline-markup.R b/R/escape-inline-markup.R index db075a2..214dd3e 100644 --- a/R/escape-inline-markup.R +++ b/R/escape-inline-markup.R @@ -66,6 +66,9 @@ escape_markup <- function(x) { x[is_left_bracket & !is_bracket] <- stringr::str_replace_all(x[is_left_bracket & !is_bracket], "\\{", "{{") x[is_right_bracket & !is_bracket] <- stringr::str_replace_all(x[is_right_bracket & !is_bracket], "\\}", "}}") + # whisker replacement {{{ vignette_title }}} in usethis by vignette_title + x <- stringr::str_replace_all(x, "\\{\\{?\\{? ([^\\}]+) \\}?\\}?\\}", "\\1") + if (any(stringr::str_detect(x, "\\{{3,10}"))) { # more than 3 { cli::cli_abort("internal errror. Did not transform string correctly.") diff --git a/R/outline-criteria.R b/R/outline-criteria.R index a96871d..f45249b 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -68,7 +68,7 @@ o_is_test_that <- function(x) { if (!any(potential_test)) { return(potential_test) } - potential_test & stringr::str_detect(x, "(? diff --git a/R/outline.R b/R/outline.R index 882ac36..e9ed80b 100644 --- a/R/outline.R +++ b/R/outline.R @@ -314,7 +314,8 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te # examples don't help understand a project. file_list_to_outline <- fs::path_filter( file_list_to_outline, - regexp = "testthat/_outline/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples|revdep/", + # TODO for usethis, add inst/templates/ but I may leave them for now.... + regexp = "testthat/_outline/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples|revdep/|inst/templates/license-", invert = TRUE ) } @@ -429,7 +430,7 @@ print.outline_report <- function(x, ...) { thing <- summary_links_files$first_line_el[[i]] print(thing) print(escape_markup(thing)) - cli::cli_abort("failed to parse in first line of file {.file {summary_links_files$file[[i]]}}.", parent = e) + cli::cli_abort("Failed to parse in first line of file {.file {summary_links_files$file[[i]]}}.", parent = e) } ) base_name <- c(base_name, " ", title_el) diff --git a/tests/testthat/_outline/roxy-general.R b/tests/testthat/_outline/roxy-general.R index 8b6a1c3..a9c5e98 100644 --- a/tests/testthat/_outline/roxy-general.R +++ b/tests/testthat/_outline/roxy-general.R @@ -17,7 +17,7 @@ #' #' Content not to be included2 #' -#' @section First to be included: +#' @section `First code` to be included: #' #' Content not to be included #' diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index 7ce4003..3ead0b1 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -26,6 +26,7 @@ test_that("o_is_work_item() works", { test_that("o_is_test_that() works", { expect_true(o_is_test_that('test_that("Serious things are happening", {')) + expect_false(o_is_test_that('test_that("", {')) }) test_that("o_is_generic_test() works", { @@ -39,6 +40,8 @@ test_that("o_is_tab_plot_title() works", { expect_false(o_is_tab_plot_title("fwd_title = 'Family'")) expect_false(o_is_tab_plot_title("guide_legend(title = 'Family'")) expect_false(o_is_tab_plot_title("title = ''")) + expect_false(o_is_tab_plot_title("title = '', symbol = 'x'")) + expect_false(o_is_tab_plot_title('title = ".+", " +\\(",")')) }) diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 71ea88b..560b942 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -18,6 +18,8 @@ test_that("roxy tags are parsed properly + object names are correct", { res$topic, c("f_to_be_index_in_outline", "topic-name-to-include", "no-topic", "dataset") ) + # strip code from roxygen2 tag + expect_contains(res$content, "`First code` to be included:") }) test_that("roxy tags don't error", { From fc50e76620e5c497a9c4aeee325158b01dded93f Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 22:06:28 -0400 Subject: [PATCH 44/71] More robust `escape_markup()` (add `\\.` as an acceptable start of var name! --- R/escape-inline-markup.R | 10 ++++------ R/outline-criteria.R | 3 ++- R/outline.R | 3 ++- tests/testthat/test-escape-inline-markup.R | 3 ++- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/R/escape-inline-markup.R b/R/escape-inline-markup.R index 214dd3e..6157d1a 100644 --- a/R/escape-inline-markup.R +++ b/R/escape-inline-markup.R @@ -33,7 +33,7 @@ escape_markup <- function(x) { return(x) } # replace fn arg {fn}(arg) -> fn({arg}) - valid_r_variable_regex <- "[:alpha:][[:alpha:]\\_\\d]+" + valid_r_variable_regex <- "\\.?[:alpha:][[:alpha:]\\_\\d]+" x <- stringr::str_replace_all( x, paste0("\\{(", valid_r_variable_regex, ")\\}\\("), @@ -59,8 +59,6 @@ escape_markup <- function(x) { # ) # replace variables - - x <- replace_r_var(x) x[is_left_bracket & !is_bracket] <- stringr::str_replace_all(x[is_left_bracket & !is_bracket], "\\{", "{{") @@ -71,7 +69,7 @@ escape_markup <- function(x) { if (any(stringr::str_detect(x, "\\{{3,10}"))) { # more than 3 { - cli::cli_abort("internal errror. Did not transform string correctly.") + rlang::abort(c("internal error. Did not transform string correctly.", x)) } x } @@ -87,7 +85,7 @@ escape_markup <- function(x) { #' is_markup_incorrect("{.file {gt}}") is_markup_incorrect <- function(x) { # no match of single { or } - valid_r_variable_regex <- "[:alpha:][[:alpha:]\\_\\d]+" + valid_r_variable_regex <- "\\.?[:alpha:][[:alpha:]\\_\\d]+" stringr::str_detect(x, pattern = paste0("(? suppressMessages() + # Suppresss roxygen2 message, suppress callr output, suppress asciicast warnings. + invisible(capture.output(parsed_files <- purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))))) |> suppressMessages() |> suppressWarnings() # if roxygen2 cannot parse a file, let's just forget about it. unparsed_files <- files_with_roxy_comments[which(is.null(parsed_files))] # browser() diff --git a/R/outline.R b/R/outline.R index e9ed80b..e623c99 100644 --- a/R/outline.R +++ b/R/outline.R @@ -315,7 +315,8 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te file_list_to_outline <- fs::path_filter( file_list_to_outline, # TODO for usethis, add inst/templates/ but I may leave them for now.... - regexp = "testthat/_outline/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples|revdep/|inst/templates/license-", + # styler tests examples may not work.. + regexp = "testthat/_outline/|testthat/assets|example-file|vignettes/test/|tests/performance-monitor|tests/gt-examples|revdep/|inst/templates/license-|tests/testthat/scope-", invert = TRUE ) } diff --git a/tests/testthat/test-escape-inline-markup.R b/tests/testthat/test-escape-inline-markup.R index d5f24cb..dbaeeb3 100644 --- a/tests/testthat/test-escape-inline-markup.R +++ b/tests/testthat/test-escape-inline-markup.R @@ -3,7 +3,8 @@ test_that("escape_markup() works", { expect_equal(escape_markup(c("gt", "y {", "{gt}")), c("gt", "y {{", "{{gt}}")) expect_equal(escape_markup("{gt}"), "{{gt}}") expect_equal(escape_markup("{.file {here}}"), "{.file {.url here}}") - + expect_equal(escape_markup("{"), "{{") + expect_equal(escape_markup("{.email}"), "{{.email}}") input <- "multi problems {{gt}} to {gt} to {.file gt} to {.file {gt}}" exp_str <- "multi problems {{gt}} to {{gt}} to {.file gt} to {.file {.url gt}}" expect_equal(escape_markup(input), exp_str) From 49e443bb7943fa77d40aae3787baed906f8638e5 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 23:16:18 -0400 Subject: [PATCH 45/71] Add some workarounds to make cli parsing and escaping work a bit better. remove `is_a_comment_or_code`, no longer needed with roxy comment parsing separation. another blow to performance.. --- R/escape-inline-markup.R | 5 ++- R/outline-criteria.R | 2 - R/outline-roxy.R | 8 +++- R/outline.R | 6 +-- README.md | 47 +++++++++++++++++++++- tests/testthat/_outline/roxy-cli.R | 17 ++++++++ tests/testthat/_outline/roxy-general2.R | 2 +- tests/testthat/_outline/titles.md | 2 + tests/testthat/_snaps/outline.md | 1 + tests/testthat/test-escape-inline-markup.R | 5 +++ tests/testthat/test-outline-roxy.R | 9 +++++ tests/testthat/test-outline.R | 2 +- 12 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 tests/testthat/_outline/roxy-cli.R diff --git a/R/escape-inline-markup.R b/R/escape-inline-markup.R index 6157d1a..193ccb8 100644 --- a/R/escape-inline-markup.R +++ b/R/escape-inline-markup.R @@ -65,7 +65,10 @@ escape_markup <- function(x) { x[is_right_bracket & !is_bracket] <- stringr::str_replace_all(x[is_right_bracket & !is_bracket], "\\}", "}}") # whisker replacement {{{ vignette_title }}} in usethis by vignette_title - x <- stringr::str_replace_all(x, "\\{\\{?\\{? ([^\\}]+) \\}?\\}?\\}", "\\1") + x <- stringr::str_replace_all(x, "\\{\\{?\\{?(\\s?[^\\}]+) \\}?\\}?\\}", "\\1") + + # make `{` and `}` work + x <- stringr::str_replace_all(x, c("`\\{`" = "`{{`", "`\\}`" = "`}}`")) if (any(stringr::str_detect(x, "\\{{3,10}"))) { # more than 3 { diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 62dc66e..64ee762 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -186,7 +186,6 @@ define_outline_criteria <- function(.data, print_todo, dir_common) { is_object_title = FALSE, tag = NA_character_, topic = NA_character_, - is_a_comment_or_code = stringr::str_detect(content, "!=|\\|\\>|\\(\\.*\\)"), is_todo_fixme = print_todo & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext) & !is_snap_file, n_leading_hash = nchar(stringr::str_extract(content, "\\#+(?!\\|)")), # don't count hashpipe n_leading_hash = dplyr::coalesce(n_leading_hash, 0), @@ -236,7 +235,6 @@ define_outline_criteria_roxy <- function(x) { x$is_cross_ref <- FALSE x$is_function_def <- FALSE x$is_todo_fixme <- FALSE - x$is_a_comment_or_code <- FALSE x$is_doc_title <- x$line == 1 & x$tag == "title" x$n_leading_hash <- nchar(stringr::str_extract(x$content, "\\#+")) x$n_leading_hash <- dplyr::case_when( diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 75a8086..c82ccfa 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -248,7 +248,7 @@ join_roxy_fun <- function(file) { )) |> tidyr::separate_longer_delim(content, delim = "\n") - roxy_parsed1 |> + r <- roxy_parsed1 |> dplyr::mutate( n = dplyr::n(), # error if something is length 0. @@ -263,6 +263,12 @@ join_roxy_fun <- function(file) { ) |> dplyr::filter(nzchar(content), !stringr::str_detect(content, "`r\\s")) |> dplyr::select(-id, -n) + # remove link... + r$content <- stringr::str_remove_all(r$content, "\\\\[^\\\\]+\\]\\{") + # FIXME escape markup see next line + # To test it add a fix tag to next line and try to figure it out... + # to fix figure out why #' @section Escaping `{` and `}` : isn't parsing. Workaround to remove it. + r |> dplyr::filter(!grepl("^Escaping", content)) } # helper for interactive checking ----------- diff --git a/R/outline.R b/R/outline.R index e623c99..6c5fdb4 100644 --- a/R/outline.R +++ b/R/outline.R @@ -480,14 +480,14 @@ keep_outline_element <- function(.data) { dat <- dplyr::filter( .data, (is_news & ( - (!simplify_news & is_section_title & !is_a_comment_or_code & before_and_after_empty) | - (simplify_news & is_section_title & !pkg_version %in% versions_to_drop & !is_second_level_heading_or_more & !is_a_comment_or_code & before_and_after_empty) + (!simplify_news & is_section_title & before_and_after_empty) | + (simplify_news & is_section_title & !pkg_version %in% versions_to_drop & !is_second_level_heading_or_more & before_and_after_empty) )) | # still regular comments in .md files # what to keep in .md docs (is_md & (is_chunk_cap | is_doc_title | is_object_title)) | - (is_md & (is_section_title & before_and_after_empty & !is_a_comment_or_code)) | + (is_md & (is_section_title & before_and_after_empty)) | # What to keep in .R files (!is_md & is_section_title_source) | # What to keep anywhere diff --git a/README.md b/README.md index 04afc34..2f3ce8d 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 5.2s 5.2s 0.192 76.3MB 1.73 +#> 1 outline <- proj_outline() 6.94s 6.94s 0.144 78.1MB 1.15 ```
@@ -246,6 +246,7 @@ outline #> `i` TODO Delete when stable debugging- `Done✔?` #> `i` TODO Delete when stable for debugging- `Done✔?` #> `i` TODO exclude S3 methods- `Done✔?` +#> `i` FIXME escape markup see next line- `Done✔?` #> `i` helper for interactive checking #> #> ── `R/outline.R` `proj_outline()` @@ -253,6 +254,7 @@ outline #> `i` If `work_only` is set to `TRUE`, the function will only return outline of the `# WORK` comment #> `i` `file_outline()` #> `i` File outline +#> `i` TODO for usethis, add inst/templates/ but I may leave them for now....- `Done✔?` #> `i` Print method #> `i` Step: tweak outline look as they show #> `i` TODO reanable cli info- `Done✔?` @@ -360,6 +362,8 @@ outline #> `i` Dashboard link #> `i` Dashboard link #> +#> ── `tests/testthat/_outline/roxy-cli.R` outline +#> #> ── `tests/testthat/_outline/roxy-general.R` #> `i` Use 'tests/testthat/_outline/roxy-general2.R' for output testing #> `i` Complete block for exported function with headings @@ -398,26 +402,63 @@ outline #> `i` Second level #> `i` TODO this is an item- `Done✔?` #> `i` Last title +#> `i` `function_name()` title +#> +#> ── `tests/testthat/_snaps/browse-pkg.md` +#> `i` browse_pkg() works #> #> ── `tests/testthat/_snaps/case-if-any.md` +#> `i` case_if_any() basic work #> `i` wrong cases error #> #> ── `tests/testthat/_snaps/dplyr-plus.md` +#> `i` filter_if_any() errors with across() #> `i` adds rows in front, but warns the user +#> `i` summarise_with_total() works +#> `i` slice_min_max() works +#> `i` na_if2() works with expr and values #> #> ── `tests/testthat/_snaps/eda-identity.md` #> `i` Side effects are what's intended in interactive sessions #> +#> ── `tests/testthat/_snaps/escape-inline-markup.md` +#> `i` escape_markup() works +#> `i` replace_r_var() works +#> +#> ── `tests/testthat/_snaps/markup.md` +#> `i` link_gh_issue() + markup_href() work +#> #> ── `tests/testthat/_snaps/outline-criteria.md` #> `i` No outline criteria are untested #> #> ── `tests/testthat/_snaps/outline.md` +#> `i` file_outline() works #> `i` alpha and work_only arguments work +#> `i` file_outline() is a data frame #> `i` pattern works as expected +#> `i` file_outline() works well with figure captions +#> +#> ── `tests/testthat/_snaps/proj-list.md` +#> `i` proj_file() works +#> +#> ── `tests/testthat/_snaps/quarto-help.md` +#> `i` href_name_url() works +#> `i` quarto_help() works #> #> ── `tests/testthat/_snaps/rename.md` +#> `i` rename_files2(): prevents file renaming if conflicts +#> `i` rename_files2(): is easier to test messages with no action +#> `i` rename_files2(): renames files if forced to do so +#> `i` rename_files2(): doesn't check for references if file name is short +#> `i` rename_files2(): priorizes references if name is generic or widely used in files +#> `i` rename_files2(): can accept overridden preferences +#> `i` rename_files2(): relaxes its conditions for figures +#> `i` rename_files2(): calls check_referenced_files() #> `i` Helper files returns the expected input #> +#> ── `tests/testthat/_snaps/screenshot.md` +#> `i` screenshot() does nothing in non-interactive sessions +#> #> ── `tests/testthat/_snaps/todo.md` #> `i` Marking a TODO item as done works #> @@ -438,6 +479,9 @@ outline #> `i` Returns identity #> `i` Side effects are what's intended in interactive sessions #> +#> ── `tests/testthat/test-escape-inline-markup.R` +#> `i` TODO could probably be {. } works?- `Done✔?` +#> #> ── `tests/testthat/test-markup.R` #> `i` link_gh_issue() + markup_href() work #> @@ -456,6 +500,7 @@ outline #> `i` roxy tags are parsed properly + object names are correct #> `i` roxy tags don't error #> `i` multiple roxy tags don't error. +#> `i` cli escaping goes well... #> #> ── `tests/testthat/test-outline.R` #> `i` alpha and work_only arguments work diff --git a/tests/testthat/_outline/roxy-cli.R b/tests/testthat/_outline/roxy-cli.R new file mode 100644 index 0000000..f8b2ea2 --- /dev/null +++ b/tests/testthat/_outline/roxy-cli.R @@ -0,0 +1,17 @@ +# outline -------- +#' Like [base::grep()] but [grepl()] for ANSI strings +#' +#' kk +#' +#' @param x A title +#' +#' @section Escaping `{` and `}`: +#' +#' things +#' @export +f2 <- function(x) { + x **2 +} + + +# Keep this line last: content to test for new roxygen output should be put in roxy-general.R diff --git a/tests/testthat/_outline/roxy-general2.R b/tests/testthat/_outline/roxy-general2.R index 74349da..5b6b7ad 100644 --- a/tests/testthat/_outline/roxy-general2.R +++ b/tests/testthat/_outline/roxy-general2.R @@ -36,4 +36,4 @@ f_not_to_index <- function() { } -# Keep this line last: content to test for new roxygen output should be put in ex-outline-roxy.R +# Keep this line last: content to test for new roxygen output should be put in roxy-general.R diff --git a/tests/testthat/_outline/titles.md b/tests/testthat/_outline/titles.md index 73eccae..954687d 100644 --- a/tests/testthat/_outline/titles.md +++ b/tests/testthat/_outline/titles.md @@ -8,3 +8,5 @@ output not a todo # Last title + +## `function_name()` title diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index 2ae054f..1c50fe7 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -20,6 +20,7 @@ `i` Another title `i` Last title `i` Second level + `i` `function_name()` title `i` TODO this is an item- `Donev?` Message diff --git a/tests/testthat/test-escape-inline-markup.R b/tests/testthat/test-escape-inline-markup.R index dbaeeb3..98f64a4 100644 --- a/tests/testthat/test-escape-inline-markup.R +++ b/tests/testthat/test-escape-inline-markup.R @@ -5,6 +5,11 @@ test_that("escape_markup() works", { expect_equal(escape_markup("{.file {here}}"), "{.file {.url here}}") expect_equal(escape_markup("{"), "{{") expect_equal(escape_markup("{.email}"), "{{.email}}") + expect_equal(escape_markup("This `}` and `{`"), "This `}}` and `{{`") + expect_equal(escape_markup("This `{` and `}`"), "This `{{` and `}}`") + + # TODO could probably be {{. }} works? + expect_equal(escape_markup("{. } works"), ". works") input <- "multi problems {{gt}} to {gt} to {.file gt} to {.file {gt}}" exp_str <- "multi problems {{gt}} to {{gt}} to {.file gt} to {.file {.url gt}}" expect_equal(escape_markup(input), exp_str) diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 560b942..981ecf7 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -35,3 +35,12 @@ test_that("multiple roxy tags don't error.", { example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) expect_no_error(join_roxy_fun(example_parsed)) }) + +test_that("cli escaping goes well...", { + skip("Not ready :(") + file_to_map <- testthat::test_path("_outline", "roxy-cli.R") + expect_no_error(file_outline(path = file_to_map)) + names(file_to_map) <- file_to_map + example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) + expect_no_error(join_roxy_fun(example_parsed)) +}) diff --git a/tests/testthat/test-outline.R b/tests/testthat/test-outline.R index 40698a4..46c92e8 100644 --- a/tests/testthat/test-outline.R +++ b/tests/testthat/test-outline.R @@ -60,7 +60,7 @@ test_that("file_outline() with only title doesn't error", { file <- file_outline(path = test_path("_outline", "titles.md")) }) # Number of items in titles.md - expect_equal(nrow(file), 5L) + expect_equal(nrow(file), 6L) }) test_that("file_outline() contains function calls", { From 57547f13d3ebcacac1ffc2a52c940e3f9a2e8b29 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 23:20:25 -0400 Subject: [PATCH 46/71] mark todos as complete... --- TODO.R | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TODO.R b/TODO.R index 079b48b..8b379ee 100644 --- a/TODO.R +++ b/TODO.R @@ -6,7 +6,6 @@ # TODO [outline] make ggtitle work # TODO [outline] show extra msg only for some, but in file outline, not in proj? # TODO [outline] detect help calls and apply markup. `?fs::file_show` disregard finishing `.` (not followed by dot) -# TODO [outline] renable cli info. # TODO escape_markup doesn't work with complex operation {{x^2}} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this. # TODO [outline] avoid evaluating in current env. # TODO wrap regexps in functions @@ -14,7 +13,6 @@ # TODO [outline] remove examples from outline. Sometimes commented code is caught. # TODO [outline] roxygen comments processing should be left to {.fn roxygen2::parse_file} # TODO [outline] show key like {.fn pak::pkg_deps_tree} does. -# TODO [outline] roxygen function title # TODO [outline] remove ggtext markup from plot title. # FIXME [outline] comments are now interpreted as section # TODO [outline] todos in qmd file inside html comment @@ -25,7 +23,7 @@ # TODO explain rationale behind `work_only`. Suggest to transform to TODO if this item is no longer critical. `work_only` goal is to show you exactly where you need to do work # TODO browse_pkg should open by default if no vignettes are found, because there is not much to do in the R-session. # TODO exclude _files from `proj_list()` -# TODO [outline] Show function call if exported + not internal + bonus if has family tagf +# TODO [outline] Show function call if exported + not internal + bonus if has family tag! rstudio/rstudio#14766 # TODO title of file could be function title if it is first element [proj_outline] # TODO rename_files should be less noisy about project name file # TODO add_to_tricks(). when detecting TRICK like complete todo, but not remove line. requires a scheme. moves the item to tricks.md at the correct place. (copy to clipboard is probably enough) From 420fd50f491a65e17c92ed6cb93460900dea9d1b Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 23:29:23 -0400 Subject: [PATCH 47/71] Rename `print_todo` -> `exclude_todos`... --- R/outline-criteria.R | 4 ++-- R/outline.R | 28 ++++++++++++++++++++-------- man/outline.Rd | 8 +++++--- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 64ee762..da27834 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -125,7 +125,7 @@ o_is_cli_info <- function(x, is_snap_file = FALSE, file = "file") { # Add variable to outline data frame -------------------- -define_outline_criteria <- function(.data, print_todo, dir_common) { +define_outline_criteria <- function(.data, exclude_todos, dir_common) { x <- .data x$file_ext <- s_file_ext(x$file) x$is_md <- x$file_ext %in% c("qmd", "md", "Rmd", "Rmarkdown") @@ -186,7 +186,7 @@ define_outline_criteria <- function(.data, print_todo, dir_common) { is_object_title = FALSE, tag = NA_character_, topic = NA_character_, - is_todo_fixme = print_todo & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext) & !is_snap_file, + is_todo_fixme = !exclude_todos & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext) & !is_snap_file, n_leading_hash = nchar(stringr::str_extract(content, "\\#+(?!\\|)")), # don't count hashpipe n_leading_hash = dplyr::coalesce(n_leading_hash, 0), # Make sure everything is second level in revdep/. diff --git a/R/outline.R b/R/outline.R index 6c5fdb4..b25286b 100644 --- a/R/outline.R +++ b/R/outline.R @@ -46,7 +46,7 @@ #' @param work_only If `TRUE`, (the default), will only show you work items #' first. Set to `FALSE` if you want to see the full outline. `WORK` will #' combine with `pattern` -#' @param print_todo Should include TODOs in the file outline? If `FALSE`, will +#' @param exclude_todos Should include TODOs in the file outline? If `FALSE`, will #' print a less verbose output with sections. #' @param alpha Whether to show in alphabetical order #' @param dir_tree If `TRUE`, will print the [fs::dir_tree()] or non-R files in @@ -64,7 +64,7 @@ #' file_outline(path = file) #' #' # Remove todo items -#' file_outline(path = file, print_todo = FALSE, alpha = TRUE) +#' file_outline(path = file, exclude_todos = TRUE, alpha = TRUE) #' #' # interact with data frame #' file_outline(path = file) |> dplyr::as_tibble() @@ -86,8 +86,9 @@ file_outline <- function(pattern = NULL, work_only = TRUE, alpha = FALSE, dir_common = NULL, - print_todo = TRUE, - recent_only = FALSE) { + exclude_todos = FALSE, + recent_only = FALSE, + print_todo = deprecated()){ # To contribute to this function, take a look at .github/CONTRIBUTING if (length(path) == 1L && interactive() && rstudioapi::isAvailable()) { @@ -99,6 +100,15 @@ file_outline <- function(pattern = NULL, cli::cli_abort("No path specified.") } + if (lifecycle::is_present(print_todo)) { + exclude_todos <- !print_todo + lifecycle::deprecate_warn( + when = "0.0.3", + what = "file_outline(print_todo)", + with = "file_outline(exclude_todos)" + ) + } + # active_rs_doc() returns `NULL` if the active document is unsaved. is_saved_doc <- !is.null(path) if (is_saved_doc) { @@ -162,7 +172,7 @@ file_outline <- function(pattern = NULL, check_string(pattern, arg = "You may have specified path Internal error") - file_sections00 <- define_outline_criteria(file_content, print_todo = print_todo, dir_common) + file_sections00 <- define_outline_criteria(file_content, exclude_todos = exclude_todos, dir_common) # filter for interesting items. file_sections0 <- keep_outline_element(file_sections00) @@ -229,7 +239,7 @@ file_outline <- function(pattern = NULL, } #' @rdname outline #' @export -proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, exclude_tests = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE) { +proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, exclude_tests = FALSE, exclude_todos = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE) { is_active_proj <- identical(proj, proj_get2()) if (is_active_proj && !is.null(pattern) && pattern %in% names(proj_list())) { @@ -245,6 +255,7 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, e pattern = pattern, work_only = work_only, exclude_tests = exclude_tests, + exclude_todos = exclude_todos, dir_tree = dir_tree, alpha = alpha, recent_only = recent_only, @@ -285,6 +296,7 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, e path = proj_dir, work_only = work_only, exclude_tests = exclude_tests, + exclude_todos = exclude_todos, dir_tree = dir_tree, alpha = alpha, recurse = TRUE @@ -292,7 +304,7 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, e } #' @rdname outline #' @export -dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_tests = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, recurse = FALSE) { +dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_tests = FALSE, exclude_todos = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, recurse = FALSE) { dir <- fs::path_real(path) file_exts <- c("R", "qmd", "Rmd", "md", "Rmarkdown") file_exts_regex <- paste0("*.", file_exts, "$", collapse = "|") @@ -340,7 +352,7 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te invert = TRUE ) } - file_outline(path = file_list_to_outline, pattern = pattern, work_only = work_only, dir_common = dir, alpha = alpha, recent_only = recent_only) + file_outline(path = file_list_to_outline, pattern = pattern, exclude_todos = exclude_todos, work_only = work_only, dir_common = dir, alpha = alpha, recent_only = recent_only) } # Print method ------------------- diff --git a/man/outline.Rd b/man/outline.Rd index 0a9a947..8ab50c7 100644 --- a/man/outline.Rd +++ b/man/outline.Rd @@ -13,7 +13,7 @@ file_outline( work_only = TRUE, alpha = FALSE, dir_common = NULL, - print_todo = TRUE, + exclude_todos = FALSE, recent_only = FALSE ) @@ -22,6 +22,7 @@ proj_outline( proj = proj_get2(), work_only = TRUE, exclude_tests = FALSE, + exclude_todos = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE @@ -32,6 +33,7 @@ dir_outline( path = ".", work_only = TRUE, exclude_tests = FALSE, + exclude_todos = FALSE, dir_tree = FALSE, alpha = FALSE, recent_only = FALSE, @@ -54,7 +56,7 @@ combine with \code{pattern}} \item{dir_common}{(Do not use it)} -\item{print_todo}{Should include TODOs in the file outline? If \code{FALSE}, will +\item{exclude_todos}{Should include TODOs in the file outline? If \code{FALSE}, will print a less verbose output with sections.} \item{recent_only}{Show outline for recent files} @@ -115,7 +117,7 @@ file <- fs::path_package("reuseme", "example-file", "outline-script.R") file_outline(path = file) # Remove todo items -file_outline(path = file, print_todo = FALSE, alpha = TRUE) +file_outline(path = file, exclude_todos = TRUE, alpha = TRUE) # interact with data frame file_outline(path = file) |> dplyr::as_tibble() From 93b1805f8141855a84e794f067742383e534892a Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 23:38:50 -0400 Subject: [PATCH 48/71] Rm redundant heading --- R/outline-criteria.R | 2 +- TODO.R | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index da27834..080c982 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -93,7 +93,7 @@ o_is_section_title <- function(x, roxy_section = FALSE) { if (roxy_section) { x <- stringr::str_remove(x, ":$") } - uninteresting_headings <- "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE|url\\{|Error before installation|unreleased|^Function ID$|^Function Introduced$|^Examples$|Newly broken$|Newly fixed$|In both$|Installation$|MIT License|nocov" + uninteresting_headings <- "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE|url\\{|Error before installation|unreleased|^Function ID$|^Function Introduced$|^Examples$|Newly broken$|Newly fixed$|In both$|Installation$|MIT License|nocov|With cli$" # potential section titles p_s_title <- which(is_section_title) is_section_title[p_s_title] <- !stringr::str_detect(x[p_s_title], uninteresting_headings) & !o_is_todo_fixme(x[p_s_title]) & !o_is_commented_code(x[p_s_title]) diff --git a/TODO.R b/TODO.R index 8b379ee..e0ff939 100644 --- a/TODO.R +++ b/TODO.R @@ -27,3 +27,4 @@ # TODO title of file could be function title if it is first element [proj_outline] # TODO rename_files should be less noisy about project name file # TODO add_to_tricks(). when detecting TRICK like complete todo, but not remove line. requires a scheme. moves the item to tricks.md at the correct place. (copy to clipboard is probably enough) +# TODO [outline] just create an `exclude` argument that will take an option? (exclude can be files or expressionsm, or elements.) From 938193b569860756d0055f8186957285677c6de1 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 30 May 2024 23:57:58 -0400 Subject: [PATCH 49/71] Avoid empty todos + html sourceCode (tidyselect integration) Also guard against variables as fig-cap... --- R/outline-criteria.R | 4 ++-- R/outline.R | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 080c982..4ba80bf 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -33,7 +33,7 @@ o_is_roxygen_comment <- function(x, file_ext = NULL, is_test_file = FALSE) { } o_is_todo_fixme <- function(x) { - has_todo <- stringr::str_detect(x, "(? 0) { - caps <- c(caps, dat |> purrr::map_chr(\(x) x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% "USELESS THING")) + # We use `format()` in case a variable is used to name the caption. + tryCatch(caps <- c(caps, dat |> purrr::map_chr(\(x) format(x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% "USELESS THING"))), error = function(e) { + cli::cli_abort("Error in {.file {unique_file[i]}}", parent = e) + }) } } caps <- caps[caps != "USELESS THING"] From 11880b104c01a33a3481523a61444c606cd22337 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 07:56:44 -0400 Subject: [PATCH 50/71] Lint + use base R replacements --- R/browse-pkg.R | 4 +-- R/dplyr-plus.R | 19 ++++++------ R/escape-inline-markup.R | 8 ++--- R/files-conflicts.R | 8 ++--- R/markup.R | 6 ++-- R/open.R | 9 +++--- R/outline-criteria.R | 10 ++++--- R/outline-roxy.R | 2 +- R/outline.R | 48 ++++++++++++++---------------- R/rename.R | 6 ++-- R/todo.R | 6 ++-- man/outline.Rd | 5 +++- tests/testthat/_outline/roxy-cli.R | 2 +- tests/testthat/test-outline.R | 2 ++ tests/testthat/test-rename.R | 2 +- 15 files changed, 71 insertions(+), 66 deletions(-) diff --git a/R/browse-pkg.R b/R/browse-pkg.R index da80eb0..bfcbf00 100644 --- a/R/browse-pkg.R +++ b/R/browse-pkg.R @@ -88,9 +88,9 @@ browse_pkg <- function(package = NULL, "reference" ) - pkgdown <- stringr::str_remove(pkgdown, "/$") + pkgdown <- sub("/$", "", pkgdown) pkgdown_tabs_url <- paste0(pkgdown, "/", pkgdown_tabs, "/") - if (stringr::str_detect(pkgdown, "r-lib.org|tidyverse.org|tidymodels.org") && !stringr::str_detect(pkgdown, "github.com")) { + if (grepl("r-lib.org|tidyverse.org|tidymodels.org", pkgdown) && !grepl("github.com", pkgdown, fixed = TRUE)) { # known packages with dev enabled. pkgdown_tabs_url[1] <- paste0(pkgdown, "/dev/news") } diff --git a/R/dplyr-plus.R b/R/dplyr-plus.R index 7b68c75..6815951 100644 --- a/R/dplyr-plus.R +++ b/R/dplyr-plus.R @@ -240,10 +240,10 @@ filter_if_any <- function(.data, ..., .by = NULL, .keep_new_var = FALSE) { if (all(purrr::map_lgl(variables[, 1:n_var], is.logical))) { res <- dplyr::filter(variables, dplyr::if_any(.cols = seq_len(n_var)), .by = {{ .by }}) - if (!.keep_new_var) { - res <- res[-seq_len(n_var)] - } else { + if (.keep_new_var) { cli::cli_warn("You have modified the original data") + } else { + res <- res[-seq_len(n_var)] } return(res) @@ -310,14 +310,13 @@ extract_cell_value <- function(data, var, filter, name = NULL, length = NULL, un if (unique) { res2 <- unique_named(res2) } - if (!is.null(length)) { + + if (!is.null(length) && !rlang::has_length(res2, length)) { # TODO use `check_length()` when implemented. r-lib/rlang#1618 - if (!rlang::has_length(res2, length)) { - cli::cli_abort(c( - "Expected an output of {length}", - "Got an output of {length(res2)}" - )) - } + cli::cli_abort(c( + "Expected an output of {length}", + "Got an output of {length(res2)}" + )) } res2 diff --git a/R/escape-inline-markup.R b/R/escape-inline-markup.R index 193ccb8..372e1e2 100644 --- a/R/escape-inline-markup.R +++ b/R/escape-inline-markup.R @@ -28,8 +28,8 @@ escape_markup <- function(x) { is_markup_okay <- is_bracket & stringr::str_detect(x, "\\{\\.[:alpha:]+[^\\{]") & !local_var_ref_in_markup & !is_markup_incorrect(x) if (all(is_markup_okay) && !any(local_var_ref_in_markup)) { - x[is_left_bracket & !is_bracket] <- stringr::str_replace_all(x[is_left_bracket & !is_bracket], "\\{", "{{") - x[is_right_bracket & !is_bracket] <- stringr::str_replace_all(x[is_right_bracket & !is_bracket], "\\}", "}}") + x[is_left_bracket & !is_bracket] <- gsub("{", "{{", x[is_left_bracket & !is_bracket], fixed = TRUE) + x[is_right_bracket & !is_bracket] <- gsub("}", "}}", x[is_right_bracket & !is_bracket], fixed = TRUE) return(x) } # replace fn arg {fn}(arg) -> fn({arg}) @@ -61,8 +61,8 @@ escape_markup <- function(x) { x <- replace_r_var(x) - x[is_left_bracket & !is_bracket] <- stringr::str_replace_all(x[is_left_bracket & !is_bracket], "\\{", "{{") - x[is_right_bracket & !is_bracket] <- stringr::str_replace_all(x[is_right_bracket & !is_bracket], "\\}", "}}") + x[is_left_bracket & !is_bracket] <- gsub("{", "{{", x[is_left_bracket & !is_bracket], fixed = TRUE) + x[is_right_bracket & !is_bracket] <- gsub("}", "}}", x[is_right_bracket & !is_bracket], fixed = TRUE) # whisker replacement {{{ vignette_title }}} in usethis by vignette_title x <- stringr::str_replace_all(x, "\\{\\{?\\{?(\\s?[^\\}]+) \\}?\\}?\\}", "\\1") diff --git a/R/files-conflicts.R b/R/files-conflicts.R index 511a1d9..c3175d4 100644 --- a/R/files-conflicts.R +++ b/R/files-conflicts.R @@ -118,7 +118,7 @@ solve_file_name_conflict <- function(files, regex, dir = ".", extra_msg = NULL, cli::cli_inform } # Remove duplicated Found x references - which_bullet_to_replace <- stringr::str_subset(extra_msg, "Found references to", negate = TRUE) + which_bullet_to_replace <- stringr::str_subset(extra_msg, stringr::fixed("Found references to"), negate = TRUE) # possibly just move up our # extra_msg[i] <- f_inform(c( @@ -152,8 +152,8 @@ get_referenced_files <- function(files) { stringr::str_subset(pattern = "file.[(exist)|(delete)]|glue\\:\\:glue|unlink", negate = TRUE) |> # don't detect where we test for existence of path or construct a path with glue stringr::str_subset(pattern = "[(regexp)|(pattern)]\\s\\=.*\".*[:alpha:]\"", negate = TRUE) |> # remove regexp = a.pdf format stringr::str_subset(pattern = "grepl?\\(|stringr|g?sub\\(", negate = TRUE) |> # avoid regexp - stringr::str_subset(pattern = "nocheck", negate = TRUE) |> # remove nocheck and unlink statements (refers to deleted files anywa) - stringr::str_subset("\"") |> + stringr::str_subset(pattern = stringr::fixed("nocheck"), negate = TRUE) |> # remove nocheck and unlink statements (refers to deleted files anywa) + stringr::str_subset(stringr::fixed("\"")) |> stringr::str_trim() |> stringr::str_extract_all("\"[^\"]+\"") |> unlist() |> @@ -163,7 +163,7 @@ get_referenced_files <- function(files) { stringr::str_subset(pattern = "tmp|temp", negate = TRUE) |> # remove common file names that are not very nice stringr::str_subset(pattern = "https?", negate = TRUE) |> # doesn't check for files read online. stringr::str_subset(pattern = "\\@.+\\.", negate = TRUE) |> # email addresses or containing @ - stringr::str_subset(pattern = "_fichiers/", negate = TRUE) |> # manually remove false positive + stringr::str_subset(pattern = stringr::fixed("_fichiers/"), negate = TRUE) |> # manually remove false positive stringr::str_subset(pattern = "\n", negate = TRUE) |> # remove things with line breaks stringr::str_subset(pattern = "^\\.[:alpha:]{1,4}$", negate = TRUE) |> # remove reference to only file extensions stringr::str_subset(pattern = "\\.\\d+$", negate = TRUE) |> # remove 0.000 type diff --git a/R/markup.R b/R/markup.R index 86a0a30..464cc7a 100644 --- a/R/markup.R +++ b/R/markup.R @@ -75,14 +75,14 @@ find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { usethis::browse_github(basename(pkg_path)), error = function(e) { # TODO possibly look into checking desc::desc_get("BugReports", "~/path/to/DESCRIPTION") - # cli::cli_abort("didn't find a way to do what is required.", parent = e) + # cli::cli_abort("didn't find a way to do what is required.", parent = e) NULL } ) if (is.null(gh_url)) { return(NULL) } - org_repo_found <- stringr::str_remove(gh_url, ".+github.com/|.+gitlab.com/") + org_repo_found <- sub(".+github.com/|.+gitlab.com/", "", gh_url) return(org_repo_found) } @@ -96,7 +96,7 @@ find_pkg_org_repo <- function(dir_common = NULL, file = NULL) { ) gh_url <- usethis::browse_github(basename(pkg_path)) - org_repo_found <- stringr::str_remove(gh_url, ".+github.com/|.+gitlab.com/") + org_repo_found <- sub(".+github.com/|.+gitlab.com/", "", gh_url) } else { org_repo_found <- NULL } diff --git a/R/open.R b/R/open.R index 1ec1e7d..c3e4bf7 100644 --- a/R/open.R +++ b/R/open.R @@ -79,20 +79,21 @@ active_rs_doc_copy <- function(new = NULL, ..., old = NULL) { cli::cli_abort("Unsaved document, focus on the saved doc you want to save.") } - if (!fs::path_ext(old) %in% c("md","R", "qmd", "Rmd")) { + if (!fs::path_ext(old) %in% c("md", "R", "qmd", "Rmd")) { cli::cli_abort("Only R and md docs for now") } old_path_file <- fs::path_ext_remove(fs::path_file(old)) - if (stringr::str_detect(old, "r-profile|Rprofile")) { + + if (grepl("r-profile|Rprofile", old)) { cli::cli_abort("Attempting to copy Rprofile (focus on the document you want)") } if (is.null(new)) { new_name <- paste0(old_path_file, "-new") } else { - new_name <- stringr::str_remove(new, "\\.R|\\.[Rq]?md$") + new_name <- sub("\\.R|\\.[Rq]?md$", "", new) } # Hack to ensure file/file.R will be correctly renamed. - new_path <- stringr::str_replace(old, paste0(old_path_file, "\\."), paste0(new_name, ".")) + new_path <- sub(paste0(old_path_file, "\\."), paste0(new_name, "."), old) copied <- file.copy(old, new_path, overwrite = FALSE) if (copied) { diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 4ba80bf..12f0406 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -91,7 +91,7 @@ o_is_section_title <- function(x, roxy_section = FALSE) { return(is_section_title) } if (roxy_section) { - x <- stringr::str_remove(x, ":$") + x <- sub(":$", "", x) } uninteresting_headings <- "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE|url\\{|Error before installation|unreleased|^Function ID$|^Function Introduced$|^Examples$|Newly broken$|Newly fixed$|In both$|Installation$|MIT License|nocov|With cli$|sourceCode" # potential section titles @@ -152,7 +152,9 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { # roxygen2 messages # TRICK purrr::safely creates an error object, while possible is better. # Suppresss roxygen2 message, suppress callr output, suppress asciicast warnings. - invisible(capture.output(parsed_files <- purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))))) |> suppressMessages() |> suppressWarnings() + invisible(capture.output(parsed_files <- purrr::map(files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL))))) |> + suppressMessages() |> + suppressWarnings() # if roxygen2 cannot parse a file, let's just forget about it. unparsed_files <- files_with_roxy_comments[which(is.null(parsed_files))] # browser() @@ -245,8 +247,8 @@ define_outline_criteria_roxy <- function(x) { ) x$content <- dplyr::case_when( !x$is_section_title ~ x$content, - x$tag == "section" ~ paste0("# ", stringr::str_remove(x$content, ":")), - x$tag == "subsection" ~ paste0("## ", stringr::str_remove(x$content, ":")), + x$tag == "section" ~ paste0("# ", sub(":", "", x$content, fixed = TRUE)), + x$tag == "subsection" ~ paste0("## ", sub(":", "", x$content, fixed = TRUE)), .default = x$content ) x$is_second_level_heading_or_more <- ((x$is_section_title_source | x$is_section_title) & x$n_leading_hash > 1) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index c82ccfa..8a0cd1e 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -116,7 +116,7 @@ extract_roxygen_tag_location <- function(file, tag) { line_pos <- x$line + seq_along(lines) - 1L final_lines_to_include <- lines[keep] # Will not make this transformation and will consider roxygen comments to be - # final_lines_to_include <- stringr::str_remove(final_lines_to_include, "^#+\\s") + # final_lines_to_include <- sub("^#+\\s", "", final_lines_to_include) final_lines_to_include <- paste0(final_lines_to_include, "____", obj_name) names(final_lines_to_include) <- line_pos[keep] diff --git a/R/outline.R b/R/outline.R index 83f03bd..1454f7f 100644 --- a/R/outline.R +++ b/R/outline.R @@ -53,6 +53,7 @@ #' the directory #' @param recent_only Show outline for recent files #' @param dir_common (Do not use it) +#' @param print_todo `r lifecycle::badge("deprecated")`. Use `exclude_todos` instead. #' @inheritParams fs::dir_ls #' @returns A `outline_report` object that contains the information. Inherits #' `tbl_df`. @@ -88,8 +89,8 @@ file_outline <- function(pattern = NULL, dir_common = NULL, exclude_todos = FALSE, recent_only = FALSE, - print_todo = deprecated()){ - # To contribute to this function, take a look at .github/CONTRIBUTING + print_todo = deprecated()) { + # To contribute to this function, take a look at .github/CONTRIBUTING.md if (length(path) == 1L && interactive() && rstudioapi::isAvailable()) { is_active_doc <- identical(path, active_rs_doc()) @@ -144,8 +145,8 @@ file_outline <- function(pattern = NULL, file_content <- dplyr::bind_rows(file_content, .id = "file") } - suppressMessages( - in_active_project <- tryCatch( + in_active_project <- suppressMessages( + tryCatch( identical(suppressWarnings(proj_get2()), dir_common), error = function(e) FALSE ) @@ -225,7 +226,7 @@ file_outline <- function(pattern = NULL, ) file_sections$recent_only <- recent_only - if (any(duplicated(file_sections$outline_el))) { + if (anyDuplicated(file_sections$outline_el) > 0L) { file_sections <- scrub_duplicate_outline(file_sections) } file_sections <- dplyr::relocate( @@ -263,14 +264,14 @@ proj_outline <- function(pattern = NULL, proj = proj_get2(), work_only = TRUE, e )) } - if (!fs::dir_exists(proj)) { # when referring to a project by name. - proj_dir <- proj_list(proj) - } else { + if (fs::dir_exists(proj)) { if (!is_active_proj) { cli::cli_warn("Use {.fn dir_outline} for that.") } - proj_dir <- proj + } else { + # when referring to a project by name. + proj_dir <- proj_list(proj) } if (!rlang::has_length(proj_dir, 1)) { @@ -352,7 +353,7 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te invert = TRUE ) } - file_outline(path = file_list_to_outline, pattern = pattern, exclude_todos = exclude_todos, work_only = work_only, dir_common = dir, alpha = alpha, recent_only = recent_only) + file_outline(path = file_list_to_outline, pattern = pattern, exclude_todos = exclude_todos, work_only = work_only, dir_common = dir, alpha = alpha, recent_only = recent_only) } # Print method ------------------- @@ -564,7 +565,7 @@ display_outline_element <- function(.data, dir_common) { names() all_true_or_single_value <- x |> dplyr::slice(dplyr::all_of(indices)) |> - dplyr::select(dplyr::where(\(x)dplyr::n_distinct(x) == 1)) |> + dplyr::select(dplyr::where(\(x) dplyr::n_distinct(x) == 1)) |> dplyr::select(!dplyr::where(\(x) all(is.na(x)))) |> dplyr::select(!dplyr::where(\(x) is.logical(x) & !suppressWarnings(any(x, na.rm = FALSE)))) |> names() @@ -747,20 +748,17 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { outline_el2 = dplyr::coalesce(outline_el2, outline_el) ) - .data <- dplyr::mutate(.data, - link = paste0(outline_el2, " {.path ", file, ":", line, "}"), - # rstudioapi::documentOpen works in the visual mode!! but not fully. - file_path = .data$file, - is_saved_doc = .env$is_saved_doc, - - # May have caused CI failure - text_in_link = stringr::str_remove(file_path, as.character(.env$dir_common)) |> stringr::str_remove("^/"), - # decide which is important - style_fun = dplyr::case_match(importance, - "not_important" ~ "cli::style_italic('i')", # cli::style_inverse for bullets - "important" ~ "cli::style_inverse('i')", - .default = NA - ) + .data$link <- paste0(.data$outline_el2, " {.path ", .data$file, ":", .data$line, "}") + # rstudioapi::documentOpen works in the visual mode!! but not fully. + .data$file_path <- .data$file + .data$is_saved_doc <- is_saved_doc + # May have caused CI failure + .data$text_in_link <- sub(as.character(dir_common), "", .data$file_path) + .data$text_in_link <- sub("^/", "", .data$text_in_link) + .data$style_fun <- dplyr::case_match(.data$importance, + "not_important" ~ "cli::style_italic('i')", # cli::style_inverse for bullets + "important" ~ "cli::style_inverse('i')", + .default = NA_character_ ) if (anyNA(.data$style_fun)) { diff --git a/R/rename.R b/R/rename.R index 3f82e8f..7348af2 100644 --- a/R/rename.R +++ b/R/rename.R @@ -71,7 +71,7 @@ rename_files2 <- function(old, # renaming should only happen in tests or interactive sessions if (action == "rename" && !(rlang::is_interactive() || identical(Sys.getenv("TESTTHAT"), "true"))) { - cli::cli_abort(c("Should only rename files in interactive sessions (or in tests)")) + cli::cli_abort("Should only rename files in interactive sessions.") } is_git <- !isFALSE(tryCatch(rprojroot::find_root_file(criterion = rprojroot::criteria$is_vcs_root), error = function(e) FALSE)) @@ -124,7 +124,7 @@ rename_files2 <- function(old, if (renaming_strategy == "object_names") { # Create regex = replace kebab-case by snake_case to verify object names # Then the regex is the union of these. - regex_friendly <- c(basename_remove_ext(old), stringr::str_replace_all(basename_remove_ext(old), "-", "_")) + regex_friendly <- c(basename_remove_ext(old), gsub("-", "_", basename_remove_ext(old), fixed = TRUE)) regex_friendly <- paste0(unique(regex_friendly), collapse = "|") } else { regex_friendly <- ifelse(renaming_strategy %in% c("object_names"), basename_remove_ext(old), old) @@ -216,7 +216,7 @@ compute_conflicts_regex <- function(file, renaming_strategy) { file_name_base <- basename_remove_ext(file) - object_snake_from_file_kebab <- stringr::str_replace_all(file_name_base, "-", "_") + object_snake_from_file_kebab <- gsub("-", "_", file_name_base, fixed = TRUE) if (renaming_strategy == "object_names") { # dat/file-name.csv|file_name diff --git a/R/todo.R b/R/todo.R index e94eff5..1ffdfb8 100644 --- a/R/todo.R +++ b/R/todo.R @@ -209,7 +209,7 @@ compute_path_todo <- function(todo, proj) { if (!is.na(proj_name_in_todo)) { proj <- proj_name_in_todo regex_proj_in_todo <- paste0(proj_name_in_todo, "\\:\\:", "\\s?") - todo[1] <- stringr::str_remove(todo[1], regex_proj_in_todo) + todo[1] <- sub(regex_proj_in_todo, "", todo[1]) } is_active_proj <- tryCatch( @@ -247,11 +247,11 @@ compute_path_todo <- function(todo, proj) { # accepts a single line strip_todo_line <- function(x, only_rm_tag = FALSE) { check_string(x) - if (!stringr::str_detect(x, "TODO|WORK|FIXME")) { + if (!grepl("TODO|WORK|FIXME", x)) { cli::cli_abort("Could not detect a todo tag in x") } if (only_rm_tag) { - x_new <- stringr::str_remove(x, "\\s(TODO|WORK|FIXME)") + x_new <- sub("\\s(TODO|WORK|FIXME)", "", x) } else { x_new <- stringr::str_extract(x, "([^#]+)\\#+", group = 1) if (is.na(x_new)) { diff --git a/man/outline.Rd b/man/outline.Rd index 8ab50c7..9772a78 100644 --- a/man/outline.Rd +++ b/man/outline.Rd @@ -14,7 +14,8 @@ file_outline( alpha = FALSE, dir_common = NULL, exclude_todos = FALSE, - recent_only = FALSE + recent_only = FALSE, + print_todo = deprecated() ) proj_outline( @@ -61,6 +62,8 @@ print a less verbose output with sections.} \item{recent_only}{Show outline for recent files} +\item{print_todo}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}. Use \code{exclude_todos} instead.} + \item{exclude_tests}{Should tests be displayed? (Not sure if this argument will stay, Will have to think)} \item{dir_tree}{If \code{TRUE}, will print the \code{\link[fs:dir_tree]{fs::dir_tree()}} or non-R files in diff --git a/tests/testthat/_outline/roxy-cli.R b/tests/testthat/_outline/roxy-cli.R index f8b2ea2..1d97a26 100644 --- a/tests/testthat/_outline/roxy-cli.R +++ b/tests/testthat/_outline/roxy-cli.R @@ -10,7 +10,7 @@ #' things #' @export f2 <- function(x) { - x **2 + x**2 } diff --git a/tests/testthat/test-outline.R b/tests/testthat/test-outline.R index 46c92e8..14575de 100644 --- a/tests/testthat/test-outline.R +++ b/tests/testthat/test-outline.R @@ -1,4 +1,5 @@ test_that("file_outline() works", { + skip_if_not_installed("lightparser") my_test_files <- test_path("_outline", c("my-analysis.R", "my-analysis.md", "title.md", "titles.md")) rlang::local_interactive(TRUE) expect_snapshot( @@ -72,6 +73,7 @@ test_that("file_outline() contains function calls", { }) test_that("dir_outline() works with no error", { + skip_if_not_installed("lightparser") expect_no_error(dir_outline(pattern = ".+", path = test_path("_outline"))) }) diff --git a/tests/testthat/test-rename.R b/tests/testthat/test-rename.R index 2b9bc38..78826d1 100644 --- a/tests/testthat/test-rename.R +++ b/tests/testthat/test-rename.R @@ -123,7 +123,7 @@ test_that("file testing are working as expected", { }) test_that("force and action are deprecated", { - file <- withr::local_tempfile(fileext = ".R", lines = c("# x1")) + file <- withr::local_tempfile(fileext = ".R", lines = "# x1") file2 <- withr::local_tempfile(fileext = ".R") unlink(file2) lifecycle::expect_deprecated( From 51bde5c5cc9217f8f5b5c9b88df3927d3be8ab49 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 08:01:55 -0400 Subject: [PATCH 51/71] Recognize the last way possible to specify chunk options. --- R/outline-criteria.R | 2 +- R/outline.R | 4 ++-- tests/testthat/_outline/my-analysis.md | 7 ++++++- tests/testthat/_snaps/outline.md | 3 ++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 12f0406..9782048 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -177,7 +177,7 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { # TODO long enough to be meanignful? is_doc_title = stringr::str_detect(content, "(? stringr::str_replace_all("\n", " ") + caps |> stringr::str_squish() } define_important_element <- function(.data) { diff --git a/tests/testthat/_outline/my-analysis.md b/tests/testthat/_outline/my-analysis.md index 2017f69..5236b18 100644 --- a/tests/testthat/_outline/my-analysis.md +++ b/tests/testthat/_outline/my-analysis.md @@ -26,6 +26,11 @@ Some text with TODO. ```{r} #| fig-cap: > #| A long ggplot2 title -#| with more details +#| with more details2 ``` +```{r} +#| fig.cap = " +#| A long ggplot2 title +#| with more details3." +``` diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index 1c50fe7..8534748 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -11,7 +11,8 @@ `i` A section2 `i` A long ggplot2 title `i` A code section - `i` A long ggplot2 title with more details + `i` A long ggplot2 title with more details2 + `i` A long ggplot2 title with more details3. `i` Dashboard card Message From 5751577fb9261a2f376f1318085bd275d64dcb43 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 08:53:27 -0400 Subject: [PATCH 52/71] Rename `is_chunk_cap` to `is_object_caption` --- R/outline-criteria.R | 47 +++++++++++++++++++------ R/outline-roxy.R | 2 +- R/outline.R | 21 +++++------ TODO.R | 1 + tests/testthat/_outline/roxy-general2.R | 13 ++++++- 5 files changed, 59 insertions(+), 25 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 9782048..fd68655 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -29,7 +29,11 @@ o_is_roxygen_comment <- function(x, file_ext = NULL, is_test_file = FALSE) { return(FALSE) } - ifelse(rep(is_r_file, length.out = length(x)), stringr::str_detect(x, "^#'\\s|^#'$"), FALSE) + ifelse( + rep(is_r_file, length.out = length(x)), + grepl("^#'\\s|^#'$", x), # detect roxygen comments in R files + FALSE # not a roxy comment in Rmd files, fusen is an exception? + ) } o_is_todo_fixme <- function(x) { @@ -45,7 +49,7 @@ o_is_todo_fixme <- function(x) { has_todo[p] <- !o_is_test_that(candidates) & !stringr::str_starts(candidates, "\\s*\"\\s*") & - !stringr::str_detect(candidates, "extract_tag_in_text") & + !grepl("extract_tag_in_text", candidates, fixed = TRUE) & !o_is_roxygen_comment(candidates) & # don't put these tags in documentation :) !stringr::str_detect(candidates, "grepl?\\(|g?sub\\(|str_detect|str_remove|str_extract|use_todo|,\\stodo\\)|TODO\\.R|TODO file|@param") & !stringr::str_detect(candidates, "[:upper:]\"|[:upper:]{4,10} item") & # eliminate false positives @@ -77,9 +81,15 @@ o_is_generic_test <- function(x) { # Returns table or plot titles. o_is_tab_plot_title <- function(x) { + generic_title_regex <- paste0( + "Foo|test|Title|TITLE|Subtitle|[eE]xample|x\\.x\\.", + "man_get_image_tab|table's|list\\(|bla\"|\", \"|use_.+\\(", + collapse = "|" + ) + stringr::str_detect(x, "(? + invisible( + capture.output( + parsed_files <- purrr::map( + files_with_roxy_comments, + purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL)))) + ) |> suppressMessages() |> suppressWarnings() # if roxygen2 cannot parse a file, let's just forget about it. @@ -175,13 +198,17 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { # TODO strip is_cli_info in Package? only valid for EDA (currently not showcased..) is_cli_info = o_is_cli_info(content, is_snap_file, file), # TODO long enough to be meanignful? - is_doc_title = stringr::str_detect(content, "(? Date: Fri, 31 May 2024 09:04:48 -0400 Subject: [PATCH 53/71] Add the factored `exclude_example_files()` --- R/outline.R | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/R/outline.R b/R/outline.R index e0f66e4..3047703 100644 --- a/R/outline.R +++ b/R/outline.R @@ -350,6 +350,25 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te file_outline(path = file_list_to_outline, pattern = pattern, exclude_todos = exclude_todos, work_only = work_only, dir_common = dir, alpha = alpha, recent_only = recent_only) } +exclude_example_files <- function(path) { + # TODO for usethis, add inst/templates/ but I may leave them for now.... + # styler tests examples may not work.. + + regexp_exclude <- paste0( + "vignettes/test/", # test vignettes + "tests/(performance-monitor|gt-examples/|testthat/scope-|testthat/assets|testthat/_outline|testthat/testTestWithFailure|testthat/testTest/)", # example files in usethis, pkgdown, reuseme, devtools, etc. + "inst/(templates/license-|example-file/)", # license templates in usethis + "revdep/", # likely don't need to outline revdep/, use dir_outline() to find something in revdep/ + collapse = "|" + ) + + fs::path_filter( + path, + regexp = regexp_exclude, + invert = TRUE + ) +} + # Print method ------------------- #' @export From 21fcdc39e0c723e7013f815594dad8312710abae Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 09:44:43 -0400 Subject: [PATCH 54/71] Improve knitr notebook support --- .github/CONTRIBUTING.md | 2 +- NEWS.md | 4 +- R/outline-criteria.R | 46 +++++++++++++++-------- R/outline-roxy.R | 5 ++- R/outline.R | 9 +++++ README.md | 44 ++++++++++++++-------- tests/testthat/_outline/knitr-notebook.R | 18 +++++++++ tests/testthat/_snaps/outline-criteria.md | 1 + tests/testthat/_snaps/outline.md | 10 +++++ tests/testthat/test-outline-criteria.R | 7 ++++ tests/testthat/test-outline.R | 7 ++++ 11 files changed, 118 insertions(+), 35 deletions(-) create mode 100644 tests/testthat/_outline/knitr-notebook.R diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 951ea85..301d95c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -47,7 +47,7 @@ See our guide on [how to create a great issue](https://code-review.tidyverse.org * `define_outline_criteria()` if an item shows as outline, but seems like a false positive, -* `keep_outline_element()`: if an element is **missing** from outline. +* `keep_outline_element()`: if an element is **missing** from outline, you can add the keyword "REQUIRED" to get an object for debugging. * `define_important_element()` if an element is important [^1] diff --git a/NEWS.md b/NEWS.md index 1609434..a3622a3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,9 @@ * `proj_outline()` now detects legacy `fig.cap` in the chunk header. See `knitr::convert_chunk_headers()` for the newer approach. -* `proj_outline()` now autolinks GitHub issuea inside the repo. +* `proj_outline()` now autolinks GitHub issue inside the repo. + +* `file_outline()` now supports [R reports](https://rmarkdown.rstudio.com/articles_report_from_r_script.html) (basic) # reuseme 0.0.2 diff --git a/R/outline-criteria.R b/R/outline-criteria.R index fd68655..f0e903e 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -18,11 +18,25 @@ extract_pkg_version <- function(x, is_news, is_heading) { #' * is_tab_plot_title #' #' @noRd -o_is_roxygen_comment <- function(x, file_ext = NULL, is_test_file = FALSE) { +o_is_notebook <- function(x, file, file_ext, line) { + # Like roxy comments and first line = --, 2nd title. + # x$is_notebook <- grepl("notebook.*\\.R", x$file) + # Detect #' --- + any_notebooks <- grep("^#' ---", x[line == 1 & file_ext == "R"], fixed = FALSE) + if (length(any_notebooks) > 0L) { + is_notebook <- file %in% file[line == 1 & file_ext == "R"][any_notebooks] + } else { + is_notebook <- FALSE + } + is_notebook +} + + +o_is_roxygen_comment <- function(x, file_ext = NULL, is_notebook = FALSE) { if (!is.null(file_ext)) { - is_r_file <- tolower(file_ext) == "r" & !is_test_file + is_r_file <- tolower(file_ext) == "r" & !is_notebook } else { - is_r_file <- !is_test_file + is_r_file <- !is_notebook } if (!any(is_r_file)) { @@ -106,7 +120,7 @@ o_is_section_title <- function(x, roxy_section = FALSE) { uninteresting_headings <- paste0( "(Tidy\\s?T(uesday|emplate)|Readme|Wrangle|Devel)$|error=TRUE", "url\\{|Error before installation|unreleased|^Function ID$|^Function Introduced$", - "^Examples$|Newly broken$|Newly fixed$|In both$|Installation$|MIT License|nocov|With cli$|sourceCode", + "^Examples$|Newly broken$|Newly fixed$|In both$|Installation$|MIT License|nocov|With cli$|sourceCode|Detect #'", collapse = "|" ) # potential section titles @@ -149,12 +163,10 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { x$is_md <- x$file_ext %in% c("qmd", "md", "Rmd", "Rmarkdown") x$is_news <- x$is_md & grepl("NEWS.md", x$file, fixed = TRUE) x$is_test_file <- grepl("tests/testthat", x$file, fixed = TRUE) - # TODO Would have to look for notebooks that don't contain notebook in their name - # Like roxy comments and first line = --, 2nd title. - x$is_notebook <- grepl("notebook.*\\.R", x$file) - x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext, x$is_test_file | x$is_notebook) - x$content[x$is_notebook] <- sub("^#' ", "", x$content[x$is_notebook]) - x$is_md <- (x$is_md | x$is_roxygen_comment) & !x$is_news # treating news and other md files differently. + x$is_notebook <- o_is_notebook(x = x$content, x$file, x$file_ext, x$line) + x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext, x$is_notebook) + x$content[x$is_notebook] <- sub("^#'\\s?", "", x$content[x$is_notebook]) + x$is_md <- (x$is_md | x$is_roxygen_comment | x$is_notebook) & !x$is_news # treating news and other md files differently. x$is_snap_file <- grepl("_snaps", x$file, fixed = TRUE) should_parse_roxy_comments <- @@ -252,7 +264,8 @@ define_outline_criteria_roxy <- function(x) { x$is_test_file <- FALSE x$is_snap_file <- FALSE x$before_and_after_empty <- TRUE - x$is_section_title <- (x$tag %in% c("section", "subsection") & stringr::str_ends(x$content, ":") & o_is_section_title(x$content, roxy_section = TRUE)) | + x$is_section_title <- + (x$tag %in% c("section", "subsection") & o_is_section_title(x$content, roxy_section = TRUE)) | (x$tag %in% c("details", "description") & stringr::str_detect(x$content, "#\\s")) x$is_section_title_source <- x$is_section_title x$is_obj_caption <- FALSE @@ -268,14 +281,17 @@ define_outline_criteria_roxy <- function(x) { x$n_leading_hash <- nchar(stringr::str_extract(x$content, "\\#+")) x$n_leading_hash <- dplyr::case_when( x$n_leading_hash > 0 ~ x$n_leading_hash, - x$tag == "section" & x$is_section_title_source ~ 1, - x$tag == "subsection" & x$is_section_title_source ~ 2, + # give second importance to doc sections.. + x$tag == "section" & x$is_section_title_source ~ 2, + x$tag == "subsection" & x$is_section_title_source ~ 3, .default = 0 ) x$content <- dplyr::case_when( !x$is_section_title ~ x$content, - x$tag == "section" ~ paste0("# ", sub(":", "", x$content, fixed = TRUE)), - x$tag == "subsection" ~ paste0("## ", sub(":", "", x$content, fixed = TRUE)), + # : removed from section tag in join_roxy_fun() + # code section may not be that interesting.. + x$tag == "section" ~ paste0("## ", x$content), + x$tag == "subsection" ~ paste0("### ", x$content), .default = x$content ) x$is_second_level_heading_or_more <- ((x$is_section_title_source | x$is_section_title) & x$n_leading_hash > 1) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index d016a2f..a46c79b 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -242,8 +242,9 @@ join_roxy_fun <- function(file) { ) |> dplyr::mutate(id = dplyr::row_number()) |> dplyr::mutate(content = dplyr::case_when( - # only keep the first line of family and concept tags. - tag %in% c("family", "concept") ~ stringr::str_extract(content, "^(.+)(\n)?"), + # only keep the first line of section, subsection, family and concept tags. + # Also remove : from section + tag %in% c("family", "concept", "section", "subsection") ~ stringr::str_extract(content, "^(.+):?(\n)?"), .default = content )) |> tidyr::separate_longer_delim(content, delim = "\n") diff --git a/R/outline.R b/R/outline.R index 3047703..6154e83 100644 --- a/R/outline.R +++ b/R/outline.R @@ -503,6 +503,9 @@ keep_outline_element <- function(.data) { versions_to_drop <- character(0L) } # browser() + # For debugging. + needed_elements <- which(grepl("REQUIRED", .data$content, fixed = TRUE) & !grepl("grep|keyword", .data$content, fixed = FALSE)) + dat <- dplyr::filter( .data, (is_news & ( @@ -521,6 +524,12 @@ keep_outline_element <- function(.data) { ) dat$simplify_news <- NULL + if (length(needed_elements) != length(grep("REQUIRED", dat$content, fixed = TRUE))) { + zz <<- dplyr::slice(.data, needed_elements) + cli::cli_abort( + "Debugging mode. An important element is absent from the outline. Review filters, regex detection etc." + ) + } dat } diff --git a/README.md b/README.md index 2f3ce8d..c97b7f4 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 6.94s 6.94s 0.144 78.1MB 1.15 +#> 1 outline <- proj_outline() 6.22s 6.22s 0.161 86.9MB 0.643 ```
@@ -235,7 +235,6 @@ outline #> #> ── `R/outline-criteria.R` #> `i` Add variable to outline data frame -#> `i` TODO Would have to look for notebooks that don't contain notebook in th…- `Done✔?` #> `i` TODO strip is_cli_info in Package? only valid for EDA (currently not sh…- `Done✔?` #> `i` TODO long enough to be meanignful?- `Done✔?` #> `i` TODO merge with define_outline_criteria- `Done✔?` @@ -308,14 +307,12 @@ outline #> `i` TODO outline make ggtitle work- `Done✔?` #> `i` TODO outline show extra msg only for some, but in file outline, not i…- `Done✔?` #> `i` TODO outline detect help calls and apply markup. `?fs::file_show` dis…- `Done✔?` -#> `i` TODO outline renable cli info.- `Done✔?` #> `i` TODO escape_markup doesn't work with complex operation {x^2} for example. Maybe if detecting something complex, use cli_escape function. escape-complex-markyp branch created to try to address this.- `Done✔?` #> `i` TODO outline avoid evaluating in current env.- `Done✔?` #> `i` TODO wrap regexps in functions- `Done✔?` #> `i` TODO outline remove examples from outline. Sometimes commented code i…- `Done✔?` #> `i` TODO outline roxygen comments processing should be left to `roxygen2::parse_file()`- `Done✔?` #> `i` TODO outline show key like `pak::pkg_deps_tree()` does.- `Done✔?` -#> `i` TODO outline roxygen function title- `Done✔?` #> `i` TODO outline remove ggtext markup from plot title.- `Done✔?` #> `i` FIXME outline comments are now interpreted as section- `Done✔?` #> `i` TODO outline todos in qmd file inside html comment- `Done✔?` @@ -326,10 +323,12 @@ outline #> `i` TODO explain rationale behind `work_only`. Suggest to transform to TODO…- `Done✔?` #> `i` TODO browse_pkg should open by default if no vignettes are found, becau…- `Done✔?` #> `i` TODO exclude _files from `proj_list()`- `Done✔?` -#> `i` TODO outline Show function call if exported + not internal + bonus if…- `Done✔?` +#> `i` TODO outline Show function call if exported + not internal + bonus if has family tag! rstudio/rstudio#14766 ()- `Done✔?` #> `i` TODO title of file could be function title if it is first element [proj…- `Done✔?` #> `i` TODO rename_files should be less noisy about project name file- `Done✔?` #> `i` TODO add_to_tricks(). when detecting TRICK like complete todo, but not …- `Done✔?` +#> `i` TODO outline just create an `exclude` argument that will take an opti…- `Done✔?` +#> `i` TODO outline remove snaps from outline and add a link in the test fil…- `Done✔?` #> #> ── `inst/example-file/outline-script.R` Example for `file_outline()` #> `i` Load packages @@ -337,6 +336,9 @@ outline #> `i` A great title #> `i` TODO improve this Viz!- `Done✔?` #> +#> ── `tests/testthat/_outline/knitr-notebook.R` Crop Analysis Q3 2013 +#> `i` A great section +#> #> ── `tests/testthat/_outline/my-analysis.R` Analyse my streets #> `i` Read my streets () data #> `i` data wrangling @@ -352,7 +354,8 @@ outline #> `i` A section2 #> `i` A long ggplot2 title #> `i` A code section -#> `i` A long ggplot2 title with more details +#> `i` A long ggplot2 title with more details2 +#> `i` A long ggplot2 title with more details3. #> #> ── `tests/testthat/_outline/quarto-caps.md` title #> `i` A long ggplot2 title with more details @@ -363,37 +366,44 @@ outline #> `i` Dashboard link #> #> ── `tests/testthat/_outline/roxy-cli.R` outline +#> `i` Like [base::grep()] but [grepl()] for ANSI strings #> #> ── `tests/testthat/_outline/roxy-general.R` #> `i` Use 'tests/testthat/_outline/roxy-general2.R' for output testing #> `i` Complete block for exported function with headings +#> `i` A title to be included #> `i` A second-level heading in description to be included? #> `i` A detail first level-heading to be included #> `i` A detail second-level heading to be included -#> `i` Commented code not included -#> `i` A title not to be included +#> `i` `First code` to be included: +#> `i` a family to include #> `i` block not to index -#> `i` Internal heading not to be included #> `i` Topic to index +#> `i` A title to be included #> `i` A second-level heading in description to be included? #> `i` A detail first level-heading to be included #> `i` A detail second-level heading to be included #> `i` First to be included -#> `i` Commented code not included -#> `i` A title not to be included +#> `i` a family to include +#> `i` Opens a RStudio project in a new session #> `i` second-level heading in desc #> `i` Details + 2nd level heading #> `i` second heading #> `i` data to index +#> `i` My data #> #> ── `tests/testthat/_outline/roxy-general2.R` Test for roxygen parsing for no error -#> `i` Use 'tests/testthat/_outline/ex-outline-roxy.R' for output testing -#> `i` Commented code not included -#> `i` A title not to be included +#> `i` Use 'tests/testthat/_outline/roxy-general.R' for output testing +#> `i` Title with `_things` +#> `i` a family to include +#> `i` An S3 method not to be include +#> `i` section AA REQUIRED: #> #> ── `tests/testthat/_outline/roxy-section.R` multiple tags + name parsing issue -#> `i` A detail first level-heading to be included -#> `i` A detail first level-heading to be included +#> `i` A title to be included +#> `i` a section: +#> `i` another section: +#> `i` another sectio2n: #> #> ── `tests/testthat/_outline/title.md` The title is the only outline element #> @@ -437,6 +447,7 @@ outline #> `i` file_outline() is a data frame #> `i` pattern works as expected #> `i` file_outline() works well with figure captions +#> `i` file_outline() detects correctly knitr notebooks #> #> ── `tests/testthat/_snaps/proj-list.md` #> `i` proj_file() works @@ -510,6 +521,7 @@ outline #> `i` file_outline() contains function calls #> `i` dir_outline() works with no error #> `i` file_outline() works well with figure captions +#> `i` file_outline() detects correctly knitr notebooks #> #> ── `tests/testthat/test-rename.R` #> `i` Helper files returns the expected input diff --git a/tests/testthat/_outline/knitr-notebook.R b/tests/testthat/_outline/knitr-notebook.R new file mode 100644 index 0000000..77510e2 --- /dev/null +++ b/tests/testthat/_outline/knitr-notebook.R @@ -0,0 +1,18 @@ +#' --- +#' title: "Crop Analysis Q3 2013" +#' author: "John Smith" +#' date: "May 3rd, 2014" +#' --- +#' + +#+ fig.width=5, fig.height=5 +plot(cars) + +#' # A great section +#' +#' Text with [markdown](https://markdown.org) +#' +#' Learn how to [Compile Report from R](https://rmarkdown.rstudio.com/articles_report_from_r_script.html) +#' + +plot(mtcars) diff --git a/tests/testthat/_snaps/outline-criteria.md b/tests/testthat/_snaps/outline-criteria.md index a97cef0..6ee8930 100644 --- a/tests/testthat/_snaps/outline-criteria.md +++ b/tests/testthat/_snaps/outline-criteria.md @@ -6,6 +6,7 @@ o_is_cli_info o_is_commented_code o_is_generic_test + o_is_notebook o_is_roxygen_comment o_is_section_title o_is_tab_plot_title diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index 8534748..e9aa555 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -102,3 +102,13 @@ `i` Dashboard link `i` Dashboard link +# file_outline() detects correctly knitr notebooks + + Code + file_outline(path = test_path("_outline", "knitr-notebook.R")) + Message + + -- `knitr-notebook.R` Crop Analysis Q3 2013 + Output + `i` A great section + diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index 3ead0b1..9b30908 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -1,4 +1,11 @@ # Test individual outline elements ------ +test_that("o_is_notebook() works", { + expect_true(o_is_notebook("#' ---", "file.R", file_ext = "R", line = 1)) + expect_false(o_is_notebook("#' ---", "file.R", file_ext = "qmd", line = 1)) + expect_false(o_is_notebook("#' Fn title", "file.R", file_ext = "qmd", line = 1)) + +}) + test_that("o_is_roxygen_comment() works", { expect_true(o_is_roxygen_comment("#' @param")) expect_equal( diff --git a/tests/testthat/test-outline.R b/tests/testthat/test-outline.R index 14575de..bc7572a 100644 --- a/tests/testthat/test-outline.R +++ b/tests/testthat/test-outline.R @@ -84,3 +84,10 @@ test_that("file_outline() works well with figure captions", { transform = ~ sub(" `[^`]+` ", " `quarto-caps.md` ", .x) ) }) + +test_that("file_outline() detects correctly knitr notebooks", { + expect_snapshot( + file_outline(path = test_path("_outline", "knitr-notebook.R")), + transform = ~ sub(" `[^`]+` ", " `knitr-notebook.R` ", .x) + ) +}) From 33a4d3319440a17a5ad4bf1ee577c9b111c3c23a Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 10:07:24 -0400 Subject: [PATCH 55/71] Sort out uninteresting headings. --- R/outline-criteria.R | 14 +++++++------- R/outline-roxy.R | 3 ++- R/outline.R | 4 ++-- tests/testthat/test-outline-criteria.R | 1 + tests/testthat/test-outline-roxy.R | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index f0e903e..32c0b45 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -95,10 +95,10 @@ o_is_generic_test <- function(x) { # Returns table or plot titles. o_is_tab_plot_title <- function(x) { - generic_title_regex <- paste0( + generic_title_regex <- paste( "Foo|test|Title|TITLE|Subtitle|[eE]xample|x\\.x\\.", "man_get_image_tab|table's|list\\(|bla\"|\", \"|use_.+\\(", - collapse = "|" + sep = "|" ) stringr::str_detect(x, "(? dplyr::mutate(content = dplyr::case_when( # only keep the first line of section, subsection, family and concept tags. + tag %in% c("family", "concept") ~ stringr::str_extract(content, "^(.+)(\n)?", group = 1), # Also remove : from section - tag %in% c("family", "concept", "section", "subsection") ~ stringr::str_extract(content, "^(.+):?(\n)?"), + tag %in% c("section", "subsection") ~ stringr::str_extract(content, "^(.+)\\s?\\:\\s?\n?", group = 1), .default = content )) |> tidyr::separate_longer_delim(content, delim = "\n") diff --git a/R/outline.R b/R/outline.R index 6154e83..249c04f 100644 --- a/R/outline.R +++ b/R/outline.R @@ -354,12 +354,12 @@ exclude_example_files <- function(path) { # TODO for usethis, add inst/templates/ but I may leave them for now.... # styler tests examples may not work.. - regexp_exclude <- paste0( + regexp_exclude <- paste( "vignettes/test/", # test vignettes "tests/(performance-monitor|gt-examples/|testthat/scope-|testthat/assets|testthat/_outline|testthat/testTestWithFailure|testthat/testTest/)", # example files in usethis, pkgdown, reuseme, devtools, etc. "inst/(templates/license-|example-file/)", # license templates in usethis "revdep/", # likely don't need to outline revdep/, use dir_outline() to find something in revdep/ - collapse = "|" + sep = "|" ) fs::path_filter( diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index 9b30908..abc3eb0 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -57,6 +57,7 @@ test_that("o_is_section_title() works", { expect_true(o_is_section_title(" # section 1 ----")) expect_false(o_is_section_title("# TidyTuesday")) expect_false(o_is_section_title("Function ID:", roxy_section = TRUE)) + expect_false(o_is_section_title("#' @section Function ID:", roxy_section = TRUE)) }) # TODO figure out if this is still needed? diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 981ecf7..7d7bbdb 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -19,7 +19,7 @@ test_that("roxy tags are parsed properly + object names are correct", { c("f_to_be_index_in_outline", "topic-name-to-include", "no-topic", "dataset") ) # strip code from roxygen2 tag - expect_contains(res$content, "`First code` to be included:") + expect_contains(res$content, "`First code` to be included") }) test_that("roxy tags don't error", { From d5782e419b1567a9de625ace17d2278f6e387c8e Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 10:18:50 -0400 Subject: [PATCH 56/71] Experimental support for displaying topic. --- R/outline-roxy.R | 7 +-- R/outline.R | 2 + README.md | 76 +++++++++++++++--------------- tests/testthat/test-outline-roxy.R | 4 +- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 5b801c5..3665830 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -33,7 +33,7 @@ extract_roxygen_tag_location <- function(file, tag) { return(object_call) } if (length(object_call) > 1) { - return(object_call[2]) + return(paste0(object_call[2], "()")) } NULL } @@ -48,12 +48,12 @@ extract_roxygen_tag_location <- function(file, tag) { if (!is.null(name_tag[[i]])) { objects[[i]] <- name_tag[[i]] } else { - objects[[i]] <- "no-topic" + objects[[i]] <- NA_character_ } } } if (any(lengths(objects) == 0)) { - # should not happen. I chose "no-topic" instead. + # should not happen. I chose NA instead. cli::cli_abort("Could not resolve object or topic names.") } } @@ -250,6 +250,7 @@ join_roxy_fun <- function(file) { )) |> tidyr::separate_longer_delim(content, delim = "\n") + roxy_parsed1$topic <- dplyr::na_if(roxy_parsed1$topic, "NA") r <- roxy_parsed1 |> dplyr::mutate( n = dplyr::n(), diff --git a/R/outline.R b/R/outline.R index 249c04f..071546d 100644 --- a/R/outline.R +++ b/R/outline.R @@ -556,6 +556,8 @@ display_outline_element <- function(.data, dir_common) { is_todo_fixme ~ stringr::str_extract(outline_el, "(TODO.+)|(FIXME.+)|(WORK.+)"), is_test_name ~ stringr::str_extract(outline_el, "test_that\\(['\"](.+)['\"],\\s?\\{", group = 1), is_cli_info ~ stringr::str_extract(outline_el, "[\"'](.{5,})[\"']") |> stringr::str_remove_all("\""), + # Add related topic if available + tag == "title" & !is.na(topic) ~ paste0(outline_el, " [", topic, "]"), # family or concept! is_tab_or_plot_title & !is.na(tag) ~ outline_el, is_tab_or_plot_title ~ stringr::str_extract(outline_el, "title =[^\"']*[\"']([^\"]{5,})[\"']", group = 1), diff --git a/README.md b/README.md index c97b7f4..e40271d 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 6.22s 6.22s 0.161 86.9MB 0.643 +#> 1 outline <- proj_outline() 4.63s 4.63s 0.216 87MB 0.865 ```
@@ -177,28 +177,28 @@ Example outline ``` r outline #> -#> ── `R/browse-pkg.R` Browse pkgdown site if it exists +#> ── `R/browse-pkg.R` Browse pkgdown site if it exists [browse_pkg()] #> -#> ── `R/case-if-any.R` case-when, but checks for all matches, returns a character +#> ── `R/case-if-any.R` case-when, but checks for all matches, returns a character [case_if_any()] #> #> ── `R/dplyr-plus.R` dplyr extra -#> `i` Count observations by group and compute percentage +#> `i` Count observations by group and compute percentage [count_pct()] #> `i` dplyr extensions -#> `i` Subset rows using their positions +#> `i` Subset rows using their positions [slice_min_max()] #> `i` dplyr extensions -#> `i` Explore all rows in a random group +#> `i` Explore all rows in a random group [slice_group_sample()] #> `i` family dplyr extensions #> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` -#> `i` Keep rows that match one of the conditions +#> `i` Keep rows that match one of the conditions [filter_if_any()] #> `i` with dplyr::filter -#> `i` Elegant wrapper around filter and pull +#> `i` Elegant wrapper around filter and pull [extract_cell_value()] #> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` #> `i` summarise with total -#> `i` Compute a summary for one group with the total included. -#> `i` Transform to NA any of the condition +#> `i` Compute a summary for one group with the total included. [summarise_with_total()] +#> `i` Transform to NA any of the condition [na_if2()] #> #> ── `R/eda-identity.R` dplyr/base identity helpers -------------------- -#> `i` Helpers that return the same value +#> `i` Helpers that return the same value [eda-identity] #> `i` Use cases / advantages #> `i` Caution #> `i` base identity functions @@ -218,19 +218,19 @@ outline #> `i` Scalars #> `i` Vectors #> -#> ── `R/named.R` Helpers that can return a named vector +#> ── `R/named.R` Helpers that can return a named vector [named-base] #> -#> ── `R/open.R` Open a Document in RStudio +#> ── `R/open.R` Open a Document in RStudio [open_rs_doc()] #> `i` FIXME why is this code like this?- `Done✔?` -#> `i` Copy the active document to the same location +#> `i` Copy the active document to the same location [active_rs_doc_copy()] #> `i` document manipulation helpers -#> `i` Delete the active RStudio document safely +#> `i` Delete the active RStudio document safely [active_rs_doc_delete()] #> `i` document manipulation helpers #> `i` TODO structure and summarise information.- `Done✔?` #> `i` FIXME (upstream) the color div doesn't go all the way r-lib/cli#694 ()- `Done✔?` -#> `i` Open Files Pane at current document location +#> `i` Open Files Pane at current document location [active_rs_doc_nav()] #> -#> ── `R/outdated-pkgs.R` Looks for outdated packages +#> ── `R/outdated-pkgs.R` Looks for outdated packages [outdated_pkgs()] #> `i` TODO figure out pad :)- `Done✔?` #> #> ── `R/outline-criteria.R` @@ -249,7 +249,7 @@ outline #> `i` helper for interactive checking #> #> ── `R/outline.R` `proj_outline()` -#> `i` Print interactive outline of file sections +#> `i` Print interactive outline of file sections [outline] #> `i` If `work_only` is set to `TRUE`, the function will only return outline of the `# WORK` comment #> `i` `file_outline()` #> `i` File outline @@ -260,23 +260,23 @@ outline #> `i` FIXME find a way to be as consistent as lightparser, but faster.- `Done✔?` #> `i` TODO Improve performance with vctrs tidyverse/dplyr#6806 ()- `Done✔?` #> -#> ── `R/proj-list.R` Opens a RStudio project in a new session +#> ── `R/proj-list.R` Opens a RStudio project in a new session [proj_switch()] #> `i` project management helpers #> `i` TODO maybe add a max?- `Done✔?` -#> `i` Access the file outline within other project +#> `i` Access the file outline within other project [proj_file()] #> `i` project management helpers #> `i` TODO improve on this message- `Done✔?` -#> `i` Returns a named project list options +#> `i` Returns a named project list options [proj_list()] #> `i` project management helpers #> -#> ── `R/proj-reuseme.R` Interact with different RStudio projects +#> ── `R/proj-reuseme.R` Interact with different RStudio projects [proj-reuseme] #> `i` Setup #> `i` Capabilities. #> `i` project management helpers #> -#> ── `R/quarto-help.R` Show links to Quarto documentation of interest +#> ── `R/quarto-help.R` Show links to Quarto documentation of interest [quarto_help()] #> -#> ── `R/rename.R` Rename an output or a data file and watch for references +#> ── `R/rename.R` Rename an output or a data file and watch for references [rename_files2()] #> `i` Use case #> `i` After here, we start doing some renaming real situations #> `i` Helpers @@ -286,9 +286,9 @@ outline #> `i` FIXME maybe not fail while testing- `Done✔?` #> `i` TODO Check that old file is more recent- `Done✔?` #> -#> ── `R/screenshot.R` Save the current image in clipboard to png in your active directory +#> ── `R/screenshot.R` Save the current image in clipboard to png in your active directory [screenshot()] #> -#> ── `R/todo.R` Add a TODO list by project to a TODO.R file in the base directory +#> ── `R/todo.R` Add a TODO list by project to a TODO.R file in the base directory [use_todo()] #> `i` TODO think about maybe using todo = clipr::read_clip()- `Done✔?` #> `i` TODO nice to have, but would need to extract duplicates- `Done✔?` #> `i` Helpers @@ -366,20 +366,20 @@ outline #> `i` Dashboard link #> #> ── `tests/testthat/_outline/roxy-cli.R` outline -#> `i` Like [base::grep()] but [grepl()] for ANSI strings +#> `i` Like [base::grep()] but [grepl()] for ANSI strings [f2()] #> #> ── `tests/testthat/_outline/roxy-general.R` #> `i` Use 'tests/testthat/_outline/roxy-general2.R' for output testing #> `i` Complete block for exported function with headings -#> `i` A title to be included +#> `i` A title to be included [f_to_be_index_in_outline()] #> `i` A second-level heading in description to be included? #> `i` A detail first level-heading to be included #> `i` A detail second-level heading to be included -#> `i` `First code` to be included: +#> `i` `First code` to be included #> `i` a family to include #> `i` block not to index #> `i` Topic to index -#> `i` A title to be included +#> `i` A title to be included [topic-name-to-include] #> `i` A second-level heading in description to be included? #> `i` A detail first level-heading to be included #> `i` A detail second-level heading to be included @@ -390,20 +390,20 @@ outline #> `i` Details + 2nd level heading #> `i` second heading #> `i` data to index -#> `i` My data +#> `i` My data [dataset] #> #> ── `tests/testthat/_outline/roxy-general2.R` Test for roxygen parsing for no error #> `i` Use 'tests/testthat/_outline/roxy-general.R' for output testing -#> `i` Title with `_things` +#> `i` Title with `_things` [f_to_be_index_in_outline()] #> `i` a family to include -#> `i` An S3 method not to be include -#> `i` section AA REQUIRED: +#> `i` An S3 method not to be include [f_not_to_index.xml()] +#> `i` section AA REQUIRED #> #> ── `tests/testthat/_outline/roxy-section.R` multiple tags + name parsing issue -#> `i` A title to be included -#> `i` a section: -#> `i` another section: -#> `i` another sectio2n: +#> `i` A title to be included [xxx] +#> `i` a section +#> `i` another section +#> `i` another sectio2n #> #> ── `tests/testthat/_outline/title.md` The title is the only outline element #> diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 7d7bbdb..2f4969f 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -16,10 +16,10 @@ test_that("roxy tags are parsed properly + object names are correct", { res_order <- dplyr::arrange(res, line) expect_setequal( res$topic, - c("f_to_be_index_in_outline", "topic-name-to-include", "no-topic", "dataset") + c("f_to_be_index_in_outline", "topic-name-to-include", NA_character_, "dataset") ) # strip code from roxygen2 tag - expect_contains(res$content, "`First code` to be included") + expect_contains(res$content, "First code to be included") }) test_that("roxy tags don't error", { From 7dac4e5ad07424d4f8affa28ba9c765e9b2309a4 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 11:57:52 -0400 Subject: [PATCH 57/71] Fix problem of incorrect dir. --- R/outline-criteria.R | 13 ++++++++++--- R/outline.R | 4 ++-- tests/testthat/_snaps/outline-roxy.md | 10 ++++++++++ tests/testthat/test-outline-roxy.R | 27 +++++++++++++++++++++------ 4 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 tests/testthat/_snaps/outline-roxy.md diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 32c0b45..b09350d 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -162,7 +162,7 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { x$file_ext <- s_file_ext(x$file) x$is_md <- x$file_ext %in% c("qmd", "md", "Rmd", "Rmarkdown") x$is_news <- x$is_md & grepl("NEWS.md", x$file, fixed = TRUE) - x$is_test_file <- grepl("tests/testthat", x$file, fixed = TRUE) + x$is_test_file <- grepl("tests/testthat/test", x$file, fixed = TRUE) x$is_notebook <- o_is_notebook(x = x$content, x$file, x$file_ext, x$line) x$is_roxygen_comment <- o_is_roxygen_comment(x$content, x$file_ext, x$is_notebook) x$content[x$is_notebook] <- sub("^#'\\s?", "", x$content[x$is_notebook]) @@ -173,7 +173,7 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { !isFALSE(getOption("reuseme.roxy_parse", default = TRUE)) && # will not parse if option is set to FALSE any(x$is_roxygen_comment) if (should_parse_roxy_comments) { - if (!is.null(dir_common)) { + if (interactive() && !is.null(dir_common)) { withr::local_dir(dir_common) } rlang::check_installed(c("roxygen2", "tidyr"), "to create roxygen2 comments outline.") @@ -227,7 +227,7 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { is_object_title = FALSE, tag = NA_character_, topic = NA_character_, - is_todo_fixme = !exclude_todos & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext) & !is_snap_file, + is_todo_fixme = !exclude_todos & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext, is_notebook) & !is_snap_file, n_leading_hash = nchar(stringr::str_extract(content, "\\#+(?!\\|)")), # don't count hashpipe n_leading_hash = dplyr::coalesce(n_leading_hash, 0), # Make sure everything is second level in revdep/. @@ -253,6 +253,13 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { define_outline_criteria_roxy <- function(x) { # TODO merge with define_outline_criteria + if (rlang::is_atomic(x)) { + # in tests, not interactively, got something bizzare + cli::cli_warn("x is {.obj_type_friendly {x}}.") + if (length(x) == 0) { + return(NULL) + } + } x$is_md <- x$tag %in% c("subsection", "details", "description", "section") # short topics are likely placeholders. x$is_object_title <- x$tag == "title" & nchar(x$content) > 4 diff --git a/R/outline.R b/R/outline.R index 071546d..f2db9ee 100644 --- a/R/outline.R +++ b/R/outline.R @@ -92,7 +92,7 @@ file_outline <- function(pattern = NULL, print_todo = deprecated()) { # To contribute to this function, take a look at .github/CONTRIBUTING.md - if (length(path) == 1L && interactive() && rstudioapi::isAvailable()) { + if (length(path) == 1L && rlang::is_interactive() && rstudioapi::isAvailable()) { is_active_doc <- identical(path, active_rs_doc()) } else { is_active_doc <- FALSE @@ -342,7 +342,7 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te fs::dir_tree( path = dir, - regexp = "R/.+|qmd|Rmd|_files|~\\$|*.Rd|_snaps|testthat.R|Rmarkdown|docs/", + regexp = "R/.+|qmd|Rmd|_files|~\\$|*.Rd|_snaps|tests/testthat.R|Rmarkdown|docs/", recurse = recurse, invert = TRUE ) diff --git a/tests/testthat/_snaps/outline-roxy.md b/tests/testthat/_snaps/outline-roxy.md new file mode 100644 index 0000000..68f53de --- /dev/null +++ b/tests/testthat/_snaps/outline-roxy.md @@ -0,0 +1,10 @@ +# cli escaping goes well + + Code + file_outline(path = file_to_map) + Message + + -- `tests/testthat/_outline/roxy-cli.R` outline + Output + `i` Like [base::grep()] but [grepl()] for ANSI strings [f2()] + diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 2f4969f..5a50eae 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -16,10 +16,10 @@ test_that("roxy tags are parsed properly + object names are correct", { res_order <- dplyr::arrange(res, line) expect_setequal( res$topic, - c("f_to_be_index_in_outline", "topic-name-to-include", NA_character_, "dataset") + c("f_to_be_index_in_outline()", "topic-name-to-include", NA_character_, "dataset") ) # strip code from roxygen2 tag - expect_contains(res$content, "First code to be included") + expect_contains(res$content, "`First code` to be included") }) test_that("roxy tags don't error", { @@ -36,10 +36,25 @@ test_that("multiple roxy tags don't error.", { expect_no_error(join_roxy_fun(example_parsed)) }) -test_that("cli escaping goes well...", { - skip("Not ready :(") - file_to_map <- testthat::test_path("_outline", "roxy-cli.R") - expect_no_error(file_outline(path = file_to_map)) +test_that("testing and not should not give different results", { + skip_on_cran() + withr::local_options("reuseme.roxy_parse" =TRUE) + expect_no_warning( + file_outline(path = file_to_map, dir_common = "tests/testthat/_outline") + ) + expect_equal( + nrow(file_outline(path = testthat::test_path("_outline", "roxy-cli.R"))), + nrow(file_outline(path = normalizePath(testthat::test_path("_outline", "roxy-cli.R")))) + ) +}) + +test_that("cli escaping goes well", { + rlang::local_interactive(FALSE) + # The fact that I need to do this is bizzare. + file_to_map <- normalizePath(testthat::test_path("_outline", "roxy-cli.R")) + expect_snapshot( + file_outline(path = file_to_map) + ) names(file_to_map) <- file_to_map example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) expect_no_error(join_roxy_fun(example_parsed)) From 9d07e3916903be9bde65045e988bc347705d2979 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 12:57:41 -0400 Subject: [PATCH 58/71] Use mocking for mocking RStudio + change dir --- R/open.R | 10 +++++----- R/outline-criteria.R | 8 +++++++- R/outline.R | 6 +++--- R/screenshot.R | 2 +- R/todo.R | 2 +- R/utils-proj.R | 5 +++++ tests/testthat/test-outline-roxy.R | 8 +++++--- 7 files changed, 27 insertions(+), 14 deletions(-) diff --git a/R/open.R b/R/open.R index c3e4bf7..6d90d1e 100644 --- a/R/open.R +++ b/R/open.R @@ -39,10 +39,10 @@ open_rs_doc <- function(path, line = -1L, col = -1L, move_cursor = TRUE) { #' @name open_rs_doc #' @export active_rs_doc <- function() { - if (!interactive() && !rstudioapi::isAvailable()) { + if (!interactive() && !is_rstudio()) { return("Non-existing doc") } - if (!rstudioapi::isAvailable()) { + if (!is_rstudio()) { cli::cli_abort("Not in RStudio.") } unsaved_doc <- tryCatch(rstudioapi::documentPath(), error = function(e) TRUE) @@ -125,7 +125,7 @@ active_rs_doc_copy <- function(new = NULL, ..., old = NULL) { #' @examplesIf FALSE #' active_rs_doc_delete() active_rs_doc_delete <- function() { - if (!rlang::is_interactive() || !rstudioapi::isAvailable()) { + if (!rlang::is_interactive() || !is_rstudio()) { cli::cli_abort(c("Can't delete files in non-interactive sessions.")) } doc <- active_rs_doc() @@ -141,7 +141,7 @@ active_rs_doc_delete <- function() { if (fs::is_dir(elems$full_path)) { cli::cli_abort("Must be a file", .internal = TRUE) } - if (interactive() && rstudioapi::isAvailable()) { + if (interactive() && is_rstudio()) { rstudioapi::documentSave() } cli::cli_inform(c( @@ -375,7 +375,7 @@ normalize_proj_and_path <- function(path, call = caller_env()) { #' @returns NULL, called for its side effects. #' @export active_rs_doc_nav <- function(path = active_rs_doc()) { - if (!rstudioapi::isAvailable() || !interactive()) { + if (!is_rstudio() || !interactive()) { cli::cli_abort("Must use in RStudio interactive sessions.") } if (is.null(path)) { diff --git a/R/outline-criteria.R b/R/outline-criteria.R index b09350d..0beebb1 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -173,8 +173,14 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { !isFALSE(getOption("reuseme.roxy_parse", default = TRUE)) && # will not parse if option is set to FALSE any(x$is_roxygen_comment) if (should_parse_roxy_comments) { - if (interactive() && !is.null(dir_common)) { + # doing this created problems in tests? + if (interactive() && !is.null(dir_common) && is_rstudio()) { + # The idea is that roxygen2 may be better at getting objects if directory is changed. + # but don't bother doing this outside RStudio for now... withr::local_dir(dir_common) + if (!fs::file_exists(x$file[1])) { + cli::cli_abort("Wrong dir done. file = {.file {x$file[1]}. dir = {.path {dir_common}}", .internal = TRUE) + } } rlang::check_installed(c("roxygen2", "tidyr"), "to create roxygen2 comments outline.") files_with_roxy_comments <- unique(x[x$is_roxygen_comment, "file", drop = TRUE]) diff --git a/R/outline.R b/R/outline.R index f2db9ee..e655ac0 100644 --- a/R/outline.R +++ b/R/outline.R @@ -92,7 +92,7 @@ file_outline <- function(pattern = NULL, print_todo = deprecated()) { # To contribute to this function, take a look at .github/CONTRIBUTING.md - if (length(path) == 1L && rlang::is_interactive() && rstudioapi::isAvailable()) { + if (length(path) == 1L && rlang::is_interactive() && is_rstudio()) { is_active_doc <- identical(path, active_rs_doc()) } else { is_active_doc <- FALSE @@ -718,13 +718,13 @@ define_important_element <- function(.data) { } construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { - rs_avail_file_link <- rstudioapi::isAvailable("2023.09.0.375") # better handling after + rs_avail_file_link <- is_rstudio("2023.09.0.375") # better handling after .data <- define_important_element(.data) if (is.null(dir_common) || !nzchar(dir_common)) { dir_common <- "Don't remove anything if not null" } - .data$rs_version <- ifelse(!rstudioapi::isAvailable("2023.12.0.274") && rstudioapi::isAvailable(), ".", "") + .data$rs_version <- ifelse(!is_rstudio("2023.12.0.274") && is_rstudio(), ".", "") .data$has_inline_markup <- dplyr::coalesce(stringr::str_detect(.data$outline_el, "\\{|\\}"), FALSE) .data$is_saved_doc <- is_saved_doc .data <- dplyr::mutate( diff --git a/R/screenshot.R b/R/screenshot.R index c4c8dbc..3650522 100644 --- a/R/screenshot.R +++ b/R/screenshot.R @@ -49,7 +49,7 @@ screenshot <- function(file = NULL, proj = proj_get(), dir = NULL) { proj_path <- proj_list(proj) - if (!rstudioapi::isAvailable()) { + if (!is_rstudio()) { cli::cli_warn("This feature may not work as excepted outside RStudio.") } diff --git a/R/todo.R b/R/todo.R index 1ffdfb8..37c84ea 100644 --- a/R/todo.R +++ b/R/todo.R @@ -82,7 +82,7 @@ complete_todo <- function(line, file, regexp, rm_line = NULL) { check_number_whole(line) line_original <- line # to defer warning. - if (interactive() && rstudioapi::isAvailable()) { + if (interactive() && is_rstudio()) { rstudioapi::documentSaveAll() } warn_change_of_line <- FALSE diff --git a/R/utils-proj.R b/R/utils-proj.R index b32b746..cd75eb6 100644 --- a/R/utils-proj.R +++ b/R/utils-proj.R @@ -112,3 +112,8 @@ get_active_qmd_post <- function(base_path = proj_get(), error_call = caller_env( fs::path_dir(relative_path) } + +# to mock. +is_rstudio <- function(v) { + rstudioapi::isAvailable(version_needed = v) +} diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 5a50eae..2980175 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -36,11 +36,13 @@ test_that("multiple roxy tags don't error.", { expect_no_error(join_roxy_fun(example_parsed)) }) -test_that("testing and not should not give different results", { +test_that("file_outline() works outside RStudio)", { skip_on_cran() - withr::local_options("reuseme.roxy_parse" =TRUE) + local_mocked_bindings( + is_rstudio = function(...) FALSE + ) expect_no_warning( - file_outline(path = file_to_map, dir_common = "tests/testthat/_outline") + file_outline(path = testthat::test_path("_outline", "roxy-cli.R")) ) expect_equal( nrow(file_outline(path = testthat::test_path("_outline", "roxy-cli.R"))), From 72c3d453bd87e74a6aaaee453b75306c7fc4151c Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 13:02:40 -0400 Subject: [PATCH 59/71] Fix snap --- R/outline-criteria.R | 2 +- tests/testthat/_snaps/outline-roxy.md | 4 ++-- tests/testthat/test-outline-roxy.R | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 0beebb1..55c5519 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -189,7 +189,7 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { # TRICK purrr::safely creates an error object, while possible is better. # Suppresss roxygen2 message, suppress callr output, suppress asciicast warnings. invisible( - capture.output( + utils::capture.output( parsed_files <- purrr::map( files_with_roxy_comments, purrr::possibly(\(x) roxygen2::parse_file(x, env = NULL)))) diff --git a/tests/testthat/_snaps/outline-roxy.md b/tests/testthat/_snaps/outline-roxy.md index 68f53de..cdf86df 100644 --- a/tests/testthat/_snaps/outline-roxy.md +++ b/tests/testthat/_snaps/outline-roxy.md @@ -1,10 +1,10 @@ -# cli escaping goes well +# cli escaping goes well in roxy comments Code file_outline(path = file_to_map) Message - -- `tests/testthat/_outline/roxy-cli.R` outline + -- `roxy-cli.R` outline Output `i` Like [base::grep()] but [grepl()] for ANSI strings [f2()] diff --git a/tests/testthat/test-outline-roxy.R b/tests/testthat/test-outline-roxy.R index 2980175..db4d690 100644 --- a/tests/testthat/test-outline-roxy.R +++ b/tests/testthat/test-outline-roxy.R @@ -50,12 +50,14 @@ test_that("file_outline() works outside RStudio)", { ) }) -test_that("cli escaping goes well", { +test_that("cli escaping goes well in roxy comments", { rlang::local_interactive(FALSE) # The fact that I need to do this is bizzare. - file_to_map <- normalizePath(testthat::test_path("_outline", "roxy-cli.R")) + file_to_map <- testthat::test_path("_outline", "roxy-cli.R") expect_snapshot( - file_outline(path = file_to_map) + file_outline(path = file_to_map), + transform = ~ sub(" `[^`]+` ", " `roxy-cli.R` ", .x) + ) names(file_to_map) <- file_to_map example_parsed <- purrr::map(file_to_map, \(x) roxygen2::parse_file(x, env = NULL)) From 9e00edcd3af8de31798fc3425bb9c1d604482f62 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 14:01:07 -0400 Subject: [PATCH 60/71] Tweaks based on integration testing, avoid parsing markdown tables, exclude some directories, --- .github/CONTRIBUTING.md | 2 +- R/outline-criteria.R | 6 ++++-- R/outline.R | 15 ++++++++++----- R/utils-proj.R | 2 +- README.md | 11 ++++++++--- TODO.R | 1 + tests/testthat/_outline/roxy-general2.R | 2 +- tests/testthat/test-outline-criteria.R | 1 + 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 301d95c..b6f9740 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -47,7 +47,7 @@ See our guide on [how to create a great issue](https://code-review.tidyverse.org * `define_outline_criteria()` if an item shows as outline, but seems like a false positive, -* `keep_outline_element()`: if an element is **missing** from outline, you can add the keyword "REQUIRED" to get an object for debugging. +* `keep_outline_element()`: if an element is **missing** from outline, you can add the keyword "REQUIRED ELEMENT" to get an object for debugging. * `define_important_element()` if an element is important [^1] diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 55c5519..4eecc65 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -101,7 +101,7 @@ o_is_tab_plot_title <- function(x) { sep = "|" ) - stringr::str_detect(x, "(? 0) { # We use `format()` in case a variable is used to name the caption. diff --git a/R/utils-proj.R b/R/utils-proj.R index cd75eb6..945f6d3 100644 --- a/R/utils-proj.R +++ b/R/utils-proj.R @@ -114,6 +114,6 @@ get_active_qmd_post <- function(base_path = proj_get(), error_call = caller_env( } # to mock. -is_rstudio <- function(v) { +is_rstudio <- function(v = NULL) { rstudioapi::isAvailable(version_needed = v) } diff --git a/README.md b/README.md index e40271d..7b33d45 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 4.63s 4.63s 0.216 87MB 0.865 +#> 1 outline <- proj_outline() 4.61s 4.61s 0.217 87.3MB 0.868 ```
@@ -329,6 +329,7 @@ outline #> `i` TODO add_to_tricks(). when detecting TRICK like complete todo, but not …- `Done✔?` #> `i` TODO outline just create an `exclude` argument that will take an opti…- `Done✔?` #> `i` TODO outline remove snaps from outline and add a link in the test fil…- `Done✔?` +#> `i` TODO outline family should be displayed differently..- `Done✔?` #> #> ── `inst/example-file/outline-script.R` Example for `file_outline()` #> `i` Load packages @@ -397,7 +398,7 @@ outline #> `i` Title with `_things` [f_to_be_index_in_outline()] #> `i` a family to include #> `i` An S3 method not to be include [f_not_to_index.xml()] -#> `i` section AA REQUIRED +#> `i` section AA REQUIRED ELEMENT #> #> ── `tests/testthat/_outline/roxy-section.R` multiple tags + name parsing issue #> `i` A title to be included [xxx] @@ -441,6 +442,9 @@ outline #> ── `tests/testthat/_snaps/outline-criteria.md` #> `i` No outline criteria are untested #> +#> ── `tests/testthat/_snaps/outline-roxy.md` +#> `i` cli escaping goes well in roxy comments +#> #> ── `tests/testthat/_snaps/outline.md` #> `i` file_outline() works #> `i` alpha and work_only arguments work @@ -511,7 +515,8 @@ outline #> `i` roxy tags are parsed properly + object names are correct #> `i` roxy tags don't error #> `i` multiple roxy tags don't error. -#> `i` cli escaping goes well... +#> `i` file_outline() works outside RStudio) +#> `i` cli escaping goes well in roxy comments #> #> ── `tests/testthat/test-outline.R` #> `i` alpha and work_only arguments work diff --git a/TODO.R b/TODO.R index 44d60a0..892922b 100644 --- a/TODO.R +++ b/TODO.R @@ -29,3 +29,4 @@ # TODO add_to_tricks(). when detecting TRICK like complete todo, but not remove line. requires a scheme. moves the item to tricks.md at the correct place. (copy to clipboard is probably enough) # TODO [outline] just create an `exclude` argument that will take an option? (exclude can be files or expressionsm, or elements.) # TODO [outline] remove snaps from outline and add a link in the test file instead? +# TODO [outline] family should be displayed differently.. diff --git a/tests/testthat/_outline/roxy-general2.R b/tests/testthat/_outline/roxy-general2.R index 40f4631..f998596 100644 --- a/tests/testthat/_outline/roxy-general2.R +++ b/tests/testthat/_outline/roxy-general2.R @@ -41,7 +41,7 @@ NULL #' #' desc #' -#' @section section AA REQUIRED: +#' @section section AA REQUIRED ELEMENT: #' #' * DO this #' diff --git a/tests/testthat/test-outline-criteria.R b/tests/testthat/test-outline-criteria.R index abc3eb0..9052a5d 100644 --- a/tests/testthat/test-outline-criteria.R +++ b/tests/testthat/test-outline-criteria.R @@ -50,6 +50,7 @@ test_that("o_is_tab_plot_title() works", { expect_false(o_is_tab_plot_title("title = '', symbol = 'x'")) expect_false(o_is_tab_plot_title('title = ".+", " +\\(",")')) + expect_false(o_is_tab_plot_title("dc:title = 'aaaaaaa'")) }) test_that("o_is_section_title() works", { From e45b749cdbfec5a8945613f6b7d05c347a542f41 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 15:07:08 -0400 Subject: [PATCH 61/71] More integration adjustments and addition of examples. --- R/outline-criteria.R | 4 ++-- R/outline.R | 14 ++++++++------ TODO.R | 2 ++ tests/testthat/_outline/my-analysis.R | 2 +- tests/testthat/_outline/quarto-caps.md | 18 ++++++++++++++++-- tests/testthat/_snaps/outline.md | 8 ++++---- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 4eecc65..ee93e37 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -219,10 +219,10 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { is_cli_info = o_is_cli_info(content, is_snap_file, file), # TODO long enough to be meanignful? # doc title cannot be after line 50 of a document. - is_doc_title = stringr::str_detect(content, "(? stringr::str_remove_all("\""), # Add related topic if available @@ -699,7 +701,7 @@ extract_object_captions <- function(file) { # tidyverse/purrr#1081 if (length(dat) > 0) { # We use `format()` in case a variable is used to name the caption. - tryCatch(caps <- c(caps, dat |> purrr::map_chr(\(x) format(x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% "USELESS THING"))), error = function(e) { + tryCatch(caps <- c(caps, dat |> purrr::map_chr(\(x) format(x[["fig-cap"]] %||% x[["tbl-cap"]] %||% x[["title"]] %||% x[["fig.cap"]] %||% x[["tbl.cap"]] %||% x[["tab.cap"]] %||% x[["cap"]] %||% "USELESS THING"))), error = function(e) { cli::cli_abort("Error in {.file {unique_file[i]}}", parent = e) }) } diff --git a/TODO.R b/TODO.R index 892922b..a90002c 100644 --- a/TODO.R +++ b/TODO.R @@ -30,3 +30,5 @@ # TODO [outline] just create an `exclude` argument that will take an option? (exclude can be files or expressionsm, or elements.) # TODO [outline] remove snaps from outline and add a link in the test file instead? # TODO [outline] family should be displayed differently.. +# TODO [outline] find a way to make print bookmarks.. +# TODO [outline] escape some content in headings see {.file tests/testthat/_outline/quarto-caps.md} for examples. diff --git a/tests/testthat/_outline/my-analysis.R b/tests/testthat/_outline/my-analysis.R index ae07114..291c6e9 100644 --- a/tests/testthat/_outline/my-analysis.R +++ b/tests/testthat/_outline/my-analysis.R @@ -1,4 +1,4 @@ -# Analyse my streets --------------- +# Analyse my {streets} --------------- ## Read my [streets](https://https://en.wikipedia.org/wiki/Street_art) data ------- my_streets <- read.csv("data/my-streets.csv", silent = TRUE) new_dat <- my_streets |> diff --git a/tests/testthat/_outline/quarto-caps.md b/tests/testthat/_outline/quarto-caps.md index cddf912..a04e8c5 100644 --- a/tests/testthat/_outline/quarto-caps.md +++ b/tests/testthat/_outline/quarto-caps.md @@ -8,7 +8,9 @@ title: title #| with more details ``` -## Heading +## Heading + +Remove the html and if possible, just print an emoji? maybe even check font-awesome. the emoji package could be used? ```{r} #| label: another-lab @@ -17,7 +19,9 @@ title: title #| with more details ``` -## Heading2 +## Heading2\_done + +Remove the backslash ```{r} #| title: Dashboard link @@ -26,3 +30,13 @@ title: title ```{r, fig.cap ="Dashboard link"} caption ``` + +```{r, fig.alt ="the caption"} +caption +``` + +```{r} +#| label: another-lab +#| fig-alt: | +#| The caption and title are left justified, the legend is inside of the plot +``` diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index e9aa555..bb6cd66 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -25,7 +25,7 @@ `i` TODO this is an item- `Donev?` Message - -- `my-analysis.R` Analyse my streets + -- `my-analysis.R` Analyse my {streets} Output `i` TODO Create a new version- `Donev?` `i` Read my streets () data @@ -43,7 +43,7 @@ file_outline("street", my_test_file, alpha = TRUE, work_only = FALSE) Message - -- `outline/my-analysis.R` Analyse my streets + -- `outline/my-analysis.R` Analyse my {streets} Output `i` Read my streets () data `i` Write my streets @@ -96,9 +96,9 @@ -- `quarto-caps.md` title Output `i` A long ggplot2 title with more details - `i` Heading + `i` Heading `i` A long ggplot2 title with more details - `i` Heading2 + `i` Heading2\_done `i` Dashboard link `i` Dashboard link From 1429c02af2d6c8a161ed858deeaa7238e0cc5a9b Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 31 May 2024 15:38:07 -0400 Subject: [PATCH 62/71] Address some comments. --- R/outline.R | 5 ++-- R/rename.R | 2 +- R/todo.R | 4 +-- README.Rmd | 11 +++---- README.md | 78 ++++++++++++++++++++++++++++---------------------- man/outline.Rd | 2 +- 6 files changed, 56 insertions(+), 46 deletions(-) diff --git a/R/outline.R b/R/outline.R index 1b5b60a..cf7165b 100644 --- a/R/outline.R +++ b/R/outline.R @@ -13,7 +13,7 @@ #' * `TODO` items #' * Parse cli hyperlinks #' * Plot or table titles -#' * FIgures caption in Quarto documents +#' * Figures caption in Quarto documents #' * test names #' * Indicator of recent modification #' * Colored output for @@ -358,11 +358,12 @@ exclude_example_files <- function(path) { "vignettes/test/", # test vignettes "LICENSE.md", # avoid indexing this. "tests/(performance-monitor|gt-examples/|testthat/scope-|testthat/assets|testthat/_outline|testthat/testTestWithFailure|testthat/testTest/|testthat/test-parallel/|testthat/test-list-reporter/)", # example files in usethis, pkgdown, reuseme, devtools, etc. - "inst/(templates/license-|example-file/|examples/rmd/)", # license templates in usethis + "inst/((rmarkdown/)?templates/|example-file/|examples/rmd/|tutorials/)", # license templates in usethis "revdep/", # likely don't need to outline revdep/, use dir_outline() to find something in revdep/ "themes/hugo-theme-console/", # protect blogdown "vignettes/.+\\.R$", # generated files "RcppExports.R", + "pkgdown/assets", sep = "|" ) diff --git a/R/rename.R b/R/rename.R index 7348af2..1352692 100644 --- a/R/rename.R +++ b/R/rename.R @@ -95,7 +95,7 @@ rename_files2 <- function(old, # remove project name from conflicts. related_files <- stringr::str_subset(related_files, proj_name, negate = TRUE) if (length(related_files) > 0) { - # maybe would need to normalize path. + # TODO verify if path should be normalized. cli::cli_warn(c( "Other files have a similar pattern", "See {.file {related_files}}", diff --git a/R/todo.R b/R/todo.R index 37c84ea..5d45f26 100644 --- a/R/todo.R +++ b/R/todo.R @@ -126,8 +126,8 @@ complete_todo <- function(line, file, regexp, rm_line = NULL) { if (warn_change_of_line) { cli::cli_warn(c( x = "Could not find {.arg regexp} as expected", - "Could not find {.val {regexp}} at line {line_original}.", - i = "Has the file content changed since you ran this code?", + "The expected pattern {.val {regexp}} was not found at line {line_original}.", + i = "Please verify if the file content has changed or if the pattern needs adjustment.", # needs qty for cli pluralization, but no printing "`regexp` was detected in {cli::qty(length(regexp_detection))} line{?s} {regexp_detection}." )) diff --git a/README.Rmd b/README.Rmd index 87b812b..637b76a 100644 --- a/README.Rmd +++ b/README.Rmd @@ -25,6 +25,8 @@ knitr::opts_chunk$set( The goal of reuseme is to provide utility functions for project management across RStudio projects. +Sometimes, managing multiple projects can be challenging. reuseme also aims to simplify project management on Windows. + Sometimes, you have to manage multiple things at once, but don't have the time to do edits. You may need to switch quickly to a project, add things or browse a certain file if you have some replications across projects. Sometimes, it is hard to do that. @@ -49,9 +51,8 @@ reuseme is adapted for a standard workflow, recommended in (find resources) - Anyone working in RStudio (recent version for hyperlink support) - Work with RStudio projects - Your RStudio projects are organized in a centralized location on your computer -- Your RStudio projects are Version controlled with git (optional, but recommended to avoid surprises! No need to be hosted on repositories like GitLab or GitHub) -- You are working on Windows (macOS is supported, but some things were designed on Windows) -- You use machine and human readable paths (i.e. no spaces, special characters) (Tip: don't hesitate to rename your files, it can take away the pain in the long run! +- Your RStudio projects are Version controlled with git (optional, but recommended for avoiding surprises! No need to be hosted on repositories like GitLab or GitHub) +- You use machine and human-readable paths (i.e. no spaces, special characters) (Tip: don't hesitate to rename your files (`reuseme::rename_files2()`), your future self will thank you! To take advantage of reuseme, it is highly recommended to set the following option in your `.Rprofile` @@ -59,7 +60,7 @@ To take advantage of reuseme, it is highly recommended to set the following opti options(reuseme.reposdir = c("~/rrr", "any-other-directories-that-contain-rstudio-projects")) ``` -This will enable functions like `proj_switch()`, `proj_list()`, `use_todo()` to be optimized. +This will enable functions like `proj_switch()`, `proj_list()`, `reuseme::use_todo()` to be optimized. ## Example @@ -95,7 +96,7 @@ With reuseme, just use the project name! +================================================================================+==================================================================================+======================================================================================================================================+ | Switch to project "cool-project" | `proj_switch(proj = "cool-project")` | `proj_activate(path = "C:/users/long/path/to/cool-project")` | +--------------------------------------------------------------------------------+----------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+ -| Write a TODO item in project "cooler-project", while working in "cool-project" | `use_todo(todo = "I need to do this ASAP as possible", proj = "cooler-project")` | `usethis::write_union(path = "C:/Users/I/do/not/want/to/type/cooler-project/TODO.R", lines = "I need to do this ASAP as possible.")` | +| Write a TODO item in project "cooler-project", while working in "cool-project" | `reuseme::use_todo(todo = "I need to do this ASAP as possible", proj = "cooler-project")` | `usethis::write_union(path = "C:/Users/I/do/not/want/to/type/cooler-project/TODO.R", lines = "I need to do this ASAP as possible.")` | +--------------------------------------------------------------------------------+----------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+ | Open pkgdown site link and see vignettes | 1. \`browse_pkg("usethis") | 1. `browse_package("usethis")` | | | 2. Click on the hyperlinks that correspond to your query | 2. Type the correct number that corresponds | diff --git a/README.md b/README.md index 7b33d45..24180d9 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,15 @@ coverage](https://codecov.io/gh/olivroy/reuseme/branch/main/graph/badge.svg)](ht The goal of reuseme is to provide utility functions for project -management across RStudio projects. Sometimes, you have to manage -multiple things at once, but don’t have the time to do edits. You may -need to switch quickly to a project, add things or browse a certain file -if you have some replications across projects. Sometimes, it is hard to -do that. reuseme also aims to help me overcome things I don’t like on -Windows. +management across RStudio projects. Sometimes, managing multiple +projects can be challenging. reuseme also aims to simplify project +management on Windows. + +Sometimes, you have to manage multiple things at once, but don’t have +the time to do edits. You may need to switch quickly to a project, add +things or browse a certain file if you have some replications across +projects. Sometimes, it is hard to do that. reuseme also aims to help me +overcome things I don’t like on Windows. ## Installation @@ -45,13 +48,11 @@ resources) - Your RStudio projects are organized in a centralized location on your computer - Your RStudio projects are Version controlled with git (optional, but - recommended to avoid surprises! No need to be hosted on repositories - like GitLab or GitHub) -- You are working on Windows (macOS is supported, but some things were - designed on Windows) -- You use machine and human readable paths (i.e. no spaces, special - characters) (Tip: don’t hesitate to rename your files, it can take - away the pain in the long run! + recommended for avoiding surprises! No need to be hosted on + repositories like GitLab or GitHub) +- You use machine and human-readable paths (i.e. no spaces, special + characters) (Tip: don’t hesitate to rename your files + (`reuseme::rename_files2()`), your future self will thank you! To take advantage of reuseme, it is highly recommended to set the following option in your `.Rprofile` @@ -61,7 +62,7 @@ options(reuseme.reposdir = c("~/rrr", "any-other-directories-that-contain-rstudi ``` This will enable functions like `proj_switch()`, `proj_list()`, -`use_todo()` to be optimized. +`reuseme::use_todo()` to be optimized. ## Example @@ -103,33 +104,36 @@ If you want to work across projects with [usethis](usethis.r-lib.org), you need to provide the full path to a project. With reuseme, just use the project name! - +
---++++ - - - - - - - - - - + + + + + + + - - + - + +
usethis vs reuseme
Workflowreusemeusethis
Switch to project “cool-project”proj_switch(proj = "cool-project")proj_activate(path = "C:/users/long/path/to/cool-project")

Workflow

+

Switch to project “cool-project”

reuseme

+

proj_switch(proj = "cool-project")

usethis

+

proj_activate(path = "C:/users/long/path/to/cool-project")

Write a TODO item in project “cooler-project”, while working in “cool-project”use_todo(todo = "I need to do this ASAP as possible", proj = "cooler-project")usethis::write_union(path = "C:/Users/I/do/not/want/to/type/cooler-project/TODO.R", lines = "I need to do this ASAP as possible.")reuseme::use_todo(todo = "I need to do this ASAP as possible", proj = "cooler-project") +| +usethis::write_union(path = "C:/Users/I/do/not/want/to/type/cooler-project/TODO.R", lines = "I need to do this ASAP as possible.")
Open pkgdown site link and see vignettes
  1. `browse_pkg(“usethis”)
  2. @@ -141,6 +145,7 @@ the project name!
  3. browseVignettes("usethis")
  4. Open it
@@ -165,7 +170,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 4.61s 4.61s 0.217 87.3MB 0.868 +#> 1 outline <- proj_outline() 7.17s 7.17s 0.139 85.1MB 0.558 ```
@@ -279,6 +284,7 @@ outline #> ── `R/rename.R` Rename an output or a data file and watch for references [rename_files2()] #> `i` Use case #> `i` After here, we start doing some renaming real situations +#> `i` TODO verify if path should be normalized.- `Done✔?` #> `i` Helpers #> `i` helpers for computing scope of renaming #> `i` TODO measure of string proximity- `Done✔?` @@ -330,6 +336,8 @@ outline #> `i` TODO outline just create an `exclude` argument that will take an opti…- `Done✔?` #> `i` TODO outline remove snaps from outline and add a link in the test fil…- `Done✔?` #> `i` TODO outline family should be displayed differently..- `Done✔?` +#> `i` TODO outline find a way to make print bookmarks..- `Done✔?` +#> `i` TODO outline escape some content in headings see 'tests/testthat/_outline/quarto-caps.md' for examples.- `Done✔?` #> #> ── `inst/example-file/outline-script.R` Example for `file_outline()` #> `i` Load packages @@ -340,7 +348,7 @@ outline #> ── `tests/testthat/_outline/knitr-notebook.R` Crop Analysis Q3 2013 #> `i` A great section #> -#> ── `tests/testthat/_outline/my-analysis.R` Analyse my streets +#> ── `tests/testthat/_outline/my-analysis.R` Analyse my {streets} #> `i` Read my streets () data #> `i` data wrangling #> `i` Write my streets @@ -360,9 +368,9 @@ outline #> #> ── `tests/testthat/_outline/quarto-caps.md` title #> `i` A long ggplot2 title with more details -#> `i` Heading +#> `i` Heading #> `i` A long ggplot2 title with more details -#> `i` Heading2 +#> `i` Heading2\_done #> `i` Dashboard link #> `i` Dashboard link #> diff --git a/man/outline.Rd b/man/outline.Rd index 9772a78..48bb3f7 100644 --- a/man/outline.Rd +++ b/man/outline.Rd @@ -91,7 +91,7 @@ Outline elements include \item \code{TODO} items \item Parse cli hyperlinks \item Plot or table titles -\item FIgures caption in Quarto documents +\item Figures caption in Quarto documents \item test names \item Indicator of recent modification \item Colored output for From cdd0cc9ed9c95257b9886628084f1d44d1f01f41 Mon Sep 17 00:00:00 2001 From: olivroy Date: Sun, 2 Jun 2024 13:55:23 -0400 Subject: [PATCH 63/71] Make sure is_doc_title doesn't interleave with , prefer is_object_title to is_doc_title. Remove code blocks from outline in roxy cpmmms --- R/outline-criteria.R | 6 +++++- R/outline-roxy.R | 2 ++ R/outline.R | 11 ++++++----- README.md | 5 ++--- tests/testthat/_outline/roxy-general2.R | 9 +++++++++ 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index ee93e37..796fe5b 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -256,6 +256,8 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { content != "NULL" ) res <- dplyr::arrange(res, .data$file, .data$line) + #res$is_object_title[res$is_doc_title] <- FALSE + res } @@ -292,7 +294,9 @@ define_outline_criteria_roxy <- function(x) { x$is_cross_ref <- FALSE x$is_function_def <- FALSE x$is_todo_fixme <- FALSE - x$is_doc_title <- x$line == 1 & x$tag == "title" + x$is_notebook <- FALSE + x$is_doc_title <- FALSE + #x$is_doc_title <- x$line == 1 & x$tag == "title" x$n_leading_hash <- nchar(stringr::str_extract(x$content, "\\#+")) x$n_leading_hash <- dplyr::case_when( x$n_leading_hash > 0 ~ x$n_leading_hash, diff --git a/R/outline-roxy.R b/R/outline-roxy.R index 3665830..c9cd6a8 100644 --- a/R/outline-roxy.R +++ b/R/outline-roxy.R @@ -244,6 +244,8 @@ join_roxy_fun <- function(file) { dplyr::mutate(content = dplyr::case_when( # only keep the first line of section, subsection, family and concept tags. tag %in% c("family", "concept") ~ stringr::str_extract(content, "^(.+)(\n)?", group = 1), + # Remove code blocks... + tag %in% c("details", "description") ~ stringr::str_remove_all(content, "```[^`]+```"), # Also remove : from section tag %in% c("section", "subsection") ~ stringr::str_extract(content, "^(.+)\\s?\\:\\s?\n?", group = 1), .default = content diff --git a/R/outline.R b/R/outline.R index cf7165b..616c7f7 100644 --- a/R/outline.R +++ b/R/outline.R @@ -351,7 +351,6 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te } exclude_example_files <- function(path) { - # TODO for usethis, add inst/templates/ but I may leave them for now.... # styler tests examples may not work.. regexp_exclude <- paste( @@ -522,12 +521,11 @@ keep_outline_element <- function(.data) { # still regular comments in .md files # what to keep in .md docs - (is_md & (is_obj_caption | is_doc_title | is_object_title)) | - (is_md & (is_section_title & before_and_after_empty)) | + (is_md & (is_obj_caption | (is_section_title & before_and_after_empty))) | # What to keep in .R files (!is_md & is_section_title_source) | # What to keep anywhere - is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def | is_object_title # | is_cli_info # TODO reanable cli info + is_tab_or_plot_title | is_todo_fixme | is_test_name | is_cross_ref | is_function_def | is_object_title | is_doc_title # | is_cli_info # TODO reanable cli info ) dat$simplify_news <- NULL @@ -820,16 +818,19 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { condition_to_truncate2 = NULL, style_fun = NULL, is_saved_doc = NULL, + is_roxygen_comment = NULL, + is_notebook = NULL, + is_news = NULL, # I may put it back... importance = NULL, # may be useful for debugging. before_and_after_empty = NULL, # may be useful for debugging has_inline_markup = NULL, - n_leading_hash = NULL ) |> dplyr::filter(is.na(outline_el) | grepl(pattern, outline_el, ignore.case = TRUE)) } + trim_outline <- function(x, width) { # problematic in case_when cli::ansi_strtrim(x, width = width) diff --git a/README.md b/README.md index 24180d9..856f997 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 7.17s 7.17s 0.139 85.1MB 0.558 +#> 1 outline <- proj_outline() 5.96s 5.96s 0.168 85.6MB 0.671 ```
@@ -195,7 +195,6 @@ outline #> `i` family dplyr extensions #> `i` FIXME Doesn't work, problem with symbols here- `Done✔?` #> `i` Keep rows that match one of the conditions [filter_if_any()] -#> `i` with dplyr::filter #> `i` Elegant wrapper around filter and pull [extract_cell_value()] #> `i` TODO use `check_length()` when implemented. r-lib/rlang#1618 ()- `Done✔?` #> `i` summarise with total @@ -258,7 +257,6 @@ outline #> `i` If `work_only` is set to `TRUE`, the function will only return outline of the `# WORK` comment #> `i` `file_outline()` #> `i` File outline -#> `i` TODO for usethis, add inst/templates/ but I may leave them for now....- `Done✔?` #> `i` Print method #> `i` Step: tweak outline look as they show #> `i` TODO reanable cli info- `Done✔?` @@ -404,6 +402,7 @@ outline #> ── `tests/testthat/_outline/roxy-general2.R` Test for roxygen parsing for no error #> `i` Use 'tests/testthat/_outline/roxy-general.R' for output testing #> `i` Title with `_things` [f_to_be_index_in_outline()] +#> `i` Section #> `i` a family to include #> `i` An S3 method not to be include [f_not_to_index.xml()] #> `i` section AA REQUIRED ELEMENT diff --git a/tests/testthat/_outline/roxy-general2.R b/tests/testthat/_outline/roxy-general2.R index f998596..961fb96 100644 --- a/tests/testthat/_outline/roxy-general2.R +++ b/tests/testthat/_outline/roxy-general2.R @@ -5,6 +5,15 @@ #' Title with `_things` #' +#' @description +#' ## Section +#' +#' +#' ```r +#' # Commented code not include +#' title = "TITLE NOT INCLUDED" +#' ``` +#' #' @examples #' # Commented code not included #' From 434b92fe62f762d1cb445d2a64ecc02182a97bee Mon Sep 17 00:00:00 2001 From: olivroy Date: Mon, 3 Jun 2024 09:11:46 -0400 Subject: [PATCH 64/71] Add files from violetcereza as testing. --- README.md | 16 +++++ playground/outline-tree.R | 111 +++++++++++++++++++++++++++++++ tests/testthat/_outline/tree.qmd | 45 +++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 playground/outline-tree.R create mode 100644 tests/testthat/_outline/tree.qmd diff --git a/README.md b/README.md index 856f997..4726fea 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,22 @@ outline #> `i` A great title #> `i` TODO improve this Viz!- `Done✔?` #> +#> ── `playground/test.R` +#> `i` TODOs (they don't affect heirarchy)- `Done✔?` +#> +#> ── `playground/test.qmd` Test +#> `i` Quarto +#> `i` Running Code +#> `i` TODO fix this in the code- `Done✔?` +#> `i` A sub header +#> `i` TODO here's a todo in the text- `Done✔?` +#> `i` Back to header 1 +#> `i` Dont skip me +#> `i` header 5 +#> `i` TODO testing section- `Done✔?` +#> `i` Another sub header +#> `i` TODO section test- `Done✔?` +#> #> ── `tests/testthat/_outline/knitr-notebook.R` Crop Analysis Q3 2013 #> `i` A great section #> diff --git a/playground/outline-tree.R b/playground/outline-tree.R new file mode 100644 index 0000000..0b01d85 --- /dev/null +++ b/playground/outline-tree.R @@ -0,0 +1,111 @@ +# From @violetcereza https://github.com/olivroy/reuseme/issues/28#issuecomment-2128290268 +outline_data <- proj_outline() |> + + # Convert the many is_ columns into mutually exclusive "outline row types" + tidyr::pivot_longer( + names_to = "type", names_prefix = "is_", c( + dplyr::starts_with("is_"), -is_md, -is_second_level_heading_or_more + ) + ) %>% + # # Double check that types are mututally exclusive + # filter(all(value == F), .by = c(file_short, title, line_id)) + # filter(sum(value) > 1, .by = c(file_short, title, line_id)) + dplyr::filter(value) %>% + # We drop these because they don't serve to add much context to TODOs (they don't affect heirarchy) + dplyr::filter(type != "tab_or_plot_title") %>% + + # Some useful definitions! + dplyr::mutate( + title = dplyr::coalesce(outline_el, title_el), + file_short = fs::path_file(file), + n_leading_hash = type %>% dplyr::case_match( + c("todo_fixme", "tab_or_plot_title") ~ NA, + .default = n_leading_hash + ) + ) %>% + + # For each file, stick a item at the top of the outline + dplyr::group_by(file, file_short) %>% + group_modify(\(data, group) data %>% add_row( + .before = 0, + n_leading_hash = -1, + title = group$file_short, + type = "file" + )) %>% + + mutate( + # Processing how title displays based on type + print_title = dplyr::case_match( + type, + "todo_fixme" ~ link, + .default = link_rs_api + ) %>% dplyr::coalesce(title) %>% purrr::map_chr(cli::format_inline), + + # Assign TODO items (and other items missing n_leading_hash) + # to be indented under the last seen header level + indent = dplyr::coalesce(n_leading_hash, zoo::na.locf0(n_leading_hash+1)), + + # If there are any headers that skip an intermediate level, pick them up + skip_level = indent > dplyr::lag(indent)+1, + skip_level_should_be = ifelse(skip_level, dplyr::lag(indent)+1, NA), + skip_level_adjust = dplyr::case_when( + # All the items below on the outline should be adjusted backwards + skip_level ~ skip_level_should_be-indent, + # Unless we reach a point on the outline where we're back up in + # the hierachy, so stop adjusting. + indent <= zoo::na.locf0(skip_level_should_be) ~ 0 + ) %>% + # Carry the adjustments to later rows + zoo::na.locf0() %>% dplyr::coalesce(0), + + indent = indent + skip_level_adjust + ) |> + dplyr::ungroup() |> + dplyr::select(title, type, n_leading_hash, indent, print_title) + +dat_ready_to_tree <- outline_data %>% + dplyr::mutate( + # Give items IDs so titles do not have to be unique + item_id = as.character(dplyr::row_number()), + indent_wider = indent, + x = TRUE + ) %>% + + # We need these wide cumsum `header1` type fields to determine which items belong to which parents + tidyr::pivot_wider(names_from = indent_wider, values_from = x, values_fill = FALSE, names_prefix = "header") %>% + dplyr::mutate(dplyr::across(dplyr::starts_with("header"), cumsum)) %>% + + # For each row, pick the IDs of all direct children from the outline + purrr::pmap(function(...) with(list(..., childdata = .), tibble( + title, + print_title, + indent, + item_id, + type, + parent_level_id = get(stringr::str_c("header", indent)), + children_ids = childdata %>% + dplyr::rename(childindent = indent) %>% + dplyr::filter( + childindent == indent+1, + cumsum(childindent == indent) == parent_level_id + ) %>% + dplyr::pull(item_id) %>% list() + ))) %>% + purrr::list_rbind() %>% + # View() + dplyr::select(item_id, children_ids, print_title) + +dat_ready_to_tree |> + cli::tree() + +# browse-pkg +dat_ready_to_tree |> + cli::tree("5") + +dat_ready_to_tree |> + cli::tree("10") +dat_ready_to_tree |> + dplyr::filter(purrr::map_lgl(children_ids, \(x) length(x) > 0)) + +dat_ready_to_tree |> + cli::tree("13") diff --git a/tests/testthat/_outline/tree.qmd b/tests/testthat/_outline/tree.qmd new file mode 100644 index 0000000..b1bb6f3 --- /dev/null +++ b/tests/testthat/_outline/tree.qmd @@ -0,0 +1,45 @@ +--- +title: "Test" +format: html +editor: source +--- + +## Quarto + +Quarto enables you to weave together content and executable code into a finished document. +To learn more about Quarto see . + +## Running Code + +When you click the **Render** button a document will be generated that includes both content and the output of embedded code. +You can embed code like this: + +```{r} +1 + 1 +# TODO: fix this in the code +``` + +You can add options to executable code like this + +### A sub header + +```{r} +#| echo: false +2 * 2 +``` + +The `echo: false` option disables the printing of code (only output is displayed). + +TODO: here's a todo in the text + +# Back to header 1 + +## Dont skip me + +##### header 5 + +TODO: testing section + +## Another sub header + +TODO: section test From 3cb922566dbabff5d5d74a07195a9aaf1af84010 Mon Sep 17 00:00:00 2001 From: olivroy Date: Mon, 3 Jun 2024 09:12:02 -0400 Subject: [PATCH 65/71] Test adding indent --- R/outline.R | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/R/outline.R b/R/outline.R index 616c7f7..0864add 100644 --- a/R/outline.R +++ b/R/outline.R @@ -796,12 +796,19 @@ construct_outline_link <- function(.data, is_saved_doc, dir_common, pattern) { cli::cli_abort("Define this in {.fn define_important_element}", .internal = TRUE) } + # Tweak n_leasing hash for todos or fixme.. + .data$n_leading_hash <- dplyr::case_when( + .data$is_todo_fixme ~ dplyr::lead(.data$n_leading_hash, default = 0) + 1, + .default = .data$n_leading_hash + ) + .data$leading_space <- purrr::map_chr(.data$n_leading_hash, \(x) paste(rep(" ", length.out = max(min(x - 1, 1), 0)), collapse = "")) dplyr::mutate(.data, # link_rs_api = paste0("{.run [", outline_el, "](reuseme::open_rs_doc('", file_path, "', line = ", line, "))}"), link_rs_api = dplyr::case_when( is.na(outline_el2) ~ NA_character_, !is_saved_doc ~ paste0("line ", line, " -", outline_el2), rs_avail_file_link ~ paste0( + leading_space, "{cli::style_hyperlink(", style_fun, ', "', paste0("file://", file_path), '", params = list(line = ', line, ", col = 1))} ", outline_el2 ), From d926d3250aed991ba925c1d44b43dcaba73bfb05 Mon Sep 17 00:00:00 2001 From: olivroy Date: Mon, 3 Jun 2024 11:42:26 -0400 Subject: [PATCH 66/71] Fix conflict [ci skip] --- NEWS.md | 4 ---- tests/testthat/_outline/my-analysis.md | 9 --------- tests/testthat/_snaps/outline.md | 1 - 3 files changed, 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index 915c6d8..4b63c34 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,12 +6,8 @@ * `proj_outline()` gains `exclude_tests` to exclude tests from outline -* `doc_title`, `is_plot_or_tab_title` is better detected now. - * `proj_outline()` now detects legacy `fig.cap` in the chunk header. See `knitr::convert_chunk_headers()` for the newer approach. -* `proj_outline()` now autolinks GitHub issue inside the repo. - * `file_outline()` now supports [R reports](https://rmarkdown.rstudio.com/articles_report_from_r_script.html) (basic) * `active_rs_doc_nav()` is a new function to navigate to files pane location. diff --git a/tests/testthat/_outline/my-analysis.md b/tests/testthat/_outline/my-analysis.md index 4c4d0a7..5236b18 100644 --- a/tests/testthat/_outline/my-analysis.md +++ b/tests/testthat/_outline/my-analysis.md @@ -6,12 +6,6 @@ title: My doc title ```{r} #| title: Dashboard card -<<<<<<< HEAD -======= - -# A code section ---- - ->>>>>>> b4803a244a484a7b686d05f28ea7be91f4d412c3 ``` ## A subsection @@ -28,7 +22,6 @@ Some text with TODO. # A code section ---- ``` -<<<<<<< HEAD ```{r} #| fig-cap: > @@ -41,5 +34,3 @@ Some text with TODO. #| A long ggplot2 title #| with more details3." ``` -======= ->>>>>>> b4803a244a484a7b686d05f28ea7be91f4d412c3 diff --git a/tests/testthat/_snaps/outline.md b/tests/testthat/_snaps/outline.md index 8e86d51..bb6cd66 100644 --- a/tests/testthat/_snaps/outline.md +++ b/tests/testthat/_snaps/outline.md @@ -7,7 +7,6 @@ -- `my-analysis.md` My doc title Output `i` A section - `i` A code section `i` A subsection `i` A section2 `i` A long ggplot2 title From 8f9978c9e0a2fb271916f9c991e5d944e56cf971 Mon Sep 17 00:00:00 2001 From: olivroy Date: Mon, 3 Jun 2024 12:02:40 -0400 Subject: [PATCH 67/71] merge [ci skip] --- R/outline-criteria.R | 9 +++++---- R/outline.R | 23 ----------------------- README.md | 3 +-- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/R/outline-criteria.R b/R/outline-criteria.R index a09d5c2..c11def6 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -226,22 +226,23 @@ define_outline_criteria <- function(.data, exclude_todos, dir_common) { is_test_name = is_test_file & o_is_test_that(content) & !o_is_generic_test(content), is_section_title = o_is_section_title(content), pkg_version = extract_pkg_version(content, is_news, is_section_title), - is_section_title_source = o_is_section_title(content) & + is_section_title_source = is_section_title & stringr::str_detect(content, "[-\\=]{3,}|^\\#'") & stringr::str_detect(content, "[:alpha:]"), - is_function_def = grepl("<- function(", content, fixed = TRUE) & !stringr::str_starts(content, "\\s*#"), - is_tab_or_plot_title = o_is_tab_plot_title(content) & !is_section_title & !is_function_def, + is_todo_fixme = !exclude_todos & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext, is_notebook) & !is_snap_file, + # roxygen2 title block is_object_title = FALSE, tag = NA_character_, topic = NA_character_, - is_todo_fixme = !exclude_todos & o_is_todo_fixme(content) & !o_is_roxygen_comment(content, file_ext, is_notebook) & !is_snap_file, n_leading_hash = nchar(stringr::str_extract(content, "\\#+(?!\\|)")), # don't count hashpipe n_leading_hash = dplyr::coalesce(n_leading_hash, 0), # Make sure everything is second level in revdep/. n_leading_hash = n_leading_hash + grepl("revdep/", file, fixed = TRUE), is_second_level_heading_or_more = (is_section_title_source | is_section_title) & n_leading_hash > 1, is_cross_ref = stringr::str_detect(content, "docs_links?\\(") & !stringr::str_detect(content, "@param|\\{\\."), + is_function_def = grepl("<- function(", content, fixed = TRUE) & !stringr::str_starts(content, "\\s*#"), + is_tab_or_plot_title = o_is_tab_plot_title(content) & !is_section_title & !is_function_def, ) x <- dplyr::mutate( x, diff --git a/R/outline.R b/R/outline.R index c1964db..d0e5425 100644 --- a/R/outline.R +++ b/R/outline.R @@ -349,29 +349,6 @@ dir_outline <- function(pattern = NULL, path = ".", work_only = TRUE, exclude_te file_outline(path = file_list_to_outline, pattern = pattern, exclude_todos = exclude_todos, work_only = work_only, dir_common = dir, alpha = alpha, recent_only = recent_only) } -exclude_example_files <- function(path) { - # styler tests examples may not work.. - - regexp_exclude <- paste( - "vignettes/test/", # test vignettes - "LICENSE.md", # avoid indexing this. - "tests/(performance-monitor|gt-examples/|testthat/scope-|testthat/assets|testthat/_outline|testthat/testTestWithFailure|testthat/testTest/|testthat/test-parallel/|testthat/test-list-reporter/)", # example files in usethis, pkgdown, reuseme, devtools, etc. - "inst/((rmarkdown/)?templates/|example-file/|examples/rmd/|tutorials/)", # license templates in usethis - "revdep/", # likely don't need to outline revdep/, use dir_outline() to find something in revdep/ - "themes/hugo-theme-console/", # protect blogdown - "vignettes/.+\\.R$", # generated files - "RcppExports.R", - "pkgdown/assets", - sep = "|" - ) - - fs::path_filter( - path, - regexp = regexp_exclude, - invert = TRUE - ) -} - exclude_example_files <- function(path) { # styler tests examples may not work.. diff --git a/README.md b/README.md index 14ae343..8b3882f 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ bench::mark( #> # A tibble: 1 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> -#> 1 outline <- proj_outline() 10.2s 10.2s 0.0982 85.6MB 0.393 +#> 1 outline <- proj_outline() 8.58s 8.58s 0.117 85.4MB 0.583 ```
@@ -357,7 +357,6 @@ outline #> ── `tests/testthat/_outline/my-analysis.md` My doc title #> `i` A section #> `i` Dashboard card -#> `i` A code section #> `i` A subsection #> `i` A section2 #> `i` A long ggplot2 title From 56a40624471c2dc16eaf74ae30beec316366fa77 Mon Sep 17 00:00:00 2001 From: olivroy Date: Fri, 7 Jun 2024 03:03:40 -0400 Subject: [PATCH 68/71] [ci skip] adjust news --- NEWS.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1ad60ae..7c7c968 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,8 +2,6 @@ * Outline elements present more than four times in a file will not be printed as they are considered placeholders. (like generic test name) -* `proj_outline()` now detects [knitr notebooks](https://rmarkdown.rstudio.com/articles_report_from_r_script.html) that use the default options. Internally, the file is transformed into a md file by stripping roxygen comments, and is processed as such. (#30) - * `proj_outline()` no longer shows `complete_todo()` links for items in non-interactive sessions. `complete_todo()` links are now only shown when calling `file_outline()` on the active file. * `proj_list()` / `proj_switch()` no longer opens a nested project if looking for `"pkgdown"`, `"testthat"`, etc. From cce28050b6f9cd3cdaa32fe44cbc5aae8eba95af Mon Sep 17 00:00:00 2001 From: olivroy Date: Sun, 9 Jun 2024 15:56:24 -0400 Subject: [PATCH 69/71] notebook are already supported on main. --- NEWS.md | 2 -- R/outline-criteria.R | 13 ------------- 2 files changed, 15 deletions(-) diff --git a/NEWS.md b/NEWS.md index 722d5ad..c59fc6f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -30,8 +30,6 @@ that will passed on to `proj_list()` * `proj_outline()` now detects legacy `fig.cap` in the chunk header. See `knitr::convert_chunk_headers()` for the newer approach. -* `file_outline()` now supports [R reports](https://rmarkdown.rstudio.com/articles_report_from_r_script.html) (basic) - * `active_rs_doc_nav()` is a new function to navigate to files pane location. `active_rs_doc_copy()` now accepts copying md and qmd files too and no longer allows renaming Rprofile. diff --git a/R/outline-criteria.R b/R/outline-criteria.R index 0f1c46a..09c3a35 100644 --- a/R/outline-criteria.R +++ b/R/outline-criteria.R @@ -18,19 +18,6 @@ extract_pkg_version <- function(x, is_news, is_heading) { #' * is_tab_plot_title #' #' @noRd -o_is_notebook <- function(x, file, file_ext, line) { - # Like roxy comments and first line = --, 2nd title. - # x$is_notebook <- grepl("notebook.*\\.R", x$file) - # Detect #' --- - any_notebooks <- grep("^#' ---", x[line == 1 & file_ext == "R"], fixed = FALSE) - if (length(any_notebooks) > 0L) { - is_notebook <- file %in% file[line == 1 & file_ext == "R"][any_notebooks] - } else { - is_notebook <- FALSE - } - is_notebook -} - o_is_roxygen_comment <- function(x, file_ext = NULL, is_notebook = FALSE) { if (!is.null(file_ext)) { From 27bdec2c05fd0f1bf8e03b0d5754aa1c50ca641a Mon Sep 17 00:00:00 2001 From: olivroy Date: Sun, 9 Jun 2024 15:57:36 -0400 Subject: [PATCH 70/71] [ci skip] --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index c59fc6f..829329f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,6 +20,8 @@ that will passed on to `proj_list()` * Outline elements present more than four times in a file will not be printed as they are considered placeholders. (like generic test name) +* `proj_outline()` now detects [knitr notebooks](https://rmarkdown.rstudio.com/articles_report_from_r_script.html) that use the default options. Internally, the file is transformed into a md file by stripping roxygen comments, and is processed as such. (#30) + * `proj_outline()` no longer shows `complete_todo()` links for items in non-interactive sessions. `complete_todo()` links are now only shown when calling `file_outline()` on the active file. * `proj_list()` / `proj_switch()` no longer opens a nested project if looking for `"pkgdown"`, `"testthat"`, etc. From 5f665cb83d03bc1c1cc27df92cd23aaf1cd087e6 Mon Sep 17 00:00:00 2001 From: olivroy Date: Thu, 13 Jun 2024 19:03:22 -0400 Subject: [PATCH 71/71] fix merge [ci skip] --- R/outline.R | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/R/outline.R b/R/outline.R index cc5e773..5ba767b 100644 --- a/R/outline.R +++ b/R/outline.R @@ -467,7 +467,6 @@ construct_outline_link <- function(.data) { is_saved_doc <- !any(.data$file == "unsaved-doc.R") is_active_doc <- length(unique(.data$file)) == 1L rs_avail_file_link <- is_rstudio("2023.09.0.375") # better handling after - rs_avail_file_link <- is_rstudio("2023.09.0.375") # better handling after .data <- define_important_element(.data) if (is.null(dir_common) || !nzchar(dir_common)) { @@ -535,9 +534,9 @@ construct_outline_link <- function(.data) { .data$text_in_link <- sub(as.character(dir_common), "", .data$file_path) .data$text_in_link <- sub("^/", "", .data$text_in_link) .data$style_fun <- dplyr::case_match(.data$importance, - "not_important" ~ "cli::style_italic('i')", # cli::style_inverse for bullets - "important" ~ "cli::style_inverse('i')", - .default = NA_character_ + "not_important" ~ "cli::style_italic('i')", # cli::style_inverse for bullets + "important" ~ "cli::style_inverse('i')", + .default = NA_character_ ) if (anyNA(.data$style_fun)) { @@ -551,26 +550,26 @@ construct_outline_link <- function(.data) { ) .data$leading_space <- purrr::map_chr(.data$n_leading_hash, \(x) paste(rep(" ", length.out = max(min(x - 1, 1), 0)), collapse = "")) dplyr::mutate(.data, - # link_rs_api = paste0("{.run [", outline_el, "](reuseme::open_rs_doc('", file_path, "', line = ", line, "))}"), - link_rs_api = dplyr::case_when( - is.na(outline_el2) ~ NA_character_, - !is_saved_doc ~ paste0("line ", line, " -", outline_el2), - rs_avail_file_link ~ paste0( - leading_space, - "{cli::style_hyperlink(", style_fun, ', "', - paste0("file://", file_path), '", params = list(line = ', line, ", col = 1))} ", outline_el2 - ), - .default = paste0(rs_version, "{.run [i](reuseme::open_rs_doc('", file_path, "', line = ", line, "))} ", outline_el2) - ), - file_hl = dplyr::case_when( - !is_saved_doc ~ file_path, - rs_avail_file_link ~ paste0("{.href [", text_in_link, "](file://", file_path, ")}"), - .default = paste0("{.run [", text_in_link, "](reuseme::open_rs_doc('", file_path, "'))}") - ), - rs_version = NULL, - outline_el2 = NULL, - condition_to_truncate = NULL, - condition_to_truncate2 = NULL + # link_rs_api = paste0("{.run [", outline_el, "](reuseme::open_rs_doc('", file_path, "', line = ", line, "))}"), + link_rs_api = dplyr::case_when( + is.na(outline_el2) ~ NA_character_, + !is_saved_doc ~ paste0("line ", line, " -", outline_el2), + rs_avail_file_link ~ paste0( + leading_space, + "{cli::style_hyperlink(", style_fun, ', "', + paste0("file://", file_path), '", params = list(line = ', line, ", col = 1))} ", outline_el2 + ), + .default = paste0(rs_version, "{.run [i](reuseme::open_rs_doc('", file_path, "', line = ", line, "))} ", outline_el2) + ), + file_hl = dplyr::case_when( + !is_saved_doc ~ file_path, + rs_avail_file_link ~ paste0("{.href [", text_in_link, "](file://", file_path, ")}"), + .default = paste0("{.run [", text_in_link, "](reuseme::open_rs_doc('", file_path, "'))}") + ), + rs_version = NULL, + outline_el2 = NULL, + condition_to_truncate = NULL, + condition_to_truncate2 = NULL ) } # Step: tweak outline look as they show ---------