mirror of
https://github.com/vale981/tridactyl
synced 2025-03-05 09:31:41 -05:00
Merge branch 'nmode'
This commit is contained in:
commit
3d99cb6025
11 changed files with 103 additions and 17 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -9,6 +9,7 @@ export type ModeName =
|
|||
| "gobble"
|
||||
| "input"
|
||||
| "visual"
|
||||
| "nmode"
|
||||
|
||||
export class PrevInput {
|
||||
inputId: string
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
50
src/parsers/nmode.ts
Normal 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
|
||||
}
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue