From f7e7e0be4876f55d55764c9f50d7da3796157f61 Mon Sep 17 00:00:00 2001 From: Greg Date: Wed, 31 Jan 2024 15:35:41 -0800 Subject: [PATCH 1/2] allow text to be both bold and italic --- Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift | 3 +++ Sources/SwiftyMarkdown/SwiftyMarkdown.swift | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift b/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift index 8aca89a..29df72d 100644 --- a/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift +++ b/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift @@ -99,6 +99,9 @@ extension SwiftyMarkdown { fontName = italic.fontName ?? fontName fontSize = italic.fontSize globalItalic = true + case .boldItalic: + globalBold = true + globalItalic = true case .strikethrough: fontName = strikethrough.fontName ?? fontName fontSize = strikethrough.fontSize diff --git a/Sources/SwiftyMarkdown/SwiftyMarkdown.swift b/Sources/SwiftyMarkdown/SwiftyMarkdown.swift index 1e9ef16..d142a83 100644 --- a/Sources/SwiftyMarkdown/SwiftyMarkdown.swift +++ b/Sources/SwiftyMarkdown/SwiftyMarkdown.swift @@ -21,6 +21,7 @@ public enum CharacterStyle : CharacterStyling { case none case bold case italic + case boldItalic case code case link case image @@ -560,11 +561,14 @@ extension SwiftyMarkdown { guard let styles = token.characterStyles as? [CharacterStyle] else { continue } - if styles.contains(.italic) { + if styles.contains(.boldItalic) { + attributes[.font] = self.font(for: line, characterOverride: .boldItalic) + } else if styles.contains(.italic), styles.contains(.bold) { + attributes[.font] = self.font(for: line, characterOverride: .boldItalic) + } else if styles.contains(.italic) { attributes[.font] = self.font(for: line, characterOverride: .italic) attributes[.foregroundColor] = self.italic.color - } - if styles.contains(.bold) { + } else if styles.contains(.bold) { attributes[.font] = self.font(for: line, characterOverride: .bold) attributes[.foregroundColor] = self.bold.color } From 9b259c47c9f8204f7c184055f6e45174199f6e52 Mon Sep 17 00:00:00 2001 From: Greg Date: Tue, 26 Mar 2024 14:15:41 -0700 Subject: [PATCH 2/2] indentation --- .../SwiftyMarkdown/SwiftyMarkdown+iOS.swift | 304 ++--- Sources/SwiftyMarkdown/SwiftyMarkdown.swift | 1022 ++++++++--------- 2 files changed, 663 insertions(+), 663 deletions(-) diff --git a/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift b/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift index 29df72d..7f2b1af 100644 --- a/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift +++ b/Sources/SwiftyMarkdown/SwiftyMarkdown+iOS.swift @@ -12,126 +12,126 @@ import Foundation import UIKit extension SwiftyMarkdown { - - func font( for line : SwiftyLine, characterOverride : CharacterStyle? = nil ) -> UIFont { - let textStyle : UIFont.TextStyle - var fontName : String? - var fontSize : CGFloat? - - var globalBold = false - var globalItalic = false - - let style : FontProperties - // What type are we and is there a font name set? - switch line.lineStyle as! MarkdownLineStyle { - case .h1: - style = self.h1 - if #available(iOS 9, *) { - textStyle = UIFont.TextStyle.title1 - } else { - textStyle = UIFont.TextStyle.headline - } - case .h2: - style = self.h2 - if #available(iOS 9, *) { - textStyle = UIFont.TextStyle.title2 - } else { - textStyle = UIFont.TextStyle.headline - } - case .h3: - style = self.h3 - if #available(iOS 9, *) { - textStyle = UIFont.TextStyle.title2 - } else { - textStyle = UIFont.TextStyle.subheadline - } - case .h4: - style = self.h4 - textStyle = UIFont.TextStyle.headline - case .h5: - style = self.h5 - textStyle = UIFont.TextStyle.subheadline - case .h6: - style = self.h6 - textStyle = UIFont.TextStyle.footnote - case .codeblock: - style = self.code - textStyle = UIFont.TextStyle.body - case .blockquote: - style = self.blockquotes - textStyle = UIFont.TextStyle.body - default: - style = self.body - textStyle = UIFont.TextStyle.body - } - - fontName = style.fontName - fontSize = style.fontSize - switch style.fontStyle { - case .bold: - globalBold = true - case .italic: - globalItalic = true - case .boldItalic: - globalItalic = true - globalBold = true - case .normal: - break - } - - if fontName == nil { - fontName = body.fontName - } - - if let characterOverride = characterOverride { - switch characterOverride { - case .code: - fontName = code.fontName ?? fontName - fontSize = code.fontSize - case .link: - fontName = link.fontName ?? fontName - fontSize = link.fontSize - case .bold: - fontName = bold.fontName ?? fontName - fontSize = bold.fontSize - globalBold = true - case .italic: - fontName = italic.fontName ?? fontName - fontSize = italic.fontSize - globalItalic = true + + func font( for line : SwiftyLine, characterOverride : CharacterStyle? = nil ) -> UIFont { + let textStyle : UIFont.TextStyle + var fontName : String? + var fontSize : CGFloat? + + var globalBold = false + var globalItalic = false + + let style : FontProperties + // What type are we and is there a font name set? + switch line.lineStyle as! MarkdownLineStyle { + case .h1: + style = self.h1 + if #available(iOS 9, *) { + textStyle = UIFont.TextStyle.title1 + } else { + textStyle = UIFont.TextStyle.headline + } + case .h2: + style = self.h2 + if #available(iOS 9, *) { + textStyle = UIFont.TextStyle.title2 + } else { + textStyle = UIFont.TextStyle.headline + } + case .h3: + style = self.h3 + if #available(iOS 9, *) { + textStyle = UIFont.TextStyle.title2 + } else { + textStyle = UIFont.TextStyle.subheadline + } + case .h4: + style = self.h4 + textStyle = UIFont.TextStyle.headline + case .h5: + style = self.h5 + textStyle = UIFont.TextStyle.subheadline + case .h6: + style = self.h6 + textStyle = UIFont.TextStyle.footnote + case .codeblock: + style = self.code + textStyle = UIFont.TextStyle.body + case .blockquote: + style = self.blockquotes + textStyle = UIFont.TextStyle.body + default: + style = self.body + textStyle = UIFont.TextStyle.body + } + + fontName = style.fontName + fontSize = style.fontSize + switch style.fontStyle { + case .bold: + globalBold = true + case .italic: + globalItalic = true + case .boldItalic: + globalItalic = true + globalBold = true + case .normal: + break + } + + if fontName == nil { + fontName = body.fontName + } + + if let characterOverride = characterOverride { + switch characterOverride { + case .code: + fontName = code.fontName ?? fontName + fontSize = code.fontSize + case .link: + fontName = link.fontName ?? fontName + fontSize = link.fontSize + case .bold: + fontName = bold.fontName ?? fontName + fontSize = bold.fontSize + globalBold = true + case .italic: + fontName = italic.fontName ?? fontName + fontSize = italic.fontSize + globalItalic = true case .boldItalic: globalBold = true globalItalic = true - case .strikethrough: - fontName = strikethrough.fontName ?? fontName - fontSize = strikethrough.fontSize - default: - break - } - } - - fontSize = fontSize == 0.0 ? nil : fontSize - var font : UIFont - if let existentFontName = fontName { - font = UIFont.preferredFont(forTextStyle: textStyle) - let finalSize : CGFloat - if let existentFontSize = fontSize { - finalSize = existentFontSize - } else { - let styleDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle) - finalSize = styleDescriptor.fontAttributes[.size] as? CGFloat ?? CGFloat(14) - } - - if let customFont = UIFont(name: existentFontName, size: finalSize) { - let fontMetrics = UIFontMetrics(forTextStyle: textStyle) - font = fontMetrics.scaledFont(for: customFont) - } else { - font = UIFont.preferredFont(forTextStyle: textStyle) - } - } else { - font = UIFont.preferredFont(forTextStyle: textStyle) - } - + case .strikethrough: + fontName = strikethrough.fontName ?? fontName + fontSize = strikethrough.fontSize + default: + break + } + } + + fontSize = fontSize == 0.0 ? nil : fontSize + var font : UIFont + if let existentFontName = fontName { + font = UIFont.preferredFont(forTextStyle: textStyle) + let finalSize : CGFloat + if let existentFontSize = fontSize { + finalSize = existentFontSize + } else { + let styleDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle) + finalSize = styleDescriptor.fontAttributes[.size] as? CGFloat ?? CGFloat(14) + } + + if let customFont = UIFont(name: existentFontName, size: finalSize) { + let fontMetrics = UIFontMetrics(forTextStyle: textStyle) + font = fontMetrics.scaledFont(for: customFont) + } else { + font = UIFont.preferredFont(forTextStyle: textStyle) + } + } else { + font = UIFont.preferredFont(forTextStyle: textStyle) + } + var traits: UIFontDescriptor.SymbolicTraits = [] if globalItalic { traits.insert(.traitItalic) @@ -144,40 +144,40 @@ extension SwiftyMarkdown { let fontMetrics = UIFontMetrics(forTextStyle: textStyle) font = fontMetrics.scaledFont(for: customFont) } - - return font - - } - - func color( for line : SwiftyLine ) -> UIColor { - // What type are we and is there a font name set? - switch line.lineStyle as! MarkdownLineStyle { - case .yaml: - return body.color - case .h1, .previousH1: - return h1.color - case .h2, .previousH2: - return h2.color - case .h3: - return h3.color - case .h4: - return h4.color - case .h5: - return h5.color - case .h6: - return h6.color - case .body: - return body.color - case .codeblock: - return code.color - case .blockquote: - return blockquotes.color - case .unorderedList, .unorderedListIndentFirstOrder, .unorderedListIndentSecondOrder, .orderedList, .orderedListIndentFirstOrder, .orderedListIndentSecondOrder: - return body.color - case .referencedLink: - return link.color - } - } - + + return font + + } + + func color( for line : SwiftyLine ) -> UIColor { + // What type are we and is there a font name set? + switch line.lineStyle as! MarkdownLineStyle { + case .yaml: + return body.color + case .h1, .previousH1: + return h1.color + case .h2, .previousH2: + return h2.color + case .h3: + return h3.color + case .h4: + return h4.color + case .h5: + return h5.color + case .h6: + return h6.color + case .body: + return body.color + case .codeblock: + return code.color + case .blockquote: + return blockquotes.color + case .unorderedList, .unorderedListIndentFirstOrder, .unorderedListIndentSecondOrder, .orderedList, .orderedListIndentFirstOrder, .orderedListIndentSecondOrder: + return body.color + case .referencedLink: + return link.color + } + } + } #endif diff --git a/Sources/SwiftyMarkdown/SwiftyMarkdown.swift b/Sources/SwiftyMarkdown/SwiftyMarkdown.swift index d142a83..29f21d5 100644 --- a/Sources/SwiftyMarkdown/SwiftyMarkdown.swift +++ b/Sources/SwiftyMarkdown/SwiftyMarkdown.swift @@ -13,28 +13,28 @@ import UIKit #endif extension OSLog { - private static var subsystem = "SwiftyMarkdown" - static let swiftyMarkdownPerformance = OSLog(subsystem: subsystem, category: "Swifty Markdown Performance") + private static var subsystem = "SwiftyMarkdown" + static let swiftyMarkdownPerformance = OSLog(subsystem: subsystem, category: "Swifty Markdown Performance") } public enum CharacterStyle : CharacterStyling { - case none - case bold - case italic + case none + case bold + case italic case boldItalic - case code - case link - case image - case referencedLink - case referencedImage - case strikethrough - - public func isEqualTo(_ other: CharacterStyling) -> Bool { - guard let other = other as? CharacterStyle else { - return false - } - return other == self - } + case code + case link + case image + case referencedLink + case referencedImage + case strikethrough + + public func isEqualTo(_ other: CharacterStyling) -> Bool { + guard let other = other as? CharacterStyle else { + return false + } + return other == self + } } enum MarkdownLineStyle : LineStyling { @@ -60,13 +60,13 @@ enum MarkdownLineStyle : LineStyling { case blockquote case codeblock case unorderedList - case unorderedListIndentFirstOrder - case unorderedListIndentSecondOrder + case unorderedListIndentFirstOrder + case unorderedListIndentSecondOrder case orderedList - case orderedListIndentFirstOrder - case orderedListIndentSecondOrder - case referencedLink - + case orderedListIndentFirstOrder + case orderedListIndentSecondOrder + case referencedLink + func styleIfFoundStyleAffectsPreviousLine() -> LineStyling? { switch self { case .previousH1: @@ -80,499 +80,499 @@ enum MarkdownLineStyle : LineStyling { } @objc public enum FontStyle : Int { - case normal - case bold - case italic - case boldItalic + case normal + case bold + case italic + case boldItalic } #if os(macOS) @objc public protocol FontProperties { - var fontName : String? { get set } - var color : NSColor { get set } - var fontSize : CGFloat { get set } - var fontStyle : FontStyle { get set } + var fontName : String? { get set } + var color : NSColor { get set } + var fontSize : CGFloat { get set } + var fontStyle : FontStyle { get set } } #else @objc public protocol FontProperties { - var fontName : String? { get set } - var color : UIColor { get set } - var fontSize : CGFloat { get set } - var fontStyle : FontStyle { get set } + var fontName : String? { get set } + var color : UIColor { get set } + var fontSize : CGFloat { get set } + var fontStyle : FontStyle { get set } } #endif @objc public protocol LineProperties { - var alignment : NSTextAlignment { get set } + var alignment : NSTextAlignment { get set } var lineSpacing: CGFloat { get set } var paragraphSpacing: CGFloat { get set } } /** -A class 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. -*/ + A class 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. + */ @objc open class BasicStyles : NSObject, FontProperties { - public var fontName : String? - #if os(macOS) - public var color = NSColor.black - #else - public var color = UIColor.black - #endif - public var fontSize : CGFloat = 0.0 - public var fontStyle : FontStyle = .normal + public var fontName : String? +#if os(macOS) + public var color = NSColor.black +#else + public var color = UIColor.black +#endif + public var fontSize : CGFloat = 0.0 + public var fontStyle : FontStyle = .normal } @objc open class LineStyles : NSObject, FontProperties, LineProperties { - public var fontName : String? - #if os(macOS) - public var color = NSColor.black - #else - public var color = UIColor.black - #endif - public var fontSize : CGFloat = 0.0 - public var fontStyle : FontStyle = .normal - public var alignment: NSTextAlignment = .left + public var fontName : String? +#if os(macOS) + public var color = NSColor.black +#else + public var color = UIColor.black +#endif + public var fontSize : CGFloat = 0.0 + public var fontStyle : FontStyle = .normal + public var alignment: NSTextAlignment = .left public var lineSpacing : CGFloat = 0.0 public var paragraphSpacing : CGFloat = 0.0 } @objc open class LinkStyles : BasicStyles { public var underlineStyle: NSUnderlineStyle = .single - #if os(macOS) - public lazy var underlineColor = self.color - #else - public lazy var underlineColor = self.color - #endif +#if os(macOS) + public lazy var underlineColor = self.color +#else + public lazy var underlineColor = self.color +#endif } /// 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. @objc open class SwiftyMarkdown: NSObject { - - static public var frontMatterRules = [ - FrontMatterRule(openTag: "---", closeTag: "---", keyValueSeparator: ":") - ] - - static public var lineRules = [ - LineRule(token: "=", type: MarkdownLineStyle.previousH1, removeFrom: .entireLine, changeAppliesTo: .previous), - LineRule(token: "-", type: MarkdownLineStyle.previousH2, removeFrom: .entireLine, changeAppliesTo: .previous), - LineRule(token: "\t\t- ", type: MarkdownLineStyle.unorderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false), - LineRule(token: "\t- ", type: MarkdownLineStyle.unorderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false), - LineRule(token: "- ",type : MarkdownLineStyle.unorderedList, removeFrom: .leading), - LineRule(token: "\t\t* ", type: MarkdownLineStyle.unorderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false), - LineRule(token: "\t* ", type: MarkdownLineStyle.unorderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false), - LineRule(token: "\t\t1. ", type: MarkdownLineStyle.orderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false), - LineRule(token: "\t1. ", type: MarkdownLineStyle.orderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false), - LineRule(token: "1. ",type : MarkdownLineStyle.orderedList, removeFrom: .leading), - LineRule(token: "* ",type : MarkdownLineStyle.unorderedList, removeFrom: .leading), - LineRule(token: " ", type: MarkdownLineStyle.codeblock, removeFrom: .leading, shouldTrim: false), - LineRule(token: "\t", type: MarkdownLineStyle.codeblock, removeFrom: .leading, shouldTrim: false), - LineRule(token: ">",type : MarkdownLineStyle.blockquote, removeFrom: .leading), - LineRule(token: "###### ",type : MarkdownLineStyle.h6, removeFrom: .both), - LineRule(token: "##### ",type : MarkdownLineStyle.h5, removeFrom: .both), - LineRule(token: "#### ",type : MarkdownLineStyle.h4, removeFrom: .both), - LineRule(token: "### ",type : MarkdownLineStyle.h3, removeFrom: .both), - LineRule(token: "## ",type : MarkdownLineStyle.h2, removeFrom: .both), - LineRule(token: "# ",type : MarkdownLineStyle.h1, removeFrom: .both) - ] - - 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), - CharacterRuleTag(tag: ")", type: .metadataClose) - ], styles: [1 : CharacterStyle.image], metadataLookup: false, definesBoundary: true), - 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: .open), otherTags: [ - CharacterRuleTag(tag: "]", type: .close), - CharacterRuleTag(tag: "(", type: .metadataOpen), - CharacterRuleTag(tag: ")", type: .metadataClose) - ], styles: [1 : CharacterStyle.link], metadataLookup: false, definesBoundary: true), - CharacterRule(primaryTag: CharacterRuleTag(tag: "`", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.code], shouldCancelRemainingRules: true, balancedTags: true), - CharacterRule(primaryTag:CharacterRuleTag(tag: "~", type: .repeating), otherTags : [], styles: [2 : CharacterStyle.strikethrough], minTags:2 , maxTags:2), - 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) - ] - - let lineProcessor = SwiftyLineProcessor(rules: SwiftyMarkdown.lineRules, defaultRule: MarkdownLineStyle.body, frontMatterRules: SwiftyMarkdown.frontMatterRules) - let tokeniser = SwiftyTokeniser(with: SwiftyMarkdown.characterRules) - - /// The styles to apply to any H1 headers found in the Markdown - open var h1 = LineStyles() - - /// The styles to apply to any H2 headers found in the Markdown - open var h2 = LineStyles() - - /// The styles to apply to any H3 headers found in the Markdown - open var h3 = LineStyles() - - /// The styles to apply to any H4 headers found in the Markdown - open var h4 = LineStyles() - - /// The styles to apply to any H5 headers found in the Markdown - open var h5 = LineStyles() - - /// The styles to apply to any H6 headers found in the Markdown - open var h6 = LineStyles() - - /// The default body styles. These are the base styles and will be used for e.g. headers if no other styles override them. - open var body = LineStyles() - - /// The styles to apply to any blockquotes found in the Markdown - open var blockquotes = LineStyles() - - /// The styles to apply to any links found in the Markdown - open var link = LinkStyles() - - /// The styles to apply to any bold text found in the Markdown - open var bold = BasicStyles() - - /// The styles to apply to any italic text found in the Markdown - open var italic = BasicStyles() - - /// The styles to apply to any code blocks or inline code text found in the Markdown - open var code = BasicStyles() - - open var strikethrough = BasicStyles() - - public var bullet : String = "・" - - public var underlineLinks : Bool = false - - public var frontMatterAttributes : [String : String] { - get { - return self.lineProcessor.frontMatterAttributes - } - } - - var currentType : MarkdownLineStyle = .body - - var string : String - - var orderedListCount = 0 - var orderedListIndentFirstOrderCount = 0 - var orderedListIndentSecondOrderCount = 0 - - var previouslyFoundTokens : [Token] = [] - - var applyAttachments = true - - let perfomanceLog = PerformanceLog(with: "SwiftyMarkdownPerformanceLogging", identifier: "Swifty Markdown", log: .swiftyMarkdownPerformance) - - /** - - - 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 - super.init() - self.setup() - } - - /** - 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 : URL ) { - - do { - self.string = try NSString(contentsOf: url, encoding: String.Encoding.utf8.rawValue) as String - - } catch { - self.string = "" - return nil - } - super.init() - self.setup() - } - - func setup() { - #if os(macOS) - self.setFontColorForAllStyles(with: .labelColor) - #elseif !os(watchOS) - if #available(iOS 13.0, tvOS 13.0, *) { - self.setFontColorForAllStyles(with: .label) - } - #endif - } - - /** - Set font size for all styles - - - parameter size: size of font - */ - open func setFontSizeForAllStyles(with size: CGFloat) { - h1.fontSize = size - h2.fontSize = size - h3.fontSize = size - h4.fontSize = size - h5.fontSize = size - h6.fontSize = size - body.fontSize = size - italic.fontSize = size - bold.fontSize = size - code.fontSize = size - link.fontSize = size - link.fontSize = size - strikethrough.fontSize = size - } - - #if os(macOS) - open func setFontColorForAllStyles(with color: NSColor) { - h1.color = color - h2.color = color - h3.color = color - h4.color = color - h5.color = color - h6.color = color - body.color = color - italic.color = color - bold.color = color - code.color = color - link.color = color - blockquotes.color = color - strikethrough.color = color - } - #else - open func setFontColorForAllStyles(with color: UIColor) { - h1.color = color - h2.color = color - h3.color = color - h4.color = color - h5.color = color - h6.color = color - body.color = color - italic.color = color - bold.color = color - code.color = color - link.color = color - blockquotes.color = color - strikethrough.color = color - } - #endif - - open func setFontNameForAllStyles(with name: String) { - h1.fontName = name - h2.fontName = name - h3.fontName = name - h4.fontName = name - h5.fontName = name - h6.fontName = name - body.fontName = name - italic.fontName = name - bold.fontName = name - code.fontName = name - link.fontName = name - blockquotes.fontName = name - strikethrough.fontName = name - } - - - /** - 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 - */ - open func attributedString(from markdownString : String? = nil) -> NSAttributedString { - - self.previouslyFoundTokens.removeAll() - self.perfomanceLog.start() - - if let existentMarkdownString = markdownString { - self.string = existentMarkdownString - } - let attributedString = NSMutableAttributedString(string: "") - self.lineProcessor.processEmptyStrings = MarkdownLineStyle.body - let foundAttributes : [SwiftyLine] = lineProcessor.process(self.string) - - let references : [SwiftyLine] = foundAttributes.filter({ $0.line.starts(with: "[") && $0.line.contains("]:") }) - let referencesRemoved : [SwiftyLine] = foundAttributes.filter({ !($0.line.starts(with: "[") && $0.line.contains("]:") ) }) - var keyValuePairs : [String : String] = [:] - for line in references { - let strings = line.line.components(separatedBy: "]:") - guard strings.count >= 2 else { - continue - } - var key : String = strings[0] - if !key.isEmpty { - let newstart = key.index(key.startIndex, offsetBy: 1) - let range : Range = newstart.. 0 { - attributedString.append(NSAttributedString(string: "\n")) - } - let finalTokens = self.tokeniser.process(line.line) - self.previouslyFoundTokens.append(contentsOf: finalTokens) - self.perfomanceLog.tag(with: "(tokenising complete for line \(idx)") - - attributedString.append(attributedStringFor(tokens: finalTokens, in: line)) - - } - - self.perfomanceLog.end() - - return attributedString - } - + + static public var frontMatterRules = [ + FrontMatterRule(openTag: "---", closeTag: "---", keyValueSeparator: ":") + ] + + static public var lineRules = [ + LineRule(token: "=", type: MarkdownLineStyle.previousH1, removeFrom: .entireLine, changeAppliesTo: .previous), + LineRule(token: "-", type: MarkdownLineStyle.previousH2, removeFrom: .entireLine, changeAppliesTo: .previous), + LineRule(token: "\t\t- ", type: MarkdownLineStyle.unorderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false), + LineRule(token: "\t- ", type: MarkdownLineStyle.unorderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false), + LineRule(token: "- ",type : MarkdownLineStyle.unorderedList, removeFrom: .leading), + LineRule(token: "\t\t* ", type: MarkdownLineStyle.unorderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false), + LineRule(token: "\t* ", type: MarkdownLineStyle.unorderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false), + LineRule(token: "\t\t1. ", type: MarkdownLineStyle.orderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false), + LineRule(token: "\t1. ", type: MarkdownLineStyle.orderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false), + LineRule(token: "1. ",type : MarkdownLineStyle.orderedList, removeFrom: .leading), + LineRule(token: "* ",type : MarkdownLineStyle.unorderedList, removeFrom: .leading), + LineRule(token: " ", type: MarkdownLineStyle.codeblock, removeFrom: .leading, shouldTrim: false), + LineRule(token: "\t", type: MarkdownLineStyle.codeblock, removeFrom: .leading, shouldTrim: false), + LineRule(token: ">",type : MarkdownLineStyle.blockquote, removeFrom: .leading), + LineRule(token: "###### ",type : MarkdownLineStyle.h6, removeFrom: .both), + LineRule(token: "##### ",type : MarkdownLineStyle.h5, removeFrom: .both), + LineRule(token: "#### ",type : MarkdownLineStyle.h4, removeFrom: .both), + LineRule(token: "### ",type : MarkdownLineStyle.h3, removeFrom: .both), + LineRule(token: "## ",type : MarkdownLineStyle.h2, removeFrom: .both), + LineRule(token: "# ",type : MarkdownLineStyle.h1, removeFrom: .both) + ] + + 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), + CharacterRuleTag(tag: ")", type: .metadataClose) + ], styles: [1 : CharacterStyle.image], metadataLookup: false, definesBoundary: true), + 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: .open), otherTags: [ + CharacterRuleTag(tag: "]", type: .close), + CharacterRuleTag(tag: "(", type: .metadataOpen), + CharacterRuleTag(tag: ")", type: .metadataClose) + ], styles: [1 : CharacterStyle.link], metadataLookup: false, definesBoundary: true), + CharacterRule(primaryTag: CharacterRuleTag(tag: "`", type: .repeating), otherTags: [], styles: [1 : CharacterStyle.code], shouldCancelRemainingRules: true, balancedTags: true), + CharacterRule(primaryTag:CharacterRuleTag(tag: "~", type: .repeating), otherTags : [], styles: [2 : CharacterStyle.strikethrough], minTags:2 , maxTags:2), + 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) + ] + + let lineProcessor = SwiftyLineProcessor(rules: SwiftyMarkdown.lineRules, defaultRule: MarkdownLineStyle.body, frontMatterRules: SwiftyMarkdown.frontMatterRules) + let tokeniser = SwiftyTokeniser(with: SwiftyMarkdown.characterRules) + + /// The styles to apply to any H1 headers found in the Markdown + open var h1 = LineStyles() + + /// The styles to apply to any H2 headers found in the Markdown + open var h2 = LineStyles() + + /// The styles to apply to any H3 headers found in the Markdown + open var h3 = LineStyles() + + /// The styles to apply to any H4 headers found in the Markdown + open var h4 = LineStyles() + + /// The styles to apply to any H5 headers found in the Markdown + open var h5 = LineStyles() + + /// The styles to apply to any H6 headers found in the Markdown + open var h6 = LineStyles() + + /// The default body styles. These are the base styles and will be used for e.g. headers if no other styles override them. + open var body = LineStyles() + + /// The styles to apply to any blockquotes found in the Markdown + open var blockquotes = LineStyles() + + /// The styles to apply to any links found in the Markdown + open var link = LinkStyles() + + /// The styles to apply to any bold text found in the Markdown + open var bold = BasicStyles() + + /// The styles to apply to any italic text found in the Markdown + open var italic = BasicStyles() + + /// The styles to apply to any code blocks or inline code text found in the Markdown + open var code = BasicStyles() + + open var strikethrough = BasicStyles() + + public var bullet : String = "・" + + public var underlineLinks : Bool = false + + public var frontMatterAttributes : [String : String] { + get { + return self.lineProcessor.frontMatterAttributes + } + } + + var currentType : MarkdownLineStyle = .body + + var string : String + + var orderedListCount = 0 + var orderedListIndentFirstOrderCount = 0 + var orderedListIndentSecondOrderCount = 0 + + var previouslyFoundTokens : [Token] = [] + + var applyAttachments = true + + let perfomanceLog = PerformanceLog(with: "SwiftyMarkdownPerformanceLogging", identifier: "Swifty Markdown", log: .swiftyMarkdownPerformance) + + /** + + - 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 + super.init() + self.setup() + } + + /** + 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 : URL ) { + + do { + self.string = try NSString(contentsOf: url, encoding: String.Encoding.utf8.rawValue) as String + + } catch { + self.string = "" + return nil + } + super.init() + self.setup() + } + + func setup() { + #if os(macOS) + self.setFontColorForAllStyles(with: .labelColor) + #elseif !os(watchOS) + if #available(iOS 13.0, tvOS 13.0, *) { + self.setFontColorForAllStyles(with: .label) + } + #endif + } + + /** + Set font size for all styles + + - parameter size: size of font + */ + open func setFontSizeForAllStyles(with size: CGFloat) { + h1.fontSize = size + h2.fontSize = size + h3.fontSize = size + h4.fontSize = size + h5.fontSize = size + h6.fontSize = size + body.fontSize = size + italic.fontSize = size + bold.fontSize = size + code.fontSize = size + link.fontSize = size + link.fontSize = size + strikethrough.fontSize = size + } + + #if os(macOS) + open func setFontColorForAllStyles(with color: NSColor) { + h1.color = color + h2.color = color + h3.color = color + h4.color = color + h5.color = color + h6.color = color + body.color = color + italic.color = color + bold.color = color + code.color = color + link.color = color + blockquotes.color = color + strikethrough.color = color + } + #else + open func setFontColorForAllStyles(with color: UIColor) { + h1.color = color + h2.color = color + h3.color = color + h4.color = color + h5.color = color + h6.color = color + body.color = color + italic.color = color + bold.color = color + code.color = color + link.color = color + blockquotes.color = color + strikethrough.color = color + } + #endif + + open func setFontNameForAllStyles(with name: String) { + h1.fontName = name + h2.fontName = name + h3.fontName = name + h4.fontName = name + h5.fontName = name + h6.fontName = name + body.fontName = name + italic.fontName = name + bold.fontName = name + code.fontName = name + link.fontName = name + blockquotes.fontName = name + strikethrough.fontName = name + } + + + /** + 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 + */ + open func attributedString(from markdownString : String? = nil) -> NSAttributedString { + + self.previouslyFoundTokens.removeAll() + self.perfomanceLog.start() + + if let existentMarkdownString = markdownString { + self.string = existentMarkdownString + } + let attributedString = NSMutableAttributedString(string: "") + self.lineProcessor.processEmptyStrings = MarkdownLineStyle.body + let foundAttributes : [SwiftyLine] = lineProcessor.process(self.string) + + let references : [SwiftyLine] = foundAttributes.filter({ $0.line.starts(with: "[") && $0.line.contains("]:") }) + let referencesRemoved : [SwiftyLine] = foundAttributes.filter({ !($0.line.starts(with: "[") && $0.line.contains("]:") ) }) + var keyValuePairs : [String : String] = [:] + for line in references { + let strings = line.line.components(separatedBy: "]:") + guard strings.count >= 2 else { + continue + } + var key : String = strings[0] + if !key.isEmpty { + let newstart = key.index(key.startIndex, offsetBy: 1) + let range : Range = newstart.. 0 { + attributedString.append(NSAttributedString(string: "\n")) + } + let finalTokens = self.tokeniser.process(line.line) + self.previouslyFoundTokens.append(contentsOf: finalTokens) + self.perfomanceLog.tag(with: "(tokenising complete for line \(idx)") + + attributedString.append(attributedStringFor(tokens: finalTokens, in: line)) + + } + + self.perfomanceLog.end() + + return attributedString + } + } extension SwiftyMarkdown { - - func attributedStringFor( tokens : [Token], in line : SwiftyLine ) -> NSAttributedString { - - var finalTokens = tokens - let finalAttributedString = NSMutableAttributedString() - var attributes : [NSAttributedString.Key : AnyObject] = [:] - - guard let markdownLineStyle = line.lineStyle as? MarkdownLineStyle else { - preconditionFailure("The passed line style is not a valid Markdown Line Style") - } - - var listItem = self.bullet - switch markdownLineStyle { - case .orderedList: - self.orderedListCount += 1 - self.orderedListIndentFirstOrderCount = 0 - self.orderedListIndentSecondOrderCount = 0 - listItem = "\(self.orderedListCount)." - case .orderedListIndentFirstOrder, .unorderedListIndentFirstOrder: - self.orderedListIndentFirstOrderCount += 1 - self.orderedListIndentSecondOrderCount = 0 - if markdownLineStyle == .orderedListIndentFirstOrder { - listItem = "\(self.orderedListIndentFirstOrderCount)." - } - - case .orderedListIndentSecondOrder, .unorderedListIndentSecondOrder: - self.orderedListIndentSecondOrderCount += 1 - if markdownLineStyle == .orderedListIndentSecondOrder { - listItem = "\(self.orderedListIndentSecondOrderCount)." - } - - default: - self.orderedListCount = 0 - self.orderedListIndentFirstOrderCount = 0 - self.orderedListIndentSecondOrderCount = 0 - } - - let lineProperties : LineProperties - switch markdownLineStyle { - case .h1: - lineProperties = self.h1 - case .h2: - lineProperties = self.h2 - case .h3: - lineProperties = self.h3 - case .h4: - lineProperties = self.h4 - case .h5: - lineProperties = self.h5 - case .h6: - lineProperties = self.h6 - case .codeblock: - lineProperties = body - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.firstLineHeadIndent = 20.0 - attributes[.paragraphStyle] = paragraphStyle - case .blockquote: - lineProperties = self.blockquotes - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.firstLineHeadIndent = 20.0 - paragraphStyle.headIndent = 20.0 - attributes[.paragraphStyle] = paragraphStyle - case .unorderedList, .unorderedListIndentFirstOrder, .unorderedListIndentSecondOrder, .orderedList, .orderedListIndentFirstOrder, .orderedListIndentSecondOrder: - - let interval : CGFloat = 30 - var addition = interval - var indent = "" - switch line.lineStyle as! MarkdownLineStyle { - case .unorderedListIndentFirstOrder, .orderedListIndentFirstOrder: - addition = interval * 2 - indent = "\t" - case .unorderedListIndentSecondOrder, .orderedListIndentSecondOrder: - addition = interval * 3 - indent = "\t\t" - default: - break - } - - lineProperties = body - - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: interval, options: [:]), NSTextTab(textAlignment: .left, location: interval, options: [:])] - paragraphStyle.defaultTabInterval = interval - paragraphStyle.headIndent = addition - - attributes[.paragraphStyle] = paragraphStyle - finalTokens.insert(Token(type: .string, inputString: "\(indent)\(listItem)\t"), at: 0) - - case .yaml: - lineProperties = body - case .previousH1: - lineProperties = body - case .previousH2: - lineProperties = body - case .body: - lineProperties = body - case .referencedLink: - lineProperties = body - } - + + func attributedStringFor( tokens : [Token], in line : SwiftyLine ) -> NSAttributedString { + + var finalTokens = tokens + let finalAttributedString = NSMutableAttributedString() + var attributes : [NSAttributedString.Key : AnyObject] = [:] + + guard let markdownLineStyle = line.lineStyle as? MarkdownLineStyle else { + preconditionFailure("The passed line style is not a valid Markdown Line Style") + } + + var listItem = self.bullet + switch markdownLineStyle { + case .orderedList: + self.orderedListCount += 1 + self.orderedListIndentFirstOrderCount = 0 + self.orderedListIndentSecondOrderCount = 0 + listItem = "\(self.orderedListCount)." + case .orderedListIndentFirstOrder, .unorderedListIndentFirstOrder: + self.orderedListIndentFirstOrderCount += 1 + self.orderedListIndentSecondOrderCount = 0 + if markdownLineStyle == .orderedListIndentFirstOrder { + listItem = "\(self.orderedListIndentFirstOrderCount)." + } + + case .orderedListIndentSecondOrder, .unorderedListIndentSecondOrder: + self.orderedListIndentSecondOrderCount += 1 + if markdownLineStyle == .orderedListIndentSecondOrder { + listItem = "\(self.orderedListIndentSecondOrderCount)." + } + + default: + self.orderedListCount = 0 + self.orderedListIndentFirstOrderCount = 0 + self.orderedListIndentSecondOrderCount = 0 + } + + let lineProperties : LineProperties + switch markdownLineStyle { + case .h1: + lineProperties = self.h1 + case .h2: + lineProperties = self.h2 + case .h3: + lineProperties = self.h3 + case .h4: + lineProperties = self.h4 + case .h5: + lineProperties = self.h5 + case .h6: + lineProperties = self.h6 + case .codeblock: + lineProperties = body + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.firstLineHeadIndent = 20.0 + attributes[.paragraphStyle] = paragraphStyle + case .blockquote: + lineProperties = self.blockquotes + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.firstLineHeadIndent = 20.0 + paragraphStyle.headIndent = 20.0 + attributes[.paragraphStyle] = paragraphStyle + case .unorderedList, .unorderedListIndentFirstOrder, .unorderedListIndentSecondOrder, .orderedList, .orderedListIndentFirstOrder, .orderedListIndentSecondOrder: + + let interval : CGFloat = 30 + var addition = interval + var indent = "" + switch line.lineStyle as! MarkdownLineStyle { + case .unorderedListIndentFirstOrder, .orderedListIndentFirstOrder: + addition = interval * 2 + indent = "\t" + case .unorderedListIndentSecondOrder, .orderedListIndentSecondOrder: + addition = interval * 3 + indent = "\t\t" + default: + break + } + + lineProperties = body + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: interval, options: [:]), NSTextTab(textAlignment: .left, location: interval, options: [:])] + paragraphStyle.defaultTabInterval = interval + paragraphStyle.headIndent = addition + + attributes[.paragraphStyle] = paragraphStyle + finalTokens.insert(Token(type: .string, inputString: "\(indent)\(listItem)\t"), at: 0) + + case .yaml: + lineProperties = body + case .previousH1: + lineProperties = body + case .previousH2: + lineProperties = body + case .body: + lineProperties = body + case .referencedLink: + lineProperties = body + } + let paragraphStyle = attributes[.paragraphStyle] as? NSMutableParagraphStyle ?? NSMutableParagraphStyle() - if lineProperties.alignment != .left { - paragraphStyle.alignment = lineProperties.alignment - } + if lineProperties.alignment != .left { + paragraphStyle.alignment = lineProperties.alignment + } paragraphStyle.lineSpacing = lineProperties.lineSpacing paragraphStyle.paragraphSpacing = lineProperties.paragraphSpacing attributes[.paragraphStyle] = paragraphStyle - - - for token in finalTokens { - attributes[.font] = self.font(for: line) - attributes[.link] = nil - attributes[.strikethroughStyle] = nil - attributes[.foregroundColor] = self.color(for: line) + + + for token in finalTokens { + attributes[.font] = self.font(for: line) + attributes[.link] = nil + attributes[.strikethroughStyle] = nil + attributes[.foregroundColor] = self.color(for: line) attributes[.underlineStyle] = nil - guard let styles = token.characterStyles as? [CharacterStyle] else { - continue - } + guard let styles = token.characterStyles as? [CharacterStyle] else { + continue + } if styles.contains(.boldItalic) { attributes[.font] = self.font(for: line, characterOverride: .boldItalic) } else if styles.contains(.italic), styles.contains(.bold) { attributes[.font] = self.font(for: line, characterOverride: .boldItalic) } else if styles.contains(.italic) { - attributes[.font] = self.font(for: line, characterOverride: .italic) - attributes[.foregroundColor] = self.italic.color - } else if styles.contains(.bold) { - attributes[.font] = self.font(for: line, characterOverride: .bold) - attributes[.foregroundColor] = self.bold.color - } - + attributes[.font] = self.font(for: line, characterOverride: .italic) + attributes[.foregroundColor] = self.italic.color + } else if styles.contains(.bold) { + attributes[.font] = self.font(for: line, characterOverride: .bold) + attributes[.foregroundColor] = self.bold.color + } + if let linkIdx = styles.firstIndex(of: .link), linkIdx < token.metadataStrings.count { attributes[.foregroundColor] = self.link.color attributes[.font] = self.font(for: line, characterOverride: .link) @@ -583,43 +583,43 @@ extension SwiftyMarkdown { attributes[.underlineColor] = self.link.underlineColor } } - - if styles.contains(.strikethrough) { - attributes[.font] = self.font(for: line, characterOverride: .strikethrough) - attributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue as AnyObject - attributes[.foregroundColor] = self.strikethrough.color - } - - #if !os(watchOS) - if let imgIdx = styles.firstIndex(of: .image), imgIdx < token.metadataStrings.count { - if !self.applyAttachments { - continue - } - #if !os(macOS) - let image1Attachment = NSTextAttachment() - image1Attachment.image = UIImage(named: token.metadataStrings[imgIdx]) - let str = NSAttributedString(attachment: image1Attachment) - finalAttributedString.append(str) - #elseif !os(watchOS) - let image1Attachment = NSTextAttachment() - image1Attachment.image = NSImage(named: token.metadataStrings[imgIdx]) - let str = NSAttributedString(attachment: image1Attachment) - finalAttributedString.append(str) - #endif - continue - } - #endif - - if styles.contains(.code) { - attributes[.foregroundColor] = self.code.color - attributes[.font] = self.font(for: line, characterOverride: .code) - } else { - // Switch back to previous font - } - let str = NSAttributedString(string: token.outputString, attributes: attributes) - finalAttributedString.append(str) - } - - return finalAttributedString - } + + if styles.contains(.strikethrough) { + attributes[.font] = self.font(for: line, characterOverride: .strikethrough) + attributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue as AnyObject + attributes[.foregroundColor] = self.strikethrough.color + } + + #if !os(watchOS) + if let imgIdx = styles.firstIndex(of: .image), imgIdx < token.metadataStrings.count { + if !self.applyAttachments { + continue + } + #if !os(macOS) + let image1Attachment = NSTextAttachment() + image1Attachment.image = UIImage(named: token.metadataStrings[imgIdx]) + let str = NSAttributedString(attachment: image1Attachment) + finalAttributedString.append(str) + #elseif !os(watchOS) + let image1Attachment = NSTextAttachment() + image1Attachment.image = NSImage(named: token.metadataStrings[imgIdx]) + let str = NSAttributedString(attachment: image1Attachment) + finalAttributedString.append(str) + #endif + continue + } + #endif + + if styles.contains(.code) { + attributes[.foregroundColor] = self.code.color + attributes[.font] = self.font(for: line, characterOverride: .code) + } else { + // Switch back to previous font + } + let str = NSAttributedString(string: token.outputString, attributes: attributes) + finalAttributedString.append(str) + } + + return finalAttributedString + } }