Compare commits

...

17 Commits

Author SHA1 Message Date
Simon Fairbairn 38920d2e09 Updating podspec 2016-03-06 13:47:40 +07:00
Simon Fairbairn cce77d50ce Version Bump to 12 2016-03-06 13:47:38 +07:00
Simon Fairbairn 23a756f23e Version Bump to 11 2016-03-06 13:45:52 +07:00
Simon Fairbairn 27741a1128 Version Bump to 10 2016-03-06 13:43:20 +07:00
Simon Fairbairn a57899227d Fixing warnings 2016-03-06 13:42:14 +07:00
Simon Fairbairn 0de132c464 Updating podspec 2016-03-06 13:39:38 +07:00
Simon Fairbairn f3d1cc33d0 Version Bump to 9 2016-03-06 13:39:37 +07:00
Simon Fairbairn 6023b1e959 Fixes #6 2016-03-06 13:39:13 +07:00
Simon Fairbairn 3a108fe096 Updating readme 2016-03-06 13:04:11 +07:00
Simon Fairbairn e6e074ed9f Updating podspec 2016-03-06 12:59:25 +07:00
Simon Fairbairn ad2d909681 Version Bump to 8 2016-03-06 12:59:23 +07:00
Simon Fairbairn a02c383230 Fixes #1 2016-03-06 12:59:08 +07:00
Simon Fairbairn 9b99765a59 Fixes #4, Fixes #3 2016-03-06 11:56:10 +07:00
Simon Fairbairn 5fae172428 Fixes #5 2016-03-06 10:28:54 +07:00
Simon Fairbairn 3e0ff29449 Updating readme 2016-03-05 19:19:50 +07:00
Simon Fairbairn 3c4b78254d Updating readme 2016-03-05 19:16:22 +07:00
Simon Fairbairn e7e4eda3c1 Updating readme 2016-03-05 19:13:15 +07:00
9 changed files with 473 additions and 129 deletions
+6 -4
View File
@@ -1,6 +1,6 @@
# SwiftyMarkdown
SwiftyMarkdown converts Markdown files and strings into NSAttributedString using sensible defaults and a Swift-style syntax. It uses dynamic type to set the font size for whatever font you want
SwiftyMarkdown converts Markdown files and strings into NSAttributedString using sensible defaults and a Swift-style syntax. It uses dynamic type to set the font size correctly with whatever font you'd like to use
## Usage
@@ -11,17 +11,19 @@ Text string
URL
if let url = NSBundle.mainBundle().URLForResource("file", withExtension: "md"), md = SwiftyMarkdown(url: url ) {
md.attributedString()
}
## Customisation
// Setting the body name will use this font for all the heading styles as well, unless explicitly overridden
md.body.fontName = "AvenirNextCondensed-Medium"
md.h1.color = UIColor.redColor()
md.h1.fontName = "AvenirNextCondensed-Bold"
![Screenshot](http://cl.ly/19292H3L2b34)
md.italic.color = UIColor.blueColor()
## Screenshot
![Screenshot](http://f.cl.ly/items/12332k3f2s0s0C281h2u/swiftymarkdown.png)
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SwiftyMarkdown"
s.version = "0.1.2"
s.version = "0.2.2"
s.summary = "Converts Markdown to NSAttributed String"
s.homepage = "https://github.com/SimonFairbairn/Stormcloud"
s.license = 'MIT'
+4 -6
View File
@@ -12,7 +12,6 @@
F4CE98911C8A921300D735C1 /* SwiftyMarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CE98901C8A921300D735C1 /* SwiftyMarkdownTests.swift */; };
F4CE989C1C8A922E00D735C1 /* SwiftyMarkdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CE989B1C8A922E00D735C1 /* SwiftyMarkdown.swift */; };
F4CE98E91C8AF01300D735C1 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = F4CE98E61C8AF01300D735C1 /* LICENSE */; };
F4CE98EA1C8AF01300D735C1 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = F4CE98E71C8AF01300D735C1 /* README.md */; };
F4CE98EB1C8AF01300D735C1 /* SwiftyMarkdown.podspec in Resources */ = {isa = PBXBuildFile; fileRef = F4CE98E81C8AF01300D735C1 /* SwiftyMarkdown.podspec */; };
/* End PBXBuildFile section */
@@ -208,7 +207,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F4CE98EA1C8AF01300D735C1 /* README.md in Sources */,
F4CE989C1C8A922E00D735C1 /* SwiftyMarkdown.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -251,7 +249,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 7;
CURRENT_PROJECT_VERSION = 12;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -299,7 +297,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 7;
CURRENT_PROJECT_VERSION = 12;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -327,7 +325,7 @@
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 7;
DYLIB_CURRENT_VERSION = 12;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = SwiftyMarkdown/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@@ -346,7 +344,7 @@
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 7;
DYLIB_CURRENT_VERSION = 12;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = SwiftyMarkdown/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+2 -2
View File
@@ -15,11 +15,11 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.1</string>
<string>0.2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>7</string>
<string>12</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
+199 -100
View File
@@ -10,45 +10,107 @@ import UIKit
public protocol FontProperties {
var fontName : String { get set }
var fontName : String? { get set }
var color : UIColor { get set }
}
/**
A struct defining the styles that can be applied to the parsed Markdown. The `fontName` property is optional, and if it's not set then the `fontName` property of the Body style will be applied.
If that is not set, then the system default will be used.
*/
public struct BasicStyles : FontProperties {
public var fontName = UIFont.preferredFontForTextStyle(UIFontTextStyleBody).fontName
public var fontName : String? = UIFont.preferredFontForTextStyle(UIFontTextStyleBody).fontName
public var color = UIColor.blackColor()
}
enum LineType : Int {
case H1, H2, H3, H4, H5, H6, Body, Italic, Bold, Code
case H1, H2, H3, H4, H5, H6, Body
}
enum LineStyle : Int {
case None
case Italic
case Bold
case Code
case Link
static func styleFromString(string : String ) -> LineStyle {
if string == "**" || string == "__" {
return .Bold
} else if string == "*" || string == "_" {
return .Italic
} else if string == "`" {
return .Code
} else if string == "[" {
return .Link
} else {
return .None
}
}
}
/// A class that takes a [Markdown](https://daringfireball.net/projects/markdown/) string or file and returns an NSAttributedString with the applied styles. Supports Dynamic Type.
public class SwiftyMarkdown {
/// The styles to apply to any H1 headers found in the Markdown
public var h1 = BasicStyles()
/// The styles to apply to any H2 headers found in the Markdown
public var h2 = BasicStyles()
/// The styles to apply to any H3 headers found in the Markdown
public var h3 = BasicStyles()
/// The styles to apply to any H4 headers found in the Markdown
public var h4 = BasicStyles()
/// The styles to apply to any H5 headers found in the Markdown
public var h5 = BasicStyles()
/// The styles to apply to any H6 headers found in the Markdown
public var h6 = BasicStyles()
/// The default body styles. These are the base styles and will be used for e.g. headers if no other styles override them.
public var body = BasicStyles()
/// The styles to apply to any links found in the Markdown
public var link = BasicStyles()
public var italic = BasicStyles()
public var code = BasicStyles()
/// The styles to apply to any bold text found in the Markdown
public var bold = BasicStyles()
var previousStyle : String = UIFontTextStyleBody
/// The styles to apply to any italic text found in the Markdown
public var italic = BasicStyles()
/// The styles to apply to any code blocks or inline code text found in the Markdown
public var code = BasicStyles()
var currentType : LineType = .Body
let string : String
let instructionSet = NSCharacterSet(charactersInString: "\\*_`")
let instructionSet = NSCharacterSet(charactersInString: "[\\*_`")
/**
- parameter string: A string containing [Markdown](https://daringfireball.net/projects/markdown/) syntax to be converted to an NSAttributedString
- returns: An initialized SwiftyMarkdown object
*/
public init(string : String ) {
self.string = string
}
/**
A failable initializer that takes a URL and attempts to read it as a UTF-8 string
- parameter url: The location of the file to read
- returns: An initialized SwiftyMarkdown object, or nil if the string couldn't be read
*/
public init?(url : NSURL ) {
do {
@@ -61,6 +123,11 @@ public class SwiftyMarkdown {
}
}
/**
Generates an NSAttributedString from the string or URL passed at initialisation. Custom fonts or styles are applied to the appropriate elements when this method is called.
- returns: An NSAttributedString with the styles applied
*/
public func attributedString() -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: "")
@@ -70,127 +137,148 @@ public class SwiftyMarkdown {
let headings = ["# ", "## ", "### ", "#### ", "##### ", "###### "]
var skipLine = false
for line in lines {
for theLine in lines {
lineCount++
if skipLine {
skipLine = false
continue
}
var headingFound = false
var line = theLine
for heading in headings {
if let range = line.rangeOfString(heading) where range.startIndex == line.startIndex {
let startHeadingString = line.stringByReplacingCharactersInRange(range, withString: "")
let endHeadingHash = " " + heading.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
// Remove ending
let endHeadingString = heading.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
line = startHeadingString.stringByReplacingOccurrencesOfString(endHeadingString, withString: "").stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
let finalHeadingString = startHeadingString.stringByReplacingOccurrencesOfString(endHeadingHash, withString: "")
// Make Hx where x == current index
let string = attributedStringFromString(finalHeadingString, withType: LineType(rawValue: headings.indexOf(heading)!)!)
attributedString.appendAttributedString(string)
headingFound = true
currentType = LineType(rawValue: headings.indexOf(heading)!)!
// We found a heading so break out of the inner loop
break
}
}
if headingFound {
continue
}
// Look for underlined headings
if lineCount < lines.count {
let nextLine = lines[lineCount]
if let range = nextLine.rangeOfString("=") where range.startIndex == nextLine.startIndex {
// Make H1
let string = attributedStringFromString(line, withType: .H1)
attributedString.appendAttributedString(string)
currentType = .H1
// We need to skip the next line
skipLine = true
continue
}
if let range = nextLine.rangeOfString("-") where range.startIndex == nextLine.startIndex {
// Make H1
let string = attributedStringFromString(line, withType: .H2)
attributedString.appendAttributedString(string)
// Make H2
currentType = .H2
// We need to skip the next line
skipLine = true
continue
}
}
// If this is not an empty line...
if line.characters.count > 0 {
// ...start scanning
let scanner = NSScanner(string: line)
// We want to be aware of spaces
scanner.charactersToBeSkipped = nil
while !scanner.atEnd {
var string : NSString?
// Get all the characters up to the ones we are interested in
if scanner.scanUpToCharactersFromSet(instructionSet, intoString: &string) {
if let hasString = string as? String {
let bodyString = attributedStringFromString(hasString, withType: .Body)
let bodyString = attributedStringFromString(hasString, withStyle: .None)
attributedString.appendAttributedString(bodyString)
let location = scanner.scanLocation
let matchedCharacters = tagFromScanner(scanner)
let matchedCharacters = tagFromScanner(scanner).foundCharacters
// If the next string after the characters is a space, then add it to the final string and continue
if !scanner.scanUpToString(" ", intoString: nil) {
let charAtts = attributedStringFromString(matchedCharacters, withType: .Body)
attributedString.appendAttributedString(charAtts)
} else {
let set = NSMutableCharacterSet.whitespaceCharacterSet()
set.formUnionWithCharacterSet(NSCharacterSet.punctuationCharacterSet())
if scanner.scanUpToCharactersFromSet(set, intoString: nil) {
scanner.scanLocation = location
attributedString.appendAttributedString(self.attributedStringFromScanner(scanner))
} else if matchedCharacters == "[" {
scanner.scanLocation = location
attributedString.appendAttributedString(self.attributedStringFromScanner(scanner))
} else {
let charAtts = attributedStringFromString(matchedCharacters, withStyle: .None)
attributedString.appendAttributedString(charAtts)
}
}
} else {
attributedString.appendAttributedString(self.attributedStringFromScanner(scanner))
attributedString.appendAttributedString(self.attributedStringFromScanner(scanner, atStartOfLine: true))
}
}
}
// Append a new line character to the end of the processed line
attributedString.appendAttributedString(NSAttributedString(string: "\n"))
currentType = .Body
}
return attributedString
}
func attributedStringFromScanner( scanner : NSScanner) -> NSAttributedString {
func attributedStringFromScanner( scanner : NSScanner, atStartOfLine start : Bool = false) -> NSAttributedString {
var followingString : NSString?
var matchedCharacters = self.tagFromScanner(scanner)
let attributedString = NSMutableAttributedString(string: "")
scanner.scanUpToCharactersFromSet(instructionSet, intoString: &followingString)
if let hasString = followingString as? String {
let attString : NSAttributedString
if matchedCharacters.containsString("\\") {
attString = attributedStringFromString(matchedCharacters.stringByReplacingOccurrencesOfString("\\", withString: "") + hasString, withType: .Body)
} else if matchedCharacters == "**" || matchedCharacters == "__" {
attString = attributedStringFromString(hasString, withType: .Bold)
} else if matchedCharacters == "`" {
attString = attributedStringFromString("\t" + hasString, withType: .Code)
} else {
attString = attributedStringFromString(hasString, withType: .Italic)
}
attributedString.appendAttributedString(attString)
}
matchedCharacters = self.tagFromScanner(scanner)
let results = self.tagFromScanner(scanner)
var style = LineStyle.styleFromString(results.foundCharacters)
if matchedCharacters.containsString("\\") {
var attributes = [String : AnyObject]()
if style == .Link {
let attString = attributedStringFromString(matchedCharacters.stringByReplacingOccurrencesOfString("\\", withString: ""), withType: .Body)
var linkText : NSString?
var linkURL : NSString?
let linkCharacters = NSCharacterSet(charactersInString: "]()")
scanner.scanUpToCharactersFromSet(linkCharacters, intoString: &linkText)
scanner.scanCharactersFromSet(linkCharacters, intoString: nil)
scanner.scanUpToCharactersFromSet(linkCharacters, intoString: &linkURL)
scanner.scanCharactersFromSet(linkCharacters, intoString: nil)
if let hasLink = linkText, hasURL = linkURL {
followingString = hasLink as String
attributes[NSLinkAttributeName] = hasURL as String
} else {
style = .None
}
} else {
scanner.scanUpToCharactersFromSet(instructionSet, intoString: &followingString)
}
let attributedString = attributedStringFromString(results.escapedCharacters, withStyle: style).mutableCopy() as! NSMutableAttributedString
if let hasString = followingString as? String {
let prefix = ( style == .Code && start ) ? "\t" : ""
let attString = attributedStringFromString(prefix + hasString, withStyle: style, attributes: attributes)
attributedString.appendAttributedString(attString)
}
let suffix = self.tagFromScanner(scanner)
attributedString.appendAttributedString(attributedStringFromString(suffix.escapedCharacters, withStyle: style))
return attributedString
}
func tagFromScanner( scanner : NSScanner ) -> String {
func tagFromScanner( scanner : NSScanner ) -> (foundCharacters : String, escapedCharacters : String) {
var matchedCharacters : String = ""
var tempCharacters : NSString?
@@ -200,20 +288,34 @@ public class SwiftyMarkdown {
matchedCharacters = matchedCharacters + chars
}
}
return matchedCharacters
var foundCharacters : String = ""
while matchedCharacters.containsString("\\") {
if let hasRange = matchedCharacters.rangeOfString("\\") {
let newRange = Range(start: hasRange.startIndex, end: hasRange.endIndex.advancedBy(1))
foundCharacters = foundCharacters + matchedCharacters.substringWithRange(newRange)
matchedCharacters.removeRange(newRange)
}
}
return (matchedCharacters, foundCharacters.stringByReplacingOccurrencesOfString("\\", withString: ""))
}
// Make H1
func attributedStringFromString(string : String, withType type : LineType ) -> NSAttributedString {
var attributes : [String : AnyObject]
func attributedStringFromString(string : String, withStyle style : LineStyle, var attributes : [String : AnyObject] = [:] ) -> NSAttributedString {
let textStyle : String
let fontName : String
var fontName : String?
// What type are we and is there a font name set?
var appendNewLine = true
switch type {
switch currentType {
case .H1:
fontName = h1.fontName
if #available(iOS 9, *) {
@@ -221,7 +323,7 @@ public class SwiftyMarkdown {
} else {
textStyle = UIFontTextStyleHeadline
}
attributes = [NSForegroundColorAttributeName : h1.color]
attributes[NSForegroundColorAttributeName] = h1.color
case .H2:
fontName = h2.fontName
if #available(iOS 9, *) {
@@ -229,7 +331,7 @@ public class SwiftyMarkdown {
} else {
textStyle = UIFontTextStyleHeadline
}
attributes = [NSForegroundColorAttributeName : h2.color]
attributes[NSForegroundColorAttributeName] = h2.color
case .H3:
fontName = h3.fontName
if #available(iOS 9, *) {
@@ -237,61 +339,62 @@ public class SwiftyMarkdown {
} else {
textStyle = UIFontTextStyleSubheadline
}
attributes = [NSForegroundColorAttributeName : h3.color]
attributes[NSForegroundColorAttributeName] = h3.color
case .H4:
fontName = h4.fontName
textStyle = UIFontTextStyleHeadline
attributes = [NSForegroundColorAttributeName : h4.color]
attributes[NSForegroundColorAttributeName] = h4.color
case .H5:
fontName = h5.fontName
textStyle = UIFontTextStyleSubheadline
attributes = [NSForegroundColorAttributeName : h5.color]
attributes[NSForegroundColorAttributeName] = h5.color
case .H6:
fontName = h6.fontName
textStyle = UIFontTextStyleFootnote
attributes = [NSForegroundColorAttributeName : h6.color]
case .Italic:
fontName = italic.fontName
attributes = [NSForegroundColorAttributeName : italic.color]
textStyle = previousStyle
appendNewLine = false
case .Bold:
fontName = bold.fontName
attributes = [NSForegroundColorAttributeName : bold.color]
appendNewLine = false
textStyle = previousStyle
case .Code:
fontName = code.fontName
attributes = [NSForegroundColorAttributeName : code.color]
appendNewLine = false
textStyle = previousStyle
attributes[NSForegroundColorAttributeName] = h6.color
default:
appendNewLine = false
fontName = body.fontName
textStyle = UIFontTextStyleBody
attributes = [NSForegroundColorAttributeName:body.color]
attributes[NSForegroundColorAttributeName] = body.color
break
}
previousStyle = textStyle
// Check for code
if style == .Code {
fontName = code.fontName
attributes[NSForegroundColorAttributeName] = code.color
}
if style == .Link {
fontName = link.fontName
attributes[NSForegroundColorAttributeName] = link.color
}
// Fallback to body
if let _ = fontName {
} else {
fontName = body.fontName
}
let font = UIFont.preferredFontForTextStyle(textStyle)
let styleDescriptor = font.fontDescriptor()
let styleSize = styleDescriptor.fontAttributes()[UIFontDescriptorSizeAttribute] as? CGFloat ?? CGFloat(14)
var finalFont : UIFont
if let font = UIFont(name: fontName, size: styleSize) {
if let finalFontName = fontName, font = UIFont(name: finalFontName, size: styleSize) {
finalFont = font
} else {
finalFont = UIFont.preferredFontForTextStyle(textStyle)
}
let finalFontDescriptor = finalFont.fontDescriptor()
if type == .Italic {
if style == .Italic {
let italicDescriptor = finalFontDescriptor.fontDescriptorWithSymbolicTraits(.TraitItalic)
finalFont = UIFont(descriptor: italicDescriptor, size: styleSize)
}
if type == .Bold {
if style == .Bold {
let boldDescriptor = finalFontDescriptor.fontDescriptorWithSymbolicTraits(.TraitBold)
finalFont = UIFont(descriptor: boldDescriptor, size: styleSize)
}
@@ -299,10 +402,6 @@ public class SwiftyMarkdown {
attributes[NSFontAttributeName] = finalFont
if appendNewLine {
return NSAttributedString(string: string + "\n", attributes: attributes)
} else {
return NSAttributedString(string: string, attributes: attributes)
}
return NSAttributedString(string: string, attributes: attributes)
}
}
@@ -17,6 +17,7 @@ class ViewController: UIViewController {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.textView.dataDetectorTypes = UIDataDetectorTypes.All
if let url = NSBundle.mainBundle().URLForResource("example", withExtension: "md"), md = SwiftyMarkdown(url: url) {
md.h2.fontName = "AvenirNextCondensed-Bold"
md.h2.color = UIColor.redColor()
@@ -4,7 +4,7 @@ SwiftyMarkdown is a Swift-based *Markdown* parser that converts *Markdown* files
## Features
Customise fonts and colours easily in a Swift like way:
Customise fonts and colours easily in a Swift-like way:
`md.code.fontName = "CourierNewPSMT"`
@@ -13,5 +13,5 @@ Customise fonts and colours easily in a Swift like way:
*An italic line*
It ignores random * and correctly handles \*escaped\* asterisks and \_underlines\_.
It ignores random * and correctly handles escaped \*asterisks\* and \_underlines\_ and has error handling for mismatched tags (\*\*bold\* == **bold*). It also supports inline Markdown [Links](http://voyagetravelapps.com/)
+2 -2
View File
@@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>0.1.1</string>
<string>0.2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>7</string>
<string>12</string>
</dict>
</plist>
+256 -12
View File
@@ -21,16 +21,260 @@ class SwiftyMarkdownTests: XCTestCase {
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock {
// Put the code you want to measure the time of here.
}
}
func testThatOctothorpeHeadersAreHandledCorrectly() {
let headerString = "# Header 1\n## Header 2 ##\n### Header 3 ### \n#### Header 4#### \n##### Header 5\n###### Header 6"
let headerStringWithBold = "# **Bold Header 1**"
let headerStringWithItalic = "## Header 2 _With Italics_"
var md = SwiftyMarkdown(string: headerString)
XCTAssertEqual(md.attributedString().string, "Header 1\nHeader 2\nHeader 3\nHeader 4\nHeader 5\nHeader 6\n")
md = SwiftyMarkdown(string: headerStringWithBold)
XCTAssertEqual(md.attributedString().string, "Bold Header 1\n")
md = SwiftyMarkdown(string: headerStringWithItalic)
XCTAssertEqual(md.attributedString().string, "Header 2 With Italics\n")
}
func testThatUndelinedHeadersAreHandledCorrectly() {
let h1String = "Header 1\n===\nSome following text"
let h2String = "Header 2\n---\nSome following text"
let h1StringWithBold = "Header 1 **With Bold**\n===\nSome following text"
let h2StringWithItalic = "Header 2 _With Italic_\n---\nSome following text"
let h2StringWithCode = "Header 2 `With Code`\n---\nSome following text"
var md = SwiftyMarkdown(string: h1String)
XCTAssertEqual(md.attributedString().string, "Header 1\nSome following text\n")
md = SwiftyMarkdown(string: h2String)
XCTAssertEqual(md.attributedString().string, "Header 2\nSome following text\n")
md = SwiftyMarkdown(string: h1StringWithBold)
XCTAssertEqual(md.attributedString().string, "Header 1 With Bold\nSome following text\n")
md = SwiftyMarkdown(string: h2StringWithItalic)
XCTAssertEqual(md.attributedString().string, "Header 2 With Italic\nSome following text\n")
md = SwiftyMarkdown(string: h2StringWithCode)
XCTAssertEqual(md.attributedString().string, "Header 2 With Code\nSome following text\n")
}
func testThatRegularTraitsAreParsedCorrectly() {
let boldAtStartOfString = "**A bold string**"
let boldWithinString = "A string with a **bold** word"
let codeAtStartOfString = "`Code (should be indented)`"
let codeWithinString = "A string with `code` (should not be indented)"
let italicAtStartOfString = "*An italicised string*"
let italicWithinString = "A string with *italicised* text"
let multipleBoldWords = "__A bold string__ with a **mix** **of** bold __styles__"
let multipleCodeWords = "`A code string` with multiple `code` `instances`"
let multipleItalicWords = "_An italic string_ with a *mix* _of_ italic *styles*"
let longMixedString = "_An italic string_, **follwed by a bold one**, `with some code`, \\*\\*and some\\*\\* \\_escaped\\_ \\`characters\\`, `ending` *with* __more__ variety."
var md = SwiftyMarkdown(string: boldAtStartOfString)
XCTAssertEqual(md.attributedString().string, "A bold string\n")
md = SwiftyMarkdown(string: boldWithinString)
XCTAssertEqual(md.attributedString().string, "A string with a bold word\n")
md = SwiftyMarkdown(string: codeAtStartOfString)
XCTAssertEqual(md.attributedString().string, "\tCode (should be indented)\n")
md = SwiftyMarkdown(string: codeWithinString)
XCTAssertEqual(md.attributedString().string, "A string with code (should not be indented)\n")
md = SwiftyMarkdown(string: italicAtStartOfString)
XCTAssertEqual(md.attributedString().string, "An italicised string\n")
md = SwiftyMarkdown(string: italicWithinString)
XCTAssertEqual(md.attributedString().string, "A string with italicised text\n")
md = SwiftyMarkdown(string: multipleBoldWords)
XCTAssertEqual(md.attributedString().string, "A bold string with a mix of bold styles\n")
md = SwiftyMarkdown(string: multipleCodeWords)
XCTAssertEqual(md.attributedString().string, "\tA code string with multiple code instances\n")
md = SwiftyMarkdown(string: multipleItalicWords)
XCTAssertEqual(md.attributedString().string, "An italic string with a mix of italic styles\n")
md = SwiftyMarkdown(string: longMixedString)
XCTAssertEqual(md.attributedString().string, "An italic string, follwed by a bold one, with some code, **and some** _escaped_ `characters`, ending with more variety.\n")
}
func testThatMarkdownMistakesAreHandledAppropriately() {
let mismatchedBoldCharactersAtStart = "**This should be bold*"
let mismatchedBoldCharactersWithin = "A string *that should be italic**"
var md = SwiftyMarkdown(string: mismatchedBoldCharactersAtStart)
XCTAssertEqual(md.attributedString().string, "This should be bold\n")
md = SwiftyMarkdown(string: mismatchedBoldCharactersWithin)
XCTAssertEqual(md.attributedString().string, "A string that should be italic\n")
}
func testThatEscapedCharactersAreEscapedCorrectly() {
let escapedBoldAtStart = "\\*\\*A normal string\\*\\*"
let escapedBoldWithin = "A string with \\*\\*escaped\\*\\* asterisks"
let escapedItalicAtStart = "\\_A normal string\\_"
let escapedItalicWithin = "A string with \\_escaped\\_ underscores"
let escapedBackticksAtStart = "\\`A normal string\\`"
let escapedBacktickWithin = "A string with \\`escaped\\` backticks"
let oneEscapedAsteriskOneNormalAtStart = "\\**A normal string\\**"
let oneEscapedAsteriskOneNormalWithin = "A string with \\**escaped\\** asterisks"
let oneEscapedAsteriskTwoNormalAtStart = "\\***A normal string*\\**"
let oneEscapedAsteriskTwoNormalWithin = "A string with *\\**escaped**\\* asterisks"
var md = SwiftyMarkdown(string: escapedBoldAtStart)
XCTAssertEqual(md.attributedString().string, "**A normal string**\n")
md = SwiftyMarkdown(string: escapedBoldWithin)
XCTAssertEqual(md.attributedString().string, "A string with **escaped** asterisks\n")
md = SwiftyMarkdown(string: escapedItalicAtStart)
XCTAssertEqual(md.attributedString().string, "_A normal string_\n")
md = SwiftyMarkdown(string: escapedItalicWithin)
XCTAssertEqual(md.attributedString().string, "A string with _escaped_ underscores\n")
md = SwiftyMarkdown(string: escapedBackticksAtStart)
XCTAssertEqual(md.attributedString().string, "`A normal string`\n")
md = SwiftyMarkdown(string: escapedBacktickWithin)
XCTAssertEqual(md.attributedString().string, "A string with `escaped` backticks\n")
md = SwiftyMarkdown(string: oneEscapedAsteriskOneNormalAtStart)
XCTAssertEqual(md.attributedString().string, "*A normal string*\n")
md = SwiftyMarkdown(string: oneEscapedAsteriskOneNormalWithin)
XCTAssertEqual(md.attributedString().string, "A string with *escaped* asterisks\n")
md = SwiftyMarkdown(string: oneEscapedAsteriskTwoNormalAtStart)
XCTAssertEqual(md.attributedString().string, "*A normal string*\n")
md = SwiftyMarkdown(string: oneEscapedAsteriskTwoNormalWithin)
XCTAssertEqual(md.attributedString().string, "A string with *escaped* asterisks\n")
}
func testThatAsterisksAndUnderscoresNotAttachedToWordsAreNotRemoved() {
let asteriskSpace = "An asterisk followed by a space: * "
let backtickSpace = "A backtick followed by a space: ` "
let underscoreSpace = "An underscore followed by a space: _ "
let asteriskFullStop = "Two asterisks followed by a full stop: **."
let backtickFullStop = "Two backticks followed by a full stop: ``."
let underscoreFullStop = "Two underscores followed by a full stop: __."
let asteriskComma = "An asterisk followed by a full stop: *, *"
let backtickComma = "A backtick followed by a space: `, `"
let underscoreComma = "An underscore followed by a space: _, _"
let asteriskWithBold = "A **bold** word followed by an asterisk * "
let backtickWithCode = "A `code` word followed by a backtick ` "
let underscoreWithItalic = "An _italic_ word followed by an underscore _ "
var md = SwiftyMarkdown(string: asteriskSpace)
XCTAssertEqual(md.attributedString().string, asteriskSpace + "\n")
md = SwiftyMarkdown(string: backtickSpace)
XCTAssertEqual(md.attributedString().string, backtickSpace + "\n")
md = SwiftyMarkdown(string: underscoreSpace)
XCTAssertEqual(md.attributedString().string, underscoreSpace + "\n")
md = SwiftyMarkdown(string: asteriskFullStop)
XCTAssertEqual(md.attributedString().string, asteriskFullStop + "\n")
md = SwiftyMarkdown(string: backtickFullStop)
XCTAssertEqual(md.attributedString().string, backtickFullStop + "\n")
md = SwiftyMarkdown(string: underscoreFullStop)
XCTAssertEqual(md.attributedString().string, underscoreFullStop + "\n")
md = SwiftyMarkdown(string: asteriskComma)
XCTAssertEqual(md.attributedString().string, asteriskComma + "\n")
md = SwiftyMarkdown(string: backtickComma)
XCTAssertEqual(md.attributedString().string, backtickComma + "\n")
md = SwiftyMarkdown(string: underscoreComma)
XCTAssertEqual(md.attributedString().string, underscoreComma + "\n")
md = SwiftyMarkdown(string: asteriskWithBold)
XCTAssertEqual(md.attributedString().string, "A bold word followed by an asterisk * \n")
md = SwiftyMarkdown(string: backtickWithCode)
XCTAssertEqual(md.attributedString().string, "A code word followed by a backtick ` \n")
md = SwiftyMarkdown(string: underscoreWithItalic)
XCTAssertEqual(md.attributedString().string, "An italic word followed by an underscore _ \n")
}
func testForLinks() {
let linkAtStart = "[Link at start](http://voyagetravelapps.com/)"
let linkWithin = "A [Link](http://voyagetravelapps.com/)"
let headerLink = "## [Header link](http://voyagetravelapps.com/)"
let multipleLinks = "[Link 1](http://voyagetravelapps.com/), [Link 2](http://voyagetravelapps.com/)"
let mailtoAndTwitterLinks = "Email us at [simon@voyagetravelapps.com](mailto:simon@voyagetravelapps.com) Twitter [@VoyageTravelApp](twitter://user?screen_name=VoyageTravelApp)"
let syntaxErrorSquareBracketAtStart = "[Link with missing square(http://voyagetravelapps.com/)"
let syntaxErrorSquareBracketWithin = "A [Link(http://voyagetravelapps.com/)"
let syntaxErrorParenthesisAtStart = "[Link with missing parenthesis](http://voyagetravelapps.com/"
let syntaxErrorParenthesisWithin = "A [Link](http://voyagetravelapps.com/"
var md = SwiftyMarkdown(string: linkAtStart)
XCTAssertEqual(md.attributedString().string, "Link at start\n")
md = SwiftyMarkdown(string: linkWithin)
XCTAssertEqual(md.attributedString().string, "A Link\n")
md = SwiftyMarkdown(string: headerLink)
XCTAssertEqual(md.attributedString().string, "Header link\n")
md = SwiftyMarkdown(string: multipleLinks)
XCTAssertEqual(md.attributedString().string, "Link 1, Link 2\n")
md = SwiftyMarkdown(string: syntaxErrorSquareBracketAtStart)
XCTAssertEqual(md.attributedString().string, "Link with missing square\n")
md = SwiftyMarkdown(string: syntaxErrorSquareBracketWithin)
XCTAssertEqual(md.attributedString().string, "A Link\n")
md = SwiftyMarkdown(string: syntaxErrorParenthesisAtStart)
XCTAssertEqual(md.attributedString().string, "Link with missing parenthesis\n")
md = SwiftyMarkdown(string: syntaxErrorParenthesisWithin)
XCTAssertEqual(md.attributedString().string, "A Link\n")
md = SwiftyMarkdown(string: mailtoAndTwitterLinks)
XCTAssertEqual(md.attributedString().string, "Email us at simon@voyagetravelapps.com Twitter @VoyageTravelApp\n")
// let mailtoAndTwitterLinks = "Twitter [@VoyageTravelApp](twitter://user?screen_name=VoyageTravelApp)"
// let md = SwiftyMarkdown(string: mailtoAndTwitterLinks)
// XCTAssertEqual(md.attributedString().string, "Twitter @VoyageTravelApp\n")
}
}