🔍 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.

210 lines
6.0KB

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