From 1c9004d2a5c27b9b15c99b0a87f901163541b224 Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Fri, 24 Jan 2020 14:15:51 -0700 Subject: [PATCH 1/3] add check_comments to R/spin.R re #yihui/knitr#1801 --- DESCRIPTION | 1 + NEWS.md | 4 ++++ R/spin.R | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) 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..7d50e37951 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,48 @@ 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)) { + if (grepl("closer", names(cs)[1])) { + notes <- append(notes, paste0(" unopened comment; closed on line ", cs[1])) + cs <- cs[-1] + err <- TRUE + } + + i <- 1 + while(i < length(cs)) { + if (grepl("opener", names(cs)[i]) & grepl("closer", names(cs)[i + 1])) { + notes <- append(notes, paste0(" opened on line ", cs[i], "; closed on line ", cs[i + 1])) + cs <- cs[-c(i, i+1)] + } else { + i <- i + 1 + } + } + + if (length(cs) == 1L & grepl("opener", names(cs)[1])) { + notes <- append(notes, paste0(" opened on line ", cs[1], "; unclosed")) + cs <- cs[-1] + err <- TRUE + } + } + + 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) +} + From 399f38bf9161b9c51ea3f37f858cb7fa75401788 Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Fri, 24 Jan 2020 15:03:13 -0700 Subject: [PATCH 2/3] improve logic in testing for the matching comment deliminators --- R/spin.R | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/R/spin.R b/R/spin.R index 7d50e37951..99e7dfdfea 100644 --- a/R/spin.R +++ b/R/spin.R @@ -195,28 +195,26 @@ 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(" unopened comment; closed on line ", cs[1])) + notes <- append(notes, paste0(" * unopened comment; closed on line ", cs[1])) cs <- cs[-1] err <- TRUE - } - - i <- 1 - while(i < length(cs)) { - if (grepl("opener", names(cs)[i]) & grepl("closer", names(cs)[i + 1])) { - notes <- append(notes, paste0(" opened on line ", cs[i], "; closed on line ", cs[i + 1])) - cs <- cs[-c(i, i+1)] - } else { - i <- i + 1 - } - } - - if (length(cs) == 1L & grepl("opener", names(cs)[1])) { - notes <- append(notes, paste0(" opened on line ", cs[1], "; unclosed")) + } else if (all(grepl("opener", names(cs)))) { + notes <- append(notes, paste0(" * opened on line ", cs[1], "; unclosed")) 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(" * opened on line ", cs[i], "; closed on line ", cs[i + 1])) + cs <- cs[-c(i, i + 1)] + break + } else { + i <- i + 1 + } + } } } From 1fec35a763d0ac90c2b9ca7969157ba737fd60c8 Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Fri, 24 Jan 2020 15:07:07 -0700 Subject: [PATCH 3/3] comment error checking uses start/end instead of open/close --- R/spin.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/spin.R b/R/spin.R index 99e7dfdfea..cc758b11af 100644 --- a/R/spin.R +++ b/R/spin.R @@ -198,17 +198,17 @@ check_comments <- function(c1, c2) { while(length(cs)) { i <- 1 if (grepl("closer", names(cs)[1])) { - notes <- append(notes, paste0(" * unopened comment; closed on line ", 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(" * opened on line ", cs[1], "; unclosed")) + 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(" * opened on line ", cs[i], "; closed on line ", 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 {