feat: add per-action require-prior-idle override for tap-hold#3
feat: add per-action require-prior-idle override for tap-hold#3
Conversation
ccdfa9f to
a0c1e43
Compare
Code ReviewThis is a well-motivated feature with a clean design. The core logic in Correctness / Potential IssuesInconsistent use of In let opts = parse_tap_hold_options(&ac_params[n_positional..], s)?;But in let opts = parse_tap_hold_options(&ac_params[4..], s)?; // parse_tap_hold
let opts = parse_tap_hold_options(&ac_params[5..], s)?; // parse_tap_hold_keys
let opts = parse_tap_hold_options(&ac_params[6..], s)?; // parse_tap_hold_keys_trigger_tap_releaseThese happen to be correct since
If a user's hold-action is a list that starts with Code DuplicationThe Minor Nits
PerformanceNo concerns. The added Test CoverageGood overall. The 6 sim tests cover the key behavioral cases (override shrinks threshold, override disables, enables without global, mixed actions, tap-hold-release, opposite-hand). One gap: the parser tests cover SummaryThe design is solid —
|
Code ReviewThis is a well-designed and cleanly implemented feature. The Bug / Correctness IssuesNone found. The core logic change in let idle_threshold = require_prior_idle.unwrap_or(self.tap_hold_require_prior_idle);
if idle_threshold > 0 { ... }
Design / Maintenance Concerns1. Inconsistent duplicate detection pattern between
// Instead of separate bool flags:
let mut seen_opts: HashSet<&str> = HashSet::default();
// ...
if !seen_opts.insert(kw) {
bail_expr!(&option[0], "duplicate option '{}'", kw);
}2.
3. Adding Test Coverage4. Redundant sim test
A true mixed test would include two tap-hold keys: one relying on the global threshold and one with a per-action override, with both exercised in sequence to confirm they behave differently. 5. Missing parser test for The sim test ( 6. Missing parser test for The PR lists this variant as supported and it's handled by Minor / Nits
SummaryThe core implementation is correct and the overall approach is solid. The main items worth addressing before merging:
Great work on the 🤖 Generated with Claude Code |
Code ReviewOverall this is a well-designed, minimal PR. The feature addresses a real user need and the implementation is clean. Here are my findings: Hot Path (keyberon/src/layout.rs) ✅The critical change is exactly one line: let idle_threshold = require_prior_idle.unwrap_or(self.tap_hold_require_prior_idle);This is exactly right — cheap Correctness:
|
| Area | Assessment |
|---|---|
| Hot-path runtime change | ✅ Minimal and correct |
| Parser design | ✅ Correct; minor fragility noted |
defhands.rs integration |
|
| Error messages | |
| Test coverage | ✅ Solid; tap-hold-tap-keys parser test missing |
| Documentation | ✅ Clear examples, well-placed in config.adoc |
| API design | ✅ Consistent with existing tap-hold-opposite-hand option convention |
The two items worth addressing before merge are the defhands.rs maintenance risk and the hardcoded valid-options strings. Everything else is minor.
Adds (require-prior-idle <ms>) as a trailing option on all tap-hold variants, allowing individual actions to override the global tap-hold-require-prior-idle defcfg value. Useful when HRMs need idle detection but layer-tap keys need immediate activation. Co-Authored-By: Claude Opus 4.6 <[email protected]>
f9301f3 to
4eda34c
Compare
Code ReviewThis is a well-designed, well-tested feature. The implementation is clean and the hot-path change is minimal. I want to also correct some factual errors in the prior three reviews left by earlier Claude runs, since they may cause unnecessary churn. Corrections to Prior ReviewsReview 1 claims: hardcoded slice offsets like Review 1 claims: Review 2 claims: Review 2 claims: there's no parser test for Genuine Issues1. The variant is listed as supported in the PR description and has both a parser test and a sim test, but the - Works with all `tap-hold` variants: `tap-hold`, `tap-hold-press`,
`tap-hold-release`, `tap-hold-press-timeout`, `tap-hold-release-timeout`,
`tap-hold-release-keys`, `tap-hold-except-keys`, `tap-hold-tap-keys`,
and `tap-hold-opposite-hand`.
2.
3. Hardcoded valid-options strings will drift (low priority)
// tap_hold.rs
"unknown tap-hold option '{}'. Valid options: require-prior-idle"
// defhands.rs
"Valid options: timeout, same-hand, neutral, unknown-hand, neutral-keys, require-prior-idle"When a new option is added, the keyword slice gets updated but the error messages won't unless explicitly remembered. For bail_expr!(
&option[0],
"unknown tap-hold option '{}'. Valid options: {}",
kw,
TAP_HOLD_OPTION_KEYWORDS.join(", ")
);What's Working Well
Summary
The first item is a one-word doc fix. The second and third are maintenance concerns that don't affect correctness today. Overall the PR is solid and ready to merge once the doc omission is addressed. |
Summary
Adds a per-action
(require-prior-idle <ms>)option to alltap-holdvariants, allowing individual actions to override the globaltap-hold-require-prior-idledefcfg value.Addresses jtroo#1967.
Motivation
With the global
tap-hold-require-prior-idlefrom jtroo#1960, users can't mix tap-hold behaviors — home-row mods benefit from idle detection, but layer-tap keys (e.g., tab/number-layer) need immediate activation even during fast typing.Usage
Design
Follows the
(keyword value)option convention established bytap-hold-opposite-hand. The option name isrequire-prior-idle(notap-hold-prefix since it's already inside a tap-hold action).(require-prior-idle 0)— disables for this action even when a global threshold is set(require-prior-idle N)— enables/overrides with N ms thresholddefcfgvalueKeyberon: Added
require_prior_idle: Option<u16>toHoldTapAction. The existing idle check indo_action()uses the per-action value when present, falling back to the globaltap_hold_require_prior_idle.Parser: Shared
parse_tap_hold_options()helper scans for known option keywords in trailing list expressions.count_trailing_options()identifies option lists by checking the first atom against a keyword allowlist, so actions with list-type positional params (e.g.,(layer-toggle nav)) aren't misidentified as options.Test coverage
6 sim tests: per-action override shrinks threshold, per-action disable overrides global, per-action enables without global, mixed actions (issue jtroo#1967 use case), works with
tap-hold-release, works withtap-hold-opposite-hand.7 parser tests: valid value parses, zero parses, non-numeric rejected, unknown option rejected, duplicate rejected, works with
tap-hold-press, works withtap-hold-release.Supported variants
Works with all tap-hold variants:
tap-hold,tap-hold-press,tap-hold-release,tap-hold-press-timeout,tap-hold-release-timeout,tap-hold-release-keys,tap-hold-release-tap-keys-release,tap-hold-except-keys,tap-hold-tap-keys, andtap-hold-opposite-hand.🤖 Generated with Claude Code