From 57b991337b6a405ed325954c807ca69c4cd656a1 Mon Sep 17 00:00:00 2001 From: Anthony DePasquale Date: Thu, 10 Oct 2024 20:35:28 +0200 Subject: [PATCH] Fix concurrency issues for Swift 6 --- Package.swift | 2 +- Sources/Splash/Grammar/Grammar.swift | 2 +- .../Output/AttributedStringOutputFormat.swift | 2 +- Sources/Splash/Output/OutputFormat.swift | 2 +- Sources/Splash/Syntax/SyntaxHighlighter.swift | 2 +- Sources/Splash/Syntax/SyntaxRule.swift | 2 +- Sources/Splash/Theming/Font.swift | 48 +++++++++++-------- Sources/Splash/Theming/Theme.swift | 9 ++-- Sources/Splash/Tokenizing/TokenType.swift | 2 +- 9 files changed, 42 insertions(+), 29 deletions(-) diff --git a/Package.swift b/Package.swift index 981b6d4..36d46ad 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.4 +// swift-tools-version: 6.0 /** * Splash diff --git a/Sources/Splash/Grammar/Grammar.swift b/Sources/Splash/Grammar/Grammar.swift index 34ad06f..59418a5 100644 --- a/Sources/Splash/Grammar/Grammar.swift +++ b/Sources/Splash/Grammar/Grammar.swift @@ -9,7 +9,7 @@ import Foundation /// Protocol used to define the grammar of a language to use for /// syntax highlighting. See `SwiftGrammar` for a default implementation /// of the Swift language grammar. -public protocol Grammar { +public protocol Grammar: Sendable { /// The set of characters that make up the delimiters that separates /// tokens within the language, such as punctuation characters. You /// can control whether delimiters should be merged when forming diff --git a/Sources/Splash/Output/AttributedStringOutputFormat.swift b/Sources/Splash/Output/AttributedStringOutputFormat.swift index 4a54733..2e4a567 100644 --- a/Sources/Splash/Output/AttributedStringOutputFormat.swift +++ b/Sources/Splash/Output/AttributedStringOutputFormat.swift @@ -54,7 +54,7 @@ public extension AttributedStringOutputFormat { } private extension NSMutableAttributedString { - func append(_ string: String, font: Font.Loaded, color: Color) { + func append(_ string: String, font: Font.LoadedFont, color: Color) { let attributedString = NSAttributedString(string: string, attributes: [ .foregroundColor: color, .font: font diff --git a/Sources/Splash/Output/OutputFormat.swift b/Sources/Splash/Output/OutputFormat.swift index f7ece1d..165ba0a 100644 --- a/Sources/Splash/Output/OutputFormat.swift +++ b/Sources/Splash/Output/OutputFormat.swift @@ -11,7 +11,7 @@ import Foundation /// NSAttributedString outputs, and custom ones can be defined by /// conforming to this protocol and passing the implementation to a /// syntax highlighter when it's created. -public protocol OutputFormat { +public protocol OutputFormat: Sendable { /// The type of builder that this output format uses. The builder's /// `Output` type determines the output type of the format. associatedtype Builder: OutputBuilder diff --git a/Sources/Splash/Syntax/SyntaxHighlighter.swift b/Sources/Splash/Syntax/SyntaxHighlighter.swift index 5e55c36..7c5eaa9 100644 --- a/Sources/Splash/Syntax/SyntaxHighlighter.swift +++ b/Sources/Splash/Syntax/SyntaxHighlighter.swift @@ -12,7 +12,7 @@ import Foundation /// To initialize this class, pass the desired output format, such as /// `AttributedStringOutputFormat` or `HTMLOutputFormat`, or a custom /// implementation. One syntax highlighter may be reused multiple times. -public struct SyntaxHighlighter { +public struct SyntaxHighlighter: Sendable { private let format: Format private let grammar: Grammar private let tokenizer = Tokenizer() diff --git a/Sources/Splash/Syntax/SyntaxRule.swift b/Sources/Splash/Syntax/SyntaxRule.swift index f3bb796..07313ec 100644 --- a/Sources/Splash/Syntax/SyntaxRule.swift +++ b/Sources/Splash/Syntax/SyntaxRule.swift @@ -11,7 +11,7 @@ import Foundation /// evaluated, is asked to check whether it matches a given segment /// of code. If the rule matches then the rule's token type will be /// associated with the given segment's current token. -public protocol SyntaxRule { +public protocol SyntaxRule: Sendable { /// The token type that this syntax rule represents var tokenType: TokenType { get } diff --git a/Sources/Splash/Theming/Font.swift b/Sources/Splash/Theming/Font.swift index a06f5be..404f22a 100644 --- a/Sources/Splash/Theming/Font.swift +++ b/Sources/Splash/Theming/Font.swift @@ -12,10 +12,9 @@ import Foundation /// Since Splash aims to be cross-platform, it uses this /// simplified font representation rather than `NSFont` /// or `UIFont`. -public struct Font { +public struct Font: Sendable { /// The underlying resource used to load the font public var resource: Resource - /// The size (in points) of the font public var size: Double /// Initialize an instance with a path to a font file @@ -34,42 +33,53 @@ public struct Font { } public extension Font { + /// Sendable representation of a font + struct FontRepresentation: Sendable { + let name: String + let size: Double + + init(name: String, size: Double) { + self.name = name + self.size = size + } + } + /// Enum describing how to load the underlying resource for a font - enum Resource { + enum Resource: Sendable { /// Use an appropriate system font case system /// Use a pre-loaded font - case preloaded(Loaded) + case preloaded(FontRepresentation) /// Load a font file from a given file system path case path(String) } } internal extension Font { - func load() -> Loaded { + func load() -> LoadedFont { switch resource { - case .system: - return loadDefaultFont() - case .preloaded(let font): - return font - case .path(let path): - return load(fromPath: path) ?? loadDefaultFont() + case .system: + return loadDefaultFont() + case .preloaded(let fontRepresentation): + return LoadedFont(name: fontRepresentation.name, size: CGFloat(fontRepresentation.size)) ?? loadDefaultFont() + case .path(let path): + return load(fromPath: path) ?? loadDefaultFont() } } - private func loadDefaultFont() -> Loaded { - let font: Loaded? + private func loadDefaultFont() -> LoadedFont { + let font: LoadedFont? - #if os(iOS) +#if os(iOS) font = UIFont(name: "Menlo-Regular", size: CGFloat(size)) - #else +#else font = load(fromPath: "/Library/Fonts/Courier New.ttf") - #endif +#endif return font ?? .systemFont(ofSize: CGFloat(size)) } - private func load(fromPath path: String) -> Loaded? { + private func load(fromPath path: String) -> LoadedFont? { guard let url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path as CFString, .cfurlposixPathStyle, false), let provider = CGDataProvider(url: url), @@ -89,7 +99,7 @@ internal extension Font { import UIKit public extension Font { - typealias Loaded = UIFont + typealias LoadedFont = UIFont } #elseif os(macOS) @@ -97,7 +107,7 @@ public extension Font { import Cocoa public extension Font { - typealias Loaded = NSFont + typealias LoadedFont = NSFont } #endif diff --git a/Sources/Splash/Theming/Theme.swift b/Sources/Splash/Theming/Theme.swift index 080014a..d70f3a1 100644 --- a/Sources/Splash/Theming/Theme.swift +++ b/Sources/Splash/Theming/Theme.swift @@ -4,14 +4,17 @@ * MIT license - see LICENSE.md */ -import Foundation - #if !os(Linux) +#if os(iOS) +import UIKit +#elseif os(macOS) +import Cocoa +#endif /// A theme describes what fonts and colors to use when rendering /// certain output formats - such as `NSAttributedString`. Several /// default implementations are provided - see Theme+Defaults.swift. -public struct Theme { +public struct Theme: Sendable { /// What font to use to render the highlighted text public var font: Font /// What color to use for plain text (no highlighting) diff --git a/Sources/Splash/Tokenizing/TokenType.swift b/Sources/Splash/Tokenizing/TokenType.swift index 8faa0a4..7bdf49b 100644 --- a/Sources/Splash/Tokenizing/TokenType.swift +++ b/Sources/Splash/Tokenizing/TokenType.swift @@ -7,7 +7,7 @@ import Foundation /// Enum defining the possible types of tokens that can be highlighted -public enum TokenType: Hashable { +public enum TokenType: Hashable, Sendable { /// A keyword, such as `if`, `class`, `let` or attributes such as @available case keyword /// A token that is part of a string literal