keyboardLayoutPicker.ts 8.4 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 { parseKeyboardLayoutDescription, areKeyboardLayoutsEqual, getKeyboardLayoutId, IKeyboardLayoutService, IKeyboardLayoutInfo } from 'vs/platform/keyboardLayout/common/keyboardLayout';
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

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

	constructor(
30
		@IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService,
P
Peng Lyu 已提交
31 32 33 34
		@IStatusbarService private readonly statusbarService: IStatusbarService,
	) {
		super();

35
		let layout = this.keyboardLayoutService.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 53
		this._register(keyboardLayoutService.onDidChangeKeyboardLayout(() => {
			let layout = this.keyboardLayoutService.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

R
rebornix 已提交
87 88 89 90 91 92
interface IUnknownLayout {
	text?: string;
	lang?: string;
	layout?: string;
}

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

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

P
Peng Lyu 已提交
105 106 107
	constructor(
		actionId: string,
		actionLabel: string,
P
Peng Lyu 已提交
108
		@IFileService private readonly fileService: IFileService,
P
Peng Lyu 已提交
109
		@IQuickInputService private readonly quickInputService: IQuickInputService,
110
		@IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService,
P
Peng Lyu 已提交
111 112 113
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IEnvironmentService private readonly environmentService: IEnvironmentService,
		@IEditorService private readonly editorService: IEditorService
P
Peng Lyu 已提交
114
	) {
B
Benjamin Pasero 已提交
115
		super(actionId, actionLabel, undefined, true);
P
Peng Lyu 已提交
116 117
	}

M
Matt Bierner 已提交
118
	override async run(): Promise<void> {
119 120
		let layouts = this.keyboardLayoutService.getAllKeyboardLayouts();
		let currentLayout = this.keyboardLayoutService.getCurrentKeyboardLayout();
P
Peng Lyu 已提交
121 122
		let layoutConfig = this.configurationService.getValue('keyboard.layout');
		let isAutoDetect = layoutConfig === 'autodetect';
P
Peng Lyu 已提交
123 124

		const picks: QuickPickInput[] = layouts.map(layout => {
P
Peng Lyu 已提交
125
			const picked = !isAutoDetect && areKeyboardLayoutsEqual(currentLayout, layout);
126
			const layoutInfo = parseKeyboardLayoutDescription(layout);
P
Peng Lyu 已提交
127
			return {
128
				layout: layout,
P
Peng Lyu 已提交
129
				label: [layoutInfo.label, (layout && layout.isUserKeyboardLayout) ? '(User configured layout)' : ''].join(' '),
R
rebornix 已提交
130
				id: (layout as IUnknownLayout).text || (layout as IUnknownLayout).lang || (layout as IUnknownLayout).layout,
P
Peng Lyu 已提交
131
				description: layoutInfo.description + (picked ? ' (Current layout)' : ''),
P
Peng Lyu 已提交
132
				picked: !isAutoDetect && areKeyboardLayoutsEqual(currentLayout, layout)
P
Peng Lyu 已提交
133
			};
P
Peng Lyu 已提交
134 135
		}).sort((a: IQuickPickItem, b: IQuickPickItem) => {
			return a.label < b.label ? -1 : (a.label > b.label ? 1 : 0);
P
Peng Lyu 已提交
136 137 138 139 140 141 142
		});

		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 已提交
143
		let configureKeyboardLayout: IQuickPickItem = { label: nls.localize('configureKeyboardLayout', "Configure Keyboard Layout") };
P
Peng Lyu 已提交
144 145 146 147 148

		picks.unshift(configureKeyboardLayout);

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

		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 已提交
168 169 170 171
			const file = this.environmentService.keyboardLayoutResource;

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

			return Promise.resolve();
P
Peng Lyu 已提交
186 187
		}

188
		this.configurationService.updateValue('keyboard.layout', getKeyboardLayoutId((<LayoutQuickPickItem>pick).layout));
P
Peng Lyu 已提交
189 190 191 192
	}
}

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