Browse Source

Updates readme

Simon Fairbairn 5 years ago
parent
commit
79467395b5

+ 46 - 7
README.md

@@ -92,6 +92,12 @@ label.attributedText = md.attributedString()
     [Links](http://voyagetravelapps.com/)
     ![Images](<Name of asset in bundle>)
     
+    [Referenced Links][1]
+    ![Referenced Images][2]
+    
+    [1]: http://voyagetravelapps.com/
+    [2]: <Name of asset in bundle>
+    
     > Blockquotes
 	
 	- Bulleted
@@ -292,13 +298,46 @@ enum CharacterStyle : CharacterStyling {
 }
 
 static public var characterRules = [
-	CharacterRule(openTag: "[", intermediateTag: "](", closingTag: ")", escapeCharacter: "\\", styles: [1 : [CharacterStyle.link]], maxTags: 1),
-	CharacterRule(openTag: "`", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.code]], maxTags: 1),
-	CharacterRule(openTag: "*", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3),
-	CharacterRule(openTag: "_", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3)
+    CharacterRule(primaryTag: CharacterRuleTag(tag: "[", type: .open), otherTags: [
+			CharacterRuleTag(tag: "]", type: .close),
+			CharacterRuleTag(tag: "[", type: .metadataOpen),
+			CharacterRuleTag(tag: "]", type: .metadataClose)
+	], styles: [1 : CharacterStyle.link], metadataLookup: true, definesBoundary: true),
+	CharacterRule(primaryTag: CharacterRuleTag(tag: "`", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.code], shouldCancelRemainingTags: true, balancedTags: true),
+	CharacterRule(primaryTag: CharacterRuleTag(tag: "*", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.italic, 2 : CharacterStyle.bold], minTags:1 , maxTags:2),
+	CharacterRule(primaryTag: CharacterRuleTag(tag: "_", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.italic, 2 : CharacterStyle.bold], minTags:1 , maxTags:2)
 ]
 ```
 
+These Character Rules are defined by SwiftyMarkdown:
+
+	public struct CharacterRule : CustomStringConvertible {
+
+		public let primaryTag : CharacterRuleTag
+		public let tags : [CharacterRuleTag]
+		public let escapeCharacters : [Character]
+		public let styles : [Int : CharacterStyling]
+		public let minTags : Int
+		public let maxTags : Int
+		public var metadataLookup : Bool = false
+		public var definesBoundary = false
+		public var shouldCancelRemainingRules = false
+		public var balancedTags = false
+	}
+
+1. `primaryTag`: Each rule must have at least one tag and it can be one of `repeating`, `open`, `close`, `metadataOpen`, or `metadataClose`. `repeating` tags are tags that have identical open and close characters (and often have more than 1 style depending on how many are in a group). For example, the `*` tag used in Markdown.
+2. `tags`: An array of other tags that the rule can look for. This is where you would put the `close` tag for a custom rule, for example.
+3. `escapeCharacters`: The characters that appear prior to any of the tag characters that tell the scanner to ignore the tag. 
+4. `styles`: The styles that should be applied to every character between the opening and closing tags. 
+5. `minTags`: The minimum number of repeating characters to be considered a successful match. For example, setting the `primaryTag` to `*` and the `minTag` to 2 would mean that `**foo**` would be a successful match wheras `*bar*` would not.
+6. `maxTags`: The maximum number of repeating characters to be considered a successful match. 
+7. `metadataLookup`: Used for Markdown reference links. Tells the scanner to try to look up the metadata from this dictionary, rather than from the inline result. 
+8. `definesBoundary`: In order for open and close tags to be successful, the `boundaryCount` for a given location in the string needs to be the same. Setting this property to `true` means that this rule will increase the `boundaryCount` for every character between its opening and closing tags. For example, the `[` rule defines a boundary. After it is applied, the string `*foo[bar*]` becomes `*foobar*` with a boundaryCount `00001111`. Applying the `*` rule results in the output `*foobar*` because the opening `*` tag and the closing `*` tag now have different `boundaryCount` values. It's basically a way to fix the `**[should not be bold**](url)` problem in Markdown. 
+9. `shouldCancelRemainingTags`: A successful match will mark every character between the opening and closing tags as complete, thereby preventing any further rules from being applied to those characters.
+10. `balancedTags`: This flag requires that the opening and closing tags be of exactly equal length. E.g. If this is set to true,  `**foo*` would result in `**foo*`. If it was false, the output would be `*foo`.
+
+
+
 #### Rule Subsets
 
 If you want to only support a small subset of Markdown, it's now easy to do. 
@@ -308,8 +347,8 @@ This example would only process strings with `*` and `_` characters, ignoring li
 SwiftyMarkdown.lineRules = []
 
 SwiftyMarkdown.characterRules = [
-	CharacterRule(openTag: "*", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3),
-	CharacterRule(openTag: "_", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3)
+	CharacterRule(primaryTag: CharacterRuleTag(tag: "*", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.italic, 2 : CharacterStyle.bold], minTags:1 , maxTags:2),
+	CharacterRule(primaryTag: CharacterRuleTag(tag: "_", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.italic, 2 : CharacterStyle.bold], minTags:1 , maxTags:2)
 ]
 ```
 
@@ -330,7 +369,7 @@ enum Characters : CharacterStyling {
 }
 
 let characterRules = [
-	CharacterRule(openTag: "%", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.elf]], maxTags: 1)
+	CharacterRule(primaryTag: CharacterRuleTag(tag: "%", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.elf])
 ]
 
 let processor = SwiftyTokeniser( with : characterRules )

+ 3 - 5
Sources/SwiftyMarkdown/CharacterRule.swift

@@ -53,27 +53,25 @@ public struct CharacterRule : CustomStringConvertible {
 		return self.primaryTag.type == .repeating
 	}
 	public var definesBoundary = false
-	public var shouldCancelRemainingTags = false
+	public var shouldCancelRemainingRules = false
 	public var balancedTags = false
 	
 	public var description: String {
 		return "Character Rule with Open tag: \(self.primaryTag.tag) and current styles : \(self.styles) "
 	}
 	
-	
-	
 	public func tag( for type : CharacterRuleTagType ) -> CharacterRuleTag? {
 		return self.tags.filter({ $0.type == type }).first ?? nil
 	}
 	
-	public init(primaryTag: CharacterRuleTag, otherTags: [CharacterRuleTag], escapeCharacters : [Character] = ["\\"], styles: [Int : CharacterStyling] = [:], minTags : Int = 1, maxTags : Int = 1, metadataLookup : Bool = false, definesBoundary : Bool = false, shouldCancelRemainingTags : Bool = false, balancedTags : Bool = false) {
+	public init(primaryTag: CharacterRuleTag, otherTags: [CharacterRuleTag], escapeCharacters : [Character] = ["\\"], styles: [Int : CharacterStyling] = [:], minTags : Int = 1, maxTags : Int = 1, metadataLookup : Bool = false, definesBoundary : Bool = false, shouldCancelRemainingRules : Bool = false, balancedTags : Bool = false) {
 		self.primaryTag = primaryTag
 		self.tags = otherTags
 		self.escapeCharacters = escapeCharacters
 		self.styles = styles
 		self.metadataLookup = metadataLookup
 		self.definesBoundary = definesBoundary
-		self.shouldCancelRemainingTags = shouldCancelRemainingTags
+		self.shouldCancelRemainingRules = shouldCancelRemainingRules
 		self.minTags = maxTags < minTags ? maxTags : minTags
 		self.maxTags = minTags > maxTags ? minTags : maxTags
 		self.balancedTags = balancedTags

+ 5 - 0
Sources/SwiftyMarkdown/SwiftyMarkdown.swift

@@ -168,6 +168,11 @@ If that is not set, then the system default will be used.
 	]
 	
 	static public var characterRules = [
+		CharacterRule(primaryTag: CharacterRuleTag(tag: "![", type: .open), otherTags: [
+				CharacterRuleTag(tag: "]", type: .close),
+				CharacterRuleTag(tag: "[", type: .metadataOpen),
+				CharacterRuleTag(tag: "]", type: .metadataClose)
+		], styles: [1 : CharacterStyle.image], metadataLookup: true, definesBoundary: true),
 		CharacterRule(primaryTag: CharacterRuleTag(tag: "![", type: .open), otherTags: [
 				CharacterRuleTag(tag: "]", type: .close),
 				CharacterRuleTag(tag: "(", type: .metadataOpen),

+ 7 - 14
Sources/SwiftyMarkdown/SwiftyScannerNonRepeating.swift

@@ -42,15 +42,19 @@ class SwiftyScannerNonRepeating {
 	let metadata : [String : String]
 	var pointer : Int = 0
 	
+	var spaceAndNewLine = CharacterSet.whitespacesAndNewlines
 	var tagGroups : [TagGroup] = []
 	
 	var isMetadataOpen = false
 	
+	
 	var enableLog = (ProcessInfo.processInfo.environment["SwiftyScannerScanner"] != nil)
 	
 	let currentPerfomanceLog = PerformanceLog(with: "SwiftyScannerScannerPerformanceLogging", identifier: "Scanner", log: OSLog.swiftyScannerPerformance)
 	let log = PerformanceLog(with: "SwiftyScannerScanner", identifier: "Scanner", log: OSLog.swiftyScannerScanner)
 		
+	
+	
 	enum Position {
 		case forward(Int)
 		case backward(Int)
@@ -278,7 +282,7 @@ class SwiftyScannerNonRepeating {
 				if self.rule.definesBoundary {
 					self.elements[idx].boundaryCount += 1
 				}
-				if self.rule.shouldCancelRemainingTags {
+				if self.rule.shouldCancelRemainingRules {
 					self.elements[idx].boundaryCount = 1000
 				}
 			}
@@ -303,6 +307,7 @@ class SwiftyScannerNonRepeating {
 		if shouldRemove {
 			self.tagGroups.removeAll(where: { $0.groupID == id })
 		}
+		self.isMetadataOpen = false
 	}
 	
 	func emptyRanges( _ ranges : inout [ClosedRange<Int>] ) {
@@ -423,11 +428,6 @@ class SwiftyScannerNonRepeating {
 		}
 	}
 	
-	var spaceAndNewLine = CharacterSet.whitespacesAndNewlines
-	
-	
-	
-	
 	func scanRepeatingTags() {
 				
 		var groupID = ""
@@ -485,9 +485,6 @@ class SwiftyScannerNonRepeating {
 					}
 				}
 				
-
-				
-				
 				if !validTagGroup {
 					if self.enableLog {
 						os_log("Tag has whitespace on both sides", log: .swiftyScannerScanner, type: .info)
@@ -497,7 +494,6 @@ class SwiftyScannerNonRepeating {
 				}
 				
 				if let idx = tagGroups.firstIndex(where: { $0.groupID == groupID }) {
-					
 					if tagType == .either {
 						if tagGroups[idx].count == count {
 							self.tagGroups[idx].tagRanges.append(openRange)
@@ -518,9 +514,6 @@ class SwiftyScannerNonRepeating {
 						}
 						continue
 					}
-					
-
-	
 				}
 				var tagGroup = TagGroup(tagRanges: [openRange])
 				groupID = tagGroup.groupID
@@ -528,7 +521,7 @@ class SwiftyScannerNonRepeating {
 				tagGroup.count = count
 				
 				if self.enableLog {
-					os_log("New open tag found. Starting new Group with ID %@", log: OSLog.swiftyScannerScanner, type:.info , groupID)
+					os_log("New open tag found with characters %@. Starting new Group with ID %@", log: OSLog.swiftyScannerScanner,  type:.info, self.elements[openRange].map( { String($0.character) }).joined(), groupID)
 				}
 				
 				self.tagGroups.append(tagGroup)

+ 1 - 1
SwiftyMarkdown.xcodeproj/xcshareddata/xcschemes/SwiftyMarkdown-Package.xcscheme

@@ -59,7 +59,7 @@
          <EnvironmentVariable
             key = "SwiftyScannerScanner"
             value = ""
-            isEnabled = "YES">
+            isEnabled = "NO">
          </EnvironmentVariable>
          <EnvironmentVariable
             key = "SwiftyScannerScannerPerformanceLogging"

+ 47 - 11
Tests/SwiftyMarkdownTests/SwiftyMarkdownCharacterTests.swift

@@ -11,12 +11,14 @@ import XCTest
 
 class SwiftyMarkdownStylingTests: SwiftyMarkdownCharacterTests {
 	
-	func testIsolatedCase() {
-
-		challenge = TokenTest(input: "a ```b`", output: "a ```b`", tokens : [
-			Token(type: .string, inputString: "a ```b`", characterStyles: [])
+	func off_testIsolatedCase() {
+		
+		challenge = TokenTest(input: "*\\***\\****b*\\***\\****\\", output: "***b***\\", tokens : [
+			Token(type: .string, inputString: "*", characterStyles: [CharacterStyle.italic]),
+			Token(type: .string, inputString: "*b**", characterStyles: [CharacterStyle.bold, CharacterStyle.italic]),
+			Token(type: .string, inputString: "\\", characterStyles: [])
 		])
-		results = self.attempt(challenge, rules: [.backticks])
+		results = self.attempt(challenge)
 		if results.stringTokens.count == challenge.tokens.count {
 			for (idx, token) in results.stringTokens.enumerated() {
 				XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
@@ -508,12 +510,10 @@ class SwiftyMarkdownStylingTests: SwiftyMarkdownCharacterTests {
 	}
 	
 	func testForExtremeEscapeCombinations() {
-		challenge = TokenTest(input: "Before *\\***\\****A bold string*\\***\\****\\ After", output: "Before ***A bold string***\\ After", tokens : [
-			Token(type: .string, inputString: "Before ", characterStyles: []),
-			Token(type: .string, inputString: "*", characterStyles: [CharacterStyle.italic]),
-			Token(type: .string, inputString: "**", characterStyles: [CharacterStyle.bold]),
-			Token(type: .string, inputString: "A bold string**", characterStyles: [CharacterStyle.bold, CharacterStyle.italic]),
-			Token(type: .string, inputString: "\\ After", characterStyles: [])
+		
+		challenge = TokenTest(input: "\\****b\\****", output: "*b*", tokens : [
+			Token(type: .string, inputString: "*", characterStyles: []),
+			Token(type: .string, inputString: "b*", characterStyles: [CharacterStyle.bold, CharacterStyle.italic])
 		])
 		results = self.attempt(challenge)
 		if results.stringTokens.count == challenge.tokens.count {
@@ -526,6 +526,42 @@ class SwiftyMarkdownStylingTests: SwiftyMarkdownCharacterTests {
 		}
 		XCTAssertEqual(results.foundStyles, results.expectedStyles)
 		XCTAssertEqual(results.attributedString.string, challenge.output)
+		
+		challenge = TokenTest(input: "**\\**b*\\***", output: "*b*", tokens : [
+			Token(type: .string, inputString: "*", characterStyles: [CharacterStyle.bold]),
+			Token(type: .string, inputString: "b", characterStyles: [CharacterStyle.italic, CharacterStyle.bold]),
+			Token(type: .string, inputString: "*", characterStyles: [CharacterStyle.bold]),
+		])
+		results = self.attempt(challenge)
+		if results.stringTokens.count == challenge.tokens.count {
+			for (idx, token) in results.stringTokens.enumerated() {
+				XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
+				XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as?  [CharacterStyle])
+			}
+		} else {
+			XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
+		}
+		XCTAssertEqual(results.foundStyles, results.expectedStyles)
+		XCTAssertEqual(results.attributedString.string, challenge.output)
+		
+//		challenge = TokenTest(input: "Before *\\***\\****A bold string*\\***\\****\\ After", output: "Before ***A bold string***\\ After", tokens : [
+//			Token(type: .string, inputString: "Before ", characterStyles: []),
+//			Token(type: .string, inputString: "*", characterStyles: [CharacterStyle.italic]),
+//			Token(type: .string, inputString: "**", characterStyles: [CharacterStyle.bold]),
+//			Token(type: .string, inputString: "A bold string**", characterStyles: [CharacterStyle.bold, CharacterStyle.italic]),
+//			Token(type: .string, inputString: "\\ After", characterStyles: [])
+//		])
+//		results = self.attempt(challenge)
+//		if results.stringTokens.count == challenge.tokens.count {
+//			for (idx, token) in results.stringTokens.enumerated() {
+//				XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
+//				XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as?  [CharacterStyle])
+//			}
+//		} else {
+//			XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
+//		}
+//		XCTAssertEqual(results.foundStyles, results.expectedStyles)
+//		XCTAssertEqual(results.attributedString.string, challenge.output)
 	}
 	
 	func testThatExtraCharactersAreHandles() {

+ 31 - 12
Tests/SwiftyMarkdownTests/SwiftyMarkdownLinkTests.swift

@@ -585,11 +585,10 @@ class SwiftyMarkdownLinkTests: SwiftyMarkdownCharacterTests {
 		}
 		XCTAssertEqual(results.attributedString.string, challenge.output)
 		XCTAssertEqual(results.foundStyles, results.expectedStyles)
-		var links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.image) ?? false) })
-		if links.count == 1 {
-			XCTAssertEqual(links[0].metadataStrings.first, "imageName")
+		if results.images.count == 1 {
+			XCTAssertEqual(results.images[0].metadataStrings.first, "imageName")
 		} else {
-			XCTFail("Incorrect link count. Expecting 1, found \(links.count)")
+			XCTFail("Incorrect link count. Expecting 1, found \(results.images.count)")
 		}
 		
 		challenge = TokenTest(input: "An [![Image](imageName)](https://www.neverendingvoyage.com/)", output: "An ", tokens: [
@@ -607,17 +606,37 @@ class SwiftyMarkdownLinkTests: SwiftyMarkdownCharacterTests {
 		}
 		XCTAssertEqual(results.attributedString.string, challenge.output)
 		XCTAssertEqual(results.foundStyles, results.expectedStyles)
-		links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.image) ?? false) })
-		if links.count == 1 {
-			XCTAssertEqual(links[0].metadataStrings.first, "imageName")
+		if results.images.count == 1 {
+			XCTAssertEqual(results.images[0].metadataStrings.first, "imageName")
 		} else {
-			XCTFail("Incorrect link count. Expecting 1, found \(links.count)")
+			XCTFail("Incorrect link count. Expecting 1, found \(results.images.count)")
 		}
-		links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
-		if links.count == 1 {
-			XCTAssertEqual(links[0].metadataStrings.last, "https://www.neverendingvoyage.com/")
+		if results.links.count == 1 {
+			XCTAssertEqual(results.links[0].metadataStrings.last, "https://www.neverendingvoyage.com/")
 		} else {
-			XCTFail("Incorrect link count. Expecting 1, found \(links.count)")
+			XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
+		}
+	}
+	
+	func testForReferencedImages() {
+		challenge = TokenTest(input: "A ![referenced image][image]\n[image]: imageName", output: "A ", tokens: [
+			Token(type: .string, inputString: "A ", characterStyles: []),
+			Token(type: .string, inputString: "referenced image", characterStyles: [CharacterStyle.image])
+		])
+		results = self.attempt(challenge)
+		if results.stringTokens.count == challenge.tokens.count {
+			for (idx, token) in results.stringTokens.enumerated() {
+				XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
+				XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as?  [CharacterStyle])
+			}
+		} else {
+			XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
+		}
+		XCTAssertEqual(results.foundStyles, results.expectedStyles)
+		if results.images.count == 1 {
+			XCTAssertEqual(results.images[0].metadataStrings.first, "imageName")
+		} else {
+			XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
 		}
 	}
 	

+ 9 - 4
Tests/SwiftyMarkdownTests/XCTest+SwiftyMarkdown.swift

@@ -14,6 +14,7 @@ struct ChallengeReturn {
 	let tokens : [Token]
 	let stringTokens : [Token]
 	let links : [Token]
+	let images : [Token]
 	let attributedString : NSAttributedString
 	let foundStyles : [[CharacterStyle]]
 	let expectedStyles : [[CharacterStyle]]
@@ -26,7 +27,8 @@ enum Rule {
 	case images
 	case links
 	case referencedLinks
-	case strikethroughs
+	case referencedImages
+	case tildes
 	
 	func asCharacterRule() -> CharacterRule {
 		switch self {
@@ -36,14 +38,16 @@ enum Rule {
 			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "[" && !$0.metadataLookup  }).first!
 		case .backticks:
 			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "`" }).first!
-		case .strikethroughs:
+		case .tildes:
 			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "~" }).first!
 		case .asterisks:
 			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "*" }).first!
 		case .underscores:
 			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "_" }).first!
 		case .referencedLinks:
-			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "[" && $0.metadataLookup  }).first!		
+			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "[" && $0.metadataLookup  }).first!
+		case .referencedImages:
+			return SwiftyMarkdown.characterRules.filter({ $0.primaryTag.tag == "![" && $0.metadataLookup  }).first!
 		}
 	}
 }
@@ -71,8 +75,9 @@ class SwiftyMarkdownCharacterTests : XCTestCase {
 		let expectedStyles = challenge.tokens.compactMap({ $0.characterStyles as? [CharacterStyle] })
 		
 		let linkTokens = tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
+		let imageTokens = tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.image) ?? false) })
 		
-		return ChallengeReturn(tokens: tokens, stringTokens: stringTokens, links : linkTokens, attributedString:  attributedString, foundStyles: existentTokenStyles, expectedStyles : expectedStyles)
+		return ChallengeReturn(tokens: tokens, stringTokens: stringTokens, links : linkTokens, images: imageTokens, attributedString:  attributedString, foundStyles: existentTokenStyles, expectedStyles : expectedStyles)
 	}
 }