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
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export(setGhostText)
export(setPersistentValue)
export(setSelectionRanges)
export(showDialog)
export(showEditSuggestion)
export(showPrompt)
export(showQuestion)
export(sourceMarkers)
Expand Down
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# rstudioapi (development version)

* `getMode()` no longer fails on very old versions of RStudio that lack the
internal `.rs.isDesktop()` helper. In that case, it now falls back to
`versionInfo()$mode`, which has been available since RStudio 0.97.124. (#326)

* Added `showEditSuggestion()` for displaying edit suggestions in the RStudio
editor. The function takes a document range and suggested replacement text,
allowing RStudio to present a visual diff that users can accept or dismiss.

# rstudioapi 0.18.0

* `rstudioapi::documentNew()` now accepts arbitrary document types. (#316)
Expand Down
14 changes: 12 additions & 2 deletions R/code.R
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,18 @@ getMode <- function() {

# use fallback if not
verifyAvailable()
rstudio <- as.environment("tools:rstudio")
if (rstudio$.rs.isDesktop()) "desktop" else "server"

# prefer the internal '.rs.isDesktop()' helper when available; this is
# fast, and lets us avoid the relatively slow 'versionInfo()' call, which
# reads the RStudio citation file (#280)
rstudio <- toolsEnv()
if (is.function(rstudio$.rs.isDesktop))
return(if (rstudio$.rs.isDesktop()) "desktop" else "server")

# otherwise, fall back to 'versionInfo()'; its '$mode' field has been
# available since RStudio 0.97.124, and so works even with very old
# versions of RStudio that lack the '.rs.isDesktop()' helper (#326)
versionInfo()$mode

}

Expand Down
76 changes: 76 additions & 0 deletions R/document-api.R
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,82 @@ documentSaveAll <- function() {
callFun("documentSaveAll")
}

#' Show an Edit Suggestion in RStudio
#'
#' Displays an edit suggestion for a specified range in the active document.
#' The suggestion shows what text would replace the given range. RStudio
#' computes and displays the necessary insertions and deletions.
#'
#' @param range A document range (or object coercible to a range) indicating
#' where the suggestion applies. Can be a \code{\link{document_range}} object or
#' a numeric vector of the form \code{c(start_row, start_col, end_row, end_col)}.
#'
#' @param text Character string containing the suggested replacement text for
#' the specified range.
#'
#' @param id The document id. When \code{NULL} or blank, the suggestion will
#' be shown in the currently open, or last focused, RStudio document.
#'
#' @return Invisibly returns \code{TRUE} if the suggestion was successfully shown,
#' \code{FALSE} otherwise.
#'
#' @details
#' The edit suggestion appears in the RStudio editor, showing what would change
#' if the suggestion were accepted. Users can accept or dismiss the suggestion
#' through RStudio's UI.
#'
#' The \code{range} parameter specifies the region of text that would be replaced.
#' RStudio automatically computes the minimal diff between the current text in
#' that range and the suggested \code{text}.
#'
#' @note The \code{showEditSuggestion} function was added in version 2026.01.0
#' of RStudio.
#'
#' @seealso \code{\link{setGhostText}} for inline completion suggestions,
#' \code{\link{insertText}} for direct text insertion,
#' \code{\link{modifyRange}} for programmatic range modification.
#'
#' @examples
#' \dontrun{
#' # Suggest replacing a word
#' range <- document_range(c(5, 1), c(5, 10))
#' showEditSuggestion(range, "corrected_text")
#'
#' # Using vector notation
#' showEditSuggestion(c(5, 1, 5, 10), "corrected_text")
#'
#' # Suggest for current selection
#' context <- getActiveDocumentContext()
#' selection <- primary_selection(context)
#' showEditSuggestion(selection$range, "improved code")
#' }
#'
#' @export
showEditSuggestion <- function(range, text, id = NULL) {

# Check if function is available in this RStudio version
if (!hasFun("showEditSuggestion")) {
warning("showEditSuggestion requires RStudio 2026.01.0 or newer")
return(invisible(FALSE))
}

# Validate and coerce range parameter
range <- as.document_range(range)

# Validate text parameter
if (!is.character(text) || length(text) != 1) {
stop("'text' must be a single character string")
}

# Call the RStudio API
callFun("showEditSuggestion",
range = range,
text = text,
id = id)

invisible(TRUE)
}

#' Retrieve Information about an RStudio Editor
#'
#' Returns information about an RStudio editor.
Expand Down
62 changes: 62 additions & 0 deletions man/showEditSuggestion.Rd

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

34 changes: 34 additions & 0 deletions tests/testthat/test-code.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
test_that("getMode() prefers the fast '.rs.isDesktop()' helper when available", {
local_edition(3)

rstudio <- new.env(parent = emptyenv())
rstudio$.rs.isDesktop <- function() TRUE

local_mocked_bindings(
hasFun = function(...) FALSE,
verifyAvailable = function(...) invisible(TRUE),
toolsEnv = function() rstudio,
versionInfo = function() stop("versionInfo() should not be called")
)

expect_identical(getMode(), "desktop")

rstudio$.rs.isDesktop <- function() FALSE
expect_identical(getMode(), "server")
})

test_that("getMode() falls back to versionInfo() when '.rs.isDesktop()' is unavailable (#326)", {
local_edition(3)

# an old RStudio that lacks the '.rs.isDesktop()' helper
rstudio <- new.env(parent = emptyenv())

local_mocked_bindings(
hasFun = function(...) FALSE,
verifyAvailable = function(...) invisible(TRUE),
toolsEnv = function() rstudio,
versionInfo = function() list(mode = "server")
)

expect_identical(getMode(), "server")
})
Loading