From c13798457988fee0247b0bbc71a67a7d31c5547a Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 00:49:05 +0900 Subject: [PATCH 01/11] feat(fuzzyhelp): prefer startDynamicHelp to Rd2HTML --- R/fuzzyhelp.R | 64 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/R/fuzzyhelp.R b/R/fuzzyhelp.R index 6a4de2a..0bf3f27 100644 --- a/R/fuzzyhelp.R +++ b/R/fuzzyhelp.R @@ -16,7 +16,13 @@ get_content <- function(x, i) { topic <- x$Topic[i] package <- x$Package[i] if (type == "help") { - return(get_help(topic, package)) + p <- startDynamicHelp() + if (is.null(p)) { + return(get_help(topic, package)) + } + h <- help((topic), (package), help_type = "html") + u <- sprintf("http://127.0.0.1:%d/library/%s/html/%s.html", p, package, basename(h)) + return(u) } if (type == "vignette") { return(get_vignette(topic, package)) @@ -318,12 +324,14 @@ create_server <- function(method = c("fzf", "lv")) { reactiveToc() # avoids noisy refresh reactable::getReactableState("tocViewer", "selected") }) - reactiveHelp <- shiny::reactive( - htmltools::tags$iframe( - srcdoc = get_content(reactiveToc(), reactiveSelection()), - style = "width: 100%; height: 100%;", - id = "helpViewer", - onload = "(function(){ + reactiveHelp <- shiny::reactive({ + arguments <- list(style = "width: 100%; height: 100%;", id = "helpViewer") + content <- get_content(reactiveToc(), reactiveSelection()) + if (grepl("^http://", content)) { + arguments$src <- content + } else { + arguments$srcdoc <- content + arguments$onload <- "(function(){ // replace anchors to avoid nesting shiny widgets const pattern = document.baseURI + '#'; const iframe = document.querySelector('#helpViewer iframe'); @@ -338,8 +346,9 @@ create_server <- function(method = c("fzf", "lv")) { } }); })();" - ) - ) + } + do.call(htmltools::tags$iframe, arguments) + }) output$tocViewer <- reactable::renderReactable(reactiveTocViewer()) output$helpViewer <- shiny::renderUI(reactiveHelp()) @@ -364,6 +373,43 @@ create_server <- function(method = c("fzf", "lv")) { .env <- new.env() +startDynamicHelp <- function() { + if ( + !is.null(.env$helpProcess) && + is.null(.env$helpProcess$get_exit_status()) && + is.integer(.env$helpPort) + ) { + return(.env$helpPort) + } + + tf <- tempfile() + writeLines("", tf) + + .env$helpProcess <- callr::r_bg(function(output) { + port <- tools::startDynamicHelp(NA) + writeLines(as.character(port), output) + + # keep the process running + while (TRUE) { + Sys.sleep(60 * 60 * 24) # without sleep, dynamic help surver stop responding + } + }, list(output = tf)) + + # Wait up to 1 second until the server is ready + for (. in seq(10)) { + port <- readLines(tf) + if (port != "") { + port <- as.integer(port) + .env$helpPort <- port + return(port) + } + Sys.sleep(0.1) + } + + # If server is unavailable, return NULL and fallback to using Rd2HTML + return(NULL) +} + #' Fuzzily Search Help and View the Selection #' #' Users no more have to afraid of exact name of the object they need help. From 769ef19dbdbdda7a62e2366515bb59a5f8bb9fff Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 08:45:11 +0900 Subject: [PATCH 02/11] fix(fuzzyhelp): hide some features unavailable in background mode --- R/fuzzyhelp.R | 62 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/R/fuzzyhelp.R b/R/fuzzyhelp.R index 0bf3f27..f81223a 100644 --- a/R/fuzzyhelp.R +++ b/R/fuzzyhelp.R @@ -4,7 +4,7 @@ NULL #' Get preview content for Shiny UI #' @noRd -get_content <- function(x, i) { +get_content <- function(x, i, background) { if (NROW(x) == 0L || length(i) == 0L) { return("") } @@ -28,7 +28,11 @@ get_content <- function(x, i) { return(get_vignette(topic, package)) } if (type == "demo") { - return('Press "Done" to see demo.') + return(if (background) { + sprintf('Call demo("%s") to see demo', topic) + } else { + 'Press "Done" to see demo.' + }) } paste("Viewer not available for the type:", type) } @@ -242,9 +246,13 @@ search_toc <- function(df, queries, ...) { dplyr::select(!"SCORE") } -create_ui <- function(query = "") { +create_ui <- function(query = "", background = FALSE) { miniUI::miniPage( - miniUI::gadgetTitleBar("Fuzzy Help Search"), + if (background) { + miniUI::gadgetTitleBar("Fuzzy Help Search", NULL, NULL) + } else { + miniUI::gadgetTitleBar("Fuzzy Help Search") + }, miniUI::miniContentPanel( shiny::textInput( "query", @@ -294,7 +302,7 @@ parse_query <- function(string) { queries[queries != ""] } -create_server <- function(method = c("fzf", "lv")) { +create_server <- function(method = c("fzf", "lv"), background = FALSE) { method <- match.arg(method) function(input, output) { toc <- create_toc() @@ -326,7 +334,7 @@ create_server <- function(method = c("fzf", "lv")) { }) reactiveHelp <- shiny::reactive({ arguments <- list(style = "width: 100%; height: 100%;", id = "helpViewer") - content <- get_content(reactiveToc(), reactiveSelection()) + content <- get_content(reactiveToc(), reactiveSelection(), background) if (grepl("^http://", content)) { arguments$src <- content } else { @@ -353,21 +361,23 @@ create_server <- function(method = c("fzf", "lv")) { output$tocViewer <- reactable::renderReactable(reactiveTocViewer()) output$helpViewer <- shiny::renderUI(reactiveHelp()) - shiny::observeEvent(input$done, { - shiny::stopApp() - selection <- reactiveToc()[reactiveSelection(), ] - type <- selection$Type[1L] - topic <- selection$Topic[1L] - package <- selection$Package[1L] - if (rstudioapi::isAvailable()) { - rstudioapi::sendToConsole( - sprintf('%s("%s", package = "%s")', type, topic, package), - execute = TRUE - ) - } else { - getNamespace("utils")[[type]]((topic), (package)) - } - }) + if (!background) { + shiny::observeEvent(input$done, { + shiny::stopApp() + selection <- reactiveToc()[reactiveSelection(), ] + type <- selection$Type[1L] + topic <- selection$Topic[1L] + package <- selection$Package[1L] + if (rstudioapi::isAvailable()) { + rstudioapi::sendToConsole( + sprintf('%s("%s", package = "%s")', type, topic, package), + execute = TRUE + ) + } else { + getNamespace("utils")[[type]]((topic), (package)) + } + }) + } } } @@ -412,11 +422,13 @@ startDynamicHelp <- function() { #' Fuzzily Search Help and View the Selection #' -#' Users no more have to afraid of exact name of the object they need help. +#' Users no longer have to remember the exact name to find help, vignettes, +#' and demo. #' A shiny gadget helps you to find a topic fuzzily. #' Click radio buttons to switch preview contents. #' Click "Done" or "Cancel" to close the widget. -#' The "Done" button will also hook `help` function on the selection. +#' When `background = FALSE`, the "Done" button will also hook `help`, +#' `vignette`, or `demo`, accordingly. #' #' @param query An initial query to search for the help system. #' @param method A fuzzy match method to use. Choices are "fzf" and "lv" @@ -448,8 +460,8 @@ fuzzyhelp <- function( method = getOption("fuzzyhelp.method", "fzf"), background = getOption("fuzzyhelp.background", TRUE), viewer = shiny::paneViewer()) { - app <- create_ui(query) - server <- create_server(method) + app <- create_ui(query, background) + server <- create_server(method, background) # Create new gadget on foreground if (!background) { From a7f8f8b7d2a58e2d95050c8641c55cd1f4eb34e0 Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 08:49:21 +0900 Subject: [PATCH 03/11] refactor(fuzzyhelp): order of if statements --- R/fuzzyhelp.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/R/fuzzyhelp.R b/R/fuzzyhelp.R index f81223a..ef770b2 100644 --- a/R/fuzzyhelp.R +++ b/R/fuzzyhelp.R @@ -15,6 +15,13 @@ get_content <- function(x, i, background) { type <- x$Type[i] topic <- x$Topic[i] package <- x$Package[i] + if (type == "demo") { + return(if (background) { + sprintf('Call demo("%s") to see demo', topic) + } else { + 'Press "Done" to see demo.' + }) + } if (type == "help") { p <- startDynamicHelp() if (is.null(p)) { @@ -27,13 +34,6 @@ get_content <- function(x, i, background) { if (type == "vignette") { return(get_vignette(topic, package)) } - if (type == "demo") { - return(if (background) { - sprintf('Call demo("%s") to see demo', topic) - } else { - 'Press "Done" to see demo.' - }) - } paste("Viewer not available for the type:", type) } From 7a0ed72b042adf237816c18be8e733038dd42e87 Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:01:14 +0900 Subject: [PATCH 04/11] fix(fuzzyhelp): suggesting demo() should include package argument --- R/fuzzyhelp.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/fuzzyhelp.R b/R/fuzzyhelp.R index ef770b2..cd6b350 100644 --- a/R/fuzzyhelp.R +++ b/R/fuzzyhelp.R @@ -17,7 +17,7 @@ get_content <- function(x, i, background) { package <- x$Package[i] if (type == "demo") { return(if (background) { - sprintf('Call demo("%s") to see demo', topic) + sprintf('Call demo("%s", "%s") to see demo', topic, package) } else { 'Press "Done" to see demo.' }) From a95f1027fbf6398ebc112f58b115f90ef0d53104 Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:21:15 +0900 Subject: [PATCH 05/11] feat(fuzzyhelp): also use native help server for vignette --- R/fuzzyhelp.R | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/R/fuzzyhelp.R b/R/fuzzyhelp.R index cd6b350..3a55090 100644 --- a/R/fuzzyhelp.R +++ b/R/fuzzyhelp.R @@ -22,18 +22,26 @@ get_content <- function(x, i, background) { 'Press "Done" to see demo.' }) } + + helpPort <- startDynamicHelp() + helpUrl <- "http://127.0.0.1:%d/library/%s/%s/%s%s" + if (type == "help") { - p <- startDynamicHelp() - if (is.null(p)) { + if (is.null(helpPort)) { return(get_help(topic, package)) } h <- help((topic), (package), help_type = "html") - u <- sprintf("http://127.0.0.1:%d/library/%s/html/%s.html", p, package, basename(h)) - return(u) + return(sprintf(helpUrl, helpPort, package, "html", basename(h), ".html")) } + if (type == "vignette") { - return(get_vignette(topic, package)) + if (is.null(helpPort)) { + return(get_vignette(topic, package)) + } + v <- vignette(topic, package) + return(sprintf(helpUrl, helpPort, basename(v$Dir), "doc", v$PDF, "")) } + paste("Viewer not available for the type:", type) } From 3adc717f7dc18a67a69ad00871a3f283a8a6c4b1 Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:39:22 +0900 Subject: [PATCH 06/11] docs(NEWS): fuzzyhelp changes --- NEWS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS.md b/NEWS.md index 1ccdc69..9a61bc9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +# felp 0.5.0 + +- On`fuzzyhelp()`, help/vignette contents gain syntax highlights and links. It formerly used `Rd2HTML()` to generate HTML contents. Now the contents inherit from a help server with `startDynamicHelp()`, which means they are exactly same as the contents of `help()` or `vignette()`. +- On `fuzzyhelp(background = FALSE)`, "Done" and "Cancel" buttons are removed because the feature of the "Done" requires the Shiny app run on the main thread. + # felp 0.4.0 - Support `fuzzyhelp` to run Shiny App in background without blocking user terminal. From 5330662e0fd88aac63b2794108a25cc64dfb49ef Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:39:45 +0900 Subject: [PATCH 07/11] chore(docs): devtools::document() --- man/fuzzyhelp.Rd | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/man/fuzzyhelp.Rd b/man/fuzzyhelp.Rd index 0f21c8d..21857aa 100644 --- a/man/fuzzyhelp.Rd +++ b/man/fuzzyhelp.Rd @@ -31,11 +31,13 @@ If the \code{background} argument is \code{TRUE}, then the return value inherits \code{callr::r_bg()}. Otherwise, \code{NULL} is returned. } \description{ -Users no more have to afraid of exact name of the object they need help. +Users no longer have to remember the exact name to find help, vignettes, +and demo. A shiny gadget helps you to find a topic fuzzily. Click radio buttons to switch preview contents. Click "Done" or "Cancel" to close the widget. -The "Done" button will also hook \code{help} function on the selection. +When \code{background = FALSE}, the "Done" button will also hook \code{help}, +\code{vignette}, or \code{demo}, accordingly. } \note{ The default fuzzy match algorithm is a simplified version of From 00773e4bd747cd920c51f68ab961c3f596e2447f Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:40:38 +0900 Subject: [PATCH 08/11] chore(docs): pkgdown::build_site() --- docs/articles/felp.html | 2 +- docs/news/index.html | 7 ++++++- docs/pkgdown.yml | 2 +- docs/reference/fuzzyhelp.html | 12 ++++++++---- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/articles/felp.html b/docs/articles/felp.html index 91b15ab..c4ef0e0 100644 --- a/docs/articles/felp.html +++ b/docs/articles/felp.html @@ -323,7 +323,7 @@

felp(package) - +

+ +
+
diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 8ecb272..96829ab 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -3,5 +3,5 @@ pkgdown: 2.0.7 pkgdown_sha: ~ articles: felp: felp.html -last_built: 2024-03-29T12:54Z +last_built: 2024-04-03T00:39Z diff --git a/docs/reference/fuzzyhelp.html b/docs/reference/fuzzyhelp.html index 4b7f8d3..63d19dc 100644 --- a/docs/reference/fuzzyhelp.html +++ b/docs/reference/fuzzyhelp.html @@ -1,9 +1,11 @@ -Fuzzily Search Help and View the Selection — fuzzyhelp • felp @@ -56,11 +58,13 @@

Fuzzily Search Help and View the Selection

-

Users no more have to afraid of exact name of the object they need help. +

Users no longer have to remember the exact name to find help, vignettes, +and demo. A shiny gadget helps you to find a topic fuzzily. Click radio buttons to switch preview contents. Click "Done" or "Cancel" to close the widget. -The "Done" button will also hook help function on the selection.

+When background = FALSE, the "Done" button will also hook help, +vignette, or demo, accordingly.

From bcc0116805806183b01b83a089bcb443393c8842 Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Fri, 5 Apr 2024 23:09:00 +0900 Subject: [PATCH 09/11] feat(fuzzyhelp): support previewing demo --- R/fuzzyhelp.R | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/R/fuzzyhelp.R b/R/fuzzyhelp.R index 3a55090..d2c960f 100644 --- a/R/fuzzyhelp.R +++ b/R/fuzzyhelp.R @@ -4,7 +4,7 @@ NULL #' Get preview content for Shiny UI #' @noRd -get_content <- function(x, i, background) { +get_content <- function(x, i) { if (NROW(x) == 0L || length(i) == 0L) { return("") } @@ -15,23 +15,15 @@ get_content <- function(x, i, background) { type <- x$Type[i] topic <- x$Topic[i] package <- x$Package[i] - if (type == "demo") { - return(if (background) { - sprintf('Call demo("%s", "%s") to see demo', topic, package) - } else { - 'Press "Done" to see demo.' - }) - } - helpPort <- startDynamicHelp() - helpUrl <- "http://127.0.0.1:%d/library/%s/%s/%s%s" + helpUrl <- "http://127.0.0.1:%d/%s/%s/%s/%s%s" if (type == "help") { if (is.null(helpPort)) { return(get_help(topic, package)) } h <- help((topic), (package), help_type = "html") - return(sprintf(helpUrl, helpPort, package, "html", basename(h), ".html")) + return(sprintf(helpUrl, helpPort, "library", package, "html", basename(h), ".html")) } if (type == "vignette") { @@ -39,7 +31,14 @@ get_content <- function(x, i, background) { return(get_vignette(topic, package)) } v <- vignette(topic, package) - return(sprintf(helpUrl, helpPort, basename(v$Dir), "doc", v$PDF, "")) + return(sprintf(helpUrl, helpPort, "library", basename(v$Dir), "doc", v$PDF, "")) + } + + if (type == "demo") { + if (is.null(helpPort)) { + return(sprintf('Call demo("%s", "%s") to see demo', topic, package)) + } + return(sprintf(helpUrl, helpPort, "library", package, "Demo", topic, "")) } paste("Viewer not available for the type:", type) @@ -342,7 +341,7 @@ create_server <- function(method = c("fzf", "lv"), background = FALSE) { }) reactiveHelp <- shiny::reactive({ arguments <- list(style = "width: 100%; height: 100%;", id = "helpViewer") - content <- get_content(reactiveToc(), reactiveSelection(), background) + content <- get_content(reactiveToc(), reactiveSelection()) if (grepl("^http://", content)) { arguments$src <- content } else { From c8e2baf259db29f94ad6859970dc2003f44dab6a Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Fri, 5 Apr 2024 23:10:24 +0900 Subject: [PATCH 10/11] docs(NEWS): fuzzyhelp() support previewing demo --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 9a61bc9..881a970 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ # felp 0.5.0 - On`fuzzyhelp()`, help/vignette contents gain syntax highlights and links. It formerly used `Rd2HTML()` to generate HTML contents. Now the contents inherit from a help server with `startDynamicHelp()`, which means they are exactly same as the contents of `help()` or `vignette()`. +- On `fuzzyhelp()`, demo contents can be previewed. - On `fuzzyhelp(background = FALSE)`, "Done" and "Cancel" buttons are removed because the feature of the "Done" requires the Shiny app run on the main thread. # felp 0.4.0 From 2609f18686d9a80882567f13b717842183c94d1d Mon Sep 17 00:00:00 2001 From: atusy <30277794+atusy@users.noreply.github.com> Date: Fri, 5 Apr 2024 23:13:50 +0900 Subject: [PATCH 11/11] chore(pkgdown): build --- docs/articles/felp.html | 2 +- docs/news/index.html | 1 + docs/pkgdown.yml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/articles/felp.html b/docs/articles/felp.html index c4ef0e0..2a299f9 100644 --- a/docs/articles/felp.html +++ b/docs/articles/felp.html @@ -323,7 +323,7 @@

felp(package)

diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 96829ab..f4388e9 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -3,5 +3,5 @@ pkgdown: 2.0.7 pkgdown_sha: ~ articles: felp: felp.html -last_built: 2024-04-03T00:39Z +last_built: 2024-04-05T14:13Z