Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

139 wiersze
4.0 KiB

  1. 'use strict';
  2. import { commands, Disposable } from 'vscode';
  3. import { CommandContext, ExtensionKey, setCommandContext } from './constants';
  4. import { Logger } from './logger';
  5. export declare interface KeyCommand {
  6. onDidPressKey?(key: Keys): Promise<{} | undefined>;
  7. }
  8. const keyNoopCommand = Object.create(null) as KeyCommand;
  9. export { keyNoopCommand as KeyNoopCommand };
  10. export declare type Keys = 'left' | 'right' | ',' | '.' | 'escape';
  11. export const keys: Keys[] = [
  12. 'left',
  13. 'right',
  14. ',',
  15. '.',
  16. 'escape'
  17. ];
  18. export declare interface KeyMapping {
  19. [id: string]: (KeyCommand | (() => Promise<KeyCommand>) | undefined);
  20. }
  21. const mappings: KeyMapping[] = [];
  22. let _instance: Keyboard;
  23. export class KeyboardScope extends Disposable {
  24. constructor(
  25. private readonly mapping: KeyMapping
  26. ) {
  27. super(() => this.dispose());
  28. for (const key in mapping) {
  29. mapping[key] = mapping[key] || keyNoopCommand;
  30. }
  31. }
  32. async dispose() {
  33. const index = mappings.indexOf(this.mapping);
  34. Logger.log('KeyboardScope.dispose', mappings.length, index);
  35. if (index === (mappings.length - 1)) {
  36. mappings.pop();
  37. await this.updateKeyCommandsContext(mappings[mappings.length - 1]);
  38. }
  39. else {
  40. mappings.splice(index, 1);
  41. }
  42. }
  43. async begin() {
  44. mappings.push(this.mapping);
  45. await this.updateKeyCommandsContext(this.mapping);
  46. return this;
  47. }
  48. async clearKeyCommand(key: Keys) {
  49. const mapping = mappings[mappings.length - 1];
  50. if (mapping !== this.mapping || !mapping[key]) return;
  51. Logger.log('KeyboardScope.clearKeyCommand', mappings.length, key);
  52. mapping[key] = undefined;
  53. await setCommandContext(`${CommandContext.Key}:${key}`, false);
  54. }
  55. async setKeyCommand(key: Keys, command: KeyCommand | (() => Promise<KeyCommand>)) {
  56. const mapping = mappings[mappings.length - 1];
  57. if (mapping !== this.mapping) return;
  58. Logger.log('KeyboardScope.setKeyCommand', mappings.length, key, !!mapping[key]);
  59. if (!mapping[key]) {
  60. mapping[key] = command;
  61. await setCommandContext(`${CommandContext.Key}:${key}`, true);
  62. }
  63. else {
  64. mapping[key] = command;
  65. }
  66. }
  67. private async updateKeyCommandsContext(mapping: KeyMapping) {
  68. const promises = [];
  69. for (const key of keys) {
  70. promises.push(setCommandContext(`${CommandContext.Key}:${key}`, !!(mapping && mapping[key])));
  71. }
  72. await Promise.all(promises);
  73. }
  74. }
  75. export class Keyboard extends Disposable {
  76. static get instance(): Keyboard {
  77. return _instance;
  78. }
  79. private _disposable: Disposable;
  80. constructor() {
  81. super(() => this.dispose());
  82. _instance = this;
  83. const subscriptions = keys.map(key => commands.registerCommand(`${ExtensionKey}.key.${key}`, () => this.execute(key), this));
  84. this._disposable = Disposable.from(...subscriptions);
  85. }
  86. dispose() {
  87. this._disposable && this._disposable.dispose();
  88. }
  89. async beginScope(mapping?: KeyMapping): Promise<KeyboardScope> {
  90. Logger.log('Keyboard.beginScope', mappings.length);
  91. return await new KeyboardScope(mapping ? Object.assign(Object.create(null), mapping) : Object.create(null)).begin();
  92. }
  93. async execute(key: Keys): Promise<{} | undefined> {
  94. if (!mappings.length) return undefined;
  95. try {
  96. const mapping = mappings[mappings.length - 1];
  97. let command = mapping[key] as KeyCommand | (() => Promise<KeyCommand>);
  98. if (typeof command === 'function') {
  99. command = await command();
  100. }
  101. if (!command || typeof command.onDidPressKey !== 'function') return undefined;
  102. Logger.log('Keyboard.execute', key);
  103. return await command.onDidPressKey(key);
  104. }
  105. catch (ex) {
  106. Logger.error(ex, 'Keyboard.execute');
  107. return undefined;
  108. }
  109. }
  110. }