diff --git a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m index 5297fde2d9b..f234b25fbfe 100644 --- a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m +++ b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m @@ -274,40 +274,17 @@ - (void)updateFonts:(WKSourceEditorFonts *)fonts inAttributedString:(NSMutableAt #pragma mark - Public - (BOOL)attributedString:(NSMutableAttributedString *)attributedString isBoldInRange:(NSRange)range { - __block BOOL isBold = NO; - if (range.length == 0) { - - if (attributedString.length > range.location) { - NSDictionary *attrs = [attributedString attributesAtIndex:range.location effectiveRange:nil]; - - if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontBold] != nil) { - isBold = YES; - } else { - // Edge case, check previous character if we are up against a closing bold or italic - if (attrs[WKSourceEditorCustomKeyColorOrange]) { - attrs = [attributedString attributesAtIndex:range.location - 1 effectiveRange:nil]; - if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontBold] != nil) { - isBold = YES; - } - } - } - } - - } else { - [attributedString enumerateAttributesInRange:range options:nil usingBlock:^(NSDictionary * _Nonnull attrs, NSRange loopRange, BOOL * _Nonnull stop) { - if ((attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontBold] != nil) && - (loopRange.location == range.location && loopRange.length == range.length)) { - isBold = YES; - stop = YES; - } - }]; - } - - - return isBold; + return [self attributedString:attributedString isFormattedInRange:range formattingKey:WKSourceEditorCustomKeyFontBold]; } + - (BOOL)attributedString:(NSMutableAttributedString *)attributedString isItalicsInRange:(NSRange)range { - __block BOOL isItalics = NO; + return [self attributedString:attributedString isFormattedInRange:range formattingKey:WKSourceEditorCustomKeyFontItalics]; +} + +#pragma mark - Private + +- (BOOL)attributedString:(NSMutableAttributedString *)attributedString isFormattedInRange:(NSRange)range formattingKey: (NSString *)formattingKey { + __block BOOL isFormatted = NO; if (range.length == 0) { @@ -315,29 +292,38 @@ - (BOOL)attributedString:(NSMutableAttributedString *)attributedString isItalics NSDictionary *attrs = [attributedString attributesAtIndex:range.location effectiveRange:nil]; if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) { - isItalics = YES; + isFormatted = YES; } else { // Edge case, check previous character if we are up against a closing bold or italic if (attrs[WKSourceEditorCustomKeyColorOrange]) { attrs = [attributedString attributesAtIndex:range.location - 1 effectiveRange:nil]; - if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) { - isItalics = YES; + if (attrs[WKSourceEditorCustomKeyFontBold] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) { + isFormatted = YES; } } } } } else { + + __block NSRange unionRange = NSMakeRange(NSNotFound, 0); [attributedString enumerateAttributesInRange:range options:nil usingBlock:^(NSDictionary * _Nonnull attrs, NSRange loopRange, BOOL * _Nonnull stop) { - if ((attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) && - (loopRange.location == range.location && loopRange.length == range.length)) { - isItalics = YES; + if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[formattingKey] != nil) { + if (unionRange.location == NSNotFound) { + unionRange = loopRange; + } else { + unionRange = NSUnionRange(unionRange, loopRange); + } stop = YES; } }]; + + if (NSEqualRanges(unionRange, range)) { + isFormatted = YES; + } } - return isItalics; + return isFormatted; } @end diff --git a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m index 5d6272f4a8c..375d432ebeb 100644 --- a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m +++ b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m @@ -149,13 +149,21 @@ - (BOOL)attributedString:(NSMutableAttributedString *)attributedString isHorizon } } else { + __block NSRange unionRange = NSMakeRange(NSNotFound, 0); [attributedString enumerateAttributesInRange:range options:nil usingBlock:^(NSDictionary * _Nonnull attrs, NSRange loopRange, BOOL * _Nonnull stop) { - if ((attrs[WKSourceEditorCustomKeyHorizontalTemplate] != nil) && - (loopRange.location == range.location && loopRange.length == range.length)) { - isTemplate = YES; - stop = YES; + if (attrs[WKSourceEditorCustomKeyHorizontalTemplate] != nil) { + if (unionRange.location == NSNotFound) { + unionRange = loopRange; + } else { + unionRange = NSUnionRange(unionRange, loopRange); } + stop = YES; + } }]; + + if (NSEqualRanges(unionRange, range)) { + isTemplate = YES; + } } return isTemplate; diff --git a/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift b/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift index 7a3e468d09b..f08fc586c86 100644 --- a/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift +++ b/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift @@ -61,6 +61,25 @@ final class WKSourceEditorTextFrameworkMediatorTests: XCTestCase { XCTAssertFalse(selectionStates9.isItalics) } + func testSelectionSpanningNonFormattedState1() throws { + let text = "Testing '''bold with {{template}}''' selection that spans nonbold." + mediator.textView.attributedText = NSAttributedString(string: text) + + // "bold with {{template}}" + let selectionStates = mediator.selectionState(selectedDocumentRange: NSRange(location: 11, length: 22)) + XCTAssertTrue(selectionStates.isBold) + XCTAssertFalse(selectionStates.isHorizontalTemplate) + } + + func testSelectionSpanningNonFormattedState2() throws { + let text = "Testing {{template | '''bold'''}} selection that spans nonbold." + mediator.textView.attributedText = NSAttributedString(string: text) + + // "template | '''bold'''" + let selectionStates1 = mediator.selectionState(selectedDocumentRange: NSRange(location: 10, length: 21)) + XCTAssertFalse(selectionStates1.isBold) + XCTAssertTrue(selectionStates1.isHorizontalTemplate) + } func testClosingBoldSelectionStateCursor() throws { let text = "One '''Two''' Three" mediator.textView.attributedText = NSAttributedString(string: text) @@ -138,6 +157,15 @@ final class WKSourceEditorTextFrameworkMediatorTests: XCTestCase { XCTAssertFalse(selectionStates1.isHorizontalTemplate) } + func testHorizontalTemplateButtonSelectionStateFormattedRange() throws { + let text = "Testing inner formatted {{cite web | url=https://en.wikipedia.org | title = The '''Free''' Encyclopedia}} template example." + mediator.textView.attributedText = NSAttributedString(string: text) + + // "cite web | url=https://en.wikipedia.org | title = The '''Free''' Encyclopedia" + let selectionStates = mediator.selectionState(selectedDocumentRange: NSRange(location: 26, length: 77)) + XCTAssertTrue(selectionStates.isHorizontalTemplate) + } + func testHeadingSelectionState() throws { let text = "== Test ==" @@ -237,7 +265,7 @@ final class WKSourceEditorTextFrameworkMediatorTests: XCTestCase { XCTAssertFalse(selectionStates2.isSubheading3) XCTAssertTrue(selectionStates2.isSubheading4) } - + func testStrikethroughSelectionState() throws { let text = "Testing Strikethrough Testing." mediator.textView.attributedText = NSAttributedString(string: text)