Fix #5475: make nmode ignore initial keyup

This commit is contained in:
Oliver Blanthorn 2026-07-23 15:10:32 +02:00
parent ceccbce949
commit f0037b1551
No known key found for this signature in database
GPG key ID: 2BB8C36BB504BFF3
5 changed files with 35 additions and 7 deletions

View file

@ -5835,11 +5835,17 @@ export async function goto(...selector: string[]) {
* 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.
*
* If the first key event is a keyup, it is ignored because it almost always belongs to the key that entered nmode.
* Use `:nmode --strict ...` to accept it.
*/
//#content
export async function nmode(mode: string, n: number, ...endexArr: string[]) {
export async function nmode(...args: string[]) {
const strict = args[0] === "--strict"
const [mode, count, ...endexArr] = args.slice(strict ? 1 : 0)
const endex = endexArr.join(" ") || "mode ignore"
return nMode.init(endex, mode, n)
const n = count === undefined ? undefined : Number(count)
return nMode.init(endex, mode, n, strict)
}
// {{{TEXT TO SPEECH

View file

@ -202,7 +202,7 @@ export class default_config {
"<AC-Escape>": "mode normal",
"<AC-`>": "mode normal",
"<S-Escape>": "mode normal",
"<CD-o><CU-o>": "nmode normal 1 mode ignore",
"<C-o>": "nmode normal 1 mode ignore",
}
/**
@ -294,7 +294,7 @@ export class default_config {
"<C-d>": "scrollpage 0.5",
"<C-f>": "scrollpage 1",
"<C-b>": "scrollpage -1",
"<CD-v><CU-v>": "nmode ignore 1 mode normal",
"<C-v>": "nmode ignore 1 mode normal",
$: "scrollto 100 x",
// "0": "scrollto 0 x", // will get interpreted as a count
"^": "scrollto 0 x",

13
src/parsers/nmode.test.ts Normal file
View file

@ -0,0 +1,13 @@
import { MinimalKey } from "@src/lib/keyseq"
import * as nmode from "@src/parsers/nmode"
const key = (keyup = false) => new MinimalKey("Enter", { keyup })
test("an initial keyup does not count unless nmode is strict", () => {
nmode.init("mode normal", "ignore", 1)
expect(nmode.parser([key(true)]).exstr).toBeUndefined()
expect(nmode.parser([key()]).exstr).toBe("mode normal")
nmode.init("mode normal", "ignore", 1, true)
expect(nmode.parser([key(true)]).exstr).toBe("mode normal")
})

View file

@ -10,22 +10,33 @@ class NModeState {
public curCommands = 0
public mode = "normal"
public endCommand = ""
public ignoreInitialKeyup = true
}
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 = 1) {
export function init(
endCommand: string,
mode = "normal",
numCommands = 1,
strict = false,
) {
contentState.mode = "nmode"
modeState = new NModeState()
modeState.endCommand = endCommand
modeState.numCommands = numCommands
modeState.mode = mode
modeState.ignoreInitialKeyup = !strict
}
/** Receive keypress. If applicable, execute a command. */
export function parser(keys: keyseq.MinimalKey[]) {
if (modeState.ignoreInitialKeyup) {
modeState.ignoreInitialKeyup = false
if (keys[0]?.keyup) keys = keys.slice(1)
}
keys = keyseq.stripOnlyModifiers(keys)
if (keys.length === 0) return { keys: [], isMatch: false }
const conf = mode2maps.get(modeState.mode) || modeState.mode + "maps"

View file

@ -4,8 +4,6 @@ Tridactyl has to override your new tab page due to WebExtension limitations. You
- You can view your current configuration with `:viewconfig`.
- **Breaking change**: user binds to `nmode` have been broken by the new keyup/keydown binds. Fix by explicitly binding to `<D-[key]><U-[key]>` to ensure that the keyup gets consumed before entering nmode
- Tridactyl funding 👀: [donate via GitHub sponsors here](https://github.com/users/bovine3dom/sponsorship). Please consider starting a recurring donation to bovine3dom (me) if you find Tridactyl useful. As of July 2026, I'm having to withdraw a little more from my savings than I'd like for regular outgoings so any contributions would be gratefully received.
- All GitHub and Patreon donors get a nice little newsletter every now and then; for people who donate at least 10USD a month I briefly did a "tips & tricks" newsletter roughly once a month ([see an example here](https://github.com/tridactyl/tridactyl/blob/master/doc/newsletters/tips-and-tricks/1-hint-css-selectors.md)) but now I'm open to other ideas for saying thanks. You can also donate via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7JQHV4N2YZCTY), but they charge fairly high fees and you won't get any newsletters. Donations currently aim to ensure that bovine3dom can work one day a week on Tridactyl at minimum wage. Previously the donations have funded an in-person developer retreat.