diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index c2adde705ccab5d9f52441038d98b2ac522922c2..73d0fd2004a1eef8607fb0f7c7ef5b428b45a6c5 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -209,7 +209,15 @@ export class EditorPart extends Part implements IEditorPart { // Close editor when input provided and input gets disposed if (input) { this.visibleInputListeners[position] = input.addListener(EventType.DISPOSE, () => { - this.closeEditors(false, input).done(null, errors.onUnexpectedError); + + // To prevent race conditions, we call the close in a timeout because it can well be + // that an input is being disposed with the intent to replace it with some other input + // right after. + setTimeout(() => { + if (input === this.visibleInputs[position]) { + this.closeEditors(false, input).done(null, errors.onUnexpectedError); + } + }, 0); }); } diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index ef61435ce44ec7c3473e12859145660b8c4d9ffd..cbcd6b576cf3745142a478b17b0dbaf9fd517151 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -1527,9 +1527,8 @@ export abstract class BaseSaveFileAction extends BaseActionWithErrorReporting { encodingOfSource = textModel && textModel.getEncoding(); // text model can be null e.g. if this is a binary file! } - let savePromise: TPromise; - // Special case: an untitled file with associated path gets saved directly unless "saveAs" is true + let savePromise: TPromise; if (!this.isSaveAs() && source.scheme === 'untitled' && this.untitledEditorService.hasAssociatedFilePath(source)) { savePromise = this.textFileService.save(source).then((result) => { if (result) { @@ -1550,9 +1549,6 @@ export abstract class BaseSaveFileAction extends BaseActionWithErrorReporting { return; } - // Add to working files - this.textFileService.getWorkingFilesModel().addEntry(target); - // Reopen editors for the resource based on the positions let reopenPromise = Promise.as(null); if (target.toString() !== source.toString() && positionsOfSource.length) { @@ -1569,11 +1565,7 @@ export abstract class BaseSaveFileAction extends BaseActionWithErrorReporting { }); } - return reopenPromise.then(() => { - - // Revert source - this.textFileService.revert(source); - }); + return reopenPromise; }); } @@ -1653,16 +1645,17 @@ export abstract class BaseSaveAllAction extends BaseActionWithErrorReporting { protected doRun(): TPromise { + // Store mimes per untitled file to restore later + const mapUntitledToProperties: {[resource:string]: { mime: string; encoding: string; }} = Object.create(null); + this.textFileService.getDirty() + .filter(r => r.scheme === 'untitled') // All untitled resources^ + .map(r => this.untitledEditorService.get(r)) // Mapped to their inputs + .filter(i => !!i) // If possible :) + .forEach(i => mapUntitledToProperties[i.getResource().toString()] = { mime: i.getMime(), encoding: i.getEncoding() }); + // Save all return this.textFileService.saveAll(this.includeUntitled()).then((result) => { - // add all targets to working files - result.results.forEach((res) => { - if (res.success && res.target) { - this.textFileService.getWorkingFilesModel().addEntry(res.target); - } - }); - // all saved - now try to reopen saved untitled ones if (this.includeUntitled()) { let untitledResults = result.results.filter((res) => res.source.scheme === 'untitled'); @@ -1674,12 +1667,12 @@ export abstract class BaseSaveAllAction extends BaseActionWithErrorReporting { let positions = findSaveAsPositions(this.editorService, res.source); let mimeOfSource: string; - let selectedMime = this.untitledEditorService.get(res.source).getMime(); + let selectedMime = mapUntitledToProperties[res.source.toString()] && mapUntitledToProperties[res.source.toString()].mime; if (!isUnspecific(selectedMime)) { mimeOfSource = [selectedMime, MIME_TEXT].join(', '); } - let encodingOfSource: string = this.untitledEditorService.get(res.source).getEncoding(); + let encodingOfSource: string = mapUntitledToProperties[res.source.toString()] && mapUntitledToProperties[res.source.toString()].encoding; let targetInput = this.instantiationService.createInstance(FileEditorInput, res.target, mimeOfSource, encodingOfSource); @@ -1708,17 +1701,7 @@ export abstract class BaseSaveAllAction extends BaseActionWithErrorReporting { }); } - // After reopen, revert untitled ones - return reopenPromise.then(() => { - return Promise.join(untitledResults.map((res) => { - let revertPromise = Promise.as(null); - if (res.success) { - revertPromise = this.textFileService.revert(res.source); - } - - return revertPromise; - })); - }); + return reopenPromise; } }); } diff --git a/src/vs/workbench/parts/files/electron-browser/textFileServices.ts b/src/vs/workbench/parts/files/electron-browser/textFileServices.ts index c4cc9cfae46ad335ba711d37a62fcd87c71be5e1..4c937a8ce92ebae32f641554bf62b6d1ba9e706a 100644 --- a/src/vs/workbench/parts/files/electron-browser/textFileServices.ts +++ b/src/vs/workbench/parts/files/electron-browser/textFileServices.ts @@ -70,17 +70,6 @@ export class TextFileService extends BrowserTextFileService { // Save if (confirm === ConfirmResult.SAVE) { return this.saveAll(true /* includeUntitled */).then((result) => { - - // Dispose saved untitled ones to not leave them around as dirty - result.results.forEach((res) => { - if (res.success && res.source.scheme === 'untitled') { - let input = this.untitledEditorService.get(res.source); - if (input) { - input.dispose(); - } - } - }); - if (result.results.some((r) => !r.success)) { return true; // veto if some saves failed } @@ -326,13 +315,22 @@ export class TextFileService extends BrowserTextFileService { // We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before) if (model) { - return this.fileService.updateContent(target, model.getValue(), { charset: model.getEncoding() }).then(() => { - return target; - }); + return this.fileService.updateContent(target, model.getValue(), { charset: model.getEncoding() }); } // Otherwise we can only copy - return this.fileService.copyFile(resource, target).then(() => target); + return this.fileService.copyFile(resource, target); + }).then(() => { + + // Add target to working files because this is an operation that indicates activity + this.getWorkingFilesModel().addEntry(target); + + // Revert the source + return this.revert(resource).then(() => { + + // Done: return target + return target; + }); }); }