diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index e28f7851263..3d8849efcc5 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -572,6 +572,10 @@ export class MouseController implements IDisposable { } private onMouseDown(e: IListMouseEvent | IListTouchEvent): void { + if (e.browserEvent.target && this.list.view.domNode.contains(e.browserEvent.target as HTMLElement)) { + return; + } + if (document.activeElement !== e.browserEvent.target) { this.list.domFocus(); } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index e7f24a249c5..21ecf7e0c28 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1333,6 +1333,7 @@ declare module 'vscode' { export interface NotebookDocument { readonly uri: Uri; readonly fileName: string; + readonly isDirty: boolean; cells: NotebookCell[]; } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 887c68f0d78..37befffcf30 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -147,7 +147,13 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo let handle = await this._proxy.$resolveNotebook(viewType, uri); if (handle !== undefined) { - return this._documents.get(handle); + const doc = this._documents.get(handle); + + if (doc === undefined) { + console.log('resolve notebook from main but undefined'); + } + + return doc; } return; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d78feb30112..1b2704ddc4f 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -128,7 +128,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol)); const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); - const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol)); + const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostDocumentsAndEditors)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); // Check that no named customers are missing diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index d614185d18e..c664cda9f98 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { readonly } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; // import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; export class ExtHostCell implements vscode.NotebookCell { @@ -21,6 +22,8 @@ export class ExtHostCell implements vscode.NotebookCell { private _outputs: any[]; private _onDidChangeOutputs = new Emitter(); onDidChangeOutputs: Event = this._onDidChangeOutputs.event; + private _textDocument: vscode.TextDocument | undefined; + private _initalVersion: number = -1; constructor( private _content: string, @@ -42,7 +45,25 @@ export class ExtHostCell implements vscode.NotebookCell { } getContent(): string { - throw new Error('Method not implemented.'); + if (this._textDocument && this._initalVersion !== this._textDocument?.version) { + return this._textDocument.getText(); + } else { + return this.source.join('\n'); + } + } + + attachTextDocument(document: vscode.TextDocument) { + this._textDocument = document; + this._initalVersion = this._textDocument.version; + } + + detachTextDocument(document: vscode.TextDocument) { + if (this._textDocument && this._textDocument.version !== this._initalVersion) { + this.source = this._textDocument.getText().split(/\r|\n|\r\n/g); + } + + this._textDocument = undefined; + this._initalVersion = -1; } } @@ -80,6 +101,8 @@ export class ExtHostNotebookDocument implements vscode.NotebookDocument { get fileName() { return this.uri.fsPath; } + get isDirty() { return false; } + async $updateCells(): Promise { return await this._proxy.$updateNotebookCells(this.handle, this.cells.map(cell => ({ handle: cell.handle, @@ -92,18 +115,74 @@ export class ExtHostNotebookDocument implements vscode.NotebookDocument { getActiveCell(cellHandle: number) { return this.cells.find(cell => cell.handle === cellHandle); } + + attachCellTextDocument(cellHandle: number, textDocument: vscode.TextDocument) { + let cell = this.cells.find(cell => cell.handle === cellHandle); + + if (cell) { + cell.attachTextDocument(textDocument); + } + } + + detachCellTextDocument(cellHandle: number, textDocument: vscode.TextDocument) { + let cell = this.cells.find(cell => cell.handle === cellHandle); + + if (cell) { + cell.detachTextDocument(textDocument); + } + } } export class ExtHostNotebookEditor implements vscode.NotebookEditor { private _viewColumn: vscode.ViewColumn | undefined; constructor( + private viewType: string, private readonly _proxy: MainThreadNotebookShape, readonly id: string, public uri: URI, public document: ExtHostNotebookDocument, - private readonly documentsProxy: MainThreadDocumentsShape + private readonly documentsProxy: MainThreadDocumentsShape, + private _documentsAndEditors: ExtHostDocumentsAndEditors ) { + let regex = new RegExp(`${viewType}-(\\d+)-(\\d+)`); + this._documentsAndEditors.onDidAddDocuments(documents => { + for (const data of documents) { + let textDocument = data.document; + let authority = textDocument.uri.authority; + + if (authority !== '') { + let matches = regex.exec(authority); + if (matches) { + const notebookHandle = matches[1]; + const cellHandle = matches[2]; + + if (Number(notebookHandle) === this.document.handle) { + document.attachCellTextDocument(Number(cellHandle), textDocument); + } + } + } + } + }); + + this._documentsAndEditors.onDidRemoveDocuments(documents => { + for (const data of documents) { + let textDocument = data.document; + let authority = textDocument.uri.authority; + + if (authority !== '') { + let matches = regex.exec(authority); + if (matches) { + const notebookHandle = matches[1]; + const cellHandle = matches[2]; + + if (Number(notebookHandle) === this.document.handle) { + document.detachCellTextDocument(Number(cellHandle), textDocument); + } + } + } + } + }); } createCell(content: string, language: string, type: 'markdown' | 'code', outputs: vscode.CellOutput[]): vscode.NotebookCell { @@ -130,7 +209,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { private readonly _documents = new Map(); - constructor(mainContext: IMainContext) { + constructor(mainContext: IMainContext, private _documentsAndEditors: ExtHostDocumentsAndEditors) { this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook); this._documentsProxy = mainContext.getProxy(MainContext.MainThreadDocuments); @@ -168,7 +247,15 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { this._documents.set(URI.revive(uri).toString(), document); } - let editor = new ExtHostNotebookEditor(this._proxy, `${ExtHostNotebookController._handlePool++}`, uri, this._documents.get(URI.revive(uri).toString())!, this._documentsProxy); + let editor = new ExtHostNotebookEditor( + viewType, + this._proxy, + `${ExtHostNotebookController._handlePool++}`, + uri, + this._documents.get(URI.revive(uri).toString())!, + this._documentsProxy, + this._documentsAndEditors + ); await provider.provider.resolveNotebook(editor); await editor.document.$updateCells(); return editor.document.handle; diff --git a/src/vs/workbench/contrib/notebook/browser/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/cellRenderer.ts index 921fec8dde5..37f0aaa4c65 100644 --- a/src/vs/workbench/contrib/notebook/browser/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/cellRenderer.ts @@ -71,6 +71,8 @@ export class ViewCell { public id: string; constructor( + public viewType: string, + public notebookHandle: number, public cell: ICell, private _isEditing: boolean, private readonly modelService: IModelService, @@ -159,7 +161,8 @@ export class ViewCell { getTextModel(): ITextModel { if (!this._textModel) { let ext = this.cellType === 'markdown' ? 'md' : 'py'; - const resource = URI.parse(`notebookcell-${Date.now()}.${ext}`); + let resource = URI.parse(`notebookcell-${Date.now()}.${ext}`); + resource = resource.with({ authority: `${this.viewType}-${this.notebookHandle}-${this.cell.handle}` }); let content = this.cell.source.join('\n'); this._textModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource), resource, false); } @@ -406,7 +409,7 @@ class StatefullMarkdownCell extends Disposable { height: totalHeight }, () => { let newWidth = cellWidthResizeObserver.getWidth(); - let height = templateData.editor!.getLayoutInfo().height; + let height = this.editor!.getLayoutInfo().height; this.editor!.layout( { width: newWidth, diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 4c2ff9b816c..31c883da7d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -121,12 +121,7 @@ export class NotebookContribution implements IWorkbenchContribution { } if (viewType === undefined) { - if ( - !resource || - !endsWith(resource.path, '.ipynb') - ) { - return undefined; - } + return undefined; } if (this._resourceMapping.has(resource!.path)) { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 1b68c2d5c8d..8e80b370794 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -30,6 +30,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getZoomLevel } from 'vs/base/browser/browser'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INotebook } from 'vs/editor/common/modes'; const $ = DOM.$; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; @@ -47,6 +48,8 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler { private list: WorkbenchList | undefined; private model: NotebookEditorModel | undefined; + private notebook: INotebook | undefined; + private viewType: string | undefined; private viewCells: ViewCell[] = []; private localStore: DisposableStore = new DisposableStore(); private editorMemento: IEditorMemento; @@ -248,9 +251,11 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler { })); let viewState = this.loadTextEditorViewState(input); - this.viewCells = model.getNotebook().cells.map(cell => { + let notebook = model.getNotebook(); + this.viewType = input.viewType; + this.viewCells = notebook.cells.map(cell => { const isEditing = viewState && viewState.editingCells[cell.handle]; - return new ViewCell(cell, !!isEditing, this.modelService, this.modeService); + return new ViewCell(input.viewType!, notebook.handle, cell, !!isEditing, this.modelService, this.modeService); }); const updateScrollPosition = () => { @@ -335,7 +340,7 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler { } insertEmptyNotebookCell(listIndex: number | undefined, cell: ViewCell, type: 'code' | 'markdown', direction: 'above' | 'below') { - let newCell = new ViewCell({ + let newCell = new ViewCell(this.viewType!, this.notebook!.handle, { handle: -1, cell_type: type, source: [],