From 7632f65fa09845cf0227a9c7b33f72124b01cca4 Mon Sep 17 00:00:00 2001 From: Richard Iannone Date: Fri, 18 Nov 2022 17:43:59 -0500 Subject: [PATCH 1/8] Add the `str_substitute()` function This is a base R replacement for `stringr::str_sub()` --- R/utils.R | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/R/utils.R b/R/utils.R index b620a6d6bf..8f62fafa58 100644 --- a/R/utils.R +++ b/R/utils.R @@ -89,8 +89,8 @@ set_preamble = function(input, patterns = knit_patterns$get()) { if (is.na(idx1) || idx1 >= idx2) return() txt = one_string(input[idx1:(idx2 - 1L)]) # rough preamble idx = stringr::str_locate(txt, hb) # locate documentclass - options(tikzDocumentDeclaration = stringr::str_sub(txt, idx[, 1L], idx[, 2L])) - preamble = pure_preamble(split_lines(stringr::str_sub(txt, idx[, 2L] + 1L)), patterns) + options(tikzDocumentDeclaration = str_substitute(txt, idx[, 1L], idx[, 2L])) + preamble = pure_preamble(split_lines(str_substitute(txt, idx[, 2L] + 1L)), patterns) .knitEnv$tikzPackages = c(.header.sweave.cmd, preamble, '\n') .knitEnv$bibliography = grep('^\\\\bibliography.+', input, value = TRUE) } @@ -1085,3 +1085,34 @@ str_split = function(x, split, ...) { y[x == ''] = list('') y } + +str_substitute = function(string, start = 1L, end = -1L) { + + if (is.matrix(start)) { + end = start[, 2] + start = start[, 1] + } + + start = recycler(start, string) + end = recycler(end, string) + + n = nchar(string) + start = ifelse(start < 0, start + n + 1, start) + end = ifelse(end < 0, end + n + 1, end) + + substr(string, start, end) +} + +recycler = function(x, to, arg = deparse(substitute(x))) { + + if (length(x) == length(to)) { + return(x) + } + + if (length(x) != 1) { + stop("Can't recycle `", arg, "` to length ", length(to), call. = FALSE) + } + + rep(x, length(to)) +} + From 712d1f3c14cef0f2d97df7ea9533f3d766cd4e01 Mon Sep 17 00:00:00 2001 From: Richard Iannone Date: Fri, 18 Nov 2022 17:44:21 -0500 Subject: [PATCH 2/8] Replace `stringr::str_sub()` with base R equiv --- R/block.R | 2 +- R/header.R | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/R/block.R b/R/block.R index fd4f05423a..98fe13ccba 100644 --- a/R/block.R +++ b/R/block.R @@ -560,7 +560,7 @@ inline_exec = function( }) d = nchar(input) # replace with evaluated results - stringr::str_sub(input, loc[i, 1], loc[i, 2]) = if (length(res)) { + str_substitute(input, loc[i, 1], loc[i, 2]) = if (length(res)) { paste(hook(res), collapse = '') } else '' if (i < n) loc[(i + 1):n, ] = loc[(i + 1):n, ] - (d - nchar(input)) diff --git a/R/header.R b/R/header.R index 2b063eba1a..07c4c32430 100644 --- a/R/header.R +++ b/R/header.R @@ -56,8 +56,8 @@ insert_header_latex = function(doc, b) { doc[j] = sub(p, '\n\\\\IfFileExists{upquote.sty}{\\\\usepackage{upquote}}{}\n\\2', doc[j], perl = TRUE) } i = i[1L]; l = stringr::str_locate(doc[i], b) - tmp = stringr::str_sub(doc[i], l[, 1], l[, 2]) - stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, make_header_latex(doc)) + tmp = str_substitute(doc[i], l[, 1], l[, 2]) + str_substitute(doc[i], l[,1], l[,2]) = paste0(tmp, make_header_latex(doc)) } else if (parent_mode() && !child_mode()) { # in parent mode, we fill doc to be a complete document doc[1L] = one_string(c( @@ -87,8 +87,8 @@ insert_header_html = function(doc, b) { i = grep(b, doc) if (length(i) == 1L) { l = stringr::str_locate(doc[i], b) - tmp = stringr::str_sub(doc[i], l[, 1], l[, 2]) - stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, '\n', make_header_html()) + tmp = str_substitute(doc[i], l[, 1], l[, 2]) + str_substitute(doc[i], l[,1], l[,2]) = paste0(tmp, '\n', make_header_html()) } doc } From 268594578ec80d732326d7cbc558882a3c05729f Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 18 Nov 2022 22:48:45 -0600 Subject: [PATCH 3/8] Revert "Replace `stringr::str_sub()` with base R equiv" This reverts commit 712d1f3c14cef0f2d97df7ea9533f3d766cd4e01. --- R/block.R | 2 +- R/header.R | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/R/block.R b/R/block.R index 98fe13ccba..fd4f05423a 100644 --- a/R/block.R +++ b/R/block.R @@ -560,7 +560,7 @@ inline_exec = function( }) d = nchar(input) # replace with evaluated results - str_substitute(input, loc[i, 1], loc[i, 2]) = if (length(res)) { + stringr::str_sub(input, loc[i, 1], loc[i, 2]) = if (length(res)) { paste(hook(res), collapse = '') } else '' if (i < n) loc[(i + 1):n, ] = loc[(i + 1):n, ] - (d - nchar(input)) diff --git a/R/header.R b/R/header.R index 07c4c32430..2b063eba1a 100644 --- a/R/header.R +++ b/R/header.R @@ -56,8 +56,8 @@ insert_header_latex = function(doc, b) { doc[j] = sub(p, '\n\\\\IfFileExists{upquote.sty}{\\\\usepackage{upquote}}{}\n\\2', doc[j], perl = TRUE) } i = i[1L]; l = stringr::str_locate(doc[i], b) - tmp = str_substitute(doc[i], l[, 1], l[, 2]) - str_substitute(doc[i], l[,1], l[,2]) = paste0(tmp, make_header_latex(doc)) + tmp = stringr::str_sub(doc[i], l[, 1], l[, 2]) + stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, make_header_latex(doc)) } else if (parent_mode() && !child_mode()) { # in parent mode, we fill doc to be a complete document doc[1L] = one_string(c( @@ -87,8 +87,8 @@ insert_header_html = function(doc, b) { i = grep(b, doc) if (length(i) == 1L) { l = stringr::str_locate(doc[i], b) - tmp = str_substitute(doc[i], l[, 1], l[, 2]) - str_substitute(doc[i], l[,1], l[,2]) = paste0(tmp, '\n', make_header_html()) + tmp = stringr::str_sub(doc[i], l[, 1], l[, 2]) + stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, '\n', make_header_html()) } doc } From 1b23851f8a4db930872cedd09ba8e4acc583d135 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 18 Nov 2022 22:48:49 -0600 Subject: [PATCH 4/8] Revert "Add the `str_substitute()` function" This reverts commit 7632f65fa09845cf0227a9c7b33f72124b01cca4. --- R/utils.R | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/R/utils.R b/R/utils.R index 8f62fafa58..b620a6d6bf 100644 --- a/R/utils.R +++ b/R/utils.R @@ -89,8 +89,8 @@ set_preamble = function(input, patterns = knit_patterns$get()) { if (is.na(idx1) || idx1 >= idx2) return() txt = one_string(input[idx1:(idx2 - 1L)]) # rough preamble idx = stringr::str_locate(txt, hb) # locate documentclass - options(tikzDocumentDeclaration = str_substitute(txt, idx[, 1L], idx[, 2L])) - preamble = pure_preamble(split_lines(str_substitute(txt, idx[, 2L] + 1L)), patterns) + options(tikzDocumentDeclaration = stringr::str_sub(txt, idx[, 1L], idx[, 2L])) + preamble = pure_preamble(split_lines(stringr::str_sub(txt, idx[, 2L] + 1L)), patterns) .knitEnv$tikzPackages = c(.header.sweave.cmd, preamble, '\n') .knitEnv$bibliography = grep('^\\\\bibliography.+', input, value = TRUE) } @@ -1085,34 +1085,3 @@ str_split = function(x, split, ...) { y[x == ''] = list('') y } - -str_substitute = function(string, start = 1L, end = -1L) { - - if (is.matrix(start)) { - end = start[, 2] - start = start[, 1] - } - - start = recycler(start, string) - end = recycler(end, string) - - n = nchar(string) - start = ifelse(start < 0, start + n + 1, start) - end = ifelse(end < 0, end + n + 1, end) - - substr(string, start, end) -} - -recycler = function(x, to, arg = deparse(substitute(x))) { - - if (length(x) == length(to)) { - return(x) - } - - if (length(x) != 1) { - stop("Can't recycle `", arg, "` to length ", length(to), call. = FALSE) - } - - rep(x, length(to)) -} - From 9e3c71ccdfff8edc45b5ac3388176623e8a8521a Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 18 Nov 2022 22:52:04 -0600 Subject: [PATCH 5/8] first try to replace the extract function substr(), because the replacement function `substr<-` is trickier --- R/header.R | 4 ++-- R/utils.R | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/header.R b/R/header.R index 2b063eba1a..74da8f480b 100644 --- a/R/header.R +++ b/R/header.R @@ -56,7 +56,7 @@ insert_header_latex = function(doc, b) { doc[j] = sub(p, '\n\\\\IfFileExists{upquote.sty}{\\\\usepackage{upquote}}{}\n\\2', doc[j], perl = TRUE) } i = i[1L]; l = stringr::str_locate(doc[i], b) - tmp = stringr::str_sub(doc[i], l[, 1], l[, 2]) + tmp = substr(doc[i], l[, 1], l[, 2]) stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, make_header_latex(doc)) } else if (parent_mode() && !child_mode()) { # in parent mode, we fill doc to be a complete document @@ -87,7 +87,7 @@ insert_header_html = function(doc, b) { i = grep(b, doc) if (length(i) == 1L) { l = stringr::str_locate(doc[i], b) - tmp = stringr::str_sub(doc[i], l[, 1], l[, 2]) + tmp = substr(doc[i], l[, 1], l[, 2]) stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, '\n', make_header_html()) } doc diff --git a/R/utils.R b/R/utils.R index b620a6d6bf..a8d0047430 100644 --- a/R/utils.R +++ b/R/utils.R @@ -89,8 +89,8 @@ set_preamble = function(input, patterns = knit_patterns$get()) { if (is.na(idx1) || idx1 >= idx2) return() txt = one_string(input[idx1:(idx2 - 1L)]) # rough preamble idx = stringr::str_locate(txt, hb) # locate documentclass - options(tikzDocumentDeclaration = stringr::str_sub(txt, idx[, 1L], idx[, 2L])) - preamble = pure_preamble(split_lines(stringr::str_sub(txt, idx[, 2L] + 1L)), patterns) + options(tikzDocumentDeclaration = substr(txt, idx[, 1L], idx[, 2L])) + preamble = pure_preamble(split_lines(substr(txt, idx[, 2L] + 1L, nchar(txt))), patterns) .knitEnv$tikzPackages = c(.header.sweave.cmd, preamble, '\n') .knitEnv$bibliography = grep('^\\\\bibliography.+', input, value = TRUE) } From f302524a06d484579649823af364a99dde8814b3 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 18 Nov 2022 23:42:31 -0600 Subject: [PATCH 6/8] add a replacement function str_replace() --- R/header.R | 4 ++-- R/utils-string.R | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 R/utils-string.R diff --git a/R/header.R b/R/header.R index 74da8f480b..a568c7eb88 100644 --- a/R/header.R +++ b/R/header.R @@ -57,7 +57,7 @@ insert_header_latex = function(doc, b) { } i = i[1L]; l = stringr::str_locate(doc[i], b) tmp = substr(doc[i], l[, 1], l[, 2]) - stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, make_header_latex(doc)) + doc[i] = str_replace(doc[i], l, paste0(tmp, make_header_latex(doc))) } else if (parent_mode() && !child_mode()) { # in parent mode, we fill doc to be a complete document doc[1L] = one_string(c( @@ -88,7 +88,7 @@ insert_header_html = function(doc, b) { if (length(i) == 1L) { l = stringr::str_locate(doc[i], b) tmp = substr(doc[i], l[, 1], l[, 2]) - stringr::str_sub(doc[i], l[,1], l[,2]) = paste0(tmp, '\n', make_header_html()) + doc[i] = str_replace(doc[i], l, paste0(tmp, '\n', make_header_html())) } doc } diff --git a/R/utils-string.R b/R/utils-string.R new file mode 100644 index 0000000000..858befdb85 --- /dev/null +++ b/R/utils-string.R @@ -0,0 +1,10 @@ +# replace parts of a string with new values; `pos` is a matrix of positions and +# each row is a pair of [start, end] +str_replace = function(x, pos, value) { + if (length(x) != 1) stop("Only a character scalar is supported.") + # extract parts of the string that are outside [start, end] + m = rbind(pos[, 1] - 1, pos[, 2] + 1) + m = matrix(c(1, m, nchar(x)), nrow = 2) + y = substring(x, m[1, ], m[2, ]) + paste(rbind(y, c(value, '')), collapse = '') +} From 300cc4f03e77125db17a521e3a42e89f0c042323 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 18 Nov 2022 23:47:38 -0600 Subject: [PATCH 7/8] add new script to Collate --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index 753c713ecd..baaff73e67 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -191,6 +191,7 @@ Collate: 'template.R' 'utils-conversion.R' 'utils-rd2html.R' + 'utils-string.R' 'utils-sweave.R' 'utils-upload.R' 'utils-vignettes.R' From a1e41a0bfa6db6a7c5b1a67161714a63a623cea4 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 18 Nov 2022 23:50:26 -0600 Subject: [PATCH 8/8] use the new str_replace() in inline_exec() to simplify the original code (no longer need to hack the location matrix) --- R/block.R | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/R/block.R b/R/block.R index 4c16ecbe84..06e7f84078 100644 --- a/R/block.R +++ b/R/block.R @@ -551,22 +551,17 @@ inline_exec = function( code = block$code; input = block$input if ((n <- length(code)) == 0) return(input) # untouched if no code is found - loc = block$location + ans = character(n) for (i in 1:n) { res = hook_eval(code[i], envir) if (inherits(res, c('knit_asis', 'knit_asis_url'))) res = sew(res, inline = TRUE) tryCatch(as.character(res), error = function(e) { stop2("The inline value cannot be coerced to character: ", code[i]) }) - d = nchar(input) - # replace with evaluated results - stringr::str_sub(input, loc[i, 1], loc[i, 2]) = if (length(res)) { - paste(hook(res), collapse = '') - } else '' - if (i < n) loc[(i + 1):n, ] = loc[(i + 1):n, ] - (d - nchar(input)) - # may need to move back and forth because replacement may be longer or shorter + if (length(res)) ans[i] = paste(hook(res), collapse = '') } - input + # replace with evaluated results + str_replace(input, block$location, ans) } process_tangle = function(x) {