relauncher.contribution.ts 8.2 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
import { IWindowsService, IWindowService, IWindowsConfiguration } from 'vs/platform/windows/common/windows';
12 13 14
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { localize } from 'vs/nls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
15
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
16
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
17
import { RunOnceScheduler } from 'vs/base/common/async';
B
Benjamin Pasero 已提交
18 19
import URI from 'vs/base/common/uri';
import { isEqual } from 'vs/base/common/resources';
20
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
21
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
22
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
23

24
interface IConfiguration extends IWindowsConfiguration {
25 26
	update: { channel: string; };
	telemetry: { enableCrashReporter: boolean };
27
	keyboard: { touchbar: { enabled: boolean } };
28
	workbench: { tree: { horizontalScrolling: boolean } };
29 30 31 32 33 34 35
}

export class SettingsChangeRelauncher implements IWorkbenchContribution {

	private toDispose: IDisposable[] = [];

	private titleBarStyle: 'native' | 'custom';
36
	private nativeTabs: boolean;
37
	private clickThroughInactive: boolean;
38 39
	private updateChannel: string;
	private enableCrashReporter: boolean;
40
	private touchbarEnabled: boolean;
41
	private treeHorizontalScrolling: boolean;
42
	private windowsSmoothScrollingWorkaround: boolean;
43

B
Benjamin Pasero 已提交
44
	private firstFolderResource: URI;
45
	private extensionHostRestarter: RunOnceScheduler;
46

47 48
	private onDidChangeWorkspaceFoldersUnbind: IDisposable;

49 50 51 52 53
	constructor(
		@IWindowsService private windowsService: IWindowsService,
		@IWindowService private windowService: IWindowService,
		@IConfigurationService private configurationService: IConfigurationService,
		@IEnvironmentService private envService: IEnvironmentService,
54
		@IDialogService private dialogService: IDialogService,
55 56
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IExtensionService private extensionService: IExtensionService
57
	) {
58
		const workspace = this.contextService.getWorkspace();
B
Benjamin Pasero 已提交
59
		this.firstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : void 0;
60
		this.extensionHostRestarter = new RunOnceScheduler(() => this.extensionService.restartExtensionHost(), 10);
61

62
		this.onConfigurationChange(configurationService.getValue<IConfiguration>(), false);
63
		this.handleWorkbenchState();
64 65 66 67 68

		this.registerListeners();
	}

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

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

76 77
		// macOS: Titlebar style
		if (isMacintosh && config.window && config.window.titleBarStyle !== this.titleBarStyle && (config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom')) {
78 79 80 81
			this.titleBarStyle = config.window.titleBarStyle;
			changed = true;
		}

82 83
		// macOS: Native tabs
		if (isMacintosh && config.window && typeof config.window.nativeTabs === 'boolean' && config.window.nativeTabs !== this.nativeTabs) {
84 85 86 87
			this.nativeTabs = config.window.nativeTabs;
			changed = true;
		}

88 89 90 91 92 93
		// macOS: Click through (accept first mouse)
		if (isMacintosh && config.window && typeof config.window.clickThroughInactive === 'boolean' && config.window.clickThroughInactive !== this.clickThroughInactive) {
			this.clickThroughInactive = config.window.clickThroughInactive;
			changed = true;
		}

94
		// Update channel
95
		if (config.update && typeof config.update.channel === 'string' && config.update.channel !== this.updateChannel) {
96 97 98 99 100
			this.updateChannel = config.update.channel;
			changed = true;
		}

		// Crash reporter
101
		if (config.telemetry && typeof config.telemetry.enableCrashReporter === 'boolean' && config.telemetry.enableCrashReporter !== this.enableCrashReporter) {
102 103 104 105
			this.enableCrashReporter = config.telemetry.enableCrashReporter;
			changed = true;
		}

106 107
		// macOS: Touchbar config
		if (isMacintosh && config.keyboard && config.keyboard.touchbar && typeof config.keyboard.touchbar.enabled === 'boolean' && config.keyboard.touchbar.enabled !== this.touchbarEnabled) {
108 109 110 111
			this.touchbarEnabled = config.keyboard.touchbar.enabled;
			changed = true;
		}

112 113 114 115 116 117
		// Tree horizontal scrolling support
		if (config.workbench && config.workbench.tree && typeof config.workbench.tree.horizontalScrolling === 'boolean' && config.workbench.tree.horizontalScrolling !== this.treeHorizontalScrolling) {
			this.treeHorizontalScrolling = config.workbench.tree.horizontalScrolling;
			changed = true;
		}

118
		// Windows: smooth scrolling workaround
119
		if (isWindows && config.window && typeof config.window.smoothScrollingWorkaround === 'boolean' && config.window.smoothScrollingWorkaround !== this.windowsSmoothScrollingWorkaround) {
120 121 122 123
			this.windowsSmoothScrollingWorkaround = config.window.smoothScrollingWorkaround;
			changed = true;
		}

124 125
		// Notify only when changed and we are the focused window (avoids notification spam across windows)
		if (notify && changed) {
126 127 128
			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),
129
				localize('restart', "&&Restart"),
130 131 132 133 134
				() => this.windowsService.relaunch(Object.create(null))
			);
		}
	}

135 136 137 138
	private handleWorkbenchState(): void {

		// React to folder changes when we are in workspace state
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
139 140 141

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

			// Install workspace folder listener
145
			if (!this.onDidChangeWorkspaceFoldersUnbind) {
146
				this.onDidChangeWorkspaceFoldersUnbind = this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders());
147 148 149 150 151 152 153 154 155
			}
		}

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

S
Sandeep Somavarapu 已提交
156
	private onDidChangeWorkspaceFolders(): void {
157 158
		const workspace = this.contextService.getWorkspace();

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

164
			this.extensionHostRestarter.schedule(); // buffer calls to extension host restart
165
		}
166
	}
167 168 169 170

	private doConfirm(message: string, detail: string, primaryButton: string, confirmed: () => void): void {
		this.windowService.isFocused().then(focused => {
			if (focused) {
171
				return this.dialogService.confirm({
172 173 174 175
					type: 'info',
					message,
					detail,
					primaryButton
176 177
				}).then(res => {
					if (res.confirmed) {
178 179
						confirmed();
					}
180 181
				});
			}
182 183

			return void 0;
184
		});
185 186 187 188 189 190 191
	}

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

192
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
193
workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher, LifecyclePhase.Running);