keyboardLayoutPicker.ts 8.1 KB
Newer Older
P
Peng Lyu 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
7
import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar';
P
Peng Lyu 已提交
8
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
9
import { IKeymapService, areKeyboardLayoutsEqual, parseKeyboardLayoutDescription, getKeyboardLayoutId, IKeyboardLayoutInfo } from 'vs/workbench/services/keybinding/common/keymapInfo';
10
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
P
Peng Lyu 已提交
11 12 13 14 15
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { KEYBOARD_LAYOUT_OPEN_PICKER } from 'vs/workbench/contrib/preferences/common/preferences';
import { Action } from 'vs/base/common/actions';
B
Benjamin Pasero 已提交
16
import { isMacintosh, isWindows } from 'vs/base/common/platform';
P
Peng Lyu 已提交
17 18 19
import { QuickPickInput, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
P
Peng Lyu 已提交
20 21 22 23
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { VSBuffer } from 'vs/base/common/buffer';
24
import { IEditorPane } from 'vs/workbench/common/editor';
P
Peng Lyu 已提交
25 26 27 28 29 30 31 32 33 34 35

export class KeyboardLayoutPickerContribution extends Disposable implements IWorkbenchContribution {
	private readonly pickerElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());

	constructor(
		@IKeymapService private readonly keymapService: IKeymapService,
		@IStatusbarService private readonly statusbarService: IStatusbarService,
	) {
		super();

		let layout = this.keymapService.getCurrentKeyboardLayout();
36
		if (layout) {
37
			let layoutInfo = parseKeyboardLayoutDescription(layout);
I
isidor 已提交
38 39
			const text = nls.localize('keyboardLayout', "Layout: {0}", layoutInfo.label);

40 41
			this.pickerElement.value = this.statusbarService.addEntry(
				{
I
isidor 已提交
42 43
					text,
					ariaLabel: text,
44 45
					command: KEYBOARD_LAYOUT_OPEN_PICKER
				},
P
Peng Lyu 已提交
46
				'status.workbench.keyboardLayout',
B
Benjamin Pasero 已提交
47
				nls.localize('status.workbench.keyboardLayout', "Keyboard Layout"),
48 49 50
				StatusbarAlignment.RIGHT
			);
		}
P
Peng Lyu 已提交
51 52

		this._register(keymapService.onDidChangeKeyboardMapper(() => {
53
			let layout = this.keymapService.getCurrentKeyboardLayout();
54
			let layoutInfo = parseKeyboardLayoutDescription(layout);
P
Peng Lyu 已提交
55

56
			if (this.pickerElement.value) {
I
isidor 已提交
57
				const text = nls.localize('keyboardLayout', "Layout: {0}", layoutInfo.label);
P
Peng Lyu 已提交
58
				this.pickerElement.value.update({
I
isidor 已提交
59 60
					text,
					ariaLabel: text,
P
Peng Lyu 已提交
61
					command: KEYBOARD_LAYOUT_OPEN_PICKER
P
Peng Lyu 已提交
62
				});
63
			} else {
I
isidor 已提交
64
				const text = nls.localize('keyboardLayout', "Layout: {0}", layoutInfo.label);
65 66
				this.pickerElement.value = this.statusbarService.addEntry(
					{
I
isidor 已提交
67 68
						text,
						ariaLabel: text,
69 70
						command: KEYBOARD_LAYOUT_OPEN_PICKER
					},
71
					'status.workbench.keyboardLayout',
B
Benjamin Pasero 已提交
72
					nls.localize('status.workbench.keyboardLayout', "Keyboard Layout"),
73 74
					StatusbarAlignment.RIGHT
				);
P
Peng Lyu 已提交
75 76 77 78 79 80 81 82
			}
		}));
	}
}

const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(KeyboardLayoutPickerContribution, LifecyclePhase.Starting);

83 84 85
interface LayoutQuickPickItem extends IQuickPickItem {
	layout: IKeyboardLayoutInfo;
}
P
Peng Lyu 已提交
86 87 88

export class KeyboardLayoutPickerAction extends Action {
	static readonly ID = KEYBOARD_LAYOUT_OPEN_PICKER;
B
Benjamin Pasero 已提交
89
	static readonly LABEL = nls.localize('keyboard.chooseLayout', "Change Keyboard Layout");
P
Peng Lyu 已提交
90

P
Peng Lyu 已提交
91 92
	private static DEFAULT_CONTENT: string = [
		`// ${nls.localize('displayLanguage', 'Defines the keyboard layout used in VS Code in the browser environment.')}`,
P
Peng Lyu 已提交
93
		`// ${nls.localize('doc', 'Open VS Code and run "Developer: Inspect Key Mappings (JSON)" from Command Palette.')}`,
P
Peng Lyu 已提交
94 95 96 97 98
		``,
		`// Once you have the keyboard layout info, please paste it below.`,
		'\n'
	].join('\n');

P
Peng Lyu 已提交
99 100 101
	constructor(
		actionId: string,
		actionLabel: string,
P
Peng Lyu 已提交
102
		@IFileService private readonly fileService: IFileService,
P
Peng Lyu 已提交
103 104
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@IKeymapService private readonly keymapService: IKeymapService,
P
Peng Lyu 已提交
105 106 107
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IEnvironmentService private readonly environmentService: IEnvironmentService,
		@IEditorService private readonly editorService: IEditorService
P
Peng Lyu 已提交
108
	) {
B
Benjamin Pasero 已提交
109
		super(actionId, actionLabel, undefined, true);
P
Peng Lyu 已提交
110 111 112 113
	}

	async run(): Promise<void> {
		let layouts = this.keymapService.getAllKeyboardLayouts();
P
Peng Lyu 已提交
114 115 116
		let currentLayout = this.keymapService.getCurrentKeyboardLayout();
		let layoutConfig = this.configurationService.getValue('keyboard.layout');
		let isAutoDetect = layoutConfig === 'autodetect';
P
Peng Lyu 已提交
117 118

		const picks: QuickPickInput[] = layouts.map(layout => {
P
Peng Lyu 已提交
119
			const picked = !isAutoDetect && areKeyboardLayoutsEqual(currentLayout, layout);
120
			const layoutInfo = parseKeyboardLayoutDescription(layout);
P
Peng Lyu 已提交
121
			return {
122
				layout: layout,
P
Peng Lyu 已提交
123
				label: [layoutInfo.label, (layout && layout.isUserKeyboardLayout) ? '(User configured layout)' : ''].join(' '),
P
Peng Lyu 已提交
124
				id: (<any>layout).text || (<any>layout).lang || (<any>layout).layout,
P
Peng Lyu 已提交
125
				description: layoutInfo.description + (picked ? ' (Current layout)' : ''),
P
Peng Lyu 已提交
126
				picked: !isAutoDetect && areKeyboardLayoutsEqual(currentLayout, layout)
P
Peng Lyu 已提交
127
			};
P
Peng Lyu 已提交
128 129
		}).sort((a: IQuickPickItem, b: IQuickPickItem) => {
			return a.label < b.label ? -1 : (a.label > b.label ? 1 : 0);
P
Peng Lyu 已提交
130 131 132 133 134 135 136
		});

		if (picks.length > 0) {
			const platform = isMacintosh ? 'Mac' : isWindows ? 'Win' : 'Linux';
			picks.unshift({ type: 'separator', label: nls.localize('layoutPicks', "Keyboard Layouts ({0})", platform) });
		}

P
Peng Lyu 已提交
137
		let configureKeyboardLayout: IQuickPickItem = { label: nls.localize('configureKeyboardLayout', "Configure Keyboard Layout") };
P
Peng Lyu 已提交
138 139 140 141 142

		picks.unshift(configureKeyboardLayout);

		// Offer to "Auto Detect"
		const autoDetectMode: IQuickPickItem = {
P
Peng Lyu 已提交
143
			label: nls.localize('autoDetect', "Auto Detect"),
144
			description: isAutoDetect ? `Current: ${parseKeyboardLayoutDescription(currentLayout).label}` : undefined,
P
Peng Lyu 已提交
145
			picked: isAutoDetect ? true : undefined
P
Peng Lyu 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
		};

		picks.unshift(autoDetectMode);

		const pick = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickKeyboardLayout', "Select Keyboard Layout"), matchOnDescription: true });
		if (!pick) {
			return;
		}

		if (pick === autoDetectMode) {
			// set keymap service to auto mode
			this.configurationService.updateValue('keyboard.layout', 'autodetect');
			return;
		}

		if (pick === configureKeyboardLayout) {
P
Peng Lyu 已提交
162 163 164 165
			const file = this.environmentService.keyboardLayoutResource;

			await this.fileService.resolve(file).then(undefined, (error) => {
				return this.fileService.createFile(file, VSBuffer.fromString(KeyboardLayoutPickerAction.DEFAULT_CONTENT));
166
			}).then((stat): Promise<IEditorPane | undefined> | undefined => {
P
Peng Lyu 已提交
167
				if (!stat) {
168
					return undefined;
P
Peng Lyu 已提交
169 170 171 172 173 174 175 176 177 178
				}
				return this.editorService.openEditor({
					resource: stat.resource,
					mode: 'jsonc'
				});
			}, (error) => {
				throw new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", file.toString(), error));
			});

			return Promise.resolve();
P
Peng Lyu 已提交
179 180
		}

181
		this.configurationService.updateValue('keyboard.layout', getKeyboardLayoutId((<LayoutQuickPickItem>pick).layout));
P
Peng Lyu 已提交
182 183 184 185
	}
}

const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
186
registry.registerWorkbenchAction(SyncActionDescriptor.from(KeyboardLayoutPickerAction, {}), 'Preferences: Change Keyboard Layout', nls.localize('preferences', "Preferences"));