diff --git a/Classes/Issues/Comments/Markdown/String+DetectShortlink.swift b/Classes/Issues/Comments/Markdown/String+DetectShortlink.swift index a9d6990c4..f69207838 100644 --- a/Classes/Issues/Comments/Markdown/String+DetectShortlink.swift +++ b/Classes/Issues/Comments/Markdown/String+DetectShortlink.swift @@ -16,7 +16,7 @@ extension NSRange { } } -private let regex = try! NSRegularExpression(pattern: "(^|\\s|[^a-zA-Z])((\\w+)/(\\w+))?#([0-9]+)([^a-zA-Z0-9]|$)", options: []) +private let regex = try! NSRegularExpression(pattern: "(^|\\s|[^a-zA-Z])((\\w+)/(\\w+))?#([0-9]+)(?![a-zA-Z0-9])", options: []) extension String { func detectAndHandleShortlink(owner: String, repo: String, builder: StyledTextBuilder) { let matches = regex.matches(in: self, options: [], range: nsrange) diff --git a/FreetimeTests/DetectShortlinkTests.swift b/FreetimeTests/DetectShortlinkTests.swift index 91ce94ae1..d4f88688e 100644 --- a/FreetimeTests/DetectShortlinkTests.swift +++ b/FreetimeTests/DetectShortlinkTests.swift @@ -22,9 +22,10 @@ class DetectShortlinkTests: XCTestCase { return builder.build() } - func checkForIssueLink(_ styledTexts: [StyledText]) -> (linkText: String, issueNumber: Int)? { + func checkForIssueLink(_ styledTexts: [StyledText]) -> [(linkText: String, issueNumber: Int)] { // scanning for a styledText unit that has been formatted with blue font and // contains an Issue MarkdownAttribute + var links = [(linkText: String, issueNumber: Int)]() for styledText in styledTexts { let style = styledText.style guard style.attributes[.foregroundColor] != nil, @@ -34,11 +35,11 @@ class DetectShortlinkTests: XCTestCase { if case let .text(text) = styledText.storage { let issueModel = style.attributes[MarkdownAttribute.issue] as! IssueDetailsModel let issueNumber = issueModel.number - return (text, issueNumber) + links.append((text, issueNumber)) } } } - return nil + return links } func test_positiveMatches() { @@ -50,83 +51,110 @@ class DetectShortlinkTests: XCTestCase { var testString = "#1234" var builder: StyledTextString = setupBuilder(with: testString) - var containsLink = checkForIssueLink(builder.styledTexts)! + var containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#1234") XCTAssertEqual(containsLink.issueNumber, 1234) XCTAssertEqual(builder.allText, testString) testString = "with a space preceding #1235" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#1235") XCTAssertEqual(containsLink.issueNumber, 1235) XCTAssertEqual(builder.allText, testString) testString = "with a newline preceding \n#345" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] + XCTAssertEqual(containsLink.linkText, "#345") + XCTAssertEqual(containsLink.issueNumber, 345) + XCTAssertEqual(builder.allText, testString) + + testString = + """ + #345 + newLine + """ + + builder = setupBuilder(with: testString) + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#345") XCTAssertEqual(containsLink.issueNumber, 345) XCTAssertEqual(builder.allText, testString) testString = "embedded in parentheses (#1900)" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#1900") XCTAssertEqual(containsLink.issueNumber, 1900) XCTAssertEqual(builder.allText, testString) testString = "with owner and repo preceding rnystrom/githawk#4321" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "rnystrom/githawk#4321") XCTAssertEqual(containsLink.issueNumber, 4321) XCTAssertEqual(builder.allText, testString) testString = "Fixes (#1" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#1") XCTAssertEqual(containsLink.issueNumber, 1) XCTAssertEqual(builder.allText, testString) testString = "Fixes #12)" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#12") XCTAssertEqual(containsLink.issueNumber, 12) XCTAssertEqual(builder.allText, testString) testString = "Fixes(#432)" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#432") XCTAssertEqual(containsLink.issueNumber, 432) XCTAssertEqual(builder.allText, testString) testString = "!#4 yada yada" builder = setupBuilder(with: testString) - containsLink = checkForIssueLink(builder.styledTexts)! + containsLink = checkForIssueLink(builder.styledTexts)[0] XCTAssertEqual(containsLink.linkText, "#4") XCTAssertEqual(containsLink.issueNumber, 4) XCTAssertEqual(builder.allText, testString) } + + func test_ConsecutivePositiveMatches() { + var testString = "#100 #150 #200" + var builder = setupBuilder(with: testString) + var links = checkForIssueLink(builder.styledTexts) + + XCTAssertEqual(links[0].issueNumber, 100) + XCTAssertEqual(links[0].linkText, "#100") + + XCTAssertEqual(links[1].issueNumber, 150) + XCTAssertEqual(links[1].linkText, "#150") + + XCTAssertEqual(links[2].issueNumber, 200) + XCTAssertEqual(links[2].linkText, "#200") + } func test_negativeMatches() { var builder = setupBuilder(with: "!1234") var containsLink = checkForIssueLink(builder.styledTexts) - XCTAssertNil(containsLink) + XCTAssertEqual(containsLink.count, 0) builder = setupBuilder(with: "imo the best pr so far is prob # 1906") containsLink = checkForIssueLink(builder.styledTexts) - XCTAssertNil(containsLink) + XCTAssertEqual(containsLink.count, 0) builder = setupBuilder(with: "#123F") containsLink = checkForIssueLink(builder.styledTexts) - XCTAssertNil(containsLink) + XCTAssertEqual(containsLink.count, 0) builder = setupBuilder(with: "f#123") containsLink = checkForIssueLink(builder.styledTexts) - XCTAssertNil(containsLink) + XCTAssertEqual(containsLink.count, 0) } }