diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index fa7c935586a..c41adbfa365 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2884,4 +2884,33 @@ declare module 'vscode' { } + //#endregion + + //#region https://github.com/microsoft/vscode/issues/126280 @mjbvz + + export interface NotebookCellData { + /** + * Mime type determines how the cell's `value` is interpreted. + * + * The mime selects which notebook renders is used to render the cell. + * + * If not set, internally the cell is treated as having a mime type of `text/plain`. + * Cells that set `language` to `markdown` instead are treated as `text/markdown`. + */ + mime?: string; + } + + export interface NotebookCell { + /** + * Mime type determines how the markup cell's `value` is interpreted. + * + * The mime selects which notebook renders is used to render the cell. + * + * If not set, internally the cell is treated as having a mime type of `text/plain`. + * Cells that set `language` to `markdown` instead are treated as `text/markdown`. + */ + mime: string | undefined; + } + + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadNotebookDto.ts b/src/vs/workbench/api/browser/mainThreadNotebookDto.ts index 8bd8f7999d7..8d7a1f60a14 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookDto.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookDto.ts @@ -28,6 +28,7 @@ export namespace NotebookDto { return { cellKind: cell.cellKind, language: cell.language, + mime: cell.mime, source: cell.source, internalMetadata: cell.internalMetadata, metadata: cell.metadata, @@ -61,6 +62,7 @@ export namespace NotebookDto { return { cellKind: cell.cellKind, language: cell.language, + mime: cell.mime, source: cell.source, outputs: cell.outputs.map(fromNotebookOutputDto), metadata: cell.metadata, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 071ededcafe..c52ecaf5b26 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1930,6 +1930,7 @@ export interface NotebookOutputDto { export interface NotebookCellDataDto { source: string; language: string; + mime: string | undefined; cellKind: notebookCommon.CellKind; outputs: NotebookOutputDto[]; metadata?: notebookCommon.NotebookCellMetadata; @@ -1947,6 +1948,7 @@ export interface NotebookCellDto { eol: string; source: string[]; language: string; + mime?: string; cellKind: notebookCommon.CellKind; outputs: NotebookOutputDto[]; metadata?: notebookCommon.NotebookCellMetadata; @@ -2000,6 +2002,7 @@ export type NotebookRawContentEventDto = readonly append: boolean; } | notebookCommon.NotebookCellsChangeLanguageEvent + | notebookCommon.NotebookCellsChangeMimeEvent | notebookCommon.NotebookCellsChangeMetadataEvent | notebookCommon.NotebookCellsChangeInternalMetadataEvent // | notebookCommon.NotebookDocumentChangeMetadataEvent diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 7a0ac1ea3f0..b021fcc621a 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -54,6 +54,7 @@ export class ExtHostCell { readonly cellKind: notebookCommon.CellKind; private _apiCell: vscode.NotebookCell | undefined; + private _mime: string | undefined; constructor( readonly notebook: ExtHostNotebookDocument, @@ -85,6 +86,8 @@ export class ExtHostCell { notebook: that.notebook.apiNotebook, kind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind), document: data.document, + get mime() { return that._mime; }, + set mime(value: string | undefined) { that._mime = value; }, get outputs() { return that._outputs.slice(0); }, get metadata() { return that._metadata; }, get executionSummary() { return that._previousResult; } @@ -116,6 +119,10 @@ export class ExtHostCell { this._internalMetadata = newInternalMetadata; this._previousResult = extHostTypeConverters.NotebookCellExecutionSummary.to(newInternalMetadata); } + + setMime(newMime: string | undefined) { + + } } export interface INotebookEventEmitter { @@ -223,6 +230,8 @@ export class ExtHostNotebookDocument { this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems); } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeLanguage) { this._changeCellLanguage(rawEvent.index, rawEvent.language); + } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellMime) { + this._changeCellMime(rawEvent.index, rawEvent.mime); } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellMetadata) { this._changeCellMetadata(rawEvent.index, rawEvent.metadata); } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellInternalMetadata) { @@ -345,6 +354,11 @@ export class ExtHostNotebookDocument { } } + private _changeCellMime(index: number, newMime: string | undefined): void { + const cell = this._cells[index]; + cell.apiCell.mime = newMime; + } + private _changeCellMetadata(index: number, newMetadata: notebookCommon.NotebookCellMetadata): void { const cell = this._cells[index]; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index f17a1b750ff..1ab59702f9a 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1448,6 +1448,7 @@ export namespace NotebookCellData { return { cellKind: NotebookCellKind.from(data.kind), language: data.languageId, + mime: data.mime, source: data.value, metadata: data.metadata, internalMetadata: NotebookCellExecutionSummary.from(data.executionSummary ?? {}), @@ -1460,6 +1461,7 @@ export namespace NotebookCellData { NotebookCellKind.to(data.cellKind), data.source, data.language, + data.mime, data.outputs ? data.outputs.map(NotebookCellOutput.to) : undefined, data.metadata, data.internalMetadata ? NotebookCellExecutionSummary.to(data.internalMetadata) : undefined diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 6da3395f4c9..21bb3cbb6d7 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3015,14 +3015,16 @@ export class NotebookCellData { kind: NotebookCellKind; value: string; languageId: string; + mime?: string; outputs?: vscode.NotebookCellOutput[]; metadata?: Record; executionSummary?: vscode.NotebookCellExecutionSummary; - constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: vscode.NotebookCellOutput[], metadata?: Record, executionSummary?: vscode.NotebookCellExecutionSummary) { + constructor(kind: NotebookCellKind, value: string, languageId: string, mime?: string, outputs?: vscode.NotebookCellOutput[], metadata?: Record, executionSummary?: vscode.NotebookCellExecutionSummary) { this.kind = kind; this.value = value; this.languageId = languageId; + this.mime = mime; this.outputs = outputs ?? []; this.metadata = metadata; this.executionSummary = executionSummary; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index c6de02de5e0..7a52587d2c9 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -48,6 +48,10 @@ export abstract class BaseCellViewModel extends Disposable { } get mime(): string { + if (typeof this.model.mime === 'string') { + return this.model.mime; + } + switch (this.language) { case 'markdown': return Mimes.markdown; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 0e742145125..af2f173a67c 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -21,8 +21,8 @@ export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeOutputs = new Emitter(); onDidChangeOutputs: Event = this._onDidChangeOutputs.event; - private _onDidChangeContent = new Emitter<'content' | 'language'>(); - onDidChangeContent: Event<'content' | 'language'> = this._onDidChangeContent.event; + private _onDidChangeContent = new Emitter<'content' | 'language' | 'mime'>(); + onDidChangeContent: Event<'content' | 'language' | 'mime'> = this._onDidChangeContent.event; private _onDidChangeMetadata = new Emitter(); onDidChangeMetadata: Event = this._onDidChangeMetadata.event; @@ -89,6 +89,19 @@ export class NotebookCellTextModel extends Disposable implements ICell { this._onDidChangeContent.fire('language'); } + public get mime(): string | undefined { + return this._mime; + } + + public set mime(newMime: string | undefined) { + if (this._mime === newMime) { + return; + } + this._mime = newMime; + this._hash = null; + this._onDidChangeContent.fire('mime'); + } + private _textBuffer!: model.IReadonlyTextBuffer; get textBuffer() { @@ -161,6 +174,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { public handle: number, private _source: string, private _language: string, + private _mime: string | undefined, public cellKind: CellKind, outputs: IOutputDto[], metadata: NotebookCellMetadata | undefined, diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 396124b2413..29f92d45c8b 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -291,7 +291,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const mainCells = cells.map(cell => { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs, cell.metadata, cell.internalMetadata, this.transientOptions, this._modeService); + return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.mime, cell.cellKind, cell.outputs, cell.metadata, cell.internalMetadata, this.transientOptions, this._modeService); }); for (let i = 0; i < mainCells.length; i++) { @@ -306,12 +306,20 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._alternativeVersionId = this._generateAlternativeId(); } - private _bindCellContentHandler(cell: NotebookCellTextModel, e: 'content' | 'language') { + private _bindCellContentHandler(cell: NotebookCellTextModel, e: 'content' | 'language' | 'mime') { this._increaseVersionId(e === 'content'); - if (e === 'content') { - this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellContent, transient: false }, true); - } else { - this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeLanguage, index: this._getCellIndexByHandle(cell.handle), language: cell.language, transient: false }, true); + switch (e) { + case 'content': + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellContent, transient: false }, true); + break; + + case 'language': + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeLanguage, index: this._getCellIndexByHandle(cell.handle), language: cell.language, transient: false }, true); + break; + + case 'mime': + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellMime, index: this._getCellIndexByHandle(cell.handle), mime: cell.mime, transient: false }, true); + break; } } @@ -544,7 +552,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const cellUri = CellUri.generate(this.uri, cellHandle); const cell = new NotebookCellTextModel( cellUri, cellHandle, - cellDto.source, cellDto.language, cellDto.cellKind, cellDto.outputs || [], cellDto.metadata, cellDto.internalMetadata, this.transientOptions, + cellDto.source, cellDto.language, cellDto.mime, cellDto.cellKind, cellDto.outputs || [], cellDto.metadata, cellDto.internalMetadata, this.transientOptions, this._modeService ); const textModel = this._modelService.getModel(cellUri); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index ac9a109880d..e5df09b804e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -238,6 +238,7 @@ export enum NotebookCellsChangeType { ChangeCellContent = 10, ChangeDocumentMetadata = 11, ChangeCellInternalMetadata = 12, + ChangeCellMime = 13, Unknown = 100 } @@ -284,6 +285,12 @@ export interface NotebookCellsChangeLanguageEvent { readonly language: string; } +export interface NotebookCellsChangeMimeEvent { + readonly kind: NotebookCellsChangeType.ChangeCellMime; + readonly index: number; + readonly mime: string | undefined; +} + export interface NotebookCellsChangeMetadataEvent { readonly kind: NotebookCellsChangeType.ChangeCellMetadata; readonly index: number; @@ -305,14 +312,14 @@ export interface NotebookDocumentUnknownChangeEvent { readonly kind: NotebookCellsChangeType.Unknown; } -export type NotebookRawContentEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent; +export type NotebookRawContentEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMimeEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent; export type NotebookCellsChangedEventDto = { readonly rawEvents: NotebookRawContentEventDto[]; readonly versionId: number; }; -export type NotebookRawContentEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean; }; +export type NotebookRawContentEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMimeEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean; }; export enum SelectionStateType { Handle = 0, @@ -355,6 +362,7 @@ export const enum CellEditType { export interface ICellDto2 { source: string; language: string; + mime?: string; cellKind: CellKind; outputs: IOutputDto[]; metadata?: NotebookCellMetadata; diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index dc6cbf70d2c..38a8ae655c4 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -46,6 +46,7 @@ import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspac import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; +import { Mimes } from 'vs/base/common/mime'; export class TestCell extends NotebookCellTextModel { constructor( @@ -57,7 +58,7 @@ export class TestCell extends NotebookCellTextModel { outputs: IOutputDto[], modeService: IModeService, ) { - super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined, undefined, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }, modeService); + super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, Mimes.text, cellKind, outputs, undefined, undefined, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }, modeService); } }