mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-05 01:25:38 -05:00
Merge VS Code 1.23.1 (#1520)
This commit is contained in:
@@ -5,21 +5,20 @@
|
||||
'use strict';
|
||||
|
||||
import * as path from 'vs/base/common/paths';
|
||||
import nls = require('vs/nls');
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { guessMimeTypes } from 'vs/base/common/mime';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import diagnostics = require('vs/base/common/diagnostics');
|
||||
import types = require('vs/base/common/types');
|
||||
import * as diagnostics from 'vs/base/common/diagnostics';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
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, IRawTextContent } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent, ILoadOptions } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { EncodingMode } from 'vs/workbench/common/editor';
|
||||
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
@@ -63,8 +62,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
private disposed: boolean;
|
||||
private lastSaveAttemptTime: number;
|
||||
private createTextEditorModelPromise: TPromise<TextFileEditorModel>;
|
||||
private _onDidContentChange: Emitter<StateChange>;
|
||||
private _onDidStateChange: Emitter<StateChange>;
|
||||
private readonly _onDidContentChange: Emitter<StateChange>;
|
||||
private readonly _onDidStateChange: Emitter<StateChange>;
|
||||
|
||||
private inConflictMode: boolean;
|
||||
private inOrphanMode: boolean;
|
||||
@@ -129,38 +128,51 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
}
|
||||
|
||||
private onFileChanges(e: FileChangesEvent): void {
|
||||
let fileEventImpactsModel = false;
|
||||
let newInOrphanModeGuess: boolean;
|
||||
|
||||
// Track ADD and DELETES for updates of this model to orphan-mode
|
||||
const modelFileDeleted = e.contains(this.resource, FileChangeType.DELETED);
|
||||
const modelFileAdded = e.contains(this.resource, FileChangeType.ADDED);
|
||||
|
||||
if (modelFileDeleted || modelFileAdded) {
|
||||
const newInOrphanModeGuess = modelFileDeleted && !modelFileAdded;
|
||||
if (this.inOrphanMode !== newInOrphanModeGuess) {
|
||||
let checkOrphanedPromise: TPromise<boolean>;
|
||||
if (newInOrphanModeGuess) {
|
||||
// We have received reports of users seeing delete events even though the file still
|
||||
// exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665).
|
||||
// Since we do not want to mark the model as orphaned, we have to check if the
|
||||
// file is really gone and not just a faulty file event.
|
||||
checkOrphanedPromise = TPromise.timeout(100).then(() => {
|
||||
if (this.disposed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.fileService.existsFile(this.resource).then(exists => !exists);
|
||||
});
|
||||
} else {
|
||||
checkOrphanedPromise = TPromise.as(false);
|
||||
}
|
||||
|
||||
checkOrphanedPromise.done(newInOrphanModeValidated => {
|
||||
if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) {
|
||||
this.setOrphaned(newInOrphanModeValidated);
|
||||
}
|
||||
});
|
||||
// If we are currently orphaned, we check if the model file was added back
|
||||
if (this.inOrphanMode) {
|
||||
const modelFileAdded = e.contains(this.resource, FileChangeType.ADDED);
|
||||
if (modelFileAdded) {
|
||||
newInOrphanModeGuess = false;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we check if the model file was deleted
|
||||
else {
|
||||
const modelFileDeleted = e.contains(this.resource, FileChangeType.DELETED);
|
||||
if (modelFileDeleted) {
|
||||
newInOrphanModeGuess = true;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileEventImpactsModel && this.inOrphanMode !== newInOrphanModeGuess) {
|
||||
let checkOrphanedPromise: TPromise<boolean>;
|
||||
if (newInOrphanModeGuess) {
|
||||
// We have received reports of users seeing delete events even though the file still
|
||||
// exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665).
|
||||
// Since we do not want to mark the model as orphaned, we have to check if the
|
||||
// file is really gone and not just a faulty file event.
|
||||
checkOrphanedPromise = TPromise.timeout(100).then(() => {
|
||||
if (this.disposed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.fileService.existsFile(this.resource).then(exists => !exists);
|
||||
});
|
||||
} else {
|
||||
checkOrphanedPromise = TPromise.as(false);
|
||||
}
|
||||
|
||||
checkOrphanedPromise.done(newInOrphanModeValidated => {
|
||||
if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) {
|
||||
this.setOrphaned(newInOrphanModeValidated);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private setOrphaned(orphaned: boolean): void {
|
||||
@@ -244,7 +256,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
if (soft) {
|
||||
loadPromise = TPromise.as(this);
|
||||
} else {
|
||||
loadPromise = this.load(true /* force */);
|
||||
loadPromise = this.load({ forceReadFromDisk: true });
|
||||
}
|
||||
|
||||
return loadPromise.then(() => {
|
||||
@@ -260,28 +272,28 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
});
|
||||
}
|
||||
|
||||
public load(force?: boolean /* bypass any caches and really go to disk */): TPromise<TextFileEditorModel> {
|
||||
public load(options?: ILoadOptions): TPromise<TextFileEditorModel> {
|
||||
diag('load() - enter', this.resource, new Date());
|
||||
|
||||
// It is very important to not reload the model when the model is dirty. We only want to reload the model from the disk
|
||||
// if no save is pending to avoid data loss. This might cause a save conflict in case the file has been modified on the disk
|
||||
// meanwhile, but this is a very low risk.
|
||||
if (this.dirty) {
|
||||
diag('load() - exit - without loading because model is dirty', this.resource, new Date());
|
||||
if (this.dirty || this.saveSequentializer.hasPendingSave()) {
|
||||
diag('load() - exit - without loading because model is dirty or being saved', this.resource, new Date());
|
||||
|
||||
return TPromise.as(this);
|
||||
}
|
||||
|
||||
// Only for new models we support to load from backup
|
||||
if (!this.textEditorModel && !this.createTextEditorModelPromise) {
|
||||
return this.loadWithBackup(force);
|
||||
return this.loadWithBackup(options);
|
||||
}
|
||||
|
||||
// Otherwise load from file resource
|
||||
return this.loadFromFile(force);
|
||||
return this.loadFromFile(options);
|
||||
}
|
||||
|
||||
private loadWithBackup(force: boolean): TPromise<TextFileEditorModel> {
|
||||
private loadWithBackup(options?: ILoadOptions): TPromise<TextFileEditorModel> {
|
||||
return this.backupFileService.loadBackupResource(this.resource).then(backup => {
|
||||
|
||||
// Make sure meanwhile someone else did not suceed or start loading
|
||||
@@ -293,34 +305,36 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
if (!!backup) {
|
||||
const content: IRawTextContent = {
|
||||
resource: this.resource,
|
||||
name: paths.basename(this.resource.fsPath),
|
||||
name: path.basename(this.resource.fsPath),
|
||||
mtime: Date.now(),
|
||||
etag: void 0,
|
||||
value: createTextBufferFactory(''), /* will be filled later from backup */
|
||||
encoding: this.fileService.getEncoding(this.resource, this.preferredEncoding)
|
||||
encoding: this.fileService.encoding.getWriteEncoding(this.resource, this.preferredEncoding)
|
||||
};
|
||||
|
||||
return this.loadWithContent(content, backup);
|
||||
}
|
||||
|
||||
// Otherwise load from file
|
||||
return this.loadFromFile(force);
|
||||
return this.loadFromFile(options);
|
||||
});
|
||||
}
|
||||
|
||||
private loadFromFile(force: boolean): TPromise<TextFileEditorModel> {
|
||||
private loadFromFile(options?: ILoadOptions): TPromise<TextFileEditorModel> {
|
||||
const forceReadFromDisk = options && options.forceReadFromDisk;
|
||||
const allowBinary = this.isResolved() /* always allow if we resolved previously */ || (options && options.allowBinary);
|
||||
|
||||
// Decide on etag
|
||||
let etag: string;
|
||||
if (force) {
|
||||
etag = void 0; // bypass cache if force loading is true
|
||||
if (forceReadFromDisk) {
|
||||
etag = void 0; // reset ETag if we enforce to read from disk
|
||||
} else if (this.lastResolvedDiskStat) {
|
||||
etag = this.lastResolvedDiskStat.etag; // otherwise respect etag to support caching
|
||||
}
|
||||
|
||||
// Resolve Content
|
||||
return this.textFileService
|
||||
.resolveTextContent(this.resource, { acceptTextOnly: true, etag, encoding: this.preferredEncoding })
|
||||
.resolveTextContent(this.resource, { acceptTextOnly: !allowBinary, etag, encoding: this.preferredEncoding })
|
||||
.then(content => this.handleLoadSuccess(content), error => this.handleLoadError(error));
|
||||
}
|
||||
|
||||
@@ -370,10 +384,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
"fileGet" : {
|
||||
"mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"path": { "classification": "CustomerContent", "purpose": "FeatureInsight" }
|
||||
"path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('fileGet', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.resource.fsPath), path: this.hashService.createSHA1(this.resource.fsPath) });
|
||||
this.telemetryService.publicLog('fileGet', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: path.extname(this.resource.fsPath), path: this.hashService.createSHA1(this.resource.fsPath) });
|
||||
}
|
||||
|
||||
return model;
|
||||
@@ -678,16 +692,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
// mark the save participant as current pending save operation
|
||||
return this.saveSequentializer.setPending(versionId, saveParticipantPromise.then(newVersionId => {
|
||||
|
||||
// Under certain conditions a save to the model will not cause the contents to the flushed on
|
||||
// disk because we can assume that the contents are already on disk. Instead, we just touch the
|
||||
// file to still trigger external file watchers for example.
|
||||
// Under certain conditions we do a short-cut of flushing contents to disk when we can assume that
|
||||
// the file has not changed and as such was not dirty before.
|
||||
// The conditions are all of:
|
||||
// - a forced, explicit save (Ctrl+S)
|
||||
// - the model is not dirty (otherwise we know there are changed which needs to go to the file)
|
||||
// - the model is not in orphan mode (because in that case we know the file does not exist on disk)
|
||||
// - the model version did not change due to save participants running
|
||||
if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) {
|
||||
return this.doTouch();
|
||||
return this.doTouch(newVersionId);
|
||||
}
|
||||
|
||||
// update versionId with its new value (if pre-save changes happened)
|
||||
@@ -725,7 +738,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
"ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('filePUT', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.lastResolvedDiskStat.resource.fsPath) });
|
||||
this.telemetryService.publicLog('filePUT', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: path.extname(this.lastResolvedDiskStat.resource.fsPath) });
|
||||
}
|
||||
|
||||
// Update dirty state unless model has changed meanwhile
|
||||
@@ -774,16 +787,20 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
// Check for workspace settings file
|
||||
return this.contextService.getWorkspace().folders.some(folder => {
|
||||
// {{SQL CARBON EDIT}}
|
||||
return paths.isEqualOrParent(this.resource.fsPath, path.join(folder.uri.fsPath, '.sqlops'));
|
||||
return path.isEqualOrParent(this.resource.fsPath, path.join(folder.uri.fsPath, '.sqlops'));
|
||||
});
|
||||
}
|
||||
|
||||
private doTouch(): TPromise<void> {
|
||||
return this.fileService.touchFile(this.resource).then(stat => {
|
||||
private doTouch(versionId: number): TPromise<void> {
|
||||
return this.saveSequentializer.setPending(versionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, this.createSnapshot(), {
|
||||
mtime: this.lastResolvedDiskStat.mtime,
|
||||
encoding: this.getEncoding(),
|
||||
etag: this.lastResolvedDiskStat.etag
|
||||
}).then(stat => {
|
||||
|
||||
// Updated resolved stat with updated stat since touching it might have changed mtime
|
||||
this.updateLastResolvedDiskStat(stat);
|
||||
}, () => void 0 /* gracefully ignore errors if just touching */);
|
||||
}, () => void 0 /* gracefully ignore errors if just touching */));
|
||||
}
|
||||
|
||||
private setDirty(dirty: boolean): () => void {
|
||||
@@ -829,8 +846,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
}
|
||||
|
||||
// Subsequent resolve - make sure that we only assign it if the mtime is equal or has advanced.
|
||||
// This is essential a If-Modified-Since check on the client ot prevent race conditions from loading
|
||||
// and saving. If a save comes in late after a revert was called, the mtime could be out of sync.
|
||||
// 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;
|
||||
}
|
||||
@@ -923,7 +940,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
this.updatePreferredEncoding(encoding);
|
||||
|
||||
// Load
|
||||
this.load(true /* force because encoding has changed */).done(null, onUnexpectedError);
|
||||
this.load({
|
||||
forceReadFromDisk: true // because encoding has changed
|
||||
}).done(null, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,10 +1102,10 @@ export class SaveSequentializer {
|
||||
|
||||
class DefaultSaveErrorHandler implements ISaveErrorHandler {
|
||||
|
||||
constructor( @INotificationService private notificationService: INotificationService) { }
|
||||
constructor(@INotificationService private notificationService: INotificationService) { }
|
||||
|
||||
public onSaveError(error: any, model: TextFileEditorModel): void {
|
||||
this.notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", paths.basename(model.getResource().fsPath), toErrorMessage(error, false)));
|
||||
this.notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", path.basename(model.getResource().fsPath), toErrorMessage(error, false)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter, debounceEvent } from 'vs/base/common/event';
|
||||
import { Event, Emitter, debounceEvent } from 'vs/base/common/event';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeEvent, StateChange, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeEvent, StateChange, IModelLoadOrCreateOptions, ILoadOptions } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
@@ -17,14 +17,14 @@ import { ResourceMap } from 'vs/base/common/map';
|
||||
export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
||||
private toUnbind: IDisposable[];
|
||||
|
||||
private _onModelDisposed: Emitter<URI>;
|
||||
private _onModelContentChanged: Emitter<TextFileModelChangeEvent>;
|
||||
private _onModelDirty: Emitter<TextFileModelChangeEvent>;
|
||||
private _onModelSaveError: Emitter<TextFileModelChangeEvent>;
|
||||
private _onModelSaved: Emitter<TextFileModelChangeEvent>;
|
||||
private _onModelReverted: Emitter<TextFileModelChangeEvent>;
|
||||
private _onModelEncodingChanged: Emitter<TextFileModelChangeEvent>;
|
||||
private _onModelOrphanedChanged: Emitter<TextFileModelChangeEvent>;
|
||||
private readonly _onModelDisposed: Emitter<URI>;
|
||||
private readonly _onModelContentChanged: Emitter<TextFileModelChangeEvent>;
|
||||
private readonly _onModelDirty: Emitter<TextFileModelChangeEvent>;
|
||||
private readonly _onModelSaveError: Emitter<TextFileModelChangeEvent>;
|
||||
private readonly _onModelSaved: Emitter<TextFileModelChangeEvent>;
|
||||
private readonly _onModelReverted: Emitter<TextFileModelChangeEvent>;
|
||||
private readonly _onModelEncodingChanged: Emitter<TextFileModelChangeEvent>;
|
||||
private readonly _onModelOrphanedChanged: Emitter<TextFileModelChangeEvent>;
|
||||
|
||||
private _onModelsDirtyEvent: Event<TextFileModelChangeEvent[]>;
|
||||
private _onModelsSaveError: Event<TextFileModelChangeEvent[]>;
|
||||
@@ -167,22 +167,27 @@ export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
||||
return pendingLoad;
|
||||
}
|
||||
|
||||
let modelLoadOptions: ILoadOptions;
|
||||
if (options && options.allowBinary) {
|
||||
modelLoadOptions = { allowBinary: true };
|
||||
}
|
||||
|
||||
let modelPromise: TPromise<ITextFileEditorModel>;
|
||||
|
||||
// Model exists
|
||||
let model = this.get(resource);
|
||||
if (model) {
|
||||
if (!options || !options.reload) {
|
||||
modelPromise = TPromise.as(model);
|
||||
if (options && options.reload) {
|
||||
modelPromise = model.load(modelLoadOptions);
|
||||
} else {
|
||||
modelPromise = model.load();
|
||||
modelPromise = TPromise.as(model);
|
||||
}
|
||||
}
|
||||
|
||||
// Model does not exist
|
||||
else {
|
||||
model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : void 0);
|
||||
modelPromise = model.load();
|
||||
modelPromise = model.load(modelLoadOptions);
|
||||
|
||||
// Install state change listener
|
||||
this.mapResourceToStateChangeListener.set(resource, model.onDidStateChange(state => {
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import errors = require('vs/base/common/errors');
|
||||
import objects = require('vs/base/common/objects');
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import platform = require('vs/base/common/platform');
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IResult, ITextFileOperationResult, ITextFileService, IRawTextContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
@@ -51,10 +51,10 @@ export abstract class TextFileService implements ITextFileService {
|
||||
private toUnbind: IDisposable[];
|
||||
private _models: TextFileEditorModelManager;
|
||||
|
||||
private _onFilesAssociationChange: Emitter<void>;
|
||||
private readonly _onFilesAssociationChange: Emitter<void>;
|
||||
private currentFilesAssociationConfig: { [key: string]: string; };
|
||||
|
||||
private _onAutoSaveConfigurationChange: Emitter<IAutoSaveConfiguration>;
|
||||
private readonly _onAutoSaveConfigurationChange: Emitter<IAutoSaveConfiguration>;
|
||||
private configuredAutoSaveDelay: number;
|
||||
private configuredAutoSaveOnFocusChange: boolean;
|
||||
private configuredAutoSaveOnWindowChange: boolean;
|
||||
@@ -484,8 +484,8 @@ export abstract class TextFileService implements ITextFileService {
|
||||
private doSaveAllFiles(resources?: URI[], options: ISaveOptions = Object.create(null)): TPromise<ITextFileOperationResult> {
|
||||
const dirtyFileModels = this.getDirtyFileModels(Array.isArray(resources) ? resources : void 0 /* Save All */)
|
||||
.filter(model => {
|
||||
if (model.hasState(ModelState.CONFLICT) && (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE)) {
|
||||
return false; // if model is in save conflict, do not save unless save reason is explicit or not provided at all
|
||||
if ((model.hasState(ModelState.CONFLICT) || model.hasState(ModelState.ERROR)) && (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE)) {
|
||||
return false; // if model is in save conflict or error, do not save unless save reason is explicit or not provided at all
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IEncodingSupport, ConfirmResult } from 'vs/workbench/common/editor';
|
||||
import { IBaseStat, IResolveContentOptions, ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
@@ -140,8 +140,22 @@ export interface IRawTextContent extends IBaseStat {
|
||||
}
|
||||
|
||||
export interface IModelLoadOrCreateOptions {
|
||||
|
||||
|
||||
/**
|
||||
* The encoding to use when resolving the model text content.
|
||||
*/
|
||||
encoding?: string;
|
||||
|
||||
/**
|
||||
* Wether to reload the model if it already exists.
|
||||
*/
|
||||
reload?: boolean;
|
||||
|
||||
/**
|
||||
* Allow to load a model even if we think it is a binary file.
|
||||
*/
|
||||
allowBinary?: boolean;
|
||||
}
|
||||
|
||||
export interface ITextFileEditorModelManager {
|
||||
@@ -179,6 +193,19 @@ export interface ISaveOptions {
|
||||
writeElevated?: boolean;
|
||||
}
|
||||
|
||||
export interface ILoadOptions {
|
||||
|
||||
/**
|
||||
* Go to disk bypassing any cache of the model if any.
|
||||
*/
|
||||
forceReadFromDisk?: boolean;
|
||||
|
||||
/**
|
||||
* Allow to load a model even if we think it is a binary file.
|
||||
*/
|
||||
allowBinary?: boolean;
|
||||
}
|
||||
|
||||
export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport {
|
||||
|
||||
onDidContentChange: Event<StateChange>;
|
||||
@@ -196,7 +223,7 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport
|
||||
|
||||
save(options?: ISaveOptions): TPromise<void>;
|
||||
|
||||
load(): TPromise<ITextFileEditorModel>;
|
||||
load(options?: ILoadOptions): TPromise<ITextFileEditorModel>;
|
||||
|
||||
revert(soft?: boolean): TPromise<void>;
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import * as nls from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import strings = require('vs/base/common/strings');
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ConfirmResult } from 'vs/workbench/common/editor';
|
||||
import { TextFileService as AbstractTextFileService } from 'vs/workbench/services/textfile/common/textFileService';
|
||||
@@ -21,17 +21,16 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { getConfirmMessage } from 'vs/workbench/services/dialogs/electron-browser/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { getConfirmMessage, IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { Severity } from 'vs/editor/common/standalone/standaloneBase';
|
||||
|
||||
export class TextFileService extends AbstractTextFileService {
|
||||
|
||||
@@ -50,7 +49,8 @@ export class TextFileService extends AbstractTextFileService {
|
||||
@IBackupFileService backupFileService: IBackupFileService,
|
||||
@IWindowsService windowsService: IWindowsService,
|
||||
@IHistoryService historyService: IHistoryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDialogService private dialogService: IDialogService
|
||||
) {
|
||||
super(lifecycleService, contextService, configurationService, fileService, untitledEditorService, instantiationService, notificationService, environmentService, backupFileService, windowsService, historyService, contextKeyService, modelService);
|
||||
}
|
||||
@@ -84,39 +84,22 @@ export class TextFileService extends AbstractTextFileService {
|
||||
const message = resourcesToConfirm.length === 1 ? nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", paths.basename(resourcesToConfirm[0].fsPath))
|
||||
: getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length), resourcesToConfirm);
|
||||
|
||||
// Button order
|
||||
// Windows: Save | Don't Save | Cancel
|
||||
// Mac: Save | Cancel | Don't Save
|
||||
// Linux: Don't Save | Cancel | Save
|
||||
const buttons: string[] = [
|
||||
resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
|
||||
nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
|
||||
const save = { label: resourcesToConfirm.length > 1 ? mnemonicButtonLabel(nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All")) : mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE };
|
||||
const dontSave = { label: mnemonicButtonLabel(nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE };
|
||||
const cancel = { label: nls.localize('cancel', "Cancel"), result: ConfirmResult.CANCEL };
|
||||
|
||||
const buttons: { label: string; result: ConfirmResult; }[] = [];
|
||||
if (isWindows) {
|
||||
buttons.push(save, dontSave, cancel);
|
||||
} else if (isLinux) {
|
||||
buttons.push(dontSave, cancel, save);
|
||||
} else {
|
||||
buttons.push(save, cancel, dontSave);
|
||||
}
|
||||
|
||||
const opts: Electron.MessageBoxOptions = {
|
||||
title: product.nameLong,
|
||||
message,
|
||||
type: 'warning',
|
||||
detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them."),
|
||||
buttons: buttons.map(b => b.label),
|
||||
noLink: true,
|
||||
cancelId: buttons.indexOf(cancel)
|
||||
};
|
||||
|
||||
if (isLinux) {
|
||||
opts.defaultId = 2;
|
||||
}
|
||||
|
||||
return this.windowService.showMessageBox(opts).then(result => buttons[result.button].result);
|
||||
return this.dialogService.show(Severity.Warning, message, buttons, {
|
||||
cancelId: 2,
|
||||
detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.")
|
||||
}).then(index => {
|
||||
switch (index) {
|
||||
case 0: return ConfirmResult.SAVE;
|
||||
case 1: return ConfirmResult.DONT_SAVE;
|
||||
default: return ConfirmResult.CANCEL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public promptForPath(defaultPath: string): TPromise<string> {
|
||||
|
||||
@@ -12,13 +12,14 @@ import { EncodingMode } from 'vs/workbench/common/editor';
|
||||
import { TextFileEditorModel, SaveSequentializer } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { ITextFileService, ModelState, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { workbenchInstantiationService, TestTextFileService, createFileInput, TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { onError, toResource } from 'vs/base/test/common/utils';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
import { FileOperationResult, FileOperationError, IFileService, snapshotToString } from 'vs/platform/files/common/files';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor( @ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService, @IFileService public fileService: TestFileService) {
|
||||
constructor(@ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService, @IFileService public fileService: TestFileService) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +47,10 @@ suite('Files - TextFileEditorModel', () => {
|
||||
accessor.fileService.setContent(content);
|
||||
});
|
||||
|
||||
test('Save', function (done) {
|
||||
test('Save', function () {
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('bar');
|
||||
assert.ok(getLastModifiedTime(model) <= Date.now());
|
||||
|
||||
@@ -59,10 +60,8 @@ suite('Files - TextFileEditorModel', () => {
|
||||
|
||||
model.dispose();
|
||||
assert.ok(!accessor.modelService.getModel(model.getResource()));
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('setEncoding - encode', function () {
|
||||
@@ -88,19 +87,17 @@ suite('Files - TextFileEditorModel', () => {
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('disposes when underlying model is destroyed', function (done) {
|
||||
test('disposes when underlying model is destroyed', function () {
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.dispose();
|
||||
|
||||
assert.ok(model.isDisposed());
|
||||
|
||||
done();
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Load does not trigger save', function (done) {
|
||||
test('Load does not trigger save', function () {
|
||||
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8');
|
||||
assert.ok(model.hasState(ModelState.SAVED));
|
||||
|
||||
@@ -108,21 +105,19 @@ suite('Files - TextFileEditorModel', () => {
|
||||
assert.ok(e !== StateChange.DIRTY && e !== StateChange.SAVED);
|
||||
});
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
assert.ok(model.isResolved());
|
||||
|
||||
model.dispose();
|
||||
|
||||
assert.ok(!accessor.modelService.getModel(model.getResource()));
|
||||
|
||||
done();
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Load returns dirty model as long as model is dirty', function (done) {
|
||||
test('Load returns dirty model as long as model is dirty', function () {
|
||||
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.ok(model.isDirty());
|
||||
@@ -131,13 +126,11 @@ suite('Files - TextFileEditorModel', () => {
|
||||
assert.ok(model.isDirty());
|
||||
|
||||
model.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Revert', function (done) {
|
||||
test('Revert', function () {
|
||||
let eventCounter = 0;
|
||||
|
||||
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
@@ -148,7 +141,7 @@ suite('Files - TextFileEditorModel', () => {
|
||||
}
|
||||
});
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.ok(model.isDirty());
|
||||
@@ -159,13 +152,11 @@ suite('Files - TextFileEditorModel', () => {
|
||||
assert.equal(eventCounter, 1);
|
||||
|
||||
model.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Revert (soft)', function (done) {
|
||||
test('Revert (soft)', function () {
|
||||
let eventCounter = 0;
|
||||
|
||||
const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
@@ -176,7 +167,7 @@ suite('Files - TextFileEditorModel', () => {
|
||||
}
|
||||
});
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.ok(model.isDirty());
|
||||
@@ -187,30 +178,26 @@ suite('Files - TextFileEditorModel', () => {
|
||||
assert.equal(eventCounter, 1);
|
||||
|
||||
model.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Load and undo turns model dirty', function (done) {
|
||||
test('Load and undo turns model dirty', function () {
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
accessor.fileService.setContent('Hello Change');
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.undo();
|
||||
|
||||
assert.ok(model.isDirty());
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('File not modified error is handled gracefully', function (done) {
|
||||
test('File not modified error is handled gracefully', function () {
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
const mtime = getLastModifiedTime(model);
|
||||
accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_MODIFIED_SINCE));
|
||||
|
||||
@@ -218,32 +205,28 @@ suite('Files - TextFileEditorModel', () => {
|
||||
assert.ok(model);
|
||||
assert.equal(getLastModifiedTime(model), mtime);
|
||||
model.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Load error is handled gracefully if model already exists', function (done) {
|
||||
test('Load error is handled gracefully if model already exists', function () {
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_FOUND));
|
||||
|
||||
return model.load().then((model: TextFileEditorModel) => {
|
||||
assert.ok(model);
|
||||
model.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('save() and isDirty() - proper with check for mtimes', function (done) {
|
||||
test('save() and isDirty() - proper with check for mtimes', function () {
|
||||
const input1 = createFileInput(instantiationService, toResource.call(this, '/path/index_async2.txt'));
|
||||
const input2 = createFileInput(instantiationService, toResource.call(this, '/path/index_async.txt'));
|
||||
|
||||
input1.resolve().done((model1: TextFileEditorModel) => {
|
||||
return input1.resolve().then((model1: TextFileEditorModel) => {
|
||||
return input2.resolve().then((model2: TextFileEditorModel) => {
|
||||
model1.textEditorModel.setValue('foo');
|
||||
|
||||
@@ -259,7 +242,7 @@ suite('Files - TextFileEditorModel', () => {
|
||||
model2.textEditorModel.setValue('foo');
|
||||
assert.ok(accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt')));
|
||||
|
||||
return TPromise.timeout(10).then(() => {
|
||||
return timeout(10).then(() => {
|
||||
accessor.textFileService.saveAll().then(() => {
|
||||
assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt')));
|
||||
assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt')));
|
||||
@@ -270,15 +253,13 @@ suite('Files - TextFileEditorModel', () => {
|
||||
|
||||
model1.dispose();
|
||||
model2.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Save Participant', function (done) {
|
||||
test('Save Participant', function () {
|
||||
let eventCounter = 0;
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
@@ -300,42 +281,39 @@ suite('Files - TextFileEditorModel', () => {
|
||||
}
|
||||
});
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
return model.save().then(() => {
|
||||
model.dispose();
|
||||
|
||||
assert.equal(eventCounter, 2);
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Save Participant, async participant', function (done) {
|
||||
test('Save Participant, async participant', function () {
|
||||
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
TextFileEditorModel.setSaveParticipant({
|
||||
participate: (model) => {
|
||||
return TPromise.timeout(10);
|
||||
return timeout(10);
|
||||
}
|
||||
});
|
||||
|
||||
return model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
const now = Date.now();
|
||||
return model.save().then(() => {
|
||||
assert.ok(Date.now() - now >= 10);
|
||||
model.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('Save Participant, bad participant', function (done) {
|
||||
test('Save Participant, bad participant', function () {
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8');
|
||||
|
||||
TextFileEditorModel.setSaveParticipant({
|
||||
@@ -347,17 +325,12 @@ suite('Files - TextFileEditorModel', () => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
return model.save().then(() => {
|
||||
assert.ok(true);
|
||||
model.dispose();
|
||||
|
||||
done();
|
||||
}, err => {
|
||||
assert.ok(false);
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('SaveSequentializer - pending basics', function (done) {
|
||||
test('SaveSequentializer - pending basics', function () {
|
||||
const sequentializer = new SaveSequentializer();
|
||||
|
||||
assert.ok(!sequentializer.hasPendingSave());
|
||||
@@ -371,26 +344,24 @@ suite('Files - TextFileEditorModel', () => {
|
||||
assert.ok(!sequentializer.pendingSave);
|
||||
|
||||
// pending removes itself after done (use timeout)
|
||||
sequentializer.setPending(2, TPromise.timeout(1));
|
||||
sequentializer.setPending(2, timeout(1));
|
||||
assert.ok(sequentializer.hasPendingSave());
|
||||
assert.ok(sequentializer.hasPendingSave(2));
|
||||
assert.ok(!sequentializer.hasPendingSave(1));
|
||||
assert.ok(sequentializer.pendingSave);
|
||||
|
||||
return TPromise.timeout(2).then(() => {
|
||||
return timeout(2).then(() => {
|
||||
assert.ok(!sequentializer.hasPendingSave());
|
||||
assert.ok(!sequentializer.hasPendingSave(2));
|
||||
assert.ok(!sequentializer.pendingSave);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('SaveSequentializer - pending and next (finishes instantly)', function (done) {
|
||||
test('SaveSequentializer - pending and next (finishes instantly)', function () {
|
||||
const sequentializer = new SaveSequentializer();
|
||||
|
||||
let pendingDone = false;
|
||||
sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; }));
|
||||
sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return null; }));
|
||||
|
||||
// next finishes instantly
|
||||
let nextDone = false;
|
||||
@@ -399,52 +370,46 @@ suite('Files - TextFileEditorModel', () => {
|
||||
return res.done(() => {
|
||||
assert.ok(pendingDone);
|
||||
assert.ok(nextDone);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('SaveSequentializer - pending and next (finishes after timeout)', function (done) {
|
||||
test('SaveSequentializer - pending and next (finishes after timeout)', function () {
|
||||
const sequentializer = new SaveSequentializer();
|
||||
|
||||
let pendingDone = false;
|
||||
sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; }));
|
||||
sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return null; }));
|
||||
|
||||
// next finishes after timeout
|
||||
let nextDone = false;
|
||||
const res = sequentializer.setNext(() => TPromise.timeout(1).then(() => { nextDone = true; return null; }));
|
||||
const res = sequentializer.setNext(() => timeout(1).then(() => { nextDone = true; return null; }));
|
||||
|
||||
return res.done(() => {
|
||||
assert.ok(pendingDone);
|
||||
assert.ok(nextDone);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('SaveSequentializer - pending and multiple next (last one wins)', function (done) {
|
||||
test('SaveSequentializer - pending and multiple next (last one wins)', function () {
|
||||
const sequentializer = new SaveSequentializer();
|
||||
|
||||
let pendingDone = false;
|
||||
sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; }));
|
||||
sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return null; }));
|
||||
|
||||
// next finishes after timeout
|
||||
let firstDone = false;
|
||||
let firstRes = sequentializer.setNext(() => TPromise.timeout(2).then(() => { firstDone = true; return null; }));
|
||||
let firstRes = sequentializer.setNext(() => timeout(2).then(() => { firstDone = true; return null; }));
|
||||
|
||||
let secondDone = false;
|
||||
let secondRes = sequentializer.setNext(() => TPromise.timeout(3).then(() => { secondDone = true; return null; }));
|
||||
let secondRes = sequentializer.setNext(() => timeout(3).then(() => { secondDone = true; return null; }));
|
||||
|
||||
let thirdDone = false;
|
||||
let thirdRes = sequentializer.setNext(() => TPromise.timeout(4).then(() => { thirdDone = true; return null; }));
|
||||
let thirdRes = sequentializer.setNext(() => timeout(4).then(() => { thirdDone = true; return null; }));
|
||||
|
||||
return TPromise.join([firstRes, secondRes, thirdRes]).then(() => {
|
||||
assert.ok(pendingDone);
|
||||
assert.ok(!firstDone);
|
||||
assert.ok(!secondDone);
|
||||
assert.ok(thirdDone);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { workbenchInstantiationService, TestEditorGroupService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { onError } from 'vs/base/test/common/utils';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
export class TestTextFileEditorModelManager extends TextFileEditorModelManager {
|
||||
|
||||
@@ -103,12 +102,12 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
model3.dispose();
|
||||
});
|
||||
|
||||
test('loadOrCreate', function (done) {
|
||||
test('loadOrCreate', function () {
|
||||
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
|
||||
const resource = URI.file('/test.html');
|
||||
const encoding = 'utf8';
|
||||
|
||||
manager.loadOrCreate(resource, { encoding, reload: true }).done(model => {
|
||||
return manager.loadOrCreate(resource, { encoding, reload: true }).then(model => {
|
||||
assert.ok(model);
|
||||
assert.equal(model.getEncoding(), encoding);
|
||||
assert.equal(manager.get(resource), model);
|
||||
@@ -123,11 +122,9 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
assert.equal(manager.get(resource), model3);
|
||||
|
||||
model3.dispose();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('removed from cache when model disposed', function () {
|
||||
@@ -150,7 +147,7 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
model3.dispose();
|
||||
});
|
||||
|
||||
test('events', function (done) {
|
||||
test('events', function () {
|
||||
TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0;
|
||||
TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 0;
|
||||
|
||||
@@ -200,7 +197,7 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
disposeCounter++;
|
||||
});
|
||||
|
||||
manager.loadOrCreate(resource1, { encoding: 'utf8' }).done(model1 => {
|
||||
return manager.loadOrCreate(resource1, { encoding: 'utf8' }).then(model1 => {
|
||||
accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }]));
|
||||
accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }]));
|
||||
|
||||
@@ -223,7 +220,7 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
assert.equal(encodingCounter, 2);
|
||||
|
||||
// content change event if done async
|
||||
TPromise.timeout(10).then(() => {
|
||||
return timeout(10).then(() => {
|
||||
assert.equal(contentCounter, 2);
|
||||
|
||||
model1.dispose();
|
||||
@@ -231,17 +228,15 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
|
||||
assert.ok(!accessor.modelService.getModel(resource1));
|
||||
assert.ok(!accessor.modelService.getModel(resource2));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('events debounced', function (done) {
|
||||
test('events debounced', function () {
|
||||
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
|
||||
|
||||
const resource1 = toResource('/path/index.txt');
|
||||
@@ -268,7 +263,7 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
assert.equal(e[0].resource.toString(), resource1.toString());
|
||||
});
|
||||
|
||||
manager.loadOrCreate(resource1, { encoding: 'utf8' }).done(model1 => {
|
||||
return manager.loadOrCreate(resource1, { encoding: 'utf8' }).then(model1 => {
|
||||
return manager.loadOrCreate(resource2, { encoding: 'utf8' }).then(model2 => {
|
||||
model1.textEditorModel.setValue('changed');
|
||||
model1.updatePreferredEncoding('utf16');
|
||||
@@ -281,7 +276,7 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
model2.dispose();
|
||||
|
||||
return model1.revert().then(() => { // should not trigger another event if disposed
|
||||
return TPromise.timeout(20).then(() => {
|
||||
return timeout(20).then(() => {
|
||||
assert.equal(dirtyCounter, 2);
|
||||
assert.equal(revertedCounter, 1);
|
||||
assert.equal(savedCounter, 1);
|
||||
@@ -291,38 +286,35 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
|
||||
assert.ok(!accessor.modelService.getModel(resource1));
|
||||
assert.ok(!accessor.modelService.getModel(resource2));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('disposing model takes it out of the manager', function (done) {
|
||||
test('disposing model takes it out of the manager', function () {
|
||||
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
|
||||
|
||||
const resource = toResource('/path/index_something.txt');
|
||||
|
||||
manager.loadOrCreate(resource, { encoding: 'utf8' }).done(model => {
|
||||
return manager.loadOrCreate(resource, { encoding: 'utf8' }).then(model => {
|
||||
model.dispose();
|
||||
|
||||
assert.ok(!manager.get(resource));
|
||||
assert.ok(!accessor.modelService.getModel(model.getResource()));
|
||||
|
||||
manager.dispose();
|
||||
done();
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('dispose prevents dirty model from getting disposed', function (done) {
|
||||
test('dispose prevents dirty model from getting disposed', function () {
|
||||
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
|
||||
|
||||
const resource = toResource('/path/index_something.txt');
|
||||
|
||||
manager.loadOrCreate(resource, { encoding: 'utf8' }).done(model => {
|
||||
return manager.loadOrCreate(resource, { encoding: 'utf8' }).then(model => {
|
||||
model.textEditorModel.setValue('make dirty');
|
||||
|
||||
manager.disposeModel(model as TextFileEditorModel);
|
||||
@@ -334,7 +326,6 @@ suite('Files - TextFileEditorModelManager', () => {
|
||||
assert.ok(model.isDisposed());
|
||||
|
||||
manager.dispose();
|
||||
done();
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ import * as platform from 'vs/base/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ILifecycleService, ShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestWindowsService, TestContextService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { onError, toResource } from 'vs/base/test/common/utils';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
@@ -77,14 +77,14 @@ suite('Files - TextFileService', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('confirm onWillShutdown - veto if user cancels', function (done) {
|
||||
test('confirm onWillShutdown - veto if user cancels', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
service.setConfirmResult(ConfirmResult.CANCEL);
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
@@ -93,12 +93,10 @@ suite('Files - TextFileService', () => {
|
||||
accessor.lifecycleService.fireWillShutdown(event);
|
||||
|
||||
assert.ok(event.value);
|
||||
|
||||
done();
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', function (done) {
|
||||
test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
@@ -106,7 +104,7 @@ suite('Files - TextFileService', () => {
|
||||
service.setConfirmResult(ConfirmResult.DONT_SAVE);
|
||||
service.onFilesConfigurationChange({ files: { hotExit: 'off' } });
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
@@ -119,19 +117,17 @@ suite('Files - TextFileService', () => {
|
||||
assert.ok(service.cleanupBackupsBeforeShutdownCalled);
|
||||
assert.ok(!veto);
|
||||
|
||||
done();
|
||||
return void 0;
|
||||
} else {
|
||||
veto.then(veto => {
|
||||
return veto.then(veto => {
|
||||
assert.ok(service.cleanupBackupsBeforeShutdownCalled);
|
||||
assert.ok(!veto);
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('confirm onWillShutdown - save (hot.exit: off)', function (done) {
|
||||
test('confirm onWillShutdown - save (hot.exit: off)', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
@@ -139,7 +135,7 @@ suite('Files - TextFileService', () => {
|
||||
service.setConfirmResult(ConfirmResult.SAVE);
|
||||
service.onFilesConfigurationChange({ files: { hotExit: 'off' } });
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
@@ -150,18 +146,16 @@ suite('Files - TextFileService', () => {
|
||||
return (<TPromise<boolean>>event.value).then(veto => {
|
||||
assert.ok(!veto);
|
||||
assert.ok(!model.isDirty());
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('isDirty/getDirty - files and untitled', function (done) {
|
||||
test('isDirty/getDirty - files and untitled', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
assert.ok(!service.isDirty(model.getResource()));
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
@@ -178,19 +172,17 @@ suite('Files - TextFileService', () => {
|
||||
assert.ok(service.isDirty(untitled.getResource()));
|
||||
assert.equal(service.getDirty().length, 2);
|
||||
assert.equal(service.getDirty([untitled.getResource()])[0].toString(), untitled.getResource().toString());
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('save - file', function (done) {
|
||||
test('save - file', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.ok(service.isDirty(model.getResource()));
|
||||
@@ -198,19 +190,17 @@ suite('Files - TextFileService', () => {
|
||||
return service.save(model.getResource()).then(res => {
|
||||
assert.ok(res);
|
||||
assert.ok(!service.isDirty(model.getResource()));
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('saveAll - file', function (done) {
|
||||
test('saveAll - file', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.ok(service.isDirty(model.getResource()));
|
||||
@@ -220,20 +210,18 @@ suite('Files - TextFileService', () => {
|
||||
assert.ok(!service.isDirty(model.getResource()));
|
||||
assert.equal(res.results.length, 1);
|
||||
assert.equal(res.results[0].source.toString(), model.getResource().toString());
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('saveAs - file', function (done) {
|
||||
test('saveAs - file', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
service.setPromptPath(model.getResource().fsPath);
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.ok(service.isDirty(model.getResource()));
|
||||
@@ -241,20 +229,18 @@ suite('Files - TextFileService', () => {
|
||||
return service.saveAs(model.getResource()).then(res => {
|
||||
assert.equal(res.toString(), model.getResource().toString());
|
||||
assert.ok(!service.isDirty(model.getResource()));
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
test('revert - file', function (done) {
|
||||
test('revert - file', function () {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
service.setPromptPath(model.getResource().fsPath);
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.ok(service.isDirty(model.getResource()));
|
||||
@@ -262,118 +248,116 @@ suite('Files - TextFileService', () => {
|
||||
return service.revert(model.getResource()).then(res => {
|
||||
assert.ok(res);
|
||||
assert.ok(!service.isDirty(model.getResource()));
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
/*
|
||||
suite('Hot Exit', () => {
|
||||
suite('"onExit" setting', () => {
|
||||
test('should hot exit on non-Mac (reason: CLOSE, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, true, !!platform.isMacintosh, done);
|
||||
test('should hot exit on non-Mac (reason: CLOSE, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, true, !!platform.isMacintosh);
|
||||
});
|
||||
test('should hot exit on non-Mac (reason: CLOSE, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh, done);
|
||||
test('should hot exit on non-Mac (reason: CLOSE, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh);
|
||||
});
|
||||
test('should NOT hot exit (reason: CLOSE, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, true, true, done);
|
||||
test('should NOT hot exit (reason: CLOSE, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, true, true);
|
||||
});
|
||||
test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, false, true, done);
|
||||
test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, false, true);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, true, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, true, false);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, false, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, false, false);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, true, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, true, false);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, false, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, false, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, true, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, true, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, false, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, false, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, true, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, true, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, false, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, false, false);
|
||||
});
|
||||
test('should NOT hot exit (reason: LOAD, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, true, true, done);
|
||||
test('should NOT hot exit (reason: LOAD, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, true, true);
|
||||
});
|
||||
test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, false, true, done);
|
||||
test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, false, true);
|
||||
});
|
||||
test('should NOT hot exit (reason: LOAD, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, true, true, done);
|
||||
test('should NOT hot exit (reason: LOAD, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, true, true);
|
||||
});
|
||||
test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, false, true, done);
|
||||
test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, false, true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('"onExitAndWindowClose" setting', () => {
|
||||
test('should hot exit (reason: CLOSE, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, true, false, done);
|
||||
test('should hot exit (reason: CLOSE, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, true, false);
|
||||
});
|
||||
test('should hot exit (reason: CLOSE, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh, done);
|
||||
test('should hot exit (reason: CLOSE, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh);
|
||||
});
|
||||
test('should hot exit (reason: CLOSE, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, true, false, done);
|
||||
test('should hot exit (reason: CLOSE, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, true, false);
|
||||
});
|
||||
test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, false, true, done);
|
||||
test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, false, true);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, true, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, true, false);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, false, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, false, false);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, true, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, true, false);
|
||||
});
|
||||
test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, false, false, done);
|
||||
test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, false, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, true, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, true, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, false, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, false, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, true, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, true, false);
|
||||
});
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, false, false, done);
|
||||
test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, false, false);
|
||||
});
|
||||
test('should hot exit (reason: LOAD, windows: single, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, true, false, done);
|
||||
test('should hot exit (reason: LOAD, windows: single, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, true, false);
|
||||
});
|
||||
test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, false, true, done);
|
||||
test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, false, true);
|
||||
});
|
||||
test('should hot exit (reason: LOAD, windows: multiple, workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, true, false, done);
|
||||
test('should hot exit (reason: LOAD, windows: multiple, workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, true, false);
|
||||
});
|
||||
test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function (done) {
|
||||
hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, false, true, done);
|
||||
test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () {
|
||||
return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, false, true);
|
||||
});
|
||||
});
|
||||
|
||||
function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: true, shouldVeto: boolean, done: () => void): void {
|
||||
function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: true, shouldVeto: boolean): TPromise<void> {
|
||||
model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8');
|
||||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model);
|
||||
|
||||
@@ -391,7 +375,7 @@ suite('Files - TextFileService', () => {
|
||||
// Set cancel to force a veto if hot exit does not trigger
|
||||
service.setConfirmResult(ConfirmResult.CANCEL);
|
||||
|
||||
model.load().done(() => {
|
||||
return model.load().then(() => {
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
@@ -404,10 +388,8 @@ suite('Files - TextFileService', () => {
|
||||
// When hot exit is set, backups should never be cleaned since the confirm result is cancel
|
||||
assert.ok(!service.cleanupBackupsBeforeShutdownCalled);
|
||||
assert.equal(veto, shouldVeto);
|
||||
|
||||
done();
|
||||
});
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
}
|
||||
});
|
||||
// {{SQL CARBON EDIT}}
|
||||
|
||||
Reference in New Issue
Block a user