mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Merge pull request #183473 from microsoft/aamunger/scratchpadHotExit
backup scratchpads on window close for hotexit=onexit
This commit is contained in:
commit
65600c155b
|
@ -114,10 +114,10 @@ export class NativeWorkingCopyBackupTracker extends WorkingCopyBackupTracker imp
|
||||||
// Trigger backup if configured and enabled for shutdown reason
|
// Trigger backup if configured and enabled for shutdown reason
|
||||||
let backups: IWorkingCopy[] = [];
|
let backups: IWorkingCopy[] = [];
|
||||||
let backupError: Error | undefined = undefined;
|
let backupError: Error | undefined = undefined;
|
||||||
const backup = await this.shouldBackupBeforeShutdown(reason);
|
const modifiedWorkingCopiesToBackup = await this.shouldBackupBeforeShutdown(reason, modifiedWorkingCopies);
|
||||||
if (backup) {
|
if (modifiedWorkingCopiesToBackup.length > 0) {
|
||||||
try {
|
try {
|
||||||
const backupResult = await this.backupBeforeShutdown(modifiedWorkingCopies);
|
const backupResult = await this.backupBeforeShutdown(modifiedWorkingCopiesToBackup);
|
||||||
backups = backupResult.backups;
|
backups = backupResult.backups;
|
||||||
backupError = backupResult.error;
|
backupError = backupResult.error;
|
||||||
|
|
||||||
|
@ -162,49 +162,53 @@ export class NativeWorkingCopyBackupTracker extends WorkingCopyBackupTracker imp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async shouldBackupBeforeShutdown(reason: ShutdownReason): Promise<boolean> {
|
private async shouldBackupBeforeShutdown(reason: ShutdownReason, modifiedWorkingCopies: readonly IWorkingCopy[]): Promise<readonly IWorkingCopy[]> {
|
||||||
let backup: boolean | undefined;
|
|
||||||
if (!this.filesConfigurationService.isHotExitEnabled) {
|
if (!this.filesConfigurationService.isHotExitEnabled) {
|
||||||
backup = false; // never backup when hot exit is disabled via settings
|
return []; // never backup when hot exit is disabled via settings
|
||||||
} else if (this.environmentService.isExtensionDevelopment) {
|
|
||||||
backup = true; // always backup closing extension development window without asking to speed up debugging
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// When quit is requested skip the confirm callback and attempt to backup all workspaces.
|
|
||||||
// When quit is not requested the confirm callback should be shown when the window being
|
|
||||||
// closed is the only VS Code window open, except for on Mac where hot exit is only
|
|
||||||
// ever activated when quit is requested.
|
|
||||||
|
|
||||||
switch (reason) {
|
|
||||||
case ShutdownReason.CLOSE:
|
|
||||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
|
|
||||||
backup = true; // backup if a folder is open and onExitAndWindowClose is configured
|
|
||||||
} else if (await this.nativeHostService.getWindowCount() > 1 || isMacintosh) {
|
|
||||||
backup = false; // do not backup if a window is closed that does not cause quitting of the application
|
|
||||||
} else {
|
|
||||||
backup = true; // backup if last window is closed on win/linux where the application quits right after
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ShutdownReason.QUIT:
|
|
||||||
backup = true; // backup because next start we restore all backups
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ShutdownReason.RELOAD:
|
|
||||||
backup = true; // backup because after window reload, backups restore
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ShutdownReason.LOAD:
|
|
||||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
|
|
||||||
backup = true; // backup if a folder is open and onExitAndWindowClose is configured
|
|
||||||
} else {
|
|
||||||
backup = false; // do not backup because we are switching contexts
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return backup;
|
if (this.environmentService.isExtensionDevelopment) {
|
||||||
|
return modifiedWorkingCopies; // always backup closing extension development window without asking to speed up debugging
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
|
||||||
|
// Window Close
|
||||||
|
case ShutdownReason.CLOSE:
|
||||||
|
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
|
||||||
|
return modifiedWorkingCopies; // backup if a workspace/folder is open and onExitAndWindowClose is configured
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMacintosh || await this.nativeHostService.getWindowCount() > 1) {
|
||||||
|
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
|
||||||
|
return modifiedWorkingCopies.filter(modifiedWorkingCopy => modifiedWorkingCopy.capabilities & WorkingCopyCapabilities.Scratchpad); // backup scratchpads automatically to avoid user confirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
return []; // do not backup if a window is closed that does not cause quitting of the application
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedWorkingCopies; // backup if last window is closed on win/linux where the application quits right after
|
||||||
|
|
||||||
|
// Application Quit
|
||||||
|
case ShutdownReason.QUIT:
|
||||||
|
return modifiedWorkingCopies; // backup because next start we restore all backups
|
||||||
|
|
||||||
|
// Window Reload
|
||||||
|
case ShutdownReason.RELOAD:
|
||||||
|
return modifiedWorkingCopies; // backup because after window reload, backups restore
|
||||||
|
|
||||||
|
// Workspace Change
|
||||||
|
case ShutdownReason.LOAD:
|
||||||
|
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
|
||||||
|
if (this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
|
||||||
|
return modifiedWorkingCopies; // backup if a workspace/folder is open and onExitAndWindowClose is configured
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedWorkingCopies.filter(modifiedWorkingCopy => modifiedWorkingCopy.capabilities & WorkingCopyCapabilities.Scratchpad); // backup scratchpads automatically to avoid user confirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
return []; // do not backup because we are switching contexts with no workspace/folder open
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private showErrorDialog(msg: string, workingCopies: readonly IWorkingCopy[], error?: Error): void {
|
private showErrorDialog(msg: string, workingCopies: readonly IWorkingCopy[], error?: Error): void {
|
||||||
|
|
|
@ -565,6 +565,109 @@ suite('WorkingCopyBackupTracker (native)', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
suite('"onExit" setting - scratchpad', () => {
|
||||||
|
test('should hot exit (reason: CLOSE, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: CLOSE, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: CLOSE, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, true, false);
|
||||||
|
});
|
||||||
|
test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, false, true);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: LOAD, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, true, false);
|
||||||
|
});
|
||||||
|
test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, false, true);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: LOAD, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, true, false);
|
||||||
|
});
|
||||||
|
test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, false, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('"onExitAndWindowClose" setting - scratchpad', () => {
|
||||||
|
test('should hot exit (reason: CLOSE, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: CLOSE, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, false, !!isMacintosh);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: CLOSE, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, true, false);
|
||||||
|
});
|
||||||
|
test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, false, true);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, true, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, false, false);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: LOAD, windows: single, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, true, false);
|
||||||
|
});
|
||||||
|
test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, false, true);
|
||||||
|
});
|
||||||
|
test('should hot exit (reason: LOAD, windows: multiple, workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, true, false);
|
||||||
|
});
|
||||||
|
test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () {
|
||||||
|
return scratchpadHotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, false, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: boolean, shouldVeto: boolean): Promise<void> {
|
async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: boolean, shouldVeto: boolean): Promise<void> {
|
||||||
const { accessor, cleanup } = await createTracker();
|
const { accessor, cleanup } = await createTracker();
|
||||||
|
|
||||||
|
@ -604,5 +707,60 @@ suite('WorkingCopyBackupTracker (native)', function () {
|
||||||
|
|
||||||
await cleanup();
|
await cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function scratchpadHotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: boolean, shouldVeto: boolean): Promise<void> {
|
||||||
|
const { accessor, cleanup } = await createTracker();
|
||||||
|
|
||||||
|
class TestBackupWorkingCopy extends TestWorkingCopy {
|
||||||
|
|
||||||
|
constructor(resource: URI) {
|
||||||
|
super(resource);
|
||||||
|
|
||||||
|
accessor.workingCopyService.registerWorkingCopy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override capabilities = WorkingCopyCapabilities.Untitled | WorkingCopyCapabilities.Scratchpad;
|
||||||
|
|
||||||
|
override isDirty(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override isModified(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set hot exit config
|
||||||
|
accessor.filesConfigurationService.testOnFilesConfigurationChange({ files: { hotExit: setting } });
|
||||||
|
|
||||||
|
// Set empty workspace if required
|
||||||
|
if (!workspace) {
|
||||||
|
accessor.contextService.setWorkspace(new Workspace('empty:1508317022751'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set multiple windows if required
|
||||||
|
if (multipleWindows) {
|
||||||
|
accessor.nativeHostService.windowCount = Promise.resolve(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set cancel to force a veto if hot exit does not trigger
|
||||||
|
accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL);
|
||||||
|
|
||||||
|
const resource = toResource.call(this, '/path/custom.txt');
|
||||||
|
new TestBackupWorkingCopy(resource);
|
||||||
|
|
||||||
|
const event = new TestBeforeShutdownEvent();
|
||||||
|
event.reason = shutdownReason;
|
||||||
|
accessor.lifecycleService.fireBeforeShutdown(event);
|
||||||
|
|
||||||
|
const veto = await event.value;
|
||||||
|
assert.ok(typeof event.finalValue === 'function'); // assert the tracker uses the internal finalVeto API
|
||||||
|
assert.strictEqual(accessor.workingCopyBackupService.discardedBackups.length, 0); // When hot exit is set, backups should never be cleaned since the confirm result is cancel
|
||||||
|
assert.strictEqual(veto, shouldVeto);
|
||||||
|
|
||||||
|
await cleanup();
|
||||||
|
|
||||||
|
await cleanup();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue