react-native-enriched 0.1.6 → 0.2.1

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 (123) hide show
  1. package/README.md +4 -14
  2. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +4 -1
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +2 -1
  4. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +10 -0
  5. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +7 -0
  6. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +0 -45
  7. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +111 -2
  8. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +9 -3
  9. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +2 -0
  10. package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +1 -1
  11. package/android/src/main/java/com/swmansion/enriched/events/OnRequestHtmlResultEvent.kt +33 -0
  12. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +6 -0
  13. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +6 -0
  14. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +42 -1
  15. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +6 -0
  16. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +6 -0
  17. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +6 -0
  18. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +135 -9
  19. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +6 -0
  20. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +5 -0
  21. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +6 -0
  22. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +6 -0
  23. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +6 -0
  24. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +13 -3
  25. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +5 -0
  26. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +5 -0
  27. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +6 -0
  28. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +4 -0
  29. package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +13 -0
  30. package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +80 -9
  31. package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +1 -0
  32. package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +188 -5
  33. package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +57 -30
  34. package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +91 -0
  35. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +24 -13
  36. package/android/src/main/java/com/swmansion/enriched/utils/ResourceManager.kt +26 -0
  37. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +3 -0
  38. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +6 -6
  39. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +6 -6
  40. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +19 -19
  41. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +40 -51
  42. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +13 -15
  43. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +23 -21
  44. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +35 -36
  45. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +4 -4
  46. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +13 -14
  47. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +12 -13
  48. package/android/src/main/res/drawable/broken_image.xml +10 -0
  49. package/ios/EnrichedTextInputView.h +27 -12
  50. package/ios/EnrichedTextInputView.mm +906 -547
  51. package/ios/attachments/ImageAttachment.h +10 -0
  52. package/ios/attachments/ImageAttachment.mm +34 -0
  53. package/ios/attachments/MediaAttachment.h +23 -0
  54. package/ios/attachments/MediaAttachment.mm +31 -0
  55. package/ios/config/InputConfig.h +12 -6
  56. package/ios/config/InputConfig.mm +71 -33
  57. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +10 -0
  58. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +7 -0
  59. package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +0 -45
  60. package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +41 -4
  61. package/ios/inputParser/InputParser.h +5 -5
  62. package/ios/inputParser/InputParser.mm +867 -333
  63. package/ios/inputTextView/InputTextView.h +1 -1
  64. package/ios/inputTextView/InputTextView.mm +100 -59
  65. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +11 -9
  66. package/ios/internals/EnrichedTextInputViewShadowNode.h +28 -24
  67. package/ios/internals/EnrichedTextInputViewShadowNode.mm +64 -47
  68. package/ios/internals/EnrichedTextInputViewState.h +3 -1
  69. package/ios/styles/BlockQuoteStyle.mm +192 -142
  70. package/ios/styles/BoldStyle.mm +96 -62
  71. package/ios/styles/CodeBlockStyle.mm +304 -0
  72. package/ios/styles/H1Style.mm +10 -3
  73. package/ios/styles/H2Style.mm +10 -3
  74. package/ios/styles/H3Style.mm +10 -3
  75. package/ios/styles/HeadingStyleBase.mm +129 -84
  76. package/ios/styles/ImageStyle.mm +160 -0
  77. package/ios/styles/InlineCodeStyle.mm +149 -84
  78. package/ios/styles/ItalicStyle.mm +77 -51
  79. package/ios/styles/LinkStyle.mm +353 -224
  80. package/ios/styles/MentionStyle.mm +434 -220
  81. package/ios/styles/OrderedListStyle.mm +172 -105
  82. package/ios/styles/StrikethroughStyle.mm +53 -34
  83. package/ios/styles/UnderlineStyle.mm +69 -45
  84. package/ios/styles/UnorderedListStyle.mm +170 -105
  85. package/ios/utils/BaseStyleProtocol.h +3 -2
  86. package/ios/utils/ColorExtension.mm +7 -5
  87. package/ios/utils/FontExtension.mm +42 -27
  88. package/ios/utils/ImageData.h +10 -0
  89. package/ios/utils/ImageData.mm +4 -0
  90. package/ios/utils/LayoutManagerExtension.h +1 -1
  91. package/ios/utils/LayoutManagerExtension.mm +334 -109
  92. package/ios/utils/MentionParams.h +0 -1
  93. package/ios/utils/MentionStyleProps.h +1 -1
  94. package/ios/utils/MentionStyleProps.mm +27 -20
  95. package/ios/utils/OccurenceUtils.h +42 -38
  96. package/ios/utils/OccurenceUtils.mm +177 -107
  97. package/ios/utils/ParagraphAttributesUtils.h +6 -1
  98. package/ios/utils/ParagraphAttributesUtils.mm +152 -41
  99. package/ios/utils/ParagraphsUtils.h +2 -1
  100. package/ios/utils/ParagraphsUtils.mm +40 -26
  101. package/ios/utils/StringExtension.h +1 -1
  102. package/ios/utils/StringExtension.mm +19 -16
  103. package/ios/utils/StyleHeaders.h +35 -11
  104. package/ios/utils/TextInsertionUtils.h +13 -2
  105. package/ios/utils/TextInsertionUtils.mm +38 -20
  106. package/ios/utils/WordsUtils.h +2 -1
  107. package/ios/utils/WordsUtils.mm +32 -22
  108. package/ios/utils/ZeroWidthSpaceUtils.h +3 -1
  109. package/ios/utils/ZeroWidthSpaceUtils.mm +153 -75
  110. package/lib/module/EnrichedTextInput.js +41 -3
  111. package/lib/module/EnrichedTextInput.js.map +1 -1
  112. package/lib/module/EnrichedTextInputNativeComponent.ts +17 -5
  113. package/lib/module/normalizeHtmlStyle.js +0 -4
  114. package/lib/module/normalizeHtmlStyle.js.map +1 -1
  115. package/lib/typescript/src/EnrichedTextInput.d.ts +2 -5
  116. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  117. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +7 -5
  118. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
  119. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -1
  120. package/package.json +8 -1
  121. package/src/EnrichedTextInput.tsx +48 -7
  122. package/src/EnrichedTextInputNativeComponent.ts +17 -5
  123. package/src/normalizeHtmlStyle.ts +0 -4
@@ -1,22 +1,28 @@
1
- #import "StyleHeaders.h"
1
+ #import "ColorExtension.h"
2
2
  #import "EnrichedTextInputView.h"
3
3
  #import "OccurenceUtils.h"
4
+ #import "StyleHeaders.h"
4
5
  #import "TextInsertionUtils.h"
5
- #import "WordsUtils.h"
6
6
  #import "UIView+React.h"
7
- #import "ColorExtension.h"
7
+ #import "WordsUtils.h"
8
8
 
9
9
  // custom NSAttributedStringKey to differentiate from links
10
10
  static NSString *const MentionAttributeName = @"MentionAttributeName";
11
11
 
12
12
  @implementation MentionStyle {
13
- EnrichedTextInputView*_input;
13
+ EnrichedTextInputView *_input;
14
14
  NSValue *_activeMentionRange;
15
15
  NSString *_activeMentionIndicator;
16
16
  BOOL _blockMentionEditing;
17
17
  }
18
18
 
19
- + (StyleType)getStyleType { return Mention; }
19
+ + (StyleType)getStyleType {
20
+ return Mention;
21
+ }
22
+
23
+ + (BOOL)isParagraphStyle {
24
+ return NO;
25
+ }
20
26
 
21
27
  - (instancetype)initWithInput:(id)input {
22
28
  self = [super init];
@@ -31,7 +37,7 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
31
37
  // no-op for mentions
32
38
  }
33
39
 
34
- - (void)addAttributes:(NSRange)range {
40
+ - (void)addAttributes:(NSRange)range withTypingAttr:(BOOL)withTypingAttr {
35
41
  // no-op for mentions
36
42
  }
37
43
 
@@ -45,28 +51,43 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
45
51
 
46
52
  NSArray<StylePair *> *mentions = [self findAllOccurences:range];
47
53
  [_input->textView.textStorage beginEditing];
48
- for(StylePair *pair in mentions) {
49
- NSRange mentionRange = [self getFullMentionRangeAt:[pair.rangeValue rangeValue].location];
50
- [_input->textView.textStorage removeAttribute:MentionAttributeName range:mentionRange];
51
- [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:mentionRange];
52
- [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:mentionRange];
53
- [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:mentionRange];
54
- [_input->textView.textStorage removeAttribute:NSBackgroundColorAttributeName range:mentionRange];
55
-
56
- if([self stylePropsWithParams:pair.styleValue].decorationLine == DecorationUnderline) {
57
- [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:mentionRange];
54
+ for (StylePair *pair in mentions) {
55
+ NSRange mentionRange =
56
+ [self getFullMentionRangeAt:[pair.rangeValue rangeValue].location];
57
+ [_input->textView.textStorage removeAttribute:MentionAttributeName
58
+ range:mentionRange];
59
+ [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName
60
+ value:[_input->config primaryColor]
61
+ range:mentionRange];
62
+ [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName
63
+ value:[_input->config primaryColor]
64
+ range:mentionRange];
65
+ [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName
66
+ value:[_input->config primaryColor]
67
+ range:mentionRange];
68
+ [_input->textView.textStorage removeAttribute:NSBackgroundColorAttributeName
69
+ range:mentionRange];
70
+
71
+ if ([self stylePropsWithParams:pair.styleValue].decorationLine ==
72
+ DecorationUnderline) {
73
+ [_input->textView.textStorage
74
+ removeAttribute:NSUnderlineStyleAttributeName
75
+ range:mentionRange];
58
76
  someMentionHadUnderline = YES;
59
77
  }
60
78
  }
61
79
  [_input->textView.textStorage endEditing];
62
-
80
+
63
81
  // remove typing attributes as well
64
- NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
65
- newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
82
+ NSMutableDictionary *newTypingAttrs =
83
+ [_input->textView.typingAttributes mutableCopy];
84
+ newTypingAttrs[NSForegroundColorAttributeName] =
85
+ [_input->config primaryColor];
66
86
  newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
67
- newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
87
+ newTypingAttrs[NSStrikethroughColorAttributeName] =
88
+ [_input->config primaryColor];
68
89
  [newTypingAttrs removeObjectForKey:NSBackgroundColorAttributeName];
69
- if(someMentionHadUnderline) {
90
+ if (someMentionHadUnderline) {
70
91
  [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
71
92
  }
72
93
  _input->textView.typingAttributes = newTypingAttrs;
@@ -74,165 +95,208 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
74
95
 
75
96
  // used for conflicts, we have to remove the whole mention
76
97
  - (void)removeTypingAttributes {
77
- NSRange mentionRange = [self getFullMentionRangeAt:_input->textView.selectedRange.location];
98
+ NSRange mentionRange =
99
+ [self getFullMentionRangeAt:_input->textView.selectedRange.location];
78
100
  [_input->textView.textStorage beginEditing];
79
- [_input->textView.textStorage removeAttribute:MentionAttributeName range:mentionRange];
80
- [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config primaryColor] range:mentionRange];
81
- [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config primaryColor] range:mentionRange];
82
- [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config primaryColor] range:mentionRange];
83
- [_input->textView.textStorage removeAttribute:NSBackgroundColorAttributeName range:mentionRange];
84
-
101
+ [_input->textView.textStorage removeAttribute:MentionAttributeName
102
+ range:mentionRange];
103
+ [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName
104
+ value:[_input->config primaryColor]
105
+ range:mentionRange];
106
+ [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName
107
+ value:[_input->config primaryColor]
108
+ range:mentionRange];
109
+ [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName
110
+ value:[_input->config primaryColor]
111
+ range:mentionRange];
112
+ [_input->textView.textStorage removeAttribute:NSBackgroundColorAttributeName
113
+ range:mentionRange];
114
+
85
115
  MentionParams *params = [self getMentionParamsAt:mentionRange.location];
86
- if([self stylePropsWithParams:params].decorationLine == DecorationUnderline) {
87
- [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName range:mentionRange];
116
+ if ([self stylePropsWithParams:params].decorationLine ==
117
+ DecorationUnderline) {
118
+ [_input->textView.textStorage removeAttribute:NSUnderlineStyleAttributeName
119
+ range:mentionRange];
88
120
  }
89
121
  [_input->textView.textStorage endEditing];
90
-
122
+
91
123
  // remove typing attributes as well
92
- NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
93
- newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
124
+ NSMutableDictionary *newTypingAttrs =
125
+ [_input->textView.typingAttributes mutableCopy];
126
+ newTypingAttrs[NSForegroundColorAttributeName] =
127
+ [_input->config primaryColor];
94
128
  newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
95
- newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
129
+ newTypingAttrs[NSStrikethroughColorAttributeName] =
130
+ [_input->config primaryColor];
96
131
  [newTypingAttrs removeObjectForKey:NSBackgroundColorAttributeName];
97
- if([self stylePropsWithParams:params].decorationLine == DecorationUnderline) {
132
+ if ([self stylePropsWithParams:params].decorationLine ==
133
+ DecorationUnderline) {
98
134
  [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
99
135
  }
100
136
  _input->textView.typingAttributes = newTypingAttrs;
101
137
  }
102
138
 
103
- - (BOOL)styleCondition:(id _Nullable)value :(NSRange)range {
139
+ - (BOOL)styleCondition:(id _Nullable)value:(NSRange)range {
104
140
  MentionParams *params = (MentionParams *)value;
105
141
  return params != nullptr;
106
142
  }
107
143
 
108
144
  - (BOOL)detectStyle:(NSRange)range {
109
- if(range.length >= 1) {
110
- return [OccurenceUtils detect:MentionAttributeName withInput:_input inRange:range
111
- withCondition: ^BOOL(id _Nullable value, NSRange range) {
112
- return [self styleCondition:value :range];
113
- }
114
- ];
145
+ if (range.length >= 1) {
146
+ return [OccurenceUtils detect:MentionAttributeName
147
+ withInput:_input
148
+ inRange:range
149
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
150
+ return [self styleCondition:value:range];
151
+ }];
115
152
  } else {
116
153
  return [self getMentionParamsAt:range.location] != nullptr;
117
154
  }
118
155
  }
119
156
 
120
157
  - (BOOL)anyOccurence:(NSRange)range {
121
- return [OccurenceUtils any:MentionAttributeName withInput:_input inRange:range
122
- withCondition:^BOOL(id _Nullable value, NSRange range) {
123
- return [self styleCondition:value :range];
124
- }
125
- ];
158
+ return [OccurenceUtils any:MentionAttributeName
159
+ withInput:_input
160
+ inRange:range
161
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
162
+ return [self styleCondition:value:range];
163
+ }];
126
164
  }
127
165
 
128
166
  - (NSArray<StylePair *> *_Nullable)findAllOccurences:(NSRange)range {
129
- return [OccurenceUtils all:MentionAttributeName withInput:_input inRange:range
130
- withCondition:^BOOL(id _Nullable value, NSRange range) {
131
- return [self styleCondition:value :range];
132
- }
133
- ];
167
+ return [OccurenceUtils all:MentionAttributeName
168
+ withInput:_input
169
+ inRange:range
170
+ withCondition:^BOOL(id _Nullable value, NSRange range) {
171
+ return [self styleCondition:value:range];
172
+ }];
134
173
  }
135
174
 
136
175
  // MARK: - Public non-standard methods
137
176
 
138
- - (void)addMention:(NSString *)indicator text:(NSString *)text attributes:(NSString *)attributes {
139
- if(_activeMentionRange == nullptr) {
177
+ - (void)addMention:(NSString *)indicator
178
+ text:(NSString *)text
179
+ attributes:(NSString *)attributes {
180
+ if (_activeMentionRange == nullptr) {
140
181
  return;
141
182
  }
142
-
143
- // we block callbacks resulting from manageMentionEditing while we tamper with them here
183
+
184
+ // we block callbacks resulting from manageMentionEditing while we tamper with
185
+ // them here
144
186
  _blockMentionEditing = YES;
145
-
187
+
146
188
  MentionParams *params = [[MentionParams alloc] init];
147
189
  params.text = text;
148
190
  params.indicator = indicator;
149
191
  params.attributes = attributes;
150
-
151
- MentionStyleProps *styleProps = [_input->config mentionStylePropsForIndicator:indicator];
152
-
192
+
193
+ MentionStyleProps *styleProps =
194
+ [_input->config mentionStylePropsForIndicator:indicator];
195
+
153
196
  NSMutableDictionary *newAttrs = [@{
154
- MentionAttributeName: params,
155
- NSForegroundColorAttributeName: styleProps.color,
156
- NSUnderlineColorAttributeName: styleProps.color,
157
- NSStrikethroughColorAttributeName: styleProps.color,
158
- NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaIfNotTransparent:0.4],
197
+ MentionAttributeName : params,
198
+ NSForegroundColorAttributeName : styleProps.color,
199
+ NSUnderlineColorAttributeName : styleProps.color,
200
+ NSStrikethroughColorAttributeName : styleProps.color,
201
+ NSBackgroundColorAttributeName :
202
+ [styleProps.backgroundColor colorWithAlphaIfNotTransparent:0.4],
159
203
  } mutableCopy];
160
-
161
- if(styleProps.decorationLine == DecorationUnderline) {
204
+
205
+ if (styleProps.decorationLine == DecorationUnderline) {
162
206
  newAttrs[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle);
163
207
  }
164
-
208
+
165
209
  // add a single space after the mention
166
210
  NSString *newText = [NSString stringWithFormat:@"%@ ", text];
167
211
  NSRange rangeToBeReplaced = [_activeMentionRange rangeValue];
168
- [TextInsertionUtils replaceText:newText at:rangeToBeReplaced additionalAttributes:nullptr input:_input withSelection:YES];
169
-
212
+ [TextInsertionUtils replaceText:newText
213
+ at:rangeToBeReplaced
214
+ additionalAttributes:nullptr
215
+ input:_input
216
+ withSelection:YES];
217
+
170
218
  // THEN, add the attributes to not apply them on the space
171
- [_input->textView.textStorage addAttributes:newAttrs range:NSMakeRange(rangeToBeReplaced.location, text.length)];
172
-
219
+ [_input->textView.textStorage
220
+ addAttributes:newAttrs
221
+ range:NSMakeRange(rangeToBeReplaced.location, text.length)];
222
+
173
223
  // mention editing should finish
174
224
  [self removeActiveMentionRange];
175
-
225
+
176
226
  // unlock editing
177
227
  _blockMentionEditing = NO;
178
228
  }
179
229
 
180
230
  - (void)addMentionAtRange:(NSRange)range params:(MentionParams *)params {
181
231
  _blockMentionEditing = YES;
182
-
183
- MentionStyleProps *styleProps = [_input->config mentionStylePropsForIndicator:params.indicator];
184
-
232
+
233
+ MentionStyleProps *styleProps =
234
+ [_input->config mentionStylePropsForIndicator:params.indicator];
235
+
185
236
  NSMutableDictionary *newAttrs = [@{
186
- MentionAttributeName: params,
187
- NSForegroundColorAttributeName: styleProps.color,
188
- NSUnderlineColorAttributeName: styleProps.color,
189
- NSStrikethroughColorAttributeName: styleProps.color,
190
- NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaIfNotTransparent:0.4],
237
+ MentionAttributeName : params,
238
+ NSForegroundColorAttributeName : styleProps.color,
239
+ NSUnderlineColorAttributeName : styleProps.color,
240
+ NSStrikethroughColorAttributeName : styleProps.color,
241
+ NSBackgroundColorAttributeName :
242
+ [styleProps.backgroundColor colorWithAlphaIfNotTransparent:0.4],
191
243
  } mutableCopy];
192
-
193
- if(styleProps.decorationLine == DecorationUnderline) {
244
+
245
+ if (styleProps.decorationLine == DecorationUnderline) {
194
246
  newAttrs[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle);
195
247
  }
196
-
248
+
197
249
  [_input->textView.textStorage addAttributes:newAttrs range:range];
198
-
250
+
199
251
  _blockMentionEditing = NO;
200
252
  }
201
253
 
202
254
  - (void)startMentionWithIndicator:(NSString *)indicator {
203
255
  NSRange currentRange = _input->textView.selectedRange;
204
-
256
+
205
257
  BOOL addSpaceBefore = NO;
206
258
  BOOL addSpaceAfter = NO;
207
-
208
- if(currentRange.location > 0) {
209
- unichar charBefore = [_input->textView.textStorage.string characterAtIndex:(currentRange.location - 1)];
210
- if(![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charBefore]) {
259
+
260
+ if (currentRange.location > 0) {
261
+ unichar charBefore = [_input->textView.textStorage.string
262
+ characterAtIndex:(currentRange.location - 1)];
263
+ if (![[NSCharacterSet whitespaceAndNewlineCharacterSet]
264
+ characterIsMember:charBefore]) {
211
265
  addSpaceBefore = YES;
212
266
  }
213
267
  }
214
-
215
- if(currentRange.location + currentRange.length < _input->textView.textStorage.string.length) {
216
- unichar charAfter = [_input->textView.textStorage.string characterAtIndex:(currentRange.location + currentRange.length)];
217
- if(![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:charAfter]) {
268
+
269
+ if (currentRange.location + currentRange.length <
270
+ _input->textView.textStorage.string.length) {
271
+ unichar charAfter = [_input->textView.textStorage.string
272
+ characterAtIndex:(currentRange.location + currentRange.length)];
273
+ if (![[NSCharacterSet whitespaceAndNewlineCharacterSet]
274
+ characterIsMember:charAfter]) {
218
275
  addSpaceAfter = YES;
219
276
  }
220
277
  }
221
-
222
- NSString *finalString = [NSString stringWithFormat:@"%@%@%@",
223
- addSpaceBefore ? @" " : @"",
224
- indicator,
225
- addSpaceAfter ? @" " : @""
226
- ];
227
-
228
- NSRange newSelect = NSMakeRange(currentRange.location + finalString.length + (addSpaceAfter ? -1 : 0), 0);
229
-
230
- if(currentRange.length == 0) {
231
- [TextInsertionUtils insertText:finalString at:currentRange.location additionalAttributes:nullptr input:_input withSelection:NO];
278
+
279
+ NSString *finalString =
280
+ [NSString stringWithFormat:@"%@%@%@", addSpaceBefore ? @" " : @"",
281
+ indicator, addSpaceAfter ? @" " : @""];
282
+
283
+ NSRange newSelect = NSMakeRange(
284
+ currentRange.location + finalString.length + (addSpaceAfter ? -1 : 0), 0);
285
+
286
+ if (currentRange.length == 0) {
287
+ [TextInsertionUtils insertText:finalString
288
+ at:currentRange.location
289
+ additionalAttributes:nullptr
290
+ input:_input
291
+ withSelection:NO];
232
292
  } else {
233
- [TextInsertionUtils replaceText:finalString at:currentRange additionalAttributes:nullptr input:_input withSelection:NO];
293
+ [TextInsertionUtils replaceText:finalString
294
+ at:currentRange
295
+ additionalAttributes:nullptr
296
+ input:_input
297
+ withSelection:NO];
234
298
  }
235
-
299
+
236
300
  [_input->textView reactFocus];
237
301
  _input->textView.selectedRange = newSelect;
238
302
  }
@@ -240,46 +304,53 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
240
304
  // handles removing no longer valid mentions
241
305
  - (void)handleExistingMentions {
242
306
  // unfortunately whole text needs to be checked for them
243
- // 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
244
-
245
- NSRange wholeText = NSMakeRange(0, _input->textView.textStorage.string.length);
307
+ // checking the modified words doesn't work because mention's text can have
308
+ // any number of spaces, which makes one mention any number of words long
309
+
310
+ NSRange wholeText =
311
+ NSMakeRange(0, _input->textView.textStorage.string.length);
246
312
  // get menntions in ascending range.location order
247
- NSArray<StylePair *> *mentions = [[self findAllOccurences:wholeText] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
248
- NSRange range1 = [((StylePair *)obj1).rangeValue rangeValue];
249
- NSRange range2 = [((StylePair *)obj2).rangeValue rangeValue];
250
- if(range1.location < range2.location) {
251
- return NSOrderedAscending;
252
- } else {
253
- return NSOrderedDescending;
254
- }
255
- }];
256
-
313
+ NSArray<StylePair *> *mentions = [[self findAllOccurences:wholeText]
314
+ sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1,
315
+ id _Nonnull obj2) {
316
+ NSRange range1 = [((StylePair *)obj1).rangeValue rangeValue];
317
+ NSRange range2 = [((StylePair *)obj2).rangeValue rangeValue];
318
+ if (range1.location < range2.location) {
319
+ return NSOrderedAscending;
320
+ } else {
321
+ return NSOrderedDescending;
322
+ }
323
+ }];
324
+
257
325
  // set of ranges to have their mentions removed - aren't valid anymore
258
326
  NSMutableSet<NSValue *> *rangesToRemove = [[NSMutableSet alloc] init];
259
-
260
- for(NSInteger i = 0; i < mentions.count; i++) {
327
+
328
+ for (NSInteger i = 0; i < mentions.count; i++) {
261
329
  StylePair *mention = mentions[i];
262
330
  NSRange currentRange = [mention.rangeValue rangeValue];
263
331
  NSString *currentText = ((MentionParams *)mention.styleValue).text;
264
- // check locations with the previous mention if it exists - if they got merged they need to be removed
265
- if(i > 0) {
266
- NSRange prevRange = [((StylePair*)mentions[i-1]).rangeValue rangeValue];
332
+ // check locations with the previous mention if it exists - if they got
333
+ // merged they need to be removed
334
+ if (i > 0) {
335
+ NSRange prevRange =
336
+ [((StylePair *)mentions[i - 1]).rangeValue rangeValue];
267
337
  // mentions merged - both need to go out
268
- if(prevRange.location + prevRange.length == currentRange.location) {
338
+ if (prevRange.location + prevRange.length == currentRange.location) {
269
339
  [rangesToRemove addObject:[NSValue valueWithRange:prevRange]];
270
340
  [rangesToRemove addObject:[NSValue valueWithRange:currentRange]];
271
341
  continue;
272
342
  }
273
343
  }
274
-
344
+
275
345
  // check for text, any modifications to it makes mention invalid
276
- NSString *existingText = [_input->textView.textStorage.string substringWithRange:currentRange];
277
- if(![existingText isEqualToString:currentText]) {
346
+ NSString *existingText =
347
+ [_input->textView.textStorage.string substringWithRange:currentRange];
348
+ if (![existingText isEqualToString:currentText]) {
278
349
  [rangesToRemove addObject:[NSValue valueWithRange:currentRange]];
279
350
  }
280
351
  }
281
-
282
- for(NSValue *value in rangesToRemove) {
352
+
353
+ for (NSValue *value in rangesToRemove) {
283
354
  [self removeAttributes:[value rangeValue]];
284
355
  }
285
356
  }
@@ -287,113 +358,124 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
287
358
  // manages active mention range, which in turn emits proper onMention event
288
359
  - (void)manageMentionEditing {
289
360
  // no actions performed when block is active
290
- if(_blockMentionEditing) {
361
+ if (_blockMentionEditing) {
291
362
  return;
292
363
  }
293
364
 
294
365
  // we don't take longer selections into consideration
295
- if(_input->textView.selectedRange.length > 0) {
366
+ if (_input->textView.selectedRange.length > 0) {
296
367
  [self removeActiveMentionRange];
297
368
  return;
298
369
  }
299
-
300
- // get the current word if it exists
301
- // we can be using current word only thanks to the fact that ongoing mentions are always one word (in contrast to ready, added mentions)
302
- NSDictionary *currentWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:_input->textView.selectedRange];
303
- if(currentWord == nullptr) {
304
- [self removeActiveMentionRange];
305
- return;
306
- }
307
-
308
- // get word properties
309
- NSString *wordText = (NSString *)[currentWord objectForKey:@"word"];
310
- NSValue *wordRangeValue = (NSValue *)[currentWord objectForKey:@"range"];
311
- if(wordText == nullptr || wordRangeValue == nullptr) {
312
- [self removeActiveMentionRange];
313
- return;
314
- }
315
- NSRange wordRange = [wordRangeValue rangeValue];
316
-
317
- // check for mentionIndicators - no sign of them means we shouldn't be editing a mention
318
- unichar firstChar = [wordText characterAtIndex:0];
319
- if(![[_input->config mentionIndicators] containsObject: @(firstChar)]) {
320
- [self removeActiveMentionRange];
321
- return;
322
- }
323
-
324
- // check for existing mentions - we don't edit them
325
- if([self detectStyle:wordRange]) {
370
+
371
+ // get the text (and its range) that could be an editable mention
372
+ NSArray *mentionCandidate = [self getMentionCandidate];
373
+ if (mentionCandidate == nullptr) {
326
374
  [self removeActiveMentionRange];
327
375
  return;
328
376
  }
329
-
330
- // get conflicting style classes
331
- LinkStyle* linkStyle = [_input->stylesDict objectForKey:@([LinkStyle getStyleType])];
332
- InlineCodeStyle* inlineCodeStyle = [_input->stylesDict objectForKey:@([InlineCodeStyle getStyleType])];
333
- if(linkStyle == nullptr || inlineCodeStyle == nullptr) {
334
- [self removeActiveMentionRange];
335
- return;
377
+ NSString *candidateText = mentionCandidate[0];
378
+ NSRange candidateRange = [(NSValue *)mentionCandidate[1] rangeValue];
379
+
380
+ // get style classes that the mention shouldn't be recognized in, together
381
+ // with other mentions
382
+ NSArray *conflicts =
383
+ _input->conflictingStyles[@([MentionStyle getStyleType])];
384
+ NSArray *blocks = _input->blockingStyles[@([MentionStyle getStyleType])];
385
+ NSArray *allConflicts = [[conflicts arrayByAddingObjectsFromArray:blocks]
386
+ arrayByAddingObject:@([MentionStyle getStyleType])];
387
+ BOOL conflictingStyle = NO;
388
+
389
+ for (NSNumber *styleType in allConflicts) {
390
+ id<BaseStyleProtocol> styleClass = _input->stylesDict[styleType];
391
+ if (styleClass != nullptr && [styleClass anyOccurence:candidateRange]) {
392
+ conflictingStyle = YES;
393
+ break;
394
+ }
336
395
  }
337
-
338
- // if there is any sign of conflicting style classes, stop editing a mention
339
- if([linkStyle anyOccurence:wordRange] || [inlineCodeStyle anyOccurence:wordRange]) {
396
+
397
+ // if any of the conflicting styles were present, don't edit the mention
398
+ if (conflictingStyle) {
340
399
  [self removeActiveMentionRange];
341
400
  return;
342
401
  }
343
-
402
+
344
403
  // everything checks out - we are indeed editing a mention
345
- [self setActiveMentionRange:wordRange text:wordText];
404
+ [self setActiveMentionRange:candidateRange text:candidateText];
346
405
  }
347
406
 
348
407
  // used to fix mentions' typing attributes
349
408
  - (void)manageMentionTypingAttributes {
350
- // same as with links, mentions' typing attributes need to be constantly removed whenever we are somewhere near
409
+ // same as with links, mentions' typing attributes need to be constantly
410
+ // removed whenever we are somewhere near
351
411
  BOOL removeAttrs = NO;
352
412
  MentionParams *params;
353
-
354
- if(_input->textView.selectedRange.length == 0) {
413
+
414
+ if (_input->textView.selectedRange.length == 0) {
355
415
  // check before
356
- if(_input->textView.selectedRange.location >= 1) {
357
- if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location - 1, 1)]) {
416
+ if (_input->textView.selectedRange.location >= 1) {
417
+ if ([self detectStyle:NSMakeRange(
418
+ _input->textView.selectedRange.location - 1,
419
+ 1)]) {
358
420
  removeAttrs = YES;
359
- params = [self getMentionParamsAt:_input->textView.selectedRange.location - 1];
421
+ params = [self
422
+ getMentionParamsAt:_input->textView.selectedRange.location - 1];
360
423
  }
361
424
  }
362
425
  // check after
363
- if(_input->textView.selectedRange.location < _input->textView.textStorage.length) {
364
- if([self detectStyle:NSMakeRange(_input->textView.selectedRange.location, 1)]) {
426
+ if (_input->textView.selectedRange.location <
427
+ _input->textView.textStorage.length) {
428
+ if ([self detectStyle:NSMakeRange(_input->textView.selectedRange.location,
429
+ 1)]) {
365
430
  removeAttrs = YES;
366
- params = [self getMentionParamsAt:_input->textView.selectedRange.location];
431
+ params =
432
+ [self getMentionParamsAt:_input->textView.selectedRange.location];
367
433
  }
368
434
  }
369
435
  } else {
370
- if([self anyOccurence:_input->textView.selectedRange]) {
436
+ if ([self anyOccurence:_input->textView.selectedRange]) {
371
437
  removeAttrs = YES;
372
438
  }
373
439
  }
374
-
375
- if(removeAttrs) {
376
- NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
377
- newTypingAttrs[NSForegroundColorAttributeName] = [_input->config primaryColor];
378
- newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config primaryColor];
379
- newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
440
+
441
+ if (removeAttrs) {
442
+ NSMutableDictionary *newTypingAttrs =
443
+ [_input->textView.typingAttributes mutableCopy];
444
+ newTypingAttrs[NSForegroundColorAttributeName] =
445
+ [_input->config primaryColor];
446
+ newTypingAttrs[NSUnderlineColorAttributeName] =
447
+ [_input->config primaryColor];
448
+ newTypingAttrs[NSStrikethroughColorAttributeName] =
449
+ [_input->config primaryColor];
380
450
  [newTypingAttrs removeObjectForKey:NSBackgroundColorAttributeName];
381
- if([self stylePropsWithParams:params].decorationLine == DecorationUnderline) {
451
+ if ([self stylePropsWithParams:params].decorationLine ==
452
+ DecorationUnderline) {
382
453
  [newTypingAttrs removeObjectForKey:NSUnderlineStyleAttributeName];
383
454
  }
384
455
  _input->textView.typingAttributes = newTypingAttrs;
385
456
  }
386
457
  }
387
458
 
388
- // replacing whole input (that starts with a mention) with a manually typed letter improperly applies mention's attributes to all the following text
389
- - (BOOL)handleLeadingMentionReplacement:(NSRange)range replacementText:(NSString *)text {
459
+ // replacing whole input (that starts with a mention) with a manually typed
460
+ // letter improperly applies mention's attributes to all the following text
461
+ - (BOOL)handleLeadingMentionReplacement:(NSRange)range
462
+ replacementText:(NSString *)text {
390
463
  // whole textView range gets replaced with a single letter
391
- if(_input->textView.textStorage.string.length > 0 && NSEqualRanges(range, NSMakeRange(0, _input->textView.textStorage.string.length)) && text.length == 1) {
464
+ if (_input->textView.textStorage.string.length > 0 &&
465
+ NSEqualRanges(
466
+ range, NSMakeRange(0, _input->textView.textStorage.string.length)) &&
467
+ text.length == 1) {
392
468
  // first character detection is enough for the removal to be done
393
- if([self detectStyle:NSMakeRange(0, 1)]) {
394
- [self removeAttributes:NSMakeRange(0, _input->textView.textStorage.string.length)];
469
+ if ([self detectStyle:NSMakeRange(0, 1)]) {
470
+ [self
471
+ removeAttributes:NSMakeRange(
472
+ 0, _input->textView.textStorage.string.length)];
395
473
  // do the replacing manually
396
- [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES];
474
+ [TextInsertionUtils replaceText:text
475
+ at:range
476
+ additionalAttributes:nullptr
477
+ input:_input
478
+ withSelection:YES];
397
479
  return YES;
398
480
  }
399
481
  }
@@ -404,19 +486,18 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
404
486
  - (MentionParams *)getMentionParamsAt:(NSUInteger)location {
405
487
  NSRange mentionRange = NSMakeRange(0, 0);
406
488
  NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
407
-
489
+
408
490
  // don't search at the very end of input
409
491
  NSUInteger searchLocation = location;
410
- if(searchLocation == _input->textView.textStorage.length) {
492
+ if (searchLocation == _input->textView.textStorage.length) {
411
493
  return nullptr;
412
494
  }
413
-
414
- MentionParams *value = [_input->textView.textStorage
415
- attribute:MentionAttributeName
416
- atIndex:searchLocation
417
- longestEffectiveRange: &mentionRange
418
- inRange:inputRange
419
- ];
495
+
496
+ MentionParams *value =
497
+ [_input->textView.textStorage attribute:MentionAttributeName
498
+ atIndex:searchLocation
499
+ longestEffectiveRange:&mentionRange
500
+ inRange:inputRange];
420
501
  return value;
421
502
  }
422
503
 
@@ -428,23 +509,21 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
428
509
  - (NSRange)getFullMentionRangeAt:(NSUInteger)location {
429
510
  NSRange mentionRange = NSMakeRange(0, 0);
430
511
  NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
431
-
512
+
432
513
  // get the previous index if possible when at the very end of input
433
514
  NSUInteger searchLocation = location;
434
- if(searchLocation == _input->textView.textStorage.length) {
435
- if(searchLocation == 0) {
515
+ if (searchLocation == _input->textView.textStorage.length) {
516
+ if (searchLocation == 0) {
436
517
  return mentionRange;
437
518
  } else {
438
519
  searchLocation = searchLocation - 1;
439
520
  }
440
521
  }
441
-
442
- [_input->textView.textStorage
443
- attribute:MentionAttributeName
444
- atIndex:searchLocation
445
- longestEffectiveRange: &mentionRange
446
- inRange:inputRange
447
- ];
522
+
523
+ [_input->textView.textStorage attribute:MentionAttributeName
524
+ atIndex:searchLocation
525
+ longestEffectiveRange:&mentionRange
526
+ inRange:inputRange];
448
527
  return mentionRange;
449
528
  }
450
529
 
@@ -454,18 +533,154 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
454
533
  return [_input->config mentionStylePropsForIndicator:params.indicator];
455
534
  }
456
535
 
457
- // both used for setting the active mention range + indicator and fires proper onMention event
536
+ // finds if any word/words around current selection are eligible to be edited as
537
+ // mentions since we allow for a single space inside an edited mention, we have
538
+ // take both current and the previous word into account
539
+ - (NSArray *)getMentionCandidate {
540
+ NSDictionary *currentWord, *previousWord;
541
+ NSString *currentWordText, *previousWordText, *finalText;
542
+ NSValue *currentWordRange, *previousWordRange;
543
+ NSRange finalRange;
544
+
545
+ // word at the current selection
546
+ currentWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string
547
+ range:_input->textView.selectedRange];
548
+ if (currentWord != nullptr) {
549
+ currentWordText = (NSString *)[currentWord objectForKey:@"word"];
550
+ currentWordRange = (NSValue *)[currentWord objectForKey:@"range"];
551
+ }
552
+
553
+ if (currentWord != nullptr) {
554
+ // current word exists
555
+ unichar currentFirstChar = [currentWordText characterAtIndex:0];
556
+
557
+ if ([[_input->config mentionIndicators]
558
+ containsObject:@(currentFirstChar)]) {
559
+ // current word exists and has a mention indicator; no need to check for
560
+ // the previous word
561
+ finalText = currentWordText;
562
+ finalRange = [currentWordRange rangeValue];
563
+ } else {
564
+ // current word exists but no traces of mention indicator; get the
565
+ // previous word
566
+
567
+ NSInteger previousWordSearchLocation =
568
+ [currentWordRange rangeValue].location - 1;
569
+ if (previousWordSearchLocation < 0) {
570
+ // previous word can't exist
571
+ return nullptr;
572
+ }
573
+
574
+ unichar separatorChar = [_input->textView.textStorage.string
575
+ characterAtIndex:previousWordSearchLocation];
576
+ if (![[NSCharacterSet whitespaceCharacterSet]
577
+ characterIsMember:separatorChar]) {
578
+ // we want to check for the previous word ONLY if the separating
579
+ // character was a space newlines don't make it
580
+ return nullptr;
581
+ }
582
+
583
+ previousWord = [WordsUtils
584
+ getCurrentWord:_input->textView.textStorage.string
585
+ range:NSMakeRange(previousWordSearchLocation, 0)];
586
+
587
+ if (previousWord != nullptr) {
588
+ // previous word exists; get its properties
589
+ previousWordText = (NSString *)[previousWord objectForKey:@"word"];
590
+ previousWordRange = (NSValue *)[previousWord objectForKey:@"range"];
591
+
592
+ // check for the mention indicators in the previous word
593
+ unichar previousFirstChar = [previousWordText characterAtIndex:0];
594
+
595
+ if ([[_input->config mentionIndicators]
596
+ containsObject:@(previousFirstChar)]) {
597
+ // previous word has a proper mention indicator: treat both words as
598
+ // an editable mention
599
+ finalText = [NSString
600
+ stringWithFormat:@"%@ %@", previousWordText, currentWordText];
601
+ // range length is both words' lengths + 1 for a space between them
602
+ finalRange =
603
+ NSMakeRange([previousWordRange rangeValue].location,
604
+ [previousWordRange rangeValue].length +
605
+ [currentWordRange rangeValue].length + 1);
606
+ } else {
607
+ // neither current nor previous words have a mention indicator
608
+ return nullptr;
609
+ }
610
+ } else {
611
+ // previous word doesn't exist and no mention indicators in the current
612
+ // word
613
+ return nullptr;
614
+ }
615
+ }
616
+ } else {
617
+ // current word doesn't exist; try getting the previous one
618
+
619
+ NSInteger previousWordSearchLocation =
620
+ _input->textView.selectedRange.location - 1;
621
+ if (previousWordSearchLocation < 0) {
622
+ // previous word can't exist
623
+ return nullptr;
624
+ }
625
+
626
+ unichar separatorChar = [_input->textView.textStorage.string
627
+ characterAtIndex:previousWordSearchLocation];
628
+ if (![[NSCharacterSet whitespaceCharacterSet]
629
+ characterIsMember:separatorChar]) {
630
+ // we want to check for the previous word ONLY if the separating character
631
+ // was a space newlines don't make it
632
+ return nullptr;
633
+ }
634
+
635
+ previousWord =
636
+ [WordsUtils getCurrentWord:_input->textView.textStorage.string
637
+ range:NSMakeRange(previousWordSearchLocation, 0)];
638
+
639
+ if (previousWord != nullptr) {
640
+ // previous word exists; get its properties
641
+ previousWordText = (NSString *)[previousWord objectForKey:@"word"];
642
+ previousWordRange = (NSValue *)[previousWord objectForKey:@"range"];
643
+
644
+ // check for the mention indicators in the previous word
645
+ unichar previousFirstChar = [previousWordText characterAtIndex:0];
646
+
647
+ if ([[_input->config mentionIndicators]
648
+ containsObject:@(previousFirstChar)]) {
649
+ // previous word has a proper mention indicator; treat previous word + a
650
+ // space as a editable mention
651
+ finalText = [NSString stringWithFormat:@"%@ ", previousWordText];
652
+ // the range length is previous word length + 1 for a space
653
+ finalRange = NSMakeRange([previousWordRange rangeValue].location,
654
+ [previousWordRange rangeValue].length + 1);
655
+ } else {
656
+ // no current word, previous has no mention indicators
657
+ return nullptr;
658
+ }
659
+ } else {
660
+ // no current word, no previous word
661
+ return nullptr;
662
+ }
663
+ }
664
+
665
+ return @[ finalText, [NSValue valueWithRange:finalRange] ];
666
+ }
667
+
668
+ // both used for setting the active mention range + indicator and fires proper
669
+ // onMention event
458
670
  - (void)setActiveMentionRange:(NSRange)range text:(NSString *)text {
459
- NSString *indicatorString = [NSString stringWithFormat:@"%C", [text characterAtIndex:0]];
460
- NSString *textString = [text substringWithRange:NSMakeRange(1, text.length - 1)];
671
+ NSString *indicatorString =
672
+ [NSString stringWithFormat:@"%C", [text characterAtIndex:0]];
673
+ NSString *textString =
674
+ [text substringWithRange:NSMakeRange(1, text.length - 1)];
461
675
  _activeMentionIndicator = indicatorString;
462
676
  _activeMentionRange = [NSValue valueWithRange:range];
463
677
  [_input emitOnMentionEvent:indicatorString text:textString];
464
678
  }
465
679
 
466
- // removes stored mention range + indicator, which means that we no longer edit a mention and onMention event gets fired
680
+ // removes stored mention range + indicator, which means that we no longer edit
681
+ // a mention and onMention event gets fired
467
682
  - (void)removeActiveMentionRange {
468
- if(_activeMentionIndicator != nullptr && _activeMentionRange != nullptr) {
683
+ if (_activeMentionIndicator != nullptr && _activeMentionRange != nullptr) {
469
684
  NSString *indicatorCopy = [_activeMentionIndicator copy];
470
685
  _activeMentionIndicator = nullptr;
471
686
  _activeMentionRange = nullptr;
@@ -474,4 +689,3 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
474
689
  }
475
690
 
476
691
  @end
477
-