Skip to content

Bug #99: Search highlight missing in some TXT #26

@lllyys

Description

@lllyys

Bug #99: Search results not highlighted in some TXT files

Severity: Medium

Repro

  1. Open a TXT file with non-UTF-8 encoding (e.g., GBK, Big5) or a large file using the chunked reader
  2. Open search → search for a term that exists in the file
  3. Tap a search result to navigate to it
  4. Observe whether the matched text is visually highlighted

Expected

Search result text is highlighted with a yellow background (systemYellow at 0.4 alpha) for 3 seconds.

Actual

Navigation scrolls to approximately the right area, but no yellow highlight is visible on the matched text.

Root Cause (Suspected)

Multiple potential causes depending on the scenario:

1. Chunked reader offset translation:

  • The chunked reader (TXTChunkedReaderBridge) splits content into chunks
  • Search results use global UTF-16 offsets, but the chunked bridge needs chunk-local offsets
  • scrollToGlobalOffset() uses binary search to find the right chunk, but the highlight range may not be correctly translated from global to chunk-local coordinates

2. Encoding edge cases:

  • For non-UTF-8 files, TXTService.detectEncodingFromSample() detects encoding
  • Search indexing may use different text than what the bridge displays if encoding detection differs between index-time and display-time
  • UTF-16 offsets from search may not align with the displayed text's UTF-16 positions

3. isProgrammaticScroll guard timing:

  • The programmaticScrollCount counter (bug Bug #12: Toolbar cannot be hidden while reading #43 fix, issue 8) prevents highlight auto-clear during programmatic scrolls
  • If the counter decrements too early (0.3s delay) and the scroll takes longer to settle, the highlight gets cleared before it's visible
  • The HighlightingLayoutManager.drawBackground() only draws highlights within the visible glyph range — if scroll hasn't settled, range may be outside visible area

Architecture Reference

Highlight rendering pipeline:

  1. Search result tapped → Locator with charRangeStartUTF16/charRangeEndUTF16 posted via .readerNavigateToLocator
  2. Container view receives notification → sets highlightRange (NSRange) + scrollToOffset
  3. Bridge's updateUIView applies scroll and passes range to HighlightableTextView
  4. HighlightingLayoutManager.setHighlightRanges(persisted:active:) stores the range
  5. drawBackground(forGlyphRange:at:) draws yellow rect during layout pass
  6. After 3s, highlightClearTimer fires → clears highlightRange

Guard mechanism:

  • programmaticScrollCount (integer counter, not boolean) prevents overlapping scrolls from clearing highlights too early
  • Incremented on programmatic scroll, decremented 0.3s later
  • clearSearchHighlightIfTemporary() only clears when counter ≤ 0

Files

  • vreader/Views/Reader/TXTTextViewBridge.swift — highlight application + scroll + programmatic guard
  • vreader/Views/Reader/TXTTextViewBridgeCoordinator.swiftprogrammaticScrollCount, auto-clear logic
  • vreader/Views/Reader/HighlightableTextView.swiftHighlightingLayoutManager.drawBackground()
  • vreader/Views/Reader/TXTChunkedReaderBridge.swift — chunked reader highlight + offset translation
  • vreader/Services/TXT/TXTOffsetMapper.swift — UTF-16 offset mapping, surrogate-pair snapping
  • vreader/Views/Reader/TextHighlightRenderer.swift — locator → NSRange conversion

Refs #6

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:mediumMedium severity

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions