fix: resolve debug console file links to remote filesystem in devcontainers - #307244
fix: resolve debug console file links to remote filesystem in devcontainers#307244Yogeshwaran C (yogeshwaran-c) wants to merge 2 commits into
Conversation
…ontainers When connected to a remote (e.g., devcontainer), file: URIs and absolute path links in the debug console now check the remote filesystem first before falling back to the local filesystem. Previously, clicking a file:///path/to/file link in the debug console always opened it from the host filesystem, even when the file was on the remote. This aligns the debug console behavior with how the terminal already handles file links in remote environments. Closes microsoft#111143
There was a problem hiding this comment.
Pull request overview
Fixes Debug Console link resolution in remote/devcontainer windows by preferring vscode-remote resources when remoteAuthority is present, aligning behavior more closely with Terminal link handling (issue #111143).
Changes:
- Update
file:web-link handling to attempt opening the correspondingvscode-remoteURI first, with local fallback. - Update absolute path link handling to stat/resolve remote paths first, with local fallback.
- Refactor selection computation to avoid duplication when opening editors.
Comments suppressed due to low confidence (1)
src/vs/workbench/contrib/debug/browser/linkDetector.ts:333
- In the remote branch, if
stat(remoteUri)succeeds but the result is a directory, the code returns without decorating the link and without attempting the localurifallback. This can cause a valid local file path to stop being linkified when a remote directory happens to exist at the same path. Consider treatingisDirectorythe same as “not found” and falling back to the localstat(uri)path.
this.fileService.stat(remoteUri).then(stat => {
if (stat.isDirectory) {
return;
}
this.decorateLink(link, remoteUri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: remoteUri, options: { ...options, preserveFocus } }));
}).catch(() => {
// Remote file not found, fall back to local filesystem
this.fileService.stat(uri).then(stat => {
if (stat.isDirectory) {
return;
}
this.decorateLink(link, uri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: uri, options: { ...options, preserveFocus } }));
| const remoteAuthority = this.environmentService.remoteAuthority; | ||
| if (remoteAuthority) { | ||
| const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: uri.path }); | ||
| const existsRemote = await this.fileService.exists(remoteUri); | ||
| if (existsRemote) { | ||
| await this.editorService.openEditor({ | ||
| resource: remoteUri, | ||
| options: { pinned: true, selection }, | ||
| }); | ||
| return; | ||
| } | ||
| } |
There was a problem hiding this comment.
fileService.exists(remoteUri) can still reject if withProvider(remoteUri) throws (e.g. scheme provider not available or transient remote disconnect). Since this is awaited without a try/catch, the click handler will throw and skip the local fallback, even when the local file exists. Consider wrapping the remote check in try/catch and falling back to the local fileUri on any error.
This issue also appears on line 322 of the same file.
| // When connected to a remote, try the remote filesystem first | ||
| // since the debug adapter likely produces paths from the remote host | ||
| // https://github.com/microsoft/vscode/issues/111143 | ||
| const remoteAuthority = this.environmentService.remoteAuthority; | ||
| if (remoteAuthority) { | ||
| const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: uri.path }); | ||
| this.fileService.stat(remoteUri).then(stat => { | ||
| if (stat.isDirectory) { | ||
| return; | ||
| } | ||
| this.decorateLink(link, remoteUri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: remoteUri, options: { ...options, preserveFocus } })); | ||
| }).catch(() => { | ||
| // Remote file not found, fall back to local filesystem | ||
| this.fileService.stat(uri).then(stat => { | ||
| if (stat.isDirectory) { | ||
| return; | ||
| } | ||
| this.decorateLink(link, uri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: uri, options: { ...options, preserveFocus } })); | ||
| }).catch(() => { | ||
| // If the uri can not be resolved we should not spam the console with error, remain quiet #86587 | ||
| }); | ||
| }); | ||
| } else { | ||
| this.fileService.stat(uri).then(stat => { | ||
| if (stat.isDirectory) { | ||
| return; | ||
| } | ||
| this.decorateLink(link, uri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: uri, options: { ...options, preserveFocus } })); | ||
| }).catch(() => { | ||
| // If the uri can not be resolved we should not spam the console with error, remain quiet #86587 | ||
| }); | ||
| } |
There was a problem hiding this comment.
createPathLink performs fileService.stat(remoteUri) during linkification (render time) for every detected path when in a remote window, potentially doubling filesystem round-trips due to the local fallback stat(uri). In output-heavy debug sessions this can add noticeable latency. Consider deferring remote/local existence checks until click time (similar to createWebLink), or caching results to avoid repeated remote stat calls.
| // When connected to a remote, try the remote filesystem first | |
| // since the debug adapter likely produces paths from the remote host | |
| // https://github.com/microsoft/vscode/issues/111143 | |
| const remoteAuthority = this.environmentService.remoteAuthority; | |
| if (remoteAuthority) { | |
| const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: uri.path }); | |
| this.fileService.stat(remoteUri).then(stat => { | |
| if (stat.isDirectory) { | |
| return; | |
| } | |
| this.decorateLink(link, remoteUri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: remoteUri, options: { ...options, preserveFocus } })); | |
| }).catch(() => { | |
| // Remote file not found, fall back to local filesystem | |
| this.fileService.stat(uri).then(stat => { | |
| if (stat.isDirectory) { | |
| return; | |
| } | |
| this.decorateLink(link, uri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: uri, options: { ...options, preserveFocus } })); | |
| }).catch(() => { | |
| // If the uri can not be resolved we should not spam the console with error, remain quiet #86587 | |
| }); | |
| }); | |
| } else { | |
| this.fileService.stat(uri).then(stat => { | |
| if (stat.isDirectory) { | |
| return; | |
| } | |
| this.decorateLink(link, uri, fulltext, hoverBehavior, (preserveFocus: boolean) => this.editorService.openEditor({ resource: uri, options: { ...options, preserveFocus } })); | |
| }).catch(() => { | |
| // If the uri can not be resolved we should not spam the console with error, remain quiet #86587 | |
| }); | |
| } | |
| // When connected to a remote, prefer the remote filesystem first | |
| // since the debug adapter likely produces paths from the remote host | |
| // https://github.com/microsoft/vscode/issues/111143 | |
| const remoteAuthority = this.environmentService.remoteAuthority; | |
| const remoteUri = remoteAuthority ? URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: uri.path }) : undefined; | |
| this.decorateLink( | |
| link, | |
| remoteUri ?? uri, | |
| fulltext, | |
| hoverBehavior, | |
| async (preserveFocus: boolean) => { | |
| const editorOptions = { ...options, preserveFocus }; | |
| if (remoteUri) { | |
| try { | |
| const stat = await this.fileService.stat(remoteUri); | |
| if (!stat.isDirectory) { | |
| await this.editorService.openEditor({ resource: remoteUri, options: editorOptions }); | |
| return; | |
| } | |
| } catch { | |
| // Remote file not found or not accessible, fall back to local filesystem | |
| } | |
| } | |
| try { | |
| const stat = await this.fileService.stat(uri); | |
| if (!stat.isDirectory) { | |
| await this.editorService.openEditor({ resource: uri, options: editorOptions }); | |
| } | |
| } catch { | |
| // If the uri cannot be resolved we should not spam the console with error, remain quiet #86587 | |
| } | |
| } | |
| ); |
| // When connected to a remote, try the remote filesystem first | ||
| // since the debug adapter likely produces paths from the remote host | ||
| // https://github.com/microsoft/vscode/issues/111143 | ||
| const remoteAuthority = this.environmentService.remoteAuthority; | ||
| if (remoteAuthority) { | ||
| const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: uri.path }); | ||
| const existsRemote = await this.fileService.exists(remoteUri); | ||
| if (existsRemote) { | ||
| await this.editorService.openEditor({ | ||
| resource: remoteUri, | ||
| options: { pinned: true, selection }, | ||
| }); | ||
| return; | ||
| } | ||
| } |
There was a problem hiding this comment.
New remote-resolution behavior (preferring vscode-remote resources and falling back to local) isn’t covered by the existing Debug Link Detector tests. Adding unit tests that stub environmentService.remoteAuthority, fileService.exists/stat, and editorService.openEditor would help prevent regressions (remote hit, remote miss -> local fallback, and error cases).
|
This looks generally good but please take a look at Copilot's code review comments |
|
Addressed Copilot's code review feedback in d17b12b:
|
- Wrap `fileService.exists(remoteUri)` in try/catch in `createWebLink` so that a transient remote disconnect or missing scheme provider falls through to the local filesystem instead of throwing in the click handler. - Defer remote `fileService.stat()` in `createPathLink` to click time instead of render time, avoiding doubled filesystem round-trips during linkification for every detected path in remote windows.
d17b12b to
f8551e7
Compare
What kind of change does this PR introduce?
Bug fix
What is the current behavior?
When connected to a remote environment (e.g., devcontainer), clicking a
file:///path/to/filelink or an absolute path link in the Debug Console opens the file from the host filesystem instead of the remote filesystem. The terminal handles this correctly, but the debug console does not.Closes #111143
What is the new behavior?
When
environmentService.remoteAuthorityis set (remote/devcontainer context), the debug console link detector now:For
file:URI web links (createWebLink): Constructs avscode-remoteURI using the remote authority and checks if the file exists on the remote filesystem first. If found, opens from remote. Falls back to local filesystem if not found.For absolute path links (
createPathLink): Same approach — tries the remote filesystem first viavscode-remotescheme, falls back to local on failure.This matches how the terminal already resolves file links in remote environments, as suggested in the maintainer comment.
Additional context
src/vs/workbench/contrib/debug/browser/linkDetector.tsSchemas.vscodeRemoteandIWorkbenchEnvironmentService.remoteAuthoritywhich are already imported/injectedURI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: uri.path })pattern follows established codebase conventions (e.g.,promptFilesLocator.ts)