diff --git a/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift b/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift index e0e70e9d2f6..2095711d9fa 100644 --- a/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift +++ b/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift @@ -16,19 +16,63 @@ extension WKSourceEditorFormatter { func toggleFormatting(startingFormattingString: String, endingFormattingString: String, action: WKSourceEditorFormatterButtonAction, in textView: UITextView) { - switch action { - case .remove: - expandSelectedRangeUpToNearestFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) - - if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) { - removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + if textView.selectedRange.length == 0 { + switch action { + case .remove: + expandSelectedRangeUpToNearestFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + + if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) { + removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + } + case .add: + if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) { + removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + } else { + addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + } } - case .add: - if textView.selectedRange.length == 0 && - selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) { - removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) - } else { - addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + } else { + + switch action { + case .remove: + + if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) { + removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + } else { + + // Note the flipped formatting string params. + // For example, this takes selected 'text' from: + // Testing Strikethrough text here + // To: + // Testing Strikethrough text here + // We have to add formatters in reverse order to remove formatting from 'text' + + addStringFormattingCharacters(startingFormattingString: endingFormattingString, endingFormattingString: startingFormattingString, in: textView) + } + + case .add: + + // Note: gross workaround to prevent italics misfire from continuing below + if startingFormattingString == "''" && endingFormattingString == "''" { + if selectedRangeIsSurroundedByFormattingString(formattingString: "''", in: textView) && + selectedRangeIsSurroundedByFormattingString(formattingString: "'''", in: textView) { + addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + return + } + } + + // Note the flipped formatting string params. + // For example, this takes selected 'text' from: + // Testing Strikethrough text here + // To: + // Testing Strikethrough text here + // We have to check and remove formatters in reverse order to add formatting to 'text' + + if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: endingFormattingString, endingFormattingString: startingFormattingString, in: textView) { + removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: endingFormattingString, endingFormattingString: startingFormattingString, in: textView) + } else { + addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) + } } } } diff --git a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m index f376e5eec50..990d0573a3e 100644 --- a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m +++ b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m @@ -278,70 +278,54 @@ - (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) { if (attributedString.length > range.location) { NSDictionary *attrs = [attributedString attributesAtIndex:range.location effectiveRange:nil]; - - if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) { - isItalics = YES; + if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[formattingKey] != nil) { + 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[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[formattingKey] != 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..b2e5bc1e321 100644 --- a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m +++ b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m @@ -33,8 +33,8 @@ - (instancetype)initWithColors:(WKSourceEditorColors *)colors fonts:(WKSourceEdi WKSourceEditorCustomKeyVerticalTemplate: [NSNumber numberWithBool:YES] }; - _horizontalTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"\\{{2}[^\\{\\}\\n]*\\}{2}" options:0 error:nil]; - _verticalStartTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^\\{{2}[^\\{\\}\\n]*$" options:NSRegularExpressionAnchorsMatchLines error:nil]; + _horizontalTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"\\{{2}[^\\{\\}\\n]*(?:\\{{2}[^\\{\\}\\n]*\\}{2})*[^\\{\\}\\n]*\\}{2}" options:0 error:nil]; + _verticalStartTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^(?:.*)(\\{{2}[^\\{\\}\\n]*)$" options:NSRegularExpressionAnchorsMatchLines error:nil]; _verticalParameterTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^\\s*\\|.*$" options:NSRegularExpressionAnchorsMatchLines error:nil]; _verticalEndTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^([^\\{\\}\n]*\\}{2})(?:.)*$" options:NSRegularExpressionAnchorsMatchLines error:nil]; } @@ -64,11 +64,12 @@ - (void)addSyntaxHighlightingToAttributedString:(nonnull NSMutableAttributedStri options:0 range:range usingBlock:^(NSTextCheckingResult *_Nullable result, NSMatchingFlags flags, BOOL *_Nonnull stop) { - NSRange matchRange = [result rangeAtIndex:0]; + NSRange fullMatch = [result rangeAtIndex:0]; + NSRange openingTemplateRange = [result rangeAtIndex:1]; - if (matchRange.location != NSNotFound) { - [attributedString addAttributes:self.verticalTemplateAttributes range:matchRange]; - } + if (fullMatch.location != NSNotFound && openingTemplateRange.location != NSNotFound) { + [attributedString addAttributes:self.verticalTemplateAttributes range:openingTemplateRange]; + } }]; [self.verticalParameterTemplateRegex enumerateMatchesInString:attributedString.string @@ -86,12 +87,14 @@ - (void)addSyntaxHighlightingToAttributedString:(nonnull NSMutableAttributedStri options:0 range:range usingBlock:^(NSTextCheckingResult *_Nullable result, NSMatchingFlags flags, BOOL *_Nonnull stop) { + NSRange fullMatch = [result rangeAtIndex:0]; NSRange closingTemplateRange = [result rangeAtIndex:1]; - if (fullMatch.location != NSNotFound && closingTemplateRange.location != NSNotFound) { - [attributedString addAttributes:self.verticalTemplateAttributes range:closingTemplateRange]; - } + if (fullMatch.location != NSNotFound && closingTemplateRange.location != NSNotFound) { + [attributedString addAttributes:self.verticalTemplateAttributes range:closingTemplateRange]; + } + }]; } @@ -149,13 +152,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/WKSourceEditorFormatterButtonActionTests.swift b/Components/Tests/ComponentsTests/WKSourceEditorFormatterButtonActionTests.swift index 35d8f3b5a64..5fd35883a8c 100644 --- a/Components/Tests/ComponentsTests/WKSourceEditorFormatterButtonActionTests.swift +++ b/Components/Tests/ComponentsTests/WKSourceEditorFormatterButtonActionTests.swift @@ -42,7 +42,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase { XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four") } - func testSingleBoldRemove() throws { + func testCursorBoldRemove() throws { let text = "One '''Two''' Three Four" mediator.textView.attributedText = NSAttributedString(string: text) mediator.textView.selectedRange = NSRange(location: 8, length: 0) // Just a cursor inside Two @@ -50,7 +50,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase { XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four") } - func testSingleItalicsRemove() throws { + func testCursorItalicsRemove() throws { let text = "One Two '''Three''' Four" mediator.textView.attributedText = NSAttributedString(string: text) mediator.textView.selectedRange = NSRange(location: 14, length: 0) @@ -77,7 +77,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase { XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four") } - func testSingleBoldInsertAndRemove() throws { + func testCursorBoldInsertAndRemove() throws { let text = "One Two Three Four" mediator.textView.attributedText = NSAttributedString(string: text) mediator.textView.selectedRange = NSRange(location: 4, length: 0) // Just a cursor before Two @@ -87,7 +87,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase { XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four") } - func testSingleItalicsInsertAndRemove() throws { + func testCursorItalicsInsertAndRemove() throws { let text = "One Two Three Four" mediator.textView.attributedText = NSAttributedString(string: text) mediator.textView.selectedRange = NSRange(location: 4, length: 0) // Just a cursor before Two @@ -97,6 +97,46 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase { XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four") } + func testBoldInnerRemoveAndInsert() throws { + let text = "One '''Two Three Four''' Five" + mediator.textView.attributedText = NSAttributedString(string: text) + mediator.textView.selectedRange = NSRange(location: 11, length: 5) // Selected Three + mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One '''Two '''Three''' Four''' Five") + mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One '''Two Three Four''' Five") + } + + func testItalicsInnerRemoveAndInsert() throws { + let text = "One ''Two Three Four'' Five" + mediator.textView.attributedText = NSAttributedString(string: text) + mediator.textView.selectedRange = NSRange(location: 10, length: 5) // Selected Three + mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One ''Two ''Three'' Four'' Five") + mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One ''Two Three Four'' Five") + } + + func testBoldItalicsInnerRemoveBoldAndInsert() throws { + let text = "One '''''Two Three Four''''' Five" + mediator.textView.attributedText = NSAttributedString(string: text) + mediator.textView.selectedRange = NSRange(location: 13, length: 5) // Selected Three + mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two '''Three''' Four''''' Five") + mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two Three Four''''' Five") + } + + func testBoldItalicsInnerRemoveItalicsAndInsert() throws { + let text = "One '''''Two Three Four''''' Five" + mediator.textView.attributedText = NSAttributedString(string: text) + mediator.textView.selectedRange = NSRange(location: 13, length: 5) // Selected Three + mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two ''Three'' Four''''' Five") + mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView) + XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two Three Four''''' Five") + } + func testTemplateInsert() throws { let text = "One Two Three Four" mediator.textView.attributedText = NSAttributedString(string: text) @@ -113,7 +153,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase { XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four") } - func testSingleTemplateInsertAndRemove() throws { + func testCursorTemplateInsertAndRemove() throws { let text = "One Two Three Four" mediator.textView.attributedText = NSAttributedString(string: text) mediator.textView.selectedRange = NSRange(location: 4, length: 0) // Just a cursor before Two diff --git a/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift b/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift index a3764296794..499e3bcb247 100644 --- a/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift +++ b/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift @@ -633,7 +633,43 @@ final class WKSourceEditorFormatterTests: XCTestCase { XCTAssertEqual(refClosingAttributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect ref formatting") } - func testVerticalStartTemplate() { + func testHorizontalNestedTemplate() { + let string = "Ford Island ({{lang-haw|Poka {{okina}}Ailana}}) is an" + let mutAttributedString = NSMutableAttributedString(string: string) + + for formatter in formatters { + formatter.addSyntaxHighlighting(to: mutAttributedString, in: NSRange(location: 0, length: string.count)) + } + + var base1Range = NSRange(location: 0, length: 0) + let base1Attributes = mutAttributedString.attributes(at: 0, effectiveRange: &base1Range) + + var templateRange = NSRange(location: 0, length: 0) + let templateAttributes = mutAttributedString.attributes(at: 13, effectiveRange: &templateRange) + + var base2Range = NSRange(location: 0, length: 0) + let base2Attributes = mutAttributedString.attributes(at: 46, effectiveRange: &base2Range) + + // "Ford Island (" + XCTAssertEqual(base1Range.location, 0, "Incorrect base formatting") + XCTAssertEqual(base1Range.length, 13, "Incorrect base formatting") + XCTAssertEqual(base1Attributes[.font] as! UIFont, fonts.baseFont, "Incorrect base formatting") + XCTAssertEqual(base1Attributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect base formatting") + + // "{{lang-haw|Poka {{okina}}Ailana}}" + XCTAssertEqual(templateRange.location, 13, "Incorrect template formatting") + XCTAssertEqual(templateRange.length, 33, "Incorrect template formatting") + XCTAssertEqual(templateAttributes[.font] as! UIFont, fonts.baseFont, "Incorrect template formatting") + XCTAssertEqual(templateAttributes[.foregroundColor] as! UIColor, colors.purpleForegroundColor, "Incorrect template formatting") + + // ") is an" + XCTAssertEqual(base2Range.location, 46, "Incorrect base formatting") + XCTAssertEqual(base2Range.length, 7, "Incorrect base formatting") + XCTAssertEqual(base2Attributes[.font] as! UIFont, fonts.baseFont, "Incorrect base formatting") + XCTAssertEqual(base2Attributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect base formatting") + } + + func testVerticalStartTemplate1() { let string = "{{Infobox officeholder" let mutAttributedString = NSMutableAttributedString(string: string) @@ -651,6 +687,33 @@ final class WKSourceEditorFormatterTests: XCTestCase { XCTAssertEqual(templateAttributes[.foregroundColor] as! UIColor, colors.purpleForegroundColor, "Incorrect template formatting") } + func testVerticalStartTemplate2() { + let string = "ending of previous sentence. {{cite web" + let mutAttributedString = NSMutableAttributedString(string: string) + + for formatter in formatters { + formatter.addSyntaxHighlighting(to: mutAttributedString, in: NSRange(location: 0, length: string.count)) + } + + var baseRange = NSRange(location: 0, length: 0) + let baseAttributes = mutAttributedString.attributes(at: 0, effectiveRange: &baseRange) + + var templateRange = NSRange(location: 0, length: 0) + let templateAttributes = mutAttributedString.attributes(at: 29, effectiveRange: &templateRange) + + // "ending of previous sentence. " + XCTAssertEqual(baseRange.location, 0, "Incorrect base formatting") + XCTAssertEqual(baseRange.length, 29, "Incorrect base formatting") + XCTAssertEqual(baseAttributes[.font] as! UIFont, fonts.baseFont, "Incorrect base formatting") + XCTAssertEqual(baseAttributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect base formatting") + + // "ending of previous sentence. " + XCTAssertEqual(templateRange.location, 29, "Incorrect template formatting") + XCTAssertEqual(templateRange.length, 10, "Incorrect template formatting") + XCTAssertEqual(templateAttributes[.font] as! UIFont, fonts.baseFont, "Incorrect template formatting") + XCTAssertEqual(templateAttributes[.foregroundColor] as! UIColor, colors.purpleForegroundColor, "Incorrect base formatting") + } + func testVerticalParameterTemplate() { let string = "| genus = Felis" let mutAttributedString = NSMutableAttributedString(string: string) diff --git a/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift b/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift index f9e23050c16..5aa38f420ed 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 testStrikethroughSelectionState() throws { let text = "Testing Strikethrough Testing." mediator.textView.attributedText = NSAttributedString(string: text)