🔍 An RStudio addin slash regex utility belt
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

236 lines
6.6KB

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