Skip to content

Commit 0c6f2c1

Browse files
edwinvegerEdwin Veger
andauthored
Improve LinkRule to accept titles and ignore internal markdown (#108)
* Add Title support to LinkRule * Adds support for links like: `[Google](http://www.google.com "custom title")` Co-authored-by: Edwin Veger <[email protected]>
1 parent 23eb0f5 commit 0c6f2c1

16 files changed

+199
-116
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 (9.2.1)
2+
- markymark (10.1.1)
33
- SwiftLint (0.28.1)
44

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

1717
SPEC CHECKSUMS:
18-
markymark: e7fa1cdb7ad51c21a46cda308ad8ef4511fa49f6
18+
markymark: 29adb8789fa6b3db97b04b7585d65f13931b5aa5
1919
SwiftLint: 7f5f7de0da74a649b16616cb5246ae323489656e
2020

2121
PODFILE CHECKSUM: e6179d5e64bda0057471cea1521ff93bf207a88b
2222

23-
COCOAPODS: 1.9.3
23+
COCOAPODS: 1.10.0

Example/Tests/Rules/Block/LinkRuleTests.swift

Lines changed: 0 additions & 61 deletions
This file was deleted.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//
2+
// Created by Maren Osnabrug on 10-05-16.
3+
// Copyright © 2016 M2mobi. All rights reserved.
4+
//
5+
6+
import XCTest
7+
@testable import markymark
8+
9+
class LinkRuleTests: XCTestCase {
10+
11+
var sut: LinkRule!
12+
13+
override func setUp() {
14+
super.setUp()
15+
sut = LinkRule()
16+
}
17+
18+
func testRecognizesLines() {
19+
XCTAssertTrue(sut.recognizesLines(["[Alt text](image.png)"]))
20+
XCTAssertFalse((sut.recognizesLines(["![Alt text](image.png)"])))
21+
XCTAssertTrue(sut.recognizesLines([#"[Alt text](image.png "some title")"#]))
22+
}
23+
24+
func test_DoesNotRecognizeLines_When_PrefixedWithExclamationMark() {
25+
XCTAssertFalse((sut.recognizesLines(["![Alt text](image.png)"])))
26+
XCTAssertFalse((sut.recognizesLines(["! [Alt text](image.png)"])))
27+
XCTAssertFalse((sut.recognizesLines([#"![Alt text](image.png "some title")"#])))
28+
}
29+
30+
func test_GetAllMatchesReturnsZero_When_InvalidInput() {
31+
// Assert
32+
XCTAssert(sut.getAllMatches(["(https://www.google.com)"]).isEmpty)
33+
XCTAssert(sut.getAllMatches(["[Google]"]).isEmpty)
34+
XCTAssert(sut.getAllMatches(["![Google](https://www.google.com)"]).isEmpty)
35+
}
36+
37+
func testCreateMarkDownItemWithLinesCreatesCorrectItem() {
38+
// Act
39+
let markDownItem = sut.createMarkDownItemWithLines(["[Google](http://www.google.com)"])
40+
41+
// Assert
42+
XCTAssert(markDownItem is LinkMarkDownItem)
43+
}
44+
45+
func testCreateMarkDownItemContainsCorrectLink() {
46+
// Act
47+
let markDownItem = sut.createMarkDownItemWithLines(["[Google](http://www.google.com)"])
48+
let markDownItem2 = sut.createMarkDownItemWithLines(["[Youtube](http://www.youtube.com)"])
49+
50+
// Assert
51+
XCTAssertEqual((markDownItem as! LinkMarkDownItem).content, "Google")
52+
XCTAssertEqual((markDownItem as! LinkMarkDownItem).url, "http://www.google.com")
53+
XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).content, "Youtube")
54+
XCTAssertEqual((markDownItem2 as! LinkMarkDownItem).url, "http://www.youtube.com")
55+
}
56+
57+
func testGetAllMatches() {
58+
// Arrange
59+
let expectedMatchesRange = NSRange(location: 0, length: 32)
60+
let expectedMatchesRange2 = NSRange(location: 38, length: 32)
61+
62+
// Act + Assert
63+
XCTAssertEqual(sut.getAllMatches(["[Google]"]).count, 0)
64+
XCTAssertEqual(sut.getAllMatches(["(https://www.google.com)"]).count, 0)
65+
XCTAssertEqual(sut.getAllMatches(["[Google](https://www.google.com)"]), [expectedMatchesRange])
66+
XCTAssertEqual(sut.getAllMatches(["![Google](https://www.google.com)"]).count, 0)
67+
XCTAssertEqual(
68+
sut.getAllMatches(["[Google](https://www.google.com) test [Google](https://www.google.com)"]),
69+
[expectedMatchesRange, expectedMatchesRange2]
70+
)
71+
72+
XCTAssertEqual(
73+
sut.getAllMatches([#"[Google](https://www.google.com) test [Google](https://www.google.com "a11y title")"#]),
74+
[
75+
NSRange(location: 0, length: 32),
76+
NSRange(location: 38, length: 45)
77+
]
78+
)
79+
}
80+
81+
func test_ParsesItem_When_InputMatches() throws {
82+
// Arrange
83+
let cases: [(String, String, String, UInt)] = [
84+
(
85+
#"[Google plain link](https://google.com)"#,
86+
"Google plain link",
87+
"https://google.com",
88+
#line
89+
),
90+
(
91+
#"[Google w/ title](http://www.google.com "with custom title")"#,
92+
"Google w/ title",
93+
"http://www.google.com",
94+
#line
95+
),
96+
(
97+
#"[Simple link](https://google.nl)"#,
98+
"Simple link",
99+
"https://google.nl",
100+
#line
101+
),
102+
(
103+
#"Inside [another link](http://m2mobi.com/) sentence"#,
104+
"another link",
105+
"http://m2mobi.com/",
106+
#line
107+
),
108+
(
109+
#"Inside [another link](http://m2mobi.com/ "with custom title") sentence"#,
110+
"another link",
111+
"http://m2mobi.com/",
112+
#line
113+
),
114+
(
115+
#"[Underscored link](https://google.nl/?param=a_b_c)"#,
116+
"Underscored link",
117+
"https://google.nl/?param=a_b_c",
118+
#line
119+
),
120+
(
121+
#"[Underscored link 2](https://google.nl/?param=a_b_c&d=e)"#,
122+
"Underscored link 2",
123+
"https://google.nl/?param=a_b_c&d=e",
124+
#line
125+
),
126+
(
127+
#"[Underscored link 4](https://google.nl/?param=a_b_c&d=e_f_g)"#,
128+
"Underscored link 4",
129+
"https://google.nl/?param=a_b_c&d=e_f_g",
130+
#line
131+
)
132+
]
133+
134+
for (input, content, url, line) in cases {
135+
// Act
136+
let output = sut.createMarkDownItemWithLines([input])
137+
138+
// Assert
139+
let linkMarkDownItem = try XCTUnwrap(output as? LinkMarkDownItem)
140+
XCTAssertEqual(linkMarkDownItem.content, content, line: line)
141+
XCTAssertEqual(linkMarkDownItem.url, url, line: line)
142+
}
143+
}
144+
}

Example/markymark.xcodeproj/project.pbxproj

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@
112112
9704F30C7296E4A0EA9A0087 /* Pods-markymark_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-markymark_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-markymark_Example/Pods-markymark_Example.release.xcconfig"; sourceTree = "<group>"; };
113113
99E80ED3FF06693617B60997 /* Pods-TodayExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TodayExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TodayExtension/Pods-TodayExtension.debug.xcconfig"; sourceTree = "<group>"; };
114114
9A06FCF01CF72B4D0040251D /* AlphabeticListTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlphabeticListTypeTests.swift; sourceTree = "<group>"; };
115-
9A8BE0A31CEE0416004593A0 /* BoldRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BoldRuleTests.swift; path = Block/BoldRuleTests.swift; sourceTree = "<group>"; };
116-
9A8BE0A41CEE0416004593A0 /* ImageRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageRuleTests.swift; path = Block/ImageRuleTests.swift; sourceTree = "<group>"; };
117-
9A8BE0A51CEE0416004593A0 /* InlineCodeRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InlineCodeRuleTests.swift; path = Block/InlineCodeRuleTests.swift; sourceTree = "<group>"; };
118-
9A8BE0A61CEE0416004593A0 /* InlineTextRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InlineTextRuleTests.swift; path = Block/InlineTextRuleTests.swift; sourceTree = "<group>"; };
119-
9A8BE0A71CEE0416004593A0 /* ItalicRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItalicRuleTests.swift; path = Block/ItalicRuleTests.swift; sourceTree = "<group>"; };
120-
9A8BE0A81CEE0416004593A0 /* LinkRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LinkRuleTests.swift; path = Block/LinkRuleTests.swift; sourceTree = "<group>"; };
121-
9A8BE0A91CEE0416004593A0 /* StrikeRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StrikeRuleTests.swift; path = Block/StrikeRuleTests.swift; sourceTree = "<group>"; };
115+
9A8BE0A31CEE0416004593A0 /* BoldRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoldRuleTests.swift; sourceTree = "<group>"; };
116+
9A8BE0A41CEE0416004593A0 /* ImageRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRuleTests.swift; sourceTree = "<group>"; };
117+
9A8BE0A51CEE0416004593A0 /* InlineCodeRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InlineCodeRuleTests.swift; sourceTree = "<group>"; };
118+
9A8BE0A61CEE0416004593A0 /* InlineTextRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InlineTextRuleTests.swift; sourceTree = "<group>"; };
119+
9A8BE0A71CEE0416004593A0 /* ItalicRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItalicRuleTests.swift; sourceTree = "<group>"; };
120+
9A8BE0A81CEE0416004593A0 /* LinkRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkRuleTests.swift; sourceTree = "<group>"; };
121+
9A8BE0A91CEE0416004593A0 /* StrikeRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StrikeRuleTests.swift; sourceTree = "<group>"; };
122122
9A8BE0B21CEE044E004593A0 /* BlockQuoteRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockQuoteRuleTests.swift; sourceTree = "<group>"; };
123123
9A8BE0B31CEE044E004593A0 /* CodeBlockRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeBlockRuleTests.swift; sourceTree = "<group>"; };
124124
9A8BE0B71CEE0458004593A0 /* HorizontalLineRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalLineRuleTests.swift; sourceTree = "<group>"; };
@@ -168,6 +168,20 @@
168168
/* End PBXFrameworksBuildPhase section */
169169

170170
/* Begin PBXGroup section */
171+
0C813F5425A4993300F805D0 /* Inline */ = {
172+
isa = PBXGroup;
173+
children = (
174+
9A8BE0A31CEE0416004593A0 /* BoldRuleTests.swift */,
175+
9A8BE0A41CEE0416004593A0 /* ImageRuleTests.swift */,
176+
9A8BE0A51CEE0416004593A0 /* InlineCodeRuleTests.swift */,
177+
9A8BE0A61CEE0416004593A0 /* InlineTextRuleTests.swift */,
178+
9A8BE0A71CEE0416004593A0 /* ItalicRuleTests.swift */,
179+
9A8BE0A81CEE0416004593A0 /* LinkRuleTests.swift */,
180+
9A8BE0A91CEE0416004593A0 /* StrikeRuleTests.swift */,
181+
);
182+
path = Inline;
183+
sourceTree = "<group>";
184+
};
171185
607FACC71AFB9204008FA782 = {
172186
isa = PBXGroup;
173187
children = (
@@ -252,8 +266,8 @@
252266
629C42251CEB47F900DA57D8 /* Rules */ = {
253267
isa = PBXGroup;
254268
children = (
255-
9A8BE0A21CEE03EB004593A0 /* Inline */,
256269
629C42261CEB47F900DA57D8 /* Block */,
270+
0C813F5425A4993300F805D0 /* Inline */,
257271
);
258272
path = Rules;
259273
sourceTree = "<group>";
@@ -315,20 +329,6 @@
315329
name = Pods;
316330
sourceTree = "<group>";
317331
};
318-
9A8BE0A21CEE03EB004593A0 /* Inline */ = {
319-
isa = PBXGroup;
320-
children = (
321-
9A8BE0A31CEE0416004593A0 /* BoldRuleTests.swift */,
322-
9A8BE0A41CEE0416004593A0 /* ImageRuleTests.swift */,
323-
9A8BE0A51CEE0416004593A0 /* InlineCodeRuleTests.swift */,
324-
9A8BE0A61CEE0416004593A0 /* InlineTextRuleTests.swift */,
325-
9A8BE0A71CEE0416004593A0 /* ItalicRuleTests.swift */,
326-
9A8BE0A81CEE0416004593A0 /* LinkRuleTests.swift */,
327-
9A8BE0A91CEE0416004593A0 /* StrikeRuleTests.swift */,
328-
);
329-
name = Inline;
330-
sourceTree = "<group>";
331-
};
332332
F927877321F8BB3D00039986 /* Helpers */ = {
333333
isa = PBXGroup;
334334
children = (
@@ -455,6 +455,7 @@
455455
developmentRegion = English;
456456
hasScannedForEncodings = 0;
457457
knownRegions = (
458+
English,
458459
en,
459460
Base,
460461
);

Example/markymark.xcodeproj/xcshareddata/xcschemes/markymark-Example.xcscheme

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,16 @@
4040
buildConfiguration = "Debug"
4141
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
4242
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
43-
language = ""
4443
shouldUseLaunchSchemeArgsEnv = "YES">
44+
<MacroExpansion>
45+
<BuildableReference
46+
BuildableIdentifier = "primary"
47+
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
48+
BuildableName = "markymark_Example.app"
49+
BlueprintName = "markymark_Example"
50+
ReferencedContainer = "container:markymark.xcodeproj">
51+
</BuildableReference>
52+
</MacroExpansion>
4553
<Testables>
4654
<TestableReference
4755
skipped = "NO">
@@ -54,23 +62,11 @@
5462
</BuildableReference>
5563
</TestableReference>
5664
</Testables>
57-
<MacroExpansion>
58-
<BuildableReference
59-
BuildableIdentifier = "primary"
60-
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
61-
BuildableName = "markymark_Example.app"
62-
BlueprintName = "markymark_Example"
63-
ReferencedContainer = "container:markymark.xcodeproj">
64-
</BuildableReference>
65-
</MacroExpansion>
66-
<AdditionalOptions>
67-
</AdditionalOptions>
6865
</TestAction>
6966
<LaunchAction
7067
buildConfiguration = "Debug"
7168
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
7269
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
73-
language = ""
7470
launchStyle = "0"
7571
useCustomWorkingDirectory = "NO"
7672
ignoresPersistentStateOnLaunch = "NO"
@@ -87,8 +83,6 @@
8783
ReferencedContainer = "container:markymark.xcodeproj">
8884
</BuildableReference>
8985
</BuildableProductRunnable>
90-
<AdditionalOptions>
91-
</AdditionalOptions>
9286
</LaunchAction>
9387
<ProfileAction
9488
buildConfiguration = "Release"

Example/markymark/markdown.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ Inline image ![Apple logo](appleLogo) Apple logo ![Apple logo](appleLogoSmall)
6868

6969
## Links
7070
[This is a test link](https://m2mobi.com)
71-
Inline links are also possible, click [here](https://m2mobi.com)
71+
Inline links are also possible, click [here](https://m2mobi.com).
72+
Do not worry about [possible markdown inside URLs](https://m2mobi.com?p_q=r_s&t_u), or [URLs with titles](https://m2mobi.com "Custom URL title").
73+
7274
# Code
7375
---
7476

markymark.podspec

Lines changed: 1 addition & 1 deletion
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.0"
3+
s.version = "10.1.1"
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.

markymark/Classes/Flavors/ContentfulFlavor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ open class ContentfulFlavor: Flavor {
2222
open var defaultRule: Rule = ParagraphRule()
2323

2424
open var inlineRules: [InlineRule] = [
25+
LinkRule(),
2526
BoldRule(),
2627
ItalicRule(),
2728
StrikeRule(),
2829
ImageRule(),
29-
LinkRule(),
3030
InlineCodeRule()
3131
]
3232

0 commit comments

Comments
 (0)