@@ -20,31 +20,38 @@ import UIKit
2020extension AttributedString {
2121
2222 public enum Checking : Hashable {
23+ /// 自定义范围
24+ case range( NSRange )
25+ /// 正则表达式
26+ case regex( String )
2327 #if os(iOS) || os(macOS)
2428 case action
2529 #endif
30+ ///
2631 case date
2732 case link
2833 case address
2934 case phoneNumber
3035 case transitInformation
31- case regex( String )
3236 }
3337}
3438
3539extension AttributedString . Checking {
3640
3741 public enum Result {
42+ /// 自定义范围
43+ case range( NSAttributedString )
44+ /// 正则表达式
45+ case regex( NSAttributedString )
3846 #if os(iOS) || os(macOS)
3947 case action( AttributedString . Action . Result )
4048 #endif
49+
4150 case date( Date )
4251 case link( URL )
4352 case address( Address )
4453 case phoneNumber( String )
4554 case transitInformation( TransitInformation )
46-
47- case regex( String )
4855 }
4956}
5057
@@ -89,45 +96,41 @@ public extension Array where Element == AttributedString.Checking {
8996extension AttributedString {
9097
9198 public mutating func add( attributes: [ Attribute ] , checkings: [ Checking ] = . defalut) {
99+ guard !attributes. isEmpty, !checkings. isEmpty else { return }
100+
92101 var temp : [ NSAttributedString . Key : Any ] = [ : ]
93102 attributes. forEach { temp. merge ( $0. attributes, uniquingKeysWith: { $1 } ) }
94103
95104 let matched = matching ( checkings)
96-
97105 let string = NSMutableAttributedString ( attributedString: value)
98- matched. forEach {
99- string. addAttributes ( temp, range: $0. 0 )
100- }
101-
106+ matched. forEach { string. addAttributes ( temp, range: $0. 0 ) }
102107 value = string
103108 }
104109
105110 public mutating func set( attributes: [ Attribute ] , checkings: [ Checking ] = . defalut) {
111+ guard !attributes. isEmpty, !checkings. isEmpty else { return }
112+
106113 var temp : [ NSAttributedString . Key : Any ] = [ : ]
107114 attributes. forEach { temp. merge ( $0. attributes, uniquingKeysWith: { $1 } ) }
108115
109116 let matched = matching ( checkings)
110-
111117 let string = NSMutableAttributedString ( attributedString: value)
112- matched. forEach {
113- string. setAttributes ( temp, range: $0. 0 )
114- }
115-
118+ matched. forEach { string. setAttributes ( temp, range: $0. 0 ) }
116119 value = string
117120 }
118121}
119122
120123extension AttributedString {
121124
122- /// 匹配检查 (Range 不会出现覆盖情况, 优先级 action > regex > other)
125+ /// 匹配检查 (Key 不会出现覆盖情况, 优先级 range > action > regex > other)
123126 /// - Parameter checkings: 检查类型
124127 /// - Returns: 匹配结果 (范围, 检查类型, 检查结果)
125128 func matching( _ checkings: [ Checking ] ) -> [ NSRange : ( Checking , Checking . Result ) ] {
126129 guard !checkings. isEmpty else {
127130 return [ : ]
128131 }
129132
130- let checkings = Set ( checkings)
133+ let checkings = checkings. filtered ( duplication : \ . self ) . sorted { $0 . order < $1 . order }
131134 var result : [ NSRange : ( Checking , Checking . Result ) ] = [ : ]
132135
133136 func contains( _ range: NSRange ) -> Bool {
@@ -140,53 +143,69 @@ extension AttributedString {
140143 return result. keys. contains ( where: { $0. overlap ( range) } )
141144 }
142145
143- // Actions
144- #if os(iOS) || os(macOS)
145- if checkings. contains ( . action) {
146- let actions : [ NSRange : AttributedString . Action ] = value. get ( . action)
147- for action in actions where !contains( action. key) {
148- result [ action. key] = ( . action, . action( value. get ( action. key) ) )
149- }
150- }
151- #endif
152-
153- // 正则表达式
154146 checkings. forEach { ( checking) in
155- guard case . regex( let string) = checking else { return }
156- guard let regex = try ? NSRegularExpression ( pattern: string, options: . caseInsensitive) else { return }
157-
158- let matches = regex. matches (
159- in: value. string,
160- options: . init( ) ,
161- range: . init( location: 0 , length: value. length)
162- )
163-
164- for match in matches where !contains( match. range) {
165- let substring = value. attributedSubstring ( from: match. range)
166- result [ match. range] = ( checking, . regex( substring. string) )
167- }
168- }
169-
170- // 数据检测器
171- if let detector = try ? NSDataDetector ( types: NSTextCheckingAllTypes) {
172- let matches = detector. matches (
173- in: value. string,
174- options: . init( ) ,
175- range: . init( location: 0 , length: value. length)
176- )
177-
178- for match in matches where !contains( match. range) {
179- guard let type = match. resultType. map ( ) else { continue }
180- guard checkings. contains ( type) else { continue }
181- guard let mapped = match. map ( ) else { continue }
182- result [ match. range] = ( type, mapped)
147+ switch checking {
148+ case . range( let range) where !contains( range) :
149+ let substring = value. attributedSubstring ( from: range)
150+ result [ range] = ( checking, . range( substring) )
151+
152+ case . regex( let string) :
153+ guard let regex = try ? NSRegularExpression ( pattern: string, options: . caseInsensitive) else { return }
154+
155+ let matches = regex. matches (
156+ in: value. string,
157+ options: . init( ) ,
158+ range: . init( location: 0 , length: value. length)
159+ )
160+
161+ for match in matches where !contains( match. range) {
162+ let substring = value. attributedSubstring ( from: match. range)
163+ result [ match. range] = ( checking, . regex( substring) )
164+ }
165+
166+ case . action:
167+ let actions : [ NSRange : AttributedString . Action ] = value. get ( . action)
168+ for action in actions where !contains( action. key) {
169+ result [ action. key] = ( . action, . action( value. get ( action. key) ) )
170+ }
171+
172+ case . date, . link, . address, . phoneNumber, . transitInformation:
173+ guard let detector = try ? NSDataDetector ( types: NSTextCheckingAllTypes) else { return }
174+
175+ let matches = detector. matches (
176+ in: value. string,
177+ options: . init( ) ,
178+ range: . init( location: 0 , length: value. length)
179+ )
180+
181+ for match in matches where !contains( match. range) {
182+ guard let type = match. resultType. map ( ) else { continue }
183+ guard checkings. contains ( type) else { continue }
184+ guard let mapped = match. map ( ) else { continue }
185+ result [ match. range] = ( type, mapped)
186+ }
187+
188+ default :
189+ break
183190 }
184191 }
185192
186193 return result
187194 }
188195}
189196
197+ fileprivate extension AttributedString . Checking {
198+
199+ var order : Int {
200+ switch self {
201+ case . range: return 0
202+ case . regex: return 1
203+ case . action: return 2
204+ default : return 3
205+ }
206+ }
207+ }
208+
190209fileprivate extension AttributedString . Checking {
191210
192211 func map( ) -> NSTextCheckingResult . CheckingType ? {
0 commit comments