diff --git a/Sources/Splash/Output/AttributedStringOutputFormat.swift b/Sources/Splash/Output/AttributedStringOutputFormat.swift index e28ac8a..c99344d 100644 --- a/Sources/Splash/Output/AttributedStringOutputFormat.swift +++ b/Sources/Splash/Output/AttributedStringOutputFormat.swift @@ -5,8 +5,12 @@ */ #if os(macOS) - import Cocoa +#endif + +#if os(iOS) +import UIKit +#endif /// Output format to use to generate an NSAttributedString from the /// highlighted code. A `Theme` is used to determine what fonts and @@ -17,7 +21,7 @@ public struct AttributedStringOutputFormat: OutputFormat { public init(theme: Theme) { self.theme = theme } - + public func makeBuilder() -> Builder { return Builder(theme: theme) } @@ -28,32 +32,33 @@ public extension AttributedStringOutputFormat { private let theme: Theme private lazy var font = loadFont() private var string = NSMutableAttributedString() - + fileprivate init(theme: Theme) { self.theme = theme } - + public mutating func addToken(_ token: String, ofType type: TokenType) { let color = theme.tokenColors[type] ?? Color(red: 1, green: 1, blue: 1) string.append(token, font: font, color: color) } - + public mutating func addPlainText(_ text: String) { string.append(text, font: font, color: theme.plainTextColor) } - + public mutating func addWhitespace(_ whitespace: String) { let color = Color(red: 1, green: 1, blue: 1) string.append(whitespace, font: font, color: color) } - + public func build() -> NSAttributedString { return NSAttributedString(attributedString: string) } - + + #if os(macOS) private mutating func loadFont() -> NSFont { let size = CGFloat(theme.font.size) - + switch theme.font.resource { case .system: return .defaultFont(ofSize: size) @@ -61,13 +66,25 @@ public extension AttributedStringOutputFormat { guard let font = NSFont.loaded(from: path, size: size) else { return .defaultFont(ofSize: size) } - + return font } } + #endif + + #if os(iOS) + private mutating func loadFont() -> UIFont { + + let size = CGFloat(theme.font.size) + return .defaultFont(ofSize: size) + + } + #endif + } } +#if os(macOS) private extension NSMutableAttributedString { func append(_ string: String, font: NSFont, color: Color) { let color = NSColor( @@ -76,12 +93,12 @@ private extension NSMutableAttributedString { blue: CGFloat(color.blue), alpha: CGFloat(color.alpha) ) - + let attributedString = NSAttributedString(string: string, attributes: [ .foregroundColor: color, .font: font - ]) - + ]) + append(attributedString) } } @@ -94,21 +111,52 @@ private extension NSFont { .cfurlposixPathStyle, false ) - + guard let font = url.flatMap(CGDataProvider.init).flatMap(CGFont.init) else { return nil } - + return CTFontCreateWithGraphicsFont(font, size, nil, nil) } - + static func defaultFont(ofSize size: CGFloat) -> NSFont { guard let courier = loaded(from: "/Library/Fonts/Courier New.ttf", size: size) else { return .systemFont(ofSize: size) } - + return courier } } - +#endif + +#if os(iOS) +private extension NSMutableAttributedString { + func append(_ string: String, font: UIFont, color: Color) { + let color = UIColor( + red: CGFloat(color.red), + green: CGFloat(color.green), + blue: CGFloat(color.blue), + alpha: CGFloat(color.alpha) + ) + + let attributedString = NSAttributedString(string: string, attributes: [ + .foregroundColor: color, + .font: font + ]) + + append(attributedString) + } +} + +private extension UIFont { + + static func defaultFont(ofSize size: CGFloat) -> UIFont { + guard let menlo = UIFont(name: "Menlo-Regular", size: size) else { + return .systemFont(ofSize: size) + } + + return menlo + } + +} #endif diff --git a/Sources/Splash/Theming/Theme+Defaults.swift b/Sources/Splash/Theming/Theme+Defaults.swift new file mode 100644 index 0000000..66b5c86 --- /dev/null +++ b/Sources/Splash/Theming/Theme+Defaults.swift @@ -0,0 +1,149 @@ +/** + * Splash + * Copyright (c) John Sundell 2018 + * MIT license - see LICENSE.md + */ + +import Foundation + +/* + * Extends the Theme struct with static properties that + * represent common Xcode defaults as well as community + * favourites. + */ +public extension Theme { + + /// Create a theme matching the "Sundell's Colors" Xcode theme + static func sundellsColors(withFont font: Font) -> Theme { + return Theme( + font: font, + plainTextColor: Color( + red: 0.66, + green: 0.74, + blue: 0.74 + ), + tokenColors: [ + .keyword : Color(red: 0.91, green: 0.2, blue: 0.54), + .string : Color(red: 0.98, green: 0.39, blue: 0.12), + .type : Color(red: 0.51, green: 0.51, blue: 0.79), + .call : Color(red: 0.2, green: 0.56, blue: 0.9), + .number : Color(red: 0.86, green: 0.44, blue: 0.34), + .comment : Color(red: 0.42, green: 0.54, blue: 0.58), + .property : Color(red: 0.2, green: 0.56, blue: 0.9), + .dotAccess : Color(red: 0.2, green: 0.56, blue: 0.9), + .preprocessing : Color(red: 0.896, green: 0.488, blue: 0.284) + ] + ) + } + + static func midnight(withFont font: Font) -> Theme { + return Theme( + font: font, + plainTextColor: Color( + red: 1.0, + green: 1.0, + blue: 1.0 + ), + tokenColors: [ + .keyword : Color(red: 0.828, green: 0.095, blue: 0.583), + .string : Color(red: 1.0, green: 0.171, blue: 0.219), + .type : Color(red: 0.137, green: 1.0, blue: 0.512), + .call : Color(red: 0.137, green: 1.0, blue: 0.512), + .number : Color(red: 0.469, green: 0.426, blue: 1.00), + .comment : Color(red: 0.255, green: 0.801, blue: 0.27), + .property : Color(red: 0.431, green: 0.714, blue: 0.533), + .dotAccess : Color(red: 0.431, green: 0.714, blue: 0.533), + .preprocessing : Color(red: 0.896, green: 0.488, blue: 0.284) + ] + ) + } + + static func wwdc17(withFont font: Font) -> Theme { + return Theme( + font: font, + plainTextColor: Color( + red: 0.84, + green: 0.84, + blue: 0.84 + ), + tokenColors: [ + .keyword : Color(red: 0.992, green: 0.791, blue: 0.45), + .string : Color(red: 0.966, green: 0.517, blue: 0.29), + .type : Color(red: 0.431, green: 0.714, blue: 0.533), + .call : Color(red: 0.431, green: 0.714, blue: 0.533), + .number : Color(red: 0.559, green: 0.504, blue: 0.745), + .comment : Color(red: 0.484, green: 0.483, blue: 0.504), + .property : Color(red: 0.431, green: 0.714, blue: 0.533), + .dotAccess : Color(red: 0.431, green: 0.714, blue: 0.533), + .preprocessing : Color(red: 0.992, green: 0.791, blue: 0.45) + ] + ) + } + + static func wwdc18(withFont font: Font) -> Theme { + return Theme( + font: font, + plainTextColor: Color( + red: 1.0, + green: 1.0, + blue: 1.0 + ), + tokenColors: [ + .keyword : Color(red: 0.948, green: 0.140, blue: 0.547), + .string : Color(red: 0.988, green: 0.273, blue: 0.317), + .type : Color(red: 0.584, green: 0.898, blue: 0.361), + .call : Color(red: 0.584, green: 0.898, blue: 0.361), + .number : Color(red: 0.587, green: 0.517, blue: 0.974), + .comment : Color(red: 0.424, green: 0.475, blue: 0.529), + .property : Color(red: 0.584, green: 0.898, blue: 0.361), + .dotAccess : Color(red: 0.584, green: 0.898, blue: 0.361), + .preprocessing : Color(red: 0.952, green: 0.526, blue: 0.229) + ] + ) + } + + static func sunset(withFont font: Font) -> Theme { + return Theme( + font: font, + plainTextColor: Color( + red: 0.0, + green: 0.0, + blue: 0.0 + ), + tokenColors: [ + .keyword : Color(red: 0.161, green: 0.259, blue: 0.467), + .string : Color(red: 0.875, green: 0.027, blue: 0.0), + .type : Color(red: 0.706, green: 0.27, blue: 0.0), + .call : Color(red: 0.278, green: 0.415, blue: 0.593), + .number : Color(red: 0.161, green: 0.259, blue: 0.467), + .comment : Color(red: 0.765, green: 0.455, blue: 0.11), + .property : Color(red: 0.278, green: 0.415, blue: 0.593), + .dotAccess : Color(red: 0.278, green: 0.415, blue: 0.593), + .preprocessing : Color(red: 0.392, green: 0.391, blue: 0.52) + ] + ) + } + + static func presentation(withFont font: Font) -> Theme { + return Theme( + font: font, + plainTextColor: Color( + red: 0.0, + green: 0.0, + blue: 0.0 + ), + tokenColors: [ + .keyword : Color(red: 0.706, green: 0.0, blue: 0.384), + .string : Color(red: 0.729, green: 0.0, blue: 0.067), + .type : Color(red: 0.267, green: 0.537, blue: 0.576), + .call : Color(red: 0.267, green: 0.537, blue: 0.576), + .number : Color(red: 0.0, green: 0.043, blue: 1.0), + .comment : Color(red: 0.336, green: 0.376, blue: 0.42), + .property : Color(red: 0.267, green: 0.537, blue: 0.576), + .dotAccess : Color(red: 0.267, green: 0.537, blue: 0.576), + .preprocessing : Color(red: 0.431, green: 0.125, blue: 0.051) + ] + ) + } + +} diff --git a/Sources/Splash/Theming/Theme.swift b/Sources/Splash/Theming/Theme.swift index 1bcc955..3b274bd 100644 --- a/Sources/Splash/Theming/Theme.swift +++ b/Sources/Splash/Theming/Theme.swift @@ -24,28 +24,3 @@ public struct Theme { self.tokenColors = tokenColors } } - -public extension Theme { - /// Create a theme matching the "Sundell's Colors" Xcode theme - static func sundellsColors(withFont font: Font) -> Theme { - return Theme( - font: font, - plainTextColor: Color( - red: 0.66, - green: 0.74, - blue: 0.74 - ), - tokenColors: [ - .keyword : Color(red: 0.91, green: 0.2, blue: 0.54), - .string : Color(red: 0.98, green: 0.39, blue: 0.12), - .type : Color(red: 0.51, green: 0.51, blue: 0.79), - .call : Color(red: 0.2, green: 0.56, blue: 0.9), - .number : Color(red: 0.86, green: 0.44, blue: 0.34), - .comment : Color(red: 0.42, green: 0.54, blue: 0.58), - .property : Color(red: 0.13, green: 0.67, blue: 0.62), - .dotAccess : Color(red: 0.57, green: 0.7, blue: 0), - .preprocessing : Color(red: 0.71, green: 0.54, blue: 0) - ] - ) - } -}