Revert "Merge from vscode ada4bddb8edc69eea6ebaaa0e88c5f903cbd43d8 (#5529)" (#5553)

This reverts commit 5d44b6a6a7.
This commit is contained in:
Anthony Dresser
2019-05-20 17:07:32 -07:00
committed by GitHub
parent 1315b8e42a
commit c9a4f8f664
325 changed files with 3332 additions and 4501 deletions

View File

@@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import { guessMimeTypes } from 'vs/base/common/mime';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { URI } from 'vs/base/common/uri';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
@@ -18,12 +18,13 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RunOnceScheduler, timeout } from 'vs/base/common/async';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { hash } from 'vs/base/common/hash';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { isLinux } from 'vs/base/common/platform';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -32,13 +33,6 @@ import { isEqual, isEqualOrParent, extname, basename } from 'vs/base/common/reso
import { onUnexpectedError } from 'vs/base/common/errors';
import { Schemas } from 'vs/base/common/network';
export interface IBackupMetaData {
mtime: number;
size: number;
etag: string;
orphaned: boolean;
}
/**
* The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk.
*/
@@ -63,16 +57,16 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
private resource: URI;
private contentEncoding: string; // encoding as reported from disk
private preferredEncoding: string; // encoding as chosen by the user
private preferredMode: string; // mode as chosen by the user
private contentEncoding: string; // encoding as reported from disk
private preferredEncoding: string; // encoding as chosen by the user
private versionId: number;
private bufferSavedVersionId: number;
private blockModelContentChange: boolean;
private lastResolvedFileStat: IFileStatWithMetadata;
private createTextEditorModelPromise: Promise<TextFileEditorModel> | null;
private lastResolvedDiskStat: IFileStatWithMetadata;
private autoSaveAfterMillies?: number;
private autoSaveAfterMilliesEnabled: boolean;
@@ -94,7 +88,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
constructor(
resource: URI,
preferredEncoding: string,
preferredMode: string,
@INotificationService private readonly notificationService: INotificationService,
@IModeService modeService: IModeService,
@IModelService modelService: IModelService,
@@ -111,7 +104,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.resource = resource;
this.preferredEncoding = preferredEncoding;
this.preferredMode = preferredMode;
this.inOrphanMode = false;
this.dirty = false;
this.versionId = 0;
@@ -207,40 +199,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
private onFilesAssociationChange(): void {
if (!this.isResolved()) {
if (!this.textEditorModel) {
return;
}
const firstLineText = this.getFirstLineText(this.textEditorModel);
const languageSelection = this.getOrCreateMode(this.resource, this.modeService, this.preferredMode, firstLineText);
const languageSelection = this.getOrCreateMode(this.modeService, undefined, firstLineText);
this.modelService.setMode(this.textEditorModel, languageSelection);
}
setMode(mode: string): void {
super.setMode(mode);
this.preferredMode = mode;
}
async backup(target = this.resource): Promise<void> {
if (this.isResolved()) {
// Only fill in model metadata if resource matches
let meta: IBackupMetaData | undefined = undefined;
if (isEqual(target, this.resource) && this.lastResolvedFileStat) {
meta = {
mtime: this.lastResolvedFileStat.mtime,
size: this.lastResolvedFileStat.size,
etag: this.lastResolvedFileStat.etag,
orphaned: this.inOrphanMode
};
}
return this.backupFileService.backupResource<IBackupMetaData>(target, this.createSnapshot(), this.versionId, meta);
}
return Promise.resolve();
getVersionId(): number {
return this.versionId;
}
async revert(soft?: boolean): Promise<void> {
@@ -275,7 +245,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
}
async load(options?: ILoadOptions): Promise<ITextFileEditorModel> {
load(options?: ILoadOptions): Promise<ITextFileEditorModel> {
this.logService.trace('load() - enter', this.resource);
// It is very important to not reload the model when the model is dirty.
@@ -284,57 +254,44 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
if (this.dirty || this.saveSequentializer.hasPendingSave()) {
this.logService.trace('load() - exit - without loading because model is dirty or being saved', this.resource);
return this;
return Promise.resolve(this);
}
// Only for new models we support to load from backup
if (!this.isResolved()) {
const backup = await this.backupFileService.loadBackupResource(this.resource);
if (this.isResolved()) {
return this; // Make sure meanwhile someone else did not suceed in loading
}
if (backup) {
try {
return await this.loadFromBackup(backup, options);
} catch (error) {
// ignore error and continue to load as file below
}
}
if (!this.textEditorModel && !this.createTextEditorModelPromise) {
return this.loadFromBackup(options);
}
// Otherwise load from file resource
return this.loadFromFile(options);
}
private async loadFromBackup(backup: URI, options?: ILoadOptions): Promise<TextFileEditorModel> {
private async loadFromBackup(options?: ILoadOptions): Promise<TextFileEditorModel> {
const backup = await this.backupFileService.loadBackupResource(this.resource);
// Resolve actual backup contents
const resolvedBackup = await this.backupFileService.resolveBackupContent<IBackupMetaData>(backup);
if (this.isResolved()) {
return this; // Make sure meanwhile someone else did not suceed in loading
// Make sure meanwhile someone else did not suceed or start loading
if (this.createTextEditorModelPromise || this.textEditorModel) {
return this.createTextEditorModelPromise || this;
}
// Load with backup
this.loadFromContent({
resource: this.resource,
name: basename(this.resource),
mtime: resolvedBackup.meta ? resolvedBackup.meta.mtime : Date.now(),
size: resolvedBackup.meta ? resolvedBackup.meta.size : 0,
etag: resolvedBackup.meta ? resolvedBackup.meta.etag : ETAG_DISABLED, // etag disabled if unknown!
value: resolvedBackup.value,
encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding,
isReadonly: false
}, options, true /* from backup */);
// If we have a backup, continue loading with it
if (!!backup) {
const content: ITextFileStreamContent = {
resource: this.resource,
name: basename(this.resource),
mtime: Date.now(),
size: 0,
etag: ETAG_DISABLED, // always allow to save content restored from a backup (see https://github.com/Microsoft/vscode/issues/72343)
value: createTextBufferFactory(''), // will be filled later from backup
encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding,
isReadonly: false
};
// Restore orphaned flag based on state
if (resolvedBackup.meta && resolvedBackup.meta.orphaned) {
this.setOrphaned(true);
return this.loadWithContent(content, options, backup);
}
return this;
// Otherwise load from file
return this.loadFromFile(options);
}
private async loadFromFile(options?: ILoadOptions): Promise<TextFileEditorModel> {
@@ -345,8 +302,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
let etag: string | undefined;
if (forceReadFromDisk) {
etag = ETAG_DISABLED; // disable ETag if we enforce to read from disk
} else if (this.lastResolvedFileStat) {
etag = this.lastResolvedFileStat.etag; // otherwise respect etag to support caching
} else if (this.lastResolvedDiskStat) {
etag = this.lastResolvedDiskStat.etag; // otherwise respect etag to support caching
}
// Ensure to track the versionId before doing a long running operation
@@ -364,11 +321,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Clear orphaned state when loading was successful
this.setOrphaned(false);
if (currentVersionId !== this.versionId) {
return this; // Make sure meanwhile someone else did not suceed loading
// Guard against the model having changed in the meantime
if (currentVersionId === this.versionId) {
return this.loadWithContent(content, options);
}
return this.loadFromContent(content, options);
return this;
} catch (error) {
const result = error.fileOperationResult;
@@ -398,11 +356,37 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
}
private loadFromContent(content: ITextFileStreamContent, options?: ILoadOptions, fromBackup?: boolean): TextFileEditorModel {
private async loadWithContent(content: ITextFileStreamContent, options?: ILoadOptions, backup?: URI): Promise<TextFileEditorModel> {
const model = await this.doLoadWithContent(content, backup);
// Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype
const settingsType = this.getTypeIfSettings();
if (settingsType) {
/* __GDPR__
"settingsRead" : {
"settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data
} else {
/* __GDPR__
"fileGet" : {
"${include}": [
"${FileTelemetryData}"
]
}
*/
this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER));
}
return model;
}
private doLoadWithContent(content: ITextFileStreamContent, backup?: URI): Promise<TextFileEditorModel> {
this.logService.trace('load() - resolved content', this.resource);
// Update our resolved disk stat model
this.updateLastResolvedFileStat({
this.updateLastResolvedDiskStat({
resource: this.resource,
name: content.name,
mtime: content.mtime,
@@ -425,61 +409,21 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
// Update Existing Model
if (this.isResolved()) {
if (this.textEditorModel) {
this.doUpdateTextModel(content.value);
return Promise.resolve(this);
}
// Join an existing request to create the editor model to avoid race conditions
else if (this.createTextEditorModelPromise) {
this.logService.trace('load() - join existing text editor model promise', this.resource);
return this.createTextEditorModelPromise;
}
// Create New Model
else {
this.doCreateTextModel(content.resource, content.value, !!fromBackup);
}
// Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype
const settingsType = this.getTypeIfSettings();
if (settingsType) {
/* __GDPR__
"settingsRead" : {
"settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data
} else {
/* __GDPR__
"fileGet" : {
"${include}": [
"${FileTelemetryData}"
]
}
*/
this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER));
}
return this;
}
private doCreateTextModel(resource: URI, value: ITextBufferFactory, fromBackup: boolean): void {
this.logService.trace('load() - created text editor model', this.resource);
// Create model
this.createTextEditorModel(value, resource, this.preferredMode);
// We restored a backup so we have to set the model as being dirty
// We also want to trigger auto save if it is enabled to simulate the exact same behaviour
// you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977)
if (fromBackup) {
this.makeDirty();
if (this.autoSaveAfterMilliesEnabled) {
this.doAutoSave(this.versionId);
}
}
// Ensure we are not tracking a stale state
else {
this.setDirty(false);
}
// Model Listeners
this.installModelListeners();
return this.doCreateTextModel(content.resource, content.value, backup);
}
private doUpdateTextModel(value: ITextBufferFactory): void {
@@ -491,7 +435,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Update model value in a block that ignores model content change events
this.blockModelContentChange = true;
try {
this.updateTextEditorModel(value, this.preferredMode);
this.updateTextEditorModel(value);
} finally {
this.blockModelContentChange = false;
}
@@ -500,6 +444,44 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.updateSavedVersionId();
}
private doCreateTextModel(resource: URI, value: ITextBufferFactory, backup: URI | undefined): Promise<TextFileEditorModel> {
this.logService.trace('load() - created text editor model', this.resource);
this.createTextEditorModelPromise = this.doLoadBackup(backup).then(backupContent => {
this.createTextEditorModelPromise = null;
// Create model
const hasBackupContent = !!backupContent;
this.createTextEditorModel(backupContent ? backupContent : value, resource);
// We restored a backup so we have to set the model as being dirty
// We also want to trigger auto save if it is enabled to simulate the exact same behaviour
// you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977)
if (hasBackupContent) {
this.makeDirty();
if (this.autoSaveAfterMilliesEnabled) {
this.doAutoSave(this.versionId);
}
}
// Ensure we are not tracking a stale state
else {
this.setDirty(false);
}
// Model Listeners
this.installModelListeners();
return this;
}, error => {
this.createTextEditorModelPromise = null;
return Promise.reject<TextFileEditorModel>(error);
});
return this.createTextEditorModelPromise;
}
private installModelListeners(): void {
// See https://github.com/Microsoft/vscode/issues/30189
@@ -507,11 +489,27 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// where `value` was captured in the content change listener closure scope.
// Content Change
if (this.isResolved()) {
if (this.textEditorModel) {
this._register(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged()));
}
}
private async doLoadBackup(backup: URI | undefined): Promise<ITextBufferFactory | null> {
if (!backup) {
return null;
}
try {
return withUndefinedAsNull(await this.backupFileService.resolveBackupContent(backup));
} catch (error) {
return null; // ignore errors
}
}
protected getOrCreateMode(modeService: IModeService, preferredModeIds: string | undefined, firstLineText?: string): ILanguageSelection {
return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText);
}
private onModelContentChanged(): void {
this.logService.trace(`onModelContentChanged() - enter`, this.resource);
@@ -528,7 +526,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// In this case we clear the dirty flag and emit a SAVED event to indicate this state.
// Note: we currently only do this check when auto-save is turned off because there you see
// a dirty indicator that you want to get rid of when undoing to the saved version.
if (!this.autoSaveAfterMilliesEnabled && this.isResolved() && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) {
if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) {
this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource);
// Clear flags
@@ -659,7 +657,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Push all edit operations to the undo stack so that the user has a chance to
// Ctrl+Z back to the saved version. We only do this when auto-save is turned off
if (!this.autoSaveAfterMilliesEnabled && this.isResolved()) {
if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel) {
this.textEditorModel.pushStackElement();
}
@@ -689,12 +687,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// saving contents to disk that are stale (see https://github.com/Microsoft/vscode/issues/50942).
// To fix this issue, we will not store the contents to disk when we got disposed.
if (this.disposed) {
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
}
// We require a resolved model from this point on, since we are about to write data to disk.
if (!this.isResolved()) {
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
return undefined;
}
// Under certain conditions we do a short-cut of flushing contents to disk when we can assume that
@@ -720,12 +713,16 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Save to Disk
// mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering)
this.logService.trace(`doSave(${versionId}) - before write()`, this.resource);
return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), {
const snapshot = this.createSnapshot();
if (!snapshot) {
throw new Error('Invalid snapshot');
}
return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, {
overwriteReadonly: options.overwriteReadonly,
overwriteEncoding: options.overwriteEncoding,
mtime: this.lastResolvedFileStat.mtime,
mtime: this.lastResolvedDiskStat.mtime,
encoding: this.getEncoding(),
etag: this.lastResolvedFileStat.etag,
etag: this.lastResolvedDiskStat.etag,
writeElevated: options.writeElevated
}).then(stat => {
this.logService.trace(`doSave(${versionId}) - after write()`, this.resource);
@@ -739,7 +736,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
// Updated resolved stat with updated stat
this.updateLastResolvedFileStat(stat);
this.updateLastResolvedDiskStat(stat);
// Cancel any content change event promises as they are no longer valid
this.contentChangeEventScheduler.cancel();
@@ -855,22 +852,19 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
private doTouch(versionId: number): Promise<void> {
if (!this.isResolved()) {
return Promise.resolve();
const snapshot = this.createSnapshot();
if (!snapshot) {
throw new Error('invalid snapshot');
}
return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), {
mtime: this.lastResolvedFileStat.mtime,
return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, {
mtime: this.lastResolvedDiskStat.mtime,
encoding: this.getEncoding(),
etag: this.lastResolvedFileStat.etag
etag: this.lastResolvedDiskStat.etag
}).then(stat => {
// Updated resolved stat with updated stat since touching it might have changed mtime
this.updateLastResolvedFileStat(stat);
// Emit File Saved Event
this._onDidStateChange.fire(StateChange.SAVED);
this.updateLastResolvedDiskStat(stat);
}, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */));
}
@@ -904,23 +898,23 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// in order to find out if the model changed back to a saved version (e.g.
// when undoing long enough to reach to a version that is saved and then to
// clear the dirty flag)
if (this.isResolved()) {
if (this.textEditorModel) {
this.bufferSavedVersionId = this.textEditorModel.getAlternativeVersionId();
}
}
private updateLastResolvedFileStat(newFileStat: IFileStatWithMetadata): void {
private updateLastResolvedDiskStat(newVersionOnDiskStat: IFileStatWithMetadata): void {
// First resolve - just take
if (!this.lastResolvedFileStat) {
this.lastResolvedFileStat = newFileStat;
if (!this.lastResolvedDiskStat) {
this.lastResolvedDiskStat = newVersionOnDiskStat;
}
// 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
// was called, the mtime could be out of sync.
else if (this.lastResolvedFileStat.mtime <= newFileStat.mtime) {
this.lastResolvedFileStat = newFileStat;
else if (this.lastResolvedDiskStat.mtime <= newVersionOnDiskStat.mtime) {
this.lastResolvedDiskStat = newVersionOnDiskStat;
}
}
@@ -943,6 +937,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return this.lastSaveAttemptTime;
}
getETag(): string | null {
return this.lastResolvedDiskStat ? this.lastResolvedDiskStat.etag || null : null;
}
hasState(state: ModelState): boolean {
switch (state) {
case ModelState.CONFLICT:
@@ -1024,12 +1022,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return true;
}
isResolved(): boolean { // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
return !!this.textEditorModel;
isResolved(): boolean {
return !isUndefinedOrNull(this.lastResolvedDiskStat);
}
isReadonly(): boolean {
return !!(this.lastResolvedFileStat && this.lastResolvedFileStat.isReadonly);
return !!(this.lastResolvedDiskStat && this.lastResolvedDiskStat.isReadonly);
}
isDisposed(): boolean {
@@ -1041,7 +1039,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
getStat(): IFileStatWithMetadata {
return this.lastResolvedFileStat;
return this.lastResolvedDiskStat;
}
dispose(): void {
@@ -1050,6 +1048,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.inOrphanMode = false;
this.inErrorMode = false;
this.createTextEditorModelPromise = null;
this.cancelPendingAutoSave();
super.dispose();

View File

@@ -153,7 +153,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
// Model does not exist
else {
const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined, options ? options.mode : undefined);
const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined);
modelPromise = model.load(options);
// Install state change listener
@@ -204,11 +204,6 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
// Remove from pending loads
this.mapResourceToPendingModelLoaders.delete(resource);
// Apply mode if provided
if (options && options.mode) {
resolvedModel.setMode(options.mode);
}
return resolvedModel;
} catch (error) {

View File

@@ -40,7 +40,6 @@ import { trim } from 'vs/base/common/strings';
import { VSBuffer } from 'vs/base/common/buffer';
import { ITextSnapshot } from 'vs/editor/common/model';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
/**
* The workbench file service implementation implements the raw file service spec and adds additional methods on top.
@@ -239,44 +238,59 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
private async doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise<void> {
// Handle file resources first
await Promise.all(dirtyFileModels.map(model => model.backup()));
await Promise.all(dirtyFileModels.map(async model => {
const snapshot = model.createSnapshot();
if (snapshot) {
await this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId());
}
}));
// Handle untitled resources
await Promise.all(untitledResources
const untitledModelPromises = untitledResources
.filter(untitled => this.untitledEditorService.exists(untitled))
.map(async untitled => (await this.untitledEditorService.loadOrCreate({ resource: untitled })).backup()));
.map(untitled => this.untitledEditorService.loadOrCreate({ resource: untitled }));
const untitledModels = await Promise.all(untitledModelPromises);
await Promise.all(untitledModels.map(async model => {
const snapshot = model.createSnapshot();
if (snapshot) {
await this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId());
}
}));
}
private async confirmBeforeShutdown(): Promise<boolean> {
const confirm = await this.confirmSave();
private confirmBeforeShutdown(): boolean | Promise<boolean> {
return this.confirmSave().then(confirm => {
// Save
if (confirm === ConfirmResult.SAVE) {
const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true });
// Save
if (confirm === ConfirmResult.SAVE) {
return this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }).then(result => {
if (result.results.some(r => !r.success)) {
return true; // veto if some saves failed
}
if (result.results.some(r => !r.success)) {
return true; // veto if some saves failed
return this.noVeto({ cleanUpBackups: true });
});
}
return this.noVeto({ cleanUpBackups: true });
}
// Don't Save
else if (confirm === ConfirmResult.DONT_SAVE) {
// Don't Save
else if (confirm === ConfirmResult.DONT_SAVE) {
// Make sure to revert untitled so that they do not restore
// see https://github.com/Microsoft/vscode/issues/29572
this.untitledEditorService.revertAll();
// Make sure to revert untitled so that they do not restore
// see https://github.com/Microsoft/vscode/issues/29572
this.untitledEditorService.revertAll();
return this.noVeto({ cleanUpBackups: true });
}
return this.noVeto({ cleanUpBackups: true });
}
// Cancel
else if (confirm === ConfirmResult.CANCEL) {
return true; // veto
}
// Cancel
else if (confirm === ConfirmResult.CANCEL) {
return true; // veto
}
return false;
return false;
});
}
private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise<boolean> {
@@ -489,7 +503,10 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
dirtyTargetModelUris.push(targetModelResource);
// Backup dirty source model to the target resource it will become later
await sourceModel.backup(targetModelResource);
const snapshot = sourceModel.createSnapshot();
if (snapshot) {
await this.backupFileService.backupResource(targetModelResource, snapshot, sourceModel.getVersionId());
}
}));
}
@@ -740,14 +757,14 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
private getFileModels(arg1?: URI | URI[]): ITextFileEditorModel[] {
if (Array.isArray(arg1)) {
const models: ITextFileEditorModel[] = [];
arg1.forEach(resource => {
(<URI[]>arg1).forEach(resource => {
models.push(...this.getFileModels(resource));
});
return models;
}
return this._models.getAll(arg1);
return this._models.getAll(<URI>arg1);
}
private getDirtyFileModels(resources?: URI | URI[]): ITextFileEditorModel[] {
@@ -856,12 +873,17 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
// take over encoding, mode and model value from source model
targetModel.updatePreferredEncoding(sourceModel.getEncoding());
if (sourceModel.isResolved() && targetModel.isResolved()) {
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()));
if (targetModel.textEditorModel) {
const snapshot = sourceModel.createSnapshot();
if (snapshot) {
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(snapshot));
}
const mode = sourceModel.textEditorModel.getLanguageIdentifier();
if (mode.language !== PLAINTEXT_MODE_ID) {
targetModel.textEditorModel.setMode(mode); // only use if more specific than plain/text
if (sourceModel.textEditorModel) {
const language = sourceModel.textEditorModel.getLanguageIdentifier();
if (language.id > 1) {
targetModel.textEditorModel.setMode(language); // only use if more specific than plain/text
}
}
}

View File

@@ -6,7 +6,7 @@
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor';
import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor';
import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
@@ -367,11 +367,6 @@ export interface IModelLoadOrCreateOptions {
*/
reason?: LoadReason;
/**
* The language mode to use for the model text content.
*/
mode?: string;
/**
* The encoding to use when resolving the model text content.
*/
@@ -448,15 +443,19 @@ export interface ILoadOptions {
reason?: LoadReason;
}
export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport {
export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport {
readonly onDidContentChange: Event<StateChange>;
readonly onDidStateChange: Event<StateChange>;
getVersionId(): number;
getResource(): URI;
hasState(state: ModelState): boolean;
getETag(): string | null;
updatePreferredEncoding(encoding: string): void;
save(options?: ISaveOptions): Promise<void>;
@@ -465,17 +464,16 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport
revert(soft?: boolean): Promise<void>;
backup(target?: URI): Promise<void>;
createSnapshot(): ITextSnapshot | null;
isDirty(): boolean;
isResolved(): this is IResolvedTextFileEditorModel;
isResolved(): boolean;
isDisposed(): boolean;
}
export interface IResolvedTextFileEditorModel extends ITextFileEditorModel {
readonly textEditorModel: ITextModel;
createSnapshot(): ITextSnapshot;

View File

@@ -11,7 +11,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { URI } from 'vs/base/common/uri';
import { IFileStatWithMetadata, ICreateFileOptions, FileOperationError, FileOperationResult, IFileStreamContent, IFileService } from 'vs/platform/files/common/files';
import { Schemas } from 'vs/base/common/network';
import { exists, stat, chmod, rimraf, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs';
import { exists, stat, chmod, rimraf } from 'vs/base/node/pfs';
import { join, dirname } from 'vs/base/common/path';
import { isMacintosh, isLinux } from 'vs/base/common/platform';
import product from 'vs/platform/product/node/product';
@@ -26,6 +26,7 @@ import { VSBufferReadable, VSBuffer, VSBufferReadableStream } from 'vs/base/comm
import { Readable } from 'stream';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/fileConstants';
import { ITextSnapshot } from 'vs/editor/common/model';
export class NodeTextFileService extends TextFileService {

View File

@@ -17,7 +17,7 @@ import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiatio
export class TextResourcePropertiesService implements ITextResourcePropertiesService {
_serviceBrand: ServiceIdentifier<ITextResourcePropertiesService>;
_serviceBrand: ServiceIdentifier<any>;
private remoteEnvironment: IRemoteAgentEnvironment | null = null;

View File

@@ -14,7 +14,6 @@ import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/commo
import { FileOperationResult, FileOperationError, IFileService } from 'vs/platform/files/common/files';
import { IModelService } from 'vs/editor/common/services/modelService';
import { timeout } from 'vs/base/common/async';
import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
class ServiceAccessor {
constructor(@ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService, @IFileService public fileService: TestFileService) {
@@ -45,53 +44,25 @@ suite('Files - TextFileEditorModel', () => {
accessor.fileService.setContent(content);
});
test('save', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
test('Save', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
await model.load();
model.textEditorModel!.setValue('bar');
assert.ok(getLastModifiedTime(model) <= Date.now());
let savedEvent = false;
model.onDidStateChange(e => {
if (e === StateChange.SAVED) {
savedEvent = true;
}
});
await model.save();
assert.ok(model.getLastSaveAttemptTime() <= Date.now());
assert.ok(!model.isDirty());
assert.ok(savedEvent);
model.dispose();
assert.ok(!accessor.modelService.getModel(model.getResource()));
});
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();
let savedEvent = false;
model.onDidStateChange(e => {
if (e === StateChange.SAVED) {
savedEvent = true;
}
});
await model.save({ force: true });
assert.ok(savedEvent);
model.dispose();
assert.ok(!accessor.modelService.getModel(model.getResource()));
});
test('setEncoding - encode', function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
model.setEncoding('utf8', EncodingMode.Encode); // no-op
assert.equal(getLastModifiedTime(model), -1);
@@ -104,7 +75,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('setEncoding - decode', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
model.setEncoding('utf16', EncodingMode.Decode);
@@ -113,24 +84,8 @@ suite('Files - TextFileEditorModel', () => {
model.dispose();
});
test('create with mode', async function () {
const mode = 'text-file-model-test';
ModesRegistry.registerLanguage({
id: mode,
});
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', mode);
await model.load();
assert.equal(model.textEditorModel!.getModeId(), mode);
model.dispose();
assert.ok(!accessor.modelService.getModel(model.getResource()));
});
test('disposes when underlying model is destroyed', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
await model.load();
@@ -139,7 +94,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('Load does not trigger save', async function () {
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined);
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8');
assert.ok(model.hasState(ModelState.SAVED));
model.onDidStateChange(e => {
@@ -153,7 +108,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('Load 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);
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
await model.load();
model.textEditorModel!.setValue('foo');
@@ -168,7 +123,7 @@ suite('Files - TextFileEditorModel', () => {
test('Revert', async function () {
let eventCounter = 0;
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
model.onDidStateChange(e => {
if (e === StateChange.REVERTED) {
@@ -190,7 +145,7 @@ suite('Files - TextFileEditorModel', () => {
test('Revert (soft)', async function () {
let eventCounter = 0;
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
model.onDidStateChange(e => {
if (e === StateChange.REVERTED) {
@@ -210,7 +165,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('Load and undo turns model dirty', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
await model.load();
accessor.fileService.setContent('Hello Change');
@@ -220,7 +175,7 @@ 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);
let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
await model.load();
@@ -235,7 +190,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('Load 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);
let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
await model.load();
accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_FOUND));
@@ -281,7 +236,7 @@ suite('Files - TextFileEditorModel', () => {
test('Save Participant', async function () {
let eventCounter = 0;
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
model.onDidStateChange(e => {
if (e === StateChange.SAVED) {
@@ -311,7 +266,7 @@ suite('Files - TextFileEditorModel', () => {
test('Save Participant, async participant', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
TextFileEditorModel.setSaveParticipant({
participate: (model) => {
@@ -329,7 +284,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('Save Participant, bad participant', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
TextFileEditorModel.setSaveParticipant({
participate: (model) => {

View File

@@ -13,7 +13,6 @@ import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/file
import { IModelService } from 'vs/editor/common/services/modelService';
import { timeout } from 'vs/base/common/async';
import { toResource } from 'vs/base/test/common/utils';
import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
export class TestTextFileEditorModelManager extends TextFileEditorModelManager {
@@ -43,9 +42,9 @@ suite('Files - TextFileEditorModelManager', () => {
test('add, remove, clear, get, getAll', function () {
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined);
const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined);
const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8', undefined);
const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8');
const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8');
const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8');
manager.add(URI.file('/test.html'), model1);
manager.add(URI.file('/some/other.html'), model2);
@@ -118,9 +117,9 @@ suite('Files - TextFileEditorModelManager', () => {
test('removed from cache when model disposed', function () {
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined);
const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined);
const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8', undefined);
const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8');
const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8');
const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8');
manager.add(URI.file('/test.html'), model1);
manager.add(URI.file('/some/other.html'), model2);
@@ -291,24 +290,4 @@ suite('Files - TextFileEditorModelManager', () => {
assert.ok(model.isDisposed());
manager.dispose();
});
test('mode', async function () {
const mode = 'text-file-model-manager-test';
ModesRegistry.registerLanguage({
id: mode,
});
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
const resource = toResource.call(this, '/path/index_something.txt');
let model = await manager.loadOrCreate(resource, { mode });
assert.equal(model.textEditorModel!.getModeId(), mode);
model = await manager.loadOrCreate(resource, { mode: 'text' });
assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID);
manager.disposeModel((model as TextFileEditorModel));
manager.dispose();
});
});

View File

@@ -65,8 +65,8 @@ suite('Files - TextFileService', () => {
accessor.untitledEditorService.revertAll();
});
test('confirm onWillShutdown - no veto', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
test('confirm onWillShutdown - no veto', function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const event = new BeforeShutdownEventImpl();
@@ -76,12 +76,14 @@ suite('Files - TextFileService', () => {
if (typeof veto === 'boolean') {
assert.ok(!veto);
} else {
assert.ok(!(await veto));
veto.then(veto => {
assert.ok(!veto);
});
}
});
test('confirm onWillShutdown - veto if user cancels', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -97,7 +99,7 @@ suite('Files - TextFileService', () => {
});
test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -123,7 +125,7 @@ suite('Files - TextFileService', () => {
});
test('confirm onWillShutdown - save (hot.exit: off)', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -142,7 +144,7 @@ suite('Files - TextFileService', () => {
});
test('isDirty/getDirty - files and untitled', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -169,7 +171,7 @@ suite('Files - TextFileService', () => {
});
test('save - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -185,11 +187,11 @@ suite('Files - TextFileService', () => {
test('save - UNC path', async function () {
const untitledUncUri = URI.from({ scheme: 'untitled', authority: 'server', path: '/share/path/file.txt' });
model = instantiationService.createInstance(TextFileEditorModel, untitledUncUri, 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, untitledUncUri, 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const mockedFileUri = untitledUncUri.with({ scheme: Schemas.file });
const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8', undefined);
const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8');
const loadOrCreateStub = sinon.stub(accessor.textFileService.models, 'loadOrCreate', () => Promise.resolve(mockedEditorInput));
sinon.stub(accessor.untitledEditorService, 'exists', () => true);
@@ -209,7 +211,7 @@ suite('Files - TextFileService', () => {
});
test('saveAll - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -226,7 +228,7 @@ suite('Files - TextFileService', () => {
});
test('saveAs - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -242,7 +244,7 @@ suite('Files - TextFileService', () => {
});
test('revert - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -258,7 +260,7 @@ suite('Files - TextFileService', () => {
});
test('delete - dirty file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -272,8 +274,8 @@ suite('Files - TextFileService', () => {
});
test('move - dirty file', async function () {
let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target.txt'), 'utf8', undefined);
let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(sourceModel.getResource(), sourceModel);
(<TextFileEditorModelManager>accessor.textFileService.models).add(targetModel.getResource(), targetModel);
@@ -395,7 +397,7 @@ suite('Files - TextFileService', () => {
});
async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: true, shouldVeto: boolean): Promise<void> {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;