diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 08beb9e..ea2b02b 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - markymark (10.1.2) + - markymark (10.1.4) - SwiftLint (0.28.1) DEPENDENCIES: @@ -15,9 +15,9 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - markymark: 98903145fd4c412c9b7836c6a40e427652e8cca2 + markymark: 14802ff7ae7a9e84f4b363a5286fd6810480b0ac SwiftLint: 7f5f7de0da74a649b16616cb5246ae323489656e PODFILE CHECKSUM: e6179d5e64bda0057471cea1521ff93bf207a88b -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/Example/Tests/Rules/Inline/LinkRuleTests.swift b/Example/Tests/Rules/Inline/LinkRuleTests.swift index 7527586..40cfeb5 100644 --- a/Example/Tests/Rules/Inline/LinkRuleTests.swift +++ b/Example/Tests/Rules/Inline/LinkRuleTests.swift @@ -20,6 +20,7 @@ class LinkRuleTests: XCTestCase { XCTAssertFalse((sut.recognizesLines(["![Alt text](image.png)"]))) XCTAssertTrue(sut.recognizesLines([#"[Alt text](image.png "some title")"#])) XCTAssertTrue(sut.recognizesLines([#"[Alt text](https://www.website.com/ "some-test")"#])) + XCTAssertTrue(sut.recognizesLines([#"[Alt text](https://www.website.com/ "some"test"with"quotation"marks")"#])) } func test_DoesNotRecognizeLines_When_PrefixedWithExclamationMark() { @@ -35,15 +36,7 @@ class LinkRuleTests: XCTestCase { XCTAssert(sut.getAllMatches(["![Google](https://www.google.com)"]).isEmpty) } - func testCreateMarkDownItemWithLinesCreatesCorrectItem() { - // Act - let markDownItem = sut.createMarkDownItemWithLines(["[Google](http://www.google.com)"]) - - // Assert - XCTAssert(markDownItem is LinkMarkDownItem) - } - - func testCreateMarkDownItemContainsCorrectLink() { + func test_MarkDownItemContainsCorrectLink_When_CreatingMarkDownItemWithLines() { // Act let markDownItem = sut.createMarkDownItemWithLines(["[Google](http://www.google.com)"]) let markDownItem2 = sut.createMarkDownItemWithLines(["[Youtube](http://www.youtube.com)"]) @@ -55,19 +48,21 @@ class LinkRuleTests: XCTestCase { XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).url, "http://www.youtube.com") } - func testCreateMarkDownItemContainsCorrectLinkWhenUsingAriaLabel() { + func test_MarkDownItemContainsCorrectLink_When_CreatingMarkdownItemWithLinesUsingLinkTitle() { // Act let markDownItem = sut.createMarkDownItemWithLines([#"[Google](http://www.google.com "Google")"#]) let markDownItem2 = sut.createMarkDownItemWithLines([#"[Youtube](http://www.youtube.com "You-tube")"#]) // Assert XCTAssertEqual((markDownItem as! LinkMarkDownItem).content, "Google") + XCTAssertEqual((markDownItem as! LinkMarkDownItem).title, "Google") XCTAssertEqual((markDownItem as! LinkMarkDownItem).url, "http://www.google.com") XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).content, "Youtube") + XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).title, "You-tube") XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).url, "http://www.youtube.com") } - func testGetAllMatches() { + func test_GetsAllMatches_When_ProvidingLinks() { // Arrange let expectedMatchesRange = NSRange(location: 0, length: 32) let expectedMatchesRange2 = NSRange(location: 38, length: 32) @@ -81,16 +76,96 @@ class LinkRuleTests: XCTestCase { sut.getAllMatches(["[Google](https://www.google.com) test [Google](https://www.google.com)"]), [expectedMatchesRange, expectedMatchesRange2] ) + } + + func test_GetsAllMatches_When_ProvidingLinksWithAdditionalTitleValues() { + // Act + Assert + XCTAssertEqual( + sut.getAllMatches([#"[http://www.google.com](http://www.google.com "title"with"lots"of"quotationmarks")"#]), + [ + NSRange(location: 0, length: 82) + ] + ) + + XCTAssertEqual( + sut.getAllMatches([#"[Google](https://www.google.com "great-url-title") test [Google](https://www.google.com "a11y title")"#]), + [ + NSRange(location: 0, length: 50), + NSRange(location: 56, length: 45) + ] + ) XCTAssertEqual( - sut.getAllMatches([#"[Google](https://www.google.com) test [Google](https://www.google.com "a11y title")"#]), + sut.getAllMatches([#"[Google](https://www.google.com "great-url-title") test [Google](https://www.google.com "a11y title") and even more [https://www.apple.com](https://www.apple.com "Apple-aria-label") test"#]), [ - NSRange(location: 0, length: 32), - NSRange(location: 38, length: 45) + NSRange(location: 0, length: 50), + NSRange(location: 56, length: 45), + NSRange(location: 116, length: 65) ] ) } + func test_FailsToMatch_When_ProvidingLinksWithIncorrectSyntax() { + // Act + Assert + XCTAssertTrue(sut.getAllMatches([#"[Google](https://www.google.com great-url-title")"#]).isEmpty) + XCTAssertTrue(sut.getAllMatches([#"[Google](https://www.google.com great url title")"#]).isEmpty) + } + + func test_OnlyMatchesFirstLink_When_ProvidingOneCorrectLinkAndOneFaulty() { + // Act + Assert + XCTAssertEqual( + sut.getAllMatches([#"[Google](https://www.google.com "great-url-title") test [Google](https://www.google.com a11y title")"#]), + [ + NSRange(location: 0, length: 50) + ] + ) + } + + func test_ParsesAdditionalTitleItems_When_InputMatches() throws { + // Arrange + let cases: [(String, String?, UInt)] = [ + ( + #"[Google w/ title](http://www.google.com "with custom title")"#, + "with custom title", + #line + ), + ( + #"[Google w/ title](http://www.google.com "with-custom-title")"#, + "with-custom-title", + #line + ), + ( + #"[http://www.google.com](http://www.google.com "http://www.google.com")"#, + "http://www.google.com", + #line + ), + ( + #"[plain link](http://www.google.com "1234567890!@#$%^&*()")"#, + "1234567890!@#$%^&*()", + #line + ), + ( + #"[http://www.google.com](http://www.google.com "title"with"lots"of"quotationmarks")"#, + #"title"with"lots"of"quotationmarks"#, + #line + ), + ( + #"[plain link](http://www.google.com)"#, + nil, + #line + ) + ] + + for (input, title, line) in cases { + // Act + let output = sut.createMarkDownItemWithLines([input]) + + // Assert + let linkMarkDownItem = try XCTUnwrap(output as? LinkMarkDownItem) + XCTAssertEqual(linkMarkDownItem.title, title, line: line) + } + } + func test_ParsesItem_When_InputMatches() throws { // Arrange let cases: [(String, String, String, UInt)] = [ @@ -154,4 +229,31 @@ class LinkRuleTests: XCTestCase { XCTAssertEqual(linkMarkDownItem.url, url, line: line) } } + + func test_LinkItemsAreCorrect_When_CreatingMarkDownItemsWithContent() throws { + // Arrange + let input = #"[Google](https://www.google.com "great-url-title") test [Google](https://www.google.com "a11y title") and even more [https://www.apple.com](https://www.apple.com "Apple-title") test"# + let markyMark = MarkyMark(build: { + $0.setFlavor(ContentfulFlavor()) + }) + + // Act + let paragraphItem = markyMark.parseMarkDown(input).first + + // Assert + let linkMarkDownItem1 = try XCTUnwrap(paragraphItem?.markDownItems?[0] as? LinkMarkDownItem) + XCTAssertEqual(linkMarkDownItem1.content, "Google") + XCTAssertEqual(linkMarkDownItem1.url, "https://www.google.com") + XCTAssertEqual(linkMarkDownItem1.title, "great-url-title") + + let linkMarkDownItem2 = try XCTUnwrap(paragraphItem?.markDownItems?[2] as? LinkMarkDownItem) + XCTAssertEqual(linkMarkDownItem2.content, "Google") + XCTAssertEqual(linkMarkDownItem2.url, "https://www.google.com") + XCTAssertEqual(linkMarkDownItem2.title, "a11y title") + + let linkMarkDownItem3 = try XCTUnwrap(paragraphItem?.markDownItems?[4] as? LinkMarkDownItem) + XCTAssertEqual(linkMarkDownItem3.content, "https://www.apple.com") + XCTAssertEqual(linkMarkDownItem3.url, "https://www.apple.com") + XCTAssertEqual(linkMarkDownItem3.title, "Apple-title") + } } diff --git a/Example/markymark.xcodeproj/project.pbxproj b/Example/markymark.xcodeproj/project.pbxproj index 3aad183..bc317ac 100644 --- a/Example/markymark.xcodeproj/project.pbxproj +++ b/Example/markymark.xcodeproj/project.pbxproj @@ -434,18 +434,19 @@ 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; DevelopmentTeam = 3B6J93GERH; - LastSwiftMigration = 0900; + LastSwiftMigration = 1330; ProvisioningStyle = Automatic; }; 607FACE41AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; DevelopmentTeam = 3B6J93GERH; - LastSwiftMigration = 0900; + LastSwiftMigration = 1330; TestTargetID = 607FACCF1AFB9204008FA782; }; F95E6FF721A5E9FC006CA76E = { CreatedOnToolsVersion = 10.1; DevelopmentTeam = 3B6J93GERH; + LastSwiftMigration = 1330; ProvisioningStyle = Automatic; }; }; @@ -828,7 +829,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -847,7 +848,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -869,7 +870,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -887,7 +888,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -916,7 +917,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -943,7 +944,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.markymark-Example.TodayExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/markymark.podspec b/markymark.podspec index 5335382..8b9f710 100644 --- a/markymark.podspec +++ b/markymark.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "markymark" - s.version = "10.1.3" + s.version = "10.1.4" s.summary = "Markdown parser for iOS" s.description = <<-DESC Marky Mark is a parser written in Swift that converts markdown into native views. The way it looks is highly customizable and the supported markdown syntax and tags are easy to extend. @@ -12,7 +12,6 @@ Marky Mark is a parser written in Swift that converts markdown into native views s.source = { :git => "https://github.com/M2Mobi/Marky-Mark.git", :tag => s.version.to_s } s.ios.deployment_target = '8.0' - s.swift_version = '4.2' s.source_files = 'markymark/Classes/**/*{.swift}' end diff --git a/markymark/Classes/Extensions/String+extensions.swift b/markymark/Classes/Extensions/String+extensions.swift index 305d456..1c5c12f 100644 --- a/markymark/Classes/Extensions/String+extensions.swift +++ b/markymark/Classes/Extensions/String+extensions.swift @@ -20,16 +20,24 @@ extension String { return subString(startIndex, endIndex) } + func optionalSubString(_ range: NSRange) -> String? { + guard range.location != NSNotFound else { return nil } + return subString(range) + } + public func subStringWithExpression(_ expression: NSRegularExpression, ofGroup group: Int) -> String { - var subString = "" + optionalSubStringWithExpression(expression, ofGroup: group) ?? "" + } + + public func optionalSubStringWithExpression(_ expression: NSRegularExpression, ofGroup group: Int) -> String? { let range = NSRange(location: 0, length: self.length()) let results = expression.matches(in: self, options: [], range: range) if let result = results.first { - subString = self.subString(result.range(at: group)) + return optionalSubString(result.range(at: group)) } - return subString + return nil } /** diff --git a/markymark/Classes/MarkDown Items/LinkMarkDownItem.swift b/markymark/Classes/MarkDown Items/LinkMarkDownItem.swift index 5d3d581..8c8893e 100644 --- a/markymark/Classes/MarkDown Items/LinkMarkDownItem.swift +++ b/markymark/Classes/MarkDown Items/LinkMarkDownItem.swift @@ -7,10 +7,12 @@ import Foundation open class LinkMarkDownItem: MarkDownItem { + let title: String? let url: String - public init(lines: [String], content: String, url: String) { + public init(lines: [String], content: String, title: String?, url: String) { self.url = url + self.title = title super.init(lines: lines, content: content) } diff --git a/markymark/Classes/Rules/Inline/LinkRule.swift b/markymark/Classes/Rules/Inline/LinkRule.swift index 46b3539..e5378c5 100644 --- a/markymark/Classes/Rules/Inline/LinkRule.swift +++ b/markymark/Classes/Rules/Inline/LinkRule.swift @@ -12,18 +12,20 @@ open class LinkRule: InlineRegexRule { /// Example: [Google](http://www.google.com "with custom title") open var expression = NSRegularExpression.expressionWithPattern( // [ title ] ( URL " optional title " ) - #"(? MarkDownItem { + let title: String? = lines.first?.optionalSubStringWithExpression(expression, ofGroup: 4) let url: String? = lines.first?.subStringWithExpression(expression, ofGroup: 2) let content: String? = lines.first?.subStringWithExpression(expression, ofGroup: 1) return LinkMarkDownItem( lines: lines, content: content ?? "", + title: title, url: url ?? "" ) }