From 4a31b3ac201e9292274f103dc20e44bdc3fae46e Mon Sep 17 00:00:00 2001 From: petoncle Date: Fri, 27 Oct 2023 23:37:54 +0200 Subject: [PATCH] Attempt to buffer keyboard events until command line is opened --- src/commandline_frame.ts | 148 ++++++++++++++++++----------- src/content/commandline_content.ts | 4 +- src/content/controller_content.ts | 28 +++++- src/lib/messaging.ts | 1 + 4 files changed, 122 insertions(+), 59 deletions(-) diff --git a/src/commandline_frame.ts b/src/commandline_frame.ts index 7f54e586..775e8097 100644 --- a/src/commandline_frame.ts +++ b/src/commandline_frame.ts @@ -162,9 +162,42 @@ const noblur = () => setTimeout(() => commandline_state.clInput.focus(), 0) /** @hidden **/ export function focus() { - commandline_state.clInput.focus() - commandline_state.clInput.removeEventListener("blur", noblur) - commandline_state.clInput.addEventListener("blur", noblur) + setTimeout(() => { + logger.info("Called focus() after 2000ms") + Messaging.messageOwnTab("clInputFocused", "unused") + commandline_state.clInput.focus() + commandline_state.clInput.removeEventListener("blur", noblur) + commandline_state.clInput.addEventListener("blur", noblur) + if (buffer.length !== 0) { + logger.info("Dispatching " + JSON.stringify(buffer)); + buffer.forEach(e => processKeyboardEvent(e)) + buffer.splice(-1) + } + }, 2000) +} + +let buffer: KeyboardEvent[] = [] +export function bufferUntil([ code, + key, + altKey, + ctrlKey, + metaKey, + shiftKey]) { + const keyevent = new KeyboardEvent('keydown', { + code: code, + key: key, + altKey: altKey, + ctrlKey: ctrlKey, + metaKey: metaKey, + shiftKey: shiftKey, + }) + logger.info("Received keyboardEvent for buffering", keyevent) + if (window.document.activeElement === commandline_state.clInput) { + processKeyboardEvent(keyevent) + } + else { + buffer.push(keyevent); + } } /** @hidden **/ @@ -182,68 +215,73 @@ let prev_cmd_called_history = false // Save programmer time by generating an immediately resolved promise // eslint-disable-next-line @typescript-eslint/no-empty-function const QUEUE: Promise[] = [(async () => {})()] +logger.info("Setting event listeners of commandline_state.clInput") /** @hidden **/ commandline_state.clInput.addEventListener( "keydown", function (keyevent: KeyboardEvent) { if (!keyevent.isTrusted) return - commandline_state.keyEvents.push(minimalKeyFromKeyboardEvent(keyevent)) - const response = keyParser(commandline_state.keyEvents) - if (response.isMatch) { - keyevent.preventDefault() - keyevent.stopImmediatePropagation() - } else { - // Ideally, all keys that aren't explicitly bound to an ex command - // should be bound to a "self-insert" command that would input the - // key itself. Because it's not possible to generate events as if - // they originated from the user, we can't do this, but we still - // need to simulate it, in order to have history() work. - prev_cmd_called_history = false - } - if (response.value) { - commandline_state.keyEvents = [] - history_called = false - - // If excmds start with 'ex.' they're coming back to us anyway, so skip that. - // This is definitely a hack. Should expand aliases with exmode, etc. - // but this whole thing should be scrapped soon, so whatever. - if (response.value.startsWith("ex.")) { - const [funcname, ...args] = response.value.slice(3).split(/\s+/) - - QUEUE[QUEUE.length - 1].then(() => { - QUEUE.push( - // Abuse async to wrap non-promises in a promise - // eslint-disable-next-line @typescript-eslint/require-await - (async () => - commandline_state.fns[ - funcname as keyof typeof commandline_state.fns - ]( - args.length === 0 ? undefined : args.join(" "), - ))(), - ) - prev_cmd_called_history = history_called - }) - } else { - // Send excmds directly to our own tab, which fixes the - // old bug where a command would be issued in one tab but - // land in another because the active tab had - // changed. Background-mode excmds will be received by the - // own tab's content script and then bounced through a - // shim to the background, but the latency increase should - // be acceptable becuase the background-mode excmds tend - // to be a touch less latency-sensitive. - Messaging.messageOwnTab("controller_content", "acceptExCmd", [ - response.value, - ]).then(_ => (prev_cmd_called_history = history_called)) - } - } else { - commandline_state.keyEvents = response.keys - } + processKeyboardEvent(keyevent); }, true, ) +function processKeyboardEvent(keyevent: KeyboardEvent) { + commandline_state.keyEvents.push(minimalKeyFromKeyboardEvent(keyevent)) + const response = keyParser(commandline_state.keyEvents) + if (response.isMatch) { + keyevent.preventDefault() + keyevent.stopImmediatePropagation() + } else { + // Ideally, all keys that aren't explicitly bound to an ex command + // should be bound to a "self-insert" command that would input the + // key itself. Because it's not possible to generate events as if + // they originated from the user, we can't do this, but we still + // need to simulate it, in order to have history() work. + prev_cmd_called_history = false + } + if (response.value) { + commandline_state.keyEvents = [] + history_called = false + + // If excmds start with 'ex.' they're coming back to us anyway, so skip that. + // This is definitely a hack. Should expand aliases with exmode, etc. + // but this whole thing should be scrapped soon, so whatever. + if (response.value.startsWith("ex.")) { + const [funcname, ...args] = response.value.slice(3).split(/\s+/) + + QUEUE[QUEUE.length - 1].then(() => { + QUEUE.push( + // Abuse async to wrap non-promises in a promise + // eslint-disable-next-line @typescript-eslint/require-await + (async () => + commandline_state.fns[ + funcname as keyof typeof commandline_state.fns + ]( + args.length === 0 ? undefined : args.join(" "), + ))(), + ) + prev_cmd_called_history = history_called + }) + } else { + // Send excmds directly to our own tab, which fixes the + // old bug where a command would be issued in one tab but + // land in another because the active tab had + // changed. Background-mode excmds will be received by the + // own tab's content script and then bounced through a + // shim to the background, but the latency increase should + // be acceptable becuase the background-mode excmds tend + // to be a touch less latency-sensitive. + Messaging.messageOwnTab("controller_content", "acceptExCmd", [ + response.value, + ]).then(_ => (prev_cmd_called_history = history_called)) + } + } else { + commandline_state.keyEvents = response.keys + } +} + export function refresh_completions(exstr) { if (!commandline_state.activeCompletions) enableCompletions() return Promise.all( diff --git a/src/content/commandline_content.ts b/src/content/commandline_content.ts index e12c3e70..b6bf86b5 100644 --- a/src/content/commandline_content.ts +++ b/src/content/commandline_content.ts @@ -63,7 +63,7 @@ export function show(hidehover = false) { * * Inspired by VVimpulation: https://github.com/amedama41/vvimpulation/commit/53065d015d1e9a892496619b51be83771f57b3d5 */ - + logger.info("Called show()") if (hidehover) { const a = window.document.createElement("A") ;(a as any).href = "" @@ -98,7 +98,7 @@ export function hide() { export function focus() { try { - cmdline_iframe.focus() + // cmdline_iframe.focus() } catch (e) { // Note: We can't use cmdline_logger.error because it will try to log // the error in the commandline, which will need to focus() it again, diff --git a/src/content/controller_content.ts b/src/content/controller_content.ts index 56491f6f..c9db1ab1 100644 --- a/src/content/controller_content.ts +++ b/src/content/controller_content.ts @@ -14,6 +14,7 @@ import * as hinting from "@src/content/hinting" import * as gobblemode from "@src/parsers/gobblemode" import * as generic from "@src/parsers/genericmode" import * as nmode from "@src/parsers/nmode" +import * as Messaging from "@src/lib/messaging"; const logger = new Logger("controller") @@ -191,6 +192,10 @@ function* ParserController() { if (response.exstr) { exstr = response.exstr + if (exstr.startsWith("fillcmdline ")) { + logger.info("Setting bufferUntilClInputFocused to true") + bufferUntilClInputFocused = true + } break } else { keyEvents = response.keys @@ -211,11 +216,30 @@ function* ParserController() { } } } - +Messaging.addListener("clInputFocused", () => { + logger.info("Callback clInputFocused") + bufferUntilClInputFocused = false +}) +let bufferUntilClInputFocused: boolean = false export const generator = ParserController() // var rather than let stops weirdness in repl. generator.next() /** Feed keys to the ParserController */ export function acceptKey(keyevent: KeyboardEvent) { - return generator.next(keyevent) + logger.info("bufferUntilClInputFocused = " + bufferUntilClInputFocused) + if (bufferUntilClInputFocused) { + logger.info("Sending keyboardEvent for buffering " + keyevent.key) + Messaging.messageOwnTab("commandline_frame", "bufferUntil", + [keyevent.code, + keyevent.key, + keyevent.altKey, + keyevent.ctrlKey, + keyevent.metaKey, + keyevent.shiftKey] + ); + keyevent.preventDefault() + keyevent.stopImmediatePropagation() + canceller.push(keyevent) + } else + return generator.next(keyevent) } diff --git a/src/lib/messaging.ts b/src/lib/messaging.ts index 3920810d..fc7b398b 100644 --- a/src/lib/messaging.ts +++ b/src/lib/messaging.ts @@ -15,6 +15,7 @@ export type TabMessageType = | "lock" | "alive" | "tab_changes" + | "clInputFocused" export type NonTabMessageType = | "owntab_background"