react-native-enriched 0.0.0 → 0.1.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 (169) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +869 -0
  3. package/ReactNativeEnriched.podspec +27 -0
  4. package/android/build.gradle +101 -0
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +146 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +55 -0
  7. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ComponentDescriptors.cpp +22 -0
  8. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ComponentDescriptors.h +24 -0
  9. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +118 -0
  10. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +95 -0
  11. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +128 -0
  12. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +577 -0
  13. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ShadowNodes.cpp +17 -0
  14. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ShadowNodes.h +23 -0
  15. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/States.cpp +16 -0
  16. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/States.h +20 -0
  17. package/android/gradle.properties +5 -0
  18. package/android/src/main/AndroidManifest.xml +3 -0
  19. package/android/src/main/AndroidManifestNew.xml +2 -0
  20. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +535 -0
  21. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +64 -0
  22. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +292 -0
  23. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +19 -0
  24. package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +40 -0
  25. package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +28 -0
  26. package/android/src/main/java/com/swmansion/enriched/events/OnChangeSelectionEvent.kt +29 -0
  27. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +24 -0
  28. package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +30 -0
  29. package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +27 -0
  30. package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +27 -0
  31. package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +30 -0
  32. package/android/src/main/java/com/swmansion/enriched/events/OnMentionDetectedEvent.kt +29 -0
  33. package/android/src/main/java/com/swmansion/enriched/events/OnMentionEvent.kt +33 -0
  34. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +34 -0
  35. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +10 -0
  36. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +38 -0
  37. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +17 -0
  38. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +17 -0
  39. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +17 -0
  40. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +41 -0
  41. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +16 -0
  42. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +10 -0
  43. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +24 -0
  44. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +36 -0
  45. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +71 -0
  46. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +111 -0
  47. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +9 -0
  48. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +9 -0
  49. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +49 -0
  50. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +4 -0
  51. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +4 -0
  52. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +4 -0
  53. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +4 -0
  54. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +4 -0
  55. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +5 -0
  56. package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +227 -0
  57. package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +146 -0
  58. package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +173 -0
  59. package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +186 -0
  60. package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +223 -0
  61. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +857 -0
  62. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSelection.kt +285 -0
  63. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +204 -0
  64. package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +91 -0
  65. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +73 -0
  66. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +51 -0
  67. package/android/src/main/new_arch/CMakeLists.txt +56 -0
  68. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +22 -0
  69. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +26 -0
  70. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +35 -0
  71. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +51 -0
  72. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +26 -0
  73. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +34 -0
  74. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +54 -0
  75. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +9 -0
  76. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +25 -0
  77. package/ios/EnrichedTextInputView.h +33 -0
  78. package/ios/EnrichedTextInputView.mm +1190 -0
  79. package/ios/EnrichedTextInputViewManager.mm +13 -0
  80. package/ios/config/InputConfig.h +67 -0
  81. package/ios/config/InputConfig.mm +382 -0
  82. package/ios/generated/RNEnrichedTextInputViewSpec/ComponentDescriptors.cpp +22 -0
  83. package/ios/generated/RNEnrichedTextInputViewSpec/ComponentDescriptors.h +24 -0
  84. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +118 -0
  85. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +95 -0
  86. package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +128 -0
  87. package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +577 -0
  88. package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +384 -0
  89. package/ios/generated/RNEnrichedTextInputViewSpec/ShadowNodes.cpp +17 -0
  90. package/ios/generated/RNEnrichedTextInputViewSpec/ShadowNodes.h +23 -0
  91. package/ios/generated/RNEnrichedTextInputViewSpec/States.cpp +16 -0
  92. package/ios/generated/RNEnrichedTextInputViewSpec/States.h +20 -0
  93. package/ios/inputParser/InputParser.h +11 -0
  94. package/ios/inputParser/InputParser.mm +669 -0
  95. package/ios/inputTextView/InputTextView.h +6 -0
  96. package/ios/inputTextView/InputTextView.mm +115 -0
  97. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +17 -0
  98. package/ios/internals/EnrichedTextInputViewShadowNode.h +40 -0
  99. package/ios/internals/EnrichedTextInputViewShadowNode.mm +83 -0
  100. package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
  101. package/ios/internals/EnrichedTextInputViewState.h +20 -0
  102. package/ios/styles/BlockQuoteStyle.mm +248 -0
  103. package/ios/styles/BoldStyle.mm +122 -0
  104. package/ios/styles/H1Style.mm +10 -0
  105. package/ios/styles/H2Style.mm +10 -0
  106. package/ios/styles/H3Style.mm +10 -0
  107. package/ios/styles/HeadingStyleBase.mm +144 -0
  108. package/ios/styles/InlineCodeStyle.mm +163 -0
  109. package/ios/styles/ItalicStyle.mm +110 -0
  110. package/ios/styles/LinkStyle.mm +463 -0
  111. package/ios/styles/MentionStyle.mm +476 -0
  112. package/ios/styles/OrderedListStyle.mm +225 -0
  113. package/ios/styles/StrikethroughStyle.mm +80 -0
  114. package/ios/styles/UnderlineStyle.mm +112 -0
  115. package/ios/styles/UnorderedListStyle.mm +225 -0
  116. package/ios/utils/BaseStyleProtocol.h +16 -0
  117. package/ios/utils/ColorExtension.h +6 -0
  118. package/ios/utils/ColorExtension.mm +27 -0
  119. package/ios/utils/FontExtension.h +13 -0
  120. package/ios/utils/FontExtension.mm +91 -0
  121. package/ios/utils/LayoutManagerExtension.h +6 -0
  122. package/ios/utils/LayoutManagerExtension.mm +171 -0
  123. package/ios/utils/LinkData.h +9 -0
  124. package/ios/utils/LinkData.mm +4 -0
  125. package/ios/utils/MentionParams.h +9 -0
  126. package/ios/utils/MentionParams.mm +4 -0
  127. package/ios/utils/MentionStyleProps.h +13 -0
  128. package/ios/utils/MentionStyleProps.mm +56 -0
  129. package/ios/utils/OccurenceUtils.h +37 -0
  130. package/ios/utils/OccurenceUtils.mm +124 -0
  131. package/ios/utils/ParagraphsUtils.h +7 -0
  132. package/ios/utils/ParagraphsUtils.mm +54 -0
  133. package/ios/utils/StringExtension.h +15 -0
  134. package/ios/utils/StringExtension.mm +57 -0
  135. package/ios/utils/StyleHeaders.h +74 -0
  136. package/ios/utils/StylePair.h +9 -0
  137. package/ios/utils/StylePair.mm +4 -0
  138. package/ios/utils/StyleTypeEnum.h +22 -0
  139. package/ios/utils/TextDecorationLineEnum.h +6 -0
  140. package/ios/utils/TextDecorationLineEnum.mm +4 -0
  141. package/ios/utils/TextInsertionUtils.h +6 -0
  142. package/ios/utils/TextInsertionUtils.mm +48 -0
  143. package/ios/utils/WordsUtils.h +6 -0
  144. package/ios/utils/WordsUtils.mm +88 -0
  145. package/ios/utils/ZeroWidthSpaceUtils.h +7 -0
  146. package/ios/utils/ZeroWidthSpaceUtils.mm +164 -0
  147. package/lib/module/EnrichedTextInput.js +191 -0
  148. package/lib/module/EnrichedTextInput.js.map +1 -0
  149. package/lib/module/EnrichedTextInputNativeComponent.ts +235 -0
  150. package/lib/module/index.js +4 -0
  151. package/lib/module/index.js.map +1 -0
  152. package/lib/module/normalizeHtmlStyle.js +141 -0
  153. package/lib/module/normalizeHtmlStyle.js.map +1 -0
  154. package/lib/module/package.json +1 -0
  155. package/lib/typescript/package.json +1 -0
  156. package/lib/typescript/src/EnrichedTextInput.d.ts +113 -0
  157. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -0
  158. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +160 -0
  159. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -0
  160. package/lib/typescript/src/index.d.ts +3 -0
  161. package/lib/typescript/src/index.d.ts.map +1 -0
  162. package/lib/typescript/src/normalizeHtmlStyle.d.ts +4 -0
  163. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -0
  164. package/package.json +172 -1
  165. package/react-native.config.js +13 -0
  166. package/src/EnrichedTextInput.tsx +358 -0
  167. package/src/EnrichedTextInputNativeComponent.ts +235 -0
  168. package/src/index.tsx +9 -0
  169. package/src/normalizeHtmlStyle.ts +188 -0
@@ -0,0 +1,476 @@
1
+ #import "StyleHeaders.h"
2
+ #import "EnrichedTextInputView.h"
3
+ #import "OccurenceUtils.h"
4
+ #import "TextInsertionUtils.h"
5
+ #import "WordsUtils.h"
6
+ #import "UIView+React.h"
7
+
8
+ // custom NSAttributedStringKey to differentiate from links
9
+ static NSString *const MentionAttributeName = @"MentionAttributeName";
10
+
11
+ @implementation MentionStyle {
12
+ EnrichedTextInputView*_input;
13
+ NSValue *_activeMentionRange;
14
+ NSString *_activeMentionIndicator;
15
+ BOOL _blockMentionEditing;
16
+ }
17
+
18
+ + (StyleType)getStyleType { return Mention; }
19
+
20
+ - (instancetype)initWithInput:(id)input {
21
+ self = [super init];
22
+ _input = (EnrichedTextInputView *)input;
23
+ _activeMentionRange = nullptr;
24
+ _activeMentionIndicator = nullptr;
25
+ _blockMentionEditing = NO;
26
+ return self;
27
+ }
28
+
29
+ - (void)applyStyle:(NSRange)range {
30
+ // no-op for mentions
31
+ }
32
+
33
+ - (void)addAttributes:(NSRange)range {
34
+ // no-op for mentions
35
+ }
36
+
37
+ - (void)addTypingAttributes {
38
+ // no-op for mentions
39
+ }
40
+
41
+ // we have to make sure all mentions get removed properly
42
+ - (void)removeAttributes:(NSRange)range {
43
+ BOOL someMentionHadUnderline = NO;
44
+
45
+ NSArray<StylePair *> *mentions = [self findAllOccurences:range];
46
+ [_input->textView.textStorage beginEditing];
47
+ for(StylePair *pair in mentions) {
48
+ NSRange mentionRange = [self getFullMentionRangeAt:[pair.rangeValue rangeValue].location];
49
+ [_input->textView.textStorage removeAttribute:MentionAttributeName range:mentionRange];
50
+ [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:mentionRange];
51
+ [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:mentionRange];
52
+ [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:mentionRange];
53
+ [_input->textView.textStorage removeAttribute:NSBackgroundColorAttributeName range:mentionRange];
54
+
55
+ if([self stylePropsWithParams:pair.styleValue].decorationLine == DecorationUnderline) {
56
+ [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:mentionRange];
57
+ someMentionHadUnderline = YES;
58
+ }
59
+ }
60
+ [_input->textView.textStorage endEditing];
61
+
62
+ // remove typing attributes as well
63
+ NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
64
+ newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
65
+ newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
66
+ newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
67
+ [newTypingAttrs removeObjectForKey:NSBackgroundColorAttributeName];
68
+ if(someMentionHadUnderline) {
69
+ [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
70
+ }
71
+ _input->textView.typingAttributes = newTypingAttrs;
72
+ }
73
+
74
+ // used for conflicts, we have to remove the whole mention
75
+ - (void)removeTypingAttributes {
76
+ NSRange mentionRange = [self getFullMentionRangeAt:_input->textView.selectedRange.location];
77
+ [_input->textView.textStorage beginEditing];
78
+ [_input->textView.textStorage removeAttribute:MentionAttributeName range:mentionRange];
79
+ [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:mentionRange];
80
+ [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:mentionRange];
81
+ [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:mentionRange];
82
+ [_input->textView.textStorage removeAttribute:NSBackgroundColorAttributeName range:mentionRange];
83
+
84
+ MentionParams *params = [self getMentionParamsAt:mentionRange.location];
85
+ if([self stylePropsWithParams:params].decorationLine == DecorationUnderline) {
86
+ [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:mentionRange];
87
+ }
88
+ [_input->textView.textStorage endEditing];
89
+
90
+ // remove typing attributes as well
91
+ NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
92
+ newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
93
+ newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
94
+ newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
95
+ [newTypingAttrs removeObjectForKey:NSBackgroundColorAttributeName];
96
+ if([self stylePropsWithParams:params].decorationLine == DecorationUnderline) {
97
+ [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
98
+ }
99
+ _input->textView.typingAttributes = newTypingAttrs;
100
+ }
101
+
102
+ - (BOOL)styleCondition:(id _Nullable)value :(NSRange)range {
103
+ MentionParams *params = (MentionParams *)value;
104
+ return params != nullptr;
105
+ }
106
+
107
+ - (BOOL)detectStyle:(NSRange)range {
108
+ if(range.length >= 1) {
109
+ return [OccurenceUtils detect:MentionAttributeName withInput:_input inRange:range
110
+ withCondition: ^BOOL(id _Nullable value, NSRange range) {
111
+ return [self styleCondition:value :range];
112
+ }
113
+ ];
114
+ } else {
115
+ return [self getMentionParamsAt:range.location] != nullptr;
116
+ }
117
+ }
118
+
119
+ - (BOOL)anyOccurence:(NSRange)range {
120
+ return [OccurenceUtils any:MentionAttributeName withInput:_input inRange:range
121
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
122
+ return [self styleCondition:value :range];
123
+ }
124
+ ];
125
+ }
126
+
127
+ - (NSArray<StylePair *> *_Nullable)findAllOccurences:(NSRange)range {
128
+ return [OccurenceUtils all:MentionAttributeName withInput:_input inRange:range
129
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
130
+ return [self styleCondition:value :range];
131
+ }
132
+ ];
133
+ }
134
+
135
+ // MARK: - Public non-standard methods
136
+
137
+ - (void)addMention:(NSString *)indicator text:(NSString *)text attributes:(NSString *)attributes {
138
+ if(_activeMentionRange == nullptr) {
139
+ return;
140
+ }
141
+
142
+ // we block callbacks resulting from manageMentionEditing while we tamper with them here
143
+ _blockMentionEditing = YES;
144
+
145
+ MentionParams *params = [[MentionParams alloc] init];
146
+ params.text = text;
147
+ params.indicator = indicator;
148
+ params.attributes = attributes;
149
+
150
+ MentionStyleProps *styleProps = [_input->config mentionStylePropsForIndicator:indicator];
151
+
152
+ NSMutableDictionary *newAttrs = [@{
153
+ MentionAttributeName: params,
154
+ NSForegroundColorAttributeName: styleProps.color,
155
+ NSUnderlineColorAttributeName: styleProps.color,
156
+ NSStrikethroughColorAttributeName: styleProps.color,
157
+ NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaComponent:0.4],
158
+ } mutableCopy];
159
+
160
+ if(styleProps.decorationLine == DecorationUnderline) {
161
+ newAttrs[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle);
162
+ }
163
+
164
+ // add a single space after the mention
165
+ NSString *newText = [NSString stringWithFormat:@"%@ ", text];
166
+ NSRange rangeToBeReplaced = [_activeMentionRange rangeValue];
167
+ [TextInsertionUtils replaceText:newText at:rangeToBeReplaced additionalAttributes:nullptr input:_input withSelection:YES];
168
+
169
+ // THEN, add the attributes to not apply them on the space
170
+ [_input->textView.textStorage addAttributes:newAttrs range:NSMakeRange(rangeToBeReplaced.location, text.length)];
171
+
172
+ // mention editing should finish
173
+ [self removeActiveMentionRange];
174
+
175
+ // unlock editing
176
+ _blockMentionEditing = NO;
177
+ }
178
+
179
+ - (void)addMentionAtRange:(NSRange)range params:(MentionParams *)params {
180
+ _blockMentionEditing = YES;
181
+
182
+ MentionStyleProps *styleProps = [_input->config mentionStylePropsForIndicator:params.indicator];
183
+
184
+ NSMutableDictionary *newAttrs = [@{
185
+ MentionAttributeName: params,
186
+ NSForegroundColorAttributeName: styleProps.color,
187
+ NSUnderlineColorAttributeName: styleProps.color,
188
+ NSStrikethroughColorAttributeName: styleProps.color,
189
+ NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaComponent:0.4],
190
+ } mutableCopy];
191
+
192
+ if(styleProps.decorationLine == DecorationUnderline) {
193
+ newAttrs[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle);
194
+ }
195
+
196
+ [_input->textView.textStorage addAttributes:newAttrs range:range];
197
+
198
+ _blockMentionEditing = NO;
199
+ }
200
+
201
+ - (void)startMentionWithIndicator:(NSString *)indicator {
202
+ NSRange currentRange = _input->textView.selectedRange;
203
+
204
+ BOOL addSpaceBefore = NO;
205
+ BOOL addSpaceAfter = NO;
206
+
207
+ if(currentRange.location > 0) {
208
+ unichar charBefore = [_input->textView.textStorage.string characterAtIndex:(currentRange.location - 1)];
209
+ if(![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charBefore]) {
210
+ addSpaceBefore = YES;
211
+ }
212
+ }
213
+
214
+ if(currentRange.location + currentRange.length < _input->textView.textStorage.string.length) {
215
+ unichar charAfter = [_input->textView.textStorage.string characterAtIndex:(currentRange.location + currentRange.length)];
216
+ if(![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charAfter]) {
217
+ addSpaceAfter = YES;
218
+ }
219
+ }
220
+
221
+ NSString *finalString = [NSString stringWithFormat:@"%@%@%@",
222
+ addSpaceBefore ? @" " : @"",
223
+ indicator,
224
+ addSpaceAfter ? @" " : @""
225
+ ];
226
+
227
+ NSRange newSelect = NSMakeRange(currentRange.location + finalString.length + (addSpaceAfter ? -1 : 0), 0);
228
+
229
+ if(currentRange.length == 0) {
230
+ [TextInsertionUtils insertText:finalString at:currentRange.location additionalAttributes:nullptr input:_input withSelection:NO];
231
+ } else {
232
+ [TextInsertionUtils replaceText:finalString at:currentRange additionalAttributes:nullptr input:_input withSelection:NO];
233
+ }
234
+
235
+ [_input->textView reactFocus];
236
+ _input->textView.selectedRange = newSelect;
237
+ }
238
+
239
+ // handles removing no longer valid mentions
240
+ - (void)handleExistingMentions {
241
+ // unfortunately whole text needs to be checked for them
242
+ // checking the modified words doesn't work because mention's text can have any number of spaces, which makes one mention any number of words long
243
+
244
+ NSRange wholeText = NSMakeRange(0, _input->textView.textStorage.string.length);
245
+ // get menntions in ascending range.location order
246
+ NSArray<StylePair *> *mentions = [[self findAllOccurences:wholeText] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
247
+ NSRange range1 = [((StylePair *)obj1).rangeValue rangeValue];
248
+ NSRange range2 = [((StylePair *)obj2).rangeValue rangeValue];
249
+ if(range1.location < range2.location) {
250
+ return NSOrderedAscending;
251
+ } else {
252
+ return NSOrderedDescending;
253
+ }
254
+ }];
255
+
256
+ // set of ranges to have their mentions removed - aren't valid anymore
257
+ NSMutableSet<NSValue *> *rangesToRemove = [[NSMutableSet alloc] init];
258
+
259
+ for(NSInteger i = 0; i < mentions.count; i++) {
260
+ StylePair *mention = mentions[i];
261
+ NSRange currentRange = [mention.rangeValue rangeValue];
262
+ NSString *currentText = ((MentionParams *)mention.styleValue).text;
263
+ // check locations with the previous mention if it exists - if they got merged they need to be removed
264
+ if(i > 0) {
265
+ NSRange prevRange = [((StylePair*)mentions[i-1]).rangeValue rangeValue];
266
+ // mentions merged - both need to go out
267
+ if(prevRange.location + prevRange.length == currentRange.location) {
268
+ [rangesToRemove addObject:[NSValue valueWithRange:prevRange]];
269
+ [rangesToRemove addObject:[NSValue valueWithRange:currentRange]];
270
+ continue;
271
+ }
272
+ }
273
+
274
+ // check for text, any modifications to it makes mention invalid
275
+ NSString *existingText = [_input->textView.textStorage.string substringWithRange:currentRange];
276
+ if(![existingText isEqualToString:currentText]) {
277
+ [rangesToRemove addObject:[NSValue valueWithRange:currentRange]];
278
+ }
279
+ }
280
+
281
+ for(NSValue *value in rangesToRemove) {
282
+ [self removeAttributes:[value rangeValue]];
283
+ }
284
+ }
285
+
286
+ // manages active mention range, which in turn emits proper onMention event
287
+ - (void)manageMentionEditing {
288
+ // no actions performed when block is active
289
+ if(_blockMentionEditing) {
290
+ return;
291
+ }
292
+
293
+ // we don't take longer selections into consideration
294
+ if(_input->textView.selectedRange.length > 0) {
295
+ [self removeActiveMentionRange];
296
+ return;
297
+ }
298
+
299
+ // get the current word if it exists
300
+ // we can be using current word only thanks to the fact that ongoing mentions are always one word (in contrast to ready, added mentions)
301
+ NSDictionary *currentWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:_input->textView.selectedRange];
302
+ if(currentWord == nullptr) {
303
+ [self removeActiveMentionRange];
304
+ return;
305
+ }
306
+
307
+ // get word properties
308
+ NSString *wordText = (NSString *)[currentWord objectForKey:@"word"];
309
+ NSValue *wordRangeValue = (NSValue *)[currentWord objectForKey:@"range"];
310
+ if(wordText == nullptr || wordRangeValue == nullptr) {
311
+ [self removeActiveMentionRange];
312
+ return;
313
+ }
314
+ NSRange wordRange = [wordRangeValue rangeValue];
315
+
316
+ // check for mentionIndicators - no sign of them means we shouldn't be editing a mention
317
+ unichar firstChar = [wordText characterAtIndex:0];
318
+ if(![[_input->config mentionIndicators] containsObject: @(firstChar)]) {
319
+ [self removeActiveMentionRange];
320
+ return;
321
+ }
322
+
323
+ // check for existing mentions - we don't edit them
324
+ if([self detectStyle:wordRange]) {
325
+ [self removeActiveMentionRange];
326
+ return;
327
+ }
328
+
329
+ // get conflicting style classes
330
+ LinkStyle* linkStyle = [_input->stylesDict objectForKey:@([LinkStyle getStyleType])];
331
+ InlineCodeStyle* inlineCodeStyle = [_input->stylesDict objectForKey:@([InlineCodeStyle getStyleType])];
332
+ if(linkStyle == nullptr || inlineCodeStyle == nullptr) {
333
+ [self removeActiveMentionRange];
334
+ return;
335
+ }
336
+
337
+ // if there is any sign of conflicting style classes, stop editing a mention
338
+ if([linkStyle anyOccurence:wordRange] || [inlineCodeStyle anyOccurence:wordRange]) {
339
+ [self removeActiveMentionRange];
340
+ return;
341
+ }
342
+
343
+ // everything checks out - we are indeed editing a mention
344
+ [self setActiveMentionRange:wordRange text:wordText];
345
+ }
346
+
347
+ // used to fix mentions' typing attributes
348
+ - (void)manageMentionTypingAttributes {
349
+ // same as with links, mentions' typing attributes need to be constantly removed whenever we are somewhere near
350
+ BOOL removeAttrs = NO;
351
+ MentionParams *params;
352
+
353
+ if(_input->textView.selectedRange.length == 0) {
354
+ // check before
355
+ if(_input->textView.selectedRange.location >= 1) {
356
+ if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location - 1, 1)]) {
357
+ removeAttrs = YES;
358
+ params = [self getMentionParamsAt:_input->textView.selectedRange.location - 1];
359
+ }
360
+ }
361
+ // check after
362
+ if(_input->textView.selectedRange.location < _input->textView.textStorage.length) {
363
+ if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location, 1)]) {
364
+ removeAttrs = YES;
365
+ params = [self getMentionParamsAt:_input->textView.selectedRange.location];
366
+ }
367
+ }
368
+ } else {
369
+ if([self anyOccurence:_input->textView.selectedRange]) {
370
+ removeAttrs = YES;
371
+ }
372
+ }
373
+
374
+ if(removeAttrs) {
375
+ NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
376
+ newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
377
+ newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
378
+ newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
379
+ [newTypingAttrs removeObjectForKey:NSBackgroundColorAttributeName];
380
+ if([self stylePropsWithParams:params].decorationLine == DecorationUnderline) {
381
+ [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
382
+ }
383
+ _input->textView.typingAttributes = newTypingAttrs;
384
+ }
385
+ }
386
+
387
+ // replacing whole input (that starts with a mention) with a manually typed letter improperly applies mention's attributes to all the following text
388
+ - (BOOL)handleLeadingMentionReplacement:(NSRange)range replacementText:(NSString *)text {
389
+ // whole textView range gets replaced with a single letter
390
+ if(_input->textView.textStorage.string.length > 0 && NSEqualRanges(range, NSMakeRange(0, _input->textView.textStorage.string.length)) && text.length == 1) {
391
+ // first character detection is enough for the removal to be done
392
+ if([self detectStyle:NSMakeRange(0, 1)]) {
393
+ [self removeAttributes:NSMakeRange(0, _input->textView.textStorage.string.length)];
394
+ // do the replacing manually
395
+ [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES];
396
+ return YES;
397
+ }
398
+ }
399
+ return NO;
400
+ }
401
+
402
+ // returns mention params if it exists
403
+ - (MentionParams *)getMentionParamsAt:(NSUInteger)location {
404
+ NSRange mentionRange = NSMakeRange(0, 0);
405
+ NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
406
+
407
+ // don't search at the very end of input
408
+ NSUInteger searchLocation = location;
409
+ if(searchLocation == _input->textView.textStorage.length) {
410
+ return nullptr;
411
+ }
412
+
413
+ MentionParams *value = [_input->textView.textStorage
414
+ attribute:MentionAttributeName
415
+ atIndex:searchLocation
416
+ longestEffectiveRange: &mentionRange
417
+ inRange:inputRange
418
+ ];
419
+ return value;
420
+ }
421
+
422
+ - (NSValue *)getActiveMentionRange {
423
+ return _activeMentionRange;
424
+ }
425
+
426
+ // returns full range of a mention at some location
427
+ - (NSRange)getFullMentionRangeAt:(NSUInteger)location {
428
+ NSRange mentionRange = NSMakeRange(0, 0);
429
+ NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
430
+
431
+ // get the previous index if possible when at the very end of input
432
+ NSUInteger searchLocation = location;
433
+ if(searchLocation == _input->textView.textStorage.length) {
434
+ if(searchLocation == 0) {
435
+ return mentionRange;
436
+ } else {
437
+ searchLocation = searchLocation - 1;
438
+ }
439
+ }
440
+
441
+ [_input->textView.textStorage
442
+ attribute:MentionAttributeName
443
+ atIndex:searchLocation
444
+ longestEffectiveRange: &mentionRange
445
+ inRange:inputRange
446
+ ];
447
+ return mentionRange;
448
+ }
449
+
450
+ // MARK: - Private non-standard methods
451
+
452
+ - (MentionStyleProps *)stylePropsWithParams:(MentionParams *)params {
453
+ return [_input->config mentionStylePropsForIndicator:params.indicator];
454
+ }
455
+
456
+ // both used for setting the active mention range + indicator and fires proper onMention event
457
+ - (void)setActiveMentionRange:(NSRange)range text:(NSString *)text {
458
+ NSString *indicatorString = [NSString stringWithFormat:@"%C", [text characterAtIndex:0]];
459
+ NSString *textString = [text substringWithRange:NSMakeRange(1, text.length - 1)];
460
+ _activeMentionIndicator = indicatorString;
461
+ _activeMentionRange = [NSValue valueWithRange:range];
462
+ [_input emitOnMentionEvent:indicatorString text:textString];
463
+ }
464
+
465
+ // removes stored mention range + indicator, which means that we no longer edit a mention and onMention event gets fired
466
+ - (void)removeActiveMentionRange {
467
+ if(_activeMentionIndicator != nullptr && _activeMentionRange != nullptr) {
468
+ NSString *indicatorCopy = [_activeMentionIndicator copy];
469
+ _activeMentionIndicator = nullptr;
470
+ _activeMentionRange = nullptr;
471
+ [_input emitOnMentionEvent:indicatorCopy text:nullptr];
472
+ }
473
+ }
474
+
475
+ @end
476
+
@@ -0,0 +1,225 @@
1
+ #import "StyleHeaders.h"
2
+ #import "EnrichedTextInputView.h"
3
+ #import "FontExtension.h"
4
+ #import "OccurenceUtils.h"
5
+ #import "ParagraphsUtils.h"
6
+ #import "TextInsertionUtils.h"
7
+
8
+ @implementation OrderedListStyle {
9
+ EnrichedTextInputView *_input;
10
+ }
11
+
12
+ + (StyleType)getStyleType { return OrderedList; }
13
+
14
+ - (CGFloat)getHeadIndent {
15
+ // lists are drawn manually
16
+ // margin before marker + gap between marker and paragraph
17
+ return [_input->config orderedListMarginLeft] + [_input->config orderedListGapWidth];
18
+ }
19
+
20
+ - (instancetype)initWithInput:(id)input {
21
+ self = [super init];
22
+ _input = (EnrichedTextInputView *)input;
23
+ return self;
24
+ }
25
+
26
+ - (void)applyStyle:(NSRange)range {
27
+ BOOL isStylePresent = [self detectStyle:range];
28
+ if(range.length >= 1) {
29
+ isStylePresent ? [self removeAttributes:range] : [self addAttributes:range];
30
+ } else {
31
+ isStylePresent ? [self removeTypingAttributes] : [self addTypingAttributes];
32
+ }
33
+ }
34
+
35
+ // we assume correct paragraph range is already given
36
+ - (void)addAttributes:(NSRange)range {
37
+ NSTextList *numberBullet = [[NSTextList alloc] initWithMarkerFormat:NSTextListMarkerDecimal options:0];
38
+ NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:_input->textView range:range];
39
+ // if we fill empty lines with zero width spaces, we need to offset later ranges
40
+ NSInteger offset = 0;
41
+ // needed for range adjustments
42
+ NSRange preModificationRange = _input->textView.selectedRange;
43
+
44
+ // let's not emit some weird selection changes or text/html changes
45
+ _input->blockEmitting = YES;
46
+
47
+ for(NSValue *value in paragraphs) {
48
+ // take previous offsets into consideration
49
+ NSRange fixedRange = NSMakeRange([value rangeValue].location + offset, [value rangeValue].length);
50
+
51
+ // length 0 with first line, length 1 and newline with some empty lines in the middle
52
+ if(fixedRange.length == 0 ||
53
+ (fixedRange.length == 1 &&
54
+ [[NSCharacterSet newlineCharacterSet] characterIsMember: [_input->textView.textStorage.string characterAtIndex:fixedRange.location]])
55
+ ) {
56
+ [TextInsertionUtils insertText:@"\u200B" at:fixedRange.location additionalAttributes:nullptr input:_input withSelection:NO];
57
+ fixedRange = NSMakeRange(fixedRange.location, fixedRange.length + 1);
58
+ offset += 1;
59
+ }
60
+
61
+ [_input->textView.textStorage enumerateAttribute:NSParagraphStyleAttributeName inRange:fixedRange options:0
62
+ usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
63
+ NSMutableParagraphStyle *pStyle = [(NSParagraphStyle *)value mutableCopy];
64
+ pStyle.textLists = @[numberBullet];
65
+ pStyle.headIndent = [self getHeadIndent];
66
+ pStyle.firstLineHeadIndent = [self getHeadIndent];
67
+ [_input->textView.textStorage addAttribute:NSParagraphStyleAttributeName value:pStyle range:range];
68
+ }
69
+ ];
70
+ }
71
+
72
+ // back to emitting
73
+ _input->blockEmitting = NO;
74
+
75
+ if(preModificationRange.length == 0) {
76
+ // fix selection if only one line was possibly made a list and filled with a space
77
+ _input->textView.selectedRange = preModificationRange;
78
+ } else {
79
+ // in other cases, fix the selection with newly made offsets
80
+ _input->textView.selectedRange = NSMakeRange(preModificationRange.location, preModificationRange.length + offset);
81
+ }
82
+
83
+ // also add typing attributes
84
+ NSMutableDictionary *typingAttrs = [_input->textView.typingAttributes mutableCopy];
85
+ NSMutableParagraphStyle *pStyle = [typingAttrs[NSParagraphStyleAttributeName] mutableCopy];
86
+ pStyle.textLists = @[numberBullet];
87
+ pStyle.headIndent = [self getHeadIndent];
88
+ pStyle.firstLineHeadIndent = [self getHeadIndent];
89
+ typingAttrs[NSParagraphStyleAttributeName] = pStyle;
90
+ _input->textView.typingAttributes = typingAttrs;
91
+ }
92
+
93
+ // does pretty much the same as normal addAttributes, just need to get the range
94
+ - (void)addTypingAttributes {
95
+ [self addAttributes:_input->textView.selectedRange];
96
+ }
97
+
98
+ - (void)removeAttributes:(NSRange)range {
99
+ NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:_input->textView range:range];
100
+
101
+ [_input->textView.textStorage beginEditing];
102
+
103
+ for(NSValue *value in paragraphs) {
104
+ NSRange range = [value rangeValue];
105
+ [_input->textView.textStorage enumerateAttribute:NSParagraphStyleAttributeName inRange:range options:0
106
+ usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
107
+ NSMutableParagraphStyle *pStyle = [(NSParagraphStyle *)value mutableCopy];
108
+ pStyle.textLists = @[];
109
+ pStyle.headIndent = 0;
110
+ pStyle.firstLineHeadIndent = 0;
111
+ [_input->textView.textStorage addAttribute:NSParagraphStyleAttributeName value:pStyle range:range];
112
+ }
113
+ ];
114
+ }
115
+
116
+ [_input->textView.textStorage endEditing];
117
+
118
+ // also remove typing attributes
119
+ NSMutableDictionary *typingAttrs = [_input->textView.typingAttributes mutableCopy];
120
+ NSMutableParagraphStyle *pStyle = [typingAttrs[NSParagraphStyleAttributeName] mutableCopy];
121
+ pStyle.textLists = @[];
122
+ pStyle.headIndent = 0;
123
+ pStyle.firstLineHeadIndent = 0;
124
+ typingAttrs[NSParagraphStyleAttributeName] = pStyle;
125
+ _input->textView.typingAttributes = typingAttrs;
126
+ }
127
+
128
+ // needed for the sake of style conflicts, needs to do exactly the same as removeAttribtues
129
+ - (void)removeTypingAttributes {
130
+ [self removeAttributes:_input->textView.selectedRange];
131
+ }
132
+
133
+ // removing first list point by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange
134
+ // so we try guessing that a point should be deleted here
135
+ - (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text {
136
+ if([self detectStyle:_input->textView.selectedRange] &&
137
+ NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) &&
138
+ [text isEqualToString:@""]
139
+ ) {
140
+ NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange];
141
+ [self removeAttributes:paragraphRange];
142
+ return YES;
143
+ }
144
+ return NO;
145
+ }
146
+
147
+ - (BOOL)tryHandlingListShorcutInRange:(NSRange)range replacementText:(NSString *)text {
148
+ NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:range];
149
+ // a dot was added - check if we are both at the paragraph beginning + 1 character (which we want to be a dash)
150
+ if([text isEqualToString:@"."] && range.location - 1 == paragraphRange.location) {
151
+ unichar charBefore = [_input->textView.textStorage.string characterAtIndex:range.location - 1];
152
+ if(charBefore == '1') {
153
+ // we got a match - add a list if possible
154
+ if([_input handleStyleBlocksAndConflicts:[[self class] getStyleType] range:paragraphRange]) {
155
+ // don't emit some html updates during the replacing
156
+ BOOL prevEmitHtml = _input->emitHtml;
157
+ if(prevEmitHtml) {
158
+ _input->emitHtml = NO;
159
+ }
160
+
161
+ // remove the number
162
+ [TextInsertionUtils replaceText:@"" at:NSMakeRange(paragraphRange.location, 1) additionalAttributes:nullptr input:_input withSelection:YES];
163
+
164
+ if(prevEmitHtml) {
165
+ _input->emitHtml = YES;
166
+ }
167
+
168
+ // add attributes on the paragraph
169
+ [self addAttributes:NSMakeRange(paragraphRange.location, paragraphRange.length - 1)];
170
+ return YES;
171
+ }
172
+ }
173
+ }
174
+ return NO;
175
+ }
176
+
177
+ - (BOOL)styleCondition:(id _Nullable)value :(NSRange)range {
178
+ NSParagraphStyle *paragraph = (NSParagraphStyle *)value;
179
+ return paragraph != nullptr && paragraph.textLists.count == 1 && paragraph.textLists.firstObject.markerFormat == NSTextListMarkerDecimal;
180
+ }
181
+
182
+ - (BOOL)detectStyle:(NSRange)range {
183
+ if(range.length >= 1) {
184
+ return [OccurenceUtils detect:NSParagraphStyleAttributeName withInput:_input inRange:range
185
+ withCondition: ^BOOL(id _Nullable value, NSRange range) {
186
+ return [self styleCondition:value :range];
187
+ }
188
+ ];
189
+ } else {
190
+ NSInteger searchLocation = range.location;
191
+ if(searchLocation == _input->textView.textStorage.length) {
192
+ NSParagraphStyle *pStyle = _input->textView.typingAttributes[NSParagraphStyleAttributeName];
193
+ return [self styleCondition:pStyle :NSMakeRange(0, 0)];
194
+ }
195
+
196
+ NSRange paragraphRange = NSMakeRange(0, 0);
197
+ NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
198
+ NSParagraphStyle *paragraph = [_input->textView.textStorage
199
+ attribute:NSParagraphStyleAttributeName
200
+ atIndex:searchLocation
201
+ longestEffectiveRange: &paragraphRange
202
+ inRange:inputRange
203
+ ];
204
+
205
+ return [self styleCondition:paragraph :NSMakeRange(0, 0)];
206
+ }
207
+ }
208
+
209
+ - (BOOL)anyOccurence:(NSRange)range {
210
+ return [OccurenceUtils any:NSParagraphStyleAttributeName withInput:_input inRange:range
211
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
212
+ return [self styleCondition:value :range];
213
+ }
214
+ ];
215
+ }
216
+
217
+ - (NSArray<StylePair *> *_Nullable)findAllOccurences:(NSRange)range {
218
+ return [OccurenceUtils all:NSParagraphStyleAttributeName withInput:_input inRange:range
219
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
220
+ return [self styleCondition:value :range];
221
+ }
222
+ ];
223
+ }
224
+
225
+ @end