react-native-enriched 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +16 -17
  2. package/android/build.gradle +77 -72
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +21 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +7 -0
  5. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
  6. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
  7. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
  8. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +194 -0
  9. package/android/lint.gradle +70 -0
  10. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputConnectionWrapper.kt +140 -0
  11. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +304 -83
  12. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +3 -1
  13. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +166 -51
  14. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +1 -3
  15. package/android/src/main/java/com/swmansion/enriched/MeasurementStore.kt +70 -21
  16. package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +21 -11
  17. package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +8 -9
  18. package/android/src/main/java/com/swmansion/enriched/events/OnChangeSelectionEvent.kt +10 -9
  19. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateDeprecatedEvent.kt +21 -0
  20. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +9 -12
  21. package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +10 -10
  22. package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +7 -9
  23. package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +7 -9
  24. package/android/src/main/java/com/swmansion/enriched/events/OnInputKeyPressEvent.kt +27 -0
  25. package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +13 -11
  26. package/android/src/main/java/com/swmansion/enriched/events/OnMentionDetectedEvent.kt +10 -9
  27. package/android/src/main/java/com/swmansion/enriched/events/OnMentionEvent.kt +9 -8
  28. package/android/src/main/java/com/swmansion/enriched/events/OnRequestHtmlResultEvent.kt +32 -0
  29. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +24 -5
  30. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +8 -1
  31. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +10 -2
  32. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +8 -1
  33. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +8 -1
  34. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +8 -1
  35. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt +24 -0
  36. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt +24 -0
  37. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt +24 -0
  38. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +34 -17
  39. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +8 -1
  40. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +7 -1
  41. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +10 -4
  42. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +14 -11
  43. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +18 -11
  44. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +174 -72
  45. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +7 -1
  46. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +7 -1
  47. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +11 -5
  48. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +3 -2
  49. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +1 -2
  50. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +1 -2
  51. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +3 -2
  52. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +5 -0
  53. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +1 -2
  54. package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +2 -1
  55. package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +155 -20
  56. package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +25 -8
  57. package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +60 -20
  58. package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +161 -25
  59. package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +128 -52
  60. package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +10 -7
  61. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedConstants.kt +11 -0
  62. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedEditableFactory.kt +17 -0
  63. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +136 -87
  64. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSelection.kt +71 -42
  65. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +183 -48
  66. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannable.kt +82 -0
  67. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpannableStringBuilder.kt +15 -0
  68. package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +0 -70
  69. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +46 -14
  70. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +34 -11
  71. package/android/src/main/new_arch/CMakeLists.txt +6 -0
  72. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +6 -6
  73. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +6 -6
  74. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +19 -19
  75. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +40 -51
  76. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +13 -15
  77. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +23 -21
  78. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +35 -36
  79. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +4 -4
  80. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +13 -14
  81. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +33 -14
  82. package/ios/EnrichedTextInputView.h +26 -14
  83. package/ios/EnrichedTextInputView.mm +1209 -586
  84. package/ios/config/InputConfig.h +24 -6
  85. package/ios/config/InputConfig.mm +154 -38
  86. package/ios/{utils → extensions}/ColorExtension.mm +7 -5
  87. package/ios/extensions/FontExtension.mm +106 -0
  88. package/ios/{utils → extensions}/LayoutManagerExtension.h +1 -1
  89. package/ios/extensions/LayoutManagerExtension.mm +396 -0
  90. package/ios/{utils → extensions}/StringExtension.mm +19 -16
  91. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +156 -0
  92. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +147 -0
  93. package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +10 -0
  94. package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +194 -0
  95. package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +95 -0
  96. package/ios/inputParser/InputParser.h +5 -5
  97. package/ios/inputParser/InputParser.mm +864 -380
  98. package/ios/inputTextView/InputTextView.h +1 -1
  99. package/ios/inputTextView/InputTextView.mm +100 -59
  100. package/ios/{utils → interfaces}/BaseStyleProtocol.h +2 -2
  101. package/ios/interfaces/ImageAttachment.h +10 -0
  102. package/ios/interfaces/ImageAttachment.mm +36 -0
  103. package/ios/interfaces/LinkRegexConfig.h +19 -0
  104. package/ios/interfaces/LinkRegexConfig.mm +37 -0
  105. package/ios/interfaces/MediaAttachment.h +23 -0
  106. package/ios/interfaces/MediaAttachment.mm +31 -0
  107. package/ios/{utils → interfaces}/MentionParams.h +0 -1
  108. package/ios/{utils → interfaces}/MentionStyleProps.mm +27 -20
  109. package/ios/{utils → interfaces}/StyleHeaders.h +37 -15
  110. package/ios/{utils → interfaces}/StyleTypeEnum.h +3 -0
  111. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +11 -9
  112. package/ios/internals/EnrichedTextInputViewShadowNode.h +28 -25
  113. package/ios/internals/EnrichedTextInputViewShadowNode.mm +45 -40
  114. package/ios/internals/EnrichedTextInputViewState.h +3 -1
  115. package/ios/styles/BlockQuoteStyle.mm +189 -118
  116. package/ios/styles/BoldStyle.mm +110 -63
  117. package/ios/styles/CodeBlockStyle.mm +204 -128
  118. package/ios/styles/H1Style.mm +10 -4
  119. package/ios/styles/H2Style.mm +10 -4
  120. package/ios/styles/H3Style.mm +10 -4
  121. package/ios/styles/H4Style.mm +17 -0
  122. package/ios/styles/H5Style.mm +17 -0
  123. package/ios/styles/H6Style.mm +17 -0
  124. package/ios/styles/HeadingStyleBase.mm +148 -86
  125. package/ios/styles/ImageStyle.mm +75 -73
  126. package/ios/styles/InlineCodeStyle.mm +162 -88
  127. package/ios/styles/ItalicStyle.mm +76 -52
  128. package/ios/styles/LinkStyle.mm +411 -232
  129. package/ios/styles/MentionStyle.mm +363 -246
  130. package/ios/styles/OrderedListStyle.mm +171 -106
  131. package/ios/styles/StrikethroughStyle.mm +52 -35
  132. package/ios/styles/UnderlineStyle.mm +68 -46
  133. package/ios/styles/UnorderedListStyle.mm +169 -106
  134. package/ios/utils/OccurenceUtils.h +42 -42
  135. package/ios/utils/OccurenceUtils.mm +142 -119
  136. package/ios/utils/ParagraphAttributesUtils.h +10 -2
  137. package/ios/utils/ParagraphAttributesUtils.mm +182 -71
  138. package/ios/utils/ParagraphsUtils.h +2 -1
  139. package/ios/utils/ParagraphsUtils.mm +41 -27
  140. package/ios/utils/TextInsertionUtils.h +13 -2
  141. package/ios/utils/TextInsertionUtils.mm +38 -20
  142. package/ios/utils/WordsUtils.h +2 -1
  143. package/ios/utils/WordsUtils.mm +32 -22
  144. package/ios/utils/ZeroWidthSpaceUtils.h +3 -1
  145. package/ios/utils/ZeroWidthSpaceUtils.mm +145 -79
  146. package/lib/module/EnrichedTextInput.js +61 -2
  147. package/lib/module/EnrichedTextInput.js.map +1 -1
  148. package/lib/module/EnrichedTextInputNativeComponent.ts +149 -12
  149. package/lib/module/{normalizeHtmlStyle.js → utils/normalizeHtmlStyle.js} +12 -0
  150. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
  151. package/lib/module/utils/regexParser.js +46 -0
  152. package/lib/module/utils/regexParser.js.map +1 -0
  153. package/lib/typescript/src/EnrichedTextInput.d.ts +24 -14
  154. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  155. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +129 -12
  156. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
  157. package/lib/typescript/src/index.d.ts +1 -1
  158. package/lib/typescript/src/index.d.ts.map +1 -1
  159. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
  160. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
  161. package/lib/typescript/src/utils/regexParser.d.ts +3 -0
  162. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
  163. package/package.json +17 -6
  164. package/src/EnrichedTextInput.tsx +96 -13
  165. package/src/EnrichedTextInputNativeComponent.ts +149 -12
  166. package/src/index.tsx +2 -0
  167. package/src/{normalizeHtmlStyle.ts → utils/normalizeHtmlStyle.ts} +14 -2
  168. package/src/utils/regexParser.ts +56 -0
  169. package/ios/utils/FontExtension.mm +0 -91
  170. package/ios/utils/LayoutManagerExtension.mm +0 -286
  171. package/lib/module/normalizeHtmlStyle.js.map +0 -1
  172. package/lib/typescript/src/normalizeHtmlStyle.d.ts +0 -4
  173. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +0 -1
  174. package/ios/{utils → extensions}/ColorExtension.h +0 -0
  175. package/ios/{utils → extensions}/FontExtension.h +0 -0
  176. package/ios/{utils → extensions}/StringExtension.h +1 -1
  177. package/ios/{utils → interfaces}/ImageData.h +0 -0
  178. package/ios/{utils → interfaces}/ImageData.mm +0 -0
  179. package/ios/{utils → interfaces}/LinkData.h +0 -0
  180. package/ios/{utils → interfaces}/LinkData.mm +0 -0
  181. package/ios/{utils → interfaces}/MentionParams.mm +0 -0
  182. package/ios/{utils → interfaces}/MentionStyleProps.h +1 -1
  183. /package/ios/{utils → interfaces}/StylePair.h +0 -0
  184. /package/ios/{utils → interfaces}/StylePair.mm +0 -0
  185. /package/ios/{utils → interfaces}/TextDecorationLineEnum.h +0 -0
  186. /package/ios/{utils → interfaces}/TextDecorationLineEnum.mm +0 -0
@@ -1,21 +1,70 @@
1
- #import "StyleHeaders.h"
2
1
  #import "EnrichedTextInputView.h"
3
2
  #import "OccurenceUtils.h"
3
+ #import "StyleHeaders.h"
4
4
  #import "TextInsertionUtils.h"
5
5
  #import "UIView+React.h"
6
6
  #import "WordsUtils.h"
7
7
 
8
- // custom NSAttributedStringKeys to differentiate manually added and automatically detected links
8
+ // custom NSAttributedStringKeys to differentiate manually added and
9
+ // automatically detected links
9
10
  static NSString *const ManualLinkAttributeName = @"ManualLinkAttributeName";
10
- static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName";
11
+ static NSString *const AutomaticLinkAttributeName =
12
+ @"AutomaticLinkAttributeName";
11
13
 
12
14
  @implementation LinkStyle {
13
15
  EnrichedTextInputView *_input;
14
16
  }
15
17
 
16
- + (StyleType)getStyleType { return Link; }
18
+ + (StyleType)getStyleType {
19
+ return Link;
20
+ }
17
21
 
18
- + (BOOL)isParagraphStyle { return NO; }
22
+ + (BOOL)isParagraphStyle {
23
+ return NO;
24
+ }
25
+
26
+ + (NSRegularExpression *)fullRegex {
27
+ static NSRegularExpression *regex;
28
+ static dispatch_once_t onceToken;
29
+ dispatch_once(&onceToken, ^{
30
+ regex =
31
+ [NSRegularExpression regularExpressionWithPattern:
32
+ @"http(s)?:\\/\\/"
33
+ @"www\\.[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-"
34
+ @"z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
35
+ options:0
36
+ error:nullptr];
37
+ });
38
+ return regex;
39
+ }
40
+
41
+ + (NSRegularExpression *)wwwRegex {
42
+ static NSRegularExpression *regex;
43
+ static dispatch_once_t onceToken;
44
+ dispatch_once(&onceToken, ^{
45
+ regex =
46
+ [NSRegularExpression regularExpressionWithPattern:
47
+ @"www\\.[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-"
48
+ @"z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
49
+ options:0
50
+ error:nullptr];
51
+ });
52
+ return regex;
53
+ }
54
+
55
+ + (NSRegularExpression *)bareRegex {
56
+ static NSRegularExpression *regex;
57
+ static dispatch_once_t onceToken;
58
+ dispatch_once(&onceToken, ^{
59
+ regex =
60
+ [NSRegularExpression regularExpressionWithPattern:
61
+ @"[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-z]{2,"
62
+ @"6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
63
+ options:0
64
+ error:nullptr];
65
+ });
66
+ return regex;
67
+ }
19
68
 
20
69
  - (instancetype)initWithInput:(id)input {
21
70
  self = [super init];
@@ -27,7 +76,7 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
27
76
  // no-op for links
28
77
  }
29
78
 
30
- - (void)addAttributes:(NSRange)range {
79
+ - (void)addAttributes:(NSRange)range withTypingAttr:(BOOL)withTypingAttr {
31
80
  // no-op for links
32
81
  }
33
82
 
@@ -39,25 +88,39 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
39
88
  - (void)removeAttributes:(NSRange)range {
40
89
  NSArray<StylePair *> *links = [self findAllOccurences:range];
41
90
  [_input->textView.textStorage beginEditing];
42
- for(StylePair *pair in links) {
43
- NSRange linkRange = [self getFullLinkRangeAt:[pair.rangeValue rangeValue].location];
44
- [_input->textView.textStorage removeAttribute:ManualLinkAttributeName range:linkRange];
45
- [_input->textView.textStorage removeAttribute:AutomaticLinkAttributeName range:linkRange];
46
- [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:linkRange];
47
- [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:linkRange];
48
- [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:linkRange];
49
- if([_input->config linkDecorationLine] == DecorationUnderline) {
50
- [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:linkRange];
91
+ for (StylePair *pair in links) {
92
+ NSRange linkRange =
93
+ [self getFullLinkRangeAt:[pair.rangeValue rangeValue].location];
94
+ [_input->textView.textStorage removeAttribute:ManualLinkAttributeName
95
+ range:linkRange];
96
+ [_input->textView.textStorage removeAttribute:AutomaticLinkAttributeName
97
+ range:linkRange];
98
+ [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName
99
+ value:[_input->config primaryColor]
100
+ range:linkRange];
101
+ [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName
102
+ value:[_input->config primaryColor]
103
+ range:linkRange];
104
+ [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName
105
+ value:[_input->config primaryColor]
106
+ range:linkRange];
107
+ if ([_input->config linkDecorationLine] == DecorationUnderline) {
108
+ [_input->textView.textStorage
109
+ removeAttribute:NSUnderlineStyleAttributeName
110
+ range:linkRange];
51
111
  }
52
112
  }
53
113
  [_input->textView.textStorage endEditing];
54
-
114
+
55
115
  // adjust typing attributes as well
56
- NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
57
- newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
116
+ NSMutableDictionary *newTypingAttrs =
117
+ [_input->textView.typingAttributes mutableCopy];
118
+ newTypingAttrs[NSForegroundColorAttributeName] =
119
+ [_input->config primaryColor];
58
120
  newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
59
- newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
60
- if([_input->config linkDecorationLine] == DecorationUnderline) {
121
+ newTypingAttrs[NSStrikethroughColorAttributeName] =
122
+ [_input->config primaryColor];
123
+ if ([_input->config linkDecorationLine] == DecorationUnderline) {
61
124
  [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
62
125
  }
63
126
  _input->textView.typingAttributes = newTypingAttrs;
@@ -65,41 +128,56 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
65
128
 
66
129
  // used for conflicts, we have to remove the whole link
67
130
  - (void)removeTypingAttributes {
68
- NSRange linkRange = [self getFullLinkRangeAt:_input->textView.selectedRange.location];
131
+ NSRange linkRange =
132
+ [self getFullLinkRangeAt:_input->textView.selectedRange.location];
69
133
  [_input->textView.textStorage beginEditing];
70
- [_input->textView.textStorage removeAttribute:ManualLinkAttributeName range:linkRange];
71
- [_input->textView.textStorage removeAttribute:AutomaticLinkAttributeName range:linkRange];
72
- [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:linkRange];
73
- [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:linkRange];
74
- [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:linkRange];
75
- if([_input->config linkDecorationLine] == DecorationUnderline) {
76
- [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:linkRange];
134
+ [_input->textView.textStorage removeAttribute:ManualLinkAttributeName
135
+ range:linkRange];
136
+ [_input->textView.textStorage removeAttribute:AutomaticLinkAttributeName
137
+ range:linkRange];
138
+ [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName
139
+ value:[_input->config primaryColor]
140
+ range:linkRange];
141
+ [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName
142
+ value:[_input->config primaryColor]
143
+ range:linkRange];
144
+ [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName
145
+ value:[_input->config primaryColor]
146
+ range:linkRange];
147
+ if ([_input->config linkDecorationLine] == DecorationUnderline) {
148
+ [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName
149
+ range:linkRange];
77
150
  }
78
151
  [_input->textView.textStorage endEditing];
79
-
152
+
80
153
  // adjust typing attributes as well
81
- NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
82
- newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
154
+ NSMutableDictionary *newTypingAttrs =
155
+ [_input->textView.typingAttributes mutableCopy];
156
+ newTypingAttrs[NSForegroundColorAttributeName] =
157
+ [_input->config primaryColor];
83
158
  newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
84
- newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
85
- if([_input->config linkDecorationLine] == DecorationUnderline) {
159
+ newTypingAttrs[NSStrikethroughColorAttributeName] =
160
+ [_input->config primaryColor];
161
+ if ([_input->config linkDecorationLine] == DecorationUnderline) {
86
162
  [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
87
163
  }
88
164
  _input->textView.typingAttributes = newTypingAttrs;
89
165
  }
90
166
 
91
- - (BOOL)styleCondition:(id _Nullable)value :(NSRange)range {
167
+ - (BOOL)styleCondition:(id _Nullable)value range:(NSRange)range {
92
168
  NSString *linkValue = (NSString *)value;
93
169
  return linkValue != nullptr;
94
170
  }
95
171
 
96
172
  - (BOOL)detectStyle:(NSRange)range {
97
- if(range.length >= 1) {
98
- BOOL onlyLinks = [OccurenceUtils detectMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:range
99
- withCondition: ^BOOL(id _Nullable value, NSRange range) {
100
- return [self styleCondition:value :range];
101
- }
102
- ];
173
+ if (range.length >= 1) {
174
+ BOOL onlyLinks = [OccurenceUtils
175
+ detectMultiple:@[ ManualLinkAttributeName, AutomaticLinkAttributeName ]
176
+ withInput:_input
177
+ inRange:range
178
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
179
+ return [self styleCondition:value range:range];
180
+ }];
103
181
  return onlyLinks ? [self isSingleLinkIn:range] : NO;
104
182
  } else {
105
183
  return [self getLinkDataAt:range.location] != nullptr;
@@ -107,67 +185,90 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
107
185
  }
108
186
 
109
187
  - (BOOL)anyOccurence:(NSRange)range {
110
- return [OccurenceUtils anyMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:range
111
- withCondition:^BOOL(id _Nullable value, NSRange range) {
112
- return [self styleCondition:value :range];
113
- }
114
- ];
188
+ return [OccurenceUtils
189
+ anyMultiple:@[ ManualLinkAttributeName, AutomaticLinkAttributeName ]
190
+ withInput:_input
191
+ inRange:range
192
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
193
+ return [self styleCondition:value range:range];
194
+ }];
115
195
  }
116
196
 
117
197
  - (NSArray<StylePair *> *_Nullable)findAllOccurences:(NSRange)range {
118
- return [OccurenceUtils allMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:range
119
- withCondition:^BOOL(id _Nullable value, NSRange range) {
120
- return [self styleCondition:value :range];
121
- }
122
- ];
198
+ return [OccurenceUtils
199
+ allMultiple:@[ ManualLinkAttributeName, AutomaticLinkAttributeName ]
200
+ withInput:_input
201
+ inRange:range
202
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
203
+ return [self styleCondition:value range:range];
204
+ }];
123
205
  }
124
206
 
125
207
  // MARK: - Public non-standard methods
126
208
 
127
- - (void)addLink:(NSString*)text url:(NSString*)url range:(NSRange)range manual:(BOOL)manual {
128
- NSString *currentText = [_input->textView.textStorage.string substringWithRange:range];
129
-
130
- NSMutableDictionary<NSAttributedStringKey, id> *newAttrs = [[NSMutableDictionary<NSAttributedStringKey, id> alloc] init];
209
+ - (void)addLink:(NSString *)text
210
+ url:(NSString *)url
211
+ range:(NSRange)range
212
+ manual:(BOOL)manual
213
+ withSelection:(BOOL)withSelection {
214
+ NSString *currentText =
215
+ [_input->textView.textStorage.string substringWithRange:range];
216
+
217
+ NSMutableDictionary<NSAttributedStringKey, id> *newAttrs =
218
+ [[NSMutableDictionary<NSAttributedStringKey, id> alloc] init];
131
219
  newAttrs[NSForegroundColorAttributeName] = [_input->config linkColor];
132
220
  newAttrs[NSUnderlineColorAttributeName] = [_input->config linkColor];
133
221
  newAttrs[NSStrikethroughColorAttributeName] = [_input->config linkColor];
134
- if([_input->config linkDecorationLine] == DecorationUnderline) {
222
+ if ([_input->config linkDecorationLine] == DecorationUnderline) {
135
223
  newAttrs[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle);
136
224
  }
137
- if(manual) {
225
+ if (manual) {
138
226
  newAttrs[ManualLinkAttributeName] = [url copy];
139
227
  } else {
140
228
  newAttrs[AutomaticLinkAttributeName] = [url copy];
141
229
  }
142
-
143
- if(range.length == 0) {
230
+
231
+ if (range.length == 0) {
144
232
  // insert link
145
- [TextInsertionUtils insertText:text at:range.location additionalAttributes:newAttrs input:_input withSelection:YES];
146
- } else if([currentText isEqualToString:text]) {
233
+ [TextInsertionUtils insertText:text
234
+ at:range.location
235
+ additionalAttributes:newAttrs
236
+ input:_input
237
+ withSelection:withSelection];
238
+ } else if ([currentText isEqualToString:text]) {
147
239
  // apply link attributes
148
240
  [_input->textView.textStorage addAttributes:newAttrs range:range];
149
- // TextInsertionUtils take care of the selection but here we have to manually set it behind the link
150
- // ONLY with manual links, automatic ones don't need the selection fix
151
- if(manual) {
241
+ // TextInsertionUtils take care of the selection but here we have to
242
+ // manually set it behind the link ONLY with manual links, automatic ones
243
+ // don't need the selection fix
244
+ if (manual && withSelection) {
152
245
  [_input->textView reactFocus];
153
- _input->textView.selectedRange = NSMakeRange(range.location + text.length, 0);
246
+ _input->textView.selectedRange =
247
+ NSMakeRange(range.location + text.length, 0);
154
248
  }
155
249
  } else {
156
250
  // replace text with link
157
- [TextInsertionUtils replaceText:text at:range additionalAttributes:newAttrs input:_input withSelection:YES];
251
+ [TextInsertionUtils replaceText:text
252
+ at:range
253
+ additionalAttributes:newAttrs
254
+ input:_input
255
+ withSelection:withSelection];
158
256
  }
159
-
257
+
160
258
  // mandatory connected links check
161
- NSDictionary *currentWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:_input->textView.selectedRange];
162
- if(currentWord != nullptr) {
259
+ NSDictionary *currentWord =
260
+ [WordsUtils getCurrentWord:_input->textView.textStorage.string
261
+ range:_input->textView.selectedRange];
262
+ if (currentWord != nullptr) {
163
263
  // get word properties
164
264
  NSString *wordText = (NSString *)[currentWord objectForKey:@"word"];
165
265
  NSValue *wordRangeValue = (NSValue *)[currentWord objectForKey:@"range"];
166
- if(wordText != nullptr && wordRangeValue != nullptr) {
167
- [self removeConnectedLinksIfNeeded:wordText range:[wordRangeValue rangeValue]];
266
+ if (wordText != nullptr && wordRangeValue != nullptr) {
267
+ [self removeConnectedLinksIfNeeded:wordText
268
+ range:[wordRangeValue rangeValue]];
168
269
  }
169
270
  }
170
-
271
+
171
272
  [self manageLinkTypingAttributes];
172
273
  }
173
274
 
@@ -176,36 +277,37 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
176
277
  NSRange manualLinkRange = NSMakeRange(0, 0);
177
278
  NSRange automaticLinkRange = NSMakeRange(0, 0);
178
279
  NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
179
-
280
+
180
281
  // don't search at the very end of input
181
282
  NSUInteger searchLocation = location;
182
- if(searchLocation == _input->textView.textStorage.length) {
283
+ if (searchLocation == _input->textView.textStorage.length) {
183
284
  return nullptr;
184
285
  }
185
-
186
- NSString *manualUrl = [_input->textView.textStorage
187
- attribute:ManualLinkAttributeName
188
- atIndex:searchLocation
189
- longestEffectiveRange: &manualLinkRange
190
- inRange:inputRange
191
- ];
192
- NSString *automaticUrl = [_input->textView.textStorage
193
- attribute:AutomaticLinkAttributeName
194
- atIndex:searchLocation
195
- longestEffectiveRange: &automaticLinkRange
196
- inRange:inputRange
197
- ];
198
-
199
- if((manualUrl == nullptr && automaticUrl == nullptr) || (manualLinkRange.length == 0 && automaticLinkRange.length == 0)) {
286
+
287
+ NSString *manualUrl =
288
+ [_input->textView.textStorage attribute:ManualLinkAttributeName
289
+ atIndex:searchLocation
290
+ longestEffectiveRange:&manualLinkRange
291
+ inRange:inputRange];
292
+ NSString *automaticUrl =
293
+ [_input->textView.textStorage attribute:AutomaticLinkAttributeName
294
+ atIndex:searchLocation
295
+ longestEffectiveRange:&automaticLinkRange
296
+ inRange:inputRange];
297
+
298
+ if ((manualUrl == nullptr && automaticUrl == nullptr) ||
299
+ (manualLinkRange.length == 0 && automaticLinkRange.length == 0)) {
200
300
  return nullptr;
201
301
  }
202
-
302
+
203
303
  NSString *linkUrl = manualUrl == nullptr ? automaticUrl : manualUrl;
204
- NSRange linkRange = manualUrl == nullptr ? automaticLinkRange : manualLinkRange;
205
-
304
+ NSRange linkRange =
305
+ manualUrl == nullptr ? automaticLinkRange : manualLinkRange;
306
+
206
307
  LinkData *data = [[LinkData alloc] init];
207
308
  data.url = linkUrl;
208
- data.text = [_input->textView.textStorage.string substringWithRange:linkRange];
309
+ data.text =
310
+ [_input->textView.textStorage.string substringWithRange:linkRange];
209
311
  return data;
210
312
  }
211
313
 
@@ -214,64 +316,71 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
214
316
  NSRange manualLinkRange = NSMakeRange(0, 0);
215
317
  NSRange automaticLinkRange = NSMakeRange(0, 0);
216
318
  NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
217
-
319
+
218
320
  // get the previous index if possible when at the very end of input
219
321
  NSUInteger searchLocation = location;
220
- if(searchLocation == _input->textView.textStorage.length) {
221
- if(searchLocation == 0) {
322
+ if (searchLocation == _input->textView.textStorage.length) {
323
+ if (searchLocation == 0) {
222
324
  return NSMakeRange(0, 0);
223
325
  } else {
224
326
  searchLocation = searchLocation - 1;
225
327
  }
226
328
  }
227
-
228
- NSString *manualLink = [_input->textView.textStorage
229
- attribute:ManualLinkAttributeName
230
- atIndex:searchLocation
231
- longestEffectiveRange: &manualLinkRange
232
- inRange:inputRange
233
- ];
234
- NSString *automaticLink = [_input->textView.textStorage
235
- attribute:AutomaticLinkAttributeName
236
- atIndex:searchLocation
237
- longestEffectiveRange: &automaticLinkRange
238
- inRange:inputRange
239
- ];
240
-
329
+
330
+ NSString *manualLink =
331
+ [_input->textView.textStorage attribute:ManualLinkAttributeName
332
+ atIndex:searchLocation
333
+ longestEffectiveRange:&manualLinkRange
334
+ inRange:inputRange];
335
+ NSString *automaticLink =
336
+ [_input->textView.textStorage attribute:AutomaticLinkAttributeName
337
+ atIndex:searchLocation
338
+ longestEffectiveRange:&automaticLinkRange
339
+ inRange:inputRange];
340
+
241
341
  return manualLink == nullptr
242
- ? automaticLink == nullptr ? NSMakeRange(0, 0) : automaticLinkRange
243
- : manualLinkRange;
342
+ ? automaticLink == nullptr ? NSMakeRange(0, 0) : automaticLinkRange
343
+ : manualLinkRange;
244
344
  }
245
345
 
246
346
  - (void)manageLinkTypingAttributes {
247
- // link's typing attribtues need to be removed at ALL times whenever we have some link around
347
+ // link's typing attribtues need to be removed at ALL times whenever we have
348
+ // some link around
248
349
  BOOL removeAttrs = NO;
249
-
250
- if(_input->textView.selectedRange.length == 0) {
350
+
351
+ if (_input->textView.selectedRange.length == 0) {
251
352
  // check before
252
- if(_input->textView.selectedRange.location >= 1) {
253
- if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location - 1, 1)]) {
353
+ if (_input->textView.selectedRange.location >= 1) {
354
+ if ([self detectStyle:NSMakeRange(
355
+ _input->textView.selectedRange.location - 1,
356
+ 1)]) {
254
357
  removeAttrs = YES;
255
358
  }
256
359
  }
257
360
  // check after
258
- if(_input->textView.selectedRange.location < _input->textView.textStorage.length) {
259
- if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location, 1)]) {
361
+ if (_input->textView.selectedRange.location <
362
+ _input->textView.textStorage.length) {
363
+ if ([self detectStyle:NSMakeRange(_input->textView.selectedRange.location,
364
+ 1)]) {
260
365
  removeAttrs = YES;
261
366
  }
262
367
  }
263
368
  } else {
264
- if([self anyOccurence:_input->textView.selectedRange]) {
369
+ if ([self anyOccurence:_input->textView.selectedRange]) {
265
370
  removeAttrs = YES;
266
371
  }
267
372
  }
268
-
269
- if(removeAttrs) {
270
- NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
271
- newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
272
- newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
273
- newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
274
- if([_input->config linkDecorationLine] == DecorationUnderline) {
373
+
374
+ if (removeAttrs) {
375
+ NSMutableDictionary *newTypingAttrs =
376
+ [_input->textView.typingAttributes mutableCopy];
377
+ newTypingAttrs[NSForegroundColorAttributeName] =
378
+ [_input->config primaryColor];
379
+ newTypingAttrs[NSUnderlineColorAttributeName] =
380
+ [_input->config primaryColor];
381
+ newTypingAttrs[NSStrikethroughColorAttributeName] =
382
+ [_input->config primaryColor];
383
+ if ([_input->config linkDecorationLine] == DecorationUnderline) {
275
384
  [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
276
385
  }
277
386
  _input->textView.typingAttributes = newTypingAttrs;
@@ -280,88 +389,125 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
280
389
 
281
390
  // handles detecting and removing automatic links
282
391
  - (void)handleAutomaticLinks:(NSString *)word inRange:(NSRange)wordRange {
283
- InlineCodeStyle *inlineCodeStyle = [_input->stylesDict objectForKey:@([InlineCodeStyle getStyleType])];
284
- MentionStyle *mentionStyle = [_input->stylesDict objectForKey:@([MentionStyle getStyleType])];
285
- CodeBlockStyle *codeBlockStyle = [_input->stylesDict objectForKey:@([CodeBlockStyle getStyleType])];
286
-
392
+ LinkRegexConfig *linkRegexConfig = [_input->config linkRegexConfig];
393
+
394
+ // no automatic links with isDisabled
395
+ if (linkRegexConfig.isDisabled) {
396
+ return;
397
+ }
398
+
399
+ InlineCodeStyle *inlineCodeStyle =
400
+ [_input->stylesDict objectForKey:@([InlineCodeStyle getStyleType])];
401
+ MentionStyle *mentionStyle =
402
+ [_input->stylesDict objectForKey:@([MentionStyle getStyleType])];
403
+ CodeBlockStyle *codeBlockStyle =
404
+ [_input->stylesDict objectForKey:@([CodeBlockStyle getStyleType])];
405
+
287
406
  if (inlineCodeStyle == nullptr || mentionStyle == nullptr) {
288
407
  return;
289
408
  }
290
-
409
+
291
410
  // we don't recognize links along mentions
292
411
  if ([mentionStyle anyOccurence:wordRange]) {
293
412
  return;
294
413
  }
295
-
414
+
296
415
  // we don't recognize links among inline code
297
416
  if ([inlineCodeStyle anyOccurence:wordRange]) {
298
417
  return;
299
418
  }
300
-
419
+
301
420
  // we don't recognize links in codeblocks
302
421
  if ([codeBlockStyle anyOccurence:wordRange]) {
303
422
  return;
304
423
  }
305
-
424
+
306
425
  // remove connected different links
307
426
  [self removeConnectedLinksIfNeeded:word range:wordRange];
308
-
427
+
309
428
  // we don't recognize automatic links along manual ones
310
429
  __block BOOL manualLinkPresent = NO;
311
- [_input->textView.textStorage enumerateAttribute:ManualLinkAttributeName inRange:wordRange options:0
312
- usingBlock:^(id value, NSRange range, BOOL *stop) {
313
- NSString *urlValue = (NSString *)value;
314
- if(urlValue != nullptr) {
315
- manualLinkPresent = YES;
316
- *stop = YES;
317
- }
318
- }];
319
- if(manualLinkPresent) {
430
+ [_input->textView.textStorage
431
+ enumerateAttribute:ManualLinkAttributeName
432
+ inRange:wordRange
433
+ options:0
434
+ usingBlock:^(id value, NSRange range, BOOL *stop) {
435
+ NSString *urlValue = (NSString *)value;
436
+ if (urlValue != nullptr) {
437
+ manualLinkPresent = YES;
438
+ *stop = YES;
439
+ }
440
+ }];
441
+ if (manualLinkPresent) {
320
442
  return;
321
443
  }
322
-
323
- NSRegularExpression *fullRegex = [NSRegularExpression regularExpressionWithPattern:@"http(s)?:\\/\\/www\\.[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
324
- options:0
325
- error:nullptr
326
- ];
327
- NSRegularExpression *wwwRegex = [NSRegularExpression regularExpressionWithPattern:@"www\\.[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
328
- options:0
329
- error:nullptr
330
- ];
331
- NSRegularExpression *bareRegex = [NSRegularExpression regularExpressionWithPattern:@"[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
332
- options:0
333
- error:nullptr
334
- ];
335
-
444
+
445
+ // all conditions are met; try matching the word to a proper regex
446
+
336
447
  NSString *regexPassedUrl = nullptr;
337
-
338
- if ([fullRegex numberOfMatchesInString:word options:0 range:NSMakeRange(0, word.length)]) {
339
- regexPassedUrl = word;
340
- } else if ([wwwRegex numberOfMatchesInString:word options:0 range:NSMakeRange(0, word.length)]) {
341
- regexPassedUrl = word;
342
- } else if ([bareRegex numberOfMatchesInString:word options:0 range:NSMakeRange(0, word.length)]) {
343
- regexPassedUrl = word;
344
- } else if ([self anyOccurence:wordRange]) {
345
- // there was some automatic link (because anyOccurence is true and we are sure there are no manual links)
346
- // still, it didn't pass any regex - needs to be removed
347
- [self removeAttributes:wordRange];
448
+ NSRange matchingRange = NSMakeRange(0, word.length);
449
+
450
+ if (linkRegexConfig.isDefault) {
451
+ // use default regex
452
+ regexPassedUrl = [self tryMatchingDefaultLinkRegex:word
453
+ matchRange:matchingRange];
454
+ } else {
455
+ // use user defined regex if it exists
456
+ NSRegularExpression *userRegex = [_input->config parsedLinkRegex];
457
+
458
+ if (userRegex == nullptr) {
459
+ // fallback to default regex
460
+ regexPassedUrl = [self tryMatchingDefaultLinkRegex:word
461
+ matchRange:matchingRange];
462
+ } else if ([userRegex numberOfMatchesInString:word
463
+ options:0
464
+ range:matchingRange]) {
465
+ regexPassedUrl = word;
466
+ }
348
467
  }
349
-
350
- if(regexPassedUrl != nullptr) {
468
+
469
+ if (regexPassedUrl != nullptr) {
351
470
  // add style only if needed
352
471
  BOOL addStyle = YES;
353
- if([self detectStyle:wordRange]) {
472
+ if ([self detectStyle:wordRange]) {
354
473
  LinkData *currentData = [self getLinkDataAt:wordRange.location];
355
- if(currentData != nullptr && currentData.url != nullptr && [currentData.url isEqualToString:regexPassedUrl]) {
474
+ if (currentData != nullptr && currentData.url != nullptr &&
475
+ [currentData.url isEqualToString:regexPassedUrl]) {
356
476
  addStyle = NO;
357
477
  }
358
478
  }
359
- if(addStyle) {
360
- [self addLink:word url:regexPassedUrl range:wordRange manual:NO];
479
+ if (addStyle) {
480
+ [self addLink:word
481
+ url:regexPassedUrl
482
+ range:wordRange
483
+ manual:NO
484
+ withSelection:NO];
361
485
  // emit onLinkDetected if style was added
362
486
  [_input emitOnLinkDetectedEvent:word url:regexPassedUrl range:wordRange];
363
487
  }
488
+ } else if ([self anyOccurence:wordRange]) {
489
+ // there was some automatic link (because anyOccurence is true and we are
490
+ // sure there are no manual links) still, it didn't pass any regex - needs
491
+ // to be removed
492
+ [self removeAttributes:wordRange];
493
+ }
494
+ }
495
+
496
+ - (NSString *)tryMatchingDefaultLinkRegex:(NSString *)word
497
+ matchRange:(NSRange)range {
498
+ if ([[LinkStyle fullRegex] numberOfMatchesInString:word
499
+ options:0
500
+ range:range] ||
501
+ [[LinkStyle wwwRegex] numberOfMatchesInString:word
502
+ options:0
503
+ range:range] ||
504
+ [[LinkStyle bareRegex] numberOfMatchesInString:word
505
+ options:0
506
+ range:range]) {
507
+ return word;
364
508
  }
509
+
510
+ return nullptr;
365
511
  }
366
512
 
367
513
  // handles refreshing manual links
@@ -371,57 +517,83 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
371
517
  __block NSString *manualLinkMaxValue = @"";
372
518
  __block NSInteger manualLinkMinIdx = -1;
373
519
  __block NSInteger manualLinkMaxIdx = -1;
374
-
375
- [_input->textView.textStorage enumerateAttribute:ManualLinkAttributeName inRange:wordRange options:0
376
- usingBlock:^(id value, NSRange range, BOOL *stop) {
377
- NSString *urlValue = (NSString *)value;
378
- if (urlValue != nullptr) {
379
- NSInteger linkMin = range.location;
380
- NSInteger linkMax = range.location + range.length - 1;
381
- if (manualLinkMinIdx == -1 || linkMin < manualLinkMinIdx) {
382
- manualLinkMinIdx = linkMin;
383
- manualLinkMinValue = value;
384
- }
385
- if (manualLinkMaxIdx == -1 || linkMax > manualLinkMaxIdx) {
386
- manualLinkMaxIdx = linkMax;
387
- manualLinkMaxValue = value;
388
- }
389
- }
390
- }];
391
-
520
+
521
+ [_input->textView.textStorage
522
+ enumerateAttribute:ManualLinkAttributeName
523
+ inRange:wordRange
524
+ options:0
525
+ usingBlock:^(id value, NSRange range, BOOL *stop) {
526
+ NSString *urlValue = (NSString *)value;
527
+ if (urlValue != nullptr) {
528
+ NSInteger linkMin = range.location;
529
+ NSInteger linkMax = range.location + range.length - 1;
530
+ if (manualLinkMinIdx == -1 || linkMin < manualLinkMinIdx) {
531
+ manualLinkMinIdx = linkMin;
532
+ manualLinkMinValue = value;
533
+ }
534
+ if (manualLinkMaxIdx == -1 || linkMax > manualLinkMaxIdx) {
535
+ manualLinkMaxIdx = linkMax;
536
+ manualLinkMaxValue = value;
537
+ }
538
+ }
539
+ }];
540
+
392
541
  // no manual links
393
- if(manualLinkMinIdx == -1 || manualLinkMaxIdx == -1) {
542
+ if (manualLinkMinIdx == -1 || manualLinkMaxIdx == -1) {
394
543
  return;
395
544
  }
396
-
545
+
397
546
  // heuristic for refreshing manual links:
398
547
  // we update the Manual attribute between the bounds of existing ones
399
548
  // we do that only if the bounds point to the same url
400
- // this way manual link gets "extended" only if some characters were added inside it
401
- if([manualLinkMinValue isEqualToString:manualLinkMaxValue]) {
402
- NSRange newRange = NSMakeRange(manualLinkMinIdx, manualLinkMaxIdx - manualLinkMinIdx + 1);
403
- [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config linkColor] range:newRange];
404
- [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config linkColor] range:newRange];
405
- [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config linkColor] range:newRange];
406
- if([_input->config linkDecorationLine] == DecorationUnderline) {
407
- [_input->textView.textStorage addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:newRange];
549
+ // this way manual link gets "extended" only if some characters were added
550
+ // inside it
551
+ if ([manualLinkMinValue isEqualToString:manualLinkMaxValue]) {
552
+ NSRange newRange =
553
+ NSMakeRange(manualLinkMinIdx, manualLinkMaxIdx - manualLinkMinIdx + 1);
554
+ [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName
555
+ value:[_input->config linkColor]
556
+ range:newRange];
557
+ [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName
558
+ value:[_input->config linkColor]
559
+ range:newRange];
560
+ [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName
561
+ value:[_input->config linkColor]
562
+ range:newRange];
563
+ if ([_input->config linkDecorationLine] == DecorationUnderline) {
564
+ [_input->textView.textStorage addAttribute:NSUnderlineStyleAttributeName
565
+ value:@(NSUnderlineStyleSingle)
566
+ range:newRange];
408
567
  }
409
- [_input->textView.textStorage addAttribute:ManualLinkAttributeName value:manualLinkMinValue range:newRange];
568
+ [_input->textView.textStorage addAttribute:ManualLinkAttributeName
569
+ value:manualLinkMinValue
570
+ range:newRange];
410
571
  }
411
-
572
+
412
573
  // link typing attributes need to be fixed after these changes
413
574
  [self manageLinkTypingAttributes];
414
575
  }
415
576
 
416
- // replacing whole input (that starts with a link) with a manually typed letter improperly applies link's attributes to all the following text
417
- - (BOOL)handleLeadingLinkReplacement:(NSRange)range replacementText:(NSString *)text {
577
+ // replacing whole input (that starts with a link) with a manually typed letter
578
+ // improperly applies link's attributes to all the following text
579
+ - (BOOL)handleLeadingLinkReplacement:(NSRange)range
580
+ replacementText:(NSString *)text {
418
581
  // whole textView range gets replaced with a single letter
419
- if(_input->textView.textStorage.string.length > 0 && NSEqualRanges(range, NSMakeRange(0, _input->textView.textStorage.string.length)) && text.length == 1) {
582
+ if (_input->textView.textStorage.string.length > 0 &&
583
+ NSEqualRanges(
584
+ range, NSMakeRange(0, _input->textView.textStorage.string.length)) &&
585
+ text.length == 1) {
420
586
  // first character detection is enough for the removal to be done
421
- if([self detectStyle:NSMakeRange(0, 1)]) {
422
- [self removeAttributes:NSMakeRange(0, _input->textView.textStorage.string.length)];
587
+ if ([self detectStyle:NSMakeRange(0, 1)]) {
588
+ [self
589
+ removeAttributes:NSMakeRange(
590
+ 0, _input->textView.textStorage.string.length)];
423
591
  // do the replacing manually
424
- [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES];
592
+ [TextInsertionUtils replaceText:text
593
+ at:range
594
+ additionalAttributes:nullptr
595
+ input:_input
596
+ withSelection:YES];
425
597
  return YES;
426
598
  }
427
599
  }
@@ -437,32 +609,39 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
437
609
  }
438
610
 
439
611
  - (void)removeConnectedLinksIfNeeded:(NSString *)word range:(NSRange)wordRange {
440
- BOOL anyAutomatic = [OccurenceUtils any:AutomaticLinkAttributeName withInput:_input inRange:wordRange
441
- withCondition: ^BOOL(id _Nullable value, NSRange range) {
442
- return [self styleCondition:value :range];
443
- }
444
- ];
445
- BOOL anyManual = [OccurenceUtils any:ManualLinkAttributeName withInput:_input inRange:wordRange
446
- withCondition: ^BOOL(id _Nullable value, NSRange range) {
447
- return [self styleCondition:value :range];
448
- }
449
- ];
450
-
612
+ BOOL anyAutomatic =
613
+ [OccurenceUtils any:AutomaticLinkAttributeName
614
+ withInput:_input
615
+ inRange:wordRange
616
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
617
+ return [self styleCondition:value range:range];
618
+ }];
619
+ BOOL anyManual =
620
+ [OccurenceUtils any:ManualLinkAttributeName
621
+ withInput:_input
622
+ inRange:wordRange
623
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
624
+ return [self styleCondition:value range:range];
625
+ }];
626
+
451
627
  // both manual and automatic links are somewhere - delete!
452
- if(anyAutomatic && anyManual) {
628
+ if (anyAutomatic && anyManual) {
453
629
  [self removeAttributes:wordRange];
454
630
  [self manageLinkTypingAttributes];
455
631
  }
456
-
457
- // we are now sure there is only one type of link there - and make sure it covers the whole word
458
- BOOL onlyLinks = [OccurenceUtils detectMultiple:@[ManualLinkAttributeName, AutomaticLinkAttributeName] withInput:_input inRange:wordRange
459
- withCondition: ^BOOL(id _Nullable value, NSRange range) {
460
- return [self styleCondition:value :range];
461
- }
462
- ];
463
-
632
+
633
+ // we are now sure there is only one type of link there - and make sure it
634
+ // covers the whole word
635
+ BOOL onlyLinks = [OccurenceUtils
636
+ detectMultiple:@[ ManualLinkAttributeName, AutomaticLinkAttributeName ]
637
+ withInput:_input
638
+ inRange:wordRange
639
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
640
+ return [self styleCondition:value range:range];
641
+ }];
642
+
464
643
  // only one link might be present!
465
- if(onlyLinks && ![self isSingleLinkIn:wordRange]) {
644
+ if (onlyLinks && ![self isSingleLinkIn:wordRange]) {
466
645
  [self removeAttributes:wordRange];
467
646
  [self manageLinkTypingAttributes];
468
647
  }