Skip to content

Commit

Permalink
add support for custom glue delimiters. small bug fix for literal bra…
Browse files Browse the repository at this point in the history
…ces in the same string as parseable braces.
  • Loading branch information
radbasa committed May 31, 2024
1 parent 309e858 commit 5a25e88
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: box.linters
Title: Linters for 'box' Modules
Version: 0.9.0.9006
Version: 0.9.0.9007
Authors@R:
c(
person("Ricardo Rodrigo", "Basa", role = c("aut", "cre"), email = "[email protected]"),
Expand Down
11 changes: 10 additions & 1 deletion R/get_objects_in_strings.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@
#' @return A character vector of object and function names found inside `glue` string templates.
#' @keywords internal
get_objects_in_strings <- function(xml) {

xpath_str_consts <- "
//expr/STR_CONST
"

all_strings <- extract_xml_and_text(xml, xpath_str_consts)

glue_open <- getOption("glue.open", default = "\\{")
glue_close <- getOption("glue.close", default = "\\}")

all_strings$text <- gsub(glue_open, "{", all_strings$text)
all_strings$text <- gsub(glue_close, "}", all_strings$text)

text_between_braces <- stringr::str_match_all(all_strings$text, "(\\{(?:\\{??[^\\{]*?\\}))")

tryCatch({
Expand All @@ -22,7 +30,8 @@ get_objects_in_strings <- function(xml) {
return(NULL)
}

parsed_code <- parse(text = each_text[, 2], keep.source = TRUE)
text_to_parse <- each_text[substr(each_text[, 2], 1, 2) != "{{", 2]
parsed_code <- parse(text = text_to_parse, keep.source = TRUE)
xml_parsed_code <- xml2::read_xml(xmlparsedata::xml_parse_data(parsed_code))
objects_called <- get_object_calls(xml_parsed_code)
functions_calls <- get_function_calls(xml_parsed_code)
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ Or, use `box.linters` from within a `{rhino}` project:
rhino::lint_r()
```

## {glue} String Interpolation

`{box.linters}` also looks inside `{glue}` strings. The default opening and closing delimiters are "`{`" and "`}`". `{glue}` provides a way to [customize delimiters](https://glue.tidyverse.org/reference/glue.html). Support for custom `{glue}` delimiters are provided *project-wide* by setting `glue.open` and `glue.close` options in the following manner:

```r
options(
list(
glue.open = "<<",
glue.close = ">>"
)
)
```

This is consistent with [`glue::glue()`](https://glue.tidyverse.org/reference/glue.html), doubling the full delimiter escapes it.

Because setting `glue.open` and `glue.close` will be *global* or *project-wide*, it is advised to invoke `glue` in the following manner to avoid confusion:

```r
glue::glue(..., .open = getOption("glue.open"), .close = getOption("glue.close"))
```

## Contribute

Expand Down
62 changes: 62 additions & 0 deletions tests/testthat/test-get_objects_in_strings.R
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ test_that("get_objects_in_strings ignores literal glue objects {{ }}", {
expect_equal(results, NULL)
})

test_that("get_objects_in_strings ignores literal glue objects {{ }} mixed in with { }", {
code <- "
string <- \"Some {{value_a + 1}} in a {value_b} string.\"
"
xml_code <- code_to_xml_expr(code)
results <- get_objects_in_strings(xml_code)
should_find <- "value_b"

expect_equal(results, should_find)
})

test_that("get_objects_in_strings extracts objects from multiline code", {
code <- "
string <- \"Some text {
Expand Down Expand Up @@ -128,3 +139,54 @@ test_that("get_objects_in_strings handles multiple string constants in code", {

expect_setequal(results, should_find)
})

test_that("get_objects_in_strings handles custom glue .open and .close symbols", {
withr::with_options(
list(
glue.open = "<<",
glue.close = ">>"
),
{
code <- "
string <- \"Some <<value_a>> and <<value_b>> in a string.\"
"
xml_code <- code_to_xml_expr(code)
results <- get_objects_in_strings(xml_code)
should_find <- c("value_a", "value_b")

expect_setequal(results, should_find)

code <- "
string <- \"Some <<value_a>> and <value_b> in a string.\"
"
xml_code <- code_to_xml_expr(code)
results <- get_objects_in_strings(xml_code)
should_find <- c("value_a")

expect_setequal(results, should_find)

code <- "
string <- \"Some <<value_a>> and <<<<value_b>>>> in a string.\"
"
xml_code <- code_to_xml_expr(code)
results <- get_objects_in_strings(xml_code)
should_find <- c("value_a")

expect_setequal(results, should_find)

code <- "
string <- \"Some text <<
{
internal_var <- external_var
some_function_call(internal_var, another_external_var)
}
>> here.\"
"
xml_code <- code_to_xml_expr(code)
results <- get_objects_in_strings(xml_code)
should_find <- c("external_var", "some_function_call", "internal_var", "another_external_var")

expect_setequal(results, should_find)
}
)
})

0 comments on commit 5a25e88

Please sign in to comment.