Skip to content

Commit 96903a6

Browse files
authored
Bugfix/link title breaking further line parsing (#115)
- 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
1 parent b7adfa0 commit 96903a6

File tree

7 files changed

+146
-32
lines changed

7 files changed

+146
-32
lines changed

Example/Podfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PODS:
2-
- markymark (10.1.2)
2+
- markymark (10.1.4)
33
- SwiftLint (0.28.1)
44

55
DEPENDENCIES:
@@ -15,9 +15,9 @@ EXTERNAL SOURCES:
1515
:path: "../"
1616

1717
SPEC CHECKSUMS:
18-
markymark: 98903145fd4c412c9b7836c6a40e427652e8cca2
18+
markymark: 14802ff7ae7a9e84f4b363a5286fd6810480b0ac
1919
SwiftLint: 7f5f7de0da74a649b16616cb5246ae323489656e
2020

2121
PODFILE CHECKSUM: e6179d5e64bda0057471cea1521ff93bf207a88b
2222

23-
COCOAPODS: 1.11.2
23+
COCOAPODS: 1.11.3

Example/Tests/Rules/Inline/LinkRuleTests.swift

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class LinkRuleTests: XCTestCase {
2020
XCTAssertFalse((sut.recognizesLines(["![Alt text](image.png)"])))
2121
XCTAssertTrue(sut.recognizesLines([#"[Alt text](image.png "some title")"#]))
2222
XCTAssertTrue(sut.recognizesLines([#"[Alt text](https://www.website.com/ "some-test")"#]))
23+
XCTAssertTrue(sut.recognizesLines([#"[Alt text](https://www.website.com/ "some"test"with"quotation"marks")"#]))
2324
}
2425

2526
func test_DoesNotRecognizeLines_When_PrefixedWithExclamationMark() {
@@ -35,15 +36,7 @@ class LinkRuleTests: XCTestCase {
3536
XCTAssert(sut.getAllMatches(["![Google](https://www.google.com)"]).isEmpty)
3637
}
3738

38-
func testCreateMarkDownItemWithLinesCreatesCorrectItem() {
39-
// Act
40-
let markDownItem = sut.createMarkDownItemWithLines(["[Google](http://www.google.com)"])
41-
42-
// Assert
43-
XCTAssert(markDownItem is LinkMarkDownItem)
44-
}
45-
46-
func testCreateMarkDownItemContainsCorrectLink() {
39+
func test_MarkDownItemContainsCorrectLink_When_CreatingMarkDownItemWithLines() {
4740
// Act
4841
let markDownItem = sut.createMarkDownItemWithLines(["[Google](http://www.google.com)"])
4942
let markDownItem2 = sut.createMarkDownItemWithLines(["[Youtube](http://www.youtube.com)"])
@@ -55,19 +48,21 @@ class LinkRuleTests: XCTestCase {
5548
XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).url, "http://www.youtube.com")
5649
}
5750

58-
func testCreateMarkDownItemContainsCorrectLinkWhenUsingAriaLabel() {
51+
func test_MarkDownItemContainsCorrectLink_When_CreatingMarkdownItemWithLinesUsingLinkTitle() {
5952
// Act
6053
let markDownItem = sut.createMarkDownItemWithLines([#"[Google](http://www.google.com "Google")"#])
6154
let markDownItem2 = sut.createMarkDownItemWithLines([#"[Youtube](http://www.youtube.com "You-tube")"#])
6255

6356
// Assert
6457
XCTAssertEqual((markDownItem as! LinkMarkDownItem).content, "Google")
58+
XCTAssertEqual((markDownItem as! LinkMarkDownItem).title, "Google")
6559
XCTAssertEqual((markDownItem as! LinkMarkDownItem).url, "http://www.google.com")
6660
XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).content, "Youtube")
61+
XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).title, "You-tube")
6762
XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).url, "http://www.youtube.com")
6863
}
6964

70-
func testGetAllMatches() {
65+
func test_GetsAllMatches_When_ProvidingLinks() {
7166
// Arrange
7267
let expectedMatchesRange = NSRange(location: 0, length: 32)
7368
let expectedMatchesRange2 = NSRange(location: 38, length: 32)
@@ -81,16 +76,96 @@ class LinkRuleTests: XCTestCase {
8176
sut.getAllMatches(["[Google](https://www.google.com) test [Google](https://www.google.com)"]),
8277
[expectedMatchesRange, expectedMatchesRange2]
8378
)
79+
}
80+
81+
func test_GetsAllMatches_When_ProvidingLinksWithAdditionalTitleValues() {
82+
// Act + Assert
83+
XCTAssertEqual(
84+
sut.getAllMatches([#"[http://www.google.com](http://www.google.com "title"with"lots"of"quotationmarks")"#]),
85+
[
86+
NSRange(location: 0, length: 82)
87+
]
88+
)
89+
90+
XCTAssertEqual(
91+
sut.getAllMatches([#"[Google](https://www.google.com "great-url-title") test [Google](https://www.google.com "a11y title")"#]),
92+
[
93+
NSRange(location: 0, length: 50),
94+
NSRange(location: 56, length: 45)
95+
]
96+
)
8497

8598
XCTAssertEqual(
86-
sut.getAllMatches([#"[Google](https://www.google.com) test [Google](https://www.google.com "a11y title")"#]),
99+
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"#]),
87100
[
88-
NSRange(location: 0, length: 32),
89-
NSRange(location: 38, length: 45)
101+
NSRange(location: 0, length: 50),
102+
NSRange(location: 56, length: 45),
103+
NSRange(location: 116, length: 65)
90104
]
91105
)
92106
}
93107

108+
func test_FailsToMatch_When_ProvidingLinksWithIncorrectSyntax() {
109+
// Act + Assert
110+
XCTAssertTrue(sut.getAllMatches([#"[Google](https://www.google.com great-url-title")"#]).isEmpty)
111+
XCTAssertTrue(sut.getAllMatches([#"[Google](https://www.google.com great url title")"#]).isEmpty)
112+
}
113+
114+
func test_OnlyMatchesFirstLink_When_ProvidingOneCorrectLinkAndOneFaulty() {
115+
// Act + Assert
116+
XCTAssertEqual(
117+
sut.getAllMatches([#"[Google](https://www.google.com "great-url-title") test [Google](https://www.google.com a11y title")"#]),
118+
[
119+
NSRange(location: 0, length: 50)
120+
]
121+
)
122+
}
123+
124+
func test_ParsesAdditionalTitleItems_When_InputMatches() throws {
125+
// Arrange
126+
let cases: [(String, String?, UInt)] = [
127+
(
128+
#"[Google w/ title](http://www.google.com "with custom title")"#,
129+
"with custom title",
130+
#line
131+
),
132+
(
133+
#"[Google w/ title](http://www.google.com "with-custom-title")"#,
134+
"with-custom-title",
135+
#line
136+
),
137+
(
138+
#"[http://www.google.com](http://www.google.com "http://www.google.com")"#,
139+
"http://www.google.com",
140+
#line
141+
),
142+
(
143+
#"[plain link](http://www.google.com "1234567890!@#$%^&*()")"#,
144+
"1234567890!@#$%^&*()",
145+
#line
146+
),
147+
(
148+
#"[http://www.google.com](http://www.google.com "title"with"lots"of"quotationmarks")"#,
149+
#"title"with"lots"of"quotationmarks"#,
150+
#line
151+
),
152+
(
153+
#"[plain link](http://www.google.com)"#,
154+
nil,
155+
#line
156+
)
157+
]
158+
159+
for (input, title, line) in cases {
160+
// Act
161+
let output = sut.createMarkDownItemWithLines([input])
162+
163+
// Assert
164+
let linkMarkDownItem = try XCTUnwrap(output as? LinkMarkDownItem)
165+
XCTAssertEqual(linkMarkDownItem.title, title, line: line)
166+
}
167+
}
168+
94169
func test_ParsesItem_When_InputMatches() throws {
95170
// Arrange
96171
let cases: [(String, String, String, UInt)] = [
@@ -154,4 +229,31 @@ class LinkRuleTests: XCTestCase {
154229
XCTAssertEqual(linkMarkDownItem.url, url, line: line)
155230
}
156231
}
232+
233+
func test_LinkItemsAreCorrect_When_CreatingMarkDownItemsWithContent() throws {
234+
// Arrange
235+
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"#
236+
let markyMark = MarkyMark(build: {
237+
$0.setFlavor(ContentfulFlavor())
238+
})
239+
240+
// Act
241+
let paragraphItem = markyMark.parseMarkDown(input).first
242+
243+
// Assert
244+
let linkMarkDownItem1 = try XCTUnwrap(paragraphItem?.markDownItems?[0] as? LinkMarkDownItem)
245+
XCTAssertEqual(linkMarkDownItem1.content, "Google")
246+
XCTAssertEqual(linkMarkDownItem1.url, "https://www.google.com")
247+
XCTAssertEqual(linkMarkDownItem1.title, "great-url-title")
248+
249+
let linkMarkDownItem2 = try XCTUnwrap(paragraphItem?.markDownItems?[2] as? LinkMarkDownItem)
250+
XCTAssertEqual(linkMarkDownItem2.content, "Google")
251+
XCTAssertEqual(linkMarkDownItem2.url, "https://www.google.com")
252+
XCTAssertEqual(linkMarkDownItem2.title, "a11y title")
253+
254+
let linkMarkDownItem3 = try XCTUnwrap(paragraphItem?.markDownItems?[4] as? LinkMarkDownItem)
255+
XCTAssertEqual(linkMarkDownItem3.content, "https://www.apple.com")
256+
XCTAssertEqual(linkMarkDownItem3.url, "https://www.apple.com")
257+
XCTAssertEqual(linkMarkDownItem3.title, "Apple-title")
258+
}
157259
}

Example/markymark.xcodeproj/project.pbxproj

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -434,18 +434,19 @@
434434
607FACCF1AFB9204008FA782 = {
435435
CreatedOnToolsVersion = 6.3.1;
436436
DevelopmentTeam = 3B6J93GERH;
437-
LastSwiftMigration = 0900;
437+
LastSwiftMigration = 1330;
438438
ProvisioningStyle = Automatic;
439439
};
440440
607FACE41AFB9204008FA782 = {
441441
CreatedOnToolsVersion = 6.3.1;
442442
DevelopmentTeam = 3B6J93GERH;
443-
LastSwiftMigration = 0900;
443+
LastSwiftMigration = 1330;
444444
TestTargetID = 607FACCF1AFB9204008FA782;
445445
};
446446
F95E6FF721A5E9FC006CA76E = {
447447
CreatedOnToolsVersion = 10.1;
448448
DevelopmentTeam = 3B6J93GERH;
449+
LastSwiftMigration = 1330;
449450
ProvisioningStyle = Automatic;
450451
};
451452
};
@@ -828,7 +829,7 @@
828829
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
829830
PRODUCT_NAME = "$(TARGET_NAME)";
830831
PROVISIONING_PROFILE = "";
831-
SWIFT_VERSION = 4.2;
832+
SWIFT_VERSION = 5.0;
832833
};
833834
name = Debug;
834835
};
@@ -847,7 +848,7 @@
847848
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
848849
PRODUCT_NAME = "$(TARGET_NAME)";
849850
PROVISIONING_PROFILE = "";
850-
SWIFT_VERSION = 4.2;
851+
SWIFT_VERSION = 5.0;
851852
};
852853
name = Release;
853854
};
@@ -869,7 +870,7 @@
869870
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
870871
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
871872
PRODUCT_NAME = "$(TARGET_NAME)";
872-
SWIFT_VERSION = 4.2;
873+
SWIFT_VERSION = 5.0;
873874
};
874875
name = Debug;
875876
};
@@ -887,7 +888,7 @@
887888
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
888889
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
889890
PRODUCT_NAME = "$(TARGET_NAME)";
890-
SWIFT_VERSION = 4.2;
891+
SWIFT_VERSION = 5.0;
891892
};
892893
name = Release;
893894
};
@@ -916,7 +917,7 @@
916917
PRODUCT_NAME = "$(TARGET_NAME)";
917918
SKIP_INSTALL = YES;
918919
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
919-
SWIFT_VERSION = 4.2;
920+
SWIFT_VERSION = 5.0;
920921
TARGETED_DEVICE_FAMILY = "1,2";
921922
};
922923
name = Debug;
@@ -943,7 +944,7 @@
943944
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.markymark-Example.TodayExtension";
944945
PRODUCT_NAME = "$(TARGET_NAME)";
945946
SKIP_INSTALL = YES;
946-
SWIFT_VERSION = 4.2;
947+
SWIFT_VERSION = 5.0;
947948
TARGETED_DEVICE_FAMILY = "1,2";
948949
};
949950
name = Release;

markymark.podspec

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "markymark"
3-
s.version = "10.1.3"
3+
s.version = "10.1.4"
44
s.summary = "Markdown parser for iOS"
55
s.description = <<-DESC
66
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
1212
s.source = { :git => "https://github.com/M2Mobi/Marky-Mark.git", :tag => s.version.to_s }
1313

1414
s.ios.deployment_target = '8.0'
15-
s.swift_version = '4.2'
1615

1716
s.source_files = 'markymark/Classes/**/*{.swift}'
1817
end

markymark/Classes/Extensions/String+extensions.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,24 @@ extension String {
2020
return subString(startIndex, endIndex)
2121
}
2222

23+
func optionalSubString(_ range: NSRange) -> String? {
24+
guard range.location != NSNotFound else { return nil }
25+
return subString(range)
26+
}
27+
2328
public func subStringWithExpression(_ expression: NSRegularExpression, ofGroup group: Int) -> String {
24-
var subString = ""
29+
optionalSubStringWithExpression(expression, ofGroup: group) ?? ""
30+
}
31+
32+
public func optionalSubStringWithExpression(_ expression: NSRegularExpression, ofGroup group: Int) -> String? {
2533
let range = NSRange(location: 0, length: self.length())
2634
let results = expression.matches(in: self, options: [], range: range)
2735

2836
if let result = results.first {
29-
subString = self.subString(result.range(at: group))
37+
return optionalSubString(result.range(at: group))
3038
}
3139

32-
return subString
40+
return nil
3341
}
3442

3543
/**

markymark/Classes/MarkDown Items/LinkMarkDownItem.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import Foundation
77

88
open class LinkMarkDownItem: MarkDownItem {
99

10+
let title: String?
1011
let url: String
1112

12-
public init(lines: [String], content: String, url: String) {
13+
public init(lines: [String], content: String, title: String?, url: String) {
1314
self.url = url
15+
self.title = title
1416
super.init(lines: lines, content: content)
1517
}
1618

markymark/Classes/Rules/Inline/LinkRule.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ open class LinkRule: InlineRegexRule {
1212
/// Example: [Google](http://www.google.com "with custom title")
1313
open var expression = NSRegularExpression.expressionWithPattern(
1414
// [ title ] ( URL " optional title " )
15-
#"(?<!!\p{Z}?)\[{1}(.+?)\]\({1}(.+?)( "[[:print:]^"]+")?\)"#
15+
#"(?<!!\p{Z}?)\[{1}(.+?)\]\({1}([^ ]+?)((?: "(.+?)[\")])?)?\)"#
1616
)
1717

1818
// MARK: Rule
1919

2020
open func createMarkDownItemWithLines(_ lines: [String]) -> MarkDownItem {
21+
let title: String? = lines.first?.optionalSubStringWithExpression(expression, ofGroup: 4)
2122
let url: String? = lines.first?.subStringWithExpression(expression, ofGroup: 2)
2223
let content: String? = lines.first?.subStringWithExpression(expression, ofGroup: 1)
2324

2425
return LinkMarkDownItem(
2526
lines: lines,
2627
content: content ?? "",
28+
title: title,
2729
url: url ?? ""
2830
)
2931
}

0 commit comments

Comments
 (0)