🔍 An RStudio addin slash regex utility belt
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

207 lines
5.9KB

  1. #' RegExplain gadget
  2. #'
  3. #' The function behind the RegExplain Selection and RegExplain File
  4. #' addins. Opens the RegExplain gadget interface in an RStudio viewer
  5. #' pane.
  6. #'
  7. #' @examples
  8. #' \dontrun{
  9. #' regexplain_gadget(text = month.name, pattern = "(Ma|Ju)|(er)")
  10. #' regexplain_web(text = month.name, pattern = "(Ma|Ju)|(er)")
  11. #' regexplain_file()
  12. #' }
  13. #'
  14. #' @import miniUI
  15. #' @import shiny
  16. #' @param text Text to explore in gadget (editable using interface)
  17. #' @param pattern Regular Expression to edit or visualize using RegExplain
  18. #' @param start_page Open gadget to this tab, one of `"Text"`, `"RegEx"`,
  19. #' `"Output"`, or `"Help"`
  20. #' @export
  21. regexplain_gadget <- function(
  22. text = NULL,
  23. pattern = NULL,
  24. start_page = if (is.null(text)) "Text" else "RegEx"
  25. ) {
  26. stopifnot(requireNamespace("miniUI"), requireNamespace("shiny"))
  27. viewer <- shiny::paneViewer(minHeight = 800)
  28. runGadget(
  29. regexplain_gadget_ui(text, pattern, start_page),
  30. regexplain_gadget_server(check_version()),
  31. viewer = viewer
  32. )
  33. }
  34. #' @describeIn regexplain_gadget Launches the RegExplain gadget in a browser or an
  35. #' RStduio viewer pane.
  36. #' @inheritDotParams shiny::shinyApp
  37. #' @export
  38. regexplain_web <- function(text = NULL, pattern = NULL, start_page = "Text", ...) {
  39. stopifnot(requireNamespace("miniUI"), requireNamespace("shiny"))
  40. shinyApp(
  41. regexplain_gadget_ui(text, pattern, start_page),
  42. regexplain_gadget_server(check_version()),
  43. ...
  44. )
  45. }
  46. # ---- Gadget Helper Functions and Variables ----
  47. sanitize_text_input <- function(x) {
  48. if (is.null(x) || !nchar(x)) {
  49. return(x)
  50. }
  51. rx_unicode <- "\\\\u[0-9a-f]{4,8}"
  52. rx_hex <- "\\\\x[0-9a-f]{2}|\\\\x\\{[0-9a-f]{1,6}\\}"
  53. rx_octal <- "\\\\[0][0-7]{1,3}"
  54. rx_escape <- paste(rx_unicode, rx_hex, rx_octal, sep = "|")
  55. if (grepl(rx_escape, x, ignore.case = TRUE)) {
  56. try(
  57. {
  58. y <- stringi::stri_unescape_unicode(x)
  59. },
  60. silent = TRUE
  61. )
  62. if (!is.na(y)) x <- y
  63. }
  64. # x <- gsub("\u201C|\u201D", '"', x)
  65. # x <- gsub("\u2018|\u2019", "'", x)
  66. x
  67. }
  68. toHTML <- function(...) {
  69. x <- paste(..., collapse = "")
  70. x <- gsub("\n", "\\\\n", x)
  71. x <- gsub("\t", "\\\\t", x)
  72. x <- gsub("\r", "\\\\r", x)
  73. HTML(x)
  74. }
  75. regexFn_choices <- list(
  76. "Choose a function" = "",
  77. base = c(
  78. "grep",
  79. "grepl",
  80. "sub", #<<
  81. "gsub", #<<
  82. "regexpr",
  83. "gregexpr",
  84. "regexec"
  85. ),
  86. stringr = c(
  87. "str_detect",
  88. "str_locate",
  89. "str_locate_all",
  90. "str_extract",
  91. "str_extract_all",
  92. "str_match",
  93. "str_match_all",
  94. "str_replace", #<<
  95. "str_replace_all", #<<
  96. "str_split"
  97. ),
  98. "rematch2" = c(
  99. "re_match",
  100. "re_match_all",
  101. "re_exec",
  102. "re_exec_all"
  103. )
  104. )
  105. available_regex_functions <- function() {
  106. pkgs <- c("stringr", "rematch2")
  107. has_pkg <- vapply(pkgs, requireNamespace, quietly = TRUE, logical(1))
  108. regexFn_choices[c("base", pkgs[has_pkg])]
  109. }
  110. regexFn_substitute <- c(
  111. paste0(c("", "g"), "sub"),
  112. paste0("str_replace", c("", "_all"))
  113. )
  114. get_pkg_namespace <- function(fn) {
  115. x <- names(purrr::keep(regexFn_choices, ~ (fn %in% .)))
  116. if (length(x) > 1) warning(fn, " matches multiple functions in regexFn_choices, please review.")
  117. x
  118. }
  119. #' Check if an updated version is available
  120. #'
  121. #' I included this because it can be difficult to tell if your RStudio Addins
  122. #' are up to date. I may add new features that you want but you won't hear about
  123. #' the updates. This function checks if an update is available, using GitHub
  124. #' tags. If an update is available, a modal dialog is shown when you start
  125. #' the regexplain gadget. This only happens once per R session, though, so feel
  126. #' free to ignore the message.
  127. #'
  128. #' @param gh_user GitHub user account
  129. #' @param gh_repo GitHub repo name
  130. #' @param this_version The currently installed version of the package
  131. #' @keywords internal
  132. check_version <- function(
  133. gh_user = "gadenbuie",
  134. gh_repo = "regexplain",
  135. this_version = packageVersion("regexplain")
  136. ) {
  137. ok_to_check <- getOption("regexplain.no.check.version", TRUE)
  138. if (!isTRUE(ok_to_check)) {
  139. return(NULL)
  140. }
  141. if (!requireNamespace("jsonlite", quietly = TRUE)) {
  142. return(NULL)
  143. }
  144. get_json <- purrr::possibly(jsonlite::fromJSON, NULL)
  145. gh_tags <- get_json(
  146. paste0("https://api.github.com/repos/", gh_user, "/", gh_repo, "/git/refs/tags"),
  147. simplifyDataFrame = TRUE
  148. )
  149. if (!is.null(gh_tags)) {
  150. gh_tags$tag <- sub("refs/tags/", "", gh_tags$ref, fixed = TRUE)
  151. gh_tags$version <- sub("^v\\.?", "", gh_tags$tag)
  152. }
  153. if (!is.null(gh_tags) && any(gh_tags$version > this_version)) {
  154. max_version <- max(gh_tags$version)
  155. max_tag <- gh_tags$tag[gh_tags$version == max_version]
  156. options(regexplain.no.check.version = FALSE)
  157. return(
  158. list(
  159. version = max_version,
  160. link = paste("https://github.com", gh_user, gh_repo, "releases/tag", max_tag, sep = "/")
  161. )
  162. )
  163. } else {
  164. return(NULL)
  165. }
  166. }
  167. #' Loads Regex Pattern Library
  168. #'
  169. #' Patterns sourced from [Regex Hub](https://projects.lukehaas.me/regexhub)
  170. #' are available at <https://github.com/lukehaas/RegexHub> and are copyright
  171. #' Luke Haas licensed under the MIT license available at
  172. #' <https://github.com/lukehaas/RegexHub/commit/3ab87b5a4fd2817b42e2e45dcf040d4f0164ea37>.
  173. #' Patterns source from [qdapRegex](https://github.com/trinker/qdapRegex) are
  174. #' copyright Tyler Rinker and Jason Gray, licensed under the GPL-2 license.
  175. #'
  176. #' @keywords internal
  177. get_regex_library <- function() {
  178. if (!requireNamespace("jsonlite", quietly = TRUE)) {
  179. warning("Please install the `jsonlite` package to use template features")
  180. return(NULL)
  181. }
  182. f_patterns <- system.file("extdata", "patterns.json", package = "regexplain")
  183. if (!file.exists(f_patterns)) {
  184. return(NULL)
  185. }
  186. patterns <- jsonlite::fromJSON(
  187. f_patterns,
  188. simplifyVector = FALSE,
  189. simplifyDataFrame = FALSE,
  190. simplifyMatrix = FALSE
  191. )
  192. patterns <- purrr::keep(patterns, ~ .$name != "")
  193. patterns[order(purrr::map_chr(patterns, "name"))]
  194. }