fix(terminal): block IME-owned Process shortcuts
Adapted from #11616 commit 9f449d75b and #12120 commit 290f42a36. Co-authored-by: holdn2 <club.makersfarm@gmail.com>
This commit is contained in:
parent
8af6876510
commit
ca9562ceee
|
|
@ -31,6 +31,7 @@ function keyboardEvent(
|
|||
function createHarness(): {
|
||||
deps: KeyboardHandlersDeps
|
||||
editable: HTMLInputElement
|
||||
sendInput: ReturnType<typeof vi.fn>
|
||||
startComposition: () => void
|
||||
terminalInput: HTMLTextAreaElement
|
||||
dispose: () => void
|
||||
|
|
@ -97,6 +98,7 @@ function createHarness(): {
|
|||
return {
|
||||
deps,
|
||||
editable,
|
||||
sendInput,
|
||||
terminalInput,
|
||||
startComposition: () => {
|
||||
terminalElement.dispatchEvent(
|
||||
|
|
@ -309,4 +311,57 @@ describe('Windows IME keyboard ownership', () => {
|
|||
hook.unmount()
|
||||
harness.dispose()
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ code: 'KeyQ', shiftKey: true },
|
||||
{ code: 'KeyW', ctrlKey: true }
|
||||
])('keeps an IME-consumed $code out of terminal shortcuts', (modifier) => {
|
||||
const harness = createHarness()
|
||||
const hook = renderHook(() => useTerminalKeyboardShortcuts(harness.deps))
|
||||
const laterWindowHandler = vi.fn()
|
||||
window.addEventListener('keydown', laterWindowHandler)
|
||||
harness.startComposition()
|
||||
const consumed = keyboardEvent('keydown', {
|
||||
key: 'Process',
|
||||
keyCode: 229,
|
||||
timeStamp: 10,
|
||||
isComposing: true,
|
||||
...modifier
|
||||
})
|
||||
|
||||
harness.terminalInput.dispatchEvent(consumed)
|
||||
|
||||
expect(consumed.defaultPrevented).toBe(false)
|
||||
expect(harness.sendInput).not.toHaveBeenCalled()
|
||||
expect(harness.deps.onRequestClosePane).not.toHaveBeenCalled()
|
||||
expect(laterWindowHandler).not.toHaveBeenCalled()
|
||||
window.removeEventListener('keydown', laterWindowHandler)
|
||||
hook.unmount()
|
||||
harness.dispose()
|
||||
})
|
||||
|
||||
it('keeps an IME-consumed Ctrl+Shift+F out of file search', () => {
|
||||
const harness = createHarness()
|
||||
const pane = harness.deps.managerRef.current?.getActivePane()
|
||||
vi.mocked(pane!.terminal.getSelection).mockReturnValue('needle')
|
||||
const hook = renderHook(() => useTerminalKeyboardShortcuts(harness.deps))
|
||||
harness.startComposition()
|
||||
|
||||
harness.terminalInput.dispatchEvent(
|
||||
keyboardEvent('keydown', {
|
||||
key: 'Process',
|
||||
code: 'KeyF',
|
||||
keyCode: 229,
|
||||
timeStamp: 10,
|
||||
isComposing: true,
|
||||
ctrlKey: true,
|
||||
shiftKey: true
|
||||
})
|
||||
)
|
||||
|
||||
expect(harness.deps.onSearchSelectedText).not.toHaveBeenCalled()
|
||||
expect(harness.sendInput).not.toHaveBeenCalled()
|
||||
hook.unmount()
|
||||
harness.dispose()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
createTerminalImeDeferredNewlineSender,
|
||||
createTerminalImeModifiedEnterChordOwner,
|
||||
getTerminalImeModifiedEnterKind,
|
||||
isTerminalImeConsumedKey,
|
||||
isTerminalImeEnterKeyUp,
|
||||
isTerminalImeProcessEnter
|
||||
} from './terminal-ime-deferred-newline'
|
||||
|
|
@ -477,6 +478,22 @@ export function useTerminalKeyboardShortcuts({
|
|||
return
|
||||
}
|
||||
|
||||
const terminalPaneForImeShortcut = manager.getActivePane() ?? manager.getPanes()[0]
|
||||
const hasPendingImeComposition = hasPendingTerminalImeComposition(
|
||||
terminalPaneForImeShortcut?.terminal.element
|
||||
)
|
||||
const imeProcessEnter = isWindows && hasPendingImeComposition && isTerminalImeProcessEnter(e)
|
||||
if (
|
||||
isWindows &&
|
||||
hasPendingImeComposition &&
|
||||
!imeProcessEnter &&
|
||||
isTerminalImeConsumedKey(e)
|
||||
) {
|
||||
// Process has no logical key, so shortcut matching would fall back to its physical code.
|
||||
e.stopImmediatePropagation()
|
||||
return
|
||||
}
|
||||
|
||||
if (matchFileSearchShortcut(e, shortcutPlatform, keybindings, terminalShortcutPolicy)) {
|
||||
const pane = manager.getActivePane() ?? manager.getPanes()[0]
|
||||
const selectedText = normalizeSelectedTextForFileSearch(pane?.terminal.getSelection())
|
||||
|
|
@ -516,11 +533,6 @@ export function useTerminalKeyboardShortcuts({
|
|||
return
|
||||
}
|
||||
|
||||
const terminalPaneForImeShortcut = manager.getActivePane() ?? manager.getPanes()[0]
|
||||
const hasPendingImeComposition = hasPendingTerminalImeComposition(
|
||||
terminalPaneForImeShortcut?.terminal.element
|
||||
)
|
||||
const imeProcessEnter = isWindows && hasPendingImeComposition && isTerminalImeProcessEnter(e)
|
||||
const shortcutEvent = imeProcessEnter
|
||||
? {
|
||||
key: 'Enter',
|
||||
|
|
|
|||
|
|
@ -106,6 +106,10 @@ export function getTerminalImeModifiedEnterKind(
|
|||
return null
|
||||
}
|
||||
|
||||
export function isTerminalImeConsumedKey(event: Pick<KeyboardEvent, 'key' | 'keyCode'>): boolean {
|
||||
return event.key === 'Process' && event.keyCode === 229
|
||||
}
|
||||
|
||||
export function isTerminalImeProcessEnter(
|
||||
event: Pick<
|
||||
KeyboardEvent,
|
||||
|
|
@ -114,8 +118,7 @@ export function isTerminalImeProcessEnter(
|
|||
): boolean {
|
||||
const { code } = event
|
||||
return (
|
||||
event.key === 'Process' &&
|
||||
event.keyCode === 229 &&
|
||||
isTerminalImeConsumedKey(event) &&
|
||||
(!code || code === 'Unidentified' || code === 'Enter' || code === 'NumpadEnter') &&
|
||||
getTerminalImeModifiedEnterKind(event) !== null
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue