search method

Future<List<UDocSearchHit>> search(
  1. String query, {
  2. UDocSearchOptions options = const UDocSearchOptions(),
})

Implementation

Future<List<UDocSearchHit>> search(String query, {UDocSearchOptions options = const UDocSearchOptions()}) async {
  final UEpubBook? book = _book;
  final String trimmed = query.trim();
  if (book == null || trimmed.isEmpty) {
    clearSearch();
    return const <UDocSearchHit>[];
  }
  final int token = ++_searchToken;
  emit(value.copyWith(searchQuery: trimmed, isSearching: true, searchHits: const <UDocSearchHit>[], searchHitIndex: -1));
  final String needle = options.normalizePersian ? UDocText.forSearch(trimmed) : trimmed.toLowerCase();
  final List<UDocSearchHit> hits = <UDocSearchHit>[];
  for (int index = 0; index < book.chapterCount; index++) {
    if (token != _searchToken || isDisposed) return hits;
    final UEpubChapter chapter = await this.chapter(index);
    if (chapter.text.isEmpty) continue;
    final UDocNormalizedText normalized = options.normalizePersian
        ? UDocNormalizedText.build(chapter.text)
        : UDocNormalizedText(chapter.text.toLowerCase(), List<int>.generate(chapter.text.length, (int i) => i));
    for (final int position in UDocText.findAll(normalized.text, needle, wholeWord: options.wholeWord)) {
      final int start = normalized.sourceAt(position);
      final int end = normalized.sourceAt(position + needle.length - 1) + 1;
      final int snippetStart = start - 48 < 0 ? 0 : start - 48;
      final int snippetEnd = end + 48 > chapter.text.length ? chapter.text.length : end + 48;
      hits.add(
        UDocSearchHit(pageIndex: index, start: start, end: end, snippet: chapter.text.substring(snippetStart, snippetEnd).replaceAll("\n", " "), rects: const <Rect>[], matchIndex: hits.length),
      );
      if (hits.length >= options.maxHits) break;
    }
    liveHits.value = List<UDocSearchHit>.from(hits);
    if (hits.length >= options.maxHits) break;
  }
  if (token != _searchToken || isDisposed) return hits;
  emit(value.copyWith(searchHits: hits, isSearching: false, searchHitIndex: hits.isEmpty ? -1 : 0));
  return hits;
}