























@@ -24,6 +24,39 @@ enum VoiceWakeTextUtils {
2424.filter { !$0.isEmpty }
2525}
262627+private static func isASCIIWordScalar(_ scalar: UnicodeScalar) -> Bool {
28+ scalar.isASCII && CharacterSet.alphanumerics.contains(scalar)
29+}
30+31+private static func requiresASCIIWordBoundaries(_ value: String) -> Bool {
32+ value.unicodeScalars.contains(where: self.isASCIIWordScalar)
33+}
34+35+private static func hasASCIIWordBoundaries(
36+ transcript: String,
37+ range: Range<String.Index>,
38+ trigger: String) -> Bool
39+{
40+guard self.requiresASCIIWordBoundaries(trigger) else { return true }
41+42+if range.lowerBound > transcript.startIndex {
43+let beforeIndex = transcript.index(before: range.lowerBound)
44+let beforeScalars = transcript[beforeIndex].unicodeScalars
45+if beforeScalars.contains(where: self.isASCIIWordScalar) {
46+return false
47+}
48+}
49+50+if range.upperBound < transcript.endIndex {
51+let afterScalars = transcript[range.upperBound].unicodeScalars
52+if afterScalars.contains(where: self.isASCIIWordScalar) {
53+return false
54+}
55+}
56+57+return true
58+}
59+2760private static func bestRawTriggerMatch(
2861 transcript: String,
2962 triggers: [String]) -> (range: Range<String.Index>, normalizedTrigger: String)?
@@ -34,21 +67,49 @@ enum VoiceWakeTextUtils {
3467let normalizedTokens = self.normalizedTriggerTokens(trigger)
3568guard !normalizedTokens.isEmpty else { continue }
3669let rawTrigger = trigger.trimmingCharacters(in: self.whitespaceAndPunctuation)
37-guard !rawTrigger.isEmpty,
70+let tokenCount = normalizedTokens.count
71+guard !rawTrigger.isEmpty else { continue }
72+73+var searchStart = transcript.startIndex
74+while searchStart < transcript.endIndex,
3875let range = transcript.range(
3976 of: rawTrigger,
40- options: [.caseInsensitive, .diacriticInsensitive, .widthInsensitive])
41-else { continue }
77+ options: [.caseInsensitive, .diacriticInsensitive, .widthInsensitive],
78+ range: searchStart..<transcript.endIndex)
79+{
80+defer {
81+ searchStart = transcript.index(after: range.lowerBound)
82+}
83+guard self.hasASCIIWordBoundaries(
84+ transcript: transcript,
85+ range: range,
86+ trigger: rawTrigger)
87+else { continue }
88+89+if let bestMatch {
90+if range.lowerBound > bestMatch.range.lowerBound { continue }
91+if range.lowerBound == bestMatch.range.lowerBound,
92+ tokenCount <= bestMatch.tokenCount
93+{
94+continue
95+}
96+}
429743-let tokenCount = normalizedTokens.count
44-if let bestMatch {
45-if range.lowerBound > bestMatch.range.lowerBound { continue }
46-if range.lowerBound == bestMatch.range.lowerBound, tokenCount <= bestMatch.tokenCount {
98+ bestMatch = (range, normalizedTokens.joined(separator: " "), tokenCount)
99+break
100+}
101+102+if let bestMatch,
103+ bestMatch.range.lowerBound == transcript.startIndex,
104+ bestMatch.tokenCount >= tokenCount
105+{
106+ // Earlier matches take precedence, so once we match from the
107+ // start there is no need to scan later triggers with fewer
108+ // tokens at the same offset.
109+if bestMatch.tokenCount > tokenCount {
47110continue
48111}
49112}
50-51- bestMatch = (range, normalizedTokens.joined(separator: " "), tokenCount)
52113}
5311454115return bestMatch.map { (range: $0.range, normalizedTrigger: $0.normalizedTrigger) }
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。