Attempt to buffer keyboard events until command line is opened

This commit is contained in:
petoncle 2023-10-27 23:37:54 +02:00
parent f224e6cf44
commit 4a31b3ac20
4 changed files with 122 additions and 59 deletions

View file

@ -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<any>[] = [(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(

View file

@ -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,

View file

@ -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)
}

View file

@ -15,6 +15,7 @@ export type TabMessageType =
| "lock"
| "alive"
| "tab_changes"
| "clInputFocused"
export type NonTabMessageType =
| "owntab_background"