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.
- package/README.md +4 -14
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +4 -1
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +2 -1
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +10 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +7 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +0 -45
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +111 -2
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +9 -3
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/events/OnRequestHtmlResultEvent.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +42 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +135 -9
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +13 -3
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +13 -0
- package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +80 -9
- package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +188 -5
- package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +57 -30
- package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +24 -13
- package/android/src/main/java/com/swmansion/enriched/utils/ResourceManager.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +3 -0
- package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +6 -6
- package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +6 -6
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +19 -19
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +40 -51
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +13 -15
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +23 -21
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +35 -36
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +4 -4
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +13 -14
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +12 -13
- package/android/src/main/res/drawable/broken_image.xml +10 -0
- package/ios/EnrichedTextInputView.h +27 -12
- package/ios/EnrichedTextInputView.mm +906 -547
- package/ios/attachments/ImageAttachment.h +10 -0
- package/ios/attachments/ImageAttachment.mm +34 -0
- package/ios/attachments/MediaAttachment.h +23 -0
- package/ios/attachments/MediaAttachment.mm +31 -0
- package/ios/config/InputConfig.h +12 -6
- package/ios/config/InputConfig.mm +71 -33
- package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +10 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +7 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +0 -45
- package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +41 -4
- package/ios/inputParser/InputParser.h +5 -5
- package/ios/inputParser/InputParser.mm +867 -333
- package/ios/inputTextView/InputTextView.h +1 -1
- package/ios/inputTextView/InputTextView.mm +100 -59
- package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +11 -9
- package/ios/internals/EnrichedTextInputViewShadowNode.h +28 -24
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +64 -47
- package/ios/internals/EnrichedTextInputViewState.h +3 -1
- package/ios/styles/BlockQuoteStyle.mm +192 -142
- package/ios/styles/BoldStyle.mm +96 -62
- package/ios/styles/CodeBlockStyle.mm +304 -0
- package/ios/styles/H1Style.mm +10 -3
- package/ios/styles/H2Style.mm +10 -3
- package/ios/styles/H3Style.mm +10 -3
- package/ios/styles/HeadingStyleBase.mm +129 -84
- package/ios/styles/ImageStyle.mm +160 -0
- package/ios/styles/InlineCodeStyle.mm +149 -84
- package/ios/styles/ItalicStyle.mm +77 -51
- package/ios/styles/LinkStyle.mm +353 -224
- package/ios/styles/MentionStyle.mm +434 -220
- package/ios/styles/OrderedListStyle.mm +172 -105
- package/ios/styles/StrikethroughStyle.mm +53 -34
- package/ios/styles/UnderlineStyle.mm +69 -45
- package/ios/styles/UnorderedListStyle.mm +170 -105
- package/ios/utils/BaseStyleProtocol.h +3 -2
- package/ios/utils/ColorExtension.mm +7 -5
- package/ios/utils/FontExtension.mm +42 -27
- package/ios/utils/ImageData.h +10 -0
- package/ios/utils/ImageData.mm +4 -0
- package/ios/utils/LayoutManagerExtension.h +1 -1
- package/ios/utils/LayoutManagerExtension.mm +334 -109
- package/ios/utils/MentionParams.h +0 -1
- package/ios/utils/MentionStyleProps.h +1 -1
- package/ios/utils/MentionStyleProps.mm +27 -20
- package/ios/utils/OccurenceUtils.h +42 -38
- package/ios/utils/OccurenceUtils.mm +177 -107
- package/ios/utils/ParagraphAttributesUtils.h +6 -1
- package/ios/utils/ParagraphAttributesUtils.mm +152 -41
- package/ios/utils/ParagraphsUtils.h +2 -1
- package/ios/utils/ParagraphsUtils.mm +40 -26
- package/ios/utils/StringExtension.h +1 -1
- package/ios/utils/StringExtension.mm +19 -16
- package/ios/utils/StyleHeaders.h +35 -11
- package/ios/utils/TextInsertionUtils.h +13 -2
- package/ios/utils/TextInsertionUtils.mm +38 -20
- package/ios/utils/WordsUtils.h +2 -1
- package/ios/utils/WordsUtils.mm +32 -22
- package/ios/utils/ZeroWidthSpaceUtils.h +3 -1
- package/ios/utils/ZeroWidthSpaceUtils.mm +153 -75
- package/lib/module/EnrichedTextInput.js +41 -3
- package/lib/module/EnrichedTextInput.js.map +1 -1
- package/lib/module/EnrichedTextInputNativeComponent.ts +17 -5
- package/lib/module/normalizeHtmlStyle.js +0 -4
- package/lib/module/normalizeHtmlStyle.js.map +1 -1
- package/lib/typescript/src/EnrichedTextInput.d.ts +2 -5
- package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +7 -5
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -1
- package/package.json +8 -1
- package/src/EnrichedTextInput.tsx +48 -7
- package/src/EnrichedTextInputNativeComponent.ts +17 -5
- package/src/normalizeHtmlStyle.ts +0 -4
|
@@ -1,22 +1,28 @@
|
|
|
1
|
-
#import "
|
|
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 "
|
|
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 {
|
|
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 =
|
|
50
|
-
|
|
51
|
-
[_input->textView.textStorage
|
|
52
|
-
|
|
53
|
-
[_input->textView.textStorage addAttribute:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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 =
|
|
65
|
-
|
|
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] =
|
|
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 =
|
|
98
|
+
NSRange mentionRange =
|
|
99
|
+
[self getFullMentionRangeAt:_input->textView.selectedRange.location];
|
|
78
100
|
[_input->textView.textStorage beginEditing];
|
|
79
|
-
[_input->textView.textStorage removeAttribute:MentionAttributeName
|
|
80
|
-
|
|
81
|
-
[_input->textView.textStorage addAttribute:
|
|
82
|
-
|
|
83
|
-
|
|
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 ==
|
|
87
|
-
|
|
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 =
|
|
93
|
-
|
|
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] =
|
|
129
|
+
newTypingAttrs[NSStrikethroughColorAttributeName] =
|
|
130
|
+
[_input->config primaryColor];
|
|
96
131
|
[newTypingAttrs removeObjectForKey:NSBackgroundColorAttributeName];
|
|
97
|
-
if([self stylePropsWithParams:params].decorationLine ==
|
|
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
|
|
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
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
122
|
-
|
|
123
|
-
|
|
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
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
139
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
210
|
-
|
|
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 <
|
|
216
|
-
|
|
217
|
-
|
|
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 =
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
|
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
|
|
244
|
-
|
|
245
|
-
|
|
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]
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
265
|
-
|
|
266
|
-
|
|
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 =
|
|
277
|
-
|
|
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
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
|
339
|
-
if(
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
421
|
+
params = [self
|
|
422
|
+
getMentionParamsAt:_input->textView.selectedRange.location - 1];
|
|
360
423
|
}
|
|
361
424
|
}
|
|
362
425
|
// check after
|
|
363
|
-
if(_input->textView.selectedRange.location <
|
|
364
|
-
|
|
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 =
|
|
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 =
|
|
377
|
-
|
|
378
|
-
newTypingAttrs[
|
|
379
|
-
|
|
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 ==
|
|
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
|
|
389
|
-
|
|
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 &&
|
|
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
|
|
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
|
|
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 =
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
//
|
|
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 =
|
|
460
|
-
|
|
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
|
|
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
|
-
|