Skip to content

feat(search): add fuzzy search with fzy-inspired scoring and position tracking#131

Open
danlylois-code wants to merge 3 commits into
floatboatai:mainfrom
danlylois-code:feat/search-fuzzy
Open

feat(search): add fuzzy search with fzy-inspired scoring and position tracking#131
danlylois-code wants to merge 3 commits into
floatboatai:mainfrom
danlylois-code:feat/search-fuzzy

Conversation

@danlylois-code

Copy link
Copy Markdown

Summary / 摘要

Add full fuzzy search support to the CodeMirror 6 search plugin: a 3-layer architecture spanning regex-bridge UI integration, fzy-inspired relevance scoring, and backtracking-based character position tracking. 104 tests pass, 0 lint errors.

Motivation / 背景与动机

  • Roadmap: P2「模糊搜索」listed in ROADMAP.zh.md
  • Users typing fuzzy queries (e.g. "fb""floatboat") get no matches with exact/regex search
  • Goal: position-weighted relevance ranking so best matches appear first

Changes / 变更

packages/plugin-search:

  • New interfaces: ScoredSearchMatch, DetailedFuzzyMatch
  • New option: SearchOptions.fuzzy (boolean)
  • Core engine: fuzzyToPattern() → regex bridge, computeFuzzyScore() → fzy-inspired scoring, findAllFuzzyMatchesDetailed() → backtracking matcher
  • Public APIs: findScoredFuzzyMatches() (ranked), findDetailedFuzzyMatches() (per-char positions)
  • UI: fuzzy toggle checkbox in search panel
  • Safety: 200-char length guard (ReDoS protection), 10K result cap

Architecture (3-Layer Design) / 架构设计(3层设计)

┌─────────────────────────────────────────────────┐
│ Layer 0: Regex Bridge (CM6 Integration)          │
│   fuzzyToPattern() → /f.*?t/gi → SearchQuery     │
│   Zero changes to CM6 internals                   │
├─────────────────────────────────────────────────┤
│ Layer A: Scored Search API                       │
│   findScoredFuzzyMatches() → ScoredSearchMatch[] │
│   fzy-inspired: prefix bonus, consecutivity,     │
│   word-boundary bonus, gap penalty                │
├─────────────────────────────────────────────────┤
│ Layer B: Detailed Position Tracking              │
│   findDetailedFuzzyMatches() → DetailedFuzzyMatch│
│   matchedIndices[i] = doc offset of query[i]     │
│   Enables per-character highlight decorations    │
└─────────────────────────────────────────────────┘

Design Decisions / 设计决策

Decision Rationale
Regex bridge instead of extending SearchQuery SearchQuery is frozen CM6 internal; regex conversion feeds existing pipeline with zero fork
Non-greedy .*? between query chars Finds shortest span containing all chars; greedy .* would span to last match
Greedy completion in backtracking matcher O(n) per start position vs exponential backtracking; leftmost-shortest matches regex bridge semantics
Separate Scored and Detailed APIs Interface segregation — most consumers skip matchedIndices allocation; upgrading is a type change
Fuzzy checkbox reset on updateQuery() SearchQuery has no fuzzy field; external updates carry no fuzzy intent — prevents stale UI state

Testing / 测试

  • pnpm test passes — 104 tests, 0 failures
  • Affected packages build (pnpm build) — success
  • New vitest cases: 82 new tests across 7 layers
    • v1 regex bridge & UI integration
    • Part A: fzy scoring (prefix bonus, consecutivity, gap penalty)
    • Part B: character position tracking (matchedIndices)
    • Boundary & edge cases (empty docs, Unicode, regex metacharacters)
    • Cross-API consistency (Scored vs Detailed alignment)
    • Performance benchmarks (50K doc <500ms)
    • Algorithmic invariants (monotonicity, determinism, subset properties)
  • Manual UI: Fuzzy checkbox renders and highlights matches in electron-demo

Scoring Algorithm Invariants Verified / 已验证计分算法的不变量

  • Prefix dominance: prefix match scores > any non-prefix match
  • Consecutive > gapped: identical chars with contiguity outscore same chars with gaps
  • Length monotonicity: shorter spans score ≥ longer spans at same positions
  • Determinism: same input → identical output across calls
  • Case-sensitive subset: CS_results ⊆ CI_results

Compliance / 合规自检

  • CLA signed ✅ — email verified on GitHub
  • AI disclosure: This PR was developed with AI assistance (CodeBuddy) used for code generation, test authoring, and documentation. Algorithm design, architecture decisions, and final review were performed by the human contributor.
  • New dependencies: none
  • No build artifacts committed
  • No secrets

Checklist / 检查清单

  • Title follows Conventional Commits
  • Public API changes update package README / types
  • Touched live-preview-table.ts — N/A
  • New capability / breaking change → OpenSpec proposal linked — N/A
  • Change aligns with project scope (GOVERNANCE.md §4)

Screenshots / Recordings · 截图或录屏 (UI changes)

  • feat_search-fuzzy_UI_change_1.png — Electron Demo 中 Fuzzy checkbox 及高亮效果
feat_search-fuzzy_UI_change_1
  • feat_search-fuzzy_API_testing.png — API 验证脚本控制台输出 (4/4 通过)
feat_search-fuzzy_API_testing

- Add fuzzy matching to SearchOptions (fzf-style character-by-character)

- Add fuzzy toggle checkbox to search panel UI

- Implement fuzzyToPattern() converting queries to non-greedy regex

- Support case-sensitive fuzzy search

- Escape regex-special characters in fuzzy queries

- Add 12 unit and UI integration tests

- Roadmap: P2 floatboatai#17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant