@theia/core 1.77.0-next.49 → 1.77.0-next.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/lib/browser/breadcrumbs/breadcrumbs-renderer.js +1 -1
  2. package/lib/browser/breadcrumbs/breadcrumbs-renderer.js.map +1 -1
  3. package/lib/browser/catalog.json +81 -0
  4. package/lib/browser/color-application-contribution.d.ts +1 -1
  5. package/lib/browser/color-application-contribution.d.ts.map +1 -1
  6. package/lib/browser/color-registry.d.ts +1 -1
  7. package/lib/browser/color-registry.d.ts.map +1 -1
  8. package/lib/browser/common-frontend-contribution.d.ts +6 -1
  9. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  10. package/lib/browser/common-frontend-contribution.js +20 -3
  11. package/lib/browser/common-frontend-contribution.js.map +1 -1
  12. package/lib/browser/common-frontend-contribution.spec.js +2 -1
  13. package/lib/browser/common-frontend-contribution.spec.js.map +1 -1
  14. package/lib/browser/components/card.d.ts +1 -1
  15. package/lib/browser/components/card.d.ts.map +1 -1
  16. package/lib/browser/components/card.js +3 -2
  17. package/lib/browser/components/card.js.map +1 -1
  18. package/lib/browser/connection-status-service.spec.js +2 -1
  19. package/lib/browser/connection-status-service.spec.js.map +1 -1
  20. package/lib/browser/context-key-service.d.ts +11 -0
  21. package/lib/browser/context-key-service.d.ts.map +1 -1
  22. package/lib/browser/context-key-service.js +7 -0
  23. package/lib/browser/context-key-service.js.map +1 -1
  24. package/lib/browser/decorations-service.spec.js +2 -1
  25. package/lib/browser/decorations-service.spec.js.map +1 -1
  26. package/lib/browser/dialogs/react-dialog.spec.js +2 -1
  27. package/lib/browser/dialogs/react-dialog.spec.js.map +1 -1
  28. package/lib/browser/diff-uris.js +1 -1
  29. package/lib/browser/diff-uris.js.map +1 -1
  30. package/lib/browser/endpoint.js +2 -1
  31. package/lib/browser/endpoint.js.map +1 -1
  32. package/lib/browser/endpoint.spec.js +2 -1
  33. package/lib/browser/endpoint.spec.js.map +1 -1
  34. package/lib/browser/keybinding.d.ts +95 -17
  35. package/lib/browser/keybinding.d.ts.map +1 -1
  36. package/lib/browser/keybinding.js +406 -76
  37. package/lib/browser/keybinding.js.map +1 -1
  38. package/lib/browser/keybinding.spec.js +789 -33
  39. package/lib/browser/keybinding.spec.js.map +1 -1
  40. package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js +1 -1
  41. package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js.map +1 -1
  42. package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts +3 -4
  43. package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts.map +1 -1
  44. package/lib/browser/keyboard/browser-keyboard-layout-provider.js +3 -3
  45. package/lib/browser/keyboard/browser-keyboard-layout-provider.js.map +1 -1
  46. package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js +42 -3
  47. package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js.map +1 -1
  48. package/lib/browser/keyboard/keyboard-layout-service.d.ts +67 -15
  49. package/lib/browser/keyboard/keyboard-layout-service.d.ts.map +1 -1
  50. package/lib/browser/keyboard/keyboard-layout-service.js +309 -98
  51. package/lib/browser/keyboard/keyboard-layout-service.js.map +1 -1
  52. package/lib/browser/keyboard/keyboard-layout-service.spec.js +541 -15
  53. package/lib/browser/keyboard/keyboard-layout-service.spec.js.map +1 -1
  54. package/lib/browser/keyboard/keys.spec.js +187 -3
  55. package/lib/browser/keyboard/keys.spec.js.map +1 -1
  56. package/lib/browser/label-provider.d.ts.map +1 -1
  57. package/lib/browser/label-provider.js +5 -3
  58. package/lib/browser/label-provider.js.map +1 -1
  59. package/lib/browser/label-provider.spec.js +2 -1
  60. package/lib/browser/label-provider.spec.js.map +1 -1
  61. package/lib/browser/markdown-rendering/markdown-link-handler.js +2 -1
  62. package/lib/browser/markdown-rendering/markdown-link-handler.js.map +1 -1
  63. package/lib/browser/markdown-rendering/markdown-renderer.d.ts +1 -1
  64. package/lib/browser/markdown-rendering/markdown-renderer.d.ts.map +1 -1
  65. package/lib/browser/markdown-rendering/markdown-renderer.js +4 -4
  66. package/lib/browser/markdown-rendering/markdown-renderer.js.map +1 -1
  67. package/lib/browser/markdown-rendering/markdown.js +2 -1
  68. package/lib/browser/markdown-rendering/markdown.js.map +1 -1
  69. package/lib/browser/markdown-rendering/markdown.spec.js +2 -1
  70. package/lib/browser/markdown-rendering/markdown.spec.js.map +1 -1
  71. package/lib/browser/menu/action-menu-node.d.ts +5 -3
  72. package/lib/browser/menu/action-menu-node.d.ts.map +1 -1
  73. package/lib/browser/menu/action-menu-node.js +2 -11
  74. package/lib/browser/menu/action-menu-node.js.map +1 -1
  75. package/lib/browser/menu/action-menu-node.spec.d.ts +2 -0
  76. package/lib/browser/menu/action-menu-node.spec.d.ts.map +1 -0
  77. package/lib/browser/menu/action-menu-node.spec.js +54 -0
  78. package/lib/browser/menu/action-menu-node.spec.js.map +1 -0
  79. package/lib/browser/menu/browser-menu-plugin.js +1 -1
  80. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  81. package/lib/browser/menu/menu.spec.js +2 -1
  82. package/lib/browser/menu/menu.spec.js.map +1 -1
  83. package/lib/browser/opener-service.spec.js +3 -2
  84. package/lib/browser/opener-service.spec.js.map +1 -1
  85. package/lib/browser/perspective-service.spec.js +2 -1
  86. package/lib/browser/perspective-service.spec.js.map +1 -1
  87. package/lib/browser/preferences/preference-proxy.spec.js +2 -1
  88. package/lib/browser/preferences/preference-proxy.spec.js.map +1 -1
  89. package/lib/browser/preferences/preference-schema-provider.spec.js +2 -1
  90. package/lib/browser/preferences/preference-schema-provider.spec.js.map +1 -1
  91. package/lib/browser/preferences/preference-service.spec.js +2 -1
  92. package/lib/browser/preferences/preference-service.spec.js.map +1 -1
  93. package/lib/browser/preferences/preference-validation-service.spec.js +78 -77
  94. package/lib/browser/preferences/preference-validation-service.spec.js.map +1 -1
  95. package/lib/browser/quick-input/quick-command-service.d.ts +10 -1
  96. package/lib/browser/quick-input/quick-command-service.d.ts.map +1 -1
  97. package/lib/browser/quick-input/quick-command-service.js +31 -6
  98. package/lib/browser/quick-input/quick-command-service.js.map +1 -1
  99. package/lib/browser/quick-input/quick-command-service.spec.d.ts +2 -0
  100. package/lib/browser/quick-input/quick-command-service.spec.d.ts.map +1 -0
  101. package/lib/browser/quick-input/quick-command-service.spec.js +130 -0
  102. package/lib/browser/quick-input/quick-command-service.spec.js.map +1 -0
  103. package/lib/browser/quick-input/quick-help-service.js +1 -1
  104. package/lib/browser/quick-input/quick-help-service.js.map +1 -1
  105. package/lib/browser/quick-input/quick-input-service.spec.js +2 -1
  106. package/lib/browser/quick-input/quick-input-service.spec.js.map +1 -1
  107. package/lib/browser/quick-input/quick-pick-service-impl.d.ts +6 -6
  108. package/lib/browser/quick-input/quick-pick-service-impl.d.ts.map +1 -1
  109. package/lib/browser/quick-input/quick-view-service.spec.js +2 -1
  110. package/lib/browser/quick-input/quick-view-service.spec.js.map +1 -1
  111. package/lib/browser/secondary-window-handler.d.ts +4 -4
  112. package/lib/browser/secondary-window-handler.d.ts.map +1 -1
  113. package/lib/browser/shell/application-shell.js +1 -1
  114. package/lib/browser/shell/application-shell.js.map +1 -1
  115. package/lib/browser/shell/application-shell.spec.js +2 -1
  116. package/lib/browser/shell/application-shell.spec.js.map +1 -1
  117. package/lib/browser/shell/shell-layout-restorer.spec.js +2 -1
  118. package/lib/browser/shell/shell-layout-restorer.spec.js.map +1 -1
  119. package/lib/browser/shell/side-panel-toolbar.d.ts +1 -1
  120. package/lib/browser/shell/side-panel-toolbar.d.ts.map +1 -1
  121. package/lib/browser/shell/sidebar-menu-widget.js +1 -1
  122. package/lib/browser/shell/sidebar-menu-widget.js.map +1 -1
  123. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +1 -1
  124. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
  125. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js +2 -1
  126. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -1
  127. package/lib/browser/shell/tab-bars.js +2 -1
  128. package/lib/browser/shell/tab-bars.js.map +1 -1
  129. package/lib/browser/status-bar/status-bar.js +1 -1
  130. package/lib/browser/status-bar/status-bar.js.map +1 -1
  131. package/lib/browser/storage-service.spec.js +2 -1
  132. package/lib/browser/storage-service.spec.js.map +1 -1
  133. package/lib/browser/test/jsdom.d.ts.map +1 -1
  134. package/lib/browser/test/jsdom.js +11 -0
  135. package/lib/browser/test/jsdom.js.map +1 -1
  136. package/lib/browser/tooltip-service.js +1 -1
  137. package/lib/browser/tooltip-service.js.map +1 -1
  138. package/lib/browser/tree/tree-consistency.spec.js +1 -1
  139. package/lib/browser/tree/tree-consistency.spec.js.map +1 -1
  140. package/lib/browser/tree/tree-container.spec.js +6 -5
  141. package/lib/browser/tree/tree-container.spec.js.map +1 -1
  142. package/lib/browser/tree/tree-iterator.js +1 -1
  143. package/lib/browser/tree/tree-iterator.js.map +1 -1
  144. package/lib/browser/tree/tree-model.js +2 -2
  145. package/lib/browser/tree/tree-model.js.map +1 -1
  146. package/lib/browser/tree/tree-view-welcome-widget.js +1 -1
  147. package/lib/browser/tree/tree-view-welcome-widget.js.map +1 -1
  148. package/lib/browser/tree/tree-widget.d.ts +2 -2
  149. package/lib/browser/tree/tree-widget.d.ts.map +1 -1
  150. package/lib/browser/tree/tree-widget.js +1 -1
  151. package/lib/browser/tree/tree-widget.js.map +1 -1
  152. package/lib/browser/tree/tree.spec.js +2 -1
  153. package/lib/browser/tree/tree.spec.js.map +1 -1
  154. package/lib/browser/user-working-directory-provider.js +1 -1
  155. package/lib/browser/user-working-directory-provider.js.map +1 -1
  156. package/lib/browser/widgets/react-renderer.js +1 -1
  157. package/lib/browser/widgets/react-renderer.js.map +1 -1
  158. package/lib/browser/widgets/react-widget.js +1 -1
  159. package/lib/browser/widgets/react-widget.js.map +1 -1
  160. package/lib/browser/widgets/select-component.js +4 -3
  161. package/lib/browser/widgets/select-component.js.map +1 -1
  162. package/lib/browser/widgets/select-component.spec.js +3 -2
  163. package/lib/browser/widgets/select-component.spec.js.map +1 -1
  164. package/lib/browser/widgets/widget.js +1 -1
  165. package/lib/browser/widgets/widget.js.map +1 -1
  166. package/lib/common/buffer.js +3 -2
  167. package/lib/common/buffer.js.map +1 -1
  168. package/lib/common/cancellation.js.map +1 -1
  169. package/lib/common/command.spec.js +2 -1
  170. package/lib/common/command.spec.js.map +1 -1
  171. package/lib/common/disposable.spec.js +3 -2
  172. package/lib/common/disposable.spec.js.map +1 -1
  173. package/lib/common/encoding-service.js +2 -2
  174. package/lib/common/encoding-service.js.map +1 -1
  175. package/lib/common/event.d.ts +1 -1
  176. package/lib/common/event.d.ts.map +1 -1
  177. package/lib/common/event.js +0 -1
  178. package/lib/common/event.js.map +1 -1
  179. package/lib/common/file-uri.js +2 -1
  180. package/lib/common/file-uri.js.map +1 -1
  181. package/lib/common/fuzzy-search.js +1 -1
  182. package/lib/common/fuzzy-search.js.map +1 -1
  183. package/lib/common/glob.d.ts.map +1 -1
  184. package/lib/common/glob.js +9 -15
  185. package/lib/common/glob.js.map +1 -1
  186. package/lib/common/index.js +1 -1
  187. package/lib/common/index.js.map +1 -1
  188. package/lib/common/keyboard/keyboard-layout-provider.d.ts +7 -0
  189. package/lib/common/keyboard/keyboard-layout-provider.d.ts.map +1 -1
  190. package/lib/common/keyboard/keyboard-layout-provider.js.map +1 -1
  191. package/lib/common/keys.d.ts +86 -3
  192. package/lib/common/keys.d.ts.map +1 -1
  193. package/lib/common/keys.js +263 -24
  194. package/lib/common/keys.js.map +1 -1
  195. package/lib/common/message-rpc/channel.spec.js +3 -2
  196. package/lib/common/message-rpc/channel.spec.js.map +1 -1
  197. package/lib/common/messaging/proxy-factory.js +2 -2
  198. package/lib/common/messaging/proxy-factory.js.map +1 -1
  199. package/lib/common/messaging/proxy-factory.spec.js +2 -1
  200. package/lib/common/messaging/proxy-factory.spec.js.map +1 -1
  201. package/lib/common/messaging/web-socket-channel.d.ts +1 -1
  202. package/lib/common/messaging/web-socket-channel.d.ts.map +1 -1
  203. package/lib/common/path.spec.js +2 -1
  204. package/lib/common/path.spec.js.map +1 -1
  205. package/lib/common/performance/stopwatch.spec.js +2 -1
  206. package/lib/common/performance/stopwatch.spec.js.map +1 -1
  207. package/lib/common/preferences/preference-schema-service.d.ts +2 -2
  208. package/lib/common/preferences/preference-schema-service.d.ts.map +1 -1
  209. package/lib/common/preferences/preference-service.js +1 -1
  210. package/lib/common/preferences/preference-service.js.map +1 -1
  211. package/lib/common/promise-util.spec.js +2 -1
  212. package/lib/common/promise-util.spec.js.map +1 -1
  213. package/lib/common/quick-pick-service.d.ts +5 -0
  214. package/lib/common/quick-pick-service.d.ts.map +1 -1
  215. package/lib/common/quick-pick-service.js +2 -1
  216. package/lib/common/quick-pick-service.js.map +1 -1
  217. package/lib/common/reference.spec.js +2 -1
  218. package/lib/common/reference.spec.js.map +1 -1
  219. package/lib/common/resource.js +1 -1
  220. package/lib/common/resource.js.map +1 -1
  221. package/lib/common/selection-service.spec.js +2 -1
  222. package/lib/common/selection-service.spec.js.map +1 -1
  223. package/lib/common/selection.js +2 -1
  224. package/lib/common/selection.js.map +1 -1
  225. package/lib/common/stream.d.ts.map +1 -1
  226. package/lib/common/stream.js +2 -1
  227. package/lib/common/stream.js.map +1 -1
  228. package/lib/common/strings.d.ts.map +1 -1
  229. package/lib/common/strings.js +1 -2
  230. package/lib/common/strings.js.map +1 -1
  231. package/lib/common/ternary-search-tree.js +2 -2
  232. package/lib/common/ternary-search-tree.js.map +1 -1
  233. package/lib/common/ternary-search-tree.spec.js +12 -0
  234. package/lib/common/ternary-search-tree.spec.js.map +1 -1
  235. package/lib/common/test/mock-resource-provider.js +1 -1
  236. package/lib/common/test/mock-resource-provider.js.map +1 -1
  237. package/lib/common/types.spec.js +2 -1
  238. package/lib/common/types.spec.js.map +1 -1
  239. package/lib/common/uri-command-handler.js +4 -3
  240. package/lib/common/uri-command-handler.js.map +1 -1
  241. package/lib/common/uri-command-handler.spec.js +3 -2
  242. package/lib/common/uri-command-handler.spec.js.map +1 -1
  243. package/lib/common/uri.spec.js +3 -2
  244. package/lib/common/uri.spec.js.map +1 -1
  245. package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts +2 -0
  246. package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts.map +1 -1
  247. package/lib/electron-browser/keyboard/electron-keyboard-module.js +8 -1
  248. package/lib/electron-browser/keyboard/electron-keyboard-module.js.map +1 -1
  249. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts +2 -0
  250. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts.map +1 -0
  251. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js +28 -0
  252. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js.map +1 -0
  253. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  254. package/lib/electron-browser/menu/electron-main-menu-factory.js +3 -2
  255. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  256. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  257. package/lib/electron-browser/menu/electron-menu-contribution.js +3 -0
  258. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  259. package/lib/electron-browser/window/window-zoom-action-bar.js +2 -1
  260. package/lib/electron-browser/window/window-zoom-action-bar.js.map +1 -1
  261. package/lib/electron-common/electron-api.d.ts +5 -0
  262. package/lib/electron-common/electron-api.d.ts.map +1 -1
  263. package/lib/electron-common/electron-api.js +21 -0
  264. package/lib/electron-common/electron-api.js.map +1 -1
  265. package/lib/electron-common/electron-api.spec.d.ts +2 -0
  266. package/lib/electron-common/electron-api.spec.d.ts.map +1 -0
  267. package/lib/electron-common/electron-api.spec.js +40 -0
  268. package/lib/electron-common/electron-api.spec.js.map +1 -0
  269. package/lib/electron-main/electron-api-main.d.ts +0 -1
  270. package/lib/electron-main/electron-api-main.d.ts.map +1 -1
  271. package/lib/electron-main/electron-api-main.js +3 -14
  272. package/lib/electron-main/electron-api-main.js.map +1 -1
  273. package/lib/electron-main/electron-main-application.js +2 -2
  274. package/lib/electron-main/electron-main-application.js.map +1 -1
  275. package/lib/electron-node/hosting/electron-backend-hosting-module.spec.js +2 -1
  276. package/lib/electron-node/hosting/electron-backend-hosting-module.spec.js.map +1 -1
  277. package/lib/electron-node/keyboard/electron-keyboard-layout-provider.js +1 -1
  278. package/lib/electron-node/keyboard/electron-keyboard-layout-provider.js.map +1 -1
  279. package/lib/electron-node/token/electron-token-validator.js +2 -2
  280. package/lib/electron-node/token/electron-token-validator.js.map +1 -1
  281. package/lib/node/backend-application.d.ts +1 -1
  282. package/lib/node/backend-application.d.ts.map +1 -1
  283. package/lib/node/backend-application.js +7 -7
  284. package/lib/node/backend-application.js.map +1 -1
  285. package/lib/node/backend-application.spec.js +1 -1
  286. package/lib/node/backend-application.spec.js.map +1 -1
  287. package/lib/node/bundled-resource-provider.js +4 -4
  288. package/lib/node/bundled-resource-provider.js.map +1 -1
  289. package/lib/node/bundled-resource-provider.spec.js +5 -4
  290. package/lib/node/bundled-resource-provider.spec.js.map +1 -1
  291. package/lib/node/cli.d.ts +1 -1
  292. package/lib/node/cli.d.ts.map +1 -1
  293. package/lib/node/cli.js +2 -2
  294. package/lib/node/cli.js.map +1 -1
  295. package/lib/node/cli.spec.js +3 -2
  296. package/lib/node/cli.spec.js.map +1 -1
  297. package/lib/node/console-logger-server.js +1 -1
  298. package/lib/node/console-logger-server.js.map +1 -1
  299. package/lib/node/env-variables/env-variables-server.js +1 -1
  300. package/lib/node/env-variables/env-variables-server.js.map +1 -1
  301. package/lib/node/environment-utils.spec.js +2 -1
  302. package/lib/node/environment-utils.spec.js.map +1 -1
  303. package/lib/node/file-uri.spec.js +4 -3
  304. package/lib/node/file-uri.spec.js.map +1 -1
  305. package/lib/node/filesystem-locking.js +1 -1
  306. package/lib/node/filesystem-locking.js.map +1 -1
  307. package/lib/node/hosting/browser-connection-token.js +2 -2
  308. package/lib/node/hosting/browser-connection-token.js.map +1 -1
  309. package/lib/node/i18n/localization-contribution.js +1 -1
  310. package/lib/node/i18n/localization-contribution.js.map +1 -1
  311. package/lib/node/key-store-server.js +1 -1
  312. package/lib/node/key-store-server.js.map +1 -1
  313. package/lib/node/logger-cli-contribution.js +2 -2
  314. package/lib/node/logger-cli-contribution.js.map +1 -1
  315. package/lib/node/logger-cli-contribution.spec.js +17 -16
  316. package/lib/node/logger-cli-contribution.spec.js.map +1 -1
  317. package/lib/node/messaging/ipc-connection-provider.js +2 -2
  318. package/lib/node/messaging/ipc-connection-provider.js.map +1 -1
  319. package/lib/node/process-utils.js +1 -1
  320. package/lib/node/process-utils.js.map +1 -1
  321. package/lib/node/process-utils.spec.js +2 -1
  322. package/lib/node/process-utils.spec.js.map +1 -1
  323. package/lib/node/request/backend-request-facade.spec.js +2 -1
  324. package/lib/node/request/backend-request-facade.spec.js.map +1 -1
  325. package/lib/node/setting-service.spec.js +2 -1
  326. package/lib/node/setting-service.spec.js.map +1 -1
  327. package/package.json +4 -4
  328. package/src/browser/common-frontend-contribution.ts +17 -1
  329. package/src/browser/components/card.tsx +1 -1
  330. package/src/browser/context-key-service.ts +15 -0
  331. package/src/browser/keybinding.spec.ts +878 -34
  332. package/src/browser/keybinding.ts +470 -85
  333. package/src/browser/keyboard/browser-keyboard-frontend-contribution.ts +1 -1
  334. package/src/browser/keyboard/browser-keyboard-layout-provider.spec.ts +52 -1
  335. package/src/browser/keyboard/browser-keyboard-layout-provider.ts +5 -5
  336. package/src/browser/keyboard/keyboard-layout-service.spec.ts +640 -15
  337. package/src/browser/keyboard/keyboard-layout-service.ts +358 -106
  338. package/src/browser/keyboard/keys.spec.ts +206 -1
  339. package/src/browser/label-provider.ts +3 -1
  340. package/src/browser/markdown-rendering/markdown-renderer.ts +1 -1
  341. package/src/browser/menu/action-menu-node.spec.ts +60 -0
  342. package/src/browser/menu/action-menu-node.ts +8 -14
  343. package/src/browser/menu/browser-menu-plugin.ts +1 -1
  344. package/src/browser/preferences/preference-validation-service.spec.ts +1 -1
  345. package/src/browser/quick-input/quick-command-service.spec.ts +159 -0
  346. package/src/browser/quick-input/quick-command-service.ts +36 -9
  347. package/src/browser/quick-input/quick-help-service.ts +1 -1
  348. package/src/browser/test/jsdom.ts +11 -0
  349. package/src/browser/tree/tree-container.spec.ts +1 -1
  350. package/src/browser/tree/tree-iterator.ts +1 -1
  351. package/src/browser/tree/tree-model.ts +2 -2
  352. package/src/browser/tree/tree-widget.tsx +2 -2
  353. package/src/common/cancellation.ts +1 -1
  354. package/src/common/disposable.spec.ts +1 -1
  355. package/src/common/event.ts +1 -2
  356. package/src/common/glob.ts +13 -30
  357. package/src/common/json-schema.ts +1 -1
  358. package/src/common/keyboard/keyboard-layout-provider.ts +8 -0
  359. package/src/common/keys.ts +312 -27
  360. package/src/common/message-rpc/channel.spec.ts +1 -1
  361. package/src/common/messaging/proxy-factory.ts +2 -2
  362. package/src/common/quick-pick-service.ts +5 -0
  363. package/src/common/stream.ts +4 -3
  364. package/src/common/strings.ts +2 -5
  365. package/src/common/ternary-search-tree.spec.ts +14 -0
  366. package/src/common/ternary-search-tree.ts +3 -3
  367. package/src/common/uri-command-handler.ts +2 -2
  368. package/src/electron-browser/keyboard/electron-keyboard-module.spec.ts +29 -0
  369. package/src/electron-browser/keyboard/electron-keyboard-module.ts +9 -2
  370. package/src/electron-browser/menu/electron-main-menu-factory.ts +3 -3
  371. package/src/electron-browser/menu/electron-menu-contribution.ts +4 -0
  372. package/src/electron-common/electron-api.spec.ts +42 -0
  373. package/src/electron-common/electron-api.ts +23 -0
  374. package/src/electron-main/electron-api-main.ts +2 -13
  375. package/src/node/backend-application.ts +1 -1
  376. package/src/node/cli.ts +1 -1
  377. package/src/node/logger-cli-contribution.spec.ts +1 -1
@@ -15,20 +15,48 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { Container, injectable } from 'inversify';
18
+ import type { IWindowsKeyMapping } from 'native-keymap';
18
19
  import { Emitter, Event } from '../../common/event';
19
- import { KeyCode } from './keys';
20
+ import { Key, KeyCode, LayoutModifiers, NormalizedKeyboardInput } from './keys';
20
21
  import { KeyboardLayoutService } from './keyboard-layout-service';
21
- import { KeyboardLayoutProvider, NativeKeyboardLayout, KeyboardLayoutChangeNotifier } from '../../common/keyboard/keyboard-layout-provider';
22
+ import {
23
+ KeyboardLayoutProvider, NativeKeyboardLayout, KeyboardLayoutChangeNotifier, KeyValidationInput, KeyValidator, KeyboardLayoutSource
24
+ } from '../../common/keyboard/keyboard-layout-provider';
22
25
  import * as os from '../../common/os';
23
26
  import * as chai from 'chai';
24
27
  import * as sinon from 'sinon';
25
28
 
29
+ class TestKeyboardLayoutService extends KeyboardLayoutService {
30
+ public override getWindowsKeyCodeInterpretations(keyCode: KeyCode, input: NormalizedKeyboardInput,
31
+ layoutModifiers: LayoutModifiers | undefined): KeyCode[] {
32
+ return super.getWindowsKeyCodeInterpretations(keyCode, input, layoutModifiers);
33
+ }
34
+ }
35
+
36
+ const RUSSIAN_LAYOUT: NativeKeyboardLayout = {
37
+ info: { id: 'ru', lang: 'ru' },
38
+ mapping: {
39
+ KeyA: { value: 'ф', withShift: 'Ф', withAltGr: '', withShiftAltGr: '' },
40
+ KeyC: { value: 'с', withShift: 'С', withAltGr: '', withShiftAltGr: '' },
41
+ KeyV: { value: 'м', withShift: 'М', withAltGr: '', withShiftAltGr: '' },
42
+ KeyS: { value: 'ы', withShift: 'Ы', withAltGr: '', withShiftAltGr: '' },
43
+ Digit8: { value: '8', withShift: '*', withAltGr: '', withShiftAltGr: '' },
44
+ BracketLeft: { value: 'х', withShift: 'Х', withAltGr: '', withShiftAltGr: '' },
45
+ Comma: { value: 'б', withShift: 'Б', withAltGr: '', withShiftAltGr: '' }
46
+ }
47
+ };
48
+
26
49
  describe('keyboard layout service', function (): void {
27
50
 
28
51
  let stubOSX: sinon.SinonStub;
29
52
  let stubWindows: sinon.SinonStub;
30
53
 
31
- const setup = async (layout: NativeKeyboardLayout, system: 'mac' | 'win' | 'linux') => {
54
+ const setup = async (
55
+ layout: NativeKeyboardLayout,
56
+ system: 'mac' | 'win' | 'linux',
57
+ layoutSource?: KeyboardLayoutSource,
58
+ keyValidator?: KeyValidator
59
+ ) => {
32
60
  switch (system) {
33
61
  case 'mac':
34
62
  stubOSX = sinon.stub(os, 'isOSX').value(true);
@@ -43,9 +71,10 @@ describe('keyboard layout service', function (): void {
43
71
  stubWindows = sinon.stub(os, 'isWindows').value(false);
44
72
  }
45
73
  const container = new Container();
46
- container.bind(KeyboardLayoutService).toSelf().inSingletonScope();
74
+ container.bind(KeyboardLayoutService).to(TestKeyboardLayoutService).inSingletonScope();
47
75
  @injectable()
48
76
  class MockLayoutProvider implements KeyboardLayoutProvider, KeyboardLayoutChangeNotifier {
77
+ readonly layoutSource = layoutSource;
49
78
  emitter = new Emitter<NativeKeyboardLayout>();
50
79
  get onDidChangeNativeLayout(): Event<NativeKeyboardLayout> {
51
80
  return this.emitter.event;
@@ -56,7 +85,10 @@ describe('keyboard layout service', function (): void {
56
85
  }
57
86
  container.bind(KeyboardLayoutProvider).to(MockLayoutProvider);
58
87
  container.bind(KeyboardLayoutChangeNotifier).to(MockLayoutProvider);
59
- const service = container.get(KeyboardLayoutService);
88
+ if (keyValidator) {
89
+ container.bind(KeyValidator).toConstantValue(keyValidator);
90
+ }
91
+ const service = container.get(KeyboardLayoutService) as TestKeyboardLayoutService;
60
92
  await service.initialize();
61
93
  return service;
62
94
  };
@@ -66,16 +98,30 @@ describe('keyboard layout service', function (): void {
66
98
  stubWindows.restore();
67
99
  });
68
100
 
101
+ it('reports an unknown layout source when no source provider is configured', async () => {
102
+ const layout: NativeKeyboardLayout = { info: { id: 'mock', lang: 'en' }, mapping: {} };
103
+ chai.expect((await setup(layout, 'linux')).layoutSource).to.equal('unknown');
104
+ });
105
+
106
+ it('reports the configured layout source', async () => {
107
+ const layout: NativeKeyboardLayout = { info: { id: 'mock', lang: 'en' }, mapping: {} };
108
+ chai.expect((await setup(layout, 'linux', 'native-keymap')).layoutSource).to.equal('native-keymap');
109
+ });
110
+
69
111
  it('resolves correct key bindings with German Mac layout', async () => {
70
112
  const macGerman = require('../../../src/common/keyboard/layouts/de-German-mac.json');
71
113
  const service = await setup(macGerman, 'mac');
72
114
 
73
115
  const toggleComment = service.resolveKeyCode(KeyCode.createKeyCode('Slash+M1'));
74
- chai.expect(toggleComment.toString()).to.equal('meta+shift+7');
116
+ chai.expect(toggleComment.toString()).to.equal('meta+7');
117
+ chai.expect(toggleComment.character).to.equal('/');
118
+ chai.expect(toggleComment.layoutModifiers).to.deep.equal('shift');
75
119
  chai.expect(service.getKeyboardCharacter(toggleComment.key!)).to.equal('7');
76
120
 
77
121
  const indentLine = service.resolveKeyCode(KeyCode.createKeyCode('BracketRight+M1'));
78
- chai.expect(indentLine.toString()).to.equal('meta+alt+ctrl+6');
122
+ chai.expect(indentLine.toString()).to.equal('meta+6');
123
+ chai.expect(indentLine.character).to.equal(']');
124
+ chai.expect(indentLine.layoutModifiers).to.deep.equal('altGraph');
79
125
  chai.expect(service.getKeyboardCharacter(indentLine.key!)).to.equal('6');
80
126
  });
81
127
 
@@ -84,14 +130,201 @@ describe('keyboard layout service', function (): void {
84
130
  const service = await setup(macFrench, 'mac');
85
131
 
86
132
  const toggleComment = service.resolveKeyCode(KeyCode.createKeyCode('Slash+M1'));
87
- chai.expect(toggleComment.toString()).to.equal('meta+shift+.');
133
+ chai.expect(toggleComment.toString()).to.equal('meta+.');
134
+ chai.expect(toggleComment.character).to.equal('/');
135
+ chai.expect(toggleComment.layoutModifiers).to.deep.equal('shift');
88
136
  chai.expect(service.getKeyboardCharacter(toggleComment.key!)).to.equal(':');
89
137
 
90
138
  const indentLine = service.resolveKeyCode(KeyCode.createKeyCode('BracketRight+M1'));
91
- chai.expect(indentLine.toString()).to.equal('meta+shift+alt+ctrl+-');
139
+ chai.expect(indentLine.toString()).to.equal('meta+-');
140
+ chai.expect(indentLine.character).to.equal(']');
141
+ chai.expect(indentLine.layoutModifiers).to.deep.equal('shiftAltGraph');
92
142
  chai.expect(service.getKeyboardCharacter(indentLine.key!)).to.equal(')');
93
143
  });
94
144
 
145
+ it('uses global layout modifier priority and a stable physical-key tie-break', async () => {
146
+ const layout: NativeKeyboardLayout = {
147
+ info: { id: 'priority', lang: 'fr' },
148
+ mapping: {
149
+ Backslash: { value: '*', withShift: 'µ', withAltGr: '@', withShiftAltGr: '#' },
150
+ IntlBackslash: { value: '@', withShift: '#', withAltGr: 'µ', withShiftAltGr: '*' },
151
+ KeyB: { value: 'x', withShift: 'X', withAltGr: '', withShiftAltGr: '' },
152
+ KeyA: { value: 'x', withShift: 'X', withAltGr: '', withShiftAltGr: '' }
153
+ }
154
+ };
155
+ const service = await setup(layout, 'linux');
156
+
157
+ chai.expect(service.resolveKeyCode(KeyCode.parse('shift+2')).key).to.equal(Key.INTL_BACKSLASH);
158
+ chai.expect(service.resolveKeyCode(KeyCode.parse('x')).key).to.equal(Key.KEY_A);
159
+
160
+ const shifted = service.resolveKeyCode(KeyCode.parse('shift+x'));
161
+ const event = service.getKeyCodeInterpretations({ key: 'X', code: 'KeyA', shiftKey: true }, 'code')
162
+ .find(candidate => candidate.interpretation === 'layoutModifiers')!;
163
+ chai.expect(event.dispatchString()).to.equal(shifted.dispatchString());
164
+
165
+ const altShifted = service.resolveKeyCode(KeyCode.parse('alt+shift+x'));
166
+ const altEvent = service.getKeyCodeInterpretations({ key: 'X', code: 'KeyA', altKey: true, shiftKey: true }, 'code')
167
+ .find(candidate => candidate.interpretation === 'layoutModifiers')!;
168
+ chai.expect(altEvent.dispatchString()).to.equal(altShifted.dispatchString());
169
+
170
+ const capsLockEvent = service.getKeyCodeInterpretations({ key: 'x', code: 'KeyA', shiftKey: true }, 'code')
171
+ .find(candidate => candidate.interpretation === 'layoutModifiers')!;
172
+ chai.expect(capsLockEvent.dispatchString()).to.equal(shifted.dispatchString());
173
+ });
174
+
175
+ it('globally ranks case-folded fallback candidates', async () => {
176
+ const layout: NativeKeyboardLayout = {
177
+ info: { id: 'case-folded-priority', lang: 'en' },
178
+ mapping: {
179
+ KeyA: { value: 'a', withShift: 'A', withAltGr: 'ä', withShiftAltGr: '' },
180
+ KeyB: { value: 'b', withShift: 'ä', withAltGr: '', withShiftAltGr: '' }
181
+ }
182
+ };
183
+ const service = await setup(layout, 'linux');
184
+ const resolved = service.resolveKeyCode(KeyCode.parse('[char:0xC4]'));
185
+ chai.expect(resolved.key).to.equal(Key.KEY_B);
186
+ chai.expect(resolved.layoutModifiers).to.deep.equal('shift');
187
+ });
188
+
189
+ it('matches shifted physical scan-code bindings through the command interpretation', async () => {
190
+ const us = require('../../../src/common/keyboard/layouts/en-US-pc.json');
191
+ const service = await setup(us, 'linux');
192
+ const physical = service.resolveKeyCode(KeyCode.parse('ctrl+shift+[BracketRight]'));
193
+ const interpretations = service.getKeyCodeInterpretations({
194
+ key: '}', code: 'BracketRight', ctrlKey: true, shiftKey: true
195
+ }, 'code');
196
+ chai.expect(physical.physical).to.be.true;
197
+ chai.expect(interpretations.some(candidate => candidate.dispatchString() === physical.dispatchString())).to.be.true;
198
+ });
199
+
200
+ it('keeps BÉPO logical bindings distinct from their former US-position collisions', async () => {
201
+ const bepo = require('../../../src/common/keyboard/layouts/fr-Bepo-pc.json');
202
+ const service = await setup(bepo, 'linux');
203
+
204
+ const undo = service.resolveKeyCode(KeyCode.parse('ctrl+z'));
205
+ const outdent = service.resolveKeyCode(KeyCode.parse('ctrl+['));
206
+ const close = service.resolveKeyCode(KeyCode.parse('ctrl+w'));
207
+ const indent = service.resolveKeyCode(KeyCode.parse('ctrl+]'));
208
+ chai.expect(undo.key).to.equal(Key.BRACKET_LEFT);
209
+ chai.expect(outdent.key).to.equal(Key.DIGIT4);
210
+ chai.expect(outdent.layoutModifiers).to.equal('altGraph');
211
+ chai.expect(close.key).to.equal(Key.BRACKET_RIGHT);
212
+ chai.expect(indent.key).to.equal(Key.DIGIT5);
213
+ chai.expect(indent.layoutModifiers).to.equal('altGraph');
214
+ });
215
+
216
+ it('falls back to the US position for Latin letters missing from a non-Latin layout', async () => {
217
+ const service = await setup(RUSSIAN_LAYOUT, 'linux');
218
+
219
+ const resolved = service.resolveKeyCode(KeyCode.parse('ctrl+c'));
220
+ chai.expect(resolved.key).to.equal(Key.KEY_C);
221
+ chai.expect(resolved.supportedByLayout).to.be.true;
222
+ chai.expect(resolved.layoutModifiers).to.equal('none');
223
+ chai.expect(resolved.dispatchString()).to.equal('ctrl+c');
224
+ chai.expect(service.getLatinLetterFallback(Key.KEY_C)).to.equal('c');
225
+ chai.expect(service.getLatinLetterFallback(Key.DIGIT8)).to.be.undefined;
226
+ chai.expect(service.resolveKeyCode(KeyCode.parse('с')).key).to.equal(Key.KEY_C);
227
+ chai.expect(service.getKeyboardCharacter(Key.KEY_C)).to.equal('с');
228
+ });
229
+
230
+ it('absorbs Shift into the Latin fallback letter', async () => {
231
+ const service = await setup(RUSSIAN_LAYOUT, 'linux');
232
+
233
+ const resolved = service.resolveKeyCode(KeyCode.parse('ctrl+shift+a'));
234
+ chai.expect(resolved.key).to.equal(Key.KEY_A);
235
+ chai.expect(resolved.layoutModifiers).to.equal('shift');
236
+ chai.expect(resolved.shift).to.be.false;
237
+ chai.expect(resolved.character).to.equal('A');
238
+ });
239
+
240
+ it('does not fall back for punctuation on a non-Latin layout', async () => {
241
+ const service = await setup(RUSSIAN_LAYOUT, 'linux');
242
+
243
+ chai.expect(service.resolveKeyCode(KeyCode.parse('ctrl+[')).supportedByLayout).to.be.false;
244
+ });
245
+
246
+ it('does not override Latin letters produced elsewhere on the layout', async () => {
247
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
248
+ const service = await setup(german, 'linux');
249
+
250
+ chai.expect(service.resolveKeyCode(KeyCode.parse('ctrl+z')).key).to.equal(Key.KEY_Y);
251
+ chai.expect(service.getLatinLetterFallback(Key.KEY_Z)).to.be.undefined;
252
+ });
253
+
254
+ it('does not override letters produced in only one case on the base or Shift layer', async () => {
255
+ const layout: NativeKeyboardLayout = {
256
+ info: { id: 'one-case-only', lang: 'en' },
257
+ mapping: {
258
+ KeyS: { value: 'o', withShift: 'Q', withAltGr: '', withShiftAltGr: '' },
259
+ KeyO: { value: 's', withShift: 'P', withAltGr: '', withShiftAltGr: '' }
260
+ }
261
+ };
262
+ const service = await setup(layout, 'linux');
263
+
264
+ chai.expect(service.resolveKeyCode(KeyCode.parse('o')).key).to.equal(Key.KEY_S);
265
+ chai.expect(service.resolveKeyCode(KeyCode.parse('p')).key).to.equal(Key.KEY_O);
266
+ chai.expect(service.resolveKeyCode(KeyCode.parse('p')).layoutModifiers).to.equal('shift');
267
+ for (const key of [Key.KEY_O, Key.KEY_P, Key.KEY_Q, Key.KEY_S]) {
268
+ chai.expect(service.getLatinLetterFallback(key)).to.be.undefined;
269
+ }
270
+ });
271
+
272
+ it('adds a fallback when a letter is produced only on the AltGraph layer', async () => {
273
+ const layout: NativeKeyboardLayout = {
274
+ info: { id: 'altgraph-only-latin', lang: 'ru' },
275
+ mapping: {
276
+ KeyQ: { value: 'й', withShift: 'Й', withAltGr: 'a', withShiftAltGr: '' }
277
+ }
278
+ };
279
+ const service = await setup(layout, 'linux');
280
+
281
+ const resolved = service.resolveKeyCode(KeyCode.parse('a'));
282
+ chai.expect(resolved.key).to.equal(Key.KEY_A);
283
+ chai.expect(resolved.layoutModifiers).to.equal('none');
284
+ chai.expect(service.getLatinLetterFallback(Key.KEY_A)).to.equal('a');
285
+ });
286
+
287
+ it('adds Latin fallbacks to Windows mappings with vkeys and character values', async () => {
288
+ const layout: NativeKeyboardLayout = {
289
+ ...RUSSIAN_LAYOUT,
290
+ mapping: {
291
+ ...RUSSIAN_LAYOUT.mapping,
292
+ KeyC: { ...RUSSIAN_LAYOUT.mapping.KeyC, vkey: 'VK_C' } as never
293
+ }
294
+ };
295
+ const service = await setup(layout, 'win');
296
+
297
+ chai.expect(service.resolveKeyCode(KeyCode.parse('ctrl+c')).key).to.equal(Key.KEY_C);
298
+ chai.expect(service.getLatinLetterFallback(Key.KEY_C)).to.equal('c');
299
+ });
300
+
301
+ it('marks printable bindings unresolved when the layout mapping is empty', async () => {
302
+ const layout: NativeKeyboardLayout = { info: { id: 'empty', lang: 'en' }, mapping: {} };
303
+ const service = await setup(layout, 'linux');
304
+
305
+ chai.expect(service.resolveKeyCode(KeyCode.parse('ctrl+[')).supportedByLayout).to.be.false;
306
+ for (const lower of 'abcdefghijklmnopqrstuvwxyz') {
307
+ const key = Key.getKey(`Key${lower.toUpperCase()}`)!;
308
+ chai.expect(service.resolveKeyCode(KeyCode.parse(`ctrl+${lower}`)).key).to.equal(key);
309
+ chai.expect(service.getLatinLetterFallback(key)).to.equal(lower);
310
+ }
311
+ chai.expect(service.resolveKeyCode(KeyCode.parse('f1')).supportedByLayout).to.be.true;
312
+ });
313
+
314
+ it('marks unavailable logical characters unresolved instead of using the US position', async () => {
315
+ const layout: NativeKeyboardLayout = {
316
+ info: { id: 'missing-bracket', lang: 'en' },
317
+ mapping: {
318
+ KeyA: { value: 'a', withShift: 'A', withAltGr: '', withShiftAltGr: '' }
319
+ }
320
+ };
321
+ const service = await setup(layout, 'linux');
322
+
323
+ const outdent = service.resolveKeyCode(KeyCode.parse('ctrl+['));
324
+ chai.expect(outdent.supportedByLayout).to.be.false;
325
+ chai.expect(outdent.key).to.equal(Key.BRACKET_LEFT);
326
+ });
327
+
95
328
  it('does not let a dead key hijack a keybinding on Mac (#17677)', async () => {
96
329
  // On macOS US Extended, Option+P is the "combining comma below" dead key whose display glyph
97
330
  // is reported as ',' by native-keymap. Since KeyP is reported before Comma, without honoring the
@@ -120,17 +353,405 @@ describe('keyboard layout service', function (): void {
120
353
  chai.expect(service.getKeyboardCharacter(openSettings.key!)).to.equal(',');
121
354
  });
122
355
 
356
+ it('filters dead AltGraph mappings on Linux and Windows', async () => {
357
+ const layout: NativeKeyboardLayout = {
358
+ info: { id: 'dead-altgraph', lang: 'en' },
359
+ mapping: {
360
+ KeyP: {
361
+ value: 'p', withShift: 'P', withAltGr: '§', withShiftAltGr: '', withAltGrIsDeadKey: true, vkey: 'VK_P'
362
+ } as IWindowsKeyMapping & { withAltGrIsDeadKey: boolean }
363
+ }
364
+ };
365
+ for (const system of ['linux', 'win'] as const) {
366
+ const service = await setup(layout, system);
367
+ chai.expect(service.resolveKeyCode(KeyCode.parse('§')).supportedByLayout).to.be.false;
368
+ stubOSX.restore();
369
+ stubWindows.restore();
370
+ }
371
+ stubOSX = sinon.stub(os, 'isOSX').value(false);
372
+ stubWindows = sinon.stub(os, 'isWindows').value(false);
373
+ });
374
+
375
+ it('retains the Windows vkey fallback for value-less mappings', async () => {
376
+ const layout: NativeKeyboardLayout = {
377
+ info: { id: 'vkey-fallback', lang: 'en' },
378
+ mapping: { KeyA: { vkey: 'VK_A', value: '' } as never }
379
+ };
380
+ const service = await setup(layout, 'win');
381
+ const resolved = service.resolveKeyCode(KeyCode.parse('a'));
382
+ chai.expect(resolved.supportedByLayout).to.be.true;
383
+ chai.expect(resolved.key).to.equal(Key.KEY_A);
384
+ });
385
+
386
+ it('absorbs authored Shift when it selects a US character or layout shift layer', async () => {
387
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
388
+ const service = await setup(german, 'linux');
389
+ const legacy = service.resolveKeyCode(KeyCode.parse('ctrl+shift+['));
390
+ chai.expect(legacy.shift).to.be.false;
391
+ chai.expect(legacy.dispatchString()).to.equal('ctrl+7@altgraph');
392
+ });
393
+
394
+ it('retains authored Shift when it is not consumed by the logical character', async () => {
395
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
396
+ const service = await setup(german, 'linux');
397
+
398
+ const umlaut = service.resolveKeyCode(KeyCode.parse('ctrl+shift+ü'));
399
+ chai.expect(umlaut.dispatchString()).to.equal('shift+ctrl+[');
400
+ chai.expect(umlaut.layoutModifiers).to.equal('none');
401
+
402
+ chai.expect(service.resolveKeyCode(KeyCode.parse('shift+[char:0x41]')).dispatchString()).to.equal('a@shift');
403
+ chai.expect(service.resolveKeyCode(KeyCode.parse('shift+[char:0x61]')).dispatchString()).to.equal('shift+a');
404
+ chai.expect(service.resolveKeyCode(KeyCode.parse('ctrl+shift+a')).dispatchString()).to.equal('ctrl+a@shift');
405
+ chai.expect(service.resolveKeyCode(KeyCode.parse('ctrl+shift+[')).dispatchString()).to.equal('ctrl+7@altgraph');
406
+ });
407
+
408
+ it('requires Option for a macOS Shift+AltGraph layer', async () => {
409
+ const macUS = require('../../../src/common/keyboard/layouts/en-US-mac.json');
410
+ const service = await setup(macUS, 'mac');
411
+ chai.expect(service.detectLayoutModifiers({ key: '±', code: 'IntlBackslash', shiftKey: true })).to.equal('shift');
412
+ chai.expect(service.detectLayoutModifiers({ key: '±', code: 'IntlBackslash', shiftKey: true, altKey: true })).to.equal('shiftAltGraph');
413
+ });
414
+
415
+ it('interprets macOS Option as a command modifier besides the Option layer', async () => {
416
+ const macUS = require('../../../src/common/keyboard/layouts/en-US-mac.json');
417
+ const service = await setup(macUS, 'mac');
418
+
419
+ const optionShift = service.getKeyCodeInterpretations({ key: '∏', code: 'KeyP', shiftKey: true, altKey: true }, 'code');
420
+ chai.expect(optionShift.map(candidate => candidate.dispatchString())).to.deep.equal(['shift+alt+p', 'alt+p@shift', 'p@shift@altgraph']);
421
+ chai.expect(optionShift.map(candidate => candidate.character)).to.deep.equal(['P', 'P', '∏']);
422
+ chai.expect(service.resolveKeyCode(KeyCode.parse('shift+alt+p')).dispatchString()).to.equal('alt+p@shift');
423
+
424
+ const option = service.getKeyCodeInterpretations({ key: 'π', code: 'KeyP', altKey: true }, 'code');
425
+ chai.expect(option.map(candidate => candidate.dispatchString())).to.deep.equal(['alt+p', 'p@altgraph']);
426
+ chai.expect(option.map(candidate => candidate.character)).to.deep.equal(['p', 'π']);
427
+
428
+ const shift = service.getKeyCodeInterpretations({ key: 'P', code: 'KeyP', shiftKey: true }, 'code');
429
+ chai.expect(shift.map(candidate => candidate.dispatchString())).to.deep.equal(['shift+p', 'p@shift']);
430
+ });
431
+
432
+ it('records the base character for dead Option keys on macOS', async () => {
433
+ const service = await setup(require('../../../src/common/keyboard/layouts/en-US-mac.json'), 'mac');
434
+
435
+ const option = service.getKeyCodeInterpretations({ key: 'Dead', code: 'KeyE', altKey: true }, 'code');
436
+ chai.expect(option).to.have.length(1);
437
+ chai.expect(option[0].character).to.equal('e');
438
+ chai.expect(option[0].interpretation).to.equal('commandModifiers');
439
+ chai.expect(option[0].dispatchString()).to.equal('alt+e');
440
+
441
+ const optionShift = service.getKeyCodeInterpretations({ key: 'Dead', code: 'KeyE', altKey: true, shiftKey: true }, 'code');
442
+ chai.expect(optionShift.map(candidate => candidate.character)).to.deep.equal(['E', 'E']);
443
+ chai.expect(optionShift.map(candidate => candidate.dispatchString())).to.deep.equal(['shift+alt+e', 'alt+e@shift']);
444
+
445
+ const withoutOption = service.getKeyCodeInterpretations({ key: 'Dead', code: 'KeyE' }, 'code');
446
+ chai.expect(withoutOption).to.have.length(1);
447
+ chai.expect(withoutOption[0].character).to.be.undefined;
448
+ });
449
+
450
+ it('normalizes the character to the layout layer when Caps Lock inverts the case', async () => {
451
+ const us = await setup(require('../../../src/common/keyboard/layouts/en-US-pc.json'), 'linux');
452
+ const capsShift = us.getKeyCodeInterpretations({ key: 'p', code: 'KeyP', ctrlKey: true, shiftKey: true }, 'code');
453
+ chai.expect(capsShift.map(candidate => candidate.character)).to.deep.equal(['P', 'P']);
454
+ chai.expect(capsShift.map(candidate => candidate.dispatchString())).to.deep.equal(['shift+ctrl+p', 'ctrl+p@shift']);
455
+ const shift = us.getKeyCodeInterpretations({ key: 'P', code: 'KeyP', shiftKey: true }, 'code');
456
+ chai.expect(shift.map(candidate => candidate.character)).to.deep.equal(['P', 'P']);
457
+ stubOSX.restore();
458
+ stubWindows.restore();
459
+
460
+ const german = await setup(require('../../../src/common/keyboard/layouts/de-German-pc.json'), 'linux');
461
+ const capsBase = german.getKeyCodeInterpretations({ key: 'Ü', code: 'BracketLeft', ctrlKey: true }, 'code');
462
+ chai.expect(capsBase).to.have.length(1);
463
+ chai.expect(capsBase[0].character).to.equal('ü');
464
+ chai.expect(capsBase[0].dispatchString()).to.equal('ctrl+[');
465
+ });
466
+
467
+ it('normalizes the character to the macOS Option layer when Caps Lock inverts the case', async () => {
468
+ const service = await setup(require('../../../src/common/keyboard/layouts/en-US-mac.json'), 'mac');
469
+ const capsOption = service.getKeyCodeInterpretations({ key: 'Å', code: 'KeyA', altKey: true }, 'code');
470
+ chai.expect(capsOption.map(candidate => candidate.character)).to.deep.equal(['a', 'å']);
471
+ chai.expect(capsOption.map(candidate => candidate.dispatchString())).to.deep.equal(['alt+a', 'a@altgraph']);
472
+
473
+ const capsShiftOption = service.getKeyCodeInterpretations({ key: 'å', code: 'KeyA', altKey: true, shiftKey: true }, 'code');
474
+ chai.expect(capsShiftOption[0].character).to.equal('A');
475
+ chai.expect(capsShiftOption[0].dispatchString()).to.equal('shift+alt+a');
476
+ });
477
+
478
+ it('normalizes the character to the Linux AltGraph layer when Caps Lock inverts the case', async () => {
479
+ const layout: NativeKeyboardLayout = {
480
+ info: { id: 'altgraph-letters', lang: 'pl' },
481
+ mapping: { KeyL: { value: 'l', withShift: 'L', withAltGr: 'ł', withShiftAltGr: 'Ł' } }
482
+ };
483
+ const service = await setup(layout, 'linux');
484
+ const capsAltGraph = service.getKeyCodeInterpretations({ key: 'Ł', code: 'KeyL', altGraph: true }, 'code');
485
+ chai.expect(capsAltGraph).to.have.length(1);
486
+ chai.expect(capsAltGraph[0].character).to.equal('ł');
487
+ chai.expect(capsAltGraph[0].dispatchString()).to.equal('l@altgraph');
488
+
489
+ const capsShiftAltGraph = service.getKeyCodeInterpretations({ key: 'ł', code: 'KeyL', shiftKey: true, altGraph: true }, 'code');
490
+ chai.expect(capsShiftAltGraph).to.have.length(1);
491
+ chai.expect(capsShiftAltGraph[0].character).to.equal('Ł');
492
+ });
493
+
494
+ it('does not add an AltGraph layer to Linux Alt+Shift input', async () => {
495
+ const service = await setup(require('../../../src/common/keyboard/layouts/en-US-pc.json'), 'linux');
496
+ chai.expect(service.getKeyCodeInterpretations({ key: 'P', code: 'KeyP', shiftKey: true, altKey: true }, 'code')
497
+ .map(candidate => candidate.dispatchString())).to.deep.equal(['shift+alt+p', 'alt+p@shift']);
498
+ });
499
+
500
+ it('requires AltGraph for a Linux Shift+AltGraph layer', async () => {
501
+ const layout: NativeKeyboardLayout = {
502
+ info: { id: 'same-shift-layers', lang: 'en' },
503
+ mapping: { KeyA: { value: 'a', withShift: 'A', withAltGr: 'ä', withShiftAltGr: 'A' } }
504
+ };
505
+ const service = await setup(layout, 'linux');
506
+ chai.expect(service.detectLayoutModifiers({ key: 'A', code: 'KeyA', shiftKey: true })).to.equal('shift');
507
+ chai.expect(service.detectLayoutModifiers({ key: 'A', code: 'KeyA', shiftKey: true, altGraph: true })).to.equal('shiftAltGraph');
508
+ });
509
+
510
+ it('uses Option as macOS AltGraph layer evidence', async () => {
511
+ const layout: NativeKeyboardLayout = {
512
+ info: { id: 'altgraph-evidence', lang: 'en' },
513
+ mapping: { KeyA: { value: 'a', withShift: 'A', withAltGr: 'ä', withShiftAltGr: 'Ä' } }
514
+ };
515
+ const service = await setup(layout, 'mac');
516
+ chai.expect(service.detectLayoutModifiers({ key: 'ä', code: 'KeyA', altKey: true })).to.equal('altGraph');
517
+ chai.expect(service.detectLayoutModifiers({ key: 'ä', code: 'KeyA' })).to.be.undefined;
518
+ });
519
+
520
+ it('uses emulated Windows AltGraph as platform layer evidence', async () => {
521
+ const layout: NativeKeyboardLayout = {
522
+ info: { id: 'altgraph-evidence', lang: 'en' },
523
+ mapping: { KeyA: { value: 'a', withShift: 'A', withAltGr: 'ä', withShiftAltGr: 'Ä' } }
524
+ };
525
+ const service = await setup(layout, 'win');
526
+ chai.expect(service.detectLayoutModifiers({ key: 'ä', code: 'KeyA', ctrlKey: true, altKey: true })).to.equal('altGraph');
527
+ });
528
+
529
+ it('constructs Linux layout modifier interpretations only for participating AltGraph', async () => {
530
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
531
+ const service = await setup(german, 'linux');
532
+
533
+ const participating = service.getKeyCodeInterpretations({ key: '[', code: 'Digit8', altGraph: true }, 'code');
534
+ chai.expect(participating).to.have.length(1);
535
+ chai.expect(participating[0].layoutModifiers).to.deep.equal('altGraph');
536
+ chai.expect(participating[0].dispatchString()).to.equal('8@altgraph');
537
+
538
+ const nonParticipating = service.getKeyCodeInterpretations({ key: 'o', code: 'KeyO', altGraph: true }, 'code');
539
+ chai.expect(nonParticipating[0].layoutModifiers).to.equal('none');
540
+
541
+ const withoutModifierEvidence = service.getKeyCodeInterpretations({ key: '[', code: 'Digit8' }, 'code');
542
+ chai.expect(withoutModifierEvidence[0].layoutModifiers).to.equal('none');
543
+
544
+ const withCommandAlt = service.getKeyCodeInterpretations({ key: '[', code: 'Digit8', altKey: true, altGraph: true }, 'code');
545
+ chai.expect(withCommandAlt[0].alt).to.be.true;
546
+ chai.expect(withCommandAlt[0].layoutModifiers).to.equal('altGraph');
547
+ });
548
+
549
+ it('keeps shifted printable bindings symmetric in keyCode dispatch mode', async () => {
550
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
551
+ const service = await setup(german, 'linux');
552
+
553
+ const commandPalette = KeyCode.parse('ctrl+shift+p');
554
+ const shiftedPrintable = KeyCode.parse('shift+/');
555
+ chai.expect(service.getKeyCodeInterpretations({ key: 'P', code: 'KeyP', keyCode: Key.KEY_P.keyCode, ctrlKey: true, shiftKey: true }, 'keyCode')[0]
556
+ .dispatchString()).to.equal(commandPalette.dispatchString());
557
+ chai.expect(service.getKeyCodeInterpretations({ key: '/', code: 'Digit7', keyCode: Key.SLASH.keyCode, shiftKey: true }, 'keyCode')[0]
558
+ .dispatchString()).to.equal(shiftedPrintable.dispatchString());
559
+ });
560
+
561
+ it('constructs command-first and layout modifier Windows interpretations', async () => {
562
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
563
+ const service = await setup(german, 'win');
564
+
565
+ const interpretations = service.getKeyCodeInterpretations({
566
+ key: '[', code: 'Digit8', ctrlKey: true, altKey: true, altGraph: false
567
+ }, 'code');
568
+ chai.expect(interpretations.map(code => code.interpretation)).to.deep.equal(['commandModifiers', 'layoutModifiers']);
569
+ chai.expect(interpretations[0].dispatchString()).to.equal('alt+ctrl+8');
570
+ chai.expect(interpretations[1].dispatchString()).to.equal('8@altgraph');
571
+
572
+ const intentional = service.getKeyCodeInterpretations({
573
+ key: 'f', code: 'KeyF', ctrlKey: true, altKey: true, altGraph: false
574
+ }, 'code');
575
+ chai.expect(intentional).to.have.length(1);
576
+ chai.expect(intentional[0].dispatchString()).to.equal('alt+ctrl+f');
577
+
578
+ const shiftedBinding = service.resolveKeyCode(KeyCode.parse('ctrl+shift+p'));
579
+ const shiftedEvent = service.getKeyCodeInterpretations({ key: 'P', code: 'KeyP', ctrlKey: true, shiftKey: true }, 'code')
580
+ .find(candidate => candidate.interpretation === 'layoutModifiers')!;
581
+ chai.expect(shiftedBinding.supportedByLayout).to.be.true;
582
+ chai.expect(shiftedEvent.dispatchString()).to.equal(shiftedBinding.dispatchString());
583
+ });
584
+
585
+ it('preserves authored command modifiers alongside explicit Windows AltGraph', async () => {
586
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
587
+ const service = await setup(german, 'win');
588
+ const cases = [
589
+ { ctrlKey: false, altKey: false, metaKey: false, expected: '8@altgraph' },
590
+ { ctrlKey: true, altKey: false, metaKey: true, expected: 'meta+ctrl+8@altgraph' },
591
+ { ctrlKey: false, altKey: true, metaKey: true, expected: 'meta+alt+8@altgraph' }
592
+ ];
593
+
594
+ for (const { expected, ...modifiers } of cases) {
595
+ const input = { key: '[', code: 'Digit8', altGraph: true, ...modifiers };
596
+ const keyCode = new KeyCode({
597
+ key: Key.DIGIT8, character: '[', ctrl: input.ctrlKey, alt: input.altKey, meta: input.metaKey
598
+ });
599
+ const interpretations = service.getWindowsKeyCodeInterpretations(keyCode, input, 'altGraph');
600
+ chai.expect(interpretations).to.have.length(1);
601
+ chai.expect(interpretations[0].interpretation).to.equal('layoutModifiers');
602
+ chai.expect(interpretations[0].dispatchString()).to.equal(expected);
603
+ }
604
+ });
605
+
606
+ it('keeps legacy Windows AltGraph reporting typing-safe and preserves Meta', async () => {
607
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
608
+ const service = await setup(german, 'win');
609
+
610
+ for (const metaKey of [false, true]) {
611
+ const input = { key: '[', code: 'Digit8', ctrlKey: true, altKey: true, altGraph: true, metaKey };
612
+ const keyCode = new KeyCode({ key: Key.DIGIT8, character: '[', ctrl: true, alt: true, meta: metaKey });
613
+ const interpretations = service.getWindowsKeyCodeInterpretations(keyCode, input, 'altGraph');
614
+ const prefix = metaKey ? 'meta+' : '';
615
+
616
+ chai.expect(interpretations.map(code => code.interpretation)).to.deep.equal(['commandModifiers', 'layoutModifiers']);
617
+ chai.expect(interpretations[0].dispatchString()).to.equal(`${prefix}alt+ctrl+8`);
618
+ chai.expect(interpretations[1].dispatchString()).to.equal(`${prefix}8@altgraph`);
619
+ }
620
+ });
621
+
622
+ it('uses confirmed withAltGr output as Shift+AltGraph fallback when the shifted layer is absent', async () => {
623
+ const layout: NativeKeyboardLayout = {
624
+ info: { id: 'shift-altgraph-fallback', lang: 'en' },
625
+ mapping: {
626
+ Digit8: { value: '8', withShift: '*', withAltGr: '[', withShiftAltGr: '' }
627
+ }
628
+ };
629
+ const service = await setup(layout, 'linux');
630
+
631
+ chai.expect(service.detectLayoutModifiers({ key: '[', code: 'Digit8', shiftKey: true, altGraph: true }))
632
+ .to.deep.equal('shiftAltGraph');
633
+ chai.expect(service.detectLayoutModifiers({ key: '*', code: 'Digit8', shiftKey: true, altGraph: true }))
634
+ .to.equal('shift');
635
+ });
636
+
637
+ it('passes Linux AltGraph evidence to the key validator', async () => {
638
+ let validated: KeyValidationInput | undefined;
639
+ const validator: KeyValidator = { validateKey: validation => validated = validation };
640
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
641
+ const service = await setup(german, 'linux', undefined, validator);
642
+ const input = { key: '[', code: 'Digit8', ctrlKey: true, altGraph: true, shiftKey: false };
643
+
644
+ service.validateKeyCode(service.getKeyCodeInterpretations(input, 'code')[0], input);
645
+
646
+ chai.expect(validated?.altGraph).to.be.true;
647
+ });
648
+
649
+ it('passes macOS Option evidence to the key validator', async () => {
650
+ let validated: KeyValidationInput | undefined;
651
+ const validator: KeyValidator = { validateKey: validation => validated = validation };
652
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
653
+ const service = await setup(german, 'mac', undefined, validator);
654
+ const input = { key: '[', code: 'Digit8', altKey: true, shiftKey: false };
655
+
656
+ service.validateKeyCode(service.getKeyCodeInterpretations(input, 'code')[0], input);
657
+
658
+ chai.expect(validated?.altGraph).to.be.true;
659
+ });
660
+
661
+ it('passes emulated Windows AltGraph evidence to the key validator', async () => {
662
+ let validated: KeyValidationInput | undefined;
663
+ const validator: KeyValidator = { validateKey: validation => validated = validation };
664
+ const german = require('../../../src/common/keyboard/layouts/de-German-pc.json');
665
+ const service = await setup(german, 'win', undefined, validator);
666
+ const input = { key: '[', code: 'Digit8', ctrlKey: true, altKey: true, shiftKey: false };
667
+
668
+ service.validateKeyCode(service.getKeyCodeInterpretations(input, 'code')[0], input);
669
+
670
+ chai.expect(validated?.altGraph).to.be.true;
671
+ });
672
+
673
+ it('does not treat excluded or non-printable keys as layout modifier', async () => {
674
+ const layout: NativeKeyboardLayout = {
675
+ info: { id: 'native-shaped', lang: 'en' },
676
+ mapping: {
677
+ Space: { value: ' ', withShift: ' ', withAltGr: ' ', withShiftAltGr: ' ' },
678
+ Numpad1: { value: '1', withShift: '1', withAltGr: '¹', withShiftAltGr: '¹' }
679
+ }
680
+ };
681
+ const service = await setup(layout, 'linux');
682
+
683
+ chai.expect(service.detectLayoutModifiers({ key: ' ', code: 'Space', shiftKey: true })).to.be.undefined;
684
+ chai.expect(service.detectLayoutModifiers({ key: '¹', code: 'Numpad1', altGraph: true })).to.be.undefined;
685
+ });
686
+
687
+ it('ingests all Windows layout modifier layers across representative PC layouts', async () => {
688
+ const fixtures = [
689
+ { layout: require('../../../src/common/keyboard/layouts/de-German-pc.json'), assertions: [['²', Key.DIGIT2, false, true], ['ẞ', Key.MINUS, true, true]] },
690
+ { layout: require('../../../src/common/keyboard/layouts/fr-French-pc.json'), assertions: [['€', Key.KEY_E, false, true]] },
691
+ { layout: require('../../../src/common/keyboard/layouts/de-Swiss_German-pc.json'), assertions: [['@', Key.DIGIT2, false, true]] },
692
+ { layout: require('../../../src/common/keyboard/layouts/fr-Bepo-pc.json'), assertions: [['Æ', Key.KEY_A, true, true]] }
693
+ ] as const;
694
+
695
+ for (const fixture of fixtures) {
696
+ const service = await setup(fixture.layout, 'win');
697
+ for (const [character, key, shift, altGraph] of fixture.assertions) {
698
+ const resolved = service.resolveKeyCode(KeyCode.parse(character));
699
+ chai.expect(resolved.key).to.equal(key);
700
+ chai.expect(resolved.character).to.equal(character);
701
+ const expected = shift ? 'shiftAltGraph' : altGraph ? 'altGraph' : 'none';
702
+ chai.expect(resolved.layoutModifiers).to.equal(expected);
703
+ }
704
+ stubOSX.restore();
705
+ stubWindows.restore();
706
+ }
707
+ stubOSX = sinon.stub(os, 'isOSX').value(false);
708
+ stubWindows = sinon.stub(os, 'isWindows').value(false);
709
+ });
710
+
711
+ it('handles missing Windows layer values defensively', async () => {
712
+ const layout: NativeKeyboardLayout = {
713
+ info: { id: 'missing-windows-layers', lang: 'en' },
714
+ mapping: { KeyA: { vkey: 'VK_A', value: 'a' } as never }
715
+ };
716
+ const service = await setup(layout, 'win');
717
+
718
+ chai.expect(service.resolveKeyCode(KeyCode.parse('a')).supportedByLayout).to.be.true;
719
+ chai.expect(service.resolveKeyCode(KeyCode.parse('§')).supportedByLayout).to.be.false;
720
+ });
721
+
722
+ it('resolves logical plus on US, German, and macOS layouts', async () => {
723
+ const fixtures = [
724
+ { layout: require('../../../src/common/keyboard/layouts/en-US-pc.json'), system: 'win' as const, key: Key.EQUAL, shift: true },
725
+ { layout: require('../../../src/common/keyboard/layouts/de-German-pc.json'), system: 'win' as const, key: Key.BRACKET_RIGHT, shift: false },
726
+ { layout: require('../../../src/common/keyboard/layouts/en-US-mac.json'), system: 'mac' as const, key: Key.EQUAL, shift: true }
727
+ ];
728
+ for (const fixture of fixtures) {
729
+ const service = await setup(fixture.layout, fixture.system);
730
+ const resolved = service.resolveKeyCode(KeyCode.parse('[char:0x2B]'));
731
+ chai.expect(resolved.key).to.equal(fixture.key);
732
+ chai.expect(resolved.layoutModifiers === 'shift' || resolved.layoutModifiers === 'shiftAltGraph').to.equal(fixture.shift);
733
+ stubOSX.restore();
734
+ stubWindows.restore();
735
+ }
736
+ stubOSX = sinon.stub(os, 'isOSX').value(false);
737
+ stubWindows = sinon.stub(os, 'isWindows').value(false);
738
+ });
739
+
123
740
  it('resolves correct key bindings with German Windows layout', async () => {
124
741
  const winGerman = require('../../../src/common/keyboard/layouts/de-German-pc.json');
125
742
  const service = await setup(winGerman, 'win');
126
743
 
127
744
  const toggleComment = service.resolveKeyCode(KeyCode.createKeyCode('Slash+M1'));
128
- chai.expect(toggleComment.toString()).to.equal('ctrl+\\');
129
- chai.expect(service.getKeyboardCharacter(toggleComment.key!)).to.equal('#');
745
+ chai.expect(toggleComment.toString()).to.equal('ctrl+7');
746
+ chai.expect(toggleComment.character).to.equal('/');
747
+ chai.expect(toggleComment.layoutModifiers).to.deep.equal('shift');
748
+ chai.expect(service.getKeyboardCharacter(toggleComment.key!)).to.equal('7');
130
749
 
131
750
  const indentLine = service.resolveKeyCode(KeyCode.createKeyCode('BracketRight+M1'));
132
- chai.expect(indentLine.toString()).to.equal('ctrl+=');
133
- chai.expect(service.getKeyboardCharacter(indentLine.key!)).to.equal('´');
751
+ chai.expect(indentLine.toString()).to.equal('ctrl+9');
752
+ chai.expect(indentLine.character).to.equal(']');
753
+ chai.expect(indentLine.layoutModifiers).to.deep.equal('altGraph');
754
+ chai.expect(service.getKeyboardCharacter(indentLine.key!)).to.equal('9');
134
755
  });
135
756
 
136
757
  it('resolves correct key bindings with French Windows layout', async () => {
@@ -139,11 +760,15 @@ describe('keyboard layout service', function (): void {
139
760
 
140
761
  const toggleComment = service.resolveKeyCode(KeyCode.createKeyCode('Slash+M1'));
141
762
  chai.expect(toggleComment.toString()).to.equal('ctrl+.');
763
+ chai.expect(toggleComment.character).to.equal('/');
764
+ chai.expect(toggleComment.layoutModifiers).to.deep.equal('shift');
142
765
  chai.expect(service.getKeyboardCharacter(toggleComment.key!)).to.equal(':');
143
766
 
144
767
  const indentLine = service.resolveKeyCode(KeyCode.createKeyCode('BracketRight+M1'));
145
- chai.expect(indentLine.toString()).to.equal('ctrl+[');
146
- chai.expect(service.getKeyboardCharacter(indentLine.key!)).to.equal('^');
768
+ chai.expect(indentLine.toString()).to.equal('ctrl+-');
769
+ chai.expect(indentLine.character).to.equal(']');
770
+ chai.expect(indentLine.layoutModifiers).to.deep.equal('altGraph');
771
+ chai.expect(service.getKeyboardCharacter(indentLine.key!)).to.equal(')');
147
772
  });
148
773
 
149
774
  });