提交 715b1358 编写于 作者: R rebornix

Slim NotebookEditorInput and model service for notebookEditorModel.

上级 20873e2e
......@@ -112,6 +112,7 @@ suite('notebook workflow', () => {
// ---- ---- //
await vscode.commands.executeCommand('workbench.action.files.save');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
......@@ -161,6 +162,7 @@ suite('notebook workflow', () => {
await vscode.commands.executeCommand('notebook.cell.execute');
assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked
await vscode.commands.executeCommand('workbench.action.files.save');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
......@@ -183,6 +185,7 @@ suite('notebook workflow', () => {
await vscode.commands.executeCommand('notebook.execute');
assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked
await vscode.commands.executeCommand('workbench.action.files.save');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
});
......@@ -7,7 +7,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext } 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';
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService';
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
......
......@@ -16,7 +16,7 @@ import { BaseCellRenderTemplate, CellEditState, CellRunState, ICellViewModel, IN
import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
......
......@@ -26,7 +26,8 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchCo
import { EditorInput, Extensions as EditorInputExtensions, IEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { NotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { INotebookService, NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl';
import { CellKind, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
......@@ -247,11 +248,13 @@ class CellContentProvider implements ITextModelContentProvider {
if (!info) {
return null;
}
const notebook = await this._notebookService.resolveNotebook(info.id, data.notebook);
if (!notebook) {
const editorModel = await this._notebookService.modelManager.get(data.notebook);
if (!editorModel) {
return null;
}
for (let cell of notebook.cells) {
for (let cell of editorModel.notebook.cells) {
if (cell.uri.toString() === resource.toString()) {
const bufferFactory: ITextBufferFactory = {
create: (defaultEOL) => {
......
......@@ -17,13 +17,13 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { Range } from 'vs/editor/common/core/range';
import { FindMatch, IReadonlyTextBuffer } from 'vs/editor/common/model';
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, IOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey<boolean>('notebookFindWidgetFocused', false);
......
......@@ -30,8 +30,9 @@ import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { EditorOptions, IEditorCloseEvent, IEditorMemento } from 'vs/workbench/common/editor';
import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, INotebookEditorContribution, NOTEBOOK_EDITOR_RUNNABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEditorInput, NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
......
......@@ -3,94 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorInput, EditorModel, IEditorInput, GroupIdentifier, ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor';
import { Emitter, Event } from 'vs/base/common/event';
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { ICell, NotebookCellTextModelSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { EditorInput, IEditorInput, GroupIdentifier, ISaveOptions } from 'vs/workbench/common/editor';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { URI } from 'vs/base/common/uri';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { isEqual } from 'vs/base/common/resources';
import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
export class NotebookEditorModel extends EditorModel {
private _dirty = false;
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
private readonly _onDidChangeCells = new Emitter<NotebookCellTextModelSplice[]>();
get onDidChangeCells(): Event<NotebookCellTextModelSplice[]> { return this._onDidChangeCells.event; }
private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
get notebook() {
return this._notebook;
}
constructor(
private _notebook: NotebookTextModel
) {
super();
if (_notebook && _notebook.onDidChangeCells) {
this._register(_notebook.onDidChangeContent(() => {
this._dirty = true;
this._onDidChangeDirty.fire();
this._onDidChangeContent.fire();
}));
this._register(_notebook.onDidChangeCells((e) => {
this._onDidChangeCells.fire(e);
}));
}
}
isDirty() {
return this._dirty;
}
getNotebook(): NotebookTextModel {
return this._notebook;
}
insertCell(cell: ICell, index: number) {
let notebook = this.getNotebook();
if (notebook) {
this.notebook.insertNewCell(index, [cell as NotebookCellTextModel]);
this._dirty = true;
this._onDidChangeDirty.fire();
}
}
deleteCell(index: number) {
let notebook = this.getNotebook();
if (notebook) {
this.notebook.removeCell(index);
}
}
moveCellToIdx(index: number, newIdx: number) {
this.notebook.moveCellToIdx(index, newIdx);
}
async save(): Promise<boolean> {
if (this._notebook) {
this._dirty = false;
this._onDidChangeDirty.fire();
// todo, flush all states
return true;
}
return false;
}
}
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
export class NotebookEditorInput extends EditorInput {
......@@ -113,36 +32,16 @@ export class NotebookEditorInput extends EditorInput {
}
static readonly ID: string = 'workbench.input.notebook';
private promise: Promise<NotebookEditorModel> | null = null;
private textModel: NotebookEditorModel | null = null;
private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
constructor(
public resource: URI,
public name: string,
public readonly viewType: string | undefined,
@INotebookService private readonly notebookService: INotebookService,
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService
) {
super();
const input = this;
const workingCopyAdapter = new class implements IWorkingCopy {
readonly resource = input.resource.with({ scheme: 'vscode-notebook' });
get name() { return input.getName(); }
readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : 0;
readonly onDidChangeDirty = input.onDidChangeDirty;
readonly onDidChangeContent = input.onDidChangeContent;
isDirty(): boolean { return input.isDirty(); }
backup(): Promise<IWorkingCopyBackup> { return input.backup(); }
save(options?: ISaveOptions): Promise<boolean> { return input.save(0, options).then(editor => !!editor); }
revert(options?: IRevertOptions): Promise<void> { return input.revert(0, options); }
};
this._register(this.workingCopyService.registerWorkingCopy(workingCopyAdapter));
}
getTypeId(): string {
......@@ -179,7 +78,6 @@ export class NotebookEditorInput extends EditorInput {
async save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
if (this.textModel) {
await this.notebookService.save(this.textModel.notebook.viewType, this.textModel.notebook.uri);
await this.textModel.save();
return this;
}
......@@ -187,38 +85,13 @@ export class NotebookEditorInput extends EditorInput {
return undefined;
}
async revert(group: GroupIdentifier, options?: IRevertOptions): Promise<void> {
if (this.textModel) {
// TODO@rebornix we need hashing
await this.textModel.save();
}
}
async resolve(): Promise<NotebookEditorModel> {
if (this.textModel) {
return this.textModel;
}
if (!this.promise) {
if (!await this.notebookService.canResolve(this.viewType!)) {
throw new Error(`Cannot open notebook of type '${this.viewType}'`);
}
this.promise = this.notebookService.resolveNotebook(this.viewType!, this.resource).then(notebook => {
this.textModel = new NotebookEditorModel(notebook!);
this.textModel.onDidChangeDirty(() => this._onDidChangeDirty.fire());
this.textModel.onDidChangeContent(() => {
this._onDidChangeContent.fire();
});
return this.textModel;
});
if (!await this.notebookService.canResolve(this.viewType!)) {
throw new Error(`Cannot open notebook of type '${this.viewType}'`);
}
return this.promise;
}
async backup(): Promise<IWorkingCopyBackup> {
throw new Error();
this.textModel = await this.notebookService.modelManager.resolve(this.resource, this.viewType!);
return this.textModel;
}
matches(otherInput: unknown): boolean {
......@@ -235,6 +108,7 @@ export class NotebookEditorInput extends EditorInput {
dispose() {
if (this.textModel) {
this.notebookService.destoryNotebookDocument(this.textModel!.notebook.viewType, this.textModel!.notebook);
this.textModel.dispose();
}
super.dispose();
......
......@@ -5,7 +5,7 @@
import * as nls from 'vs/nls';
import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
......@@ -19,46 +19,13 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IEditorService, ICustomEditorViewTypesHandler, ICustomEditorInfo } from 'vs/workbench/services/editor/common/editorService';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookEditorModelManager } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService';
function MODEL_ID(resource: URI): string {
return resource.toString();
}
export const INotebookService = createDecorator<INotebookService>('notebookService');
export interface IMainNotebookController {
resolveNotebook(viewType: string, uri: URI): Promise<NotebookTextModel | undefined>;
executeNotebook(viewType: string, uri: URI, token: CancellationToken): Promise<void>;
onDidReceiveMessage(uri: URI, message: any): void;
executeNotebookCell(uri: URI, handle: number, token: CancellationToken): Promise<void>;
destoryNotebookDocument(notebook: INotebookTextModel): Promise<void>;
save(uri: URI): Promise<boolean>;
}
export interface INotebookService {
_serviceBrand: undefined;
canResolve(viewType: string): Promise<boolean>;
onDidChangeActiveEditor: Event<{ viewType: string, uri: URI }>;
registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void;
unregisterNotebookProvider(viewType: string): void;
registerNotebookRenderer(handle: number, extensionData: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: URI[]): void;
unregisterNotebookRenderer(handle: number): void;
getRendererInfo(handle: number): INotebookRendererInfo | undefined;
resolveNotebook(viewType: string, uri: URI): Promise<NotebookTextModel | undefined>;
executeNotebook(viewType: string, uri: URI): Promise<void>;
executeNotebookCell(viewType: string, uri: URI, handle: number, token: CancellationToken): Promise<void>;
getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[];
getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined;
getNotebookProviderResourceRoots(): URI[];
destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void;
updateActiveNotebookDocument(viewType: string, resource: URI): void;
save(viewType: string, resource: URI): Promise<boolean>;
onDidReceiveMessage(viewType: string, uri: URI, message: any): void;
setToCopy(items: NotebookCellTextModel[]): void;
getToCopy(): NotebookCellTextModel[] | undefined;
}
export class NotebookProviderInfoStore {
private readonly contributedEditors = new Map<string, NotebookProviderInfo>();
......@@ -126,8 +93,6 @@ class ModelData implements IDisposable {
this._modelEventListeners.dispose();
}
}
export class NotebookService extends Disposable implements INotebookService, ICustomEditorViewTypesHandler {
_serviceBrand: undefined;
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, extensionData: NotebookExtensionDescription }>();
......@@ -142,13 +107,18 @@ export class NotebookService extends Disposable implements INotebookService, ICu
onDidChangeViewTypes: Event<void> = this._onDidChangeViewTypes.event;
private cutItems: NotebookCellTextModel[] | undefined;
modelManager: NotebookEditorModelManager;
constructor(
@IExtensionService private readonly extensionService: IExtensionService,
@IEditorService private readonly editorService: IEditorService
@IEditorService private readonly editorService: IEditorService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
this._models = {};
this.modelManager = this.instantiationService.createInstance(NotebookEditorModelManager);
notebookProviderExtensionPoint.setHandler((extensions) => {
this.notebookProviderInfoStore.clear();
......
......@@ -9,7 +9,7 @@ import * as path from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import * as UUID from 'vs/base/common/uuid';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/resourceLoader';
......
......@@ -12,7 +12,7 @@ import * as nls from 'vs/nls';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { CellOutputKind, IOutput, IRenderOutput, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
......
......@@ -18,15 +18,15 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { WorkspaceTextEdit } from 'vs/editor/common/modes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
import { CellEditState, CellFindMatch, ICellRange, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { DeleteCellEdit, InsertCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEdit';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { CellFoldingState, EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges';
export interface INotebookEditorViewState {
......@@ -241,7 +241,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
constructor(
public viewType: string,
private _model: NotebookEditorModel,
private _model: INotebookEditorModel,
readonly eventDispatcher: NotebookEventDispatcher,
private _layoutInfo: NotebookLayoutInfo | null,
@IInstantiationService private readonly instantiationService: IInstantiationService,
......
......@@ -77,15 +77,15 @@ export class NotebookCellTextModel implements ICell {
}
getValue(): string {
const lineCount = this._textBuffer.getLineCount();
const fullRange = new Range(1, 1, lineCount, this._textBuffer.getLineLength(lineCount) + 1);
const lineCount = this.textBuffer.getLineCount();
const fullRange = new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1);
// todo@rebornix eol?
const eol = this._textBuffer.getEOL();
const eol = this.textBuffer.getEOL();
if (eol === '\n') {
return this._textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.LF);
return this.textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.LF);
} else {
return this._textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.CRLF);
return this.textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.CRLF);
}
}
......
......@@ -12,6 +12,8 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
export enum CellKind {
Markdown = 1,
......@@ -462,3 +464,15 @@ export interface ICellEditorViewState {
}
export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' | 'bottom' | 'both'>('notebookEditorCursorAtBoundary', 'none');
export interface INotebookEditorModel extends IEditorModel {
notebook: NotebookTextModel;
onDidChangeCells: Event<NotebookCellTextModelSplice[]>;
isDirty(): boolean;
getNotebook(): NotebookTextModel;
insertCell(cell: ICell, index: number): void;
deleteCell(index: number): void;
moveCellToIdx(index: number, newIdx: number): void;
save(): Promise<boolean>;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor';
import { Emitter, Event } from 'vs/base/common/event';
import { ICell, NotebookCellTextModelSplice, INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ResourceMap } from 'vs/base/common/map';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { URI } from 'vs/base/common/uri';
import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { basename } from 'vs/base/common/resources';
export interface INotebookEditorModelManager {
models: NotebookEditorModel[];
resolve(resource: URI, viewType: string): Promise<NotebookEditorModel>;
get(resource: URI): NotebookEditorModel | undefined;
}
export class NotebookEditorModel extends EditorModel implements IWorkingCopy, INotebookEditorModel {
private _dirty = false;
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
private readonly _onDidChangeCells = new Emitter<NotebookCellTextModelSplice[]>();
get onDidChangeCells(): Event<NotebookCellTextModelSplice[]> { return this._onDidChangeCells.event; }
private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
private _notebook!: NotebookTextModel;
get notebook() {
return this._notebook;
}
private _name!: string;
get name() {
return this._name;
}
constructor(
public readonly resource: URI,
public readonly viewType: string,
@INotebookService private readonly notebookService: INotebookService,
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService
) {
super();
this._register(this.workingCopyService.registerWorkingCopy(this));
}
capabilities: WorkingCopyCapabilities = WorkingCopyCapabilities.Untitled;
async backup(): Promise<IWorkingCopyBackup> {
return {};
}
async revert(options?: IRevertOptions | undefined): Promise<void> {
return;
}
async load(): Promise<NotebookEditorModel> {
const notebook = await this.notebookService.resolveNotebook(this.viewType!, this.resource);
this._notebook = notebook!;
this._name = basename(this._notebook!.uri);
if (this._notebook.onDidChangeCells) {
this._register(this._notebook.onDidChangeContent(() => {
this._dirty = true;
this._onDidChangeDirty.fire();
this._onDidChangeContent.fire();
}));
this._register(this._notebook.onDidChangeCells((e) => {
this._onDidChangeCells.fire(e);
}));
}
return this;
}
isDirty() {
return this._dirty;
}
getNotebook(): NotebookTextModel {
return this._notebook;
}
insertCell(cell: ICell, index: number) {
let notebook = this.getNotebook();
if (notebook) {
this.notebook.insertNewCell(index, [cell as NotebookCellTextModel]);
this._dirty = true;
this._onDidChangeDirty.fire();
}
}
deleteCell(index: number) {
let notebook = this.getNotebook();
if (notebook) {
this.notebook.removeCell(index);
}
}
moveCellToIdx(index: number, newIdx: number) {
this.notebook.moveCellToIdx(index, newIdx);
}
async save(): Promise<boolean> {
await this.notebookService.save(this.notebook.viewType, this.notebook.uri);
this._dirty = false;
this._onDidChangeDirty.fire();
return true;
}
}
export class NotebookEditorModelManager extends Disposable implements INotebookEditorModelManager {
private readonly mapResourceToModel = new ResourceMap<NotebookEditorModel>();
private readonly mapResourceToModelListeners = new ResourceMap<IDisposable>();
private readonly mapResourceToDisposeListener = new ResourceMap<IDisposable>();
private readonly mapResourceToPendingModelLoaders = new ResourceMap<Promise<NotebookEditorModel>>();
// private readonly modelLoadQueue = this._register(new ResourceQueue());
get models(): NotebookEditorModel[] {
return this.mapResourceToModel.values();
}
constructor(
@IInstantiationService readonly instantiationService: IInstantiationService
) {
super();
}
async resolve(resource: URI, viewType: string): Promise<NotebookEditorModel> {
// Return early if model is currently being loaded
const pendingLoad = this.mapResourceToPendingModelLoaders.get(resource);
if (pendingLoad) {
return pendingLoad;
}
let modelPromise: Promise<NotebookEditorModel>;
let model = this.get(resource);
// let didCreateModel = false;
// Model exists
if (model) {
// if (options?.reload) {
// } else {
modelPromise = Promise.resolve(model);
// }
}
// Model does not exist
else {
// didCreateModel = true;
const newModel = model = this.instantiationService.createInstance(NotebookEditorModel, resource, viewType);
modelPromise = model.load();
this.registerModel(newModel);
}
// Store pending loads to avoid race conditions
this.mapResourceToPendingModelLoaders.set(resource, modelPromise);
// Make known to manager (if not already known)
this.add(resource, model);
// dispose and bind new listeners
try {
const resolvedModel = await modelPromise;
// Remove from pending loads
this.mapResourceToPendingModelLoaders.delete(resource);
return resolvedModel;
} catch (error) {
// Free resources of this invalid model
if (model) {
model.dispose();
}
// Remove from pending loads
this.mapResourceToPendingModelLoaders.delete(resource);
throw error;
}
}
add(resource: URI, model: NotebookEditorModel): void {
const knownModel = this.mapResourceToModel.get(resource);
if (knownModel === model) {
return; // already cached
}
// dispose any previously stored dispose listener for this resource
const disposeListener = this.mapResourceToDisposeListener.get(resource);
if (disposeListener) {
disposeListener.dispose();
}
// store in cache but remove when model gets disposed
this.mapResourceToModel.set(resource, model);
this.mapResourceToDisposeListener.set(resource, model.onDispose(() => this.remove(resource)));
}
remove(resource: URI): void {
this.mapResourceToModel.delete(resource);
const disposeListener = this.mapResourceToDisposeListener.get(resource);
if (disposeListener) {
dispose(disposeListener);
this.mapResourceToDisposeListener.delete(resource);
}
const modelListener = this.mapResourceToModelListeners.get(resource);
if (modelListener) {
dispose(modelListener);
this.mapResourceToModelListeners.delete(resource);
}
}
private registerModel(model: NotebookEditorModel): void {
}
get(resource: URI): NotebookEditorModel | undefined {
return this.mapResourceToModel.get(resource);
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Event } from 'vs/base/common/event';
import { INotebookTextModel, INotebookMimeTypeSelector, INotebookRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CancellationToken } from 'vs/base/common/cancellation';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { INotebookEditorModelManager } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
export const INotebookService = createDecorator<INotebookService>('notebookService');
export interface IMainNotebookController {
resolveNotebook(viewType: string, uri: URI): Promise<NotebookTextModel | undefined>;
executeNotebook(viewType: string, uri: URI, token: CancellationToken): Promise<void>;
onDidReceiveMessage(uri: URI, message: any): void;
executeNotebookCell(uri: URI, handle: number, token: CancellationToken): Promise<void>;
destoryNotebookDocument(notebook: INotebookTextModel): Promise<void>;
save(uri: URI): Promise<boolean>;
}
export interface INotebookService {
_serviceBrand: undefined;
modelManager: INotebookEditorModelManager;
canResolve(viewType: string): Promise<boolean>;
onDidChangeActiveEditor: Event<{ viewType: string, uri: URI }>;
registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void;
unregisterNotebookProvider(viewType: string): void;
registerNotebookRenderer(handle: number, extensionData: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: URI[]): void;
unregisterNotebookRenderer(handle: number): void;
getRendererInfo(handle: number): INotebookRendererInfo | undefined;
resolveNotebook(viewType: string, uri: URI): Promise<NotebookTextModel | undefined>;
executeNotebook(viewType: string, uri: URI): Promise<void>;
executeNotebookCell(viewType: string, uri: URI, handle: number, token: CancellationToken): Promise<void>;
getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[];
getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined;
getNotebookProviderResourceRoots(): URI[];
destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void;
updateActiveNotebookDocument(viewType: string, resource: URI): void;
save(viewType: string, resource: URI): Promise<boolean>;
onDidReceiveMessage(viewType: string, uri: URI, message: any): void;
setToCopy(items: NotebookCellTextModel[]): void;
getToCopy(): NotebookCellTextModel[] | undefined;
}
......@@ -6,10 +6,9 @@
import * as assert from 'assert';
import { URI } from 'vs/base/common/uri';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind, NotebookCellMetadata, diff } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { withTestNotebook, TestCell } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
import { withTestNotebook, TestCell, NotebookEditorTestModel } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
......@@ -25,7 +24,7 @@ suite('NotebookViewModel', () => {
test('ctor', function () {
const notebook = new NotebookTextModel(0, 'notebook', URI.parse('test'));
const model = new NotebookEditorModel(notebook);
const model = new NotebookEditorTestModel(notebook);
const eventDispatcher = new NotebookEventDispatcher();
const viewModel = new NotebookViewModel('notebook', model, eventDispatcher, null, instantiationService, blukEditService, undoRedoService);
assert.equal(viewModel.viewType, 'notebook');
......
......@@ -4,10 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { CellKind, IOutput, CellUri, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, IOutput, CellUri, NotebookCellMetadata, NotebookCellTextModelSplice, ICell, INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookViewModel, IModelDecorationsChangeAccessor, CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { INotebookEditor, NotebookLayoutInfo, ICellViewModel, ICellRange, INotebookEditorMouseEvent, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
......@@ -21,7 +20,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { EditorModel } from 'vs/workbench/common/editor';
export class TestCell extends NotebookCellTextModel {
constructor(
public viewType: string,
......@@ -206,6 +205,83 @@ export class TestNotebookEditor implements INotebookEditor {
// return createCellViewModel(instantiationService, viewType, notebookHandle, mockCell);
// }
export class NotebookEditorTestModel extends EditorModel implements INotebookEditorModel {
private _dirty = false;
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
private readonly _onDidChangeCells = new Emitter<NotebookCellTextModelSplice[]>();
get onDidChangeCells(): Event<NotebookCellTextModelSplice[]> { return this._onDidChangeCells.event; }
private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
get notebook() {
return this._notebook;
}
constructor(
private _notebook: NotebookTextModel
) {
super();
if (_notebook && _notebook.onDidChangeCells) {
this._register(_notebook.onDidChangeContent(() => {
this._dirty = true;
this._onDidChangeDirty.fire();
this._onDidChangeContent.fire();
}));
this._register(_notebook.onDidChangeCells((e) => {
this._onDidChangeCells.fire(e);
}));
}
}
isDirty() {
return this._dirty;
}
getNotebook(): NotebookTextModel {
return this._notebook;
}
insertCell(cell: ICell, index: number) {
let notebook = this.getNotebook();
if (notebook) {
this.notebook.insertNewCell(index, [cell as NotebookCellTextModel]);
this._dirty = true;
this._onDidChangeDirty.fire();
}
}
deleteCell(index: number) {
let notebook = this.getNotebook();
if (notebook) {
this.notebook.removeCell(index);
}
}
moveCellToIdx(index: number, newIdx: number) {
this.notebook.moveCellToIdx(index, newIdx);
}
async save(): Promise<boolean> {
if (this._notebook) {
this._dirty = false;
this._onDidChangeDirty.fire();
// todo, flush all states
return true;
}
return false;
}
}
export function withTestNotebook(instantiationService: IInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) {
const viewType = 'notebook';
const editor = new TestNotebookEditor();
......@@ -213,7 +289,7 @@ export function withTestNotebook(instantiationService: IInstantiationService, bl
notebook.cells = cells.map((cell, index) => {
return new NotebookCellTextModel(notebook.uri, index, cell[0], cell[1], cell[2], cell[3], cell[4]);
});
const model = new NotebookEditorModel(notebook);
const model = new NotebookEditorTestModel(notebook);
const eventDispatcher = new NotebookEventDispatcher();
const viewModel = new NotebookViewModel(viewType, model, eventDispatcher, null, instantiationService, blukEditService, undoRedoService);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册