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