From 0288cbd550b79a85180bf4813cfe52466150b5df Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 21:36:51 +0200 Subject: [PATCH 01/20] First draft of rtf_rich_text --- DESCRIPTION | 1 + NAMESPACE | 2 + R/rtf_rich_text.R | 117 ++++++++++++++++++ man/rtf_rich_text.Rd | 41 ++++++ .../test-developer-testing-rtf_rich_text.R | 19 +++ 5 files changed, 180 insertions(+) create mode 100644 R/rtf_rich_text.R create mode 100644 man/rtf_rich_text.Rd create mode 100644 tests/testthat/test-developer-testing-rtf_rich_text.R diff --git a/DESCRIPTION b/DESCRIPTION index d98661fa..da9c71ad 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,6 +32,7 @@ VignetteBuilder: knitr LazyData: true Depends: R (>= 3.5.0) Imports: + checkmate, grDevices, tools Suggests: diff --git a/NAMESPACE b/NAMESPACE index 81a448db..5875ed45 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,5 +17,7 @@ export(rtf_subline) export(rtf_title) export(utf8Tortf) export(write_rtf) +importFrom(checkmate,assert_choice) +importFrom(checkmate,assert_true) importFrom(grDevices,colors) importFrom(utils,tail) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R new file mode 100644 index 00000000..15d97007 --- /dev/null +++ b/R/rtf_rich_text.R @@ -0,0 +1,117 @@ +# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. All rights reserved. +# +# This file is part of the r2rtf program. +# +# r2rtf is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#' Text to formatted RTF Encode +#' +#' @param text Plain text. +#' @param theme Named list defining themes for tags. See \code(rtf_text) for +#' details on possible formatting. +#' +#' @section Specification: +#' \if{latex}{ +#' \itemize{ +#' \item Validate if theme list items correspond to \code{font_type()} arguments. +#' \item Create regex expressions to match `{}` and `.tag` in text. +#' \item Extract tagged text from input text. +#' \item Extract tags from tagged text. +#' \item Extract text from tagged text. +#' \item Validate that lengths of extractions are all the same. +#' \item Validate that tags are defined in the `theme` argument. +#' \item Execute `rtf_text` with extracted text and relevant formatting. +#' \item Reinsert encoded formatted text to original input text. +#' } +#' } +#' \if{html}{The contents of this section are shown in PDF user manual only.} +#' +#' @importFrom checkmate assert_choice assert_true +#' +#' @export +#' +#' @examples +#' rtf_rich_text() +rtf_rich_text <- function(text = "This is {.emph important}. This is {.blah relevant}.", + theme = list( + .emph = list(color = "blue", `format` = "b"), + .blah = list(color = "red") + )) { + + # bulletproof theme + theme_args <- theme + names(theme_args) <- NULL + unique_theme_args <- unique(names(unlist(theme_args))) + purrr::walk( + unique_theme_args, + ~ checkmate::assert_choice(.x, + choices = names(formals(rtf_text)), + label = paste0("theme: ", .x) + ) + ) + + # Regex patterns for parsing input text. + matches_pattern <- "(?<=\\{).*?(?=\\})" + extraction_pattern <- "(^\\.[A-Za-z]*)(\\s)(.*$)" + + extracted <- list() + + # find all paired braces in text string + extracted$matches <- regmatches( + x = text, + m = gregexpr(matches_pattern, + text = text, + perl = TRUE + ) + )[[1]] + + # for each paired brace: extract the theme tag (only allow one per match string) + extracted$tags <- gsub( + pattern = extraction_pattern, + x = extracted$matches, replacement = "\\1" + ) + checkmate::assert_true(length(extracted$tags) == length(extracted$matches)) + + # for each paired brace: extract the text to be wrapped with rtf_text() + extracted$text <- gsub( + pattern = extraction_pattern, + x = extracted$matches, replacement = "\\3" + ) + checkmate::assert_true(length(extracted$text) == length(extracted$matches)) + + # validate that tags in text are reflected in themes argument + purrr::walk(extracted$tags, + .f = ~ checkmate::assert_choice(.x, choices = names(theme)) + ) + + # execute rtf_text() calls with theme tags. + extracted$replacements <- purrr::map2_chr( + .x = extracted$text, + .y = extracted$tags, + .f = ~ do.call(rtf_text, args = c(text = .x, theme[[.y]])) + ) + + # insert rtf_text() calls into original text. + new_text <- text + for (i in seq_along(extracted$matches)) { + new_text <- gsub( + x = new_text, + pattern = paste0("{", extracted$matches[i], "}"), + replacement = extracted$replacements[i], + fixed = TRUE + ) + } + + new_text +} diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd new file mode 100644 index 00000000..3a03bf77 --- /dev/null +++ b/man/rtf_rich_text.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rtf_rich_text.R +\name{rtf_rich_text} +\alias{rtf_rich_text} +\title{Text to formatted RTF Encode} +\usage{ +rtf_rich_text( + text = "This is {.emph important}. This is {.blah relevant}.", + theme = list(.emph = list(color = "blue", format = "b"), .blah = list(color = "red")) +) +} +\arguments{ +\item{text}{Plain text.} + +\item{theme}{Named list defining themes for tags. See \code(rtf_text) for +details on possible formatting.} +} +\description{ +Text to formatted RTF Encode +} +\section{Specification}{ + +\if{latex}{ + \itemize{ + \item Validate if theme list items correspond to \code{font_type()} arguments. + \item Create regex expressions to match `{}` and `.tag` in text. + \item Extract tagged text from input text. + \item Extract tags from tagged text. + \item Extract text from tagged text. + \item Validate that lengths of extractions are all the same. + \item Validate that tags are defined in the `theme` argument. + \item Execute `rtf_text` with extracted text and relevant formatting. + \item Reinsert encoded formatted text to original input text. + } + } +\if{html}{The contents of this section are shown in PDF user manual only.} +} + +\examples{ +rtf_rich_text() +} diff --git a/tests/testthat/test-developer-testing-rtf_rich_text.R b/tests/testthat/test-developer-testing-rtf_rich_text.R new file mode 100644 index 00000000..4827cf6f --- /dev/null +++ b/tests/testthat/test-developer-testing-rtf_rich_text.R @@ -0,0 +1,19 @@ +test_that("rtf_rich_text fundamentally works.", { + output <- rtf_rich_text( + text = "This is {.emph important}. This is {.blah relevant}.", + theme = list( + .emph = list(color = "blue", `format` = "b"), + .blah = list(color = "red") + ) + ) + + expectation <- paste0( + "This is ", + rtf_text("important", color = "blue", `format` = "b"), + ". This is ", + rtf_text("relevant", color = "red"), + "." + ) + + expect_equal(output, expectation) +}) From f0cceeebd0554bd266e619ae721a47fb5e48a9ce Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 19:38:45 +0000 Subject: [PATCH 02/20] Style code --- R/rtf_rich_text.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 15d97007..60489865 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -48,7 +48,6 @@ rtf_rich_text <- function(text = "This is {.emph important}. This is {.blah rele .emph = list(color = "blue", `format` = "b"), .blah = list(color = "red") )) { - # bulletproof theme theme_args <- theme names(theme_args) <- NULL From 118fec099a1facb7e74bb49f4105a669114ee20d Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 21:46:33 +0200 Subject: [PATCH 03/20] update pkgdown.yml --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index a7501be9..e1c04ec2 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -70,6 +70,7 @@ articles: reference: - title: RTF Table contents: + - "rtf_rich_text" - "rtf_page" - "rtf_page_header" - "rtf_page_footer" From 80dc178862d6aa646a3290852dfd95a164d0d80a Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 21:49:02 +0200 Subject: [PATCH 04/20] update assertion for correct argument --- R/rtf_rich_text.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 60489865..5880fefb 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -56,7 +56,7 @@ rtf_rich_text <- function(text = "This is {.emph important}. This is {.blah rele unique_theme_args, ~ checkmate::assert_choice(.x, choices = names(formals(rtf_text)), - label = paste0("theme: ", .x) + .var.name = paste0("theme: ", .x) ) ) From e06e61490189a1b00bcdffe620daf6769e72b34c Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 21:58:52 +0200 Subject: [PATCH 05/20] add purrr to dependencies and fix roxygen --- DESCRIPTION | 1 + NAMESPACE | 3 +++ R/rtf_rich_text.R | 5 +++-- man/rtf_rich_text.Rd | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index da9c71ad..cf5476be 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -34,6 +34,7 @@ Depends: R (>= 3.5.0) Imports: checkmate, grDevices, + purrr, tools Suggests: covr, diff --git a/NAMESPACE b/NAMESPACE index 5875ed45..697c578b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -12,6 +12,7 @@ export(rtf_page_footer) export(rtf_page_header) export(rtf_read_figure) export(rtf_read_png) +export(rtf_rich_text) export(rtf_source) export(rtf_subline) export(rtf_title) @@ -20,4 +21,6 @@ export(write_rtf) importFrom(checkmate,assert_choice) importFrom(checkmate,assert_true) importFrom(grDevices,colors) +importFrom(purrr,map2_chr) +importFrom(purrr,walk) importFrom(utils,tail) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 5880fefb..5c756642 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -18,7 +18,7 @@ #' Text to formatted RTF Encode #' #' @param text Plain text. -#' @param theme Named list defining themes for tags. See \code(rtf_text) for +#' @param theme Named list defining themes for tags. See \code{rtf_text()} for #' details on possible formatting. #' #' @section Specification: @@ -31,13 +31,14 @@ #' \item Extract text from tagged text. #' \item Validate that lengths of extractions are all the same. #' \item Validate that tags are defined in the `theme` argument. -#' \item Execute `rtf_text` with extracted text and relevant formatting. +#' \item Execute \code{rtf_text} with extracted text and relevant formatting. #' \item Reinsert encoded formatted text to original input text. #' } #' } #' \if{html}{The contents of this section are shown in PDF user manual only.} #' #' @importFrom checkmate assert_choice assert_true +#' @importFrom purrr walk map2_chr #' #' @export #' diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index 3a03bf77..93c84d3d 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -12,7 +12,7 @@ rtf_rich_text( \arguments{ \item{text}{Plain text.} -\item{theme}{Named list defining themes for tags. See \code(rtf_text) for +\item{theme}{Named list defining themes for tags. See \code{rtf_text()} for details on possible formatting.} } \description{ @@ -29,7 +29,7 @@ Text to formatted RTF Encode \item Extract text from tagged text. \item Validate that lengths of extractions are all the same. \item Validate that tags are defined in the `theme` argument. - \item Execute `rtf_text` with extracted text and relevant formatting. + \item Execute \code{rtf_text} with extracted text and relevant formatting. \item Reinsert encoded formatted text to original input text. } } From 0c875c51c7b4677a39c2ac146fa2605de608fbe8 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 22:30:56 +0200 Subject: [PATCH 06/20] update for check_args example test --- R/rtf_rich_text.R | 6 ++-- man/rtf_rich_text.Rd | 2 +- .../test-developer-testing-rtf_rich_text.R | 29 +++++++++++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 5c756642..3f415da4 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -31,7 +31,7 @@ #' \item Extract text from tagged text. #' \item Validate that lengths of extractions are all the same. #' \item Validate that tags are defined in the `theme` argument. -#' \item Execute \code{rtf_text} with extracted text and relevant formatting. +#' \item Execute \code{rtf_text()} with extracted text and relevant formatting. #' \item Reinsert encoded formatted text to original input text. #' } #' } @@ -44,7 +44,7 @@ #' #' @examples #' rtf_rich_text() -rtf_rich_text <- function(text = "This is {.emph important}. This is {.blah relevant}.", +rtf_rich_text <- function(text, theme = list( .emph = list(color = "blue", `format` = "b"), .blah = list(color = "red") @@ -113,5 +113,5 @@ rtf_rich_text <- function(text = "This is {.emph important}. This is {.blah rele ) } - new_text + rtf_text(new_text) } diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index 93c84d3d..03e54d71 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -5,7 +5,7 @@ \title{Text to formatted RTF Encode} \usage{ rtf_rich_text( - text = "This is {.emph important}. This is {.blah relevant}.", + text, theme = list(.emph = list(color = "blue", format = "b"), .blah = list(color = "red")) ) } diff --git a/tests/testthat/test-developer-testing-rtf_rich_text.R b/tests/testthat/test-developer-testing-rtf_rich_text.R index 4827cf6f..b509f38e 100644 --- a/tests/testthat/test-developer-testing-rtf_rich_text.R +++ b/tests/testthat/test-developer-testing-rtf_rich_text.R @@ -7,13 +7,38 @@ test_that("rtf_rich_text fundamentally works.", { ) ) - expectation <- paste0( + expectation <- rtf_text(paste0( "This is ", rtf_text("important", color = "blue", `format` = "b"), ". This is ", rtf_text("relevant", color = "red"), "." - ) + )) expect_equal(output, expectation) }) + +test_that("rtf_rich_text works with example from check_args.", { + output <- r2rtf:::rtf_paragraph( + r2rtf:::rtf_rich_text( + text = "3.5{.ft \\dagger}\\line{.red red} {.hl highlight}", + theme = list( + .ft = list(format = "^"), + .red = list(color = "red"), + .hl = list(background_color = "yellow") + ) + )) + + expectation <- r2rtf:::rtf_paragraph( + r2rtf:::rtf_text(paste0( + "3.5", + r2rtf:::rtf_text("\\dagger", format = "^"), + "\\line", + r2rtf:::rtf_text("red", color = "red"), + " ", + r2rtf:::rtf_text("highlight", background_color = "yellow") + ))) + + expect_equal(output, expectation) +}) + From 50c90cf004444b656a8646759910a6f8bd9a4efa Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 22:32:45 +0200 Subject: [PATCH 07/20] update examples --- R/rtf_rich_text.R | 7 ++++++- man/rtf_rich_text.Rd | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 3f415da4..1c3c158a 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -43,7 +43,12 @@ #' @export #' #' @examples -#' rtf_rich_text() +#' rtf_rich_text(text = "This is {.emph important}. This is {.blah relevant}.", +#' theme = list( +#` .emph = list(color = "blue", `format` = "b"), +#` .blah = list(color = "red") +#` `)) +# rtf_rich_text <- function(text, theme = list( .emph = list(color = "blue", `format` = "b"), diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index 03e54d71..b501dd40 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -29,7 +29,7 @@ Text to formatted RTF Encode \item Extract text from tagged text. \item Validate that lengths of extractions are all the same. \item Validate that tags are defined in the `theme` argument. - \item Execute \code{rtf_text} with extracted text and relevant formatting. + \item Execute \code{rtf_text()} with extracted text and relevant formatting. \item Reinsert encoded formatted text to original input text. } } @@ -37,5 +37,6 @@ Text to formatted RTF Encode } \examples{ -rtf_rich_text() +rtf_rich_text(text = "This is {.emph important}. This is {.blah relevant}.", +theme = list( } From 283d0eff2c649c308204afaad21da16c702dc180 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 22:37:58 +0200 Subject: [PATCH 08/20] update examples --- R/rtf_rich_text.R | 8 ++++---- man/rtf_rich_text.Rd | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 1c3c158a..c2a7621c 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -45,10 +45,10 @@ #' @examples #' rtf_rich_text(text = "This is {.emph important}. This is {.blah relevant}.", #' theme = list( -#` .emph = list(color = "blue", `format` = "b"), -#` .blah = list(color = "red") -#` `)) -# +#' .emph = list(color = "blue", `format` = "b"), +#' .blah = list(color = "red") +#' )) +#' rtf_rich_text <- function(text, theme = list( .emph = list(color = "blue", `format` = "b"), diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index b501dd40..fe804c29 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -39,4 +39,8 @@ Text to formatted RTF Encode \examples{ rtf_rich_text(text = "This is {.emph important}. This is {.blah relevant}.", theme = list( +.emph = list(color = "blue", `format` = "b"), +.blah = list(color = "red") +)) + } From 037f4c52df61cefa8badb668ee0f681199a802a6 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 5 May 2023 20:40:34 +0000 Subject: [PATCH 09/20] Style code --- R/rtf_rich_text.R | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index c2a7621c..ab683a8b 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -43,11 +43,13 @@ #' @export #' #' @examples -#' rtf_rich_text(text = "This is {.emph important}. This is {.blah relevant}.", -#' theme = list( -#' .emph = list(color = "blue", `format` = "b"), -#' .blah = list(color = "red") -#' )) +#' rtf_rich_text( +#' text = "This is {.emph important}. This is {.blah relevant}.", +#' theme = list( +#' .emph = list(color = "blue", `format` = "b"), +#' .blah = list(color = "red") +#' ) +#' ) #' rtf_rich_text <- function(text, theme = list( From 109f5d591e3017d9bd52dd0e05a74d3298f505eb Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 30 Jun 2023 22:30:07 +0200 Subject: [PATCH 10/20] remove dependencies enable proper matching of nested braces --- NAMESPACE | 4 - R/rtf_rich_text.R | 179 +++++++++++++----- man/check_braces.Rd | 15 ++ man/extract_tagged_text.Rd | 15 ++ man/match_braces.Rd | 17 ++ man/rtf_rich_text.Rd | 10 +- .../test-developer-testing-rtf_rich_text.R | 7 +- 7 files changed, 184 insertions(+), 63 deletions(-) create mode 100644 man/check_braces.Rd create mode 100644 man/extract_tagged_text.Rd create mode 100644 man/match_braces.Rd diff --git a/NAMESPACE b/NAMESPACE index 697c578b..a05a3863 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -18,9 +18,5 @@ export(rtf_subline) export(rtf_title) export(utf8Tortf) export(write_rtf) -importFrom(checkmate,assert_choice) -importFrom(checkmate,assert_true) importFrom(grDevices,colors) -importFrom(purrr,map2_chr) -importFrom(purrr,walk) importFrom(utils,tail) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index ab683a8b..d512c74f 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -37,79 +37,75 @@ #' } #' \if{html}{The contents of this section are shown in PDF user manual only.} #' -#' @importFrom checkmate assert_choice assert_true -#' @importFrom purrr walk map2_chr -#' #' @export #' #' @examples -#' rtf_rich_text( -#' text = "This is {.emph important}. This is {.blah relevant}.", -#' theme = list( -#' .emph = list(color = "blue", `format` = "b"), -#' .blah = list(color = "red") -#' ) -#' ) +#' rtf_rich_text(text = "This is {.emph important}. This is {.strong relevant}. +#' This is {.zebra ZEBRA}.", +#' theme = list( +#' .emph = list(format = "i"), +#' .strong = list(format = "b"), +#' .zebra = list(color = "white", `background_color = "black") +#' )) #' rtf_rich_text <- function(text, theme = list( - .emph = list(color = "blue", `format` = "b"), - .blah = list(color = "red") + .emph = list(format = "i"), + .strong = list(format = "b") )) { - # bulletproof theme - theme_args <- theme - names(theme_args) <- NULL - unique_theme_args <- unique(names(unlist(theme_args))) - purrr::walk( - unique_theme_args, - ~ checkmate::assert_choice(.x, - choices = names(formals(rtf_text)), - .var.name = paste0("theme: ", .x) - ) - ) - - # Regex patterns for parsing input text. - matches_pattern <- "(?<=\\{).*?(?=\\})" - extraction_pattern <- "(^\\.[A-Za-z]*)(\\s)(.*$)" + # Bulletproof the styles requested within the theme argument. + theme_arg <- theme + names(theme_arg) <- NULL + unique_styles <- unique(names(unlist(theme_arg))) + bad_style <- unique_styles[!(unique_styles %in% names(formals(rtf_text)))] + if(length(bad_style) > 0){ + stop("Theme lists have styles which are not supported (" , + paste0(bad_style, collapse = ", "),").") + } + # Find all paired braces in text string. extracted <- list() + extracted$matches <- gsub(pattern = "^\\{",replacement = "", + gsub(pattern = "\\}$", replacement = "", + x = extract_tagged_text(text))) - # find all paired braces in text string - extracted$matches <- regmatches( - x = text, - m = gregexpr(matches_pattern, - text = text, - perl = TRUE - ) - )[[1]] + # For each paired brace: extract the theme tag (only allow one per match string). + # Regex patterns for parsing input text. + extraction_pattern <- "(^\\.[A-Za-z]*)(\\s)(.*$)" - # for each paired brace: extract the theme tag (only allow one per match string) extracted$tags <- gsub( pattern = extraction_pattern, x = extracted$matches, replacement = "\\1" ) - checkmate::assert_true(length(extracted$tags) == length(extracted$matches)) + if(length(extracted$tags) != length(extracted$matches)){ + stop("Length missmatch of tags found and matches found") + } - # for each paired brace: extract the text to be wrapped with rtf_text() + # For each paired brace: extract the text to be wrapped with rtf_text() extracted$text <- gsub( pattern = extraction_pattern, x = extracted$matches, replacement = "\\3" ) - checkmate::assert_true(length(extracted$text) == length(extracted$matches)) + if(length(extracted$text) != length(extracted$matches)){ + stop("Length missmatch of extracted text found and matches found") + } - # validate that tags in text are reflected in themes argument - purrr::walk(extracted$tags, - .f = ~ checkmate::assert_choice(.x, choices = names(theme)) - ) + # Validate that tags in text are reflected in themes argument + missing_themes <- extracted$tags[!(extracted$tags %in% names(theme))] + if(length(missing_themes) != 0){ + stop("Input text has tags which are not available in the theme (", + paste0(missing_themes, collapse = ", "),").") + } - # execute rtf_text() calls with theme tags. - extracted$replacements <- purrr::map2_chr( - .x = extracted$text, - .y = extracted$tags, - .f = ~ do.call(rtf_text, args = c(text = .x, theme[[.y]])) - ) + # Execute rtf_text() calls with theme tags. + extracted$replacements <- vapply(X = seq_along(extracted$tags), + FUN = function(x){ + do.call(rtf_text, + args = c(text = extracted$text[x], theme[[extracted$tags[x]]])) + }, + FUN.VALUE = "character") - # insert rtf_text() calls into original text. + # Insert rtf_text() calls into original text. new_text <- text for (i in seq_along(extracted$matches)) { new_text <- gsub( @@ -122,3 +118,86 @@ rtf_rich_text <- function(text, rtf_text(new_text) } + +#' Extract tagged text +#' +#' Identify the text that is in brackets and correctly resolve the ordering of +#' the brackets such that everything is correctly tagged with the needed style. +#' +#' @param input Plain text containing matched curly braces with tags. +#' +extract_tagged_text <- function(input) { + opening <- gregexec("\\{", text = input, perl = TRUE)[[1]] + closing <- gregexec("\\}", text = input, perl = TRUE)[[1]] + styles <- gregexec("\\{\\.[A-Za-z]*", text = input, perl = TRUE) + + # Check for equal number of opening and closing braces. + check_braces(input) + + # Identify matching brace pairs. + brace_matches <- match_braces(opening, closing) + + # Identify which matched braces are associated with a style tag. + styles_matches <- brace_matches[which(brace_matches$opening %in% styles[[1]]), ] + + # Extract tagged brackets. + unname(apply(styles_matches, + MARGIN = 1, + FUN = function(X) { + substr(input, start = X[["opening"]], stop = X[["closing"]]) + }) + ) + +} + +#' Identify opening and closing brace pairs +#' +#' Identify which opening and closing braces in a string belong together. Follows +#' a first-in-last-out matching. +#' +#' @param openings Vector of indices indicating location of opening braces. +#' @param closings Vector of indices indicating location of closing braces. +#' +match_braces <- function(openings, closings) { + # Verify that equal numbers of opening and closing braces exist + stopifnot(length(openings) == length(closings)) + + # Create data frame to hold opening / closing pairs + holder <- data.frame( + opening = numeric(length(openings)), + closing = numeric(length(closings)) + ) + + for (i in seq_along(closings)) { + # Find the matching opening brace. + # The matching opening brace is the one most recently preceding a closing brace. + match_opening <- max(openings[openings < closings[i]]) + # Record the opening brace index to be removed. + match_opening_index <- which(openings == match_opening) + # Add the pair of opening and closing braces to the holder data frame. + holder[i, ] <- c(match_opening, closings[i]) + # Remove the matching opening brace from the vector of opening braces. + openings <- openings[-match_opening_index] + } + + holder +} + +#' Check that braces are correctly matched +#' +#' Braces must be matched appropriately. First brace should be an opening brace. +#' Every brace should be closed appropriate. +#' +#' @param input Plain text containing matched curly braces with tags. +#' +check_braces <- function(input){ + input_parse <- gsub(x = input, pattern = "[^{}]", replacement = "") + input_split <- unlist(strsplit(input_parse, '')) + checker <- ifelse(input_split == "{", 1, -1) + if (grepl(x = input, pattern = "(\\\\{)|(\\\\})", perl = TRUE)) { + warning(c("It seems that you have some escaped brackets in your input,", + " this might not work as expected.")) + } + if (sum(checker) != 0) stop("Number of opening { and closing } must match.") + if (any(cumsum(checker) < 0)) stop("Input has at least one unpaired '{'.") +} diff --git a/man/check_braces.Rd b/man/check_braces.Rd new file mode 100644 index 00000000..ab5faf18 --- /dev/null +++ b/man/check_braces.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rtf_rich_text.R +\name{check_braces} +\alias{check_braces} +\title{Check that braces are correctly matched} +\usage{ +check_braces(input) +} +\arguments{ +\item{input}{Plain text containing matched curly braces with tags.} +} +\description{ +Braces must be matched appropriately. First brace should be an opening brace. +Every brace should be closed appropriate. +} diff --git a/man/extract_tagged_text.Rd b/man/extract_tagged_text.Rd new file mode 100644 index 00000000..bc5a24b1 --- /dev/null +++ b/man/extract_tagged_text.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rtf_rich_text.R +\name{extract_tagged_text} +\alias{extract_tagged_text} +\title{Extract tagged text} +\usage{ +extract_tagged_text(input) +} +\arguments{ +\item{input}{Plain text containing matched curly braces with tags.} +} +\description{ +Identify the text that is in brackets and correctly resolve the ordering of +the brackets such that everything is correctly tagged with the needed style. +} diff --git a/man/match_braces.Rd b/man/match_braces.Rd new file mode 100644 index 00000000..5f560e0b --- /dev/null +++ b/man/match_braces.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rtf_rich_text.R +\name{match_braces} +\alias{match_braces} +\title{Identify opening and closing brace pairs} +\usage{ +match_braces(openings, closings) +} +\arguments{ +\item{openings}{Vector of indices indicating location of opening braces.} + +\item{closings}{Vector of indices indicating location of closing braces.} +} +\description{ +Identify which opening and closing braces in a string belong together. Follows +a first-in-last-out matching. +} diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index fe804c29..f7c9e9dd 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -6,7 +6,7 @@ \usage{ rtf_rich_text( text, - theme = list(.emph = list(color = "blue", format = "b"), .blah = list(color = "red")) + theme = list(.emph = list(format = "i"), .strong = list(format = "b")) ) } \arguments{ @@ -37,10 +37,12 @@ Text to formatted RTF Encode } \examples{ -rtf_rich_text(text = "This is {.emph important}. This is {.blah relevant}.", +rtf_rich_text(text = "This is {.emph important}. This is {.strong relevant}. +This is {.zebra ZEBRA}.", theme = list( -.emph = list(color = "blue", `format` = "b"), -.blah = list(color = "red") +.emph = list(format = "i"), +.strong = list(format = "b"), +.zebra = list(color = "white", `background_color = "black") )) } diff --git a/tests/testthat/test-developer-testing-rtf_rich_text.R b/tests/testthat/test-developer-testing-rtf_rich_text.R index b509f38e..5f02ab4e 100644 --- a/tests/testthat/test-developer-testing-rtf_rich_text.R +++ b/tests/testthat/test-developer-testing-rtf_rich_text.R @@ -21,9 +21,8 @@ test_that("rtf_rich_text fundamentally works.", { test_that("rtf_rich_text works with example from check_args.", { output <- r2rtf:::rtf_paragraph( r2rtf:::rtf_rich_text( - text = "3.5{.ft \\dagger}\\line{.red red} {.hl highlight}", + text = "3.5{^\\dagger}\n{.red red} {.hl highlight}", theme = list( - .ft = list(format = "^"), .red = list(color = "red"), .hl = list(background_color = "yellow") ) @@ -31,9 +30,7 @@ test_that("rtf_rich_text works with example from check_args.", { expectation <- r2rtf:::rtf_paragraph( r2rtf:::rtf_text(paste0( - "3.5", - r2rtf:::rtf_text("\\dagger", format = "^"), - "\\line", + "3.5{^\\dagger}\\line ", r2rtf:::rtf_text("red", color = "red"), " ", r2rtf:::rtf_text("highlight", background_color = "yellow") From 30e29df7ab2c4249051cdb2073549396ac92da4a Mon Sep 17 00:00:00 2001 From: BrianLang Date: Fri, 30 Jun 2023 22:35:41 +0200 Subject: [PATCH 11/20] remove checkmate and purrr from dependencies --- DESCRIPTION | 2 -- 1 file changed, 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index cf5476be..d98661fa 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,9 +32,7 @@ VignetteBuilder: knitr LazyData: true Depends: R (>= 3.5.0) Imports: - checkmate, grDevices, - purrr, tools Suggests: covr, From 907306bba001d4cebf83640e32b0f27925de8722 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Sat, 1 Jul 2023 07:27:35 +0200 Subject: [PATCH 12/20] fix broken example --- R/rtf_rich_text.R | 6 +++--- man/rtf_rich_text.Rd | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index d512c74f..69e56ddd 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -40,12 +40,12 @@ #' @export #' #' @examples -#' rtf_rich_text(text = "This is {.emph important}. This is {.strong relevant}. -#' This is {.zebra ZEBRA}.", +#' rtf_rich_text(text = paste("This is {.emph important}.", +#' "This is {.strong relevant}.", "This is {.zebra ZEBRA}."), #' theme = list( #' .emph = list(format = "i"), #' .strong = list(format = "b"), -#' .zebra = list(color = "white", `background_color = "black") +#' .zebra = list(color = "white", background_color = "black") #' )) #' rtf_rich_text <- function(text, diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index f7c9e9dd..3e40cf0c 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -37,12 +37,12 @@ Text to formatted RTF Encode } \examples{ -rtf_rich_text(text = "This is {.emph important}. This is {.strong relevant}. -This is {.zebra ZEBRA}.", +rtf_rich_text(text = paste("This is {.emph important}.", +"This is {.strong relevant}.", "This is {.zebra ZEBRA}."), theme = list( .emph = list(format = "i"), .strong = list(format = "b"), -.zebra = list(color = "white", `background_color = "black") +.zebra = list(color = "white", background_color = "black") )) } From 3dea338e165fcf54a96176d8ea48e47364126767 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Sat, 1 Jul 2023 05:29:40 +0000 Subject: [PATCH 13/20] Style code --- R/rtf_rich_text.R | 78 ++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 69e56ddd..ecf49582 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -40,13 +40,17 @@ #' @export #' #' @examples -#' rtf_rich_text(text = paste("This is {.emph important}.", -#' "This is {.strong relevant}.", "This is {.zebra ZEBRA}."), -#' theme = list( -#' .emph = list(format = "i"), -#' .strong = list(format = "b"), -#' .zebra = list(color = "white", background_color = "black") -#' )) +#' rtf_rich_text( +#' text = paste( +#' "This is {.emph important}.", +#' "This is {.strong relevant}.", "This is {.zebra ZEBRA}." +#' ), +#' theme = list( +#' .emph = list(format = "i"), +#' .strong = list(format = "b"), +#' .zebra = list(color = "white", background_color = "black") +#' ) +#' ) #' rtf_rich_text <- function(text, theme = list( @@ -58,16 +62,22 @@ rtf_rich_text <- function(text, names(theme_arg) <- NULL unique_styles <- unique(names(unlist(theme_arg))) bad_style <- unique_styles[!(unique_styles %in% names(formals(rtf_text)))] - if(length(bad_style) > 0){ - stop("Theme lists have styles which are not supported (" , - paste0(bad_style, collapse = ", "),").") + if (length(bad_style) > 0) { + stop( + "Theme lists have styles which are not supported (", + paste0(bad_style, collapse = ", "), ")." + ) } # Find all paired braces in text string. extracted <- list() - extracted$matches <- gsub(pattern = "^\\{",replacement = "", - gsub(pattern = "\\}$", replacement = "", - x = extract_tagged_text(text))) + extracted$matches <- gsub( + pattern = "^\\{", replacement = "", + gsub( + pattern = "\\}$", replacement = "", + x = extract_tagged_text(text) + ) + ) # For each paired brace: extract the theme tag (only allow one per match string). # Regex patterns for parsing input text. @@ -77,7 +87,7 @@ rtf_rich_text <- function(text, pattern = extraction_pattern, x = extracted$matches, replacement = "\\1" ) - if(length(extracted$tags) != length(extracted$matches)){ + if (length(extracted$tags) != length(extracted$matches)) { stop("Length missmatch of tags found and matches found") } @@ -86,24 +96,29 @@ rtf_rich_text <- function(text, pattern = extraction_pattern, x = extracted$matches, replacement = "\\3" ) - if(length(extracted$text) != length(extracted$matches)){ + if (length(extracted$text) != length(extracted$matches)) { stop("Length missmatch of extracted text found and matches found") } # Validate that tags in text are reflected in themes argument missing_themes <- extracted$tags[!(extracted$tags %in% names(theme))] - if(length(missing_themes) != 0){ - stop("Input text has tags which are not available in the theme (", - paste0(missing_themes, collapse = ", "),").") + if (length(missing_themes) != 0) { + stop( + "Input text has tags which are not available in the theme (", + paste0(missing_themes, collapse = ", "), ")." + ) } # Execute rtf_text() calls with theme tags. - extracted$replacements <- vapply(X = seq_along(extracted$tags), - FUN = function(x){ + extracted$replacements <- vapply( + X = seq_along(extracted$tags), + FUN = function(x) { do.call(rtf_text, - args = c(text = extracted$text[x], theme[[extracted$tags[x]]])) - }, - FUN.VALUE = "character") + args = c(text = extracted$text[x], theme[[extracted$tags[x]]]) + ) + }, + FUN.VALUE = "character" + ) # Insert rtf_text() calls into original text. new_text <- text @@ -145,9 +160,8 @@ extract_tagged_text <- function(input) { MARGIN = 1, FUN = function(X) { substr(input, start = X[["opening"]], stop = X[["closing"]]) - }) - ) - + } + )) } #' Identify opening and closing brace pairs @@ -190,14 +204,16 @@ match_braces <- function(openings, closings) { #' #' @param input Plain text containing matched curly braces with tags. #' -check_braces <- function(input){ +check_braces <- function(input) { input_parse <- gsub(x = input, pattern = "[^{}]", replacement = "") - input_split <- unlist(strsplit(input_parse, '')) + input_split <- unlist(strsplit(input_parse, "")) checker <- ifelse(input_split == "{", 1, -1) if (grepl(x = input, pattern = "(\\\\{)|(\\\\})", perl = TRUE)) { - warning(c("It seems that you have some escaped brackets in your input,", - " this might not work as expected.")) - } + warning(c( + "It seems that you have some escaped brackets in your input,", + " this might not work as expected." + )) + } if (sum(checker) != 0) stop("Number of opening { and closing } must match.") if (any(cumsum(checker) < 0)) stop("Input has at least one unpaired '{'.") } From 6459026273c49e9c89817479a48bee9096f989a6 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Sat, 1 Jul 2023 07:31:41 +0200 Subject: [PATCH 14/20] add keywords internal to small helper functions. --- R/rtf_rich_text.R | 5 ++++- man/check_braces.Rd | 1 + man/extract_tagged_text.Rd | 1 + man/match_braces.Rd | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index ecf49582..e13bb300 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -141,6 +141,7 @@ rtf_rich_text <- function(text, #' #' @param input Plain text containing matched curly braces with tags. #' +#' @keywords internal extract_tagged_text <- function(input) { opening <- gregexec("\\{", text = input, perl = TRUE)[[1]] closing <- gregexec("\\}", text = input, perl = TRUE)[[1]] @@ -172,6 +173,7 @@ extract_tagged_text <- function(input) { #' @param openings Vector of indices indicating location of opening braces. #' @param closings Vector of indices indicating location of closing braces. #' +#' @keywords internal match_braces <- function(openings, closings) { # Verify that equal numbers of opening and closing braces exist stopifnot(length(openings) == length(closings)) @@ -204,7 +206,8 @@ match_braces <- function(openings, closings) { #' #' @param input Plain text containing matched curly braces with tags. #' -check_braces <- function(input) { +#' @keywords internal +check_braces <- function(input){ input_parse <- gsub(x = input, pattern = "[^{}]", replacement = "") input_split <- unlist(strsplit(input_parse, "")) checker <- ifelse(input_split == "{", 1, -1) diff --git a/man/check_braces.Rd b/man/check_braces.Rd index ab5faf18..ebf6d600 100644 --- a/man/check_braces.Rd +++ b/man/check_braces.Rd @@ -13,3 +13,4 @@ check_braces(input) Braces must be matched appropriately. First brace should be an opening brace. Every brace should be closed appropriate. } +\keyword{internal} diff --git a/man/extract_tagged_text.Rd b/man/extract_tagged_text.Rd index bc5a24b1..89194ae2 100644 --- a/man/extract_tagged_text.Rd +++ b/man/extract_tagged_text.Rd @@ -13,3 +13,4 @@ extract_tagged_text(input) Identify the text that is in brackets and correctly resolve the ordering of the brackets such that everything is correctly tagged with the needed style. } +\keyword{internal} diff --git a/man/match_braces.Rd b/man/match_braces.Rd index 5f560e0b..9960604a 100644 --- a/man/match_braces.Rd +++ b/man/match_braces.Rd @@ -15,3 +15,4 @@ match_braces(openings, closings) Identify which opening and closing braces in a string belong together. Follows a first-in-last-out matching. } +\keyword{internal} From aa97cf5b5e7d3d38fa33f25c7b814365150688a3 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Sat, 1 Jul 2023 05:35:17 +0000 Subject: [PATCH 15/20] Style code --- R/rtf_rich_text.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index e13bb300..08686df0 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -207,7 +207,7 @@ match_braces <- function(openings, closings) { #' @param input Plain text containing matched curly braces with tags. #' #' @keywords internal -check_braces <- function(input){ +check_braces <- function(input) { input_parse <- gsub(x = input, pattern = "[^{}]", replacement = "") input_split <- unlist(strsplit(input_parse, "")) checker <- ifelse(input_split == "{", 1, -1) From eb0c8e69a528409b39ac8c021c7eae762c9831d7 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Sat, 1 Jul 2023 07:45:42 +0200 Subject: [PATCH 16/20] update description for suggests:tidyr. unclear why github actions previously worked without this. --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index d98661fa..a15a7e29 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -45,6 +45,7 @@ Suggests: rmarkdown, stringi, testthat, + tidyr, xml2 Config/testthat/edition: 3 Roxygen: list(markdown = TRUE) From 6f85d6e6221701765723e7c73a6ba053523675e0 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Sat, 1 Jul 2023 07:47:39 +0200 Subject: [PATCH 17/20] update for styler --- man/rtf_rich_text.Rd | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index 3e40cf0c..98c3a106 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -37,12 +37,16 @@ Text to formatted RTF Encode } \examples{ -rtf_rich_text(text = paste("This is {.emph important}.", -"This is {.strong relevant}.", "This is {.zebra ZEBRA}."), -theme = list( -.emph = list(format = "i"), -.strong = list(format = "b"), -.zebra = list(color = "white", background_color = "black") -)) +rtf_rich_text( + text = paste( + "This is {.emph important}.", + "This is {.strong relevant}.", "This is {.zebra ZEBRA}." + ), + theme = list( + .emph = list(format = "i"), + .strong = list(format = "b"), + .zebra = list(color = "white", background_color = "black") + ) +) } From 0d6e24575faee0a08929383fff28808900ef41e0 Mon Sep 17 00:00:00 2001 From: BrianLang Date: Sat, 1 Jul 2023 07:49:59 +0200 Subject: [PATCH 18/20] style tests --- .../test-developer-testing-rtf_rich_text.R | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/testthat/test-developer-testing-rtf_rich_text.R b/tests/testthat/test-developer-testing-rtf_rich_text.R index 5f02ab4e..663bf8ed 100644 --- a/tests/testthat/test-developer-testing-rtf_rich_text.R +++ b/tests/testthat/test-developer-testing-rtf_rich_text.R @@ -26,16 +26,17 @@ test_that("rtf_rich_text works with example from check_args.", { .red = list(color = "red"), .hl = list(background_color = "yellow") ) - )) + ) + ) expectation <- r2rtf:::rtf_paragraph( r2rtf:::rtf_text(paste0( - "3.5{^\\dagger}\\line ", - r2rtf:::rtf_text("red", color = "red"), - " ", - r2rtf:::rtf_text("highlight", background_color = "yellow") - ))) + "3.5{^\\dagger}\\line ", + r2rtf:::rtf_text("red", color = "red"), + " ", + r2rtf:::rtf_text("highlight", background_color = "yellow") + )) + ) expect_equal(output, expectation) }) - From 43474edb5cf448b9c0cd7a36cf39075913d7515b Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Sat, 1 Jul 2023 03:03:41 -0400 Subject: [PATCH 19/20] Fix styles in `rtf_rich_text()` and use `@noRd` for internal functions --- R/rtf_rich_text.R | 102 ++++++++++++++++++++----------------- man/check_braces.Rd | 16 ------ man/extract_tagged_text.Rd | 16 ------ man/match_braces.Rd | 18 ------- man/rtf_rich_text.Rd | 9 ++-- 5 files changed, 59 insertions(+), 102 deletions(-) delete mode 100644 man/check_braces.Rd delete mode 100644 man/extract_tagged_text.Rd delete mode 100644 man/match_braces.Rd diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 08686df0..86f88e1a 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -15,11 +15,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -#' Text to formatted RTF Encode +#' Text to Formatted RTF Encode #' #' @param text Plain text. -#' @param theme Named list defining themes for tags. See \code{rtf_text()} for -#' details on possible formatting. +#' @param theme Named list defining themes for tags. See [rtf_text()] for +#' details on possible formatting. #' #' @section Specification: #' \if{latex}{ @@ -34,7 +34,7 @@ #' \item Execute \code{rtf_text()} with extracted text and relevant formatting. #' \item Reinsert encoded formatted text to original input text. #' } -#' } +#' } #' \if{html}{The contents of this section are shown in PDF user manual only.} #' #' @export @@ -51,13 +51,13 @@ #' .zebra = list(color = "white", background_color = "black") #' ) #' ) -#' -rtf_rich_text <- function(text, - theme = list( - .emph = list(format = "i"), - .strong = list(format = "b") - )) { - # Bulletproof the styles requested within the theme argument. +rtf_rich_text <- function( + text, + theme = list( + .emph = list(format = "i"), + .strong = list(format = "b") + )) { + # Bulletproof the styles requested within the `theme` argument theme_arg <- theme names(theme_arg) <- NULL unique_styles <- unique(names(unlist(theme_arg))) @@ -65,16 +65,19 @@ rtf_rich_text <- function(text, if (length(bad_style) > 0) { stop( "Theme lists have styles which are not supported (", - paste0(bad_style, collapse = ", "), ")." + paste0(bad_style, collapse = ", "), ").", + call. = FALSE ) } - # Find all paired braces in text string. + # Find all paired braces in text string extracted <- list() extracted$matches <- gsub( - pattern = "^\\{", replacement = "", - gsub( - pattern = "\\}$", replacement = "", + pattern = "^\\{", + replacement = "", + x = gsub( + pattern = "\\}$", + replacement = "", x = extract_tagged_text(text) ) ) @@ -85,48 +88,52 @@ rtf_rich_text <- function(text, extracted$tags <- gsub( pattern = extraction_pattern, - x = extracted$matches, replacement = "\\1" + replacement = "\\1", + x = extracted$matches ) if (length(extracted$tags) != length(extracted$matches)) { - stop("Length missmatch of tags found and matches found") + stop("Length missmatch of tags found and matches found.", call. = FALSE) } - # For each paired brace: extract the text to be wrapped with rtf_text() + # For each paired brace: extract the text to be wrapped with `rtf_text()` extracted$text <- gsub( pattern = extraction_pattern, - x = extracted$matches, replacement = "\\3" + replacement = "\\3", + x = extracted$matches ) if (length(extracted$text) != length(extracted$matches)) { - stop("Length missmatch of extracted text found and matches found") + stop("Length missmatch of extracted text found and matches found.", call. = FALSE) } - # Validate that tags in text are reflected in themes argument + # Validate that tags in text are reflected in the `themes` argument missing_themes <- extracted$tags[!(extracted$tags %in% names(theme))] if (length(missing_themes) != 0) { stop( "Input text has tags which are not available in the theme (", - paste0(missing_themes, collapse = ", "), ")." + paste0(missing_themes, collapse = ", "), ").", + call. = FALSE ) } - # Execute rtf_text() calls with theme tags. + # Execute `rtf_text()` calls with theme tags extracted$replacements <- vapply( X = seq_along(extracted$tags), FUN = function(x) { - do.call(rtf_text, + do.call( + rtf_text, args = c(text = extracted$text[x], theme[[extracted$tags[x]]]) ) }, FUN.VALUE = "character" ) - # Insert rtf_text() calls into original text. + # Insert `rtf_text()` calls into the original text new_text <- text for (i in seq_along(extracted$matches)) { new_text <- gsub( - x = new_text, pattern = paste0("{", extracted$matches[i], "}"), replacement = extracted$replacements[i], + x = new_text, fixed = TRUE ) } @@ -134,30 +141,31 @@ rtf_rich_text <- function(text, rtf_text(new_text) } -#' Extract tagged text +#' Extract Tagged Text #' #' Identify the text that is in brackets and correctly resolve the ordering of #' the brackets such that everything is correctly tagged with the needed style. #' #' @param input Plain text containing matched curly braces with tags. #' -#' @keywords internal +#' @noRd extract_tagged_text <- function(input) { opening <- gregexec("\\{", text = input, perl = TRUE)[[1]] closing <- gregexec("\\}", text = input, perl = TRUE)[[1]] styles <- gregexec("\\{\\.[A-Za-z]*", text = input, perl = TRUE) - # Check for equal number of opening and closing braces. + # Check for equal number of opening and closing braces check_braces(input) - # Identify matching brace pairs. + # Identify matching brace pairs brace_matches <- match_braces(opening, closing) - # Identify which matched braces are associated with a style tag. + # Identify which matched braces are associated with a style tag styles_matches <- brace_matches[which(brace_matches$opening %in% styles[[1]]), ] - # Extract tagged brackets. - unname(apply(styles_matches, + # Extract tagged brackets + unname(apply( + styles_matches, MARGIN = 1, FUN = function(X) { substr(input, start = X[["opening"]], stop = X[["closing"]]) @@ -165,15 +173,15 @@ extract_tagged_text <- function(input) { )) } -#' Identify opening and closing brace pairs +#' Identify Opening and Closing Brace Pairs #' -#' Identify which opening and closing braces in a string belong together. Follows -#' a first-in-last-out matching. +#' Identify which opening and closing braces in a string belong together. +#' Follows a first-in-last-out matching. #' #' @param openings Vector of indices indicating location of opening braces. #' @param closings Vector of indices indicating location of closing braces. #' -#' @keywords internal +#' @noRd match_braces <- function(openings, closings) { # Verify that equal numbers of opening and closing braces exist stopifnot(length(openings) == length(closings)) @@ -188,11 +196,11 @@ match_braces <- function(openings, closings) { # Find the matching opening brace. # The matching opening brace is the one most recently preceding a closing brace. match_opening <- max(openings[openings < closings[i]]) - # Record the opening brace index to be removed. + # Record the opening brace index to be removed match_opening_index <- which(openings == match_opening) - # Add the pair of opening and closing braces to the holder data frame. + # Add the pair of opening and closing braces to the holder data frame holder[i, ] <- c(match_opening, closings[i]) - # Remove the matching opening brace from the vector of opening braces. + # Remove the matching opening brace from the vector of opening braces openings <- openings[-match_opening_index] } @@ -202,21 +210,21 @@ match_braces <- function(openings, closings) { #' Check that braces are correctly matched #' #' Braces must be matched appropriately. First brace should be an opening brace. -#' Every brace should be closed appropriate. +#' Every brace should be closed appropriately. #' #' @param input Plain text containing matched curly braces with tags. #' -#' @keywords internal +#' @noRd check_braces <- function(input) { input_parse <- gsub(x = input, pattern = "[^{}]", replacement = "") input_split <- unlist(strsplit(input_parse, "")) checker <- ifelse(input_split == "{", 1, -1) if (grepl(x = input, pattern = "(\\\\{)|(\\\\})", perl = TRUE)) { warning(c( - "It seems that you have some escaped brackets in your input,", - " this might not work as expected." + "It seems that you have some escaped brackets in your input, ", + "this might not work as expected." )) } - if (sum(checker) != 0) stop("Number of opening { and closing } must match.") - if (any(cumsum(checker) < 0)) stop("Input has at least one unpaired '{'.") + if (sum(checker) != 0) stop("Number of opening { and closing } must match.", call. = FALSE) + if (any(cumsum(checker) < 0)) stop("Input has at least one unpaired '{'.", call. = FALSE) } diff --git a/man/check_braces.Rd b/man/check_braces.Rd deleted file mode 100644 index ebf6d600..00000000 --- a/man/check_braces.Rd +++ /dev/null @@ -1,16 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/rtf_rich_text.R -\name{check_braces} -\alias{check_braces} -\title{Check that braces are correctly matched} -\usage{ -check_braces(input) -} -\arguments{ -\item{input}{Plain text containing matched curly braces with tags.} -} -\description{ -Braces must be matched appropriately. First brace should be an opening brace. -Every brace should be closed appropriate. -} -\keyword{internal} diff --git a/man/extract_tagged_text.Rd b/man/extract_tagged_text.Rd deleted file mode 100644 index 89194ae2..00000000 --- a/man/extract_tagged_text.Rd +++ /dev/null @@ -1,16 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/rtf_rich_text.R -\name{extract_tagged_text} -\alias{extract_tagged_text} -\title{Extract tagged text} -\usage{ -extract_tagged_text(input) -} -\arguments{ -\item{input}{Plain text containing matched curly braces with tags.} -} -\description{ -Identify the text that is in brackets and correctly resolve the ordering of -the brackets such that everything is correctly tagged with the needed style. -} -\keyword{internal} diff --git a/man/match_braces.Rd b/man/match_braces.Rd deleted file mode 100644 index 9960604a..00000000 --- a/man/match_braces.Rd +++ /dev/null @@ -1,18 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/rtf_rich_text.R -\name{match_braces} -\alias{match_braces} -\title{Identify opening and closing brace pairs} -\usage{ -match_braces(openings, closings) -} -\arguments{ -\item{openings}{Vector of indices indicating location of opening braces.} - -\item{closings}{Vector of indices indicating location of closing braces.} -} -\description{ -Identify which opening and closing braces in a string belong together. Follows -a first-in-last-out matching. -} -\keyword{internal} diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index 98c3a106..aee029ba 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/rtf_rich_text.R \name{rtf_rich_text} \alias{rtf_rich_text} -\title{Text to formatted RTF Encode} +\title{Text to Formatted RTF Encode} \usage{ rtf_rich_text( text, @@ -12,11 +12,11 @@ rtf_rich_text( \arguments{ \item{text}{Plain text.} -\item{theme}{Named list defining themes for tags. See \code{rtf_text()} for +\item{theme}{Named list defining themes for tags. See \code{\link[=rtf_text]{rtf_text()}} for details on possible formatting.} } \description{ -Text to formatted RTF Encode +Text to Formatted RTF Encode } \section{Specification}{ @@ -32,7 +32,7 @@ Text to formatted RTF Encode \item Execute \code{rtf_text()} with extracted text and relevant formatting. \item Reinsert encoded formatted text to original input text. } - } +} \if{html}{The contents of this section are shown in PDF user manual only.} } @@ -48,5 +48,4 @@ rtf_rich_text( .zebra = list(color = "white", background_color = "black") ) ) - } From e3f55600f82cc9ffabd66dcfc0ed417698057bb4 Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Sat, 1 Jul 2023 03:09:48 -0400 Subject: [PATCH 20/20] Avoid using links for internal function `rtf_text()` --- R/rtf_rich_text.R | 2 +- man/rtf_rich_text.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/rtf_rich_text.R b/R/rtf_rich_text.R index 86f88e1a..faa50344 100644 --- a/R/rtf_rich_text.R +++ b/R/rtf_rich_text.R @@ -18,7 +18,7 @@ #' Text to Formatted RTF Encode #' #' @param text Plain text. -#' @param theme Named list defining themes for tags. See [rtf_text()] for +#' @param theme Named list defining themes for tags. See `rtf_text()` for #' details on possible formatting. #' #' @section Specification: diff --git a/man/rtf_rich_text.Rd b/man/rtf_rich_text.Rd index aee029ba..be294f4a 100644 --- a/man/rtf_rich_text.Rd +++ b/man/rtf_rich_text.Rd @@ -12,7 +12,7 @@ rtf_rich_text( \arguments{ \item{text}{Plain text.} -\item{theme}{Named list defining themes for tags. See \code{\link[=rtf_text]{rtf_text()}} for +\item{theme}{Named list defining themes for tags. See \code{rtf_text()} for details on possible formatting.} } \description{