mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(terminal): keep the text of an IME key pressed before a commit finalizes (#28441)
* fix(terminal): keep the text of an IME key pressed before a commit finalizes A Process (229) keydown that lands after compositionend but before xterm's deferred finalizer runs took the composing early return and never recorded that it owed its own commit. When the finalizer ran first, the key's insertText found no pending transaction and no owed commit, so xterm dropped it: `你` then `,` reached the PTY as `你`. Record the debt for such a keydown when the ended session's commit is already in the textarea, and clear it when the pending transaction settles the text itself. Sessions still waiting for their post-composition echo keep their old path, so the echo cannot be sent twice. Fixes the intermittent terminal-linux-direct-ime-commit e2e failure. * fix(terminal): keep a commit when Escape or a keypress follows compositionend A plain Escape after compositionend cancelled the accepted commit, dropping both the text and the Escape; it now settles the commit and then sends Escape. The Escape that itself ends a session (still composing, reported after compositionend) still cancels. A keypress for the Process key that owes a commit now clears that debt, so a later input cannot send the key's text a second time.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -88,7 +88,7 @@ index 497afcf535f3eaca00889525a77e15eb633ccd96..96d499b34605f860608382114c3fbdc0
|
||||
|
||||
export interface IBrowser {
|
||||
diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts
|
||||
index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee3acfddfa 100644
|
||||
index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..8ce5bad71b26515ecbbb82b76d218d0b16e280a8 100644
|
||||
--- a/src/browser/input/CompositionHelper.ts
|
||||
+++ b/src/browser/input/CompositionHelper.ts
|
||||
@@ -1,10 +1,15 @@
|
||||
@@ -413,7 +413,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +313,19 @@ export class CompositionHelper {
|
||||
@@ -113,10 +313,33 @@ export class CompositionHelper {
|
||||
* @returns Whether the Terminal should continue processing the keydown event.
|
||||
*/
|
||||
public keydown(ev: KeyboardEvent): boolean {
|
||||
@@ -422,7 +422,13 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ this._canceledKey = undefined;
|
||||
+ return false;
|
||||
+ }
|
||||
+ if (ev.key === 'Escape' && (this._isComposing || this.hasPendingCompositionFinalization)) {
|
||||
+ // An ended session's commit is accepted; only the Escape that ended it (macOS reports that
|
||||
+ // keydown after compositionend, still composing) cancels it.
|
||||
+ if (
|
||||
+ ev.key === 'Escape' &&
|
||||
+ (this._isComposing ||
|
||||
+ (this.hasPendingCompositionFinalization && (ev.isComposing || ev.keyCode === 229)))
|
||||
+ ) {
|
||||
+ this._canceledKey = { code: ev.code, timeStamp: ev.timeStamp };
|
||||
+ this._cancelComposition();
|
||||
+ return false;
|
||||
@@ -434,7 +440,15 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
if (ev.keyCode === 20 || ev.keyCode === 229) {
|
||||
// 20 is CapsLock, 229 is Enter
|
||||
// Continue composing if the keyCode is the "composition character"
|
||||
@@ -128,6 +340,10 @@ export class CompositionHelper {
|
||||
+ if (ev.keyCode === 229 && !this._isComposing) {
|
||||
+ // The ended session's commit is already in the textarea unless it still awaits its echo,
|
||||
+ // so this key owes its own commit if the finalizer runs before that text arrives.
|
||||
+ this._imeKeydownAwaitingCommit = !this._pendingComposition?.expectsPostCompositionInput;
|
||||
+ }
|
||||
return false;
|
||||
}
|
||||
if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
|
||||
@@ -128,6 +351,10 @@ export class CompositionHelper {
|
||||
this._finalizeComposition(false);
|
||||
}
|
||||
|
||||
@@ -445,7 +459,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
if (ev.keyCode === 229) {
|
||||
// If the "composition character" is used but gets to this point it means a non-composition
|
||||
// character (eg. numbers and punctuation) was pressed when the IME was active.
|
||||
@@ -138,6 +354,74 @@ export class CompositionHelper {
|
||||
@@ -138,6 +365,78 @@ export class CompositionHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -454,6 +468,8 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ * after reconciliation with the final textarea candidate.
|
||||
+ */
|
||||
+ public keypress(text: string): boolean {
|
||||
+ // Every path below sends this key's text, so its keydown no longer owes a commit.
|
||||
+ this._imeKeydownAwaitingCommit = false;
|
||||
+ const pending = this._pendingComposition;
|
||||
+ if (!pending) {
|
||||
+ return false;
|
||||
@@ -480,6 +496,8 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ if (!pending) {
|
||||
+ return this._claimImeKeydownCommit(text);
|
||||
+ }
|
||||
+ // The pending transaction settles this text below, so no keydown still owes it.
|
||||
+ this._imeKeydownAwaitingCommit = false;
|
||||
+ if (pending.expectsPostCompositionInput) {
|
||||
+ pending.inputData += text;
|
||||
+ pending.expectsPostCompositionInput = false;
|
||||
@@ -520,7 +538,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
/**
|
||||
* Finalizes the composition, resuming regular input actions. This is called when a composition
|
||||
* is ending.
|
||||
@@ -146,23 +430,52 @@ export class CompositionHelper {
|
||||
@@ -146,23 +445,52 @@ export class CompositionHelper {
|
||||
* compositionend event is triggered, such as enter, so that the composition is sent before
|
||||
* the command is executed.
|
||||
*/
|
||||
@@ -584,7 +602,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
|
||||
// Since composition* events happen before the changes take place in the textarea on most
|
||||
// browsers, use a setTimeout with 0ms time to allow the native compositionend event to
|
||||
@@ -172,37 +485,315 @@ export class CompositionHelper {
|
||||
@@ -172,35 +500,313 @@ export class CompositionHelper {
|
||||
// - The last compositionupdate event's data property does not always accurately describe
|
||||
// the character, a counter example being Korean where an ending consonsant can move to
|
||||
// the following character if the following input is a vowel.
|
||||
@@ -621,7 +639,8 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ }
|
||||
+ if (this._pendingComposition === pending) {
|
||||
+ this._sendPendingComposition(pending, true);
|
||||
+ }
|
||||
}
|
||||
- }, 0);
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
@@ -654,7 +673,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ private _cancelPendingFinalizer(pending: IPendingComposition): void {
|
||||
+ if (pending.finalizerTimer === undefined) {
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
+ clearTimeout(pending.finalizerTimer);
|
||||
+ this._compositionTimers.delete(pending.finalizerTimer);
|
||||
+ pending.finalizerTimer = undefined;
|
||||
@@ -813,8 +832,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ id: pending.transactionId,
|
||||
+ data: input,
|
||||
+ dataPendingReconciliation: true
|
||||
}
|
||||
- }, 0);
|
||||
+ }
|
||||
+ }
|
||||
+ ));
|
||||
+ }
|
||||
@@ -822,9 +840,9 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ private _dispatchCompositionSessionEvent(event: CustomEvent): void {
|
||||
+ if (typeof this._textarea.dispatchEvent === 'function') {
|
||||
+ this._textarea.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private _dispatchCompositionTransactionSettled(): void {
|
||||
+ this._dispatchCompositionSessionEvent(new CustomEvent(
|
||||
+ 'xterm-composition-transaction-settled',
|
||||
@@ -922,12 +940,10 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
+ }
|
||||
+ clearTimeout(timer);
|
||||
+ this._compositionTimers.delete(timer);
|
||||
+ }
|
||||
+
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any changes made to the textarea after the current event chain is allowed to complete.
|
||||
* This should be called when not currently composing but a keydown event with the "composition
|
||||
@@ -222,6 +813,9 @@ export class CompositionHelper {
|
||||
@@ -222,6 +828,9 @@ export class CompositionHelper {
|
||||
|
||||
const diff = newValue.replace(oldValue, '');
|
||||
|
||||
@@ -937,7 +953,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
this._dataAlreadySent = diff;
|
||||
|
||||
if (newValue.length > oldValue.length) {
|
||||
@@ -236,6 +830,105 @@ export class CompositionHelper {
|
||||
@@ -236,6 +845,105 @@ export class CompositionHelper {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -1043,7 +1059,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
/**
|
||||
* Positions the composition view on top of the cursor and the textarea just below it (so the
|
||||
* IME helper dialog is positioned correctly).
|
||||
@@ -243,10 +936,23 @@ export class CompositionHelper {
|
||||
@@ -243,10 +951,23 @@ export class CompositionHelper {
|
||||
* necessary as the IME events across browsers are not consistently triggered.
|
||||
*/
|
||||
public updateCompositionElements(dontRecurse?: boolean): void {
|
||||
@@ -1068,7 +1084,7 @@ index c9ec396ab66cb966d49aa63bed09cdf9cd6c4246..4d62369d0005f465318ec93e221fd3ee
|
||||
if (this._bufferService.buffer.isCursorInViewport) {
|
||||
const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);
|
||||
|
||||
@@ -254,31 +960,156 @@ export class CompositionHelper {
|
||||
@@ -254,31 +975,156 @@ export class CompositionHelper {
|
||||
const cursorTop = this._bufferService.buffer.y * this._renderService.dimensions.css.cell.height;
|
||||
const cursorLeft = cursorX * this._renderService.dimensions.css.cell.width;
|
||||
|
||||
|
||||
Generated
+27
-27
@@ -172,7 +172,7 @@ patchedDependencies:
|
||||
'@xterm/addon-search@0.17.0-beta.301': eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0
|
||||
'@xterm/addon-serialize@0.15.0-beta.301': b35533fe252e7e45433150170348889f4e08a6c17f7017ac34ea694d831fec7f
|
||||
'@xterm/addon-webgl@0.20.0-beta.300': 5349718f2c19d9319a81552580bf9618ded9b00f3707a5c6cdbbae1910849cd9
|
||||
'@xterm/xterm@6.1.0-beta.304': 273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb
|
||||
'@xterm/xterm@6.1.0-beta.304': 9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c
|
||||
i18next-cli@1.74.2: 7955b89d3aa229f477408608d331f78c85148a85a85913729f89fac65ad8b207
|
||||
lint-staged@16.4.0: 7333b3837f80a7fbd045964db6d76ba4fc118e49134bdbabb00585b6b7b60673
|
||||
monaco-editor@0.55.1: 53b71f7d8a7c00b76ffdb73ce83d18e31692f193f5fafa6a390e48f5b3fdd636
|
||||
@@ -204,7 +204,7 @@ importers:
|
||||
version: 2.5.6
|
||||
'@xterm/addon-serialize':
|
||||
specifier: 0.15.0-beta.301
|
||||
version: 0.15.0-beta.301(patch_hash=b35533fe252e7e45433150170348889f4e08a6c17f7017ac34ea694d831fec7f)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.15.0-beta.301(patch_hash=b35533fe252e7e45433150170348889f4e08a6c17f7017ac34ea694d831fec7f)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/headless':
|
||||
specifier: 6.1.0-beta.303
|
||||
version: 6.1.0-beta.303
|
||||
@@ -409,28 +409,28 @@ importers:
|
||||
version: 1.18.0
|
||||
'@xterm/addon-fit':
|
||||
specifier: 0.12.0-beta.301
|
||||
version: 0.12.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.12.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/addon-image':
|
||||
specifier: 0.10.0-beta.301
|
||||
version: 0.10.0-beta.301(patch_hash=cb569e3005b47547189ba473280c3ae7cf4040161da538cd998ae6e69ad403f5)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.10.0-beta.301(patch_hash=cb569e3005b47547189ba473280c3ae7cf4040161da538cd998ae6e69ad403f5)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/addon-ligatures':
|
||||
specifier: 0.11.0-beta.301
|
||||
version: 0.11.0-beta.301(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.11.0-beta.301(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/addon-search':
|
||||
specifier: 0.17.0-beta.301
|
||||
version: 0.17.0-beta.301(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.17.0-beta.301(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/addon-unicode11':
|
||||
specifier: 0.10.0-beta.301
|
||||
version: 0.10.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.10.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/addon-web-links':
|
||||
specifier: 0.13.0-beta.301
|
||||
version: 0.13.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.13.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/addon-webgl':
|
||||
specifier: 0.20.0-beta.300
|
||||
version: 0.20.0-beta.300(patch_hash=5349718f2c19d9319a81552580bf9618ded9b00f3707a5c6cdbbae1910849cd9)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))
|
||||
version: 0.20.0-beta.300(patch_hash=5349718f2c19d9319a81552580bf9618ded9b00f3707a5c6cdbbae1910849cd9)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))
|
||||
'@xterm/xterm':
|
||||
specifier: 6.1.0-beta.304
|
||||
version: 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
version: 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
@@ -11234,43 +11234,43 @@ snapshots:
|
||||
|
||||
'@xmldom/xmldom@0.8.15': {}
|
||||
|
||||
'@xterm/addon-fit@0.12.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-fit@0.12.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
|
||||
'@xterm/addon-image@0.10.0-beta.301(patch_hash=cb569e3005b47547189ba473280c3ae7cf4040161da538cd998ae6e69ad403f5)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-image@0.10.0-beta.301(patch_hash=cb569e3005b47547189ba473280c3ae7cf4040161da538cd998ae6e69ad403f5)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
|
||||
'@xterm/addon-ligatures@0.11.0-beta.301(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-ligatures@0.11.0-beta.301(patch_hash=47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
lru-cache: 11.5.1
|
||||
opentype.js: 2.0.0
|
||||
|
||||
'@xterm/addon-search@0.17.0-beta.301(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-search@0.17.0-beta.301(patch_hash=eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
|
||||
'@xterm/addon-serialize@0.15.0-beta.301(patch_hash=b35533fe252e7e45433150170348889f4e08a6c17f7017ac34ea694d831fec7f)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-serialize@0.15.0-beta.301(patch_hash=b35533fe252e7e45433150170348889f4e08a6c17f7017ac34ea694d831fec7f)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
|
||||
'@xterm/addon-unicode11@0.10.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-unicode11@0.10.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
|
||||
'@xterm/addon-web-links@0.13.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-web-links@0.13.0-beta.301(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
|
||||
'@xterm/addon-webgl@0.20.0-beta.300(patch_hash=5349718f2c19d9319a81552580bf9618ded9b00f3707a5c6cdbbae1910849cd9)(@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb))':
|
||||
'@xterm/addon-webgl@0.20.0-beta.300(patch_hash=5349718f2c19d9319a81552580bf9618ded9b00f3707a5c6cdbbae1910849cd9)(@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c))':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)
|
||||
'@xterm/xterm': 6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)
|
||||
|
||||
'@xterm/headless@6.1.0-beta.303': {}
|
||||
|
||||
'@xterm/xterm@6.1.0-beta.304(patch_hash=273b687a9c4b571f2f6f4eb4a2da1f651300065500bc30b4df2e911827e9c7eb)': {}
|
||||
'@xterm/xterm@6.1.0-beta.304(patch_hash=9e1c200b2332745ef9e01e64696e9cda6f629937c37a0ee652323f25ccc8df5c)': {}
|
||||
|
||||
abbrev@4.0.0: {}
|
||||
|
||||
|
||||
+16
@@ -730,6 +730,22 @@ describe('xterm IME composition de-duplication', () => {
|
||||
terminal.dispose()
|
||||
})
|
||||
|
||||
// vim: commit a word, then leave insert mode at once; the accepted commit must precede the ESC.
|
||||
it('sends an accepted commit before a plain Escape that lands before finalization', async () => {
|
||||
const { emitted, terminal, textarea } = openTerminal()
|
||||
startComposition(textarea, '日本語')
|
||||
await nextEventLoop()
|
||||
|
||||
textarea.dispatchEvent(
|
||||
new CompositionEvent('compositionend', { data: '日本語', bubbles: true })
|
||||
)
|
||||
dispatchKeydown(textarea, 'Escape', 'Escape', 27)
|
||||
await nextEventLoop()
|
||||
|
||||
expect(emitted).toEqual(['日本語', '\x1b'])
|
||||
terminal.dispose()
|
||||
})
|
||||
|
||||
it('cancels a pending completion when Escape follows compositionend', async () => {
|
||||
const { emitted, terminal, textarea } = openTerminal()
|
||||
startComposition(textarea, '한')
|
||||
|
||||
+66
@@ -145,6 +145,72 @@ describe('a candidate commit that arrives with no composition session', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Chromium writes the commit into the textarea before compositionend, so a Process keydown that
|
||||
// lands before xterm's deferred finalizer owes its own text, not an echo of the commit.
|
||||
for (const finalizerFirst of [true, false]) {
|
||||
it(`sends punctuation keyed before the commit finalizes, finalizerFirst=${finalizerFirst}`, async () => {
|
||||
const rig = openTerminal()
|
||||
try {
|
||||
dispatchComposition(rig, 'compositionstart', '')
|
||||
dispatchComposition(rig, 'compositionupdate', 'ni')
|
||||
dispatchComposition(rig, 'compositionend', '你')
|
||||
dispatchKey(rig, 'keydown', { key: 'Process', code: 'Comma', keyCode: IME_KEYCODE })
|
||||
if (finalizerFirst) {
|
||||
await nextEventLoop()
|
||||
}
|
||||
rig.textarea.value = '你,'
|
||||
const input = new InputEvent('input', { bubbles: true })
|
||||
Object.defineProperty(input, 'inputType', { value: 'insertText' })
|
||||
Object.defineProperty(input, 'data', { value: ',' })
|
||||
Object.defineProperty(input, 'composed', { value: true })
|
||||
rig.textarea.dispatchEvent(input)
|
||||
dispatchKey(rig, 'keyup', { key: 'Process', code: 'Comma', keyCode: IME_KEYCODE })
|
||||
await nextEventLoop()
|
||||
await nextEventLoop()
|
||||
expect(rig.emitted.join('')).toBe('你,')
|
||||
} finally {
|
||||
rig.terminal.dispose()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// The keypress sends the key's text on every path, so the input that follows must not claim it.
|
||||
for (const finalizerRuns of ['beforeKeypress', 'beforeInput', 'afterInput'] as const) {
|
||||
it(`sends a keypress-carried key once after a commit, finalizer ${finalizerRuns}`, async () => {
|
||||
const rig = openTerminal()
|
||||
try {
|
||||
dispatchComposition(rig, 'compositionstart', '')
|
||||
dispatchComposition(rig, 'compositionupdate', 'ni')
|
||||
dispatchComposition(rig, 'compositionend', '你')
|
||||
dispatchKey(rig, 'keydown', { key: 'Process', code: 'Comma', keyCode: IME_KEYCODE })
|
||||
if (finalizerRuns === 'beforeKeypress') {
|
||||
await nextEventLoop()
|
||||
}
|
||||
dispatchKey(rig, 'keypress', {
|
||||
key: ',',
|
||||
code: 'Comma',
|
||||
keyCode: 0xff0c,
|
||||
charCode: 0xff0c
|
||||
})
|
||||
if (finalizerRuns === 'beforeInput') {
|
||||
await nextEventLoop()
|
||||
}
|
||||
rig.textarea.value = '你,'
|
||||
const input = new InputEvent('input', { bubbles: true })
|
||||
Object.defineProperty(input, 'inputType', { value: 'insertText' })
|
||||
Object.defineProperty(input, 'data', { value: ',' })
|
||||
Object.defineProperty(input, 'composed', { value: true })
|
||||
rig.textarea.dispatchEvent(input)
|
||||
dispatchKey(rig, 'keyup', { key: 'Process', code: 'Comma', keyCode: IME_KEYCODE })
|
||||
await nextEventLoop()
|
||||
await nextEventLoop()
|
||||
expect(rig.emitted.join('')).toBe('你,')
|
||||
} finally {
|
||||
rig.terminal.dispose()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
it('sends direct punctuation delivered after the consumed key is released', async () => {
|
||||
const rig = openTerminal()
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user