diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2b0ccd7306b..da1949edda6 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1722,7 +1722,7 @@ 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): NotebookCell; + createCell(content: string, language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): NotebookCell; } export interface NotebookProvider { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 904c394eac0..a6e41261c6c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -671,7 +671,7 @@ export interface ICellDto { language: string; cellKind: CellKind; outputs: IOutput[]; - metadata: NotebookCellMetadata; + metadata?: NotebookCellMetadata; } export type NotebookCellsSplice = [ @@ -694,7 +694,7 @@ export interface MainThreadNotebookShape extends IDisposable { $createNotebookDocument(handle: number, viewType: string, resource: UriComponents): 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): Promise; + $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise; $spliceNotebookCells(viewType: string, resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): Promise; $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise; $postMessage(handle: number, value: any): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index c2e332f60ec..8213bad9d7c 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -21,10 +21,6 @@ const notebookDocumentMetadataDefaults: vscode.NotebookDocumentMetadata = { cellEditable: true }; -const notebookCellMetadataDefaults: vscode.NotebookCellMetadata = { - editable: true -}; - export class ExtHostCell implements vscode.NotebookCell { public source: string[]; @@ -44,7 +40,7 @@ export class ExtHostCell implements vscode.NotebookCell { public cellKind: CellKind, public language: string, outputs: any[], - private _metadata: vscode.NotebookCellMetadata, + private _metadata: vscode.NotebookCellMetadata | undefined, private _proxy: MainThreadNotebookShape ) { this.source = this._content.split(/\r|\n|\r\n/g); @@ -78,11 +74,10 @@ export class ExtHostCell implements vscode.NotebookCell { return this._metadata; } - set metadata(newMetadata: vscode.NotebookCellMetadata) { - let newMetadataWithDefaults = { - ...notebookCellMetadataDefaults, - ...(newMetadata || {}) - }; + set metadata(newMetadata: vscode.NotebookCellMetadata | undefined) { + const newMetadataWithDefaults: vscode.NotebookCellMetadata | undefined = newMetadata ? { + editable: newMetadata.editable + } : undefined; this._metadata = newMetadataWithDefaults; this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, newMetadataWithDefaults); @@ -157,19 +152,14 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages); } - private _metadata: vscode.NotebookDocumentMetadata = notebookDocumentMetadataDefaults; + private _metadata: vscode.NotebookDocumentMetadata | undefined = notebookDocumentMetadataDefaults; get metadata() { return this._metadata; } set metadata(newMetadata: vscode.NotebookDocumentMetadata | undefined) { - let newMetadataWithDefaults = { - ...notebookDocumentMetadataDefaults, - ...(newMetadata || {}) - }; - - this._metadata = newMetadataWithDefaults; + this._metadata = newMetadata || notebookDocumentMetadataDefaults; this._proxy.$updateNotebookMetadata(this.viewType, this.uri, this._metadata); } @@ -413,10 +403,6 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook 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); - metadata = { - ...notebookCellMetadataDefaults, - ...(metadata || {}) - }; const cell = new ExtHostCell(this.viewType, this.uri, handle, uri, content, type, language, outputs, metadata, this._proxy); return cell; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 195de3a7bf3..5fbbc429adc 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -71,7 +71,7 @@ export interface ICellViewModel { runState: CellRunState; focusMode: CellFocusMode; getText(): string; - metadata: NotebookCellMetadata; + metadata: NotebookCellMetadata | undefined; } export interface INotebookEditor { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 7bf60cc0870..0e2f685aa49 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -339,6 +339,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { this.eventDispatcher = new NotebookEventDispatcher(); this.notebookViewModel = this.instantiationService.createInstance(NotebookViewModel, input.viewType!, model, this.eventDispatcher); + this.editorEditable?.set(!!this.notebookViewModel.metadata?.editable); this.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); const viewState = this.loadTextEditorViewState(input); this.notebookViewModel.restoreEditorViewState(viewState); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 6137ef0380a..918ca3b4137 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -216,9 +216,9 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const contextKeyService = this.contextKeyService.createScoped(templateData.container); contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'markdown'); - const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, element.metadata.editable); + const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, !!(element.metadata?.editable)); elementDisposable.add(element.onDidChangeMetadata((e) => { - cellEditableKey.set(e.editable); + cellEditableKey.set(!!e?.editable); })); const editModeKey = contextKeyService.createKey(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY, element.editState === CellEditState.Editing); @@ -356,9 +356,9 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const contextKeyService = this.contextKeyService.createScoped(templateData.container); contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'code'); - const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, element.metadata.editable); + const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, !!(element.metadata?.editable)); elementDisposable.add(element.onDidChangeMetadata((e) => { - cellEditableKey.set(e.editable); + cellEditableKey.set(!!e?.editable); })); this.setupCellToolbarActions(contextKeyService, templateData, elementDisposable); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index d49f42ae6bf..ac0ac7fb97b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -80,9 +80,9 @@ export class CodeCell extends Disposable { } })); - templateData.editor?.updateOptions({ readOnly: !viewCell.metadata.editable }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) }); this._register(viewCell.onDidChangeMetadata((e) => { - templateData.editor?.updateOptions({ readOnly: !e.editable }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) }); })); let cellWidthResizeObserver = getResizesObserver(templateData.editorContainer!, { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index c97eb533ddc..316ebbb8f05 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -9,11 +9,15 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; -import { ICell, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICell, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CursorAtBoundary, CellFocusMode, CellEditState, CellRunState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { EDITOR_TOP_PADDING, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; +export const NotebookCellMetadataDefaults = { + editable: true +}; + export abstract class BaseCellViewModel extends Disposable { protected readonly _onDidDispose = new Emitter(); readonly onDidDispose = this._onDidDispose.event; @@ -27,8 +31,8 @@ export abstract class BaseCellViewModel extends Disposable { readonly onDidChangeEditorAttachState = this._onDidChangeEditorAttachState.event; protected readonly _onDidChangeCursorSelection: Emitter = this._register(new Emitter()); public readonly onDidChangeCursorSelection: Event = this._onDidChangeCursorSelection.event; - protected readonly _onDidChangeMetadata: Emitter = this._register(new Emitter()); - public readonly onDidChangeMetadata: Event = this._onDidChangeMetadata.event; + protected readonly _onDidChangeMetadata: Emitter = this._register(new Emitter()); + public readonly onDidChangeMetadata: Event = this._onDidChangeMetadata.event; get handle() { return this.cell.handle; } @@ -311,6 +315,16 @@ export abstract class BaseCellViewModel extends Disposable { return cellMatches; } + getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata | undefined): NotebookCellMetadata { + const editable: boolean = this.metadata?.editable === undefined + ? (documentMetadata?.cellEditable === undefined ? NotebookCellMetadataDefaults.editable : documentMetadata?.cellEditable) + : this.metadata?.editable; + + return { + editable: editable + }; + } + toJSON(): any { return { handle: this.handle diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index aa89ca52d4c..9c920405eda 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -15,8 +15,8 @@ export class NotebookCellTextModel implements ICell { private _onDidChangeContent = new Emitter(); onDidChangeContent: Event = this._onDidChangeContent.event; - private _onDidChangeMetadata = new Emitter(); - onDidChangeMetadata: Event = this._onDidChangeMetadata.event; + private _onDidChangeMetadata = new Emitter(); + onDidChangeMetadata: Event = this._onDidChangeMetadata.event; private _outputs: IOutput[]; @@ -33,13 +33,13 @@ export class NotebookCellTextModel implements ICell { this._buffer = null; } - private _metadata: NotebookCellMetadata; + private _metadata: NotebookCellMetadata | undefined; get metadata() { return this._metadata; } - set metadata(newMetadata: NotebookCellMetadata) { + set metadata(newMetadata: NotebookCellMetadata | undefined) { this._metadata = newMetadata; this._onDidChangeMetadata.fire(this._metadata); } @@ -53,7 +53,7 @@ export class NotebookCellTextModel implements ICell { public language: string, public cellKind: CellKind, outputs: IOutput[], - metadata: NotebookCellMetadata + metadata: NotebookCellMetadata | undefined ) { this._outputs = outputs; this._metadata = metadata; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 7e41e61e9ef..ee9f411e366 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -38,10 +38,11 @@ export const NOTEBOOK_DISPLAY_ORDER = [ export interface NotebookDocumentMetadata { editable: boolean; + cellEditable?: boolean; } export interface NotebookCellMetadata { - editable: boolean; + editable?: boolean; } export interface INotebookDisplayOrder { @@ -130,9 +131,9 @@ export interface ICell { language: string; cellKind: CellKind; outputs: IOutput[]; - metadata: NotebookCellMetadata; + metadata?: NotebookCellMetadata; onDidChangeOutputs?: Event; - onDidChangeMetadata: Event; + onDidChangeMetadata: Event; resolveTextBufferFactory(): PieceTreeTextBufferFactory; // TODO@rebornix it should be later on replaced by moving textmodel resolution into CellTextModel contentChange(): void; diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 36738cb64a4..2a423d24a0b 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -39,8 +39,8 @@ suite('NotebookViewModel', () => { [['var b = 2;'], 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel) => { - assert.equal(viewModel.viewCells[0].metadata.editable, true); - assert.equal(viewModel.viewCells[1].metadata.editable, false); + assert.equal(viewModel.viewCells[0].metadata?.editable, true); + assert.equal(viewModel.viewCells[1].metadata?.editable, false); const cell = viewModel.insertCell(1, new TestCell(viewModel.viewType, 0, ['var c = 3;'], 'javascript', CellKind.Code, []), true); assert.equal(viewModel.viewCells.length, 3);