diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 6677f0fcac7eb96975b1f0f5c16a9535912c5dcd..9b34262daf183ddf30d89b83758e085210c4e1ed 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -62,8 +62,8 @@ export class SimpleModel implements IResolvedTextEditorModel { return this._onWillDispose.event; } - public load(): Promise { - return Promise.resolve(this); + public resolve(): Promise { + return Promise.resolve(); } public get textEditorModel(): ITextModel { diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index fb5935da1a0816d1df69787c6a1131ccf73d32bb..800f63ceb3c3efd4d4eb34513acbe9a592196337 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -14,9 +14,14 @@ export interface IEditorModel { readonly onWillDispose: Event; /** - * Loads the model. + * Resolves the model. */ - load(): Promise; + resolve(): Promise; + + /** + * Find out if the editor model was resolved or not. + */ + isResolved(): boolean; /** * Find out if this model has been disposed. diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 4fd471708bec8564e91c396ae638b2949317d6a4..186b8b9fa89b14e415cb9c4b0125f68bfbfbdb7d 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -868,8 +868,8 @@ export interface ITextEditorModel extends IEditorModel { /** * The editor model is the heavyweight counterpart of editor input. Depending on the editor input, it - * connects to the disk to retrieve content and may allow for saving it back or reverting it. Editor models - * are typically cached for some while because they are expensive to construct. + * resolves from a file system retrieve content and may allow for saving it back or reverting it. + * Editor models are typically cached for some while because they are expensive to construct. */ export class EditorModel extends Disposable implements IEditorModel { @@ -877,19 +877,20 @@ export class EditorModel extends Disposable implements IEditorModel { readonly onWillDispose = this._onWillDispose.event; private disposed = false; + private resolved = false; /** - * Causes this model to load returning a promise when loading is completed. + * Causes this model to resolve returning a promise when loading is completed. */ - async load(): Promise { - return this; + async resolve(): Promise { + this.resolved = true; } /** * Returns whether this model was loaded or not. */ isResolved(): boolean { - return true; + return this.resolved; } /** diff --git a/src/vs/workbench/common/editor/binaryEditorModel.ts b/src/vs/workbench/common/editor/binaryEditorModel.ts index edf4d54b15e3f9054e1fe4c32a8aa9fea1ea43dd..a426062a9aeda5e89de363db58a664b56f3fc864 100644 --- a/src/vs/workbench/common/editor/binaryEditorModel.ts +++ b/src/vs/workbench/common/editor/binaryEditorModel.ts @@ -56,7 +56,7 @@ export class BinaryEditorModel extends EditorModel { return this.etag; } - async load(): Promise { + async resolve(): Promise { // Make sure to resolve up to date stat for file resources if (this.fileService.canHandleResource(this.resource)) { @@ -66,7 +66,5 @@ export class BinaryEditorModel extends EditorModel { this.size = stat.size; } } - - return this; } } diff --git a/src/vs/workbench/common/editor/diffEditorModel.ts b/src/vs/workbench/common/editor/diffEditorModel.ts index 5550bf997c7b659d36a7a20275b9ee7fc72e6517..ff95f51a8c964115e6fb7404fe47cd5a652e6546 100644 --- a/src/vs/workbench/common/editor/diffEditorModel.ts +++ b/src/vs/workbench/common/editor/diffEditorModel.ts @@ -25,17 +25,15 @@ export class DiffEditorModel extends EditorModel { this._modifiedModel = modifiedModel; } - async load(): Promise { + async resolve(): Promise { await Promise.all([ - this._originalModel?.load(), - this._modifiedModel?.load() + this._originalModel?.resolve(), + this._modifiedModel?.resolve() ]); - - return this; } isResolved(): boolean { - return this.originalModel instanceof EditorModel && this.originalModel.isResolved() && this.modifiedModel instanceof EditorModel && this.modifiedModel.isResolved(); + return !!(this.originalModel?.isResolved() && this.modifiedModel?.isResolved()); } dispose(): void { diff --git a/src/vs/workbench/common/editor/textDiffEditorModel.ts b/src/vs/workbench/common/editor/textDiffEditorModel.ts index 98528b2e51bbff189bd9196c1e2bfd274048f419..daf1e7e01e191e8ac1c4bdcaa62fc4d88a34c0c0 100644 --- a/src/vs/workbench/common/editor/textDiffEditorModel.ts +++ b/src/vs/workbench/common/editor/textDiffEditorModel.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IDiffEditorModel } from 'vs/editor/common/editorCommon'; -import { EditorModel } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; @@ -32,12 +31,10 @@ export class TextDiffEditorModel extends DiffEditorModel { this.updateTextDiffEditorModel(); } - async load(): Promise { - await super.load(); + async resolve(): Promise { + await super.resolve(); this.updateTextDiffEditorModel(); - - return this; } private updateTextDiffEditorModel(): void { diff --git a/src/vs/workbench/contrib/backup/test/browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/browser/backupRestorer.test.ts index 0671325a416f422cc0bcce63c532d07c070dffba..1cbc3778c2b334ce9c30874a214fd12079bd6bbb 100644 --- a/src/vs/workbench/contrib/backup/test/browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/browser/backupRestorer.test.ts @@ -78,7 +78,7 @@ suite('BackupRestorer', () => { const resource = editor.resource; if (isEqual(resource, untitledFile1)) { const model = await accessor.textFileService.untitled.resolve({ untitledResource: resource }); - if (model.textEditorModel.getValue() !== 'untitled-1') { + if (model.textEditorModel?.getValue() !== 'untitled-1') { const backupContents = await backupFileService.getBackupContents(untitledFile1); assert.fail(`Unable to restore backup for resource ${untitledFile1.toString()}. Backup contents: ${backupContents}`); } @@ -86,21 +86,23 @@ suite('BackupRestorer', () => { counter++; } else if (isEqual(resource, untitledFile2)) { const model = await accessor.textFileService.untitled.resolve({ untitledResource: resource }); - if (model.textEditorModel.getValue() !== 'untitled-2') { + if (model.textEditorModel?.getValue() !== 'untitled-2') { const backupContents = await backupFileService.getBackupContents(untitledFile2); assert.fail(`Unable to restore backup for resource ${untitledFile2.toString()}. Backup contents: ${backupContents}`); } model.dispose(); counter++; } else if (isEqual(resource, fooFile)) { - const model = await accessor.textFileService.files.get(fooFile!)?.load(); + const model = accessor.textFileService.files.get(fooFile); + await model?.resolve(); if (model?.textEditorModel?.getValue() !== 'fooFile') { const backupContents = await backupFileService.getBackupContents(fooFile); assert.fail(`Unable to restore backup for resource ${fooFile.toString()}. Backup contents: ${backupContents}`); } counter++; } else { - const model = await accessor.textFileService.files.get(barFile!)?.load(); + const model = accessor.textFileService.files.get(barFile); + await model?.resolve(); if (model?.textEditorModel?.getValue() !== 'barFile') { const backupContents = await backupFileService.getBackupContents(barFile); assert.fail(`Unable to restore backup for resource ${barFile.toString()}. Backup contents: ${backupContents}`); diff --git a/src/vs/workbench/contrib/backup/test/browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/browser/backupTracker.test.ts index 17c3e8b56f6f7d84cda5e638e9d535e8d5bd640f..fecd4daba47a889aa5cca2f2ea1d0de86631f74c 100644 --- a/src/vs/workbench/contrib/backup/test/browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/browser/backupTracker.test.ts @@ -79,7 +79,7 @@ suite('BackupTracker (browser)', function () { const untitledModel = await untitledEditor.resolve(); if (!untitled?.contents) { - untitledModel.textEditorModel.setValue('Super Good'); + untitledModel.textEditorModel?.setValue('Super Good'); } await backupFileService.joinBackupResource(); diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index abb2ae3bcc233f44332c6a139ffe79cff5556118..54580acfab35043e32096b108010d897737d6b94 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -214,7 +214,7 @@ flakySuite('BackupTracker (native)', function () { accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } }); - await model?.load(); + await model?.resolve(); model?.textEditorModel?.setValue('foo'); assert.strictEqual(accessor.workingCopyService.dirtyCount, 1); @@ -235,7 +235,7 @@ flakySuite('BackupTracker (native)', function () { const model = accessor.textFileService.files.get(resource); - await model?.load(); + await model?.resolve(); model?.textEditorModel?.setValue('foo'); assert.strictEqual(accessor.workingCopyService.dirtyCount, 1); @@ -261,7 +261,7 @@ flakySuite('BackupTracker (native)', function () { accessor.fileDialogService.setConfirmResult(ConfirmResult.DONT_SAVE); accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } }); - await model?.load(); + await model?.resolve(); model?.textEditorModel?.setValue('foo'); assert.strictEqual(accessor.workingCopyService.dirtyCount, 1); const event = new BeforeShutdownEventImpl(); @@ -285,7 +285,7 @@ flakySuite('BackupTracker (native)', function () { accessor.fileDialogService.setConfirmResult(ConfirmResult.SAVE); accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } }); - await model?.load(); + await model?.resolve(); model?.textEditorModel?.setValue('foo'); assert.strictEqual(accessor.workingCopyService.dirtyCount, 1); const event = new BeforeShutdownEventImpl(); @@ -425,7 +425,7 @@ flakySuite('BackupTracker (native)', function () { // Set cancel to force a veto if hot exit does not trigger accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); - await model?.load(); + await model?.resolve(); model?.textEditorModel?.setValue('foo'); assert.strictEqual(accessor.workingCopyService.dirtyCount, 1); diff --git a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts index 4e693565f9f2d210e48ad66b089f08b163d034ab..24eb70fa001cfe9ca6dcda289a6cc5fe75ff759c 100644 --- a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts @@ -33,7 +33,7 @@ suite('Save Participants', function () { test('insert final new line', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; - await model.load(); + await model.resolve(); const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'insertFinalNewline': true }); const participant = new FinalNewLineParticipant(configService, undefined!); @@ -66,7 +66,7 @@ suite('Save Participants', function () { test('trim final new lines', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; - await model.load(); + await model.resolve(); const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'trimFinalNewlines': true }); const participant = new TrimFinalNewLinesParticipant(configService, undefined!); @@ -101,7 +101,7 @@ suite('Save Participants', function () { test('trim final new lines bug#39750', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; - await model.load(); + await model.resolve(); const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'trimFinalNewlines': true }); const participant = new TrimFinalNewLinesParticipant(configService, undefined!); @@ -128,7 +128,7 @@ suite('Save Participants', function () { test('trim final new lines bug#46075', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; - await model.load(); + await model.resolve(); const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'trimFinalNewlines': true }); const participant = new TrimFinalNewLinesParticipant(configService, undefined!); @@ -155,7 +155,7 @@ suite('Save Participants', function () { test('trim whitespace', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; - await model.load(); + await model.resolve(); const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'trimTrailingWhitespace': true }); const participant = new TrimWhitespaceParticipant(configService, undefined!); diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index cc8e04596ff9c1858334ba87662d368bb9dc547d..530f47bd49c58239485235650fe20919ba8fb72e 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -9,7 +9,7 @@ import { EncodingMode, IFileEditorInput, Verbosity, GroupIdentifier, IMoveResult import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; -import { ITextFileService, TextFileEditorModelState, TextFileLoadReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, TextFileEditorModelState, TextFileResolveReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IReference, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -291,7 +291,7 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements encoding: this.preferredEncoding, reload: { async: true }, // trigger a reload of the model if it exists already but do not wait to show the model allowBinary: this.forceOpenAs === ForceOpenAs.Text, - reason: TextFileLoadReason.EDITOR + reason: TextFileResolveReason.EDITOR }); // This is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary @@ -328,7 +328,10 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements } private async doResolveAsBinary(): Promise { - return this.instantiationService.createInstance(BinaryEditorModel, this.preferredResource, this.getName()).load(); + const model = this.instantiationService.createInstance(BinaryEditorModel, this.preferredResource, this.getName()); + await model.resolve(); + + return model; } isResolved(): boolean { diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts index c959c634dab64706c79ee90da10ca738e7f1b0c6..91b0271c6ea5018b3cdc850bdb20871a919f9394 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts @@ -149,7 +149,7 @@ suite('Files - TextFileEditorTracker', () => { assert.ok(!accessor.editorService.isOpen(untitledEditor)); - model.textEditorModel.setValue('Super Good'); + model.textEditorModel?.setValue('Super Good'); await awaitEditorOpening(accessor.editorService); assert.ok(accessor.editorService.isOpen(untitledEditor)); @@ -169,12 +169,12 @@ suite('Files - TextFileEditorTracker', () => { accessor.hostService.setFocus(false); accessor.hostService.setFocus(true); - await awaitModelLoadEvent(accessor.textFileService, resource); + await awaitModelResolveEvent(accessor.textFileService, resource); }); - function awaitModelLoadEvent(textFileService: ITextFileService, resource: URI): Promise { + function awaitModelResolveEvent(textFileService: ITextFileService, resource: URI): Promise { return new Promise(resolve => { - const listener = textFileService.files.onDidLoad(e => { + const listener = textFileService.files.onDidResolve(e => { if (isEqual(e.model.resource, resource)) { listener.dispose(); resolve(); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 4de19397cb5c467d05e4eed9222ba36544f72d08..87131ec930f2e8718aa5fe65c7bac2fad718ad75 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -1033,7 +1033,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { return undefined; } - return editorModel!.load(); + return editorModel!.resolve(); }) .then(editorModel => { if (token.isCancellationRequested) { diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 511c8296399a10c4803a673a2e5c1b4d88b48988..7369c405ee02c04d3d0eeedf30f8c620d9993894 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -19,7 +19,7 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { ITextFileService, ITextFileSaveEvent, ITextFileLoadEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileSaveEvent, ITextFileResolveEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { extname, basename, isEqual, isEqualOrParent } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -124,14 +124,14 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr this._register(configurationTelemetry(telemetryService, configurationService)); // Files Telemetry - this._register(textFileService.files.onDidLoad(e => this.onTextFileModelLoaded(e))); + this._register(textFileService.files.onDidResolve(e => this.onTextFileModelResolved(e))); this._register(textFileService.files.onDidSave(e => this.onTextFileModelSaved(e))); // Lifecycle this._register(lifecycleService.onShutdown(() => this.dispose())); } - private onTextFileModelLoaded(e: ITextFileLoadEvent): void { + private onTextFileModelResolved(e: ITextFileResolveEvent): void { const settingsType = this.getTypeIfSettings(e.model.resource); if (settingsType) { type SettingsReadClassification = { diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 0643822f1b6e3a6f3cd62ffbac31d0c944d26f7b..a7a4a7e2a327eb827a5f065f945f53f8235455ae 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -479,10 +479,6 @@ class SimpleDiffEditorModel extends EditorModel { super(); } - async load(): Promise { - return this; - } - public dispose() { super.dispose(); this._original.dispose(); diff --git a/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts index fd75f337148ea55128e8efaacb09b6fc04d0b3ad..1b9c1c74ae73721932991f1df156253a14e82e8b 100644 --- a/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/browser/keybindingsEditorModel.ts @@ -140,7 +140,7 @@ export class KeybindingsEditorModel extends EditorModel { return result; } - resolve(actionLabels: Map): Promise { + async resolve(actionLabels = new Map()): Promise { const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); this._keybindingItemsSortedByPrecedence = []; @@ -158,7 +158,6 @@ export class KeybindingsEditorModel extends EditorModel { this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(command, keybindingItem, workbenchActionsRegistry, actionLabels)); } this._keybindingItems = this._keybindingItemsSortedByPrecedence.slice(0).sort((a, b) => KeybindingsEditorModel.compareKeybindingData(a, b)); - return Promise.resolve(this); } private static getId(keybindingItem: IKeybindingItem): string { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 79b0cbc55c8520ddedf9069fc77797b71cff412b..fda4e2745fbb8ab98a962684dc4954afc4f2fd9f 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -326,7 +326,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex sourceModelEncoding = sourceModelWithEncodingSupport.getEncoding(); } - // Prefer an existing model if it is already loaded for the given target resource + // Prefer an existing model if it is already resolved for the given target resource let targetExists: boolean = false; let targetModel = this.files.get(target); if (targetModel?.isResolved()) { @@ -346,7 +346,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex targetModel = await this.files.resolve(target, { encoding: sourceModelEncoding }); } catch (error) { // if the target already exists and was not created by us, it is possible - // that we cannot load the target as text model if it is binary or too + // that we cannot resolve the target as text model if it is binary or too // large. in that case we have to delete the target file first and then // re-run the operation. if (targetExists) { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 6c00a4b49138a7e10512fe00def7e0fea6253f4b..ce6cdd87bdc43bfb614858c738b14bc187f38c21 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; -import { ITextFileService, TextFileEditorModelState, ITextFileEditorModel, ITextFileStreamContent, ITextFileLoadOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions, TextFileLoadReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, TextFileEditorModelState, ITextFileEditorModel, ITextFileStreamContent, ITextFileResolveOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions, TextFileResolveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; @@ -43,8 +43,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private readonly _onDidChangeContent = this._register(new Emitter()); readonly onDidChangeContent = this._onDidChangeContent.event; - private readonly _onDidLoad = this._register(new Emitter()); - readonly onDidLoad = this._onDidLoad.event; + private readonly _onDidResolve = this._register(new Emitter()); + readonly onDidResolve = this._onDidResolve.event; private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; @@ -221,7 +221,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil const softUndo = options?.soft; if (!softUndo) { try { - await this.load({ forceReadFromFile: true }); + await this.resolve({ forceReadFromFile: true }); } catch (error) { // FileNotFound means the file got deleted meanwhile, so ignore it @@ -246,52 +246,52 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil //#endregion - //#region Load + //#region Resolve - async load(options?: ITextFileLoadOptions): Promise { - this.logService.trace('[text file model] load() - enter', this.resource.toString(true)); + async resolve(options?: ITextFileResolveOptions): Promise { + this.logService.trace('[text file model] resolve() - enter', this.resource.toString(true)); // Return early if we are disposed if (this.isDisposed()) { - this.logService.trace('[text file model] load() - exit - without loading because model is disposed', this.resource.toString(true)); + this.logService.trace('[text file model] resolve() - exit - without resolving because model is disposed', this.resource.toString(true)); - return this; + return; } // Unless there are explicit contents provided, it is important that we do not - // load a model that is dirty or is in the process of saving to prevent data + // resolve a model that is dirty or is in the process of saving to prevent data // loss. if (!options?.contents && (this.dirty || this.saveSequentializer.hasPending())) { - this.logService.trace('[text file model] load() - exit - without loading because model is dirty or being saved', this.resource.toString(true)); + this.logService.trace('[text file model] resolve() - exit - without resolving because model is dirty or being saved', this.resource.toString(true)); - return this; + return; } - return this.doLoad(options); + return this.doResolve(options); } - private async doLoad(options?: ITextFileLoadOptions): Promise { + private async doResolve(options?: ITextFileResolveOptions): Promise { // First check if we have contents to use for the model if (options?.contents) { - return this.loadFromBuffer(options.contents, options); + return this.resolveFromBuffer(options.contents, options); } - // Second, check if we have a backup to load from (only for new models) + // Second, check if we have a backup to resolve from (only for new models) const isNewModel = !this.isResolved(); if (isNewModel) { - const loadedFromBackup = await this.loadFromBackup(options); - if (loadedFromBackup) { - return loadedFromBackup; + const resolvedFromBackup = await this.resolveFromBackup(options); + if (resolvedFromBackup) { + return; } } - // Finally, load from file resource - return this.loadFromFile(options); + // Finally, resolve from file resource + return this.resolveFromFile(options); } - private async loadFromBuffer(buffer: ITextBufferFactory, options?: ITextFileLoadOptions): Promise { - this.logService.trace('[text file model] loadFromBuffer()', this.resource.toString(true)); + private async resolveFromBuffer(buffer: ITextBufferFactory, options?: ITextFileResolveOptions): Promise { + this.logService.trace('[text file model] resolveFromBuffer()', this.resource.toString(true)); // Try to resolve metdata from disk let mtime: number; @@ -321,8 +321,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil const preferredEncoding = await this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding); - // Load with buffer - this.loadFromContent({ + // Resolve with buffer + this.resolveFromContent({ resource: this.resource, name: this.name, mtime, @@ -331,12 +331,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil etag, value: buffer, encoding: preferredEncoding.encoding - }, true /* dirty (loaded from buffer) */, options); - - return this; + }, true /* dirty (resolved from buffer) */, options); } - private async loadFromBackup(options?: ITextFileLoadOptions): Promise { + private async resolveFromBackup(options?: ITextFileResolveOptions): Promise { // Resolve backup if any const backup = await this.backupFileService.resolve(this.resource); @@ -350,25 +348,27 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Abort if someone else managed to resolve the model by now let isNewModel = !this.isResolved(); if (!isNewModel) { - this.logService.trace('[text file model] loadFromBackup() - exit - without loading because previously new model got created meanwhile', this.resource.toString(true)); + this.logService.trace('[text file model] resolveFromBackup() - exit - without resolving because previously new model got created meanwhile', this.resource.toString(true)); - return this; // imply that loading has happened in another operation + return true; // imply that resolving has happened in another operation } - // Try to load from backup if we have any + // Try to resolve from backup if we have any if (backup) { - return this.doLoadFromBackup(backup, encoding, options); + this.doResolveFromBackup(backup, encoding, options); + + return true; } - // Otherwise signal back that loading did not happen - return undefined; + // Otherwise signal back that resolving did not happen + return false; } - private doLoadFromBackup(backup: IResolvedBackup, encoding: string, options?: ITextFileLoadOptions): TextFileEditorModel { - this.logService.trace('[text file model] doLoadFromBackup()', this.resource.toString(true)); + private doResolveFromBackup(backup: IResolvedBackup, encoding: string, options?: ITextFileResolveOptions): void { + this.logService.trace('[text file model] doResolveFromBackup()', this.resource.toString(true)); - // Load with backup - this.loadFromContent({ + // Resolve with backup + this.resolveFromContent({ resource: this.resource, name: this.name, mtime: backup.meta ? backup.meta.mtime : Date.now(), @@ -377,18 +377,16 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil etag: backup.meta ? backup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! value: backup.value, encoding - }, true /* dirty (loaded from backup) */, options); + }, true /* dirty (resolved from backup) */, options); // Restore orphaned flag based on state if (backup.meta && backup.meta.orphaned) { this.setOrphaned(true); } - - return this; } - private async loadFromFile(options?: ITextFileLoadOptions): Promise { - this.logService.trace('[text file model] loadFromFile()', this.resource.toString(true)); + private async resolveFromFile(options?: ITextFileResolveOptions): Promise { + this.logService.trace('[text file model] resolveFromFile()', this.resource.toString(true)); const forceReadFromFile = options?.forceReadFromFile; const allowBinary = this.isResolved() /* always allow if we resolved previously */ || options?.allowBinary; @@ -409,18 +407,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil try { const content = await this.textFileService.readStream(this.resource, { acceptTextOnly: !allowBinary, etag, encoding: this.preferredEncoding }); - // Clear orphaned state when loading was successful + // Clear orphaned state when resolving was successful this.setOrphaned(false); // Return early if the model content has changed // meanwhile to prevent loosing any changes if (currentVersionId !== this.versionId) { - this.logService.trace('[text file model] loadFromFile() - exit - without loading because model content changed', this.resource.toString(true)); + this.logService.trace('[text file model] resolveFromFile() - exit - without resolving because model content changed', this.resource.toString(true)); - return this; + return; } - return this.loadFromContent(content, false /* not dirty (loaded from file) */, options); + return this.resolveFromContent(content, false /* not dirty (resolved from file) */, options); } catch (error) { const result = error.fileOperationResult; @@ -430,15 +428,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // NotModified status is expected and can be handled gracefully // if we are resolved if (this.isResolved() && result === FileOperationResult.FILE_NOT_MODIFIED_SINCE) { - return this; + return; } // Unless we are forced to read from the file, Ignore when a model has been resolved once - // and the file was deleted meanwhile. Since we already have the model loaded, we can return + // and the file was deleted meanwhile. Since we already have the model resolved, we can return // to this state and update the orphaned flag to indicate that this model has no version on // disk anymore. if (this.isResolved() && result === FileOperationResult.FILE_NOT_FOUND && !forceReadFromFile) { - return this; + return; } // Otherwise bubble up the error @@ -446,14 +444,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private loadFromContent(content: ITextFileStreamContent, dirty: boolean, options?: ITextFileLoadOptions): TextFileEditorModel { - this.logService.trace('[text file model] loadFromContent() - enter', this.resource.toString(true)); + private resolveFromContent(content: ITextFileStreamContent, dirty: boolean, options?: ITextFileResolveOptions): void { + this.logService.trace('[text file model] resolveFromContent() - enter', this.resource.toString(true)); // Return early if we are disposed if (this.isDisposed()) { - this.logService.trace('[text file model] loadFromContent() - exit - because model is disposed', this.resource.toString(true)); + this.logService.trace('[text file model] resolveFromContent() - exit - because model is disposed', this.resource.toString(true)); - return this; + return; } // Update our resolved disk stat model @@ -498,9 +496,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.setDirty(!!dirty); // Emit as event - this._onDidLoad.fire(options?.reason ?? TextFileLoadReason.OTHER); - - return this; + this._onDidResolve.fire(options?.reason ?? TextFileResolveReason.OTHER); } private doCreateTextModel(resource: URI, value: ITextBufferFactory): void { @@ -550,7 +546,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // We mark check for a dirty-state change upon model content change, unless: - // - explicitly instructed to ignore it (e.g. from model.load()) + // - explicitly instructed to ignore it (e.g. from model.resolve()) // - the model is readonly (in that case we never assume the change was done by the user) if (!this.ignoreDirtyOnModelContentChange && !this.isReadonly()) { @@ -884,7 +880,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Subsequent resolve - make sure that we only assign it if the mtime is equal or has advanced. - // This prevents race conditions from loading and saving. If a save comes in late after a revert + // This prevents race conditions from resolving and saving. If a save comes in late after a revert // was called, the mtime could be out of sync. else if (this.lastResolvedFileStat.mtime <= newFileStat.mtime) { this.lastResolvedFileStat = newFileStat; @@ -950,7 +946,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - // Decode: Load with encoding + // Decode: Resolve with encoding else { if (this.isDirty()) { this.notificationService.info(localize('saveFileFirst', "The file is dirty. Please save it first before reopening it with another encoding.")); @@ -960,8 +956,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updatePreferredEncoding(encoding); - // Load - this.load({ + this.resolve({ forceReadFromFile: true // because encoding has changed }); } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index aafcbbef2dc5bdc540d1fb1689c0135ef35655fe..d0a43d12c02de91e039c15c59eb21a6365f6dbcb 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ITextFileEditorModel, ITextFileEditorModelManager, ITextFileEditorModelLoadOrCreateOptions, ITextFileLoadEvent, ITextFileSaveEvent, ITextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileEditorModel, ITextFileEditorModelManager, ITextFileEditorModelResolveOrCreateOptions, ITextFileResolveEvent, ITextFileSaveEvent, ITextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textfiles'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceMap } from 'vs/base/common/map'; @@ -32,8 +32,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private readonly _onDidCreate = this._register(new Emitter()); readonly onDidCreate = this._onDidCreate.event; - private readonly _onDidLoad = this._register(new Emitter()); - readonly onDidLoad = this._onDidLoad.event; + private readonly _onDidResolve = this._register(new Emitter()); + readonly onDidResolve = this._onDidResolve.event; private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; @@ -53,9 +53,9 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private readonly mapResourceToModel = new ResourceMap(); private readonly mapResourceToModelListeners = new ResourceMap(); private readonly mapResourceToDisposeListener = new ResourceMap(); - private readonly mapResourceToPendingModelLoaders = new ResourceMap>(); + private readonly mapResourceToPendingModelResolvers = new ResourceMap>(); - private readonly modelLoadQueue = this._register(new ResourceQueue()); + private readonly modelResolveQueue = this._register(new ResourceQueue()); saveErrorHandler = (() => { const notificationService = this.notificationService; @@ -104,25 +104,25 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE continue; // require a resolved, saved model to continue } - // Trigger a model load for any update or add event that impacts + // Trigger a model resolve for any update or add event that impacts // the model. We also consider the added event because it could // be that a file was added and updated right after. if (e.contains(model.resource, FileChangeType.UPDATED, FileChangeType.ADDED)) { - this.queueModelLoad(model); + this.queueModelResolve(model); } } } - private queueModelLoad(model: TextFileEditorModel): void { + private queueModelResolve(model: TextFileEditorModel): void { - // Load model to update (use a queue to prevent accumulation of loads - // when the load actually takes long. At most we only want the queue - // to have a size of 2 (1 running load and 1 queued load). - const queue = this.modelLoadQueue.queueFor(model.resource); + // Resolve model to update (use a queue to prevent accumulation of resolves + // when the resolve actually takes long. At most we only want the queue + // to have a size of 2 (1 running resolve and 1 queued resolve). + const queue = this.modelResolveQueue.queueFor(model.resource); if (queue.size <= 1) { queue.queue(async () => { try { - await model.load(); + await model.resolve(); } catch (error) { onUnexpectedError(error); } @@ -152,7 +152,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE } } - // remember each source model to load again after move is done + // remember each source model to resolve again after move is done // with optional content to restore if it was dirty for (const sourceModel of sourceModels) { const sourceModelResource = sourceModel.resource; @@ -219,7 +219,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE })()); break; - // Move/Copy: restore models that were loaded before the operation took place + // Move/Copy: restore models that were resolved before the operation took place case FileOperation.MOVE: case FileOperation.COPY: e.waitUntil((async () => { @@ -255,17 +255,17 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this.mapResourceToModel.get(resource); } - async resolve(resource: URI, options?: ITextFileEditorModelLoadOrCreateOptions): Promise { + async resolve(resource: URI, options?: ITextFileEditorModelResolveOrCreateOptions): Promise { - // Await a pending model load first before proceeding - // to ensure that we never load a model more than once + // Await a pending model resolve first before proceeding + // to ensure that we never resolve a model more than once // in parallel const pendingResolve = this.joinPendingResolve(resource); if (pendingResolve) { await pendingResolve; } - let modelPromise: Promise; + let modelPromise: Promise; let model = this.get(resource); let didCreateModel = false; @@ -274,7 +274,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Always reload if contents are provided if (options?.contents) { - modelPromise = model.load(options); + modelPromise = model.resolve(options); } // Reload async or sync based on options @@ -282,19 +282,19 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // async reload: trigger a reload but return immediately if (options.reload.async) { - modelPromise = Promise.resolve(model); - model.load(options); + modelPromise = Promise.resolve(); + model.resolve(options); } // sync reload: do not return until model reloaded else { - modelPromise = model.load(options); + modelPromise = model.resolve(options); } } // Do not reload else { - modelPromise = Promise.resolve(model); + modelPromise = Promise.resolve(); } } @@ -303,13 +303,13 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE didCreateModel = true; const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined, options ? options.mode : undefined); - modelPromise = model.load(options); + modelPromise = model.resolve(options); this.registerModel(newModel); } - // Store pending loads to avoid race conditions - this.mapResourceToPendingModelLoaders.set(resource, modelPromise); + // Store pending resolves to avoid race conditions + this.mapResourceToPendingModelResolvers.set(resource, modelPromise); // Make known to manager (if not already known) this.add(resource, model); @@ -326,23 +326,23 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE } try { - const resolvedModel = await modelPromise; + await modelPromise; - // Remove from pending loads - this.mapResourceToPendingModelLoaders.delete(resource); + // Remove from pending resolves + this.mapResourceToPendingModelResolvers.delete(resource); // Apply mode if provided if (options?.mode) { - resolvedModel.setMode(options.mode); + model.setMode(options.mode); } // Model can be dirty if a backup was restored, so we make sure to // have this event delivered if we created the model here - if (didCreateModel && resolvedModel.isDirty()) { - this._onDidChangeDirty.fire(resolvedModel); + if (didCreateModel && model.isDirty()) { + this._onDidChangeDirty.fire(model); } - return resolvedModel; + return model; } catch (error) { // Free resources of this invalid model @@ -350,17 +350,17 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE model.dispose(); } - // Remove from pending loads - this.mapResourceToPendingModelLoaders.delete(resource); + // Remove from pending resolves + this.mapResourceToPendingModelResolvers.delete(resource); throw error; } } private joinPendingResolve(resource: URI): Promise | undefined { - const pendingModelLoad = this.mapResourceToPendingModelLoaders.get(resource); - if (pendingModelLoad) { - return pendingModelLoad.then(undefined, error => {/* ignore any error here, it will bubble to the original requestor*/ }); + const pendingModelResolve = this.mapResourceToPendingModelResolvers.get(resource); + if (pendingModelResolve) { + return pendingModelResolve.then(undefined, error => {/* ignore any error here, it will bubble to the original requestor*/ }); } return undefined; @@ -370,7 +370,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Install model listeners const modelListeners = new DisposableStore(); - modelListeners.add(model.onDidLoad(reason => this._onDidLoad.fire({ model, reason }))); + modelListeners.add(model.onDidResolve(reason => this._onDidResolve.fire({ model, reason }))); modelListeners.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire(model))); modelListeners.add(model.onDidSaveError(() => this._onDidSaveError.fire(model))); modelListeners.add(model.onDidSave(reason => this._onDidSave.fire({ model, reason }))); @@ -432,7 +432,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // model caches this.mapResourceToModel.clear(); - this.mapResourceToPendingModelLoaders.clear(); + this.mapResourceToPendingModelResolvers.clear(); // dispose the dispose listeners this.mapResourceToDisposeListener.forEach(listener => listener.dispose()); @@ -445,10 +445,10 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE canDispose(model: TextFileEditorModel): true | Promise { - // quick return if model already disposed or not dirty and not loading + // quick return if model already disposed or not dirty and not resolving if ( model.isDisposed() || - (!this.mapResourceToPendingModelLoaders.has(model.resource) && !model.isDirty()) + (!this.mapResourceToPendingModelResolvers.has(model.resource) && !model.isDirty()) ) { return true; } @@ -459,7 +459,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private async doCanDispose(model: TextFileEditorModel): Promise { - // if we have a pending model load, await it first and then try again + // if we have a pending model resolve, await it first and then try again const pendingResolve = this.joinPendingResolve(model.resource); if (pendingResolve) { await pendingResolve; diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 132dcfac80e6c011783c4aaae9b7221106b98f12..6a2f37551c8b065da5a73e15bed8216c34c9a061 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -214,7 +214,7 @@ export const enum TextFileEditorModelState { ERROR } -export const enum TextFileLoadReason { +export const enum TextFileResolveReason { EDITOR = 1, REFERENCE = 2, OTHER = 3 @@ -244,12 +244,12 @@ export interface ITextFileStreamContent extends IBaseTextFileContent { value: ITextBufferFactory; } -export interface ITextFileEditorModelLoadOrCreateOptions { +export interface ITextFileEditorModelResolveOrCreateOptions { /** - * Context why the model is being loaded or created. + * Context why the model is being resolved or created. */ - reason?: TextFileLoadReason; + reason?: TextFileResolveReason; /** * The language mode to use for the model text content. @@ -269,7 +269,7 @@ export interface ITextFileEditorModelLoadOrCreateOptions { contents?: ITextBufferFactory; /** - * If the model was already loaded before, allows to trigger + * If the model was already resolved before, allows to trigger * a reload of it to fetch the latest contents: * - async: resolve() will return immediately and trigger * a reload that will run in the background. @@ -281,7 +281,7 @@ export interface ITextFileEditorModelLoadOrCreateOptions { }; /** - * Allow to load a model even if we think it is a binary file. + * Allow to resolve a model even if we think it is a binary file. */ allowBinary?: boolean; } @@ -291,9 +291,9 @@ export interface ITextFileSaveEvent { reason: SaveReason; } -export interface ITextFileLoadEvent { +export interface ITextFileResolveEvent { model: ITextFileEditorModel; - reason: TextFileLoadReason; + reason: TextFileResolveReason; } export interface ITextFileSaveParticipant { @@ -313,7 +313,7 @@ export interface ITextFileSaveParticipant { export interface ITextFileEditorModelManager { readonly onDidCreate: Event; - readonly onDidLoad: Event; + readonly onDidResolve: Event; readonly onDidChangeDirty: Event; readonly onDidChangeEncoding: Event; readonly onDidSaveError: Event; @@ -337,9 +337,9 @@ export interface ITextFileEditorModelManager { get(resource: URI): ITextFileEditorModel | undefined; /** - * Allows to load a text file model from disk. + * Allows to resolve a text file model from disk. */ - resolve(resource: URI, options?: ITextFileEditorModelLoadOrCreateOptions): Promise; + resolve(resource: URI, options?: ITextFileEditorModelResolveOrCreateOptions): Promise; /** * Adds a participant for saving text file models. @@ -392,7 +392,7 @@ export interface ITextFileSaveAsOptions extends ITextFileSaveOptions { suggestedTarget?: URI; } -export interface ITextFileLoadOptions { +export interface ITextFileResolveOptions { /** * The contents to use for the model if known. If not @@ -407,14 +407,14 @@ export interface ITextFileLoadOptions { forceReadFromFile?: boolean; /** - * Allow to load a model even if we think it is a binary file. + * Allow to resolve a model even if we think it is a binary file. */ allowBinary?: boolean; /** - * Context why the model is being loaded. + * Context why the model is being resolved. */ - reason?: TextFileLoadReason; + reason?: TextFileResolveReason; } export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport, IWorkingCopy { @@ -432,7 +432,7 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport save(options?: ITextFileSaveOptions): Promise; revert(options?: IRevertOptions): Promise; - load(options?: ITextFileLoadOptions): Promise; + resolve(options?: ITextFileResolveOptions): Promise; isDirty(): this is IResolvedTextFileEditorModel; diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 15f46ae855c4f7a60c428f7a7719ba01dd9e9db6..9ad4e3ce61f4a9df7411d1cae5bfd1ccb7d32186 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -43,12 +43,12 @@ suite('Files - TextFileEditorModel', () => { test('basic events', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - let onDidLoadCounter = 0; - model.onDidLoad(() => onDidLoadCounter++); + let onDidResolveCounter = 0; + model.onDidResolve(() => onDidResolveCounter++); - await model.load(); + await model.resolve(); - assert.strictEqual(onDidLoadCounter, 1); + assert.strictEqual(onDidResolveCounter, 1); let onDidChangeContentCounter = 0; model.onDidChangeContent(() => onDidChangeContentCounter++); @@ -84,7 +84,7 @@ suite('Files - TextFileEditorModel', () => { test('save', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); assert.strictEqual(accessor.workingCopyService.dirtyCount, 0); @@ -133,7 +133,7 @@ suite('Files - TextFileEditorModel', () => { test('save - touching also emits saved event', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); let savedEvent = false; model.onDidSave(() => savedEvent = true); @@ -157,7 +157,7 @@ suite('Files - TextFileEditorModel', () => { test('save - touching with error turns model dirty', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); let saveErrorEvent = false; model.onDidSaveError(() => saveErrorEvent = true); @@ -191,7 +191,7 @@ suite('Files - TextFileEditorModel', () => { test('save error (generic)', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('bar')); @@ -221,7 +221,7 @@ suite('Files - TextFileEditorModel', () => { test('save error (conflict)', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('bar')); @@ -274,7 +274,7 @@ suite('Files - TextFileEditorModel', () => { model.setEncoding('utf16', EncodingMode.Decode); await timeout(0); - assert.ok(model.isResolved()); // model got loaded due to decoding + assert.ok(model.isResolved()); // model got resolved due to decoding model.dispose(); }); @@ -286,7 +286,7 @@ suite('Files - TextFileEditorModel', () => { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', mode); - await model.load(); + await model.resolve(); assert.strictEqual(model.textEditorModel!.getModeId(), mode); @@ -297,47 +297,47 @@ suite('Files - TextFileEditorModel', () => { test('disposes when underlying model is destroyed', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); model.textEditorModel!.dispose(); assert.ok(model.isDisposed()); }); - test('Load does not trigger save', async function () { + test('Resolve does not trigger save', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined); assert.ok(model.hasState(TextFileEditorModelState.SAVED)); model.onDidSave(() => assert.fail()); model.onDidChangeDirty(() => assert.fail()); - await model.load(); + await model.resolve(); assert.ok(model.isResolved()); model.dispose(); assert.ok(!accessor.modelService.getModel(model.resource)); }); - test('Load returns dirty model as long as model is dirty', async function () { + test('Resolve returns dirty model as long as model is dirty', async function () { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); assert.ok(model.isDirty()); assert.ok(model.hasState(TextFileEditorModelState.DIRTY)); - await model.load(); + await model.resolve(); assert.ok(model.isDirty()); model.dispose(); }); - test('Load with contents', async function () { + test('Resolve with contents', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load({ contents: createTextBufferFactory('Hello World') }); + await model.resolve({ contents: createTextBufferFactory('Hello World') }); assert.strictEqual(model.textEditorModel?.getValue(), 'Hello World'); assert.strictEqual(model.isDirty(), true); - await model.load({ contents: createTextBufferFactory('Hello Changes') }); + await model.resolve({ contents: createTextBufferFactory('Hello Changes') }); assert.strictEqual(model.textEditorModel?.getValue(), 'Hello Changes'); assert.strictEqual(model.isDirty(), true); @@ -365,7 +365,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); assert.ok(model.isDirty()); @@ -398,7 +398,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); assert.ok(model.isDirty()); @@ -419,7 +419,7 @@ suite('Files - TextFileEditorModel', () => { test('Undo to saved state turns model non-dirty', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('Hello Text')); assert.ok(model.isDirty()); @@ -427,12 +427,12 @@ suite('Files - TextFileEditorModel', () => { assert.ok(!model.isDirty()); }); - test('Load and undo turns model dirty', async function () { + test('Resolve and undo turns model dirty', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); accessor.fileService.setContent('Hello Change'); - await model.load(); + await model.resolve(); await model.textEditorModel!.undo(); assert.ok(model.isDirty()); @@ -448,7 +448,7 @@ suite('Files - TextFileEditorModel', () => { model.setDirty(true); assert.ok(!model.isDirty()); // needs to be resolved - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); assert.ok(model.isDirty()); @@ -491,7 +491,7 @@ suite('Files - TextFileEditorModel', () => { saveEvent = true; }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); assert.ok(!model.isDirty()); @@ -509,25 +509,25 @@ suite('Files - TextFileEditorModel', () => { test('File not modified error is handled gracefully', async function () { let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); const mtime = getLastModifiedTime(model); accessor.textFileService.setReadStreamErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_MODIFIED_SINCE)); - model = await model.load() as TextFileEditorModel; + await model.resolve(); assert.ok(model); assert.strictEqual(getLastModifiedTime(model), mtime); model.dispose(); }); - test('Load error is handled gracefully if model already exists', async function () { + test('Resolve error is handled gracefully if model already exists', async function () { let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); accessor.textFileService.setReadStreamErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_FOUND)); - model = await model.load() as TextFileEditorModel; + await model.resolve(); assert.ok(model); model.dispose(); }); @@ -583,7 +583,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); assert.ok(model.isDirty()); @@ -610,7 +610,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); await model.save({ skipSaveParticipants: true }); @@ -640,7 +640,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); const now = Date.now(); @@ -661,7 +661,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); await model.save(); @@ -685,7 +685,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); const p1 = model.save(); @@ -744,7 +744,7 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.load(); + await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); savePromise = model.save(); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts index 396928a87e64f820698e43734bd76e1fa3d72cef..7c41f30afbebec952eecf9562bb5753ddc0de84a 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts @@ -186,16 +186,16 @@ suite('Files - TextFileEditorModelManager', () => { const resource1 = toResource.call(this, '/path/index.txt'); const resource2 = toResource.call(this, '/path/other.txt'); - let loadedCounter = 0; + let resolvedCounter = 0; let gotDirtyCounter = 0; let gotNonDirtyCounter = 0; let revertedCounter = 0; let savedCounter = 0; let encodingCounter = 0; - manager.onDidLoad(({ model }) => { + manager.onDidResolve(({ model }) => { if (model.resource.toString() === resource1.toString()) { - loadedCounter++; + resolvedCounter++; } }); @@ -228,13 +228,13 @@ suite('Files - TextFileEditorModelManager', () => { }); const model1 = await manager.resolve(resource1, { encoding: 'utf8' }); - assert.strictEqual(loadedCounter, 1); + assert.strictEqual(resolvedCounter, 1); accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }], false)); accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }], false)); const model2 = await manager.resolve(resource2, { encoding: 'utf8' }); - assert.strictEqual(loadedCounter, 2); + assert.strictEqual(resolvedCounter, 2); model1.updateTextEditorModel(createTextBufferFactory('changed')); model1.updatePreferredEncoding('utf16'); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts index 7a0f551042653693ebcc3755d4463fa8f9d196aa..9bd5ac1d044bfcaa9eb8ba3ae788cd46c6c51523 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts @@ -31,7 +31,7 @@ suite('Files - TextFileService', () => { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.files).add(model.resource, model); - await model.load(); + await model.resolve(); assert.ok(!accessor.textFileService.isDirty(model.resource)); model.textEditorModel!.setValue('foo'); @@ -41,7 +41,7 @@ suite('Files - TextFileService', () => { const untitled = await accessor.textFileService.untitled.resolve(); assert.ok(!accessor.textFileService.isDirty(untitled.resource)); - untitled.textEditorModel.setValue('changed'); + untitled.textEditorModel?.setValue('changed'); assert.ok(accessor.textFileService.isDirty(untitled.resource)); @@ -53,7 +53,7 @@ suite('Files - TextFileService', () => { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.files).add(model.resource, model); - await model.load(); + await model.resolve(); model.textEditorModel!.setValue('foo'); assert.ok(accessor.textFileService.isDirty(model.resource)); @@ -66,7 +66,7 @@ suite('Files - TextFileService', () => { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.files).add(model.resource, model); - await model.load(); + await model.resolve(); model.textEditorModel!.setValue('foo'); assert.ok(accessor.textFileService.isDirty(model.resource)); @@ -80,7 +80,7 @@ suite('Files - TextFileService', () => { (accessor.textFileService.files).add(model.resource, model); accessor.fileDialogService.setPickFileToSave(model.resource); - await model.load(); + await model.resolve(); model.textEditorModel!.setValue('foo'); assert.ok(accessor.textFileService.isDirty(model.resource)); @@ -94,7 +94,7 @@ suite('Files - TextFileService', () => { (accessor.textFileService.files).add(model.resource, model); accessor.fileDialogService.setPickFileToSave(model.resource); - await model.load(); + await model.resolve(); model!.textEditorModel!.setValue('foo'); assert.ok(accessor.textFileService.isDirty(model.resource)); @@ -106,7 +106,7 @@ suite('Files - TextFileService', () => { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.files).add(model.resource, model); - await model.load(); + await model.resolve(); model!.textEditorModel!.setValue('foo'); assert.ok(accessor.textFileService.isDirty(model.resource)); diff --git a/src/vs/workbench/services/textfile/test/electron-browser/nativeTextFileService.test.ts b/src/vs/workbench/services/textfile/test/electron-browser/nativeTextFileService.test.ts index 9ac72acddc7de3dadad2a8358b999cf397955c66..2706bf1ad95c6f44b4b4d491382140fb20432b06 100644 --- a/src/vs/workbench/services/textfile/test/electron-browser/nativeTextFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-browser/nativeTextFileService.test.ts @@ -54,7 +54,7 @@ suite('Files - NativeTextFileService', function () { test('shutdown joins on pending saves', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - await model.load(); + await model.resolve(); let pendingSaveAwaited = false; model.save().then(() => pendingSaveAwaited = true); diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 4ef57070742154d62399aef3924b9013c74c2a9f..e924b129cf2d6a97b5c95ad9a303c531a834526e 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -9,7 +9,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IDisposable, toDisposable, IReference, ReferenceCollection, Disposable } from 'vs/base/common/lifecycle'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; -import { ITextFileService, TextFileLoadReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, TextFileResolveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { Schemas } from 'vs/base/common/network'; import { ITextModelService, ITextModelContentProvider, ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; @@ -60,7 +60,7 @@ class ResourceModelCollection extends ReferenceCollection { const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); (accessor.textFileService.files).add(textModel.resource, textModel); - await textModel.load(); + await textModel.resolve(); const ref = await accessor.textModelResolverService.createModelReference(textModel.resource); @@ -97,14 +97,14 @@ suite('Workbench - TextModelResolverService', () => { const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); (accessor.textFileService.files).add(textModel.resource, textModel); - const loadedModel = await textModel.load(); + await textModel.resolve(); - loadedModel.updateTextEditorModel(createTextBufferFactory('make dirty')); + textModel.updateTextEditorModel(createTextBufferFactory('make dirty')); const ref = await accessor.textModelResolverService.createModelReference(textModel.resource); let disposed = false; - Event.once(loadedModel.onWillDispose)(() => { + Event.once(textModel.onWillDispose)(() => { disposed = true; }); @@ -112,7 +112,7 @@ suite('Workbench - TextModelResolverService', () => { await timeout(0); assert.strictEqual(disposed, false); // not disposed because model still dirty - loadedModel.revert(); + textModel.revert(); await timeout(0); assert.strictEqual(disposed, true); // now disposed because model got reverted @@ -122,14 +122,14 @@ suite('Workbench - TextModelResolverService', () => { const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); (accessor.textFileService.files).add(textModel.resource, textModel); - const loadedModel = await textModel.load(); + await textModel.resolve(); - loadedModel.updateTextEditorModel(createTextBufferFactory('make dirty')); + textModel.updateTextEditorModel(createTextBufferFactory('make dirty')); const ref1 = await accessor.textModelResolverService.createModelReference(textModel.resource); let disposed = false; - Event.once(loadedModel.onWillDispose)(() => { + Event.once(textModel.onWillDispose)(() => { disposed = true; }); @@ -139,7 +139,7 @@ suite('Workbench - TextModelResolverService', () => { const ref2 = await accessor.textModelResolverService.createModelReference(textModel.resource); - loadedModel.revert(); + textModel.revert(); await timeout(0); assert.strictEqual(disposed, false); // not disposed because we got another ref meanwhile diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts index 6e194ecdae937c3864590bc559e5d1925e1c3b8d..171c9e185c94a8d87685d927ca2a56d4ec701ad4 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts @@ -8,7 +8,6 @@ import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/text import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IFileService } from 'vs/platform/files/common/files'; @@ -22,7 +21,7 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp static readonly ID: string = 'workbench.editors.untitledEditorInput'; - private modelResolve: Promise | undefined = undefined; + private modelResolve: Promise | undefined = undefined; constructor( public readonly model: IUntitledTextEditorModel, @@ -110,16 +109,14 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp return this.model.getMode(); } - resolve(): Promise { - - // Join a model resolve if we have had one before - if (this.modelResolve) { - return this.modelResolve; + async resolve(): Promise { + if (!this.modelResolve) { + this.modelResolve = this.model.resolve(); } - this.modelResolve = this.model.load(); + await this.modelResolve; - return this.modelResolve; + return this.model; } matches(otherInput: unknown): boolean { diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index 630443962d1553b69932668e512a08976230d8ba..261c565f1b88aa3aab0f00598e7de3f6b0b52cfc 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -13,7 +13,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { ITextBufferFactory, ITextModel } from 'vs/editor/common/model'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; -import { IResolvedTextEditorModel, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; @@ -57,15 +57,15 @@ export interface IUntitledTextEditorModel extends ITextEditorModel, IModeSupport setEncoding(encoding: string): void; /** - * Load the untitled model. + * Resolves the untitled model. */ - load(): Promise; + resolve(): Promise; /** * Updates the value of the untitled model optionally allowing to ignore dirty. * The model must be resolved for this method to work. */ - setValue(this: IResolvedTextEditorModel, value: string, ignoreDirty?: boolean): void; + setValue(value: string, ignoreDirty?: boolean): void; } export class UntitledTextEditorModel extends BaseTextEditorModel implements IUntitledTextEditorModel { @@ -272,7 +272,7 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt return { content: withNullAsUndefined(this.createSnapshot()) }; } - async load(): Promise { + async resolve(): Promise { // Check for backups const backup = await this.backupFileService.resolve(this.resource); @@ -321,8 +321,6 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt this._onDidChangeContent.fire(); } } - - return this as UntitledTextEditorModel & IResolvedTextEditorModel; } private onModelContentChanged(textEditorModel: ITextModel, e: IModelContentChangedEvent): void { diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts index 4884d54ffa8173f4cbe99a12cf49c6b379fbd155..7c9e2dcacfa6afaa02c0b150abc62d97cb8a95d0 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts @@ -13,7 +13,6 @@ import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; export const IUntitledTextEditorService = createDecorator('untitledTextEditorService'); @@ -110,9 +109,9 @@ export interface IUntitledTextEditorModelManager { * property is provided and the untitled editor exists, it will return that existing * instance instead of creating a new one. */ - resolve(options?: INewUntitledTextEditorOptions): Promise; - resolve(options?: INewUntitledTextEditorWithAssociatedResourceOptions): Promise; - resolve(options?: IExistingUntitledTextEditorOptions): Promise; + resolve(options?: INewUntitledTextEditorOptions): Promise; + resolve(options?: INewUntitledTextEditorWithAssociatedResourceOptions): Promise; + resolve(options?: IExistingUntitledTextEditorOptions): Promise; } export interface IUntitledTextEditorService extends IUntitledTextEditorModelManager { @@ -153,8 +152,11 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe return this.get(resource)?.textEditorModel?.getValue(); } - resolve(options?: IInternalUntitledTextEditorOptions): Promise { - return this.doCreateOrGet(options).load(); + async resolve(options?: IInternalUntitledTextEditorOptions): Promise { + const model = this.doCreateOrGet(options); + await model.resolve(); + + return model; } create(options?: IInternalUntitledTextEditorOptions): UntitledTextEditorModel { diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index c583b6a6454e8312a890e5107596ee559d4d0c9d..b80bebbb2ad707e5922ad625764590efec78f50b 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -62,7 +62,7 @@ suite('Untitled text editors', () => { const resourcePromise = awaitDidChangeDirty(accessor.untitledTextEditorService); - model.textEditorModel.setValue('foo bar'); + model.textEditorModel?.setValue('foo bar'); const resource = await resourcePromise; @@ -147,10 +147,10 @@ suite('Untitled text editors', () => { // dirty const model = await input.resolve(); - model.textEditorModel.setValue('foo bar'); + model.textEditorModel?.setValue('foo bar'); assert.ok(model.isDirty()); assert.ok(workingCopyService.isDirty(model.resource)); - model.textEditorModel.setValue(''); + model.textEditorModel?.setValue(''); assert.ok(!model.isDirty()); assert.ok(!workingCopyService.isDirty(model.resource)); input.dispose(); @@ -196,9 +196,9 @@ suite('Untitled text editors', () => { // dirty const model = await input.resolve(); - model.textEditorModel.setValue('foo bar'); + model.textEditorModel?.setValue('foo bar'); assert.ok(model.isDirty()); - model.textEditorModel.setValue(''); + model.textEditorModel?.setValue(''); assert.ok(model.isDirty()); input.dispose(); model.dispose(); @@ -345,7 +345,7 @@ suite('Untitled text editors', () => { // label const model = await input.resolve(); - model.textEditorModel.setValue('Foo Bar'); + model.textEditorModel?.setValue('Foo Bar'); assert.strictEqual(counter, 1); input.dispose(); model.dispose(); @@ -391,16 +391,16 @@ suite('Untitled text editors', () => { const model = await input.resolve(); model.onDidChangeContent(() => counter++); - model.textEditorModel.setValue('foo'); + model.textEditorModel?.setValue('foo'); assert.strictEqual(counter, 1, 'Dirty model should trigger event'); - model.textEditorModel.setValue('bar'); + model.textEditorModel?.setValue('bar'); assert.strictEqual(counter, 2, 'Content change when dirty should trigger event'); - model.textEditorModel.setValue(''); + model.textEditorModel?.setValue(''); assert.strictEqual(counter, 3, 'Manual revert should trigger event'); - model.textEditorModel.setValue('foo'); + model.textEditorModel?.setValue('foo'); assert.strictEqual(counter, 4, 'Dirty model should trigger event'); @@ -417,7 +417,7 @@ suite('Untitled text editors', () => { const model = await input.resolve(); model.onDidRevert(() => counter++); - model.textEditorModel.setValue('foo'); + model.textEditorModel?.setValue('foo'); await model.revert(); @@ -434,43 +434,43 @@ suite('Untitled text editors', () => { let model = await input.resolve(); model.onDidChangeName(() => counter++); - model.textEditorModel.setValue('foo'); + model.textEditorModel?.setValue('foo'); assert.strictEqual(input.getName(), 'foo'); assert.strictEqual(model.name, 'foo'); assert.strictEqual(counter, 1); - model.textEditorModel.setValue('bar'); + model.textEditorModel?.setValue('bar'); assert.strictEqual(input.getName(), 'bar'); assert.strictEqual(model.name, 'bar'); assert.strictEqual(counter, 2); - model.textEditorModel.setValue(''); + model.textEditorModel?.setValue(''); assert.strictEqual(input.getName(), 'Untitled-1'); assert.strictEqual(model.name, 'Untitled-1'); - model.textEditorModel.setValue(' '); + model.textEditorModel?.setValue(' '); assert.strictEqual(input.getName(), 'Untitled-1'); assert.strictEqual(model.name, 'Untitled-1'); - model.textEditorModel.setValue('([]}'); // require actual words + model.textEditorModel?.setValue('([]}'); // require actual words assert.strictEqual(input.getName(), 'Untitled-1'); assert.strictEqual(model.name, 'Untitled-1'); - model.textEditorModel.setValue('([]}hello '); // require actual words + model.textEditorModel?.setValue('([]}hello '); // require actual words assert.strictEqual(input.getName(), '([]}hello'); assert.strictEqual(model.name, '([]}hello'); - model.textEditorModel.setValue('12345678901234567890123456789012345678901234567890'); // trimmed at 40chars max + model.textEditorModel?.setValue('12345678901234567890123456789012345678901234567890'); // trimmed at 40chars max assert.strictEqual(input.getName(), '1234567890123456789012345678901234567890'); assert.strictEqual(model.name, '1234567890123456789012345678901234567890'); - model.textEditorModel.setValue('123456789012345678901234567890123456789🌞'); // do not break grapehems (#111235) + model.textEditorModel?.setValue('123456789012345678901234567890123456789🌞'); // do not break grapehems (#111235) assert.strictEqual(input.getName(), '123456789012345678901234567890123456789'); assert.strictEqual(model.name, '123456789012345678901234567890123456789'); assert.strictEqual(counter, 6); - model.textEditorModel.setValue('Hello\nWorld'); + model.textEditorModel?.setValue('Hello\nWorld'); assert.strictEqual(counter, 7); function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): IIdentifiedSingleEditOperation { @@ -489,7 +489,7 @@ suite('Untitled text editors', () => { }; } - model.textEditorModel.applyEdits([createSingleEditOp('hello', 2, 2)]); + model.textEditorModel?.applyEdits([createSingleEditOp('hello', 2, 2)]); assert.strictEqual(counter, 7); // change was not on first line input.dispose(); @@ -513,10 +513,10 @@ suite('Untitled text editors', () => { const model = await input.resolve(); model.onDidChangeDirty(() => counter++); - model.textEditorModel.setValue('foo'); + model.textEditorModel?.setValue('foo'); assert.strictEqual(counter, 1, 'Dirty model should trigger event'); - model.textEditorModel.setValue('bar'); + model.textEditorModel?.setValue('bar'); assert.strictEqual(counter, 1, 'Another change does not fire event'); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts index 5e82f847f07884355f8ed8836870c594ea8dd64b..41c546de3fca059a1c586a07674600e7244e3a1a 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts @@ -146,7 +146,7 @@ suite('WorkingCopyFileService', () => { let dirty = accessor.workingCopyFileService.getDirty(model1.resource); assert.strictEqual(dirty.length, 0); - await model1.load(); + await model1.resolve(); model1.textEditorModel!.setValue('foo'); dirty = accessor.workingCopyFileService.getDirty(model1.resource); @@ -157,7 +157,7 @@ suite('WorkingCopyFileService', () => { assert.strictEqual(dirty.length, 1); assert.strictEqual(dirty[0], model1); - await model2.load(); + await model2.resolve(); model2.textEditorModel!.setValue('bar'); dirty = accessor.workingCopyFileService.getDirty(toResource.call(this, '/path')); @@ -170,7 +170,7 @@ suite('WorkingCopyFileService', () => { test('registerWorkingCopyProvider', async function () { const model1 = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-1.txt'), 'utf8', undefined); (accessor.textFileService.files).add(model1.resource, model1); - await model1.load(); + await model1.resolve(); model1.textEditorModel!.setValue('foo'); const testWorkingCopy = new TestWorkingCopy(toResource.call(this, '/path/file-2.txt'), true); @@ -327,11 +327,11 @@ suite('WorkingCopyFileService', () => { (accessor.textFileService.files).add(sourceModel.resource, sourceModel); (accessor.textFileService.files).add(targetModel.resource, targetModel); - await sourceModel.load(); + await sourceModel.resolve(); sourceModel.textEditorModel!.setValue('foo' + i); assert.ok(accessor.textFileService.isDirty(sourceModel.resource)); if (targetDirty) { - await targetModel.load(); + await targetModel.resolve(); targetModel.textEditorModel!.setValue('bar' + i); assert.ok(accessor.textFileService.isDirty(targetModel.resource)); } @@ -420,7 +420,7 @@ suite('WorkingCopyFileService', () => { const model = instantiationService.createInstance(TextFileEditorModel, resource, 'utf8', undefined); (accessor.textFileService.files).add(model.resource, model); - await model.load(); + await model.resolve(); model!.textEditorModel!.setValue('foo'); assert.ok(accessor.workingCopyService.isDirty(model.resource)); return model; @@ -480,7 +480,7 @@ suite('WorkingCopyFileService', () => { const model = instantiationService.createInstance(TextFileEditorModel, resource, 'utf8', undefined); (accessor.textFileService.files).add(model.resource, model); - await model.load(); + await model.resolve(); model!.textEditorModel!.setValue('foo'); assert.ok(accessor.workingCopyService.isDirty(model.resource)); diff --git a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts index b44d01511ea853dff1322ab5a97b9425fa767233..a739759a32d32c7359a51e72741f3be15cbed6f0 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts @@ -72,23 +72,21 @@ suite('Workbench editor model', () => { counter++; }); - const resolvedModel = await model.load(); - assert(resolvedModel === model); - assert.strictEqual(resolvedModel.isDisposed(), false); + await model.resolve(); + assert.strictEqual(model.isDisposed(), false); assert.strictEqual(model.isResolved(), true); model.dispose(); assert.strictEqual(counter, 1); - assert.strictEqual(resolvedModel.isDisposed(), true); + assert.strictEqual(model.isDisposed(), true); }); test('BaseTextEditorModel', async () => { let modelService = stubModelService(instantiationService); const model = new MyTextEditorModel(modelService, modeService); - const resolvedModel = await model.load() as MyTextEditorModel; + await model.resolve(); - assert(resolvedModel === model); - resolvedModel.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain'); + model.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain'); assert.strictEqual(model.isResolved(), true); model.dispose(); });