Skip to content

Commit 5af968d

Browse files
authored
Fix duplicated description for R6 classes with methods (#1823)
The first bug arises because `parse_description()` thinks the method `@description` belongs to the class, so the second line of text becomes the details. There's no easy way to change this without significant changes to the parser, so the simplest approach seems to be to patch it up after the fact. The second bug arises about because we were adding the default title twice. Fixes #1504
1 parent fd154e9 commit 5af968d

3 files changed

Lines changed: 75 additions & 15 deletions

File tree

R/rd-r6-methods.R

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,13 @@ find_method_for_tag <- function(methods, tag) {
114114
if (nrow(methods) == 0) {
115115
return(NA_character_)
116116
}
117-
w <- which(
118-
basename(methods$file) == basename(tag$file) &
119-
methods$line > tag$line
120-
)[1]
117+
if (tag$file == "<text>") {
118+
# for testing
119+
same_file <- TRUE
120+
} else {
121+
same_file <- basename(methods$file) == basename(tag$file)
122+
}
123+
w <- which(same_file & methods$line > tag$line)[1]
121124
methods$name[w]
122125
}
123126

R/rd-r6.R

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
topic_add_r6_methods <- function(rd, block, env, base_path) {
22
docs <- r6_class_from_block(block, env)
3+
block <- r6_fix_intro(block)
34

45
# Add class-level tags
56
for (tag in block$tags) {
@@ -20,27 +21,46 @@ topic_add_r6_methods <- function(rd, block, env, base_path) {
2021
}
2122
}
2223

24+
# When an R6 class has inline @description tags for methods, parse_description()
25+
# parser puts the class description into @details instead of @description.
26+
# This function detects that case and promotes the class-level @details back
27+
# to @description.
28+
r6_fix_intro <- function(block) {
29+
types <- map_chr(block$tags, \(t) r6_tag_type(t, block))
30+
tags <- map_chr(block$tags, \(t) t$tag)
31+
32+
has_class_desc <- any(tags == "description" & types == "class")
33+
has_class_details <- any(tags == "details" & types == "class")
34+
has_method_desc <- any(tags == "description" & types == "method")
35+
36+
if (!has_class_desc && has_class_details && has_method_desc) {
37+
# Promote the first class-level @details to @description
38+
for (i in seq_along(block$tags)) {
39+
if (tags[[i]] == "details" && types[[i]] == "class") {
40+
block$tags[[i]]$tag <- "description"
41+
break
42+
}
43+
}
44+
}
45+
46+
block
47+
}
48+
2349
# Classify an R6 block tag:
2450
# - "class": top-level Rd (e.g. @title, @description before class body)
2551
# - "method": inline tag associated with a method
2652
# - "other": @field/@param tags consumed by field/param extraction
2753
r6_tag_type <- function(tag, block) {
2854
inline <- !is.na(tag$line) && tag$line >= block$line
29-
method_tags <- c(
30-
"description",
31-
"details",
32-
"param",
33-
"return",
34-
"returns",
35-
"examples"
36-
)
37-
38-
if (tag$tag == "field") {
55+
method_tags <- c("description", "details", "return", "returns", "examples")
56+
57+
if (tag$tag %in% c("field", "name", "title")) {
3958
"other"
4059
} else if (tag$tag %in% method_tags && inline) {
4160
"method"
4261
} else if (tag$tag == "param") {
43-
"other"
62+
# class-level @param are used as defaults for all methods
63+
if (inline) "method" else "other"
4464
} else {
4565
"class"
4666
}

tests/testthat/test-rd-r6-class.R

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,43 @@ test_that("can construct empty class", {
1212
expect_equal(docs$active_bindings, rd_r6_bindings())
1313
})
1414

15+
test_that("class description is not duplicated", {
16+
text <- "
17+
#' Title
18+
#'
19+
#' Description
20+
foo <- R6::R6Class(
21+
public = list(
22+
#' @description foo
23+
foo = function() {}
24+
)
25+
)
26+
"
27+
28+
out <- roc_proc_text(rd_roclet(), text)[[1]]
29+
expect_equal(out$get_value("description"), "Description")
30+
})
31+
32+
test_that("title-only class has single description", {
33+
out <- roc_proc_text(
34+
rd_roclet(),
35+
"
36+
#' Title
37+
foo <- R6::R6Class(cloneable = FALSE)
38+
"
39+
)[[1]]
40+
expect_equal(out$get_value("description"), "Title")
41+
42+
out <- roc_proc_text(
43+
rd_roclet(),
44+
"
45+
#' @title Title
46+
foo <- R6::R6Class(cloneable = FALSE)
47+
"
48+
)[[1]]
49+
expect_equal(out$get_value("description"), "Title")
50+
})
51+
1552
test_that("class with only active bindings doesn't error (#1610)", {
1653
text <- "
1754
#' Class

0 commit comments

Comments
 (0)