diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 9185dc2a201270afbc06d187dadeabb82f359d1e..ccb5ca5df8ef70bc8adf6598a99f7946357a9356 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -621,6 +621,7 @@ "./vs/workbench/parts/tasks/common/taskTemplates.ts", "./vs/workbench/parts/tasks/common/tasks.ts", "./vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts", + "./vs/workbench/parts/tasks/electron-browser/runAutomaticTasks.ts", "./vs/workbench/parts/tasks/node/tasks.ts", "./vs/workbench/parts/terminal/browser/terminalTab.ts", "./vs/workbench/parts/terminal/browser/terminalWidgetManager.ts", diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 6a53ee82f87a5799680d0ac0b30e73a1840d2f5f..e427d7d24030bbee2b3a01b6cb77080a8ff701d2 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -14,6 +14,7 @@ import { fromNodeEventEmitter, anyEvent, mapEvent, debounceEvent } from 'vs/base import * as objects from 'vs/base/common/objects'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { rimraf } from 'vs/base/node/pfs'; export class ExtensionsLifecycle extends Disposable { @@ -26,15 +27,15 @@ export class ExtensionsLifecycle extends Disposable { super(); } - postUninstall(extension: ILocalExtension): Thenable { + async postUninstall(extension: ILocalExtension): Promise { const script = this.parseScript(extension, 'uninstall'); if (script) { this.logService.info(extension.identifier.id, `Running post uninstall script`); - return this.processesLimiter.queue(() => + await this.processesLimiter.queue(() => this.runLifecycleHook(script.script, 'uninstall', script.args, true, extension) .then(() => this.logService.info(extension.identifier.id, `Finished running post uninstall script`), err => this.logService.error(extension.identifier.id, `Failed to run post uninstall script: ${err}`))); } - return Promise.resolve(); + return rimraf(this.getExtensionStoragePath(extension)).then(null, e => this.logService.error('Error while removing extension storage path', e)); } postInstall(extension: ILocalExtension): Thenable { @@ -66,10 +67,9 @@ export class ExtensionsLifecycle extends Disposable { } private runLifecycleHook(lifecycleHook: string, lifecycleType: string, args: string[], timeout: boolean, extension: ILocalExtension): Thenable { - const extensionStoragePath = posix.join(this.environmentService.globalStorageHome, extension.identifier.id.toLocaleLowerCase()); return new Promise((c, e) => { - const extensionLifecycleProcess = this.start(lifecycleHook, lifecycleType, args, extension, extensionStoragePath); + const extensionLifecycleProcess = this.start(lifecycleHook, lifecycleType, args, extension); let timeoutHandler; const onexit = (error?: string) => { @@ -105,12 +105,12 @@ export class ExtensionsLifecycle extends Disposable { }); } - private start(uninstallHook: string, lifecycleType: string, args: string[], extension: ILocalExtension, extensionStoragePath: string): ChildProcess { + private start(uninstallHook: string, lifecycleType: string, args: string[], extension: ILocalExtension): ChildProcess { const opts = { silent: true, execArgv: undefined, env: objects.mixin(objects.deepClone(process.env), { - VSCODE_EXTENSION_STORAGE_LOCATION: extensionStoragePath + VSCODE_EXTENSION_STORAGE_LOCATION: this.getExtensionStoragePath(extension) }) }; const extensionUninstallProcess = fork(uninstallHook, [`--type=extension-post-${lifecycleType}`, ...args], opts); @@ -147,4 +147,8 @@ export class ExtensionsLifecycle extends Disposable { return extensionUninstallProcess; } + + private getExtensionStoragePath(extension: ILocalExtension): string { + return posix.join(this.environmentService.globalStorageHome, extension.identifier.id.toLocaleLowerCase()); + } } diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 27a179e3cf11d552e68588cf24cf27f53532aa0b..41fe7ac90896f302ebc75bc20f7bda690881a264 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -18,7 +18,7 @@ export const ILifecycleService = createDecorator('lifecycleSe * Note: It is absolutely important to avoid long running promises if possible. Please try hard * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence! */ -export interface WillShutdownEvent { +export interface BeforeShutdownEvent { /** * Allows to veto the shutdown. The veto can be a long running operation but it @@ -27,7 +27,7 @@ export interface WillShutdownEvent { veto(value: boolean | Thenable): void; /** - * The reason why Code will be shutting down. + * The reason why the application will be shutting down. */ reason: ShutdownReason; } @@ -40,7 +40,7 @@ export interface WillShutdownEvent { * Note: It is absolutely important to avoid long running promises if possible. Please try hard * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence! */ -export interface ShutdownEvent { +export interface WillShutdownEvent { /** * Allows to join the shutdown. The promise can be a long running operation but it @@ -49,7 +49,7 @@ export interface ShutdownEvent { join(promise: Thenable): void; /** - * The reason why Code is shutting down. + * The reason why the application is shutting down. */ reason: ShutdownReason; } @@ -137,17 +137,26 @@ export interface ILifecycleService { /** * Fired before shutdown happens. Allows listeners to veto against the - * shutdown. + * shutdown to prevent it from happening. + * + * The event carries a shutdown reason that indicates how the shutdown was triggered. */ - readonly onWillShutdown: Event; + readonly onBeforeShutdown: Event; /** - * Fired when no client is preventing the shutdown from happening. Can be used to dispose heavy resources - * like running processes. Can also be used to save UI state to storage. + * Fired when no client is preventing the shutdown from happening (from onBeforeShutdown). + * Can be used to save UI state even if that is long running through the WillShutdownEvent#join() + * method. * * The event carries a shutdown reason that indicates how the shutdown was triggered. */ - readonly onShutdown: Event; + readonly onWillShutdown: Event; + + /** + * Fired when the shutdown is about to happen after long running shutdown operations + * have finished (from onWillShutdown). This is the right place to dispose resources. + */ + readonly onShutdown: Event; /** * Returns a promise that resolves when a certain lifecycle phase @@ -158,6 +167,7 @@ export interface ILifecycleService { export const NullLifecycleService: ILifecycleService = { _serviceBrand: null, + onBeforeShutdown: Event.None, onWillShutdown: Event.None, onShutdown: Event.None, phase: LifecyclePhase.Restored, diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index fb0b87eef59fb67e2318770cb129d2f42c425044..a21b5c3ba7249c5b60d15cfe91bf92e47741b52a 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { ILifecycleService, WillShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, handleVetos, LifecyclePhaseToString, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, handleVetos, LifecyclePhaseToString, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ipcRenderer as ipc } from 'electron'; import { Event, Emitter } from 'vs/base/common/event'; @@ -22,11 +22,14 @@ export class LifecycleService extends Disposable implements ILifecycleService { _serviceBrand: any; + private readonly _onBeforeShutdown = this._register(new Emitter()); + get onBeforeShutdown(): Event { return this._onBeforeShutdown.event; } + private readonly _onWillShutdown = this._register(new Emitter()); get onWillShutdown(): Event { return this._onWillShutdown.event; } - private readonly _onShutdown = this._register(new Emitter()); - get onShutdown(): Event { return this._onShutdown.event; } + private readonly _onShutdown = this._register(new Emitter()); + get onShutdown(): Event { return this._onShutdown.event; } private readonly _startupKind: StartupKind; get startupKind(): StartupKind { return this._startupKind; } @@ -77,8 +80,8 @@ export class LifecycleService extends Disposable implements ILifecycleService { // store shutdown reason to retrieve next startup this.storageService.store(LifecycleService.LAST_SHUTDOWN_REASON_KEY, JSON.stringify(reply.reason), StorageScope.WORKSPACE); - // trigger onWillShutdown events and veto collecting - this.handleWillShutdown(reply.reason).then(veto => { + // trigger onBeforeShutdown events and veto collecting + this.handleBeforeShutdown(reply.reason).then(veto => { if (veto) { this.logService.trace('lifecycle: onBeforeUnload prevented via veto'); this.storageService.remove(LifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE); @@ -94,17 +97,22 @@ export class LifecycleService extends Disposable implements ILifecycleService { ipc.on('vscode:onWillUnload', (event, reply: { replyChannel: string, reason: ShutdownReason }) => { this.logService.trace(`lifecycle: onWillUnload (reason: ${reply.reason})`); - // trigger onShutdown events and joining - return this.handleShutdown(reply.reason).then(() => { + // trigger onWillShutdown events and joining + return this.handleWillShutdown(reply.reason).then(() => { + + // trigger onShutdown event now that we know we will quit + this._onShutdown.fire(); + + // acknowledge to main side ipc.send(reply.replyChannel, windowId); }); }); } - private handleWillShutdown(reason: ShutdownReason): Promise { + private handleBeforeShutdown(reason: ShutdownReason): Promise { const vetos: (boolean | Thenable)[] = []; - this._onWillShutdown.fire({ + this._onBeforeShutdown.fire({ veto(value) { vetos.push(value); }, @@ -117,10 +125,10 @@ export class LifecycleService extends Disposable implements ILifecycleService { }); } - private handleShutdown(reason: ShutdownReason): Thenable { + private handleWillShutdown(reason: ShutdownReason): Thenable { const joiners: Thenable[] = []; - this._onShutdown.fire({ + this._onWillShutdown.fire({ join(promise) { if (promise) { joiners.push(promise); diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 74a4a65324150619bb1205af800509151cd50342..a9f51628d7c110a3ba28ea6c65d3c0f0605b5c2c 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -53,8 +53,8 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._toDispose.push(_webviewService.registerReviver(MainThreadWebviews.viewType, this)); - lifecycleService.onWillShutdown(e => { - e.veto(this._onWillShutdown()); + lifecycleService.onBeforeShutdown(e => { + e.veto(this._onBeforeShutdown()); }, this, this._toDispose); } @@ -183,7 +183,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv return this._revivers.has(webview.state.viewType) || !!webview.reviver; } - private _onWillShutdown(): boolean { + private _onBeforeShutdown(): boolean { this._webviews.forEach((view) => { if (this.canRevive(view)) { view.state.state = view.webviewState; diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 559ece51d39ff95ca04446a4a8359056365860c8..2a8055169a23b8bb0b1bf9b0575802f49c832928 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -44,6 +44,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer'; import { ILabelService } from 'vs/platform/label/common/label'; import { dirname } from 'vs/base/common/resources'; +import { escape } from 'vs/base/common/strings'; export class CustomTreeViewPanel extends ViewletPanel { @@ -406,7 +407,7 @@ export class CustomTreeView extends Disposable implements ITreeView { this.resetMessageElement(); this._messageValue = message; if (isString(this._messageValue)) { - this.messageElement.innerText = this._messageValue; + this.messageElement.innerText = escape(this._messageValue); } else { this.markdownResult = this.markdownRenderer.render(this._messageValue); DOM.append(this.messageElement, this.markdownResult.element); @@ -626,12 +627,12 @@ class TreeRenderer implements IRenderer { renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void { const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; const treeItemLabel: ITreeItemLabel = node.label ? node.label : resource ? { label: basename(resource.path) } : void 0; - const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : void 0; - const label = treeItemLabel ? treeItemLabel.label : void 0; + const description = isString(node.description) ? escape(node.description) : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : void 0; + const label = treeItemLabel ? escape(treeItemLabel.label) : void 0; const matches = treeItemLabel && treeItemLabel.highlights ? treeItemLabel.highlights.map(([start, end]) => ({ start, end })) : void 0; const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark; const iconUrl = icon ? URI.revive(icon) : null; - const title = node.tooltip ? node.tooltip : resource ? void 0 : label; + const title = node.tooltip ? node.tooltip : resource ? void 0 : treeItemLabel ? treeItemLabel.label : void 0; // reset templateData.resourceLabel.clear(); diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 54e9daa9c9cca675dd28f9d24425672644ef088d..fcd3e24c904f2980546206aa31a17a88ba40a7c8 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -138,7 +138,7 @@ function openWorkbench(configuration: IWindowConfiguration): Promise { }, mainServices, mainProcessClient, configuration); // Gracefully Shutdown Storage - shell.onShutdown(event => { + shell.onWillShutdown(event => { event.join(storageService.close()); }); diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 1f2ac72109af53e935a0f576fbfc63fd1a9c2c6f..78796b5c45f6c8680b543fdc8b775253d638a2a1 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -42,7 +42,7 @@ import { ExtensionService } from 'vs/workbench/services/extensions/electron-brow import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService'; -import { ILifecycleService, LifecyclePhase, ShutdownReason, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ISearchService, ISearchHistoryService } from 'vs/platform/search/common/search'; @@ -122,8 +122,8 @@ export interface ICoreServices { */ export class WorkbenchShell extends Disposable { - private readonly _onShutdown = this._register(new Emitter()); - get onShutdown(): Event { return this._onShutdown.event; } + private readonly _onWillShutdown = this._register(new Emitter()); + get onWillShutdown(): Event { return this._onWillShutdown.event; } private storageService: DelegatingStorageService; private environmentService: IEnvironmentService; @@ -428,11 +428,8 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IDialogService, instantiationService.createInstance(DialogService)); const lifecycleService = instantiationService.createInstance(LifecycleService); - this._register(lifecycleService.onShutdown(event => { - this._onShutdown.fire(event); - - this.dispose(event.reason); - })); + this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event))); + this._register(lifecycleService.onShutdown(() => this.dispose())); serviceCollection.set(ILifecycleService, lifecycleService); this.lifecycleService = lifecycleService; @@ -585,12 +582,12 @@ export class WorkbenchShell extends Disposable { this.workbench.layout(); } - dispose(reason = ShutdownReason.QUIT): void { + dispose(): void { super.dispose(); // Dispose Workbench if (this.workbench) { - this.workbench.dispose(reason); + this.workbench.dispose(); } this.mainProcessClient.dispose(); diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 75a43f0999dd8e52de186485117d67c1e1db9ff4..0aaba8ded7b3ee63450c2c588be516cd5fa8e0f7 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -477,7 +477,7 @@ export class Workbench extends Disposable implements IPartService { private registerListeners(): void { // Lifecycle - this._register(this.lifecycleService.onWillShutdown(e => this.saveState(e.reason))); + this._register(this.lifecycleService.onBeforeShutdown(e => this.saveState(e.reason))); // Listen to visible editor changes this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange())); @@ -1142,7 +1142,7 @@ export class Workbench extends Disposable implements IPartService { } } - dispose(reason = ShutdownReason.QUIT): void { + dispose(): void { super.dispose(); this.workbenchShutdown = true; diff --git a/src/vs/workbench/node/proxyResolver.ts b/src/vs/workbench/node/proxyResolver.ts index 26191794d829287f861617e3ab4a9da35a2cbaea..a1c2630b675ace394b372ee5b0a422dde14f08a9 100644 --- a/src/vs/workbench/node/proxyResolver.ts +++ b/src/vs/workbench/node/proxyResolver.ts @@ -49,12 +49,9 @@ function createProxyAgent( let cacheRolls = 0; let oldCache = new Map(); let cache = new Map(); - function getCacheKey(url: string) { + function getCacheKey(url: nodeurl.UrlWithStringQuery) { // Expecting proxies to usually be the same per scheme://host:port. Assuming that for performance. - const parsed = nodeurl.parse(url); // Coming from Node's URL, sticking with that. - delete parsed.pathname; - delete parsed.search; - return nodeurl.format(parsed); + return nodeurl.format({ ...url, ...{ pathname: undefined, search: undefined, hash: undefined } }); } function getCachedProxy(key: string) { let proxy = cache.get(key); @@ -85,6 +82,7 @@ function createProxyAgent( let cacheCount = 0; let envCount = 0; let settingsCount = 0; + let localhostCount = 0; function logEvent() { timeout = undefined; /* __GDPR__ @@ -96,11 +94,12 @@ function createProxyAgent( "cacheSize": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cacheRolls": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "envCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "settingsCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + "settingsCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "localhostCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ - mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount }); - count = duration = errorCount = cacheCount = envCount = settingsCount = 0; + mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount }); + count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = 0; } function resolveProxy(url: string, callback: (proxy?: string) => void) { @@ -108,6 +107,16 @@ function createProxyAgent( timeout = setTimeout(logEvent, 10 * 60 * 1000); } + const parsedUrl = nodeurl.parse(url); // Coming from Node's URL, sticking with that. + + const hostname = parsedUrl.hostname; + if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '::ffff:127.0.0.1') { + localhostCount++; + callback('DIRECT'); + extHostLogService.trace('ProxyResolver#resolveProxy localhost', url, 'DIRECT'); + return; + } + if (settingsProxy) { settingsCount++; callback(settingsProxy); @@ -122,7 +131,7 @@ function createProxyAgent( return; } - const key = getCacheKey(url); + const key = getCacheKey(parsedUrl); const proxy = getCachedProxy(key); if (proxy) { cacheCount++; diff --git a/src/vs/workbench/parts/tasks/electron-browser/runAutomaticTasks.ts b/src/vs/workbench/parts/tasks/electron-browser/runAutomaticTasks.ts index d639ebc506a1ee71c3f4aa8b959b57b8302aec91..c47b6506a2cefe3119594129fe82b96ffa293a78 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/runAutomaticTasks.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/runAutomaticTasks.ts @@ -17,11 +17,13 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut taskService.getWorkspaceTasks().then(workspaceTaskResult => { if (workspaceTaskResult) { workspaceTaskResult.forEach(resultElement => { - resultElement.set.tasks.forEach(task => { - if (task.runOptions.runOn === RunOnOptions.folderOpen) { - taskService.run(task); - } - }); + if (resultElement.set) { + resultElement.set.tasks.forEach(task => { + if (task.runOptions.runOn === RunOnOptions.folderOpen) { + taskService.run(task); + } + }); + } if (resultElement.configurations) { forEach(resultElement.configurations.byIdentifier, (configedTask) => { if (configedTask.value.runOptions.runOn === RunOnOptions.folderOpen) { diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index e2b77e20e401f894eb03735faf0b486e5b3ca9b8..3a7a8d9b9f0a57ae20defbd93a64f7287e587c0f 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -529,7 +529,7 @@ class TaskService extends Disposable implements ITaskService { this.updateWorkspaceTasks(); })); this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService); - this._register(lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown()))); + this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown()))); this._register(storageService.onWillSaveState(() => this.saveState())); this._onDidStateChange = this._register(new Emitter()); this.registerCommands(); diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 88d47af4759e6b57d39863edb6e8b2959a47e3e6..729c7d30b56e3d26bf6755cbaa039f6e9552f39d 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -63,7 +63,7 @@ export abstract class TerminalService implements ITerminalService { this._activeTabIndex = 0; this._isShuttingDown = false; this._findState = new FindReplaceState(); - lifecycleService.onWillShutdown(event => event.veto(this._onWillShutdown())); + lifecycleService.onBeforeShutdown(event => event.veto(this._onBeforeShutdown())); lifecycleService.onShutdown(() => this._onShutdown()); this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); @@ -92,7 +92,7 @@ export abstract class TerminalService implements ITerminalService { public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; - private _onWillShutdown(): boolean | PromiseLike { + private _onBeforeShutdown(): boolean | PromiseLike { if (this.terminalInstances.length === 0) { // No terminal instances, don't veto return false; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 28a41e9029c05d6ae2c16eabe14a5dba67e914e6..1b955e1bd16b813457d3a533b8178a89f584d17c 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -124,7 +124,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._toDispose = []; this._toDispose.push(this._onCrashed); - this._toDispose.push(this._lifecycleService.onWillShutdown((e) => this._onWillShutdown(e))); + this._toDispose.push(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate())); this._toDispose.push(this._broadcastService.onBroadcast(b => this._onBroadcast(b))); @@ -563,7 +563,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } }); - event.veto(timeout(100 /* wait a bit for IPC to get delivered */).then(() => false)); + event.join(timeout(100 /* wait a bit for IPC to get delivered */)); } } } diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 766d4e482b38b66dcbc675a275bdccd0a5e6764d..e34dac4fc568ef774a81fc8fa962eefc5e061171 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -106,7 +106,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private registerListeners(): void { // Lifecycle - this.lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown(event.reason))); + this.lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown(event.reason))); this.lifecycleService.onShutdown(this.dispose, this); // Files configuration changes diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index fdf826a714cd19a483db8cdd2183b0382549c514..f50f71ca6a464ab69637a27d9ad25bd4ab334930 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -7,7 +7,7 @@ import * as sinon from 'sinon'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ILifecycleService, WillShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, BeforeShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestWindowsService, TestContextService, TestFileService } from 'vs/workbench/test/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -37,7 +37,7 @@ class ServiceAccessor { } } -class ShutdownEventImpl implements WillShutdownEvent { +class BeforeShutdownEventImpl implements BeforeShutdownEvent { public value: boolean | TPromise; public reason = ShutdownReason.CLOSE; @@ -69,7 +69,7 @@ suite('Files - TextFileService', () => { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); (accessor.textFileService.models).add(model.getResource(), model); - const event = new ShutdownEventImpl(); + const event = new BeforeShutdownEventImpl(); accessor.lifecycleService.fireWillShutdown(event); const veto = event.value; @@ -94,7 +94,7 @@ suite('Files - TextFileService', () => { assert.equal(service.getDirty().length, 1); - const event = new ShutdownEventImpl(); + const event = new BeforeShutdownEventImpl(); accessor.lifecycleService.fireWillShutdown(event); assert.ok(event.value); @@ -114,7 +114,7 @@ suite('Files - TextFileService', () => { assert.equal(service.getDirty().length, 1); - const event = new ShutdownEventImpl(); + const event = new BeforeShutdownEventImpl(); accessor.lifecycleService.fireWillShutdown(event); const veto = event.value; @@ -145,7 +145,7 @@ suite('Files - TextFileService', () => { assert.equal(service.getDirty().length, 1); - const event = new ShutdownEventImpl(); + const event = new BeforeShutdownEventImpl(); accessor.lifecycleService.fireWillShutdown(event); return (>event.value).then(veto => { @@ -450,7 +450,7 @@ suite('Files - TextFileService', () => { assert.equal(service.getDirty().length, 1); - const event = new ShutdownEventImpl(); + const event = new BeforeShutdownEventImpl(); event.reason = shutdownReason; accessor.lifecycleService.fireWillShutdown(event); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 4adc3d609c89d32b7b74d181cb6a482482a94d69..9fd2c82eb1375e23f12b662d718b5dccbe863953 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -24,7 +24,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, Workspace } from 'vs/platform/workspace/common/workspace'; -import { ILifecycleService, WillShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IResolveFileResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent, ICreateFileOptions, ITextSnapshot, IResourceEncodings } from 'vs/platform/files/common/files'; @@ -1144,29 +1144,34 @@ export class TestLifecycleService implements ILifecycleService { public phase: LifecyclePhase; public startupKind: StartupKind; + private _onBeforeShutdown = new Emitter(); private _onWillShutdown = new Emitter(); - private _onShutdown = new Emitter(); + private _onShutdown = new Emitter(); when(): TPromise { return TPromise.as(void 0); } public fireShutdown(reason = ShutdownReason.QUIT): void { - this._onShutdown.fire({ + this._onWillShutdown.fire({ join: () => { }, reason }); } - public fireWillShutdown(event: WillShutdownEvent): void { - this._onWillShutdown.fire(event); + public fireWillShutdown(event: BeforeShutdownEvent): void { + this._onBeforeShutdown.fire(event); + } + + public get onBeforeShutdown(): Event { + return this._onBeforeShutdown.event; } public get onWillShutdown(): Event { return this._onWillShutdown.event; } - public get onShutdown(): Event { + public get onShutdown(): Event { return this._onShutdown.event; } } diff --git a/yarn.lock b/yarn.lock index b6319a3816c37ee1dbb1827a4d43df64638c5867..fe2e5130e14f7a56cce6f46340bda85191c9bc7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9405,10 +9405,10 @@ vscode-proxy-agent@0.1.1: https-proxy-agent "2.2.1" socks-proxy-agent "4.0.1" -vscode-ripgrep@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.4.tgz#b3cfbe08ed13f6cf6b134147ea4d982970ab4f70" - integrity sha512-TysaK20aCSfsFIQGd0DfMshjkHof0fG6zx7DoO0tdWNAZgsvoqLtOWdqHcocICRZ3RSpdiMiEJRaMK+iOzx16w== +vscode-ripgrep@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.5.tgz#2093c8f36d52bd2dab9eb45b003dd02533c5499c" + integrity sha512-n5XBm9od5hahpljw9T8wbkuMnAY7LlAG1OyEEtcCZEX9aCHFuBKSP0IcvciGRTbtWRovNuT83A2iRjt6PL3bLg== vscode-sqlite3@4.0.5: version "4.0.5"