feat: smart double-click selection (OSC 8 links + regex rules)#1629
Open
nikicat wants to merge 5 commits into
Open
feat: smart double-click selection (OSC 8 links + regex rules)#1629nikicat wants to merge 5 commits into
nikicat wants to merge 5 commits into
Conversation
Double-clicking inside an OSC 8 link span now selects the whole link (across soft-wrapped rows) instead of the semantic-word boundaries, which truncate URLs whose anchor text contains characters like `:` or spaces. Falls back to the existing semantic selection when the click cell has no hyperlink, so plain text behavior is unchanged. The span walk lives in `crosswords::hyperlink::hyperlink_span_at` and is now also used by `find_hyperlink_at_point` in the hint path, so the two places agree on link boundaries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> (cherry picked from commit 25c7287)
Adds an iTerm2-style smart-selection layer between the OSC 8 fast path and the existing semantic selection. Six precision-tagged regex rules ship as defaults — URL, file:line:col, UUID, IPv4, email, git SHA — so a double-click on any of them selects the whole token instead of truncating at the semantic boundary characters (`:` in particular). The engine scans the soft-wrapped logical line containing the click, returns the highest-precision match that covers it, and falls back to the previous semantic behavior when no rule matches. URL precision is above IPv4 so a URL like `https://192.168.1.1/admin` selects whole instead of being shadowed by its embedded address. A `Vec<SmartRule>` lives on `Screen` so DFA compilation happens once at startup. Config exposure (user rules, overrides) is the next phase. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> (cherry picked from commit b24c6f3)
Adds a `[smart-selection]` config section so users can disable the engine, remove built-in rules, override their precision/pattern, or append fresh rules (e.g. JIRA tickets, internal ID schemes). The runtime rule list is the merge of the hardcoded defaults and the user's `[[smart-selection.rules]]` entries, layered by name: same name replaces, `enabled = false` removes, fresh name appends. Bad user regexes log a warning and skip just that rule. `SmartSelector` owns the compiled rules and the reload step, so a config change picked up by `Screen::update_config` swaps the rule set without rebuilding `Screen`. Splitting the reload into its own type also gets it covered by unit tests — three new cases assert that the master switch, a rule disable, and a fresh user rule all take effect on reload. Documents the table in `rio.5.scd` next to the other config sections. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> (cherry picked from commit b6325e6)
Adds a sentinel test that fills an 8192-cell (MAX_SCAN_CELLS) logical line with content that frequently kicks off but rarely completes a match for every built-in rule, then asserts one select_at() stays under 50ms. Typical wall-clock is sub-millisecond; the 50× headroom is meant to catch obvious regressions (a per-click DFA recompile, an accidental O(n²)) without being CI-flaky. Alt-bypass from the plan is intentionally skipped — Alt+click is already bound to OSC 8 / hint activation, so the bypass would conflict with the existing modifier role. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> (cherry picked from commit a5a50c2)
Formatting-only; no behavior change. (CI `cargo fmt --check` gate.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Makes double-click selection URL- and token-aware so it stops truncating at
semantic boundary characters (
:in particular). Implements #1621.Details
Two layers in front of the existing semantic selection:
the whole link across soft-wrapped rows. The span walk lives in
crosswords::hyperlink::hyperlink_span_atand is now shared with the hint pathso both agree on link boundaries.
(URL,
file:line:col, UUID, IPv4, email, git SHA). The engine scans thesoft-wrapped logical line containing the click and returns the highest-precision
match covering it, falling back to the previous semantic behavior on no match.
URL precision sits above IPv4 so
https://192.168.1.1/adminselects whole.A
[smart-selection]config table lets users disable the engine, remove/overridebuilt-in rules, or append their own (e.g. JIRA tickets) with hot reload. The
runtime rule list is the merge of defaults and user
[[smart-selection.rules]]layered by name (same name replaces,
enabled = falseremoves, fresh nameappends); bad regexes log a warning and skip just that rule.
SmartSelectorownsthe compiled rules and the reload step, so a config change swaps the rule set
without rebuilding
Screen. Documented in rio.5.Testing
cargo test -p rio-backend— rule merge/disable/override on reload, plus aworst-case timing sentinel that fills an 8192-cell line and asserts one
select_at()stays well under budget.Closes #1621
🤖 Generated with Claude Code