🔍 An RStudio addin slash regex utility belt
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

234 linhas
6.6KB

  1. #' regexplain gadget
  2. #'
  3. #' @import miniUI
  4. #' @import shiny
  5. #' @param text Text to explore in gadget (editable using interface)
  6. #' @param start_page Open gadget to this tab, one of `"Text"`, `"Regex"`,
  7. #' `"Output"`, or `"Help"`
  8. #' @export
  9. regex_gadget <- function(text = NULL,
  10. start_page = if (is.null(text)) "Text" else "Regex") {
  11. stopifnot(requireNamespace("miniUI"), requireNamespace("shiny"))
  12. ui <- miniPage(
  13. shiny::includeCSS(system.file("styles", "style.css", package = "regexplain")),
  14. shiny::includeCSS(system.file("styles", "gadget.css", package = "regexplain")),
  15. gadgetTitleBar(
  16. "regexplain",
  17. right = miniTitleBarButton("done", "Send Regex To Console", TRUE)
  18. ),
  19. miniTabstripPanel(
  20. selected = match.arg(start_page, c("Text", "Regex", "Output", "Help")),
  21. miniTabPanel(
  22. "Text", icon = icon('file-text-o'),
  23. miniContentPanel(
  24. fillCol(
  25. textAreaInputAlt('text',
  26. label = "Text to search or parse",
  27. value = paste(text, collapse = "\n"),
  28. resize = "both",
  29. width = "100%",
  30. height="90%",
  31. placeholder = "Paste, enter, or edit your sample text here.")
  32. )
  33. )
  34. ),
  35. miniTabPanel(
  36. "Regex", icon = icon('terminal'),
  37. miniContentPanel(
  38. fillCol(
  39. flex = c(1, 3),
  40. fillCol(
  41. flex = c(1, 1),
  42. textInputCode('pattern', 'Regex', width = "100%",
  43. placeholder = "Enter regex, single \\ okay"),
  44. checkboxGroupInput(
  45. 'regex_options',
  46. label = "",
  47. inline = TRUE,
  48. width = "90%",
  49. choices = c("Break Lines" = "text_break_lines",
  50. "Ignore Case" = "ignore.case",
  51. "Perl Style" = "perl",
  52. "Fixed" = "fixed",
  53. "Use Bytes" = "useBytes"
  54. # , "Invert" = "invert"
  55. ),
  56. selected = c('text_break_lines')
  57. )
  58. ),
  59. tags$div(
  60. class = "gadget-result",
  61. style = "overflow-y: scroll; height: 100%;",
  62. htmlOutput('result')
  63. )
  64. )
  65. )
  66. ),
  67. miniTabPanel(
  68. "Output", icon = icon("table"),
  69. miniContentPanel(
  70. fillCol(
  71. flex = c(1, 3),
  72. inputPanel(
  73. width = "100%;",
  74. selectInput('regexFn', label = 'Apply Function',
  75. choices = regexFn_choices)
  76. ),
  77. # verbatimTextOutput('output_result', placeholder = TRUE)
  78. tags$pre(
  79. id = "output_result",
  80. class = "shiny-text-output",
  81. style = "overflow-y: scroll; height: 100%;"
  82. )
  83. )
  84. )
  85. ),
  86. miniTabPanel(
  87. "Help", icon = icon("support"),
  88. help_ui("help")
  89. )
  90. )
  91. )
  92. server <- function(input, output, session) {
  93. rtext <- reactive({
  94. x <- if ('text_break_lines' %in% input$regex_options) {
  95. strsplit(input$text, "\n")[[1]]
  96. } else input$text
  97. x
  98. })
  99. pattern <- reactive({
  100. sanitize_text_input(input$pattern)
  101. })
  102. alert_result <- function(msg, type = "danger") {
  103. msg <- gsub("\n", "<br>", msg)
  104. msg <- gsub("\t", "&nbsp;&nbsp;", msg)
  105. paste0("<pre class='alert alert-", type, "' ",
  106. "style='padding: 4px; margin-top: 1px; margin-bottom: 4px;'>",
  107. paste(msg, collapse = "<br>"),
  108. "</pre>")
  109. }
  110. output$result <- renderUI({
  111. if (is.null(rtext())) return(NULL)
  112. if (pattern() == "") {
  113. return(toHTML(paste('<p class="results">', escape_html(rtext()), "</p>", collapse = "")))
  114. }
  115. res <- NULL
  116. error_message <- NULL
  117. warning_message <- NULL
  118. tryCatch({
  119. res <- paste(
  120. view_regex(
  121. rtext(),
  122. pattern(),
  123. ignore.case = 'ignore.case' %in% input$regex_options,
  124. perl = 'perl' %in% input$regex_options,
  125. fixed = 'fixed' %in% input$regex_options,
  126. useBytes = 'useBytes' %in% input$regex_options,
  127. # invert = 'invert' %in% input$regex_options,
  128. render = FALSE,
  129. escape = TRUE),
  130. collapse = ""
  131. )
  132. },
  133. error = function(e) {
  134. error_message <<- alert_result(e$message, "danger")
  135. },
  136. warning = function(w) {
  137. warning_message <<- alert_result(w$message, "warning")
  138. })
  139. if (is.null(res)) res <- toHTML(
  140. paste('<p class="results">', escape_html(rtext()), "</p>", collapse = "")
  141. )
  142. toHTML(paste(error_message, warning_message, res))
  143. })
  144. output$output_result <- renderPrint({
  145. req(input$regexFn)
  146. regexPkg <- get_pkg_namespace(input$regexFn)
  147. regexFn <- getFromNamespace(input$regexFn, regexPkg)
  148. x <- if (regexPkg == "base") {
  149. regexFn(pattern(), rtext())
  150. } else if (regexPkg == "stringr") {
  151. regexFn(rtext(), pattern())
  152. } else {
  153. "Um. Not sure how I got here."
  154. }
  155. print(x)
  156. })
  157. # ---- Help Section ---- #
  158. help_text <- callModule(help_server, "help")
  159. observeEvent(input$done, {
  160. # browser()
  161. if (pattern() != "") {
  162. pattern <- paste0('pattern <- "', escape_backslash(pattern()), '"')
  163. rstudioapi::sendToConsole(pattern, FALSE)
  164. }
  165. stopApp()
  166. })
  167. observeEvent(input$cancel, {
  168. stopApp()
  169. })
  170. }
  171. viewer <- shiny::paneViewer(700)
  172. runGadget(ui, server, viewer = viewer)
  173. }
  174. sanitize_text_input <- function(x) {
  175. if (grepl("\\u|\\x|\\N|\\a|\\o", x)) {
  176. try({
  177. y <- stringi::stri_unescape_unicode(x)
  178. }, silent = TRUE)
  179. if (!is.na(y)) x <- y
  180. }
  181. # x <- gsub("\u201C|\u201D", '"', x)
  182. # x <- gsub("\u2018|\u2019", "'", x)
  183. x
  184. }
  185. toHTML <- function(...) {
  186. x <- paste(..., collapse = "")
  187. x <- gsub("\n", "\\\\n", x)
  188. x <- gsub("\t", "\\\\t", x)
  189. x <- gsub("\r", "\\\\r", x)
  190. HTML(x)
  191. }
  192. regexFn_choices <- list(
  193. "Choose a function" = "",
  194. base = c(
  195. "grep",
  196. "grepl",
  197. "regexpr",
  198. "gregexpr",
  199. "regexec"
  200. ),
  201. stringr = c(
  202. "str_detect",
  203. "str_locate",
  204. "str_locate_all",
  205. "str_extract",
  206. "str_extract_all",
  207. "str_match",
  208. "str_match_all",
  209. "str_split"
  210. )
  211. )
  212. get_pkg_namespace <- function(fn) {
  213. x <- names(purrr::keep(regexFn_choices, ~ (fn %in% .)))
  214. if (length(x) > 1) warning(fn, " matches multiple functions in regexFn_choices, please review.")
  215. x
  216. }