提交 0ff871ce 编写于 作者: B Benjamin Pasero

Hot Exit: properly deal with backups to files that got deleted (fixes #14054)

上级 2e77e6c9
......@@ -126,6 +126,11 @@ export interface IFileService {
*/
updateOptions(options: any): void;
/**
* Returns the preferred encoding to use for a given resource.
*/
getEncoding(resource: URI): string;
/**
* Frees up any resources occupied by this service.
*/
......
......@@ -59,6 +59,27 @@ export class BackupRestorer implements IWorkbenchContribution {
});
}
private doResolveOpenedBackups(backups: URI[]): TPromise<URI[]> {
const stacks = this.groupService.getStacksModel();
const restorePromises: TPromise<any>[] = [];
const unresolved: URI[] = [];
backups.forEach(backup => {
if (stacks.isOpen(backup)) {
if (backup.scheme === 'file') {
restorePromises.push(this.textModelResolverService.createModelReference(backup).then(null, () => unresolved.push(backup)));
} else if (backup.scheme === 'untitled') {
restorePromises.push(this.untitledEditorService.get(backup).resolve().then(null, () => unresolved.push(backup)));
}
} else {
unresolved.push(backup);
}
});
return TPromise.join(restorePromises).then(() => unresolved, () => unresolved);
}
private doOpenEditors(inputs: URI[]): TPromise<void> {
const stacks = this.groupService.getStacksModel();
const hasOpenedEditors = stacks.groups.length > 0;
......@@ -83,27 +104,6 @@ export class BackupRestorer implements IWorkbenchContribution {
return this.editorService.createInput({ resource });
}
private doResolveOpenedBackups(backups: URI[]): TPromise<URI[]> {
const stacks = this.groupService.getStacksModel();
const restorePromises: TPromise<any>[] = [];
const unresolved: URI[] = [];
backups.forEach(backup => {
if (stacks.isOpen(backup)) {
if (backup.scheme === 'file') {
restorePromises.push(this.textModelResolverService.createModelReference(backup).then(null, () => unresolved.push(backup)));
} else if (backup.scheme === 'untitled') {
restorePromises.push(this.untitledEditorService.get(backup).resolve().then(null, () => unresolved.push(backup)));
}
} else {
unresolved.push(backup);
}
});
return TPromise.join(restorePromises).then(() => unresolved, () => unresolved);
}
public getId(): string {
return 'vs.backup.backupRestorer';
}
......
......@@ -289,6 +289,10 @@ export class FileService implements IFileService {
this.raw.unwatchFileChanges(arg1);
}
public getEncoding(resource: uri): string {
return this.raw.getEncoding(resource);
}
public dispose(): void {
this.toUnbind = dispose(this.toUnbind);
......
......@@ -518,7 +518,7 @@ export class FileService implements IFileService {
});
}
private getEncoding(resource: uri, preferredEncoding?: string): string {
public getEncoding(resource: uri, preferredEncoding?: string): string {
let fileEncoding: string;
const override = this.getEncodingOverride(resource);
......
......@@ -19,11 +19,11 @@ import types = require('vs/base/common/types');
import { IModelContentChangedEvent, IRawText } from 'vs/editor/common/editorCommon';
import { IMode } from 'vs/editor/common/modes';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, IModelSaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, IModelSaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent } from 'vs/workbench/services/textfile/common/textfiles';
import { EncodingMode, EditorModel } from 'vs/workbench/common/editor';
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
import { IFileService, IFileStat, IFileOperationResult, FileOperationResult } from 'vs/platform/files/common/files';
import { IFileService, IFileStat, IFileOperationResult, FileOperationResult, IContent } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IModeService } from 'vs/editor/common/services/modeService';
......@@ -196,7 +196,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Emit file change event
this._onDidStateChange.fire(StateChange.REVERTED);
}, (error) => {
}, error => {
// FileNotFound means the file got deleted meanwhile, so emit revert event because thats ok
if ((<IFileOperationResult>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
......@@ -233,7 +233,45 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
// Resolve Content
return this.textFileService.resolveTextContent(this.resource, { acceptTextOnly: true, etag: etag, encoding: this.preferredEncoding }).then((content) => {
return this.textFileService.resolveTextContent(this.resource, { acceptTextOnly: true, etag: etag, encoding: this.preferredEncoding }).then(content => this.loadWithContent(content), error => {
const result = (<IFileOperationResult>error).fileOperationResult;
// NotModified status is expected and can be handled gracefully
if (result === FileOperationResult.FILE_NOT_MODIFIED_SINCE) {
this.setDirty(false); // Ensure we are not tracking a stale state
return TPromise.as<EditorModel>(this);
}
// FileNotFound needs to be handled if we have a backup
if (result === FileOperationResult.FILE_NOT_FOUND) {
if (!this.textEditorModel && !this.createTextEditorModelPromise) {
return this.backupFileService.loadBackupResource(this.resource).then(backup => {
if (!!backup) {
const content: IContent = {
resource: this.resource,
name: paths.basename(this.resource.fsPath),
mtime: Date.now(),
etag: void 0,
value: '', /* will be filled later from backup */
encoding: this.fileService.getEncoding(this.resource)
};
return this.loadWithContent(content);
}
// Otherwise bubble up the error
return TPromise.wrapError(error);
}, ignoreError => TPromise.wrapError(error));
}
}
// Otherwise bubble up the error
return TPromise.wrapError(error);
});
}
private loadWithContent(content: IRawTextContent | IContent): TPromise<EditorModel> {
diag('load() - resolved content', this.resource, new Date());
// Telemetry
......@@ -314,7 +352,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.toDispose.push(this.textEditorModel.onDidChangeRawContent((e: IModelContentChangedEvent) => this.onModelContentChanged(e)));
return this;
}, (error) => {
}, error => {
this.createTextEditorModelPromise = null;
return TPromise.wrapError(error);
......@@ -324,18 +362,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return this.createTextEditorModelPromise;
});
}
}, (error) => {
// NotModified status code is expected and can be handled gracefully
if ((<IFileOperationResult>error).fileOperationResult === FileOperationResult.FILE_NOT_MODIFIED_SINCE) {
this.setDirty(false); // Ensure we are not tracking a stale state
return TPromise.as<EditorModel>(this);
}
// Otherwise bubble up the error
return TPromise.wrapError(error);
});
}
protected getOrCreateMode(modeService: IModeService, preferredModeIds: string, firstLineText?: string): TPromise<IMode> {
......@@ -561,7 +587,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Emit File Saved Event
this._onDidStateChange.fire(StateChange.SAVED);
}, (error) => {
}, error => {
diag(`doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource, new Date());
// Remove from pending saves
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册