LCOV - code coverage report
Current view: top level - src/script - script_filter.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 20 20
Test Date: 2026-08-24 07:45:26 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2026 The Authors.
       2              : //
       3              : // Licensed under the Apache License, Version 2.0 (the "License");
       4              : // you may not use this file except in compliance with the License.
       5              : // You may obtain a copy of the License at
       6              : //
       7              : //     https://www.apache.org/licenses/LICENSE-2.0
       8              : //
       9              : // Unless required by applicable law or agreed to in writing, software
      10              : // distributed under the License is distributed on an "AS IS" BASIS,
      11              : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : // See the License for the specific language governing permissions and
      13              : // limitations under the License.
      14              : 
      15              : import 'script_ranges.g.dart' show scriptOfRune;
      16              : 
      17              : /// Maximum number of runes examined from the start of the input.
      18              : ///
      19              : /// Bounds cost on very large inputs. This is a defensive cap, not a tuned
      20              : /// limit — text this package is expected to see (document titles, search
      21              : /// queries, vault chunks) is far shorter, so the cap is not expected to bind
      22              : /// in practice. Mirrors the sampling pattern in `betto_charset_detector`.
      23              : const int sampleRuneCap = 5000;
      24              : 
      25              : /// ISO 15924 code for the Unicode `Common` script property value — script-
      26              : /// neutral codepoints (whitespace, digits, punctuation, symbols) that carry
      27              : /// no language signal.
      28              : const String _commonScript = 'Zyyy';
      29              : 
      30              : /// ISO 15924 code for the Unicode `Inherited` script property value —
      31              : /// combining marks that inherit their script from the preceding base
      32              : /// character, also carrying no independent signal.
      33              : const String _inheritedScript = 'Zinh';
      34              : 
      35              : /// ISO 15924 code for the Hiragana script.
      36              : const String hiraganaScript = 'Hira';
      37              : 
      38              : /// ISO 15924 code for the Katakana script.
      39              : const String katakanaScript = 'Kana';
      40              : 
      41              : /// ISO 15924 code for the Han script (shared by Chinese and — together with
      42              : /// Hiragana/Katakana — Japanese).
      43              : const String hanScript = 'Hani';
      44              : 
      45              : /// The result of a single-pass scan of a text's Unicode scripts.
      46              : final class ScriptScan {
      47              :   /// The most common script among the input's non-neutral letter codepoints,
      48              :   /// or `null` if none were found (empty, whitespace-only,
      49              :   /// digits/punctuation/emoji-only input).
      50              :   final String? dominantScript;
      51              : 
      52              :   /// Whether any Hiragana or Katakana codepoint was present.
      53              :   ///
      54              :   /// Tracked independently of [dominantScript]'s tally because Japanese text
      55              :   /// is often Han-majority by raw codepoint count — kana presence, not the
      56              :   /// dominant-script count, is the decisive Japanese/Chinese signal.
      57              :   final bool hasKana;
      58              : 
      59            3 :   const ScriptScan({required this.dominantScript, required this.hasKana});
      60              : }
      61              : 
      62              : /// Scans up to [sampleRuneCap] runes of [text], returning both the dominant
      63              : /// script and whether any kana codepoint was present, from a single pass.
      64              : ///
      65              : /// Computing both signals together avoids scanning `text.runes` twice when a
      66              : /// caller (e.g. `CompositeBackend`) needs both.
      67            3 : ScriptScan scanScript(String text) {
      68            3 :   final tally = <String, int>{};
      69              :   var hasKana = false;
      70              :   var examined = 0;
      71              : 
      72            6 :   for (final rune in text.runes) {
      73            3 :     if (examined >= sampleRuneCap) break;
      74            3 :     examined++;
      75              : 
      76            3 :     final code = scriptOfRune(rune);
      77            6 :     if (code == null || code == _commonScript || code == _inheritedScript) {
      78              :       continue;
      79              :     }
      80            6 :     if (code == hiraganaScript || code == katakanaScript) {
      81              :       hasKana = true;
      82              :     }
      83            9 :     tally[code] = (tally[code] ?? 0) + 1;
      84              :   }
      85              : 
      86            3 :   if (tally.isEmpty) {
      87            3 :     return ScriptScan(dominantScript: null, hasKana: hasKana);
      88              :   }
      89              : 
      90            6 :   var bestCode = tally.keys.first;
      91            3 :   var bestCount = tally[bestCode]!;
      92            6 :   for (final entry in tally.entries) {
      93            6 :     if (entry.value > bestCount) {
      94            2 :       bestCode = entry.key;
      95            2 :       bestCount = entry.value;
      96              :     }
      97              :   }
      98            3 :   return ScriptScan(dominantScript: bestCode, hasKana: hasKana);
      99              : }
     100              : 
     101              : /// Cheap, script-only classification of [text].
     102              : ///
     103              : /// Returns a 4-letter ISO 15924 script code (e.g. `"Latn"`, `"Cyrl"`,
     104              : /// `"Hani"`) for the most common script among the input's letter codepoints,
     105              : /// or `null` if the input has no scripted letters.
     106              : ///
     107              : /// This is a convenience wrapper around [scanScript] for callers that only
     108              : /// need the dominant script, not the kana signal.
     109            6 : String? dominantScript(String text) => scanScript(text).dominantScript;
        

Generated by: LCOV version 2.0-1