Skip to content

Commit fb3c370

Browse files
authored
feat: support keybinding via extensions (#198)
* feat: develop keybinding services & controller * feat: init actions via extension rather than MoleculeProvider * feat: support keybinding in the menus of activitybar * feat: support keybinding in the menus of menuBar * fix: replace context menu in activity bar * feat: improve keybinding icon in windows * refactor: improve the usage of keybinding * fix: extensionService no longer export
1 parent a8ba3e3 commit fb3c370

20 files changed

+282
-60
lines changed

src/components/menu/style.scss

+8
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@
8585
}
8686
}
8787

88+
&__keybinding {
89+
display: inline-block;
90+
flex: 2 1 auto;
91+
line-height: 1;
92+
padding: 0 2em;
93+
text-align: right;
94+
}
95+
8896
&__separator {
8997
height: 1px;
9098
width: 100%;

src/controller/activityBar.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ import {
99
CONTEXT_MENU_EXPLORER,
1010
CONTEXT_MENU_SEARCH,
1111
CONTEXT_MENU_HIDE,
12-
CONTEXT_MENU_COLOR_THEME,
13-
CONTEXT_MENU_COMMAND_PALETTE,
14-
CONTEXT_MENU_SETTINGS,
1512
IActivityBarItem,
1613
} from 'mo/model';
1714
import { SelectColorThemeAction } from 'mo/monaco/selectColorThemeAction';
@@ -24,6 +21,12 @@ import {
2421
} from 'mo/services';
2522
import { CommandQuickAccessViewAction } from 'mo/monaco/quickAccessViewAction';
2623
import { IMonacoService, MonacoService } from 'mo/monaco/monacoService';
24+
import {
25+
ACTION_QUICK_COMMAND,
26+
ACTION_QUICK_ACCESS_SETTINGS,
27+
ACTION_SELECT_THEME,
28+
} from 'mo/model/keybinding';
29+
2730
export interface IActivityBarController {
2831
/**
2932
* Called when activity bar item is clicked
@@ -107,15 +110,15 @@ export class ActivityBarController
107110
break;
108111
}
109112
// manage button contextMenu
110-
case CONTEXT_MENU_COMMAND_PALETTE: {
113+
case ACTION_QUICK_COMMAND: {
111114
this.gotoQuickCommand();
112115
break;
113116
}
114-
case CONTEXT_MENU_SETTINGS: {
117+
case ACTION_QUICK_ACCESS_SETTINGS: {
115118
this.settingsService.openSettingsInEditor();
116119
break;
117120
}
118-
case CONTEXT_MENU_COLOR_THEME: {
121+
case ACTION_SELECT_THEME: {
119122
this.onSelectColorTheme();
120123
break;
121124
}

src/extensions/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ExtendsActivityBar } from './activityBar';
77
import { ExtendsPanel } from './panel';
88
import { ExtendsExplorer } from './explorer';
99
import { ExtendsEditorTree } from './editorTree';
10+
import { ExtendsKeybinding } from './keybinding';
1011

1112
import { defaultColorThemeExtension } from './theme-defaults';
1213
import { monokaiColorThemeExtension } from './theme-monokai';
@@ -28,4 +29,5 @@ export const defaultExtensions = [
2829
monokaiColorThemeExtension,
2930
paleNightColorThemeExtension,
3031
ExtendsFolderTree,
32+
ExtendsKeybinding,
3133
];

src/extensions/keybinding/index.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { IExtensionService } from 'mo/services';
2+
import { IExtension } from 'mo/model';
3+
import { SelectLocaleAction } from 'mo/i18n/selectLocaleAction';
4+
import { QuickAccessSettings } from 'mo/monaco/quickAccessSettingsAction';
5+
import { CommandQuickAccessViewAction } from 'mo/monaco/quickAccessViewAction';
6+
import { SelectColorThemeAction } from 'mo/monaco/selectColorThemeAction';
7+
8+
export const ExtendsKeybinding: IExtension = {
9+
activate(extensionCtx: IExtensionService) {
10+
extensionCtx.registerAction(CommandQuickAccessViewAction);
11+
extensionCtx.registerAction(SelectColorThemeAction);
12+
extensionCtx.registerAction(QuickAccessSettings);
13+
extensionCtx.registerAction(SelectLocaleAction);
14+
},
15+
};

src/i18n/selectLocaleAction.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import { Action2 } from 'mo/monaco/common';
99
import { localize } from './localize';
1010
import { ILocaleService, LocaleService } from './localeService';
1111
import { ILocale } from './localization';
12+
import { KeyCode, KeyMod } from 'mo/monaco';
13+
import { ACTION_SELECT_LOCALE } from 'mo/model/keybinding';
1214

1315
export class SelectLocaleAction extends Action2 {
14-
static readonly ID = 'workbench.action.selectLocale';
16+
static readonly ID = ACTION_SELECT_LOCALE;
1517
static readonly LABEL = localize(
1618
'select.locale',
1719
'Select Display Language'
@@ -29,6 +31,10 @@ export class SelectLocaleAction extends Action2 {
2931
alias: 'Select Display Language',
3032
precondition: undefined,
3133
f1: true,
34+
keybinding: {
35+
when: undefined,
36+
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L,
37+
},
3238
});
3339
}
3440

src/model/keybinding.ts

+69-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,69 @@
1-
export interface IKeybinding {}
1+
import { KeyCode } from 'mo/monaco';
2+
3+
export const KeyCodeString: Partial<{ [key in KeyCode]: string }> = {
4+
[KeyCode.Unknown]: '',
5+
[KeyCode.Backspace]: '⌫',
6+
[KeyCode.Tab]: '⇥',
7+
[KeyCode.Enter]: '↩︎',
8+
[KeyCode.KEY_0]: '0',
9+
[KeyCode.KEY_1]: '1',
10+
[KeyCode.KEY_2]: '2',
11+
[KeyCode.KEY_3]: '3',
12+
[KeyCode.KEY_4]: '4',
13+
[KeyCode.KEY_5]: '5',
14+
[KeyCode.KEY_6]: '6',
15+
[KeyCode.KEY_7]: '7',
16+
[KeyCode.KEY_8]: '8',
17+
[KeyCode.KEY_9]: '9',
18+
[KeyCode.KEY_A]: 'A',
19+
[KeyCode.KEY_B]: 'B',
20+
[KeyCode.KEY_C]: 'C',
21+
[KeyCode.KEY_D]: 'D',
22+
[KeyCode.KEY_E]: 'E',
23+
[KeyCode.KEY_F]: 'F',
24+
[KeyCode.KEY_G]: 'G',
25+
[KeyCode.KEY_H]: 'H',
26+
[KeyCode.KEY_I]: 'I',
27+
[KeyCode.KEY_J]: 'J',
28+
[KeyCode.KEY_K]: 'K',
29+
[KeyCode.KEY_L]: 'L',
30+
[KeyCode.KEY_M]: 'M',
31+
[KeyCode.KEY_N]: 'N',
32+
[KeyCode.KEY_O]: 'O',
33+
[KeyCode.KEY_P]: 'P',
34+
[KeyCode.KEY_Q]: 'Q',
35+
[KeyCode.KEY_R]: 'R',
36+
[KeyCode.KEY_S]: 'S',
37+
[KeyCode.KEY_T]: 'T',
38+
[KeyCode.KEY_U]: 'U',
39+
[KeyCode.KEY_V]: 'V',
40+
[KeyCode.KEY_W]: 'W',
41+
[KeyCode.KEY_X]: 'X',
42+
[KeyCode.KEY_Y]: 'Y',
43+
[KeyCode.KEY_Z]: 'Z',
44+
[KeyCode.US_SEMICOLON]: ';',
45+
[KeyCode.US_EQUAL]: '+',
46+
[KeyCode.US_COMMA]: ',',
47+
[KeyCode.US_MINUS]: '-',
48+
[KeyCode.US_DOT]: '.',
49+
[KeyCode.US_SLASH]: '/',
50+
[KeyCode.US_BACKTICK]: '~',
51+
[KeyCode.US_OPEN_SQUARE_BRACKET]: '[',
52+
[KeyCode.US_BACKSLASH]: '\\',
53+
[KeyCode.US_CLOSE_SQUARE_BRACKET]: ']',
54+
[KeyCode.US_QUOTE]: '"',
55+
};
56+
57+
export interface ISimpleKeybinding {
58+
ctrlKey: boolean;
59+
shiftKey: boolean;
60+
altKey: boolean;
61+
metaKey: boolean;
62+
keyCode: KeyCode;
63+
}
64+
65+
export const ACTION_QUICK_ACCESS_SETTINGS =
66+
'workbench.action.quickAccessSettings';
67+
export const ACTION_QUICK_COMMAND = 'workbench.action.quickCommand';
68+
export const ACTION_SELECT_THEME = 'workbench.action.selectTheme';
69+
export const ACTION_SELECT_LOCALE = 'workbench.action.selectLocale';

src/model/workbench/activityBar.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import * as React from 'react';
22
import { IMenuItemProps } from 'mo/components/menu';
33
import { localize } from 'mo/i18n/localize';
4+
import {
5+
ACTION_QUICK_ACCESS_SETTINGS,
6+
ACTION_QUICK_COMMAND,
7+
ACTION_SELECT_THEME,
8+
} from '../keybinding';
49

510
/**
611
* The activity bar event definition
@@ -38,10 +43,6 @@ export interface IActivityBar {
3843
export const ACTIVITY_BAR_GLOBAL_SETTINGS = 'global.menu.settings';
3944
export const ACTIVITY_BAR_GLOBAL_ACCOUNT = 'global.menu.account';
4045

41-
export const CONTEXT_MENU_COMMAND_PALETTE = 'menu.commandPalette';
42-
export const CONTEXT_MENU_SETTINGS = 'menu.settings';
43-
export const CONTEXT_MENU_COLOR_THEME = 'menu.colorTheme';
44-
4546
export const CONTEXT_MENU_MENU = 'menubar';
4647
export const CONTEXT_MENU_EXPLORER = 'sidebar.explore.title';
4748
export const CONTEXT_MENU_SEARCH = 'sidebar.search.title';
@@ -62,15 +63,15 @@ export function builtInActivityBar(): IActivityBar {
6263
type: 'global',
6364
contextMenu: [
6465
{
65-
id: CONTEXT_MENU_COMMAND_PALETTE,
66+
id: ACTION_QUICK_COMMAND,
6667
name: localize('menu.commandPalette', 'Command Palette'),
6768
},
6869
{
69-
id: CONTEXT_MENU_SETTINGS,
70+
id: ACTION_QUICK_ACCESS_SETTINGS,
7071
name: localize('menu.settings', 'Settings'),
7172
},
7273
{
73-
id: CONTEXT_MENU_COLOR_THEME,
74+
id: ACTION_SELECT_THEME,
7475
name: localize('menu.colorTheme', 'Color Theme'),
7576
},
7677
],

src/molecule.api.ts

-8
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ import {
4242
NotificationService,
4343
IColorThemeService,
4444
ColorThemeService,
45-
IExtensionService,
46-
ExtensionService,
4745
ISettingsService,
4846
SettingsService,
4947
IProblemsService,
@@ -101,12 +99,6 @@ export const colorTheme = container.resolve<IColorThemeService>(
10199
ColorThemeService
102100
);
103101

104-
/**
105-
* Note: The extension service depends on other workbench services,
106-
* So it need initialized be last one.
107-
*/
108-
export const extension = container.resolve<IExtensionService>(ExtensionService);
109-
110102
/**
111103
* Settings service
112104
*/

src/monaco/common.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,15 @@ export enum KeybindingWeight {
2020
}
2121

2222
export abstract class Action2 {
23-
constructor(readonly desc: Readonly<any>) {}
23+
constructor(
24+
readonly desc: Readonly<{
25+
/**
26+
* Specify visible in quick access view
27+
*/
28+
f1: boolean;
29+
[key: string]: any;
30+
}>
31+
) {}
2432
abstract run(accessor: ServicesAccessor, ...args: any[]): any;
2533
}
2634

src/monaco/quickAccessSettingsAction.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import { ISettingsService, SettingsService } from 'mo/services';
66
import { ServicesAccessor } from 'monaco-editor/esm/vs/platform/instantiation/common/instantiation';
77
import { container } from 'tsyringe';
88
import { Action2, KeybindingWeight } from './common';
9+
import { ACTION_QUICK_ACCESS_SETTINGS } from 'mo/model/keybinding';
910

1011
export class QuickAccessSettings extends Action2 {
11-
static readonly ID = 'workbench.action.quickAccessSettings';
12+
static readonly ID = ACTION_QUICK_ACCESS_SETTINGS;
1213
static readonly LABEL = localize(
1314
'quickAccessSettings.label',
1415
'Open Settings (JSON)'

src/monaco/quickAccessViewAction.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { EditorService, IEditorService } from 'mo/services';
2929
import { Action2, KeybindingWeight } from './common';
3030
import { MonacoService } from './monacoService';
3131
import { registerQuickAccessProvider } from './quickAccessProvider';
32+
import { ACTION_QUICK_COMMAND } from 'mo/model/keybinding';
3233

3334
export class CommandQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider {
3435
static PREFIX = '>';
@@ -166,7 +167,7 @@ registerQuickAccessProvider({
166167
});
167168

168169
export class CommandQuickAccessViewAction extends Action2 {
169-
static ID = 'workbench.action.quickCommand';
170+
static ID = ACTION_QUICK_COMMAND;
170171

171172
constructor() {
172173
super({

src/monaco/selectColorThemeAction.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import { ColorThemeService, IColorThemeService } from 'mo/services';
1111
import { ServicesAccessor } from 'monaco-editor/esm/vs/platform/instantiation/common/instantiation';
1212
import { container } from 'tsyringe';
1313
import { Action2, KeybindingWeight } from './common';
14+
import { ACTION_SELECT_THEME } from 'mo/model/keybinding';
1415

1516
export class SelectColorThemeAction extends Action2 {
16-
static readonly ID = 'workbench.action.selectTheme';
17+
static readonly ID = ACTION_SELECT_THEME;
1718
static readonly LABEL = localize('selectTheme.label', 'Color Theme');
1819
private readonly colorThemeService: IColorThemeService;
1920

src/provider/molecule.tsx

+1-17
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,9 @@ import {
1111
IExtensionService,
1212
} from 'mo/services/extensionService';
1313
import { IMonacoService, MonacoService } from 'mo/monaco/monacoService';
14-
import { CommandQuickAccessViewAction } from 'mo/monaco/quickAccessViewAction';
15-
import { registerAction2 } from 'mo/monaco/common';
16-
import { QuickAccessSettings } from 'mo/monaco/quickAccessSettingsAction';
17-
import { SelectColorThemeAction } from 'mo/monaco/selectColorThemeAction';
1814
import { ILocaleService, LocaleService } from 'mo/i18n/localeService';
1915
import { ILayoutService, LayoutService } from 'mo/services';
20-
import { SelectLocaleAction } from 'mo/i18n/selectLocaleAction';
16+
2117
export interface IMoleculeProps {
2218
extensions?: IExtension[];
2319
locales?: ILocale[];
@@ -64,18 +60,6 @@ export class MoleculeProvider extends React.Component<IMoleculeProps> {
6460
this.monacoService.initWorkspace(this.container!);
6561
this.extensionService.load(defaultExtensions);
6662
this.extensionService.load(extensions);
67-
68-
this.initWorkbenchActions();
69-
}
70-
71-
/**
72-
* TODO: move the register of actions to extensionService
73-
*/
74-
initWorkbenchActions() {
75-
registerAction2(CommandQuickAccessViewAction);
76-
registerAction2(SelectColorThemeAction);
77-
registerAction2(QuickAccessSettings);
78-
registerAction2(SelectLocaleAction);
7963
}
8064

8165
public render() {

src/services/extensionService.ts

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ColorThemeService,
99
IColorThemeService,
1010
} from './theme/colorThemeService';
11+
import { Action2, registerAction2 } from 'mo/monaco/common';
1112

1213
export interface IExtensionService {
1314
/**
@@ -22,6 +23,15 @@ export interface IExtensionService {
2223
load(extensions: IExtension[]);
2324
loadContributes(contributes: IContribute);
2425
unload(extension: IExtension);
26+
/**
27+
* Register action based in Action2,
28+
* @example
29+
* ```ts
30+
* const action = class Action extends Action2 {};
31+
* registerAction(action);
32+
* ```
33+
*/
34+
registerAction(actionClass: { new (): Action2 }): void;
2535
}
2636

2737
@singleton()
@@ -71,6 +81,10 @@ export class ExtensionService implements IExtensionService {
7181
});
7282
}
7383

84+
public registerAction(actionClass: { new (): Action2 }) {
85+
registerAction2(actionClass);
86+
}
87+
7488
unload(extension: IExtension) {
7589
console.log('unload extension:', extension.name);
7690
}

src/services/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './extensionService';
1+
export type { IExtensionService } from './extensionService';
22
export * from './theme/colorThemeService';
33
export * from './workbench';
44
export * from './settingsService';

0 commit comments

Comments
 (0)