diff --git a/DESCRIPTION b/DESCRIPTION index 0a4183295f..13c7e5cd5f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -76,6 +76,7 @@ Authors@R: c( person("Niels Richard", "Hansen", role = "ctb"), person("Noam", "Ross", role = "ctb"), person("Obada", "Mahdi", role = "ctb"), + person("Peter", "DeWitt", role = "ctb"), person("Qiang", "Li", role = "ctb"), person("Ramnath", "Vaidyanathan", role = "ctb"), person("Richard", "Cotton", role = "ctb"), diff --git a/NEWS.md b/NEWS.md index 60288e0d7d..dbdf51a176 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,10 @@ - By default, `include_graphics(files)` will signal an error if any `files` do not exist and are not web resources. To avoid the error (e.g., if you think it is a false positive), use `include_graphics(..., error = FALSE)` (thanks, @hadley, #1717). +## BUG FIXES + +- Check for the correct ordering of start/end deliminators of comments for spin files (#1801) + # CHANGES IN knitr VERSION 1.27 ## NEW FEATURES diff --git a/R/spin.R b/R/spin.R index 2e0f77133d..cc758b11af 100644 --- a/R/spin.R +++ b/R/spin.R @@ -59,8 +59,7 @@ spin = function( x = if (nosrc <- is.null(text)) read_utf8(hair) else split_lines(text) stopifnot(length(comment) == 2L) c1 = grep(comment[1], x); c2 = grep(comment[2], x) - if (length(c1) != length(c2)) - stop('comments must be put in pairs of start and end delimiters') + check_comments(c1, c2) # remove comments if (length(c1)) x = x[-unique(unlist(mapply(seq, c1, c2, SIMPLIFY = FALSE)))] @@ -183,3 +182,46 @@ spin_child = function(input, format) { quiet = TRUE )) } + +#' Check Comments +#' +#' A more robust check for open/close matching sets of comments in a spin file. +#' +#' @param c1 index (line numbers) for the start delimiter of comments +#' @param c1 index (line numbers) for the end delimiter of comments +#' +check_comments <- function(c1, c2) { + + cs <- sort(c(openers = c1, closers = c2)) + err <- FALSE + notes <- character() + while(length(cs)) { + i <- 1 + if (grepl("closer", names(cs)[1])) { + notes <- append(notes, paste0(" * no starting delimiter; ended on line ", cs[1])) + cs <- cs[-1] + err <- TRUE + } else if (all(grepl("opener", names(cs)))) { + notes <- append(notes, paste0(" * started on line ", cs[1], "; no end delimiter")) + cs <- cs[-1] + err <- TRUE + } else { + while (i < length(cs)) { + if (grepl("opener", names(cs)[i]) & grepl("closer", names(cs)[i + 1])) { + notes <- append(notes, paste0(" * started on line ", cs[i], "; ended on line ", cs[i + 1])) + cs <- cs[-c(i, i + 1)] + break + } else { + i <- i + 1 + } + } + } + } + + if (err) { + stop(paste('comments must be put in pairs of start and end delimiters.\n', paste(notes, collapse = '\n'), collapse = "\n"), + call. = FALSE) + } + invisible(notes) +} +