diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts index d6e9571f7..d4552bb83 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -338,6 +338,10 @@ import { OutputEditorFactory } from './theia/output/output-editor-factory'; import { StartupTaskProvider } from '../electron-common/startup-task'; import { DeleteSketch } from './contributions/delete-sketch'; import { UserFields } from './contributions/user-fields'; +import { KeybindingRegistry } from './theia/core/keybinding'; +import { KeybindingRegistry as TheiaKeybindingRegistry } from '@theia/core/lib/browser/keybinding'; +import { KeyboardLayoutService } from './theia/core/keyboard-layout-service'; +import { KeyboardLayoutService as TheiaKeyboardLayoutService } from '@theia/core/lib/browser/keyboard/keyboard-layout-service'; const registerArduinoThemes = () => { const themes: MonacoThemeJson[] = [ @@ -996,4 +1000,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport); bind(HostedPluginEvents).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(HostedPluginEvents); + + // #1428 + bind(KeybindingRegistry).toSelf().inSingletonScope(); + rebind(TheiaKeybindingRegistry).toService(KeybindingRegistry); + bind(KeyboardLayoutService).toSelf().inSingletonScope(); + rebind(TheiaKeyboardLayoutService).toService(KeyboardLayoutService); }); diff --git a/arduino-ide-extension/src/browser/theia/core/keybinding.ts b/arduino-ide-extension/src/browser/theia/core/keybinding.ts new file mode 100644 index 000000000..69c72a08f --- /dev/null +++ b/arduino-ide-extension/src/browser/theia/core/keybinding.ts @@ -0,0 +1,20 @@ +import { + KeybindingContext, + KeybindingRegistry as TheiaKeybindingRegistry, +} from '@theia/core/lib/browser/keybinding'; +import { injectable } from '@theia/core/shared/inversify'; +import { measure } from '../../../common/utils'; + +@injectable() +export class KeybindingRegistry extends TheiaKeybindingRegistry { + override onStart(): Promise { + return measure('KeybindingRegistry#onStart', () => super.onStart()); + } + protected override registerContext(...contexts: KeybindingContext[]): void { + return measure( + 'KeybindingRegistry#registerContext', + () => super.registerContext(...contexts), + ...contexts.map(({ id }) => id) + ); + } +} diff --git a/arduino-ide-extension/src/browser/theia/core/keyboard-layout-service.ts b/arduino-ide-extension/src/browser/theia/core/keyboard-layout-service.ts new file mode 100644 index 000000000..7a4cdac5f --- /dev/null +++ b/arduino-ide-extension/src/browser/theia/core/keyboard-layout-service.ts @@ -0,0 +1,34 @@ +import { + KeyboardLayout, + KeyboardLayoutService as TheiaKeyboardLayoutService, +} from '@theia/core/lib/browser/keyboard/keyboard-layout-service'; +import { NativeKeyboardLayout } from '@theia/core/lib/common/keyboard/keyboard-layout-provider'; +import { injectable } from '@theia/core/shared/inversify'; +import { measure } from '../../../common/utils'; + +@injectable() +export class KeyboardLayoutService extends TheiaKeyboardLayoutService { + override initialize(): Promise { + return measure('KeyboardLayoutService#initialize', () => + super.initialize() + ); + } + protected override updateLayout( + newLayout: NativeKeyboardLayout + ): KeyboardLayout { + return measure( + 'KeyboardLayoutService#updateLayout', + () => super.updateLayout(newLayout), + newLayout + ); + } + protected override transformNativeLayout( + nativeLayout: NativeKeyboardLayout + ): KeyboardLayout { + return measure( + 'KeyboardLayoutService#transformNativeLayout', + () => super.transformNativeLayout(nativeLayout), + nativeLayout + ); + } +} diff --git a/arduino-ide-extension/src/common/utils.ts b/arduino-ide-extension/src/common/utils.ts index 8ffa1fd9a..5b4677821 100644 --- a/arduino-ide-extension/src/common/utils.ts +++ b/arduino-ide-extension/src/common/utils.ts @@ -16,3 +16,49 @@ export function firstToUpperCase(what: string): string { export function isNullOrUndefined(what: any): what is undefined | null { return what === undefined || what === null; } + +export function measure( + what: string, + task: () => Promise, + ...args: unknown[] +): Promise; +export function measure(what: string, task: () => T, ...args: unknown[]): T; +export function measure( + what: string, + task: (() => Promise) | (() => T), + ...args: unknown[] +): Promise | T { + const start = performance.now(); + const result = task(); + if (typeof result === 'object') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const object = result as any; + if ('then' in object) { + return Promise.resolve(result).then((resolved) => + end(resolved, start, what, args) + ); + } + } + return end(result, start, what, args); +} +function end(object: T, start: number, what: string, args: unknown[]): T { + console.log( + `--- #1428 --- ${what} took ${performance.now() - start} ms.${ + args.length ? ` Args: ${tryStringify(args)}` : '' + }` + ); + return object; +} +function tryStringify(args: unknown[]): string { + try { + return JSON.stringify(args); + } catch (err) { + if ( + err instanceof TypeError && + err.message.toLowerCase().includes('circular structure to json') + ) { + return ''; + } + throw err; + } +} diff --git a/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts b/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts index 9ab5bc99d..e209da523 100644 --- a/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts +++ b/arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts @@ -18,6 +18,7 @@ import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl'; import { ElectronMainApplication } from './theia/electron-main-application'; import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service'; import { TheiaElectronWindow } from './theia/theia-electron-window'; +import { ElectronNativeKeymap } from '@theia/core/lib/electron-main/electron-native-keymap'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ElectronMainApplication).toSelf().inSingletonScope(); @@ -58,4 +59,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { .inSingletonScope(); bind(IsTempSketch).toSelf().inSingletonScope(); + + // https://github.com/eclipse-theia/theia/issues/11688 + bind(ElectronNativeKeymap).toSelf().inSingletonScope(); + bind(ElectronMainApplicationContribution).toService(ElectronNativeKeymap); }); diff --git a/arduino-ide-extension/src/electron-node/theia/electron-keyboard-layout-provider.ts b/arduino-ide-extension/src/electron-node/theia/electron-keyboard-layout-provider.ts new file mode 100644 index 000000000..13b6330f6 --- /dev/null +++ b/arduino-ide-extension/src/electron-node/theia/electron-keyboard-layout-provider.ts @@ -0,0 +1,26 @@ +import { ElectronKeyboardLayoutProvider as TheiaElectronKeyboardLayoutProvider } from '@theia/core/lib/electron-node/keyboard/electron-keyboard-layout-provider'; +import { NativeKeyboardLayout } from '@theia/core/lib/common/keyboard/keyboard-layout-provider'; +import { injectable } from '@theia/core/shared/inversify'; +import * as nativeKeymap from '@theia/electron/shared/native-keymap'; +import { measure } from '../../common/utils'; + +@injectable() +export class ElectronKeyboardLayoutProvider extends TheiaElectronKeyboardLayoutProvider { + override getNativeLayout(): Promise { + return measure('ElectronKeyboardLayoutProvider#getNativeLayout', () => + super.getNativeLayout() + ); + } + protected override getNativeLayoutSync(): NativeKeyboardLayout { + return { + info: measure( + 'ElectronKeyboardLayoutProvider#getNativeLayoutSync#getCurrentKeyboardLayout', + () => nativeKeymap.getCurrentKeyboardLayout() + ), + mapping: measure( + 'ElectronKeyboardLayoutProvider#getNativeLayoutSync#getKeyMap', + () => nativeKeymap.getKeyMap() + ), + }; + } +} diff --git a/arduino-ide-extension/src/node/arduino-ide-backend-module.ts b/arduino-ide-extension/src/node/arduino-ide-backend-module.ts index b2486e478..d22da2586 100644 --- a/arduino-ide-extension/src/node/arduino-ide-backend-module.ts +++ b/arduino-ide-extension/src/node/arduino-ide-backend-module.ts @@ -111,6 +111,8 @@ import { } from '../common/protocol/survey-service'; import { IsTempSketch } from './is-temp-sketch'; import { rebindNsfwFileSystemWatcher } from './theia/filesystem/nsfw-watcher/nsfw-bindings'; +import { ElectronKeyboardLayoutProvider } from '../electron-node/theia/electron-keyboard-layout-provider'; +import { ElectronKeyboardLayoutProvider as TheiaElectronKeyboardLayoutProvider } from '@theia/core/lib/electron-node/keyboard/electron-keyboard-layout-provider'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(BackendApplication).toSelf().inSingletonScope(); @@ -380,6 +382,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { .inSingletonScope(); bind(IsTempSketch).toSelf().inSingletonScope(); + + // #1428 + bind(ElectronKeyboardLayoutProvider).toSelf().inSingletonScope(); + rebind(TheiaElectronKeyboardLayoutProvider).toService( + ElectronKeyboardLayoutProvider + ); }); function bindChildLogger(bind: interfaces.Bind, name: string): void {