Merge branch 'nmode'

This commit is contained in:
Oliver Blanthorn 2020-05-09 12:15:00 +01:00
commit 3d99cb6025
No known key found for this signature in database
GPG key ID: 2BB8C36BB504BFF3
11 changed files with 103 additions and 17 deletions

View file

@ -58,6 +58,7 @@ This is a (non-exhaustive) list of the most common normal-mode bindings. Type `:
- `Shift` + `Insert` — enter "ignore mode". Press `Shift` + `Insert` again to return to "normal mode".
- `ZZ` — close all tabs and windows, but only "save" them if your about:preferences are set to "show your tabs and windows from last time"
- `.` — repeat the last command
- `<C-v>` send a single keystroke to the current website, bypassing bindings
You can try `:help key` to know more about `key`. If it is an existing binding, it will take you to the help section of the command that will be executed when pressing `key`. For example `:help .` will take you to the help section of the `repeat` command.

View file

@ -7,6 +7,7 @@ import { KeyEventLike } from "@src/lib/keyseq"
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"
const logger = new Logger("controller")
@ -102,6 +103,7 @@ function* ParserController() {
hint: hinting.parser,
gobble: gobblemode.parser,
visual: keys => generic.parser("vmaps", keys),
nmode: nmode.parser,
}
while (true) {

View file

@ -9,6 +9,7 @@ export type ModeName =
| "gobble"
| "input"
| "visual"
| "nmode"
export class PrevInput {
inputId: string

View file

@ -134,6 +134,7 @@ import * as finding from "@src/content/finding"
import * as toys from "./content/toys"
import * as hinting from "@src/content/hinting"
import * as gobbleMode from "@src/parsers/gobblemode"
import * as nMode from "@src/parsers/nmode"
ALL_EXCMDS = {
"": CTSELF,
@ -4200,6 +4201,24 @@ export async function gobble(nChars: number, endCmd: string) {
// }}}
/**
* Initialize n [mode] mode.
*
* In this special mode, a series of key sequences are executed as bindings from a different mode, as specified by the
* `mode` argument. After the count of accepted sequences is `n`, the finalizing ex command given as the `endexArr`
* argument is executed, which defaults to `mode ignore`.
*
* Example: `:nmode normal 1 mode ignore`
* This looks up the next key sequence in the normal mode bindings, executes it, and switches the mode to `ignore`.
* If the key sequence does not match a binding, it will be silently passed through to Firefox, but it will be counted
* for the termination condition.
*/
//#content
export async function nmode(mode: string, n: number, ...endexArr: string[]) {
const endex = endexArr.join(" ") || "mode ignore"
return nMode.init(endex, mode, n)
}
// {{{TEXT TO SPEECH
/**

View file

@ -127,6 +127,7 @@ export class default_config {
"<S-Escape>": "mode normal",
"<C-^>": "tab #",
"<C-6>": "tab #",
"<C-o>": "nmode normal 1 mode ignore",
}
/**
@ -209,6 +210,7 @@ export class default_config {
"<C-d>": "scrollpage 0.5",
"<C-f>": "scrollpage 1",
"<C-b>": "scrollpage -1",
"<C-v>": "nmode ignore 1 mode normal", // Is this a terrible idea? Pentadactyl did it http://bug.5digits.org/help/pentadactyl/browsing.xhtml#send-key
$: "scrollto 100 x",
// "0": "scrollto 0 x", // will get interpreted as a count
"^": "scrollto 0 x",

View file

@ -23,6 +23,7 @@
/** */
import { filter, find, izip } from "@src/lib/itertools"
import { Parser } from "@src/lib/nearley_utils"
import * as config from "@src/lib/config"
import grammar from "@src/grammars/.bracketexpr.generated"
const bracketexpr_grammar = grammar
const bracketexpr_parser = new Parser(bracketexpr_grammar)
@ -132,12 +133,16 @@ function splitNumericPrefix(keyseq: KeyEventLike[]): [KeyEventLike[], KeyEventLi
}
}
export function parse(keyseq: KeyEventLike[], map: KeyMap): ParserResponse {
// Remove bare modifiers
keyseq = keyseq.filter(
export function stripOnlyModifiers(keyseq) {
return keyseq.filter(
key =>
!["Control", "Shift", "Alt", "AltGraph", "Meta"].includes(key.key),
)
}
export function parse(keyseq: KeyEventLike[], map: KeyMap): ParserResponse {
// Remove bare modifiers
keyseq = stripOnlyModifiers(keyseq)
// If the keyseq is now empty, abort.
if (keyseq.length === 0)
@ -345,6 +350,21 @@ export function mapstrMapToKeyMap(mapstrMap: Map<string, MapTarget>): KeyMap {
return newKeyMap
}
export function keyMap(conf, keys): KeyMap {
let maps: any = config.get(conf)
if (maps === undefined) throw new Error("No binds defined for this mode. Reload page with <C-r> and add binds, e.g. :bind --mode=[mode] <Esc> mode normal")
// If so configured, translate keys using the key translation map
if (config.get("keytranslatemodes")[conf] === "true") {
const translationmap = config.get("keytranslatemap")
translateKeysUsingKeyTranslateMap(keys, translationmap)
}
// Convert to KeyMap
maps = new Map(Object.entries(maps))
return mapstrMapToKeyMap(maps)
}
// }}}
// {{{ Utility functions for dealing with KeyboardEvents

View file

@ -1,21 +1,8 @@
/** Tridactyl helper mode */
import * as config from "@src/lib/config"
import * as keyseq from "@src/lib/keyseq"
export function parser(conf, keys): keyseq.ParserResponse {
let maps: any = config.get(conf)
if (maps === undefined) throw new Error("No binds defined for this mode. Reload page with <C-r> and add binds, e.g. :bind --mode=[mode] <Esc> mode normal")
// If so configured, translate keys using the key translation map
if (config.get("keytranslatemodes")[conf] === "true") {
const translationmap = config.get("keytranslatemap")
keyseq.translateKeysUsingKeyTranslateMap(keys, translationmap)
}
// Convert to KeyMap
maps = new Map(Object.entries(maps))
maps = keyseq.mapstrMapToKeyMap(maps)
const maps = keyseq.keyMap(conf, keys)
return keyseq.parse(keys, maps)
}

50
src/parsers/nmode.ts Normal file
View file

@ -0,0 +1,50 @@
/** Accept n [mode] commands then execute the other command */
import { contentState } from "@src/content/state_content"
import * as keyseq from "@src/lib/keyseq"
import { mode2maps } from "@src/lib/binding"
/** Simple container for the nmode state. */
class NModeState {
public numCommands = 1
public curCommands = 0
public mode = "normal"
public endCommand = ""
}
let modeState: NModeState
/** Init n [mode] mode. After parsing the defined number of commands, execute
`endCmd`. `Escape` cancels the mode and executes `endCmd`. */
export function init(endCommand: string, mode = "normal", numCommands: number = 1) {
contentState.mode = "nmode"
modeState = new NModeState()
modeState.endCommand = endCommand
modeState.numCommands = numCommands
modeState.mode = mode
}
/** Receive keypress. If applicable, execute a command. */
export function parser(keys: KeyboardEvent[]) {
keys = keyseq.stripOnlyModifiers(keys)
if (keys.length === 0) return { keys: [], isMatch: false }
const conf = mode2maps.get(modeState.mode) || modeState.mode + "maps"
const maps: any = keyseq.keyMap(conf, keys)
const key = keys[0].key
if (key === "Escape") {
const exstr = modeState.endCommand
modeState = undefined
return { keys: [], exstr }
}
const response = keyseq.parse(keys, maps)
if ((response.exstr !== undefined && response.isMatch) || !response.isMatch) modeState.curCommands += 1
if (modeState.curCommands >= modeState.numCommands) {
const prefix =
(response.exstr === undefined) ? "" : ("composite " + response.exstr + "; ")
response.exstr = prefix + modeState.endCommand // NB: this probably breaks any `js` binds
modeState = undefined
}
return response
}

View file

@ -27,6 +27,7 @@ The idea behind Tridactyl is to allow you to navigate the web more efficiently w
- Ignore mode
- This mode passes all keypresses through to the web page. It is useful for websites that have their own keybinds, such as games and Gmail.
- You can toggle the mode with `Shift-Insert`, `Ctrl-Alt-Backtick`, or `Shift-Esc`.
- While in ignore mode, you can execute a single normal mode binding by pressing `<C-o>` followed by the keys for the binding.
Almost all of the modes are controlled by series of keypresses. In this tutorial, a sequence of keys such as `zz` should be entered by pressing the key `z`, letting go, and then pressing the key `z`. There is no need to hold both keys at once, if that were even possible. (`zz` resets the zoom level to the default, so it probably didn't seem to do anything). Sometimes `help` refers to a command that must be entered in command mode; it should hopefully always be clear from context which we mean.

View file

@ -26,6 +26,7 @@ Many keypresses in normal mode take you into another mode. `t`, for example, put
* Protip: quickly search for the source of a quote by using `;p` to copy a paragraph, and `P` to search the internet for it
* `zi`,`zo`,`zz` zoom in, out and return to the default zoom
* Search text with Firefox's standard `/` binding, jump from match to match with `<C-g>` or `<C-G>` (note that it isn't possible to rebind searching/jumping between matches for now). If you want to use Firefox's `<C-f>` search you'll have to run `unbind <C-f>`.
- `<C-v>` sends the next keystroke to the current website, bypassing bindings
All the keys in normal mode are bound to commands; for example, `j` is bound to `scrolline 10`. If you are ever curious as to what a key sequence does in normal mode, you can simply use `:bind [keys]` and the command line will tell you to which command they are bound.

View file

@ -39,6 +39,8 @@ REPLACE_ME_WITH_THE_CHANGE_LOG_USING_SED
- `yy` — copy the current page URL to your clipboard.
- `[[`/`]]` — navigate forward/backward though paginated pages.
- `ZZ` — close all tabs and windows, but it will only "save" them if your about:preferences are set to "show your tabs and windows from last time".
- `<C-v>` send a single keystroke to the current website, bypassing bindings
- `<C-o>` run a single normal mode binding when in ignore mode
- [`:help hint`][help-hint] to see all the other useful hint modes (this is the `f` magic. :) ).
- `:help <keybinding>` to learn more about what a specific key binding does.