diff --git a/DESCRIPTION b/DESCRIPTION index c5805116bc..500463fa9c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: knitr Type: Package Title: A General-Purpose Package for Dynamic Report Generation in R -Version: 1.49.4 +Version: 1.49.5 Authors@R: c( person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666", URL = "https://yihui.org")), person("Abhraneel", "Sarma", role = "ctb"), diff --git a/NEWS.md b/NEWS.md index 421ee5265f..14a5cee6c5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # CHANGES IN knitr VERSION 1.50 +## NEW FEATURES + +- For inline code expressions, their specific line numbers will be shown in the message when errors occur (thanks, @kevinushey, #2387). Previously, the numbers were not specific to the inline code but the lines of the whole text chunk containing the inline code, which are often quite vague. + ## MINOR CHANGES - Moved implementations of `combine_words()` and `write_bib()` to the **xfun** package as `xfun::join_words()` and `xfun::pkg_bib()`, respectively, since they are not directly relevant to **knitr**. The functions `combine_words()` and `write_bib()` are still kept in **knitr**, and can continue to be used in the future. diff --git a/R/block.R b/R/block.R index f1d3dcab15..28ea201183 100644 --- a/R/block.R +++ b/R/block.R @@ -568,9 +568,11 @@ inline_exec = function( code = block$code; input = block$input if ((n <- length(code)) == 0) return(input) # untouched if no code is found code.src = block$code.src + lines = block$lines ans = character(n) for (i in 1:n) { + knit_concord$set(offset = lines[i, ]) tryCatch(parse_only(code[i]), error = function(e) { stop2('Failed to parse the inline R code: ', code.src[i], '\nReason: ', e$message) }) diff --git a/R/concordance.R b/R/concordance.R index 3e267cf1e0..97238f7638 100644 --- a/R/concordance.R +++ b/R/concordance.R @@ -2,7 +2,7 @@ # record input/output lines numbers in Rnw/tex and filenames knit_concord = new_defaults(list( - inlines = NULL, outlines = NULL, infile = NULL, outfile = NULL, block = NULL + inlines = NULL, outlines = NULL, infile = NULL, outfile = NULL, block = NULL, offset = NULL )) # do not consider child mode for concordance @@ -13,8 +13,12 @@ concord_mode = function() { current_lines = function(i = knit_concord$get('block')) { # a helpr function to return line numbers for block i n = knit_concord$get('inlines') - n1 = sum(head(n, i)); n0 = n1 - n[i] + 2 - paste(c(min(n0, n1), n1), collapse = '-') + n1 = sum(head(n, i)); n0 = min(n1, n1 - n[i] + 1) + # adjust line numbers for inline code expressions + if (length(o <- knit_concord$get('offset')) == 2) { + n1 = n0 + o[2]; n0 = n0 + o[1] + } + paste(c(n0, n1), collapse = '-') } # generate concordance for RStudio diff --git a/R/output.R b/R/output.R index f3960b597b..727f6d4cb4 100644 --- a/R/output.R +++ b/R/output.R @@ -323,6 +323,7 @@ process_file = function(text, output) { }, if (labels[i] != '') sprintf(' [%s]', labels[i]), get_loc ) + knit_concord$set(offset = NULL) } if (!tangle) res = insert_header(res) # insert header diff --git a/R/parser.R b/R/parser.R index 18de0a76a7..741a14f34f 100644 --- a/R/parser.R +++ b/R/parser.R @@ -232,18 +232,21 @@ parse_inline = function(input, patterns) { loc = cbind(start = numeric(0), end = numeric(0)) if (group_pattern(inline.code)) loc = str_locate(input, inline.code)[[1]] code1 = code2 = character() + lines = integer() if (nrow(loc)) { code = t(str_match(input, inline.code)) if (NCOL(code) >= 2L) { code1 = code[, 1L] code2 = apply(code[, -1L, drop = FALSE], 1, paste, collapse = '') + nl = gregexpr('\n', input, fixed = TRUE)[[1]] + lines = if (length(nl) > 1 || nl > -1) findInterval(loc, nl) else c(0L, 0L) } } - structure( - list(input = input, location = loc, code = code2, code.src = code1), - class = 'inline' - ) + structure(list( + input = input, location = loc, code = code2, code.src = code1, + lines = matrix(lines, ncol = 2) + ), class = 'inline') } print_inline = function(x) {