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

This reverts commit c9a4f8f664.
This commit is contained in:
Anthony Dresser
2019-05-21 14:19:32 -07:00
committed by GitHub
parent 7670104e4d
commit 81ae86ff79
325 changed files with 4497 additions and 3328 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, withUndefinedAsNull } from 'vs/base/common/types';
import { isUndefinedOrNull } 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,13 +18,12 @@ 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, ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModeService } 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';
@@ -33,6 +32,13 @@ 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.
*/
@@ -57,16 +63,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 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 versionId: number;
private bufferSavedVersionId: number;
private blockModelContentChange: boolean;
private createTextEditorModelPromise: Promise<TextFileEditorModel> | null;
private lastResolvedDiskStat: IFileStatWithMetadata;
private lastResolvedFileStat: IFileStatWithMetadata;
private autoSaveAfterMillies?: number;
private autoSaveAfterMilliesEnabled: boolean;
@@ -88,6 +94,7 @@ 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,
@@ -104,6 +111,7 @@ 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;
@@ -199,18 +207,40 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
private onFilesAssociationChange(): void {
if (!this.textEditorModel) {
if (!this.isResolved()) {
return;
}
const firstLineText = this.getFirstLineText(this.textEditorModel);
const languageSelection = this.getOrCreateMode(this.modeService, undefined, firstLineText);
const languageSelection = this.getOrCreateMode(this.resource, this.modeService, this.preferredMode, firstLineText);
this.modelService.setMode(this.textEditorModel, languageSelection);
}
getVersionId(): number {
return this.versionId;
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();
}
async revert(soft?: boolean): Promise<void> {
@@ -245,7 +275,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
}
load(options?: ILoadOptions): Promise<ITextFileEditorModel> {
async 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.
@@ -254,44 +284,57 @@ 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 Promise.resolve(this);
return this;
}
// Only for new models we support to load from backup
if (!this.textEditorModel && !this.createTextEditorModelPromise) {
return this.loadFromBackup(options);
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
}
}
}
// Otherwise load from file resource
return this.loadFromFile(options);
}
private async loadFromBackup(options?: ILoadOptions): Promise<TextFileEditorModel> {
const backup = await this.backupFileService.loadBackupResource(this.resource);
private async loadFromBackup(backup: URI, options?: ILoadOptions): Promise<TextFileEditorModel> {
// Make sure meanwhile someone else did not suceed or start loading
if (this.createTextEditorModelPromise || this.textEditorModel) {
return this.createTextEditorModelPromise || this;
// 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
}
// 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
};
// 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 */);
return this.loadWithContent(content, options, backup);
// Restore orphaned flag based on state
if (resolvedBackup.meta && resolvedBackup.meta.orphaned) {
this.setOrphaned(true);
}
// Otherwise load from file
return this.loadFromFile(options);
return this;
}
private async loadFromFile(options?: ILoadOptions): Promise<TextFileEditorModel> {
@@ -302,8 +345,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.lastResolvedDiskStat) {
etag = this.lastResolvedDiskStat.etag; // otherwise respect etag to support caching
} else if (this.lastResolvedFileStat) {
etag = this.lastResolvedFileStat.etag; // otherwise respect etag to support caching
}
// Ensure to track the versionId before doing a long running operation
@@ -321,12 +364,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Clear orphaned state when loading was successful
this.setOrphaned(false);
// Guard against the model having changed in the meantime
if (currentVersionId === this.versionId) {
return this.loadWithContent(content, options);
if (currentVersionId !== this.versionId) {
return this; // Make sure meanwhile someone else did not suceed loading
}
return this;
return this.loadFromContent(content, options);
} catch (error) {
const result = error.fileOperationResult;
@@ -356,37 +398,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
}
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> {
private loadFromContent(content: ITextFileStreamContent, options?: ILoadOptions, fromBackup?: boolean): TextFileEditorModel {
this.logService.trace('load() - resolved content', this.resource);
// Update our resolved disk stat model
this.updateLastResolvedDiskStat({
this.updateLastResolvedFileStat({
resource: this.resource,
name: content.name,
mtime: content.mtime,
@@ -409,21 +425,61 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
// Update Existing Model
if (this.textEditorModel) {
if (this.isResolved()) {
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
return this.doCreateTextModel(content.resource, content.value, backup);
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();
}
private doUpdateTextModel(value: ITextBufferFactory): void {
@@ -435,7 +491,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.updateTextEditorModel(value, this.preferredMode);
} finally {
this.blockModelContentChange = false;
}
@@ -444,44 +500,6 @@ 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
@@ -489,27 +507,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// where `value` was captured in the content change listener closure scope.
// Content Change
if (this.textEditorModel) {
if (this.isResolved()) {
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);
@@ -526,7 +528,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.textEditorModel && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) {
if (!this.autoSaveAfterMilliesEnabled && this.isResolved() && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) {
this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource);
// Clear flags
@@ -657,7 +659,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.textEditorModel) {
if (!this.autoSaveAfterMilliesEnabled && this.isResolved()) {
this.textEditorModel.pushStackElement();
}
@@ -687,7 +689,12 @@ 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;
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
}
// Under certain conditions we do a short-cut of flushing contents to disk when we can assume that
@@ -713,16 +720,12 @@ 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);
const snapshot = this.createSnapshot();
if (!snapshot) {
throw new Error('Invalid snapshot');
}
return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, {
return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), {
overwriteReadonly: options.overwriteReadonly,
overwriteEncoding: options.overwriteEncoding,
mtime: this.lastResolvedDiskStat.mtime,
mtime: this.lastResolvedFileStat.mtime,
encoding: this.getEncoding(),
etag: this.lastResolvedDiskStat.etag,
etag: this.lastResolvedFileStat.etag,
writeElevated: options.writeElevated
}).then(stat => {
this.logService.trace(`doSave(${versionId}) - after write()`, this.resource);
@@ -736,7 +739,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
// Updated resolved stat with updated stat
this.updateLastResolvedDiskStat(stat);
this.updateLastResolvedFileStat(stat);
// Cancel any content change event promises as they are no longer valid
this.contentChangeEventScheduler.cancel();
@@ -852,19 +855,22 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
private doTouch(versionId: number): Promise<void> {
const snapshot = this.createSnapshot();
if (!snapshot) {
throw new Error('invalid snapshot');
if (!this.isResolved()) {
return Promise.resolve();
}
return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, {
mtime: this.lastResolvedDiskStat.mtime,
return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), {
mtime: this.lastResolvedFileStat.mtime,
encoding: this.getEncoding(),
etag: this.lastResolvedDiskStat.etag
etag: this.lastResolvedFileStat.etag
}).then(stat => {
// Updated resolved stat with updated stat since touching it might have changed mtime
this.updateLastResolvedDiskStat(stat);
this.updateLastResolvedFileStat(stat);
// Emit File Saved Event
this._onDidStateChange.fire(StateChange.SAVED);
}, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */));
}
@@ -898,23 +904,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.textEditorModel) {
if (this.isResolved()) {
this.bufferSavedVersionId = this.textEditorModel.getAlternativeVersionId();
}
}
private updateLastResolvedDiskStat(newVersionOnDiskStat: IFileStatWithMetadata): void {
private updateLastResolvedFileStat(newFileStat: IFileStatWithMetadata): void {
// First resolve - just take
if (!this.lastResolvedDiskStat) {
this.lastResolvedDiskStat = newVersionOnDiskStat;
if (!this.lastResolvedFileStat) {
this.lastResolvedFileStat = newFileStat;
}
// 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.lastResolvedDiskStat.mtime <= newVersionOnDiskStat.mtime) {
this.lastResolvedDiskStat = newVersionOnDiskStat;
else if (this.lastResolvedFileStat.mtime <= newFileStat.mtime) {
this.lastResolvedFileStat = newFileStat;
}
}
@@ -937,10 +943,6 @@ 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:
@@ -1022,12 +1024,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return true;
}
isResolved(): boolean {
return !isUndefinedOrNull(this.lastResolvedDiskStat);
isResolved(): boolean { // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
return !!this.textEditorModel;
}
isReadonly(): boolean {
return !!(this.lastResolvedDiskStat && this.lastResolvedDiskStat.isReadonly);
return !!(this.lastResolvedFileStat && this.lastResolvedFileStat.isReadonly);
}
isDisposed(): boolean {
@@ -1039,7 +1041,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
getStat(): IFileStatWithMetadata {
return this.lastResolvedDiskStat;
return this.lastResolvedFileStat;
}
dispose(): void {
@@ -1048,8 +1050,6 @@ 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);
const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined, options ? options.mode : undefined);
modelPromise = model.load(options);
// Install state change listener
@@ -204,6 +204,11 @@ 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,6 +40,7 @@ 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.
@@ -238,59 +239,44 @@ 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(async model => {
const snapshot = model.createSnapshot();
if (snapshot) {
await this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId());
}
}));
await Promise.all(dirtyFileModels.map(model => model.backup()));
// Handle untitled resources
const untitledModelPromises = untitledResources
await Promise.all(untitledResources
.filter(untitled => this.untitledEditorService.exists(untitled))
.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());
}
}));
.map(async untitled => (await this.untitledEditorService.loadOrCreate({ resource: untitled })).backup()));
}
private confirmBeforeShutdown(): boolean | Promise<boolean> {
return this.confirmSave().then(confirm => {
private async confirmBeforeShutdown(): Promise<boolean> {
const confirm = await this.confirmSave();
// 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
}
// Save
if (confirm === ConfirmResult.SAVE) {
const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true });
return this.noVeto({ cleanUpBackups: true });
});
if (result.results.some(r => !r.success)) {
return true; // veto if some saves failed
}
// Don't Save
else if (confirm === ConfirmResult.DONT_SAVE) {
return this.noVeto({ cleanUpBackups: true });
}
// Make sure to revert untitled so that they do not restore
// see https://github.com/Microsoft/vscode/issues/29572
this.untitledEditorService.revertAll();
// Don't Save
else if (confirm === ConfirmResult.DONT_SAVE) {
return this.noVeto({ cleanUpBackups: true });
}
// Make sure to revert untitled so that they do not restore
// see https://github.com/Microsoft/vscode/issues/29572
this.untitledEditorService.revertAll();
// Cancel
else if (confirm === ConfirmResult.CANCEL) {
return true; // veto
}
return this.noVeto({ cleanUpBackups: true });
}
return false;
});
// Cancel
else if (confirm === ConfirmResult.CANCEL) {
return true; // veto
}
return false;
}
private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise<boolean> {
@@ -503,10 +489,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
dirtyTargetModelUris.push(targetModelResource);
// Backup dirty source model to the target resource it will become later
const snapshot = sourceModel.createSnapshot();
if (snapshot) {
await this.backupFileService.backupResource(targetModelResource, snapshot, sourceModel.getVersionId());
}
await sourceModel.backup(targetModelResource);
}));
}
@@ -757,14 +740,14 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
private getFileModels(arg1?: URI | URI[]): ITextFileEditorModel[] {
if (Array.isArray(arg1)) {
const models: ITextFileEditorModel[] = [];
(<URI[]>arg1).forEach(resource => {
arg1.forEach(resource => {
models.push(...this.getFileModels(resource));
});
return models;
}
return this._models.getAll(<URI>arg1);
return this._models.getAll(arg1);
}
private getDirtyFileModels(resources?: URI | URI[]): ITextFileEditorModel[] {
@@ -873,17 +856,12 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
// take over encoding, mode and model value from source model
targetModel.updatePreferredEncoding(sourceModel.getEncoding());
if (targetModel.textEditorModel) {
const snapshot = sourceModel.createSnapshot();
if (snapshot) {
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(snapshot));
}
if (sourceModel.isResolved() && targetModel.isResolved()) {
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()));
if (sourceModel.textEditorModel) {
const language = sourceModel.textEditorModel.getLanguageIdentifier();
if (language.id > 1) {
targetModel.textEditorModel.setMode(language); // only use if more specific than plain/text
}
const mode = sourceModel.textEditorModel.getLanguageIdentifier();
if (mode.language !== PLAINTEXT_MODE_ID) {
targetModel.textEditorModel.setMode(mode); // 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 } from 'vs/workbench/common/editor';
import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } 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,6 +367,11 @@ 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.
*/
@@ -443,19 +448,15 @@ export interface ILoadOptions {
reason?: LoadReason;
}
export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport {
export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport {
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>;
@@ -464,16 +465,17 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport
revert(soft?: boolean): Promise<void>;
createSnapshot(): ITextSnapshot | null;
backup(target?: URI): Promise<void>;
isDirty(): boolean;
isResolved(): boolean;
isResolved(): this is IResolvedTextFileEditorModel;
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 } from 'vs/base/node/pfs';
import { exists, stat, chmod, rimraf, MAX_FILE_SIZE, MAX_HEAP_SIZE } 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,7 +26,6 @@ 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<any>;
_serviceBrand: ServiceIdentifier<ITextResourcePropertiesService>;
private remoteEnvironment: IRemoteAgentEnvironment | null = null;

View File

@@ -14,6 +14,7 @@ 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) {
@@ -44,25 +45,53 @@ 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');
test('save', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
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');
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
model.setEncoding('utf8', EncodingMode.Encode); // no-op
assert.equal(getLastModifiedTime(model), -1);
@@ -75,7 +104,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('setEncoding - decode', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
model.setEncoding('utf16', EncodingMode.Decode);
@@ -84,8 +113,24 @@ 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');
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
await model.load();
@@ -94,7 +139,7 @@ suite('Files - TextFileEditorModel', () => {
});
test('Load does not trigger save', async function () {
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8');
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined);
assert.ok(model.hasState(ModelState.SAVED));
model.onDidStateChange(e => {
@@ -108,7 +153,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');
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
await model.load();
model.textEditorModel!.setValue('foo');
@@ -123,7 +168,7 @@ suite('Files - TextFileEditorModel', () => {
test('Revert', async function () {
let eventCounter = 0;
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
model.onDidStateChange(e => {
if (e === StateChange.REVERTED) {
@@ -145,7 +190,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');
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
model.onDidStateChange(e => {
if (e === StateChange.REVERTED) {
@@ -165,7 +210,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');
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
await model.load();
accessor.fileService.setContent('Hello Change');
@@ -175,7 +220,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');
let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
await model.load();
@@ -190,7 +235,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');
let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
await model.load();
accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_FOUND));
@@ -236,7 +281,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');
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
model.onDidStateChange(e => {
if (e === StateChange.SAVED) {
@@ -266,7 +311,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');
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
TextFileEditorModel.setSaveParticipant({
participate: (model) => {
@@ -284,7 +329,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');
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);
TextFileEditorModel.setSaveParticipant({
participate: (model) => {

View File

@@ -13,6 +13,7 @@ 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 {
@@ -42,9 +43,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');
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');
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);
manager.add(URI.file('/test.html'), model1);
manager.add(URI.file('/some/other.html'), model2);
@@ -117,9 +118,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');
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');
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);
manager.add(URI.file('/test.html'), model1);
manager.add(URI.file('/some/other.html'), model2);
@@ -290,4 +291,24 @@ 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', function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
test('confirm onWillShutdown - no veto', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const event = new BeforeShutdownEventImpl();
@@ -76,14 +76,12 @@ suite('Files - TextFileService', () => {
if (typeof veto === 'boolean') {
assert.ok(!veto);
} else {
veto.then(veto => {
assert.ok(!veto);
});
assert.ok(!(await veto));
}
});
test('confirm onWillShutdown - veto if user cancels', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -99,7 +97,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');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -125,7 +123,7 @@ suite('Files - TextFileService', () => {
});
test('confirm onWillShutdown - save (hot.exit: off)', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -144,7 +142,7 @@ suite('Files - TextFileService', () => {
});
test('isDirty/getDirty - files and untitled', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -171,7 +169,7 @@ suite('Files - TextFileService', () => {
});
test('save - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -187,11 +185,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');
model = instantiationService.createInstance(TextFileEditorModel, untitledUncUri, 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const mockedFileUri = untitledUncUri.with({ scheme: Schemas.file });
const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8');
const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8', undefined);
const loadOrCreateStub = sinon.stub(accessor.textFileService.models, 'loadOrCreate', () => Promise.resolve(mockedEditorInput));
sinon.stub(accessor.untitledEditorService, 'exists', () => true);
@@ -211,7 +209,7 @@ suite('Files - TextFileService', () => {
});
test('saveAll - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -228,7 +226,7 @@ suite('Files - TextFileService', () => {
});
test('saveAs - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -244,7 +242,7 @@ suite('Files - TextFileService', () => {
});
test('revert - file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -260,7 +258,7 @@ suite('Files - TextFileService', () => {
});
test('delete - dirty file', async function () {
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;
@@ -274,8 +272,8 @@ suite('Files - TextFileService', () => {
});
test('move - dirty file', async function () {
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');
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);
(<TextFileEditorModelManager>accessor.textFileService.models).add(sourceModel.getResource(), sourceModel);
(<TextFileEditorModelManager>accessor.textFileService.models).add(targetModel.getResource(), targetModel);
@@ -397,7 +395,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');
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined);
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
const service = accessor.textFileService;