Skip to content

Commit 4db079b

Browse files
committed
Validate workspace undo again after async prompt
1 parent 0cc09fc commit 4db079b

1 file changed

Lines changed: 64 additions & 66 deletions

File tree

src/vs/platform/undoRedo/common/undoRedoService.ts

Lines changed: 64 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -376,22 +376,29 @@ export class UndoRedoService implements IUndoRedoService {
376376
}
377377
}
378378

379-
private _verifyWorkspaceUndo(element: WorkspaceStackElement): WorkspaceVerificationResult {
379+
private _getAffectedEditStacks(element: WorkspaceStackElement): ResourceEditStack[] {
380+
const affectedEditStacks: ResourceEditStack[] = [];
381+
for (const strResource of element.strResources) {
382+
affectedEditStacks.push(this._editStacks.get(strResource)!);
383+
}
384+
return affectedEditStacks;
385+
}
386+
387+
private _checkWorkspaceUndo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[]): WorkspaceVerificationError | null {
380388
if (element.removedResources) {
389+
this._splitPastWorkspaceElement(element, element.removedResources);
381390
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
382-
return new InvalidWorkspaceVerificationResult(message, element.removedResources);
391+
this._notificationService.info(message);
392+
return new WorkspaceVerificationError(this.undo(resource));
383393
}
384394
if (element.invalidatedResources) {
395+
this._splitPastWorkspaceElement(element, element.invalidatedResources);
385396
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage());
386-
return new InvalidWorkspaceVerificationResult(message, element.invalidatedResources);
397+
this._notificationService.info(message);
398+
return new WorkspaceVerificationError(this.undo(resource));
387399
}
388400

389401
// this must be the last past element in all the impacted resources!
390-
const affectedEditStacks: ResourceEditStack[] = [];
391-
for (const strResource of element.strResources) {
392-
affectedEditStacks.push(this._editStacks.get(strResource)!);
393-
}
394-
395402
const cannotUndoDueToResources: URI[] = [];
396403
for (const editStack of affectedEditStacks) {
397404
if (editStack.past.length === 0 || editStack.past[editStack.past.length - 1] !== element) {
@@ -400,29 +407,27 @@ export class UndoRedoService implements IUndoRedoService {
400407
}
401408

402409
if (cannotUndoDueToResources.length > 0) {
410+
this._splitPastWorkspaceElement(element, null);
403411
const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
404412
const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
405-
return new InvalidWorkspaceVerificationResult(message, null);
413+
this._notificationService.info(message);
414+
return new WorkspaceVerificationError(this.undo(resource));
406415
}
407416

408-
return new ValidWorkspaceVerificationResult(affectedEditStacks);
417+
return null;
409418
}
410419

411420
private _workspaceUndo(resource: URI, element: WorkspaceStackElement): Promise<void> | void {
412-
const verificationResult = this._verifyWorkspaceUndo(element);
413-
if (verificationResult.type === WorkspaceVerificationResultType.Invalid) {
414-
// cannot apply the workspace undo
415-
this._splitPastWorkspaceElement(element, verificationResult.ignoreResources);
416-
this._notificationService.info(verificationResult.message);
417-
return this.undo(resource);
421+
const affectedEditStacks = this._getAffectedEditStacks(element);
422+
const verificationError = this._checkWorkspaceUndo(resource, element, affectedEditStacks);
423+
if (verificationError) {
424+
return verificationError.returnValue;
418425
}
419-
420-
const affectedEditStacks = verificationResult.affectedEditStacks;
421426
return this._confirmAndExecuteWorkspaceUndo(resource, element, affectedEditStacks);
422427
}
423428

424429
private async _confirmAndExecuteWorkspaceUndo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[]): Promise<void> {
425-
return this._dialogService.show(
430+
const result = await this._dialogService.show(
426431
Severity.Info,
427432
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label),
428433
[
@@ -433,21 +438,29 @@ export class UndoRedoService implements IUndoRedoService {
433438
{
434439
cancelId: 2
435440
}
436-
).then((result) => {
437-
if (result.choice === 2) {
438-
// cancel
439-
return;
440-
} else if (result.choice === 0) {
441-
for (const editStack of affectedEditStacks) {
442-
editStack.past.pop();
443-
editStack.future.push(element);
444-
}
445-
return this._safeInvoke(element, () => element.actual.undo());
446-
} else {
447-
this._splitPastWorkspaceElement(element, null);
448-
return this.undo(resource);
441+
);
442+
443+
// At this point, it is possible that the element has been made invalid in the meantime (due to the confirmation await)
444+
const verificationError = this._checkWorkspaceUndo(resource, element, affectedEditStacks);
445+
if (verificationError) {
446+
return verificationError.returnValue;
447+
}
448+
449+
if (result.choice === 2) {
450+
// cancel
451+
return;
452+
}
453+
454+
if (result.choice === 0) {
455+
for (const editStack of affectedEditStacks) {
456+
editStack.past.pop();
457+
editStack.future.push(element);
449458
}
450-
});
459+
return this._safeInvoke(element, () => element.actual.undo());
460+
} else {
461+
this._splitPastWorkspaceElement(element, null);
462+
return this.undo(resource);
463+
}
451464
}
452465

453466
private _resourceUndo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
@@ -490,48 +503,46 @@ export class UndoRedoService implements IUndoRedoService {
490503
return false;
491504
}
492505

493-
private _verifyWorkspaceRedo(element: WorkspaceStackElement): WorkspaceVerificationResult {
506+
private _checkWorkspaceRedo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[]): WorkspaceVerificationError | null {
494507
if (element.removedResources) {
508+
this._splitFutureWorkspaceElement(element, element.removedResources);
495509
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
496-
return new InvalidWorkspaceVerificationResult(message, element.removedResources);
510+
this._notificationService.info(message);
511+
return new WorkspaceVerificationError(this.redo(resource));
497512
}
498513
if (element.invalidatedResources) {
514+
this._splitFutureWorkspaceElement(element, element.invalidatedResources);
499515
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage());
500-
return new InvalidWorkspaceVerificationResult(message, element.invalidatedResources);
516+
this._notificationService.info(message);
517+
return new WorkspaceVerificationError(this.redo(resource));
501518
}
502519

503520
// this must be the last future element in all the impacted resources!
504-
let affectedEditStacks: ResourceEditStack[] = [];
505-
for (const strResource of element.strResources) {
506-
affectedEditStacks.push(this._editStacks.get(strResource)!);
507-
}
508-
509-
let cannotRedoDueToResources: URI[] = [];
521+
const cannotRedoDueToResources: URI[] = [];
510522
for (const editStack of affectedEditStacks) {
511523
if (editStack.future.length === 0 || editStack.future[editStack.future.length - 1] !== element) {
512524
cannotRedoDueToResources.push(editStack.resource);
513525
}
514526
}
515527

516528
if (cannotRedoDueToResources.length > 0) {
529+
this._splitFutureWorkspaceElement(element, null);
517530
const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
518531
const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
519-
return new InvalidWorkspaceVerificationResult(message, null);
532+
this._notificationService.info(message);
533+
return new WorkspaceVerificationError(this.redo(resource));
520534
}
521535

522-
return new ValidWorkspaceVerificationResult(affectedEditStacks);
536+
return null;
523537
}
524538

525539
private _workspaceRedo(resource: URI, element: WorkspaceStackElement): Promise<void> | void {
526-
const verificationResult = this._verifyWorkspaceRedo(element);
527-
if (verificationResult.type === WorkspaceVerificationResultType.Invalid) {
528-
// cannot apply the workspace redo
529-
this._splitFutureWorkspaceElement(element, verificationResult.ignoreResources);
530-
this._notificationService.info(verificationResult.message);
531-
return this.redo(resource);
540+
const affectedEditStacks = this._getAffectedEditStacks(element);
541+
const verificationError = this._checkWorkspaceRedo(resource, element, affectedEditStacks);
542+
if (verificationError) {
543+
return verificationError.returnValue;
532544
}
533545

534-
const affectedEditStacks = verificationResult.affectedEditStacks;
535546
for (const editStack of affectedEditStacks) {
536547
editStack.future.pop();
537548
editStack.past.push(element);
@@ -571,21 +582,8 @@ export class UndoRedoService implements IUndoRedoService {
571582
}
572583
}
573584

574-
const enum WorkspaceVerificationResultType {
575-
Invalid = 0,
576-
Valid = 1
577-
}
578-
class InvalidWorkspaceVerificationResult {
579-
public readonly type = WorkspaceVerificationResultType.Invalid;
580-
constructor(
581-
public readonly message: string,
582-
public readonly ignoreResources: RemovedResources | null
583-
) { }
584-
}
585-
class ValidWorkspaceVerificationResult {
586-
public readonly type = WorkspaceVerificationResultType.Valid;
587-
constructor(public readonly affectedEditStacks: ResourceEditStack[]) { }
585+
class WorkspaceVerificationError {
586+
constructor(public readonly returnValue: Promise<void> | void) { }
588587
}
589-
type WorkspaceVerificationResult = InvalidWorkspaceVerificationResult | ValidWorkspaceVerificationResult;
590588

591589
registerSingleton(IUndoRedoService, UndoRedoService);

0 commit comments

Comments
 (0)