diff --git a/src/sql/workbench/api/common/notebooks/notebookUtils.ts b/src/sql/workbench/api/common/notebooks/notebookUtils.ts index 9f6cfd2dd7..c000e12b2d 100644 --- a/src/sql/workbench/api/common/notebooks/notebookUtils.ts +++ b/src/sql/workbench/api/common/notebooks/notebookUtils.ts @@ -17,6 +17,10 @@ const DotnetInteractiveLanguagePrefix = 'dotnet-interactive.'; export const DotnetInteractiveDisplayName = '.NET Interactive'; export function convertToVSCodeNotebookCell(cellKind: azdata.nb.CellType, cellIndex: number, cellUri: URI, docUri: URI, cellLanguage: string, cellSource?: string | string[]): vscode.NotebookCell { + // We only use this notebook field for .NET Interactive's intellisense, which only uses the notebook's URI + let notebook = { + uri: docUri + }; return { kind: cellKind === CellTypes.Code ? NotebookCellKind.Code : NotebookCellKind.Markup, index: cellIndex, @@ -24,10 +28,9 @@ export function convertToVSCodeNotebookCell(cellKind: azdata.nb.CellType, cellIn uri: cellUri, languageId: cellLanguage, getText: () => Array.isArray(cellSource) ? cellSource.join('') : (cellSource ?? ''), + notebook: notebook }, - notebook: { - uri: docUri - }, + notebook: notebook, outputs: [], metadata: {}, mime: undefined diff --git a/src/sql/workbench/common/constants.ts b/src/sql/workbench/common/constants.ts index 2ac4543c7d..e6b74f6ffd 100644 --- a/src/sql/workbench/common/constants.ts +++ b/src/sql/workbench/common/constants.ts @@ -41,6 +41,7 @@ export const INTERACTIVE_PROVIDER_ID = 'dotnet-interactive'; export const INTERACTIVE_LANGUAGE_MODE = 'dib'; export const DEFAULT_NB_LANGUAGE_MODE = 'notebook'; export const TSGOPS_WEB_QUALITY = 'tsgops-image'; +export const CELL_URI_PATH_PREFIX = 'notebook-editor-'; // The version of the notebook file format that we support export const NBFORMAT = 4; diff --git a/src/sql/workbench/contrib/notebook/test/stubs.ts b/src/sql/workbench/contrib/notebook/test/stubs.ts index c21f0c9011..69545b4c25 100644 --- a/src/sql/workbench/contrib/notebook/test/stubs.ts +++ b/src/sql/workbench/contrib/notebook/test/stubs.ts @@ -235,6 +235,9 @@ export class ServerManagerStub implements nb.ServerManager { } export class NotebookServiceStub implements INotebookService { + getNotebookURIForCell(cellUri: URI): URI { + throw new Error('Method not implemented.'); + } getSupportedLanguagesForProvider(provider: string, kernelDisplayName?: string): Promise { throw new Error('Method not implemented.'); } diff --git a/src/sql/workbench/services/notebook/browser/models/cell.ts b/src/sql/workbench/services/notebook/browser/models/cell.ts index de130a4ab6..7fd1724e5b 100644 --- a/src/sql/workbench/services/notebook/browser/models/cell.ts +++ b/src/sql/workbench/services/notebook/browser/models/cell.ts @@ -35,6 +35,7 @@ import { CellOutputEdit, CellOutputDataEdit } from 'sql/workbench/services/noteb import { ILogService } from 'vs/platform/log/common/log'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ICellMetadata } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { CELL_URI_PATH_PREFIX } from 'sql/workbench/common/constants'; let modelId = 0; const ads_execute_command = 'ads_execute_command'; @@ -1121,7 +1122,7 @@ export class CellModel extends Disposable implements ICellModel { } private createUri(): void { - let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.id}` }); + let uri = URI.from({ scheme: Schemas.untitled, path: `${CELL_URI_PATH_PREFIX}${this.id}` }); // Use this to set the internal (immutable) and public (shared with extension) uri properties this.cellUri = uri; } diff --git a/src/sql/workbench/services/notebook/browser/notebookService.ts b/src/sql/workbench/services/notebook/browser/notebookService.ts index 770c2149b3..24866efebb 100644 --- a/src/sql/workbench/services/notebook/browser/notebookService.ts +++ b/src/sql/workbench/services/notebook/browser/notebookService.ts @@ -144,6 +144,8 @@ export interface INotebookService { openNotebook(resource: UriComponents, options: INotebookShowOptions): Promise; getUntitledUriPath(originalTitle: string): string; + + getNotebookURIForCell(cellUri: URI): URI | undefined; } export interface IExecuteProvider { diff --git a/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts b/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts index 123bca921e..52b2b29abf 100644 --- a/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts +++ b/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts @@ -371,6 +371,19 @@ export class NotebookService extends Disposable implements INotebookService { return title; } + public getNotebookURIForCell(cellUri: URI): URI | undefined { + for (let editor of this.listNotebookEditors()) { + if (editor.cells) { + for (let cell of editor.cells) { + if (cell.cellUri === cellUri) { + return editor.notebookParams.notebookUri; + } + } + } + } + return undefined; + } + private updateSQLRegistrationWithConnectionProviders() { // Update the SQL extension let sqlNotebookKernels = this._providerToStandardKernels.get(notebookConstants.SQL); diff --git a/src/sql/workbench/test/electron-browser/api/vscodeNotebookApi.test.ts b/src/sql/workbench/test/electron-browser/api/vscodeNotebookApi.test.ts index f84c1e8744..365d17cb1d 100644 --- a/src/sql/workbench/test/electron-browser/api/vscodeNotebookApi.test.ts +++ b/src/sql/workbench/test/electron-browser/api/vscodeNotebookApi.test.ts @@ -425,6 +425,9 @@ suite('Notebook Serializer', () => { assert.deepStrictEqual(actual.document.uri, expected.document.uri); assert.strictEqual(actual.document.languageId, expected.document.languageId); assert.deepStrictEqual(actual.notebook.uri, expected.notebook.uri); + assert.deepStrictEqual(actual.document.notebook.uri, expected.document.notebook.uri); + assert.deepStrictEqual(actual.document.notebook.uri, expected.notebook.uri); + assert.deepStrictEqual(actual.notebook.uri, expected.document.notebook.uri); } function validateCellsMatch(actual: vscode.NotebookCell[], expected: vscode.NotebookCell[]): void { assert.strictEqual(actual.length, expected.length, 'Cell arrays did not have equal lengths.'); diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index 71e65ab11c..9788549e2f 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -33,6 +33,8 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { diffSets, diffMaps } from 'vs/base/common/collections'; import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; +import { Schemas } from 'vs/base/common/network'; +import { CELL_URI_PATH_PREFIX } from 'sql/workbench/common/constants'; class TextEditorSnapshot { @@ -390,13 +392,20 @@ export class MainThreadDocumentsAndEditors { } private _toModelAddData(model: ITextModel): IModelAddedData { + // {{SQL CARBON EDIT}} + // Check if this TextModel is part of a notebook cell + let notebookUri: URI; + if (model.uri.scheme === Schemas.untitled && model.uri.path.startsWith(CELL_URI_PATH_PREFIX)) { + notebookUri = this._notebookService.getNotebookURIForCell(model.uri); + } return { uri: model.uri, versionId: model.getVersionId(), lines: model.getLinesContent(), EOL: model.getEOL(), modeId: model.getLanguageIdentifier().language, - isDirty: this._textFileService.isDirty(model.uri) + isDirty: this._textFileService.isDirty(model.uri), + notebookUri: notebookUri }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 95498b4b76..314bac3c8f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1212,6 +1212,8 @@ export interface IModelAddedData { EOL: string; modeId: string; isDirty: boolean; + // {{SQL CARBON EDIT}} + notebookUri?: URI; } export interface ExtHostDocumentsShape { $acceptModelModeChanged(strURL: UriComponents, newModeId: string): void; diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index 17ae0226bc..158443f002 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -97,6 +97,14 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } } if (!ref) { + // {{SQL CARBON EDIT}} + // Add URI of the notebook that is using this document for a cell's editor. + if (!data.notebook && data.notebookUri) { + // We only use this notebook field for .NET Interactive's intellisense, which only uses the notebook's URI + data.notebook = { + uri: URI.revive(data.notebookUri) + }; + } ref = new Reference(new ExtHostDocumentData( this._extHostRpc.getProxy(MainContext.MainThreadDocuments), resource, diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index 565ee1e078..67b6603955 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -11,6 +11,7 @@ import { Disposable, IDisposable, toDisposable, DisposableStore, dispose } from import { ResourceMap } from 'vs/base/common/map'; import { IWorkingCopy, IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { Schemas } from 'vs/base/common/network'; // {{SQL CARBON EDIT}} @chlafreniere need to block working copies of notebook editors from being tracked +import { CELL_URI_PATH_PREFIX } from 'sql/workbench/common/constants'; export const IWorkingCopyService = createDecorator('workingCopyService'); @@ -142,7 +143,7 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic } // {{SQL CARBON EDIT}} @chlafreniere need to block working copies of notebook editors from being tracked - if (workingCopy.resource.path.includes('notebook-editor-') && workingCopy.resource.scheme === Schemas.untitled) { + if (workingCopy.resource.path.includes(CELL_URI_PATH_PREFIX) && workingCopy.resource.scheme === Schemas.untitled) { return new DisposableStore(); }