relauncher.contribution.ts 6.8 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
10
import { Registry } from 'vs/platform/registry/common/platform';
11 12
import { IMessageService } from 'vs/platform/message/common/message';
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
13
import { IWindowsService, IWindowService, IWindowsConfiguration } from 'vs/platform/windows/common/windows';
14 15 16
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { localize } from 'vs/nls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
17
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
B
Benjamin Pasero 已提交
18
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
19
import { RunOnceScheduler } from 'vs/base/common/async';
B
Benjamin Pasero 已提交
20 21 22
import URI from 'vs/base/common/uri';
import { isEqual } from 'vs/base/common/resources';
import { isLinux } from 'vs/base/common/platform';
23

24
interface IConfiguration extends IWindowsConfiguration {
25 26 27 28 29 30 31 32 33
	update: { channel: string; };
	telemetry: { enableCrashReporter: boolean };
}

export class SettingsChangeRelauncher implements IWorkbenchContribution {

	private toDispose: IDisposable[] = [];

	private titleBarStyle: 'native' | 'custom';
34
	private nativeTabs: boolean;
35 36
	private updateChannel: string;
	private enableCrashReporter: boolean;
37

B
Benjamin Pasero 已提交
38
	private firstFolderResource: URI;
39
	private extensionHostRestarter: RunOnceScheduler;
40

41 42
	private onDidChangeWorkspaceFoldersUnbind: IDisposable;

43 44 45 46 47 48
	constructor(
		@IWindowsService private windowsService: IWindowsService,
		@IWindowService private windowService: IWindowService,
		@IConfigurationService private configurationService: IConfigurationService,
		@IPreferencesService private preferencesService: IPreferencesService,
		@IEnvironmentService private envService: IEnvironmentService,
49
		@IMessageService private messageService: IMessageService,
50 51
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IExtensionService private extensionService: IExtensionService
52
	) {
53
		const workspace = this.contextService.getWorkspace();
B
Benjamin Pasero 已提交
54
		this.firstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : void 0;
55
		this.extensionHostRestarter = new RunOnceScheduler(() => this.extensionService.restartExtensionHost(), 10);
56

57
		this.onConfigurationChange(configurationService.getConfiguration<IConfiguration>(), false);
58
		this.handleWorkbenchState();
59 60 61 62 63

		this.registerListeners();
	}

	private registerListeners(): void {
64
		this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration<IConfiguration>(), true)));
65
		this.toDispose.push(this.contextService.onDidChangeWorkbenchState(() => setTimeout(() => this.handleWorkbenchState())));
66 67 68 69 70 71 72 73 74 75 76
	}

	private onConfigurationChange(config: IConfiguration, notify: boolean): void {
		let changed = false;

		// Titlebar style
		if (config.window && config.window.titleBarStyle !== this.titleBarStyle && (config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom')) {
			this.titleBarStyle = config.window.titleBarStyle;
			changed = true;
		}

77 78 79 80 81 82
		// Native tabs
		if (config.window && typeof config.window.nativeTabs === 'boolean' && config.window.nativeTabs !== this.nativeTabs) {
			this.nativeTabs = config.window.nativeTabs;
			changed = true;
		}

83
		// Update channel
84
		if (config.update && typeof config.update.channel === 'string' && config.update.channel !== this.updateChannel) {
85 86 87 88 89
			this.updateChannel = config.update.channel;
			changed = true;
		}

		// Crash reporter
90
		if (config.telemetry && typeof config.telemetry.enableCrashReporter === 'boolean' && config.telemetry.enableCrashReporter !== this.enableCrashReporter) {
91 92 93 94 95 96
			this.enableCrashReporter = config.telemetry.enableCrashReporter;
			changed = true;
		}

		// Notify only when changed and we are the focused window (avoids notification spam across windows)
		if (notify && changed) {
97 98 99
			this.doConfirm(
				localize('relaunchSettingMessage', "A setting has changed that requires a restart to take effect."),
				localize('relaunchSettingDetail', "Press the restart button to restart {0} and enable the setting.", this.envService.appNameLong),
100
				localize('restart', "&&Restart"),
101 102 103 104 105
				() => this.windowsService.relaunch(Object.create(null))
			);
		}
	}

106 107 108 109
	private handleWorkbenchState(): void {

		// React to folder changes when we are in workspace state
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
110 111 112

			// Update our known first folder path if we entered workspace
			const workspace = this.contextService.getWorkspace();
B
Benjamin Pasero 已提交
113
			this.firstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : void 0;
114 115

			// Install workspace folder listener
116
			if (!this.onDidChangeWorkspaceFoldersUnbind) {
117
				this.onDidChangeWorkspaceFoldersUnbind = this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders());
118 119 120 121 122 123 124 125 126
			}
		}

		// Ignore the workspace folder changes in EMPTY or FOLDER state
		else {
			this.onDidChangeWorkspaceFoldersUnbind = dispose(this.onDidChangeWorkspaceFoldersUnbind);
		}
	}

S
Sandeep Somavarapu 已提交
127
	private onDidChangeWorkspaceFolders(): void {
128 129
		const workspace = this.contextService.getWorkspace();

130
		// Restart extension host if first root folder changed (impact on deprecated workspace.rootPath API)
B
Benjamin Pasero 已提交
131 132 133
		const newFirstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : void 0;
		if (!isEqual(this.firstFolderResource, newFirstFolderResource, !isLinux)) {
			this.firstFolderResource = newFirstFolderResource;
134

135
			this.extensionHostRestarter.schedule(); // buffer calls to extension host restart
136
		}
137
	}
138 139 140 141

	private doConfirm(message: string, detail: string, primaryButton: string, confirmed: () => void): void {
		this.windowService.isFocused().then(focused => {
			if (focused) {
142
				const confirm = this.messageService.confirmSync({
143 144 145 146 147 148 149 150 151 152 153
					type: 'info',
					message,
					detail,
					primaryButton
				});

				if (confirm) {
					confirmed();
				}
			}
		});
154 155
	}

156
	public getId(): string {
157 158 159 160 161 162 163 164 165 166
		return 'workbench.relauncher';
	}

	public dispose(): void {
		this.toDispose = dispose(this.toDispose);
	}
}

const workbenchRegistry = <IWorkbenchContributionsRegistry>Registry.as(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher);