From 4bb89bf6a6ac0b80c8376f8f2167fbc14012fabc Mon Sep 17 00:00:00 2001 From: TPOB <19909103+TTTPOB@users.noreply.github.com> Date: Fri, 7 Aug 2026 02:37:41 +0800 Subject: [PATCH] fix(vscode): open notebook links in notebook editor (#2373) --- .../vscode/src/bridge-system-runtime.test.js | 98 +++++++++++++++++++ packages/vscode/src/bridge-system-runtime.ts | 3 +- 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 packages/vscode/src/bridge-system-runtime.test.js diff --git a/packages/vscode/src/bridge-system-runtime.test.js b/packages/vscode/src/bridge-system-runtime.test.js new file mode 100644 index 00000000..09a17d26 --- /dev/null +++ b/packages/vscode/src/bridge-system-runtime.test.js @@ -0,0 +1,98 @@ +import { beforeEach, describe, expect, mock, test } from 'bun:test'; + +const executeCommand = mock(async () => undefined); + +class Position { + constructor(line, character) { + this.line = line; + this.character = character; + } +} + +class Range { + constructor(start, end) { + this.start = start; + this.end = end; + } +} + +mock.module('vscode', () => ({ + commands: { executeCommand }, + workspace: { + workspaceFolders: [], + }, + Uri: { + file: (fsPath) => ({ scheme: 'file', fsPath }), + }, + Position, + Range, +})); + +mock.module('./opencodeConfig', () => ({ + removeProviderConfig: mock(), + getProviderSources: mock(), +})); +mock.module('./opencodeAuth', () => ({ + getProviderAuth: mock(), + removeProviderAuth: mock(), +})); +mock.module('./quotaProviders', () => ({ + fetchQuotaForProvider: mock(), + listConfiguredQuotaProviders: mock(), +})); +mock.module('./opencodeGoQuota', () => ({ fetchOpenCodeGoUsage: mock() })); +mock.module('./quotaCredentials', () => ({ + credentialStatus: mock(), + deleteCredential: mock(), + importCursorCredential: mock(), + normalizeCredential: mock(), + readCredential: mock(), + validateCredential: mock(), + writeCredential: mock(), +})); +mock.module('./sessionActivityWatcher', () => ({ getSessionActivitySnapshot: mock() })); + +const { handleSystemBridgeMessage } = await import('./bridge-system-runtime.ts'); + +const deps = { + resolveUserPath: (value) => value, + fetchModelsMetadata: async () => ({}), + updateCheckUrl: 'https://example.com/update-check', + clientReloadDelayMs: 800, +}; + +describe('VS Code system bridge editor:openFile', () => { + beforeEach(() => { + executeCommand.mockClear(); + }); + + test('uses vscode.open so VS Code can select the notebook editor', async () => { + const response = await handleSystemBridgeMessage({ + id: 'open-notebook', + type: 'editor:openFile', + payload: { path: '/workspace/notebook.ipynb' }, + }, undefined, deps); + + expect(response).toEqual({ id: 'open-notebook', type: 'editor:openFile', success: true }); + expect(executeCommand).toHaveBeenCalledWith( + 'vscode.open', + { scheme: 'file', fsPath: '/workspace/notebook.ipynb' }, + {}, + ); + }); + + test('preserves line and column selection for regular files', async () => { + await handleSystemBridgeMessage({ + id: 'open-text', + type: 'editor:openFile', + payload: { path: '/workspace/source.ts', line: 4, column: 7 }, + }, undefined, deps); + + const position = new Position(3, 7); + expect(executeCommand).toHaveBeenCalledWith( + 'vscode.open', + { scheme: 'file', fsPath: '/workspace/source.ts' }, + { selection: new Range(position, position) }, + ); + }); +}); diff --git a/packages/vscode/src/bridge-system-runtime.ts b/packages/vscode/src/bridge-system-runtime.ts index 67ec64e1..5564bc23 100644 --- a/packages/vscode/src/bridge-system-runtime.ts +++ b/packages/vscode/src/bridge-system-runtime.ts @@ -356,13 +356,12 @@ export async function handleSystemBridgeMessage( case 'editor:openFile': { const { path: filePath, line, column } = payload as { path: string; line?: number; column?: number }; try { - const doc = await vscode.workspace.openTextDocument(filePath); const options: vscode.TextDocumentShowOptions = {}; if (typeof line === 'number') { const pos = new vscode.Position(Math.max(0, line - 1), column || 0); options.selection = new vscode.Range(pos, pos); } - await vscode.window.showTextDocument(doc, options); + await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(filePath), options); return { id, type, success: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error);