diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6074ae5204c..92ce903af30 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1718,6 +1718,12 @@ declare module 'vscode' { metadata?: NotebookDocumentMetadata; } + export interface NotebookEditorCellEdit { + // createCell(content: string, language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): NotebookCell; + insert(index: number, content: string, language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void; + delete(index: number): void; + } + export interface NotebookEditor { readonly document: NotebookDocument; viewColumn?: ViewColumn; @@ -1738,6 +1744,8 @@ declare module 'vscode' { * Create a notebook cell. The cell is not inserted into current document when created. Extensions should insert the cell into the document by [TextDocument.cells](#TextDocument.cells) */ createCell(content: string, language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): NotebookCell; + + edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable; } export interface NotebookProvider { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 4daa68da9c1..047f8e8f73c 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext } from '../common/extHost.protocol'; +import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext, ICellEditOperation } from '../common/extHost.protocol'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/browser/notebookService'; @@ -65,6 +65,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo this.registerListeners(); } + $tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise { + throw new Error('Method not implemented.'); + } + registerListeners() { this._register(this._notebookService.onDidChangeActiveEditor(e => { this._proxy.$updateActiveEditor(e.viewType, e.uri); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index a6e41261c6c..dce58c344a3 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -686,12 +686,41 @@ export type NotebookCellOutputsSplice = [ IOutput[] ]; + +export enum CellEditType { + Insert = 1, + Delete = 2 +} + +export interface ICellInsertEdit { + editType: CellEditType.Insert; + index: number; + content: string; + language: string; + type: CellKind; + outputs: IOutput[]; + metadata?: NotebookCellMetadata; +} + +export interface ICellDeleteEdit { + editType: CellEditType.Delete; + index: number; +} + +export type ICellEditOperation = ICellInsertEdit | ICellDeleteEdit; + +export interface INotebookEditData { + documentVersionId: number; + edits: ICellEditOperation[]; +} + export interface MainThreadNotebookShape extends IDisposable { $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string): Promise; $unregisterNotebookProvider(viewType: string): Promise; $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise; $unregisterNotebookRenderer(handle: number): Promise; $createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise; + $tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 3a60ef04051..739450cf8d3 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ExtHostNotebookShape, IMainContext, MainThreadNotebookShape, MainContext, ICellDto, NotebookCellsSplice, NotebookCellOutputsSplice, CellKind, CellOutputKind } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookShape, IMainContext, MainThreadNotebookShape, MainContext, ICellDto, NotebookCellsSplice, NotebookCellOutputsSplice, CellKind, CellOutputKind, ICellEditOperation, INotebookEditData, CellEditType } from 'vs/workbench/api/common/extHost.protocol'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Disposable as VSCodeDisposable } from './extHostTypes'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -292,7 +292,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo } - transformMimeTypes(cell: ExtHostCell, output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto { + transformMimeTypes(cell: vscode.NotebookCell, output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto { let mimeTypes = Object.keys(output.data); // TODO@rebornix, the document display order might be assigned a bit later. We need to postpone sending the outputs to the core side. @@ -364,6 +364,57 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo } } +export class NotebookEditorCellEdit { + private _finalized: boolean = false; + private readonly _documentVersionId: number; + private _collectedEdits: ICellEditOperation[] = []; + + + constructor( + readonly editor: ExtHostNotebookEditor + ) { + // TODO@rebornix + this._documentVersionId = 0; + } + + finalize(): INotebookEditData { + this._finalized = true; + return { + documentVersionId: this._documentVersionId, + edits: this._collectedEdits + }; + } + + private _throwIfFinalized() { + if (this._finalized) { + throw new Error('Edit is only valid while callback runs'); + } + } + + insert(index: number, content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void { + this._throwIfFinalized(); + + this._collectedEdits.push({ + editType: CellEditType.Insert, + index, + content, + language, + type, + outputs: (outputs as any[]), // TODO@rebornix + metadata + }); + } + + delete(index: number): void { + this._throwIfFinalized(); + + this._collectedEdits.push({ + editType: CellEditType.Delete, + index + }); + } +} + export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor { private _viewColumn: vscode.ViewColumn | undefined; private static _cellhandlePool: number = 0; @@ -402,6 +453,23 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook })); } + edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable { + const edit = new NotebookEditorCellEdit(this); + callback(edit); + return this._applyEdit(edit); + } + + private _applyEdit(editBuilder: NotebookEditorCellEdit): Promise { + const editData = editBuilder.finalize(); + + // return when there is nothing to do + if (editData.edits.length === 0) { + return Promise.resolve(true); + } + + return this._proxy.$tryApplyEdits(this.uri, editData.documentVersionId, editData.edits); + } + createCell(content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): vscode.NotebookCell { const handle = ExtHostNotebookEditor._cellhandlePool++; const uri = CellUri.generate(this.document.uri, handle); @@ -444,7 +512,7 @@ export class ExtHostNotebookOutputRenderer { return false; } - render(document: ExtHostNotebookDocument, cell: ExtHostCell, output: vscode.CellOutput, mimeType: string): string { + render(document: ExtHostNotebookDocument, cell: vscode.NotebookCell, output: vscode.CellOutput, mimeType: string): string { let html = this.renderer.render(document, cell, output, mimeType); return html;