From 52c8af5abfb18dad5417ae97fe4441ec456d056e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 13 Oct 2016 02:30:33 -0700 Subject: [PATCH] Perform a last minute backup when closing the last window --- .../common/editor/untitledEditorModel.ts | 10 +++- .../services/files/node/fileService.ts | 1 + .../textfile/browser/textFileService.ts | 54 +++++++++++++++++-- .../textfile/common/textFileEditorModel.ts | 14 ++++- .../services/textfile/common/textfiles.ts | 2 + 5 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 168e5a93ea2..454167b3d76 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -193,10 +193,18 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS this.fileService.discardBackup(this.resource); } - private doBackup(): TPromise { + public backup(): TPromise { + return this.doBackup(true); + } + + private doBackup(immediate?: boolean): TPromise { // Cancel any currently running backups to make this the one that succeeds this.cancelBackupPromises(); + if (immediate) { + return this.fileService.backupFile(this.resource, this.getValue()).then(f => void 0); + } + // Create new backup promise and keep it const promise = TPromise.timeout(1000).then(() => { this.fileService.backupFile(this.resource, this.getValue()); // Very important here to not return the promise because if the timeout promise is canceled it will bubble up the error otherwise - do not change diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index 3cb3e807b17..525e673af95 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -458,6 +458,7 @@ export class FileService implements IFileService { } public backupFile(resource: uri, content: string): TPromise { + // TODO: This should not backup unless necessary. Currently this is called for each file on close to ensure the files are backed up. if (resource.scheme === 'file') { this.backupService.registerBackupFile(resource); } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 690018a2e44..adae9515905 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -119,14 +119,12 @@ export abstract class TextFileService implements ITextFileService { // If hot exit is enabled then save the dirty files in the workspace and then exit if (this.configuredHotExit) { - // TODO: Check if dirty - // TODO: Do a last minute backup if required - // TODO: There may be a better way of verifying that only a single window is open? // Only remove the workspace from the backup service if it's not the last one or it's not dirty if (this.backupService.getBackupWorkspaces().length > 1 || this.getDirty().length === 0) { this.backupService.removeWorkspace(this.contextService.getWorkspace().resource.fsPath); } else { - return false; // the backup will be restored, no veto + // TODO: Better error handling here? Perhaps present confirm if there was an error? + return this.backupAll().then(() => false); // the backup will be restored, no veto } } @@ -384,6 +382,54 @@ export abstract class TextFileService implements ITextFileService { }); } + /** + * Performs an immedate backup of all dirty file and untitled models. + */ + private backupAll(): TPromise { + const toBackup = this.getDirty(); + + // split up between files and untitled + const filesToBackup: URI[] = []; + const untitledToBackup: URI[] = []; + toBackup.forEach(s => { + if (s.scheme === 'file') { + filesToBackup.push(s); + } else if (s.scheme === 'untitled') { + untitledToBackup.push(s); + } + }); + + return this.doBackupAll(filesToBackup, untitledToBackup); + } + + private doBackupAll(fileResources: URI[], untitledResources: URI[]): TPromise { + // Handle file resources first + const dirtyFileModels = this.getDirtyFileModels(fileResources); + + const mapResourceToResult: { [resource: string]: IResult } = Object.create(null); + dirtyFileModels.forEach(m => { + mapResourceToResult[m.getResource().toString()] = { + source: m.getResource() + }; + }); + + return TPromise.join(dirtyFileModels.map(model => { + return model.backup().then(() => { + mapResourceToResult[model.getResource().toString()].success = true; + }); + })).then(result => { + // Handle untitled resources + const untitledModelPromises = untitledResources.map(untitledResource => this.untitledEditorService.get(untitledResource)) + .filter(untitled => !!untitled) + .map(untitled => untitled.resolve()); + + return TPromise.join(untitledModelPromises).then(untitledModels => { + const untitledBackupPromises = untitledModels.map(model => model.backup()); + return TPromise.join(untitledBackupPromises).then(() => void 0); + }); + }); + } + private getFileModels(resources?: URI[]): ITextFileEditorModel[]; private getFileModels(resource?: URI): ITextFileEditorModel[]; private getFileModels(arg1?: any): ITextFileEditorModel[] { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index db1abd15848..e0a68abbce3 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -412,10 +412,22 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private doBackup(): TPromise { + public backup(): TPromise { + if (!this.dirty) { + return TPromise.as(null); + } + + return this.doBackup(true); + } + + private doBackup(immediate?: boolean): TPromise { // Cancel any currently running backups to make this the one that succeeds this.cancelBackupPromises(); + if (immediate) { + return this.fileService.backupFile(this.resource, this.getValue()).then(f => void 0); + } + // Create new backup promise and keep it const promise = TPromise.timeout(1000).then(() => { this.fileService.backupFile(this.resource, this.getValue()); // Very important here to not return the promise because if the timeout promise is canceled it will bubble up the error otherwise - do not change diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index dcc091f51c4..e564139e8b4 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -217,6 +217,8 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport save(options?: IModelSaveOptions): TPromise; + backup(): TPromise; + setRestoreResource(resource: URI): void; revert(): TPromise; -- GitLab