mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-13 04:34:21 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f582435f6b |
No files matched your search
@@ -142,6 +142,27 @@ describe("ownTerminalInput", () => {
|
||||
expect(reached).toEqual(["keydown", "insertText"]);
|
||||
});
|
||||
|
||||
it("sends a shifted character an IME commits before its keydown", () => {
|
||||
// WeChat / Pinyin on macOS, Shift+/ for `?`: insertText arrives while only Shift is down.
|
||||
// Its keydown armed xterm's gate, which then drops the first press.
|
||||
const { textarea, sent, reached } = setup();
|
||||
keydown(textarea, { key: "Shift", keyCode: 16 });
|
||||
input(textarea, { inputType: "insertText", data: "?" });
|
||||
keydown(textarea, { key: "?", keyCode: 229 });
|
||||
keyup(textarea, 191);
|
||||
expect(sent).toEqual(["?"]);
|
||||
expect(reached).toEqual(["keydown"]);
|
||||
});
|
||||
|
||||
it("leaves shifted keys xterm sent itself to xterm", () => {
|
||||
const { textarea, sent, reached } = setup();
|
||||
keydown(textarea, { key: "Shift", keyCode: 16 });
|
||||
keydown(textarea, { key: "A", keyCode: 65 });
|
||||
input(textarea, { inputType: "insertText", data: "A" });
|
||||
expect(sent).toEqual([]);
|
||||
expect(reached).toEqual(["keydown", "keydown", "insertText"]);
|
||||
});
|
||||
|
||||
it("leaves input with no keydown behind it to xterm", () => {
|
||||
// Dictation, an on-screen keyboard or the emoji picker.
|
||||
const { textarea, sent, reached } = setup();
|
||||
@@ -259,6 +280,25 @@ describe("ownTerminalInput with xterm", () => {
|
||||
return sent;
|
||||
}
|
||||
|
||||
it("sends `?` on the first and the second press of Shift+/ with a direct-commit IME", async () => {
|
||||
const sent = await replay([
|
||||
["keydown", "Shift", 16],
|
||||
["input", "insertText", "?", "?"],
|
||||
["keydown", "?", 229],
|
||||
["keyup", "?", 191],
|
||||
["input", "insertText", "?", "??"],
|
||||
["keydown", "?", 229],
|
||||
["keyup", "?", 191],
|
||||
["keyup", "Shift", 16],
|
||||
["keydown", "Shift", 16],
|
||||
["keydown", "A", 65],
|
||||
["input", "insertText", "A", "??A"],
|
||||
["keyup", "A", 65],
|
||||
["keyup", "Shift", 16],
|
||||
]);
|
||||
expect(sent).toEqual(["?", "?", "A"]);
|
||||
});
|
||||
|
||||
it.each([true, false])("sends a retyped word and following plain keys once (keyup first: %s)", async (release) => {
|
||||
// Uppercase typing reaches both keypress and input; lowercase keydown is cancelled by xterm.
|
||||
const sent = await replay([
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
* - a keyCode-229 keydown never reaches xterm, so its deferred textarea diff cannot run;
|
||||
* - while such a keydown is pending, an `insertText` is sent from the event's own `data`
|
||||
* (not from a value diff), then the textarea is cleared;
|
||||
* - composition input that arrives without a `compositionstart` is sent the same way.
|
||||
* - composition input that arrives without a `compositionstart` is sent the same way;
|
||||
* - so is an `insertText` while only a modifier is down: an IME that commits a shifted
|
||||
* character directly (`?` from Shift+/ with WeChat or Pinyin) delivers it before the
|
||||
* character's keydown, and the Shift keydown already armed xterm's gate, so the first press
|
||||
* was dropped.
|
||||
*
|
||||
* Everything else stays xterm's: real compositions (`compositionstart` … `compositionend`),
|
||||
* input events for keys xterm already sent, and input with no IME keydown behind it.
|
||||
@@ -28,6 +32,8 @@
|
||||
*/
|
||||
import { isImeKey } from "@/platform/ime";
|
||||
|
||||
const MODIFIERS = new Set(["Shift", "Control", "Alt", "Meta"]);
|
||||
|
||||
/** Commit-shaped input events that xterm's own input path ignores entirely. */
|
||||
const COMPOSITION_COMMITS = new Set(["insertFromComposition", "insertCompositionText"]);
|
||||
|
||||
@@ -62,6 +68,9 @@ export function ownTerminalInput({ host, textarea, send }: TerminalInputOptions)
|
||||
let imeKey = false;
|
||||
/** The pending key belongs to an IME: its commit is ours to deliver. */
|
||||
let imeDown = false;
|
||||
/** Since the last keyup: a modifier went down, and any other key went down. */
|
||||
let modifierDown = false;
|
||||
let keyDown = false;
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (composing) {
|
||||
@@ -69,6 +78,8 @@ export function ownTerminalInput({ host, textarea, send }: TerminalInputOptions)
|
||||
if (e.keyCode === 0) e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (MODIFIERS.has(e.key)) modifierDown = true;
|
||||
else keyDown = true;
|
||||
imeKey = isImeKey(e);
|
||||
imeDown = imeKey;
|
||||
if (imeKey) {
|
||||
@@ -90,6 +101,8 @@ export function ownTerminalInput({ host, textarea, send }: TerminalInputOptions)
|
||||
const onKeyUp = () => {
|
||||
imeKey = false;
|
||||
imeDown = false;
|
||||
modifierDown = false;
|
||||
keyDown = false;
|
||||
};
|
||||
|
||||
const onInput = (e: InputEvent) => {
|
||||
@@ -97,7 +110,8 @@ export function ownTerminalInput({ host, textarea, send }: TerminalInputOptions)
|
||||
if (e.inputType === "insertText") {
|
||||
// Without an IME keydown behind it this is xterm's: it drops the event for a key it
|
||||
// already sent (Option/AltGr, plain typing) and delivers the rest (dictation, emoji).
|
||||
if (!imeDown) return;
|
||||
// With only a modifier down, no key of xterm's is behind it: xterm would drop it too.
|
||||
if (!imeDown && !(modifierDown && !keyDown)) return;
|
||||
} else if (!COMPOSITION_COMMITS.has(e.inputType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user