feat(char): CJK-aware f/t/F/T through flash.nvim char mode - #21
Open
fang2hou wants to merge 4 commits into
Open
Conversation
Wrap flash.plugins.char.mode idempotently: the single typed character
is compiled through the mix mode, so pinyin/romaji/romanization first
letters and CJK punctuation classes match CJK characters in flash's
enhanced char motions. t/T keep native before/after landing positions
and the multi_line line anchor; max_length, labeler, actions and the
Rust matcher path stay untouched (char mode is vim-regex only).
Opt out with setup({ char = false }).
…char
The char integration flag moves from a top-level boolean into the
motions group -- the config surface for integrations on entry points
flash.nvim itself owns (modes.char today; flash-owned surfaces such as
the / search mode land here later). Matching-semantic options stay
top-level. Non-boolean flags are rejected ("false" is truthy in Lua
and would silently enable the integration); a rejected setup leaves
the config untouched.
Deep-extending arbitrary user fields into config.motions persisted unknown keys (typos look accepted) and left no room for future table-shaped motion surfaces. normalize_motions mirrors the languages precedent instead: known flags are validated booleans, unknown fields are dropped (forward compatibility) -- only normalized values merge.
fang2hou
force-pushed
the
worktree/rapid-cloud-3f8e
branch
from
August 28, 2026 08:56
898c7df to
937a6ce
Compare
Wrap flash.plugins.search.start idempotently: after the native state
is created, the pattern mode is swapped for a mix-mode compiler and
the default labeler for flash-cjk's predictive one (multi-char queries
need next-letter prediction so a continuation letter never collides
with an assigned label -- flash's check_jump treats the extending char
as a label press).
Native vim regex keeps its contract: queries containing metacharacters
(the magic set plus the search delimiter) or non-ASCII bytes pass
through verbatim, so /.* and /^func are untouched; flash's
operator-pending position-pattern rewrite relies on the same
passthrough. <cr>, n and N stay native -- labels are the CJK carrier.
Requires flash's own modes.search.enabled; opt out with
setup({ motions = { search = false } }).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
flash.nvim ships enhanced
f/t/F/Tmotions (modes.char, on by default) and a//?label overlay (modes.search, off by default). This PR makes both flash-owned surfaces CJK-aware through one config group:motions = { char = true }—fvjumps to中(Xiaohe-stylevinitial for zhong),ftto中(kunrei-shikityuu) or梯(pinyinti). Single-character matching;;/,, counts, operator-pending stay native.motions = { search = true }— with flash'smodes.search.enabled = true, typing pinyin/romaji in//?puts flash-cjk's predictive labels on CJK matches; pressing a label char jumps (flash's own check_jump).<cr>/n/Nkeep native vim semantics.User-owned triggers stay wrapper-style (
flash-cjk.jump()/remote()to bind yourself) and share the same matching config (languages/priority/mixed_input);motionsgroups the flash-owned triggers. Both integrations follow the same mechanism: idempotent, pcall-safe wraps installed atsetup()/build_opts().Impact
lua/flash-cjk/patches.lua—char_mode_patch()wrapsflash.plugins.char.mode(per-motion pattern builder;t=\m.\ze<cjk>,T=\m<cjk>\zs.,f/Fbare;multi_line=falseline anchor preserved).search_mode_patch()wrapsflash.plugins.search.start: post-construction swap ofstate.pattern.modeto a mix compiler andstate.labelerto flash-cjk's predictive labeler (multi-char queries need next-letter prediction — a typed continuation letter must never collide with an assigned label)./path: queries containing metacharacters (magic set\ . * [ ] ^ $ ~plus the/delimiter) or non-ASCII bytes pass through verbatim —/.*,/^funcuntouched; flash's operator-pending\%<line>l\%<col>c.cmdline rewrite relies on the same passthrough. Trade-off: punctuation CJK classes in/cover only non-meta keys (,;:'"); thes-jump keeps the full set.lua/flash-cjk/config.lua/init.lua—motions = { char = true, search = true };normalize_motionsvalidates known flags as booleans and drops unknown fields (forward compatibility); non-booleans error ("false"is truthy in Lua), a rejected setup leaves config untouched.tests/run.lua— char + search sections: pattern forms, passthrough set, idempotence, gates, validation, predictive-labeler collision check, realChar.jumpflow.tests/e2e/repro.lua+scenario.lua— 9 char checks and 13 search checks through the REAL flows (char: prefedChar.jump; search: a coroutine-driven cmdline harness feeding real//?keys through CmdlineEnter/Changed/check_jump, including a real label-char jump landing the cursor on 梯).motionsgroup docs, requirements, trade-offs.Not touched: char
max_length=1, char labeler/actions, the Rust matcher path (both wraps are vim-regexSearch; parity enforced by the existing e2e phase diff).Context
Verified reading map against the repo data:
中←v(flypy zh→v singlepin) andt(jatyuu);梯←t(zhcn ti);你←n/z(ja). Both wraps were prototyped against real flash.nvim before implementation. Known inherited constraints, documented in the READMEs: char matching is single-character only; flash's clever-f repeat means a repeated motion letter cycles matches;/label matching needs flash'smodes.search.enabled.Risks
motions = { char = false }.flash.plugins.char.mode/flash.plugins.search.start; upstream renames no-op via pcall and native behavior is preserved./overlay intentionally does not rewrite the search register: after a plain<cr>,n/Nsearch literally. Labels are the CJK carrier.Testing
mise run check— passmise run test— pass (lua suite 221/0 — 53 new assertions across char+search; cargo 43; rust↔vim cross-validation)mise run e2e— E2E PASSED: 9 char + 13 search checks green in both the rust and vim-regex phases, parity identical, server lifecycle clean.