Skip to content

Commit 446bcb1

Browse files
committed
Fix option b and f
1 parent 4316263 commit 446bcb1

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

src/browser/services/KeyboardService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class KeyboardService implements IKeyboardService {
4040
}
4141
const kittyFlags = this._coreService.kittyKeyboard.flags;
4242
return this.useKitty
43-
? this._getKittyKeyboard().evaluate(event, kittyFlags, event.repeat ? KittyKeyboardEventType.REPEAT : KittyKeyboardEventType.PRESS)
43+
? this._getKittyKeyboard().evaluate(event, kittyFlags, event.repeat ? KittyKeyboardEventType.REPEAT : KittyKeyboardEventType.PRESS, isMac && this._optionsService.rawOptions.macOptionIsMeta)
4444
: evaluateKeyboardEvent(event, this._coreService.decPrivateModes.applicationCursorKeys, isMac, this._optionsService.rawOptions.macOptionIsMeta);
4545
}
4646

@@ -51,7 +51,7 @@ export class KeyboardService implements IKeyboardService {
5151
}
5252
const kittyFlags = this._coreService.kittyKeyboard.flags;
5353
if (this.useKitty && (kittyFlags & KittyKeyboardFlags.REPORT_EVENT_TYPES)) {
54-
return this._getKittyKeyboard().evaluate(event, kittyFlags, KittyKeyboardEventType.RELEASE);
54+
return this._getKittyKeyboard().evaluate(event, kittyFlags, KittyKeyboardEventType.RELEASE, isMac && this._optionsService.rawOptions.macOptionIsMeta);
5555
}
5656
return undefined;
5757
}

src/common/input/KittyKeyboard.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,5 +729,87 @@ describe('KittyKeyboard', () => {
729729
assert.strictEqual(result.key, '\x1b[57440u');
730730
});
731731
});
732+
733+
// https://github.com/microsoft/vscode/issues/304765
734+
describe('macOS Option as Alt (macOptionIsMeta)', () => {
735+
const flags = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES;
736+
const press = KittyKeyboardEventType.PRESS;
737+
738+
it('Opt+f (key=ƒ) → CSI 102;3 u', () => {
739+
const result = kitty.evaluate(createEvent({ key: 'ƒ', code: 'KeyF', altKey: true }), flags, press, true);
740+
assert.strictEqual(result.key, '\x1b[102;3u');
741+
});
742+
743+
it('Opt+b (key=∫) → CSI 98;3 u', () => {
744+
const result = kitty.evaluate(createEvent({ key: '∫', code: 'KeyB', altKey: true }), flags, press, true);
745+
assert.strictEqual(result.key, '\x1b[98;3u');
746+
});
747+
748+
it('Opt+d (key=∂) → CSI 100;3 u', () => {
749+
const result = kitty.evaluate(createEvent({ key: '∂', code: 'KeyD', altKey: true }), flags, press, true);
750+
assert.strictEqual(result.key, '\x1b[100;3u');
751+
});
752+
753+
it('Opt+n dead key (key=Dead, code=KeyN) → CSI 110;3 u', () => {
754+
const result = kitty.evaluate(createEvent({ key: 'Dead', code: 'KeyN', altKey: true }), flags, press, true);
755+
assert.strictEqual(result.key, '\x1b[110;3u');
756+
});
757+
758+
it('Opt+e dead key (key=Dead, code=KeyE) → CSI 101;3 u', () => {
759+
const result = kitty.evaluate(createEvent({ key: 'Dead', code: 'KeyE', altKey: true }), flags, press, true);
760+
assert.strictEqual(result.key, '\x1b[101;3u');
761+
});
762+
763+
it('Opt+u dead key (key=Dead, code=KeyU) → CSI 117;3 u', () => {
764+
const result = kitty.evaluate(createEvent({ key: 'Dead', code: 'KeyU', altKey: true }), flags, press, true);
765+
assert.strictEqual(result.key, '\x1b[117;3u');
766+
});
767+
768+
it('Opt+5 (key=∞) → CSI 53;3 u', () => {
769+
const result = kitty.evaluate(createEvent({ key: '∞', code: 'Digit5', altKey: true }), flags, press, true);
770+
assert.strictEqual(result.key, '\x1b[53;3u');
771+
});
772+
773+
it('Opt+Shift+f (key=Ï) → CSI 102;4 u', () => {
774+
const result = kitty.evaluate(createEvent({ key: 'Ï', code: 'KeyF', altKey: true, shiftKey: true }), flags, press, true);
775+
assert.strictEqual(result.key, '\x1b[102;4u');
776+
});
777+
778+
it('Ctrl+Opt+f (key=ƒ) → CSI 102;7 u', () => {
779+
const result = kitty.evaluate(createEvent({ key: 'ƒ', code: 'KeyF', altKey: true, ctrlKey: true }), flags, press, true);
780+
assert.strictEqual(result.key, '\x1b[102;7u');
781+
});
782+
783+
it('does not unwind when macOptionAsAlt is false (Linux Alt is a chord)', () => {
784+
const result = kitty.evaluate(createEvent({ key: 'a', code: 'KeyA', altKey: true }), flags, press, false);
785+
assert.strictEqual(result.key, '\x1b[97;3u');
786+
});
787+
788+
it('does not unwind on Linux AZERTY (key=a, code=KeyQ) — uses ev.key not ev.code', () => {
789+
const result = kitty.evaluate(createEvent({ key: 'a', code: 'KeyQ', altKey: true }), flags, press, false);
790+
assert.strictEqual(result.key, '\x1b[97;3u');
791+
});
792+
793+
it('does not unwind when macOptionAsAlt is false even with composed key', () => {
794+
const result = kitty.evaluate(createEvent({ key: 'ƒ', code: 'KeyF', altKey: true }), flags, press, false);
795+
assert.strictEqual(result.key, '\x1b[402;3u');
796+
});
797+
798+
it('does not unwind when altKey is false', () => {
799+
const result = kitty.evaluate(createEvent({ key: 'ƒ', code: 'KeyF' }), flags, press, true);
800+
assert.strictEqual(result.key, 'ƒ');
801+
});
802+
803+
it('falls through when ev.code is not Key*/Digit* (Opt+;)', () => {
804+
const result = kitty.evaluate(createEvent({ key: '…', code: 'Semicolon', altKey: true }), flags, press, true);
805+
assert.strictEqual(result.key, '\x1b[8230;3u');
806+
});
807+
808+
it('Opt+f release with REPORT_EVENT_TYPES → CSI 102;3:3 u', () => {
809+
const releaseFlags = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES | KittyKeyboardFlags.REPORT_EVENT_TYPES;
810+
const result = kitty.evaluate(createEvent({ key: 'ƒ', code: 'KeyF', altKey: true }), releaseFlags, KittyKeyboardEventType.RELEASE, true);
811+
assert.strictEqual(result.key, '\x1b[102;3:3u');
812+
});
813+
});
732814
});
733815
});

src/common/input/KittyKeyboard.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export class KittyKeyboard {
218218
* Returns the lowercase codepoint for letters.
219219
* For shifted keys, uses the code property to get the base key.
220220
*/
221-
private _getKeyCode(ev: IKeyboardEvent): number | undefined {
221+
private _getKeyCode(ev: IKeyboardEvent, macOptionAsAlt: boolean): number | undefined {
222222
const numpadCode = this._getNumpadKeyCode(ev);
223223
if (numpadCode !== undefined) {
224224
return numpadCode;
@@ -234,7 +234,7 @@ export class KittyKeyboard {
234234
return funcCode;
235235
}
236236

237-
if (ev.shiftKey && ev.code) {
237+
if ((ev.shiftKey || (macOptionAsAlt && ev.altKey)) && ev.code) {
238238
if (ev.code.startsWith('Digit') && ev.code.length === 6) {
239239
const digit = ev.code.charAt(5);
240240
if (digit >= '0' && digit <= '9') {
@@ -410,12 +410,16 @@ export class KittyKeyboard {
410410
* @param ev The keyboard event.
411411
* @param flags The active Kitty keyboard enhancement flags.
412412
* @param eventType The event type (press, repeat, release).
413+
* @param macOptionAsAlt isMac && macOptionIsMeta (kitty: macos_option_as_alt).
413414
* @returns The keyboard result with the encoded key sequence.
414415
*/
415416
public evaluate(
416417
ev: IKeyboardEvent,
417418
flags: number,
418-
eventType: KittyKeyboardEventType = KittyKeyboardEventType.PRESS
419+
eventType: KittyKeyboardEventType = KittyKeyboardEventType.PRESS,
420+
// Analogous to kitty's macos_option_as_alt (kitty/options/definition.py:2624).
421+
// When true, macOS Option-composed ev.key values are unwound via ev.code.
422+
macOptionAsAlt: boolean = false
419423
): IKeyboardResult {
420424
const result: IKeyboardResult = {
421425
type: KeyboardResultType.SEND_KEY,
@@ -464,7 +468,7 @@ export class KittyKeyboard {
464468
return result;
465469
}
466470

467-
const keyCode = this._getKeyCode(ev);
471+
const keyCode = this._getKeyCode(ev, macOptionAsAlt);
468472
if (keyCode === undefined) {
469473
return result;
470474
}

0 commit comments

Comments
 (0)