Skip to content

Commit

Permalink
working copy - properly return false when save fails (#139764)
Browse files Browse the repository at this point in the history
  • Loading branch information
bpasero committed Dec 30, 2021
1 parent fdb4587 commit 42a43eb
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 18 deletions.
12 changes: 6 additions & 6 deletions src/vs/workbench/services/textfile/browser/textFileService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,14 +426,14 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
}
}

// Revert the source if result is success
if (success) {
await this.revert(source);

return target;
if (!success) {
return undefined;

This comment has been minimized.

Copy link
@yecril71pl

yecril71pl Dec 30, 2021

just return; would be enough

This comment has been minimized.

Copy link
@bpasero

bpasero Dec 30, 2021

Author Member

I prefer it this way because the method explicitly sets a return value XY | undefined and this makes clear what gets returned.

}

return undefined;
// Revert the source
await this.revert(source);

return target;
}

private async doSaveAsTextFile(sourceModel: IResolvedTextEditorModel | ITextModel, source: URI, target: URI, options?: ITextFileSaveOptions): Promise<boolean> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
await this.doSave(options);
this.logService.trace('[text file model] save() - exit', this.resource.toString(true));

return true;
return this.hasState(TextFileEditorModelState.SAVED);
}

private async doSave(options: ITextFileSaveOptions): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,26 @@ suite('Files - TextFileEditorModel', () => {
assert.ok(!accessor.modelService.getModel(model.resource));
});

test('save - returns false when save fails', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);

await model.resolve();

accessor.fileService.writeShouldThrowError = new Error('failed to write');
try {
const res = await model.save({ force: true });
assert.strictEqual(res, false);
} finally {
accessor.fileService.writeShouldThrowError = undefined;
}

const res = await model.save({ force: true });
assert.strictEqual(res, true);

model.dispose();
assert.ok(!accessor.modelService.getModel(model.resource));
});

test('save error (generic)', async function () {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,15 @@ export abstract class BaseFileWorkingCopyManager<M extends IFileWorkingCopyModel
private async saveWithFallback(workingCopy: W): Promise<void> {

// First try regular save
let saveFailed = false;
let saveSuccess = false;
try {
await workingCopy.save();
saveSuccess = await workingCopy.save();
} catch (error) {
saveFailed = true;
// Ignore
}

// Then fallback to backup if that exists
if (saveFailed || workingCopy.isDirty()) {
if (!saveSuccess || workingCopy.isDirty()) {
const backup = await this.workingCopyBackupService.resolve(workingCopy);
if (backup) {
await this.fileService.writeFile(workingCopy.resource, backup.value, { unlock: true });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,10 @@ export class FileWorkingCopyManager<S extends IStoredFileWorkingCopyModel, U ext
await targetStoredFileWorkingCopy.model?.update(sourceContents, CancellationToken.None);

// Save target
await targetStoredFileWorkingCopy.save({ ...options, force: true /* force to save, even if not dirty (https://github.com/microsoft/vscode/issues/99619) */ });
const success = await targetStoredFileWorkingCopy.save({ ...options, force: true /* force to save, even if not dirty (https://github.com/microsoft/vscode/issues/99619) */ });
if (!success) {
return undefined;
}

// Revert the source
await sourceWorkingCopy?.revert();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ export class StoredFileWorkingCopy<M extends IStoredFileWorkingCopyModel> extend
await this.doSave(options);
this.trace('[stored file working copy] save() - exit');

return true;
return this.hasState(StoredFileWorkingCopyState.SAVED);
}

private async doSave(options: IStoredFileWorkingCopySaveOptions): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,6 @@ export class NativeWorkingCopyBackupTracker extends WorkingCopyBackupTracker imp
}

// If we still have dirty working copies, revert those directly
// unless the revert operation was not successful (e.g. cancelled)
await Promises.settled(dirtyWorkingCopies.map(workingCopy => workingCopy.isDirty() ? workingCopy.revert(revertOptions) : Promise.resolve()));
}, localize('revertBeforeShutdown', "Reverting editors with unsaved changes is taking longer than expected..."));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,22 @@ suite('StoredFileWorkingCopy', function () {
assert.ok(error);
});

test('save - returns false when save fails', async function () {
await workingCopy.resolve();

try {
accessor.fileService.writeShouldThrowError = new FileOperationError('write error', FileOperationResult.FILE_PERMISSION_DENIED);

const res = await workingCopy.save({ force: true });
assert.strictEqual(res, false);
} finally {
accessor.fileService.writeShouldThrowError = undefined;
}

const res = await workingCopy.save({ force: true });
assert.strictEqual(res, true);
});

test('save participant', async () => {
await workingCopy.resolve();

Expand Down
8 changes: 4 additions & 4 deletions src/vscode-dts/vscode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,8 @@ declare module 'vscode' {
/**
* Save the underlying file.
*
* @return A promise that will resolve to true when the file
* has been saved. If the file was not dirty or the save failed,
* will return false.
* @return A promise that will resolve to `true` when the file
* has been saved. If the save failed, will return `false`.
*/
save(): Thenable<boolean>;

Expand Down Expand Up @@ -10984,7 +10983,8 @@ declare module 'vscode' {
* Save all dirty files.
*
* @param includeUntitled Also save files that have been created during this session.
* @return A thenable that resolves when the files have been saved.
* @return A thenable that resolves when the files have been saved. Will return `false`
* for any file that failed to save.

This comment has been minimized.

Copy link
@yecril71pl

yecril71pl Dec 30, 2021

if any file failed

*/
export function saveAll(includeUntitled?: boolean): Thenable<boolean>;

Expand Down

5 comments on commit 42a43eb

@yecril71pl
Copy link

@yecril71pl yecril71pl commented on 42a43eb Dec 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to properly return false, you should return false. While I do not see any need to properly return false, the commit summary does not reflect what happened.

@bpasero
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, what is the issue? Maybe best if you showcase it in a PR with a suggested fix?

@yecril71pl
Copy link

@yecril71pl yecril71pl commented on 42a43eb Dec 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, what is the issue? Maybe best if you showcase it in a PR with a suggested fix?

There is no issue, just a trifle: the commit summary does not match the commit. I would say:

TextFileService.save: return value undefined on failure

That would be easier to find in changes.

@bpasero
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. The actual behaviour change are these two:

42a43eb#diff-013267b5ed968fe933b3c450853a4492a20babb59f1ce1a77cb76fe3d6cb1818R714

and

42a43eb#diff-0dc06494259be4c5123e46208d2f359c93e85ad6ab409a515482f34123a40e1aR767

Previously they would always return true even if the save failed and the working copy was dirty. Now we probe on the saved state.

The other changes are more or less an adoption of that.

@yecril71pl
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK then

TextFileService.save: return whether document saved

Please sign in to comment.