From b653280da8792d2bb150f16b3905f3e045bd83cc Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Wed, 30 Apr 2025 19:46:30 -0400 Subject: [PATCH] Data Explorer: Preserve non-file URIs when creating DuckDB clients. Previously we encoded only the original path in the data explorer, losing the scheme (and other URI attributes) and fundamentally assuming that we could only open local files. This commit instead encodes the original URI in its entirety and adds a helper to retrieve it, which makes it possible to use the `Open as Plain Text` button even with files backed by a virtual filesystem provider. Double-encoding the URI here is pretty gross, of course, but it's consistent with what we were already doing. This is the last piece of #7351. Signed-off-by: Aaron Jacobs --- .../browser/positronDataExplorerActions.ts | 23 ++++++++++--------- ...positronDataExplorerEditor.contribution.ts | 2 +- .../browser/positronDataExplorerEditor.tsx | 5 ++-- .../positronDataExplorerDuckDBBackend.ts | 2 +- .../common/positronDataExplorerUri.ts | 15 ++++++++++++ 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts index 58915d7e2ff..df49c98b98d 100644 --- a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts +++ b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts @@ -21,7 +21,6 @@ import { PositronDataExplorerEditorInput } from './positronDataExplorerEditorInp import { POSITRON_DATA_EXPLORER_IS_ACTIVE_EDITOR, POSITRON_DATA_EXPLORER_IS_COLUMN_SORTING, POSITRON_DATA_EXPLORER_IS_PLAINTEXT, POSITRON_DATA_EXPLORER_LAYOUT } from './positronDataExplorerContextKeys.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { PositronDataExplorerUri } from '../../../services/positronDataExplorer/common/positronDataExplorerUri.js'; -import { URI } from '../../../../base/common/uri.js'; import { EditorOpenSource } from '../../../../platform/editor/common/editor.js'; import { IPathService } from '../../../services/path/common/pathService.js'; import { toLocalResource } from '../../../../base/common/resources.js'; @@ -750,22 +749,24 @@ class PositronDataExplorerOpenAsPlaintextAction extends Action2 { return; } - // Parse this URI - gives underlying FS URI if not memory-backed (scheme = duckdb) - const parsedDataExplorerURI = PositronDataExplorerUri.parse(originalURI); - if (!parsedDataExplorerURI) { + let backingUri = PositronDataExplorerUri.backingUri(originalURI); + if (!backingUri) { return; } - // Convert raw duckdb URI to appropriate file URI (scheme = file if local, vscode-remote if server) - const localURI = toLocalResource( - URI.parse(parsedDataExplorerURI), - environmentService.remoteAuthority, - pathService.defaultUriScheme - ); + // Convert file URIs to the "local" scheme, i.e. vscode-remote when + // running as a server. + if (backingUri.scheme === 'file') { + backingUri = toLocalResource( + backingUri, + environmentService.remoteAuthority, + pathService.defaultUriScheme + ); + } // Invoke editor for file, using default editor (text) association await editorService.openEditor({ - resource: localURI, + resource: backingUri, options: { override: DEFAULT_EDITOR_ASSOCIATION.id, source: EditorOpenSource.USER diff --git a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.contribution.ts b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.contribution.ts index dee71d57f89..16f2b1fd6bb 100644 --- a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.contribution.ts +++ b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.contribution.ts @@ -99,7 +99,7 @@ class PositronDataExplorerContribution extends Disposable { // We create a data explorer URI that will use the DuckDB client // that we just created. - const newResource = PositronDataExplorerUri.generate(`duckdb:${resource.path}`); + const newResource = PositronDataExplorerUri.generate(`duckdb:${resource.toString()}`); return createDataExplorerEditor({ resource: newResource, options diff --git a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx index 6511a0d2ab5..68a37fe7ecb 100644 --- a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx +++ b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx @@ -36,7 +36,6 @@ import { IPositronDataExplorerService, PositronDataExplorerLayout } from '../../ import { PositronDataExplorerEditorInput } from './positronDataExplorerEditorInput.js'; import { PositronDataExplorerClosed, PositronDataExplorerClosedStatus } from '../../../browser/positronDataExplorer/components/dataExplorerClosed/positronDataExplorerClosed.js'; import { POSITRON_DATA_EXPLORER_IS_COLUMN_SORTING, POSITRON_DATA_EXPLORER_IS_PLAINTEXT, POSITRON_DATA_EXPLORER_LAYOUT } from './positronDataExplorerContextKeys.js'; -import { URI } from '../../../../base/common/uri.js'; /** * IPositronDataExplorerEditorOptions interface. @@ -362,8 +361,8 @@ export class PositronDataExplorerEditor extends EditorPane implements IPositronD positronDataExplorerInstance.tableDataDataGridInstance.isColumnSorting ); - const uri = URI.parse(this._identifier); - if (uri.scheme === 'duckdb') { + const uri = PositronDataExplorerUri.backingUri(input.resource); + if (uri) { this._isPlaintextContextKey.set(PLAINTEXT_EXTS.some(ext => uri.path.endsWith(ext))); } else { this._isPlaintextContextKey.reset(); diff --git a/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerDuckDBBackend.ts b/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerDuckDBBackend.ts index 181ce29f375..c6cb6c1639a 100644 --- a/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerDuckDBBackend.ts +++ b/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerDuckDBBackend.ts @@ -97,7 +97,7 @@ export class PositronDataExplorerDuckDBBackend extends Disposable implements IDa private readonly uri: URI ) { super(); - this.clientId = `duckdb:${this.uri.path}`; + this.clientId = `duckdb:${this.uri.toString()}`; this.initialSetup = this.openDataset(); } diff --git a/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerUri.ts b/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerUri.ts index 7da015f048a..ce8600aec39 100644 --- a/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerUri.ts +++ b/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerUri.ts @@ -56,4 +56,19 @@ export class PositronDataExplorerUri { return undefined; } } + + /** + * Parses a Positron data explorer URI and retrieves the URI of the backing file, if any. + * @param resource The data explorer resource. + * @returns A URI for the backing file, if any. + */ + public static backingUri(resource: URI): URI | undefined { + const identifier = PositronDataExplorerUri.parse(resource); + // Runtime comm IDs have no originating URIs. + if (!identifier || !identifier.startsWith('duckdb:')) { + return undefined; + } + // This will be something like "duckdb:file:///path/to/file.csv". + return URI.parse(identifier.replace('duckdb:', '')); + } }