Skip to content

Commit 1a247cc

Browse files
committed
fix(rstudioapi): resolve emulation issues and viewer routing
* Fix `rstudioapi::documentContext()` to return the complete document context, including `contents`, `path`, and `selection`. * Fix `rstudioapi::documentPath()` by accessing the corrected document context structure. * Refactor viewer dispatching to remove hardcoded `viewer` arguments from JSON-RPC payloads. `webview`, `dataview`, `browser`, and `page_viewer` now explicitly define their intent and dynamically resolve against the VS Code `session.viewers.viewColumn` settings. * Ignore `*.d.ts` files in `.eslintignore` to fix linting errors.
1 parent 65d8e13 commit 1a247cc

File tree

6 files changed

+105
-54
lines changed

6 files changed

+105
-54
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
scripts
2+
*.d.ts

sess/R/hooks.R

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -118,17 +118,15 @@ register_hooks <- function(use_rstudioapi = TRUE, use_httpgd = TRUE) {
118118
title = title,
119119
file = file_path,
120120
source = "table",
121-
type = "json",
122-
viewer = getOption("sess.dataview", "Two")
121+
type = "json"
123122
))
124123
} else if (is.list(x)) {
125124
jsonlite::write_json(x, file_path, auto_unbox = TRUE, null = "null", na = "string")
126125
notify_client("dataview", list(
127126
title = title,
128127
file = file_path,
129128
source = "list",
130-
type = "json",
131-
viewer = getOption("sess.dataview", "Two")
129+
type = "json"
132130
))
133131
} else {
134132
code <- if (is.primitive(x)) utils::capture.output(print(x)) else deparse(x)
@@ -138,41 +136,42 @@ register_hooks <- function(use_rstudioapi = TRUE, use_httpgd = TRUE) {
138136
title = title,
139137
file = file_path,
140138
source = "object",
141-
type = "R",
142-
viewer = getOption("sess.dataview", "Two")
139+
type = "R"
143140
))
144141
}
145142
}
146143
rebind("View", show_dataview, ns = "utils")
147144

148145
# 2. Browser & Webview Options
149-
viewer <- function(url, ...) {
150-
if (!is.character(url)) {
151-
real_url <- NULL
152-
temp_viewer <- function(url, ...) {
153-
real_url <<- url
154-
}
155-
op <- options(viewer = temp_viewer, page_viewer = temp_viewer)
156-
on.exit(options(op))
157-
print(url)
158-
if (is.character(real_url)) {
159-
url <- real_url
160-
} else {
161-
stop("Invalid object")
146+
make_viewer <- function(method) {
147+
function(url, ...) {
148+
if (!is.character(url)) {
149+
real_url <- NULL
150+
temp_viewer <- function(url, ...) {
151+
real_url <<- url
152+
}
153+
op <- options(viewer = temp_viewer, page_viewer = temp_viewer, browser = temp_viewer)
154+
on.exit(options(op))
155+
print(url)
156+
if (is.character(real_url)) {
157+
url <- real_url
158+
} else {
159+
stop("Invalid object")
160+
}
162161
}
163-
}
164162

165-
url <- sub("^file\\://", "", url)
166-
if (file.exists(url)) {
167-
url <- normalizePath(url, "/", mustWork = TRUE)
163+
url <- sub("^file\\://", "", url)
164+
if (file.exists(url)) {
165+
url <- normalizePath(url, "/", mustWork = TRUE)
166+
}
167+
notify_client(method, list(url = url))
168168
}
169-
notify_client("webview", list(url = url))
170169
}
171170

172171
options(
173-
browser = viewer,
174-
viewer = viewer,
175-
page_viewer = viewer,
172+
browser = make_viewer("browser"),
173+
viewer = make_viewer("webview"),
174+
page_viewer = make_viewer("page_viewer"),
176175
help_type = "html"
177176
)
178177

sess/R/rstudioapi.R

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ getActiveProject <- function() {
116116
}
117117

118118
document_context <- function(id = NULL) {
119-
request_client("rstudioapi/document_context", args = list(id = id))
119+
editor_context <- request_client("rstudioapi/document_context", args = list(id = id))
120+
make_rs_document_context(editor_context)
120121
}
121122

122-
documentId <- function(allowConsole = TRUE) document_context()$id$external
123-
documentPath <- function(id = NULL) document_context(id)$id$path
123+
documentId <- function(allowConsole = TRUE) document_context()$id
124+
documentPath <- function(id = NULL) document_context(id)$path
124125

125126
documentSaveAll <- function() {
126127
invisible(request_client("rstudioapi/document_save_all", args = list()))
@@ -155,11 +156,14 @@ restartSession <- function(command = "", clean = FALSE) {
155156
}
156157

157158
viewer <- function(url, height = NULL) {
158-
notify_client("webview", list(file = url, title = "Viewer"))
159+
notify_client("webview", list(url = url, title = "Viewer"))
159160
}
160161

161162
page_viewer <- function(url, title = NULL) {
162-
notify_client("browser", list(url = url, title = if (is.null(title)) "Page Viewer" else title))
163+
notify_client(
164+
"page_viewer",
165+
list(url = url, title = if (is.null(title)) "Page Viewer" else title)
166+
)
163167
}
164168

165169
getVersion <- function() numeric_version("0")
@@ -372,7 +376,6 @@ patch_rstudioapi <- function() {
372376
setDocumentContents = setDocumentContents,
373377
restartSession = restartSession,
374378
viewer = viewer,
375-
page_viewer = page_viewer,
376379
getVersion = getVersion,
377380
versionInfo = versionInfo,
378381
sendToConsole = sendToConsole,

src/liveShare/shareSession.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,23 +134,43 @@ export async function updateGuestRequest(file: string, force: boolean = false):
134134
sessionStatusBarItem.show();
135135
break;
136136
}
137-
case 'browser': {
138-
if (request.url && request.title && request.viewer !== undefined) {
139-
await showBrowser(request.url, request.title, request.viewer);
140-
}
141-
break;
142-
}
137+
case 'browser':
138+
case 'page_viewer':
143139
case 'webview': {
144-
if (request.file && request.title && request.viewer !== undefined) {
145-
await showWebView(request.file, request.title, request.viewer);
140+
if (request.url) {
141+
const url = String(request.url);
142+
const title = String(request.title ?? (request.command === 'browser' ? 'Browser' : request.command === 'page_viewer' ? 'Page Viewer' : 'Viewer'));
143+
144+
const viewColumnConfig = config().get<Record<string, string>>('session.viewers.viewColumn') ?? {};
145+
const configKey = request.command === 'page_viewer' ? 'pageViewer' : (request.command === 'browser' ? 'browser' : 'viewer');
146+
const viewerChoice = viewColumnConfig[configKey] ?? 'Active';
147+
const viewColumn = viewerChoice === 'Disable' ? false : viewerChoice;
148+
149+
if (url.startsWith('http://') || url.startsWith('https://')) {
150+
const isLocalHost = url.match(/^https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?/i);
151+
if (isLocalHost) {
152+
const externalUri = await vscode.env.asExternalUri(vscode.Uri.parse(url));
153+
await showBrowser(externalUri.toString(true), title, viewColumn);
154+
} else {
155+
await showBrowser(url, title, viewColumn);
156+
}
157+
} else {
158+
if (url.toLowerCase().endsWith('.html') || url.toLowerCase().endsWith('.htm')) {
159+
await showWebView(url, title, viewColumn);
160+
} else {
161+
await showDataView('object', 'txt', title, url, String(viewColumn));
162+
}
163+
}
146164
}
147165
break;
148166
}
149167
case 'dataview': {
150-
if (request.source && request.type && request.title && request.file
151-
&& request.viewer !== undefined) {
152-
await showDataView(request.source,
153-
request.type, request.title, request.file, request.viewer);
168+
if (request.source && request.type && request.title && request.file) {
169+
const viewColumnConfig = config().get<Record<string, string>>('session.viewers.viewColumn') ?? {};
170+
const viewer = viewColumnConfig['view'] ?? 'Two';
171+
if (viewer !== 'Disable') {
172+
await showDataView(String(request.source), String(request.type), String(request.title), String(request.file), viewer);
173+
}
154174
}
155175
break;
156176
}

src/rstudioapi.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,32 @@ export function activeEditorContext(): RSDocumentContext {
6767
};
6868
}
6969

70-
export async function documentContext(id: string | null): Promise<{ id: { external: string } }> {
70+
export async function documentContext(id: string | null): Promise<RSDocumentContext> {
7171
const target = findTargetUri(id);
7272
const targetDocument = await workspace.openTextDocument(target);
7373
console.info(`[documentContext] getting context for: ${target.path}`);
74+
75+
let selections: RSSelection[] = [];
76+
const knownEditors = [getLastActiveTextEditor(), ...window.visibleTextEditors];
77+
const editor = knownEditors.find(e => e?.document?.uri.toString() === targetDocument.uri.toString());
78+
79+
if (editor) {
80+
selections = editor.selections.map(s => ({
81+
start: { line: s.start.line + 1, character: s.start.character + 1 },
82+
end: { line: s.end.line + 1, character: s.end.character + 1 }
83+
}));
84+
} else {
85+
selections = [{
86+
start: { line: 1, character: 1 },
87+
end: { line: 1, character: 1 }
88+
}];
89+
}
90+
7491
return {
75-
id: { external: targetDocument.uri.toString() }
92+
id: { external: targetDocument.uri.toString() },
93+
contents: targetDocument.getText(),
94+
path: targetDocument.fileName,
95+
selection: selections
7696
};
7797
}
7898

src/session.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -750,35 +750,43 @@ async function handleNotification(message: Record<string, unknown>, ws: ExtWebSo
750750
}
751751
break;
752752
}
753+
case 'browser':
754+
case 'page_viewer':
753755
case 'webview': {
754756
if (params.url) {
755757
const url = String(params.url);
758+
const title = String(params.title ?? (method === 'browser' ? 'Browser' : method === 'page_viewer' ? 'Page Viewer' : 'Viewer'));
756759

757760
const viewColumnConfig = config().get<Record<string, string>>('session.viewers.viewColumn') ?? {};
758-
const viewerChoice = viewColumnConfig['viewer'] ?? 'Active';
761+
const configKey = method === 'page_viewer' ? 'pageViewer' : (method === 'browser' ? 'browser' : 'viewer');
762+
const viewerChoice = viewColumnConfig[configKey] ?? 'Active';
759763
const viewColumn = viewerChoice === 'Disable' ? false : viewerChoice;
760764

761765
if (url.startsWith('http://') || url.startsWith('https://')) {
762766
const isLocalHost = url.match(/^https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?/i);
763767
if (isLocalHost) {
764768
const externalUri = await env.asExternalUri(Uri.parse(url));
765-
await showBrowser(externalUri.toString(true), url, viewColumn);
769+
await showBrowser(externalUri.toString(true), title, viewColumn);
766770
} else {
767-
await showBrowser(url, url, viewColumn);
771+
await showBrowser(url, title, viewColumn);
768772
}
769773
} else {
770774
if (url.toLowerCase().endsWith('.html') || url.toLowerCase().endsWith('.htm')) {
771-
await showWebView(url, 'Viewer', viewColumn);
775+
await showWebView(url, title, viewColumn);
772776
} else {
773-
await showDataView('object', 'txt', 'Data Viewer', url, String(viewColumn));
777+
await showDataView('object', 'txt', title, url, String(viewColumn));
774778
}
775779
}
776780
}
777781
break;
778782
}
779783
case 'dataview': {
780-
if (params.source && params.type && params.file && params.title && params.viewer !== undefined) {
781-
await showDataView(String(params.source), String(params.type), String(params.title), String(params.file), String(params.viewer));
784+
if (params.source && params.type && params.file && params.title) {
785+
const viewColumnConfig = config().get<Record<string, string>>('session.viewers.viewColumn') ?? {};
786+
const viewer = viewColumnConfig['view'] ?? 'Two';
787+
if (viewer !== 'Disable') {
788+
await showDataView(String(params.source), String(params.type), String(params.title), String(params.file), viewer);
789+
}
782790
}
783791
break;
784792
}

0 commit comments

Comments
 (0)