Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion R/sess-package-install.R
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ sess_verify_package <- function(library, required, expected_revision, interactiv
"stopifnot(identical(",
"utils::packageDescription('sess',lib.loc=a[1],fields='Config/vscode-R/source-revision'),",
"a[3]));",
"stopifnot(all(c('connect','notify_client','register_hooks','request_client') %in% getNamespaceExports(ns)));",
"stopifnot(all(c('connect','notify_client','request_client') %in% getNamespaceExports(ns)));",
"if (a[4] == 'TRUE') {",
"stopifnot(all(c('interactive_stop','display') %in% getNamespaceExports(ns)));",
"stopifnot(all(vapply(c('interactive_start','interactive_execute'),",
Expand Down
2 changes: 1 addition & 1 deletion R/tests/sess_source.R
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ local({
cli_library <- file.path(project, "library")
dir.create(cli_library, recursive = TRUE)
writeLines(".libPaths(c(file.path(getwd(), \"library\"), .libPaths()))", file.path(project, ".Rprofile"))
exports <- c("connect", "notify_client", "register_hooks", "request_client")
exports <- c("connect", "notify_client", "request_client")
writeLines(paste0(exports, " <- function(...) NULL"), file.path(pkg, "R", "zzz.R"))
writeLines(paste0("export(", exports, ")"), file.path(pkg, "NAMESPACE"))
previous_profile <- Sys.getenv("R_PROFILE_USER", unset = NA_character_)
Expand Down
3 changes: 2 additions & 1 deletion sess/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: sess
Type: Package
Title: High-Performance IPC Bridge for R Sessions
Version: 3.0.9000.9001
Version: 3.0.9000.9002
Authors@R: c(
person(given = "Randy",
family = "Lai",
Expand Down Expand Up @@ -42,6 +42,7 @@ Suggests:
tinytest
NeedsCompilation: no
Config/vscode-R/Interactive: 1
Config/roxygen2/markdown: TRUE
Config/roxygen2/version: 8.1.0
Config/build/bootstrap: TRUE
Config/vscode-R/source-revision: @VSCODE_R_SESS_SOURCE_REVISION@
1 change: 0 additions & 1 deletion sess/NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ export(connect)
export(display)
export(interactive_stop)
export(notify_client)
export(register_hooks)
export(request_client)
41 changes: 0 additions & 41 deletions sess/R/hooks.R
Original file line number Diff line number Diff line change
@@ -1,49 +1,8 @@
#' Register VS Code runtime integrations
#'
#' @param use_rstudioapi Logical. Enable rstudioapi emulation.
#' @param use_httpgd Deprecated. Logical. Enable httpgd plot device if available.
#' NULL means unspecified; legacy calls default to TRUE. Use `plot_backend` instead.
#' @param use_jgd Deprecated. Logical. Enable jgd plot device if available.
#' NULL means unspecified; legacy calls default to FALSE. Use `plot_backend` instead.
#' @param plot_backend Plot backend: `auto`, `jgd`, `httpgd`, `standard`, or
#' `native`. NULL also selects `auto`. Deprecated flags select the backend
#' only when this argument is omitted.
#' @export
register_hooks <- function(use_rstudioapi = TRUE, use_httpgd = NULL, use_jgd = NULL,
plot_backend = c("auto", "jgd", "httpgd", "standard", "native")) {
has_httpgd <- !is.null(use_httpgd)
has_jgd <- !is.null(use_jgd)
.warn_deprecated_plot_args(has_httpgd, has_jgd)
backend <- if (missing(plot_backend) && (has_httpgd || has_jgd)) {
.legacy_plot_backend(use_httpgd, use_jgd)
} else {
.resolve_plot_backend(plot_backend)
}
runtime_start(use_rstudioapi, backend)
}

.warn_deprecated_plot_args <- function(has_httpgd, has_jgd) {
old_args <- c(if (has_httpgd) "use_httpgd", if (has_jgd) "use_jgd")
if (length(old_args)) {
warning("[sess] ", paste(old_args, collapse = " and "),
if (length(old_args) == 1L) " is deprecated; use plot_backend instead." else
" are deprecated; use plot_backend instead.", call. = FALSE)
}
invisible(NULL)
}

.resolve_plot_backend <- function(plot_backend) {
if (is.null(plot_backend)) return("auto")
match.arg(plot_backend, c("auto", "jgd", "httpgd", "standard", "native"))
}

.legacy_plot_backend <- function(use_httpgd, use_jgd) {
if (is.null(use_httpgd) || is.na(use_httpgd)) use_httpgd <- TRUE
if (is.null(use_jgd) || is.na(use_jgd)) use_jgd <- FALSE
if (use_jgd && use_httpgd) "auto" else if (use_jgd) "jgd" else
if (use_httpgd) "httpgd" else "standard"
}

.select_plot_backend <- function(plot_backend, has_httpgd, has_jgd) {
if (plot_backend == "native") return("native")
if (plot_backend %in% c("auto", "jgd") && has_jgd) return("jgd")
Expand Down
19 changes: 3 additions & 16 deletions sess/R/server.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,8 @@
#' @param endpoint Character. Local named pipe / Unix domain socket endpoint.
#' If NULL, uses SESS_ENDPOINT, then SESS_DISCOVERY_FILE.
#' @param use_rstudioapi Logical. Enable rstudioapi emulation. Defaults to TRUE.
#' @param use_httpgd Deprecated. Logical. Use httpgd for plotting if available.
#' NULL means unspecified; legacy calls default to TRUE. Use `plot_backend` instead.
#' @param use_jgd Deprecated. Logical. Use jgd for plotting if available.
#' NULL means unspecified; legacy calls default to FALSE. Use `plot_backend` instead.
#' @param plot_backend Plot backend: `auto`, `jgd`, `httpgd`, `standard`, or
#' `native`. NULL also selects `auto`. Deprecated flags select the backend
#' only when this argument is omitted.
#' `native`. NULL also selects `auto`.
#' @details When SESS_DISCOVERY_FILE describes the connected endpoint, an
#' unexpected disconnect waits for a replacement endpoint in that file and
#' reconnects with the same runtime options and session identity. The optional
Expand All @@ -18,17 +13,9 @@
#' string clears it, and an omitted field leaves it unchanged. Set
#' `options(sess.quiet = TRUE)` to suppress the successful connection message.
#' @export
connect <- function(endpoint = NULL, use_rstudioapi = TRUE, use_httpgd = NULL,
use_jgd = NULL,
connect <- function(endpoint = NULL, use_rstudioapi = TRUE,
plot_backend = c("auto", "jgd", "httpgd", "standard", "native")) {
has_httpgd <- !is.null(use_httpgd)
has_jgd <- !is.null(use_jgd)
.warn_deprecated_plot_args(has_httpgd, has_jgd)
plot_backend <- if (missing(plot_backend) && (has_httpgd || has_jgd)) {
.legacy_plot_backend(use_httpgd, use_jgd)
} else {
.resolve_plot_backend(plot_backend)
}
plot_backend <- .resolve_plot_backend(plot_backend)
# Invalidate poll callbacks and restore a previous runtime before reconnecting.
.transport_disconnect(silent = TRUE)
.sess_env$con <- NULL
Expand Down
48 changes: 0 additions & 48 deletions sess/R/utils.R

This file was deleted.

23 changes: 10 additions & 13 deletions sess/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ sess::connect(
)
```

If `plot_backend` is omitted, `sess::connect()` uses `auto`. The VS Code extension
passes its configured backend explicitly. Calls that supply the deprecated
`use_httpgd` or `use_jgd` arguments still use those values when `plot_backend`
is omitted. These deprecated arguments default to `NULL`, meaning unspecified.
When either is non-NULL, the unspecified flag uses its legacy default
(`use_httpgd = TRUE`, `use_jgd = FALSE`). If both are NULL, the backend is `auto`.
If `plot_backend` is omitted or set to `NULL`, `sess::connect()` uses `auto`.
The VS Code extension passes its configured backend explicitly.

For code migrating from earlier `sess` APIs, replace `use_httpgd` and
`use_jgd` with `plot_backend` (for example, `sess::connect(plot_backend = "httpgd")`).
`register_hooks()` has been removed; `connect()` initializes the runtime
integration when it connects.

If `endpoint` is omitted, `connect()` resolves it in this order:

Expand Down Expand Up @@ -116,7 +117,7 @@ finding managed terminal discovery files; `sess` does not use it as identity.

## What `sess` changes in your R session

Once connected, `sess` registers hooks (via `register_hooks()`) that redirect
Once connected, `sess` registers runtime integrations that redirect
R's interactive features to the client:

| R feature | Behavior |
Expand All @@ -132,8 +133,6 @@ These changes are undone when the connection closes. `sess` removes its task
callbacks, closes its graphics devices, and restores any options, bindings, S3
methods and plot hooks it replaced (unless other code has since changed them).
A plot held only by a jgd device may not survive a disconnect or window reload.
Calling `register_hooks()` again replaces the previous installation rather than
stacking hooks.

### Graphics devices

Expand All @@ -152,10 +151,8 @@ For displaying R plots, `sess` chooses a graphics device in this order when
In VS Code, this is controlled by the `r.plot.backend` setting.
`plot_backend = "native"` leaves the existing R graphics device option, plot
hooks, plot task callbacks, and devices untouched. The `standard` backend continues
to use the static plot viewer. When `plot_backend` is omitted, the existing
`use_httpgd`/`use_jgd` arguments keep their previous meanings, but are
deprecated and warn when supplied with non-NULL values, including `FALSE`.
Use `plot_backend` for new code.
to use the static plot viewer. When `plot_backend` is omitted or `NULL`, `auto`
is used.

### Options and environment variables

Expand Down
116 changes: 18 additions & 98 deletions sess/inst/tinytest/test-native-plot.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
# Explicit backends preserve the old boolean API and keep native distinct from
# the standard static viewer.
# Explicit backends preserve native behavior and keep it distinct from the
# standard static viewer.
local({
resolve <- sess:::.resolve_plot_backend
legacy <- sess:::.legacy_plot_backend
select <- sess:::.select_plot_backend
choices <- c("auto", "jgd", "httpgd", "standard", "native")
expect_equal(eval(formals(sess::connect)$plot_backend), choices)
expect_equal(eval(formals(sess::register_hooks)$plot_backend), choices)
expect_equal(resolve(choices), "auto")
expect_equal(resolve(NULL), "auto")
expect_equal(legacy(FALSE, FALSE), "standard")
expect_equal(legacy(TRUE, FALSE), "httpgd")
expect_equal(legacy(FALSE, TRUE), "jgd")
expect_equal(legacy(TRUE, TRUE), "auto")
for (backend in c("auto", "jgd", "httpgd", "standard", "native")) {
expect_equal(resolve(backend), backend)
}
Expand All @@ -39,7 +33,7 @@ local({
options(device = original_device)
}, add = TRUE)

sess::register_hooks(use_rstudioapi = FALSE, plot_backend = "native")
sess:::runtime_start(use_rstudioapi = FALSE, plot_backend = "native")
state <- sess:::.runtime_state()
expect_true(isTRUE(state$active))
expect_true(identical(getOption("device"), sentinel_device))
Expand All @@ -57,97 +51,23 @@ local({
expect_equal(grDevices::dev.list(), original_devices)
})

# Legacy arguments still work, but public callers are directed to plot_backend.
# Removed arguments are rejected before connect changes transport or runtime state.
local({
old_device <- getOption("device")
sentinel_device <- function(...) stop("R's device option was used")
options(device = sentinel_device)
on.exit({
sess:::runtime_stop()
options(device = old_device)
}, add = TRUE)

warnings <- character()
withCallingHandlers(
sess::register_hooks(use_rstudioapi = FALSE, use_httpgd = TRUE,
use_jgd = TRUE, plot_backend = "native"),
warning = function(w) {
warnings <<- c(warnings, conditionMessage(w))
invokeRestart("muffleWarning")
}
)
expect_length(warnings, 1L)
expect_true(grepl("use_httpgd and use_jgd", warnings[[1L]], fixed = TRUE))
expect_true(grepl("plot_backend", warnings[[1L]], fixed = TRUE))
expect_true(identical(getOption("device"), sentinel_device))
})

local({
# Exercise both public entry points without starting graphics or connecting
# to a server. NULL can also be forwarded by wrappers as an unspecified flag.
cases <- list(
list(args = list(), backend = "auto", deprecated = character()),
list(args = list(use_httpgd = NULL, use_jgd = NULL), backend = "auto",
deprecated = character()),
list(args = list(use_httpgd = TRUE), backend = "httpgd", deprecated = "use_httpgd"),
list(args = list(use_httpgd = FALSE), backend = "standard", deprecated = "use_httpgd"),
list(args = list(use_jgd = TRUE), backend = "auto", deprecated = "use_jgd"),
list(args = list(use_jgd = FALSE), backend = "httpgd", deprecated = "use_jgd"),
list(args = list(use_httpgd = NULL, use_jgd = TRUE), backend = "auto",
deprecated = "use_jgd"),
list(args = list(use_httpgd = FALSE, use_jgd = NULL), backend = "standard",
deprecated = "use_httpgd"),
list(args = list(use_httpgd = FALSE, use_jgd = TRUE), backend = "jgd",
deprecated = c("use_httpgd", "use_jgd")),
list(args = list(use_httpgd = FALSE, use_jgd = FALSE), backend = "standard",
deprecated = c("use_httpgd", "use_jgd")),
list(args = list(use_httpgd = NA, use_jgd = NA), backend = "httpgd",
deprecated = c("use_httpgd", "use_jgd")),
list(args = list(use_httpgd = FALSE, plot_backend = "native"), backend = "native",
deprecated = "use_httpgd"),
list(args = list(use_httpgd = FALSE, plot_backend = NULL), backend = "auto",
deprecated = "use_httpgd")
)
for (entry_point in c("connect", "register_hooks")) {
env <- new.env(parent = asNamespace("sess"))
entry <- getExportedValue("sess", entry_point)
environment(entry) <- env
selected <- NULL
env$.legacy_plot_backend <- function(...) {
selected <<- sess:::.legacy_plot_backend(...)
selected
}
env$.resolve_plot_backend <- function(...) {
selected <<- sess:::.resolve_plot_backend(...)
selected
}
env$runtime_start <- function(use_rstudioapi, plot_backend) {
selected <<- plot_backend
}
env$.resolve_endpoint <- function(...) ""
for (case in cases) {
selected <- NULL
warnings <- character()
withCallingHandlers(
do.call(entry, c(list(use_rstudioapi = FALSE), case$args)),
warning = function(w) {
msg <- conditionMessage(w)
if (entry_point != "connect" ||
msg != "[sess] Connection info not available. Cannot connect to VS Code.") {
warnings <<- c(warnings, msg)
}
invokeRestart("muffleWarning")
}
)
expect_equal(selected, case$backend)
expect_length(warnings, as.integer(length(case$deprecated) > 0L))
if (length(case$deprecated)) {
expect_true(grepl(paste(case$deprecated, collapse = " and "),
warnings[[1L]], fixed = TRUE))
expect_true(grepl("deprecated; use plot_backend", warnings[[1L]], fixed = TRUE))
}
}
env <- new.env(parent = asNamespace("sess"))
entry <- sess::connect
environment(entry) <- env
disconnected <- FALSE
env$.transport_disconnect <- function(...) {
disconnected <<- TRUE
stop("connect reached transport cleanup")
}
for (arg in c("use_httpgd", "use_jgd")) {
failure <- tryCatch(do.call(entry, setNames(list(TRUE), arg)), error = identity)
expect_true(inherits(failure, "error"))
expect_true(grepl("unused argument", conditionMessage(failure), fixed = TRUE))
expect_false(disconnected)
}
expect_false("register_hooks" %in% getNamespaceExports("sess"))
})

# The public connection path stores native for discovery reconnects and still
Expand Down
13 changes: 2 additions & 11 deletions sess/man/connect.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading