2018-04-13 19:28:03 +01:00
|
|
|
import { MsgSafeKeyboardEvent, MsgSafeNode } from "./msgsafe"
|
|
|
|
import { isTextEditable } from "./dom"
|
|
|
|
import { isSimpleKey } from "./keyseq"
|
2017-10-05 18:01:44 +01:00
|
|
|
import state from "./state"
|
2018-04-22 17:15:40 +01:00
|
|
|
import { repeat } from "./.excmds_background.generated"
|
2017-12-30 00:46:26 +00:00
|
|
|
import Logger from "./logging"
|
2017-10-05 18:01:44 +01:00
|
|
|
|
2018-04-13 19:28:03 +01:00
|
|
|
import { parser as exmode_parser } from "./parsers/exmode"
|
|
|
|
import { parser as hintmode_parser } from "./hinting_background"
|
|
|
|
import { parser as findmode_parser } from "./finding_background"
|
|
|
|
import * as gobblemode from "./parsers/gobblemode"
|
2018-07-25 12:08:04 +01:00
|
|
|
import * as generic from "./parsers/genericmode"
|
2017-11-09 00:41:07 +00:00
|
|
|
|
2018-04-13 19:28:03 +01:00
|
|
|
const logger = new Logger("controller")
|
2017-12-04 17:02:31 +01:00
|
|
|
|
2017-10-02 00:59:51 +01:00
|
|
|
/** Accepts keyevents, resolves them to maps, maps to exstrs, executes exstrs */
|
2018-04-13 19:28:03 +01:00
|
|
|
function* ParserController() {
|
2017-11-09 00:41:07 +00:00
|
|
|
const parsers = {
|
2018-07-25 12:25:56 +01:00
|
|
|
normal: keys => generic.parser("nmaps", keys),
|
|
|
|
insert: keys => generic.parser("imaps", keys),
|
|
|
|
input: keys => generic.parser("inputmaps", keys),
|
|
|
|
ignore: keys => generic.parser("ignoremaps", keys),
|
2017-11-09 00:41:07 +00:00
|
|
|
hint: hintmode_parser,
|
2018-01-28 21:18:29 +00:00
|
|
|
find: findmode_parser,
|
2017-11-20 23:21:49 +00:00
|
|
|
gobble: gobblemode.parser,
|
2017-11-09 00:41:07 +00:00
|
|
|
}
|
|
|
|
|
2017-10-02 00:59:51 +01:00
|
|
|
while (true) {
|
2018-04-15 23:12:54 +01:00
|
|
|
let exstr = ""
|
|
|
|
let keyEvents = []
|
2017-10-02 00:59:51 +01:00
|
|
|
try {
|
2018-04-13 19:28:03 +01:00
|
|
|
while (true) {
|
2017-10-06 03:28:14 +01:00
|
|
|
let keyevent: MsgSafeKeyboardEvent = yield
|
2017-10-02 00:59:51 +01:00
|
|
|
|
2018-03-07 02:09:41 +00:00
|
|
|
// This code was sort of the cause of the most serious bug in Tridactyl
|
|
|
|
// to date (March 2018).
|
|
|
|
// https://github.com/cmcaine/tridactyl/issues/311
|
2018-04-13 19:28:03 +01:00
|
|
|
if (
|
|
|
|
state.mode != "ignore" &&
|
|
|
|
state.mode != "hint" &&
|
2018-04-22 15:47:38 +01:00
|
|
|
state.mode != "input" &&
|
2018-04-13 19:28:03 +01:00
|
|
|
state.mode != "find"
|
|
|
|
) {
|
2017-11-15 00:03:34 +00:00
|
|
|
if (isTextEditable(keyevent.target)) {
|
2018-04-13 19:28:03 +01:00
|
|
|
if (state.mode !== "insert") {
|
2018-03-07 02:09:41 +00:00
|
|
|
state.mode = "insert"
|
|
|
|
}
|
2018-04-22 15:47:38 +01:00
|
|
|
} else if (state.mode === "insert") {
|
2017-11-15 00:03:34 +00:00
|
|
|
state.mode = "normal"
|
|
|
|
}
|
2018-04-22 15:47:38 +01:00
|
|
|
} else if (
|
|
|
|
state.mode === "input" &&
|
|
|
|
!isTextEditable(keyevent.target)
|
|
|
|
) {
|
|
|
|
state.mode = "normal"
|
2017-10-05 18:01:44 +01:00
|
|
|
}
|
2017-12-30 00:46:26 +00:00
|
|
|
logger.debug(keyevent, state.mode)
|
2017-10-05 18:01:44 +01:00
|
|
|
|
2018-04-15 23:12:54 +01:00
|
|
|
keyEvents.push(keyevent)
|
2017-11-09 00:41:07 +00:00
|
|
|
let response = undefined
|
2018-07-25 12:25:56 +01:00
|
|
|
response = (parsers[state.mode] as any)(keyEvents)
|
2018-04-15 23:12:54 +01:00
|
|
|
logger.debug(keyEvents, response)
|
2017-10-02 00:59:51 +01:00
|
|
|
|
2018-04-15 23:12:54 +01:00
|
|
|
if (response.exstr) {
|
|
|
|
exstr = response.exstr
|
2017-10-02 00:59:51 +01:00
|
|
|
break
|
|
|
|
} else {
|
2018-04-15 23:12:54 +01:00
|
|
|
keyEvents = response.keys
|
2017-10-02 00:59:51 +01:00
|
|
|
}
|
|
|
|
}
|
2018-04-15 23:12:54 +01:00
|
|
|
acceptExCmd(exstr)
|
2017-10-02 00:59:51 +01:00
|
|
|
} catch (e) {
|
|
|
|
// Rumsfeldian errors are caught here
|
2018-05-18 14:09:12 +01:00
|
|
|
logger.error(
|
|
|
|
"An error occurred in the controller: ",
|
|
|
|
e,
|
|
|
|
" ¯\\_(ツ)_/¯",
|
|
|
|
)
|
2017-10-02 00:59:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let generator = ParserController() // var rather than let stops weirdness in repl.
|
|
|
|
generator.next()
|
|
|
|
|
|
|
|
/** Feed keys to the ParserController */
|
2017-10-06 03:28:14 +01:00
|
|
|
export function acceptKey(keyevent: MsgSafeKeyboardEvent) {
|
2017-10-02 00:59:51 +01:00
|
|
|
generator.next(keyevent)
|
|
|
|
}
|
|
|
|
|
{excmds,controller}.ts: Fix race condition in state.mode synchronization
This fixes https://github.com/cmcaine/tridactyl/issues/613.
This was a really fun bug. What happened was this:
- First, the content script set state.mode to "hint", which was then
synchronized
- The event listener in the background script noticed the update and set
state.mode to "hint" in the background script
- Then, the content script translated the hint selection into an excmd
that needed to be executed in the background script and sent said
command to the controller, which lives in the background script
- The content script then set state.mode to "normal"
- The background script executed the command and saved it in
state.last_ex_str, which was then synchronized
- The event listener in the content script noticed the update and set
last_ex_str to the last executed str and "state.mode" to "hint"
So basically, the problem was that the background script didn't notice
the state.mode update right after it happened. I fixed this by moving
last_ex_str out of "state" since it doesn't need to be synchronized with
the content script.
This means that the same kind of race condition can still happen. I'm
not sure how to fix this. We could just kill state completely and
instead have state be updated through message passing, but this probably
wouldn't be very ergonomic.
Another solution, the one envisioned for Tridactylv2, is to move to the
content script entirely. This is probably the best option.
2018-06-05 21:13:29 +02:00
|
|
|
export let last_ex_str = ""
|
|
|
|
|
2017-10-02 00:59:51 +01:00
|
|
|
/** Parse and execute ExCmds */
|
2018-05-05 14:14:02 +01:00
|
|
|
export async function acceptExCmd(exstr: string): Promise<any> {
|
2017-10-05 13:26:28 +01:00
|
|
|
// TODO: Errors should go to CommandLine.
|
2017-10-02 00:59:51 +01:00
|
|
|
try {
|
2018-04-15 23:12:54 +01:00
|
|
|
let [func, args] = exmode_parser(exstr)
|
2017-12-04 17:02:31 +01:00
|
|
|
// Stop the repeat excmd from recursing.
|
{excmds,controller}.ts: Fix race condition in state.mode synchronization
This fixes https://github.com/cmcaine/tridactyl/issues/613.
This was a really fun bug. What happened was this:
- First, the content script set state.mode to "hint", which was then
synchronized
- The event listener in the background script noticed the update and set
state.mode to "hint" in the background script
- Then, the content script translated the hint selection into an excmd
that needed to be executed in the background script and sent said
command to the controller, which lives in the background script
- The content script then set state.mode to "normal"
- The background script executed the command and saved it in
state.last_ex_str, which was then synchronized
- The event listener in the content script noticed the update and set
last_ex_str to the last executed str and "state.mode" to "hint"
So basically, the problem was that the background script didn't notice
the state.mode update right after it happened. I fixed this by moving
last_ex_str out of "state" since it doesn't need to be synchronized with
the content script.
This means that the same kind of race condition can still happen. I'm
not sure how to fix this. We could just kill state completely and
instead have state be updated through message passing, but this probably
wouldn't be very ergonomic.
Another solution, the one envisioned for Tridactylv2, is to move to the
content script entirely. This is probably the best option.
2018-06-05 21:13:29 +02:00
|
|
|
if (func !== repeat) last_ex_str = exstr
|
2017-10-05 13:26:28 +01:00
|
|
|
try {
|
2018-05-05 14:14:02 +01:00
|
|
|
return await func(...args)
|
2017-10-05 13:26:28 +01:00
|
|
|
} catch (e) {
|
|
|
|
// Errors from func are caught here (e.g. no next tab)
|
2018-04-23 15:09:20 +01:00
|
|
|
logger.error(e)
|
2017-10-05 13:26:28 +01:00
|
|
|
}
|
2017-10-02 00:59:51 +01:00
|
|
|
} catch (e) {
|
2017-10-05 13:26:28 +01:00
|
|
|
// Errors from parser caught here
|
2018-04-23 15:09:20 +01:00
|
|
|
logger.error(e)
|
2017-10-02 00:59:51 +01:00
|
|
|
}
|
|
|
|
}
|
2017-11-09 00:41:07 +00:00
|
|
|
|
2018-04-13 19:28:03 +01:00
|
|
|
import { activeTabId } from "./lib/webext"
|
|
|
|
browser.webNavigation.onBeforeNavigate.addListener(async function(details) {
|
|
|
|
if (details.frameId === 0 && details.tabId === (await activeTabId())) {
|
|
|
|
state.mode = "normal"
|
2017-11-09 00:41:07 +00:00
|
|
|
}
|
|
|
|
})
|
2018-04-13 19:28:03 +01:00
|
|
|
browser.tabs.onActivated.addListener(() => (state.mode = "normal"))
|