提交 9d5a45a6 编写于 作者: J Johannes Rieken

nuke NotebookEditorModelManager, add ref-counting notebook resolver service,...

nuke NotebookEditorModelManager, add ref-counting notebook resolver service, don't cache or re-create notebook editor inputs, cache notebook widgets by "tab id" (uri and group), extract move spy'ing into separate open override handler
上级 ac4e67b5
......@@ -6,9 +6,8 @@
import { coalesce, distinct } from 'vs/base/common/arrays';
import { Schemas } from 'vs/base/common/network';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { ResourceMap } from 'vs/base/common/map';
import { parse } from 'vs/base/common/marshalling';
import { basename, isEqual } from 'vs/base/common/resources';
import { isEqual } from 'vs/base/common/resources';
import { assertType } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { ITextModel, ITextBufferFactory, DefaultEndOfLine, ITextBuffer } from 'vs/editor/common/model';
......@@ -17,7 +16,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
import * as nls from 'vs/nls';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorOptions, ITextEditorOptions, IResourceEditorInput } from 'vs/platform/editor/common/editor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
......@@ -83,7 +82,6 @@ class NotebookEditorFactory implements IEditorInputFactory {
resource: input.resource,
name: input.name,
viewType: input.viewType,
group: input.group
});
}
deserialize(instantiationService: IInstantiationService, raw: string) {
......@@ -97,13 +95,7 @@ class NotebookEditorFactory implements IEditorInputFactory {
return undefined;
}
// if we have two editors open with the same resource (in different editor groups), we should then create two different
// editor inputs, instead of `getOrCreate`.
const input = NotebookEditorInput.create(instantiationService, resource, name, viewType);
if (typeof data.group === 'number') {
input.updateGroup(data.group);
}
return input;
}
......@@ -147,7 +139,6 @@ function getFirstNotebookInfo(notebookService: INotebookService, uri: URI): Note
}
export class NotebookContribution extends Disposable implements IWorkbenchContribution {
private _resourceMapping = new ResourceMap<NotebookEditorInput>();
constructor(
@IEditorService private readonly editorService: IEditorService,
......@@ -193,7 +184,27 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
};
});
},
open: (editor, options, group, context) => this.onEditorOpening(editor, options, group, context)
open: (editor, options, group) => {
return this.onEditorOpening2(editor, options, group);
}
}));
// HACK
// we use the open override to spy on tab movements because that's the only
// way to do that...
this._register(this.editorService.overrideOpenEditor({
open: (input, _options, group, context) => {
if (input instanceof NotebookEditorInput && context === OpenEditorContext.MOVE_EDITOR) {
// when moving a notebook editor we release it from its current tab and we
// "place" it into its future slot so that the editor can pick it up from there
const widgetRef = NotebookRegistry.getNotebookEditorWidget(input.resource, this.editorGroupsService.activeGroup);
if (widgetRef) {
NotebookRegistry.releaseNotebookEditorWidget(input.resource, this.editorGroupsService.activeGroup);
NotebookRegistry.claimNotebookEditorWidget(input.resource, group, widgetRef);
}
}
return undefined;
}
}));
this._register(this.editorService.onDidVisibleEditorsChange(() => {
......@@ -215,22 +226,6 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
this.notebookService.updateActiveNotebookEditor(null);
}
}));
this._register(this.editorService.onDidCloseEditor(({ editor }) => {
if (!(editor instanceof NotebookEditorInput)) {
return;
}
if (!this.editorService.editors.some(other => (
other.resource === editor.resource
&& other instanceof NotebookEditorInput
&& other.viewType === editor.viewType
))) {
editor.clearTextModel();
}
editor.dispose();
}));
}
getUserAssociatedEditors(resource: URI) {
......@@ -252,54 +247,38 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
return this.notebookService.getContributedNotebookProviders(resource);
}
private onEditorOpening(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup, context: OpenEditorContext): IOpenEditorOverride | undefined {
private onEditorOpening2(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined {
const id = typeof options?.override === 'string' ? options.override : undefined;
if (id === undefined && originalInput.isUntitled()) {
return;
return undefined;
}
if (originalInput instanceof NotebookEditorInput) {
if ((originalInput.group === group.id || originalInput.group === undefined) && (originalInput.viewType === id || typeof id !== 'string')) {
// No need to do anything
originalInput.updateGroup(group.id);
return {
override: this.editorService.openEditor(originalInput, new NotebookEditorOptions(options || {}).with({ override: false }), group)
};
} else {
// Create a copy of the input.
// Unlike normal editor inputs, we do not want to share custom editor inputs
// between multiple editors / groups.
const copiedInput = NotebookEditorInput.create(this.instantiationService, originalInput.resource, originalInput.name, originalInput.viewType);
copiedInput.updateGroup(group.id);
if (context === OpenEditorContext.MOVE_EDITOR) {
// transfer ownership of editor widget
const widgetRef = NotebookRegistry.getNotebookEditorWidget(originalInput.resource, this.editorGroupsService.activeGroup);
if (widgetRef) {
NotebookRegistry.releaseNotebookEditorWidget(originalInput.resource, this.editorGroupsService.activeGroup);
NotebookRegistry.claimNotebookEditorWidget(originalInput.resource, group, widgetRef);
}
}
return {
override: this.editorService.openEditor(copiedInput, new NotebookEditorOptions(options || {}).with({ override: false }), group)
};
}
if (!originalInput.resource) {
return undefined;
}
let resource = originalInput.resource;
if (!resource) {
if (originalInput instanceof NotebookEditorInput) {
return undefined;
}
let notebookUri: URI = originalInput.resource;
let cellOptions: IResourceEditorInput | undefined;
const data = CellUri.parse(originalInput.resource);
if (data) {
notebookUri = data.notebook;
cellOptions = { resource: originalInput.resource, options };
}
if (id === undefined) {
const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, resource) && !(editor instanceof NotebookEditorInput));
const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, notebookUri) && !(editor instanceof NotebookEditorInput));
if (existingEditors.length) {
return undefined;
}
const userAssociatedEditors = this.getUserAssociatedEditors(resource);
const userAssociatedEditors = this.getUserAssociatedEditors(notebookUri);
const notebookEditor = userAssociatedEditors.filter(association => this.notebookService.getContributedNotebookProvider(association.viewType));
if (userAssociatedEditors.length && !notebookEditor.length) {
......@@ -310,46 +289,18 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
// user might pick a notebook editor
const associatedEditors = distinct([
...this.getUserAssociatedNotebookEditors(resource),
...this.getContributedEditors(resource)
...this.getUserAssociatedNotebookEditors(notebookUri),
...this.getContributedEditors(notebookUri)
], editor => editor.id).filter(editor => editor.priority === NotebookEditorPriority.default);
if (!associatedEditors.length) {
// there is no notebook editor contribution which is enabled by default
return;
}
} else {
const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, resource) && (editor instanceof NotebookEditorInput) && editor.viewType === id);
if (existingEditors.length) {
// switch to this cell
return { override: this.editorService.openEditor(existingEditors[0], new NotebookEditorOptions(options || {}).with({ override: false }), group) };
}
}
let info: NotebookProviderInfo | undefined;
const data = CellUri.parse(resource);
if (data) {
const infos = this.getContributedEditors(data.notebook);
if (infos.length) {
const info = id === undefined ? infos[0] : (infos.find(info => info.id === id) || infos[0]);
// cell-uri -> open (container) notebook
const name = basename(data.notebook);
let input = this._resourceMapping.get(data.notebook);
if (!input || input.isDisposed()) {
input = NotebookEditorInput.create(this.instantiationService, data.notebook, name, info.id);
this._resourceMapping.set(data.notebook, input);
}
input.updateGroup(group.id);
return { override: this.editorService.openEditor(input, new NotebookEditorOptions({ ...options, forceReload: true, cellOptions: { resource, options } }), group) };
return undefined;
}
}
const infos = this.notebookService.getContributedNotebookProviders(resource);
info = id === undefined ? infos[0] : infos.find(info => info.id === id);
const infos = this.notebookService.getContributedNotebookProviders(notebookUri);
let info = infos.find(info => !id || info.id === id);
if (!info && id !== undefined) {
info = this.notebookService.getContributedNotebookProvider(id);
......@@ -359,20 +310,19 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
return undefined;
}
const input = NotebookEditorInput.create(this.instantiationService, resource, originalInput.getName(), info.id);
input.updateGroup(group.id);
this._resourceMapping.set(resource, input);
/**
* Scenario: we are reopening a file editor input which is pinned, we should open in a new editor tab.
*/
let index = undefined;
if (group.activeEditor === originalInput && isEqual(originalInput.resource, resource)) {
if (group.activeEditor === originalInput && isEqual(originalInput.resource, notebookUri)) {
const originalEditorIndex = group.getIndexOfEditor(originalInput);
index = group.isPinned(originalInput) ? originalEditorIndex + 1 : originalEditorIndex;
}
return { override: this.editorService.openEditor(input, new NotebookEditorOptions(options || {}).with({ override: false, index }), group) };
const notebookInput = NotebookEditorInput.create(this.instantiationService, notebookUri, originalInput.getName(), info.id);
const notebookOptions = new NotebookEditorOptions({ ...options, cellOptions, override: false, index });
return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) };
}
}
......@@ -385,6 +335,7 @@ class CellContentProvider implements ITextModelContentProvider {
@IModelService private readonly _modelService: IModelService,
@IModeService private readonly _modeService: IModeService,
@INotebookService private readonly _notebookService: INotebookService,
@INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService,
) {
this._registration = textModelService.registerTextModelContentProvider(CellUri.scheme, this);
}
......@@ -408,12 +359,10 @@ class CellContentProvider implements ITextModelContentProvider {
return null;
}
const editorModel = await this._notebookService.modelManager.resolve(data.notebook, info.id);
if (!editorModel) {
return null;
}
const ref = await this._notebookModelResolverService.resolve(data.notebook, info.id);
let result: ITextModel | null = null;
for (let cell of editorModel.notebook.cells) {
for (let cell of ref.object.notebook.cells) {
if (cell.uri.toString() === resource.toString()) {
const bufferFactory: ITextBufferFactory = {
create: (defaultEOL) => {
......@@ -426,15 +375,23 @@ class CellContentProvider implements ITextModelContentProvider {
}
};
const language = cell.cellKind === CellKind.Markdown ? this._modeService.create('markdown') : (cell.language ? this._modeService.create(cell.language) : this._modeService.createByFilepathOrFirstLine(resource, cell.textBuffer.getLineContent(1)));
return this._modelService.createModel(
result = this._modelService.createModel(
bufferFactory,
language,
resource
);
break;
}
}
return null;
if (result) {
const once = result.onWillDispose(() => {
once.dispose();
ref.dispose();
});
}
return result;
}
}
......
......@@ -43,10 +43,9 @@ export class NotebookEditor extends BaseEditor {
@IStorageService storageService: IStorageService,
@IEditorService private readonly editorService: IEditorService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@INotificationService private readonly notificationService: INotificationService) {
@INotificationService private readonly notificationService: INotificationService
) {
super(NotebookEditor.ID, telemetryService, themeService, storageService);
// this._widget = this.instantiationService.createInstance(NotebookEditorWidget);
this.editorMemento = this.getEditorMemento<INotebookEditorViewState>(editorGroupService, NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY);
}
......@@ -144,17 +143,16 @@ export class NotebookEditor extends BaseEditor {
await super.setInput(input, options, token);
// input attached
Event.once(input.onDispose)(() => {
// make sure the editor widget is removed from the view
const existingEditorWidgetForInput = NotebookRegistry.getNotebookEditorWidget(input.resource, group);
if (existingEditorWidgetForInput) {
// the editor widget is only referenced by the editor input
// clear its state
existingEditorWidgetForInput.onWillHide();
existingEditorWidgetForInput.getDomNode().remove();
existingEditorWidgetForInput.dispose();
NotebookRegistry.releaseNotebookEditorWidget(input.resource, group);
// todo@jrieken
// there is no more input for a resource and that is used as signal to clean up
// all widget that might still be around
for (let widget of NotebookRegistry.releaseAllNotebookEditorWidgets(input.resource)) {
widget.onWillHide();
widget.getDomNode().remove();
widget.dispose();
}
});
......
......@@ -9,10 +9,10 @@ import { URI } from 'vs/base/common/uri';
import { isEqual, basename } from 'vs/base/common/resources';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
let NOTEBOOK_EDITOR_INPUT_HANDLE = 0;
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
import { IReference } from 'vs/base/common/lifecycle';
import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
interface NotebookEditorInputOptions {
startDirty?: boolean;
......@@ -24,30 +24,19 @@ export class NotebookEditorInput extends EditorInput {
}
static readonly ID: string = 'workbench.input.notebook';
private textModel: NotebookEditorModel | null = null;
private _group: GroupIdentifier | undefined;
public get group(): GroupIdentifier | undefined {
return this._group;
}
public updateGroup(group: GroupIdentifier): void {
this._group = group;
}
private _textModel: IReference<INotebookEditorModel> | null = null;
private _defaultDirtyState: boolean = false;
readonly id: number = NOTEBOOK_EDITOR_INPUT_HANDLE++;
constructor(
public resource: URI,
public name: string,
public readonly resource: URI,
public readonly name: string,
public readonly viewType: string | undefined,
public readonly options: NotebookEditorInputOptions,
@INotebookService private readonly notebookService: INotebookService,
@INotebookEditorModelResolverService private readonly notebookModelResolverService: INotebookEditorModelResolverService,
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService,
@IFileDialogService private readonly fileDialogService: IFileDialogService,
// @IEditorService private readonly editorService: IEditorService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
......@@ -63,18 +52,17 @@ export class NotebookEditorInput extends EditorInput {
}
isDirty() {
if (!this.textModel) {
if (!this._textModel) {
return !!this._defaultDirtyState;
}
return this.textModel?.isDirty() || false;
return this._textModel.object.isDirty();
}
isReadonly() {
return false;
}
public isSaving(): boolean {
isSaving(): boolean {
if (this.isUntitled()) {
return false; // untitled is never saving automatically
}
......@@ -91,8 +79,8 @@ export class NotebookEditorInput extends EditorInput {
}
async save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
if (this.textModel) {
await this.textModel.save();
if (this._textModel) {
await this._textModel.object.save();
return this;
}
......@@ -100,17 +88,17 @@ export class NotebookEditorInput extends EditorInput {
}
async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
if (!this.textModel) {
if (!this._textModel) {
return undefined;
}
const dialogPath = this.textModel.resource;
const dialogPath = this._textModel.object.resource;
const target = await this.fileDialogService.pickFileToSave(dialogPath, options?.availableFileSystems);
if (!target) {
return undefined; // save cancelled
}
if (!await this.textModel.saveAs(target)) {
if (!await this._textModel.object.saveAs(target)) {
return undefined;
}
......@@ -119,45 +107,47 @@ export class NotebookEditorInput extends EditorInput {
// called when users rename a notebook document
rename(group: GroupIdentifier, target: URI): IMoveResult | undefined {
if (this.textModel) {
if (this._textModel) {
const contributedNotebookProviders = this.notebookService.getContributedNotebookProviders(target);
if (contributedNotebookProviders.find(provider => provider.id === this.textModel!.viewType)) {
if (contributedNotebookProviders.find(provider => provider.id === this._textModel!.object.viewType)) {
return this._move(group, target);
}
}
return undefined;
}
_move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined {
private _move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined {
const editorInput = NotebookEditorInput.create(this.instantiationService, newResource, basename(newResource), this.viewType);
return { editor: editorInput };
}
async revert(group: GroupIdentifier, options?: IRevertOptions): Promise<void> {
if (this.textModel) {
await this.textModel.revert(options);
if (this._textModel) {
await this._textModel.object.revert(options);
}
return;
}
async resolve(editorId?: string): Promise<NotebookEditorModel | null> {
async resolve(editorId?: string): Promise<INotebookEditorModel | null> {
if (!await this.notebookService.canResolve(this.viewType!)) {
return null;
}
this.textModel = await this.notebookService.modelManager.resolve(this.resource, this.viewType!, editorId);
if (!this._textModel) {
this._textModel = await this.notebookModelResolverService.resolve(this.resource, this.viewType!, editorId);
this._register(this.textModel.onDidChangeDirty(() => {
this._onDidChangeDirty.fire();
}));
this._register(this._textModel.object.onDidChangeDirty(() => {
this._onDidChangeDirty.fire();
}));
if (this.textModel.isDirty()) {
this._onDidChangeDirty.fire();
if (this._textModel.object.isDirty()) {
this._onDidChangeDirty.fire();
}
}
return this.textModel;
return this._textModel.object;
}
matches(otherInput: unknown): boolean {
......@@ -171,14 +161,11 @@ export class NotebookEditorInput extends EditorInput {
return false;
}
clearTextModel() {
if (this.textModel) {
this.notebookService.destoryNotebookDocument(this.textModel!.notebook.viewType, this.textModel!.notebook);
this.textModel.dispose();
}
}
dispose() {
if (this._textModel) {
this._textModel.dispose();
this._textModel = null;
}
super.dispose();
}
}
......@@ -9,6 +9,7 @@ import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/cont
import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget';
import { URI } from 'vs/base/common/uri';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ResourceMap } from 'vs/base/common/map';
export type IOutputTransformCtor = IConstructorSignature1<INotebookEditor, IOutputTransformContribution>;
......@@ -18,14 +19,11 @@ export interface IOutputTransformDescription {
ctor: IOutputTransformCtor;
}
function EditorTabId(uri: URI, group: IEditorGroup) {
return `${uri.toString()}@${group.id}`;
}
export const NotebookRegistry = new class NotebookRegistryImpl {
readonly outputTransforms: IOutputTransformDescription[] = [];
readonly notebookEditorWidgetOwnership = new Map<string, NotebookEditorWidget>();
readonly notebookEditorWidgetOwnership = new ResourceMap<Map<number, NotebookEditorWidget>>();
registerOutputTransform<Services extends BrandedService[]>(id: string, kind: CellOutputKind, ctor: { new(editor: INotebookEditor, ...services: Services): IOutputTransformContribution }): void {
this.outputTransforms.push({ id: id, kind: kind, ctor: ctor as IOutputTransformCtor });
......@@ -36,14 +34,32 @@ export const NotebookRegistry = new class NotebookRegistryImpl {
}
claimNotebookEditorWidget(notebook: URI, group: IEditorGroup, widget: NotebookEditorWidget) {
this.notebookEditorWidgetOwnership.set(EditorTabId(notebook, group), widget);
let map = this.notebookEditorWidgetOwnership.get(notebook);
if (!map) {
map = new Map();
this.notebookEditorWidgetOwnership.set(notebook, map);
}
map.set(group.id, widget);
}
releaseNotebookEditorWidget(notebook: URI, group: IEditorGroup) {
this.notebookEditorWidgetOwnership.delete(EditorTabId(notebook, group));
const map = this.notebookEditorWidgetOwnership.get(notebook);
if (!map) {
return;
}
map.delete(group.id);
if (map.size === 0) {
this.notebookEditorWidgetOwnership.delete(notebook);
}
}
getNotebookEditorWidget(notebook: URI, group: IEditorGroup) {
return this.notebookEditorWidgetOwnership.get(EditorTabId(notebook, group));
return this.notebookEditorWidgetOwnership.get(notebook)?.get(group.id);
}
releaseAllNotebookEditorWidgets(notebook: URI) {
let values = [...this.notebookEditorWidgetOwnership.get(notebook)?.values() ?? []];
this.notebookEditorWidgetOwnership.delete(notebook);
return values;
}
};
......@@ -5,7 +5,6 @@
import * as nls from 'vs/nls';
import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { URI, UriComponents } from 'vs/base/common/uri';
import { notebookProviderExtensionPoint, notebookRendererExtensionPoint, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { NotebookProviderInfo, NotebookEditorDescriptor } from 'vs/workbench/contrib/notebook/common/notebookProvider';
......@@ -19,7 +18,6 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no
import { CancellationToken } 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';
import * as glob from 'vs/base/common/glob';
import { basename } from 'vs/base/common/path';
......@@ -179,7 +177,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu
onDidChangeKernels: Event<void> = this._onDidChangeKernels.event;
private cutItems: NotebookCellTextModel[] | undefined;
modelManager: NotebookEditorModelManager;
private _displayOrder: { userOrder: string[], defaultOrder: string[] } = Object.create(null);
constructor(
......@@ -187,13 +184,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStorageService private readonly storageService: IStorageService
) {
super();
this.modelManager = this.instantiationService.createInstance(NotebookEditorModelManager);
this._register(this.modelManager);
this.notebookProviderInfoStore = new NotebookProviderInfoStore(this.storageService);
this._register(this.notebookProviderInfoStore);
......
......@@ -7,9 +7,6 @@ import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor';
import { Emitter, Event } from 'vs/base/common/event';
import { INotebookEditorModel, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
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, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService';
......@@ -227,119 +224,3 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
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, editorId?: 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({ editorId });
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);
}
}
......@@ -12,7 +12,6 @@ import { INotebookTextModel, INotebookRendererInfo, NotebookDocumentMetadata, IC
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');
......@@ -32,7 +31,6 @@ export interface IMainNotebookController {
export interface INotebookService {
readonly _serviceBrand: undefined;
modelManager: INotebookEditorModelManager;
canResolve(viewType: string): Promise<boolean>;
onDidChangeActiveEditor: Event<string | null>;
onDidChangeVisibleEditors: Event<string[]>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册