Skip to content

Commit

Permalink
Bugfix/link title breaking further line parsing (#115)
Browse files Browse the repository at this point in the history
- Fixes aug with multiple link's that contain a title on the same line, such as

```
"[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"
```

- Removes hardcoded Swift 4 version
  • Loading branch information
Basca authored Apr 15, 2022
1 parent b7adfa0 commit 96903a6
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 32 deletions.
6 changes: 3 additions & 3 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PODS:
- markymark (10.1.2)
- markymark (10.1.4)
- SwiftLint (0.28.1)

DEPENDENCIES:
Expand All @@ -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
130 changes: 116 additions & 14 deletions Example/Tests/Rules/Inline/LinkRuleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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)"])
Expand All @@ -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)
Expand All @@ -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)] = [
Expand Down Expand Up @@ -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")
}
}
17 changes: 9 additions & 8 deletions Example/markymark.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
};
Expand Down Expand Up @@ -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;
};
Expand All @@ -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;
};
Expand All @@ -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;
};
Expand All @@ -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;
};
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions markymark.podspec
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
14 changes: 11 additions & 3 deletions markymark/Classes/Extensions/String+extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/**
Expand Down
4 changes: 3 additions & 1 deletion markymark/Classes/MarkDown Items/LinkMarkDownItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
4 changes: 3 additions & 1 deletion markymark/Classes/Rules/Inline/LinkRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 " )
#"(?<!!\p{Z}?)\[{1}(.+?)\]\({1}(.+?)( "[[:print:]^"]+")?\)"#
#"(?<!!\p{Z}?)\[{1}(.+?)\]\({1}([^ ]+?)((?: "(.+?)[\")])?)?\)"#
)

// MARK: Rule

open func createMarkDownItemWithLines(_ lines: [String]) -> 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 ?? ""
)
}
Expand Down

0 comments on commit 96903a6

Please sign in to comment.