terminalConfigHelper.ts 7.0 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6 7
import * as nls from 'vs/nls';
import * as platform from 'vs/base/common/platform';
8
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from "vs/editor/common/config/editorOptions";
9
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
10 11
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IChoiceService } from 'vs/platform/message/common/message';
12
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
13
import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/parts/terminal/common/terminal';
14
import { Severity } from 'vs/editor/common/standalone/standaloneBase';
15
import { TPromise } from 'vs/base/common/winjs.base';
16

17 18 19 20
interface IEditorConfiguration {
	editor: IEditorOptions;
}

21 22 23 24 25 26
interface IFullTerminalConfiguration {
	terminal: {
		integrated: ITerminalConfiguration;
	};
}

27 28
const DEFAULT_LINE_HEIGHT = 1.2;

29 30 31 32
/**
 * Encapsulates terminal configuration logic, the primary purpose of this file is so that platform
 * specific test cases can be written.
 */
33
export class TerminalConfigHelper implements ITerminalConfigHelper {
34
	public panelContainer: HTMLElement;
D
Daniel Imms 已提交
35

D
Daniel Imms 已提交
36
	private _charMeasureElement: HTMLElement;
37
	private _lastFontMeasurement: ITerminalFont;
38

39
	public constructor(
40 41 42 43 44
		private _platform: platform.Platform,
		@IConfigurationService private _configurationService: IConfigurationService,
		@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
		@IChoiceService private _choiceService: IChoiceService,
		@IStorageService private _storageService: IStorageService) {
45 46
	}

47 48 49
	public get config(): ITerminalConfiguration {
		return this._configurationService.getConfiguration<IFullTerminalConfiguration>().terminal.integrated;
	}
D
Daniel Imms 已提交
50

D
Daniel Imms 已提交
51
	private _measureFont(fontFamily: string, fontSize: number, lineHeight: number): ITerminalFont {
52
		// Create charMeasureElement if it hasn't been created or if it was orphaned by its parent
D
Daniel Imms 已提交
53
		if (!this._charMeasureElement || !this._charMeasureElement.parentElement) {
54 55
			this._charMeasureElement = document.createElement('div');
			this.panelContainer.appendChild(this._charMeasureElement);
56
		}
57
		const style = this._charMeasureElement.style;
58
		style.display = 'block';
59
		style.fontFamily = fontFamily;
60
		style.fontSize = fontSize + 'px';
61
		style.lineHeight = lineHeight.toString(10);
D
Daniel Imms 已提交
62
		this._charMeasureElement.innerText = 'X';
63
		const rect = this._charMeasureElement.getBoundingClientRect();
64
		style.display = 'none';
65 66 67 68 69 70 71

		// Bounding client rect was invalid, use last font measurement if available.
		if (this._lastFontMeasurement && !rect.width && !rect.height) {
			return this._lastFontMeasurement;
		}

		this._lastFontMeasurement = {
72
			fontFamily,
73
			fontSize: fontSize + 'px',
74
			lineHeight,
75 76
			charWidth: rect.width,
			charHeight: rect.height
77
		};
78
		return this._lastFontMeasurement;
79 80
	}

81
	/**
82
	 * Gets the font information based on the terminal.integrated.fontFamily
83
	 * terminal.integrated.fontSize, terminal.integrated.lineHeight configuration properties
84
	 */
85
	public getFont(): ITerminalFont {
86 87
		const config = this._configurationService.getConfiguration();
		const editorConfig = (<IEditorConfiguration>config).editor;
88
		const terminalConfig = this.config;
89

D
Daniel Imms 已提交
90
		const fontFamily = terminalConfig.fontFamily || editorConfig.fontFamily;
D
Daniel Imms 已提交
91
		let fontSize = this._toInteger(terminalConfig.fontSize, 0);
92
		if (fontSize <= 0) {
93
			fontSize = EDITOR_FONT_DEFAULTS.fontSize;
94
		}
95
		let lineHeight = terminalConfig.lineHeight <= 0 ? DEFAULT_LINE_HEIGHT : terminalConfig.lineHeight;
D
Daniel Imms 已提交
96 97 98
		if (!lineHeight) {
			lineHeight = DEFAULT_LINE_HEIGHT;
		}
99

100
		return this._measureFont(fontFamily, fontSize, lineHeight);
101 102
	}

103 104 105 106
	public setWorkspaceShellAllowed(isAllowed: boolean): void {
		this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE);
	}

107
	public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): void {
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
		// Check whether there is a workspace setting
		const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux';
		const shellConfigValue = this._workspaceConfigurationService.lookup<string>(`terminal.integrated.shell.${platformKey}`);
		const shellArgsConfigValue = this._workspaceConfigurationService.lookup<string[]>(`terminal.integrated.shellArgs.${platformKey}`);

		// Check if workspace setting exists and whether it's whitelisted
		let isWorkspaceShellAllowed = false;
		if (shellConfigValue.workspace !== undefined || shellArgsConfigValue.workspace !== undefined) {
			isWorkspaceShellAllowed = this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, undefined);
		}

		// Check if the value is neither blacklisted (false) or whitelisted (true) and ask for
		// permission
		if (isWorkspaceShellAllowed === undefined) {
			let shellString: string;
			if (shellConfigValue.workspace) {
				shellString = `"${shellConfigValue.workspace}"`;
B
Benjamin Pasero 已提交
125
			}
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
			let argsString: string;
			if (shellArgsConfigValue.workspace) {
				argsString = `[${shellArgsConfigValue.workspace.map(v => '"' + v + '"').join(', ')}]`;
			}
			// Should not be localized as it's json-like syntax referencing settings keys
			let changeString: string;
			if (shellConfigValue.workspace !== undefined) {
				if (shellArgsConfigValue.workspace !== undefined) {
					changeString = `shell: ${shellString}, shellArgs: ${argsString}`;
				} else {
					changeString = `shell: ${shellString}`;
				}
			} else { // if (shellArgsConfigValue.workspace !== undefined)
				changeString = `shellArgs: ${argsString}`;
			}
141 142
			const message = nls.localize('terminal.integrated.allowWorkspaceShell', "Do you allow {0} (defined as a workspace setting) to be launched in the terminal?", changeString);
			const options = [nls.localize('allow', "Allow"), nls.localize('disallow', "Disallow")];
143
			this._choiceService.choose(Severity.Info, message, options, 1).then(choice => {
D
Daniel Imms 已提交
144 145 146 147
				if (choice === 0) {
					this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, true, StorageScope.WORKSPACE);
				} else {
					this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, false, StorageScope.WORKSPACE);
148
				}
D
Daniel Imms 已提交
149
				return TPromise.as(null);
150
			});
151
		}
152 153 154

		shell.executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || shellConfigValue.default;
		shell.args = (isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default;
155
	}
156

D
Daniel Imms 已提交
157
	private _toInteger(source: any, minimum?: number): number {
158 159 160 161 162 163 164 165 166
		let r = parseInt(source, 10);
		if (isNaN(r)) {
			r = 0;
		}
		if (typeof minimum === 'number') {
			r = Math.max(minimum, r);
		}
		return r;
	}
167
}