Merge pull request #4 from JohnSundell/cross-platform-fix
Streamline cross-platform implementation
This commit is contained in:
commit
0ec69daecd
@ -4,13 +4,9 @@
|
|||||||
* MIT license - see LICENSE.md
|
* MIT license - see LICENSE.md
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if os(macOS)
|
#if !os(Linux)
|
||||||
import Cocoa
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if os(iOS)
|
import Foundation
|
||||||
import UIKit
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Output format to use to generate an NSAttributedString from the
|
/// Output format to use to generate an NSAttributedString from the
|
||||||
/// highlighted code. A `Theme` is used to determine what fonts and
|
/// highlighted code. A `Theme` is used to determine what fonts and
|
||||||
@ -30,7 +26,7 @@ public struct AttributedStringOutputFormat: OutputFormat {
|
|||||||
public extension AttributedStringOutputFormat {
|
public extension AttributedStringOutputFormat {
|
||||||
struct Builder: OutputBuilder {
|
struct Builder: OutputBuilder {
|
||||||
private let theme: Theme
|
private let theme: Theme
|
||||||
private lazy var font = loadFont()
|
private lazy var font = theme.font.load()
|
||||||
private var string = NSMutableAttributedString()
|
private var string = NSMutableAttributedString()
|
||||||
|
|
||||||
fileprivate init(theme: Theme) {
|
fileprivate init(theme: Theme) {
|
||||||
@ -54,109 +50,18 @@ public extension AttributedStringOutputFormat {
|
|||||||
public func build() -> NSAttributedString {
|
public func build() -> NSAttributedString {
|
||||||
return NSAttributedString(attributedString: string)
|
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)
|
|
||||||
case .path(let path):
|
|
||||||
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 {
|
private extension NSMutableAttributedString {
|
||||||
func append(_ string: String, font: NSFont, color: Color) {
|
func append(_ string: String, font: Font.Loaded, color: Color) {
|
||||||
let color = NSColor(
|
|
||||||
red: CGFloat(color.red),
|
|
||||||
green: CGFloat(color.green),
|
|
||||||
blue: CGFloat(color.blue),
|
|
||||||
alpha: CGFloat(color.alpha)
|
|
||||||
)
|
|
||||||
|
|
||||||
let attributedString = NSAttributedString(string: string, attributes: [
|
let attributedString = NSAttributedString(string: string, attributes: [
|
||||||
.foregroundColor: color,
|
.foregroundColor: color.renderable,
|
||||||
.font: font
|
.font: font
|
||||||
])
|
])
|
||||||
|
|
||||||
append(attributedString)
|
append(attributedString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension NSFont {
|
|
||||||
static func loaded(from path: String, size: CGFloat) -> NSFont? {
|
|
||||||
let url = CFURLCreateWithFileSystemPath(
|
|
||||||
kCFAllocatorDefault,
|
|
||||||
path as CFString,
|
|
||||||
.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
|
#endif
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
/// A representation of a color, for use with a `Theme`.
|
/// A representation of a color, for use with a `Theme`.
|
||||||
/// Since Splash aims to be cross-platform, it uses this
|
/// Since Splash aims to be cross-platform, it uses this
|
||||||
/// simplified color representation rather than `NSColor`
|
/// simplified color representation rather than `NSColor`
|
||||||
@ -23,3 +25,34 @@ public struct Color {
|
|||||||
self.alpha = alpha
|
self.alpha = alpha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal extension Color {
|
||||||
|
var renderable: Renderable {
|
||||||
|
return Renderable(
|
||||||
|
red: CGFloat(red),
|
||||||
|
green: CGFloat(green),
|
||||||
|
blue: CGFloat(blue),
|
||||||
|
alpha: CGFloat(alpha)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
internal extension Color {
|
||||||
|
typealias Renderable = UIColor
|
||||||
|
}
|
||||||
|
|
||||||
|
#elseif os(macOS)
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
internal extension Color {
|
||||||
|
typealias Renderable = NSColor
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
/// A representation of a font, for use with a `Theme`.
|
/// A representation of a font, for use with a `Theme`.
|
||||||
/// Since Splash aims to be cross-platform, it uses this
|
/// Since Splash aims to be cross-platform, it uses this
|
||||||
/// simplified color representation rather than `NSFont`
|
/// simplified font representation rather than `NSFont`
|
||||||
/// or `UIFont`.
|
/// or `UIFont`.
|
||||||
public struct Font {
|
public struct Font {
|
||||||
/// The underlying resource used to load the font
|
/// The underlying resource used to load the font
|
||||||
@ -19,12 +21,7 @@ public struct Font {
|
|||||||
/// Initialize an instance with a path to a font file
|
/// Initialize an instance with a path to a font file
|
||||||
/// on disk and a size.
|
/// on disk and a size.
|
||||||
public init(path: String, size: Double) {
|
public init(path: String, size: Double) {
|
||||||
#if os(macOS)
|
|
||||||
resource = .path((path as NSString).expandingTildeInPath)
|
resource = .path((path as NSString).expandingTildeInPath)
|
||||||
#else
|
|
||||||
resource = .path(path)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +38,69 @@ public extension Font {
|
|||||||
enum Resource {
|
enum Resource {
|
||||||
/// Use an appropriate system font
|
/// Use an appropriate system font
|
||||||
case system
|
case system
|
||||||
|
/// Use a pre-loaded font
|
||||||
|
case preloaded(Loaded)
|
||||||
/// Load a font file from a given file system path
|
/// Load a font file from a given file system path
|
||||||
case path(String)
|
case path(String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal extension Font {
|
||||||
|
func load() -> Loaded {
|
||||||
|
switch resource {
|
||||||
|
case .system:
|
||||||
|
return loadDefaultFont()
|
||||||
|
case .preloaded(let font):
|
||||||
|
return font
|
||||||
|
case .path(let path):
|
||||||
|
return load(fromPath: path) ?? loadDefaultFont()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadDefaultFont() -> Loaded {
|
||||||
|
let font: Loaded?
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
font = UIFont(name: "Menlo-Regular", size: CGFloat(size))
|
||||||
|
#else
|
||||||
|
font = load(fromPath: "/Library/Fonts/Courier New.ttf")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return font ?? .systemFont(ofSize: CGFloat(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func load(fromPath path: String) -> Loaded? {
|
||||||
|
let url = CFURLCreateWithFileSystemPath(
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
path as CFString,
|
||||||
|
.cfurlposixPathStyle,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
guard let font = url.flatMap(CGDataProvider.init).flatMap(CGFont.init) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return CTFontCreateWithGraphicsFont(font, CGFloat(size), nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public extension Font {
|
||||||
|
typealias Loaded = UIFont
|
||||||
|
}
|
||||||
|
|
||||||
|
#elseif os(macOS)
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
public extension Font {
|
||||||
|
typealias Loaded = NSFont
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -6,13 +6,9 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/*
|
#if !os(Linux)
|
||||||
* Extends the Theme struct with static properties that
|
|
||||||
* represent common Xcode defaults as well as community
|
|
||||||
* favourites.
|
|
||||||
*/
|
|
||||||
public extension Theme {
|
|
||||||
|
|
||||||
|
public extension Theme {
|
||||||
/// Create a theme matching the "Sundell's Colors" Xcode theme
|
/// Create a theme matching the "Sundell's Colors" Xcode theme
|
||||||
static func sundellsColors(withFont font: Font) -> Theme {
|
static func sundellsColors(withFont font: Font) -> Theme {
|
||||||
return Theme(
|
return Theme(
|
||||||
@ -36,6 +32,7 @@ public extension Theme {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a theme matching Xcode's "Midnight" theme
|
||||||
static func midnight(withFont font: Font) -> Theme {
|
static func midnight(withFont font: Font) -> Theme {
|
||||||
return Theme(
|
return Theme(
|
||||||
font: font,
|
font: font,
|
||||||
@ -58,6 +55,7 @@ public extension Theme {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creating a theme matching the colors used for the WWDC 2017 sample code
|
||||||
static func wwdc17(withFont font: Font) -> Theme {
|
static func wwdc17(withFont font: Font) -> Theme {
|
||||||
return Theme(
|
return Theme(
|
||||||
font: font,
|
font: font,
|
||||||
@ -80,6 +78,7 @@ public extension Theme {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creating a theme matching the colors used for the WWDC 2018 sample code
|
||||||
static func wwdc18(withFont font: Font) -> Theme {
|
static func wwdc18(withFont font: Font) -> Theme {
|
||||||
return Theme(
|
return Theme(
|
||||||
font: font,
|
font: font,
|
||||||
@ -102,6 +101,7 @@ public extension Theme {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a theme matching Xcode's "Sunset" theme
|
||||||
static func sunset(withFont font: Font) -> Theme {
|
static func sunset(withFont font: Font) -> Theme {
|
||||||
return Theme(
|
return Theme(
|
||||||
font: font,
|
font: font,
|
||||||
@ -124,6 +124,7 @@ public extension Theme {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a theme matching Xcode's "Presentation" theme
|
||||||
static func presentation(withFont font: Font) -> Theme {
|
static func presentation(withFont font: Font) -> Theme {
|
||||||
return Theme(
|
return Theme(
|
||||||
font: font,
|
font: font,
|
||||||
@ -145,5 +146,6 @@ public extension Theme {
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
/// A theme describes what fonts and colors to use when rendering
|
/// A theme describes what fonts and colors to use when rendering
|
||||||
/// certain output formats - such as `NSAttributedString`. A default
|
/// certain output formats - such as `NSAttributedString`. Several
|
||||||
/// implementation is provided that matches the "Sundell's Colors"
|
/// default implementations are provided - see Theme+Defaults.swift.
|
||||||
/// Xcode theme, by using the `sundellsColors(withFont:)` method.
|
|
||||||
public struct Theme {
|
public struct Theme {
|
||||||
/// What font to use to render the highlighted text
|
/// What font to use to render the highlighted text
|
||||||
public var font: Font
|
public var font: Font
|
||||||
@ -24,3 +25,5 @@ public struct Theme {
|
|||||||
self.tokenColors = tokenColors
|
self.tokenColors = tokenColors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user