url: drop digit-bridge requirement, leading (/+/0 alone is enough

`0731098515` is a real local-format phone (trunk-prefix 0, no
separators) but the previous rule required a digit-separator-digit
transition and rejected it. The leading-char gate + whitespace
boundary already filter the bare-digit false positives we cared about
(`1778840642934`, `order-…`, `smoke-original-…`), so drop the bridge
check.
This commit is contained in:
agra
2026-05-22 12:40:33 +03:00
parent 54da3ef01f
commit 3a2f6ef2e9

View File

@@ -254,7 +254,7 @@ bool _hasSigil(String text) {
// count. 64 is well above any real chat density.
const int _kMaxMatchesPerMessage = 64;
/// Drop phone candidates that don't look like real phones. Three checks:
/// Drop phone candidates that don't look like real phones. Two checks:
/// 1. The character immediately before the match (if any) must be
/// whitespace. Anything else — letter, digit, `-`, `:`, `/`, … —
/// means the digits are glued to surrounding text (identifiers
@@ -262,16 +262,13 @@ const int _kMaxMatchesPerMessage = 64;
/// 2. The slice must start with `(`, `+`, or `0`. International
/// numbers begin with `+`, US-style begins with `(area)`, most
/// domestic formats outside the US use a `0` trunk prefix.
/// 3. The slice must look formatted: either a leading `+` (E.164) or
/// a digit-separator-digit transition (`555-1234`). A bare digit
/// run with no internal separator stays unmatched.
List<UrlMatch> _tightenPhoneMatches(List<UrlMatch> matches, String text) {
if (matches.isEmpty) return matches;
final out = <UrlMatch>[];
for (final m in matches) {
if (m.kind == UrlMatchKind.phone &&
(!_phoneLeftBoundaryOk(text, m.start) ||
!_phoneSliceIsFormatted(text, m.start, m.end))) {
!_phoneLeadingCharOk(text, m.start, m.end))) {
continue;
}
out.add(m);
@@ -292,36 +289,14 @@ bool _phoneLeftBoundaryOk(String text, int start) {
c == 0x2029 /* paragraph separator */;
}
bool _phoneSliceIsFormatted(String text, int start, int end) {
bool _phoneLeadingCharOk(String text, int start, int end) {
final hi = end > text.length ? text.length : end;
final lo = start < 0 ? 0 : start;
if (lo >= hi) return false;
final first = text.codeUnitAt(lo);
if (first != 0x28 /* ( */ &&
first != 0x2B /* + */ &&
first != 0x30 /* 0 */) {
return false;
}
if (first == 0x2B /* + */) return true;
// 0 = pre-digit, 1 = digit run, 2 = separator after digit.
int state = 0;
for (int i = lo; i < hi; i++) {
final c = text.codeUnitAt(i);
final isDigit = c >= 0x30 && c <= 0x39;
final isSep = c == 0x20 /* space */ ||
c == 0x2D /* - */ ||
c == 0x28 /* ( */ ||
c == 0x29 /* ) */ ||
c == 0x2E /* . */;
if (state == 0 && isDigit) {
state = 1;
} else if (state == 1 && isSep) {
state = 2;
} else if (state == 2 && isDigit) {
return true;
}
}
return false;
return first == 0x28 /* ( */ ||
first == 0x2B /* + */ ||
first == 0x30 /* 0 */;
}
List<UrlMatch> _decode(Uint8List buf) {