1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
@dynamicMemberLookup
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public struct AttributeContainer : Sendable {
internal var storage : AttributedString._AttributeStorage
public init() {
storage = .init()
}
internal init(_ storage: AttributedString._AttributeStorage) {
self.storage = storage
}
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributeContainer {
@preconcurrency
public subscript<T: AttributedStringKey>(_: T.Type) -> T.Value? where T.Value : Sendable {
get { storage[T.self] }
set { storage[T.self] = newValue }
}
@preconcurrency
@inlinable // Trivial implementation, allows callers to optimize away the keypath allocation
public subscript<K: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeDynamicLookup, K>) -> K.Value? where K.Value : Sendable {
get { self[K.self] }
set { self[K.self] = newValue }
}
public subscript<S: AttributeScope>(dynamicMember keyPath: KeyPath<AttributeScopes, S.Type>) -> ScopedAttributeContainer<S> {
get {
return ScopedAttributeContainer(storage)
}
_modify {
var container = ScopedAttributeContainer<S>()
defer {
if let removedKey = container.removedKey {
storage[removedKey] = nil
} else {
storage.mergeIn(container.storage)
}
}
yield &container
}
}
public static subscript<K: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeDynamicLookup, K>) -> Builder<K> {
return Builder(container: AttributeContainer())
}
@_disfavoredOverload
public subscript<K: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeDynamicLookup, K>) -> Builder<K> {
return Builder(container: self)
}
public struct Builder<T: AttributedStringKey> : Sendable {
var container : AttributeContainer
@preconcurrency
public func callAsFunction(_ value: T.Value) -> AttributeContainer where T.Value : Sendable {
var new = container
new[T.self] = value
return new
}
}
public mutating func merge(_ other: AttributeContainer, mergePolicy: AttributedString.AttributeMergePolicy = .keepNew) {
self.storage.mergeIn(other.storage, mergePolicy: mergePolicy)
}
public func merging(_ other: AttributeContainer, mergePolicy: AttributedString.AttributeMergePolicy = .keepNew) -> AttributeContainer {
var copy = self
copy.merge(other, mergePolicy: mergePolicy)
return copy
}
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributeContainer: Equatable {}
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
extension AttributeContainer: Hashable {}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributeContainer: CustomStringConvertible {
public var description: String {
storage.description
}
}
extension AttributeContainer {
internal var _hasConstrainedAttributes: Bool {
storage.hasConstrainedAttributes
}
}
@available(FoundationPreview 6.2, *)
extension AttributeContainer {
/// Returns a copy of the attribute container with only attributes that specify the provided inheritance behavior.
/// - Parameter inheritedByAddedText: An `inheritedByAddedText` value to filter. Attributes matching this value are included in the returned container.
/// - Returns: A copy of the attribute container with only attributes whose `inheritedByAddedText` property matches the provided value.
public func filter(inheritedByAddedText: Bool) -> AttributeContainer {
var storage = self.storage
for (key, value) in storage.contents {
let inherited = value.inheritedByAddedText && !value.isInvalidatedOnTextChange
if inherited != inheritedByAddedText {
storage[key] = nil
}
}
return AttributeContainer(storage)
}
/// Returns a copy of the attribute container with only attributes that have the provided run boundaries.
/// - Parameter runBoundaries: The required `runBoundaries` value of the filtered attributes. If `nil` is provided, only attributes not bound to any specific boundary will be returned.
/// - Returns: A copy of the attribute container with only attributes whose `runBoundaries` property matches the provided value.
public func filter(runBoundaries: AttributedString.AttributeRunBoundaries?) -> AttributeContainer {
var storage = self.storage
for (key, value) in storage.contents {
if value.runBoundaries != runBoundaries {
storage[key] = nil
}
}
return AttributeContainer(storage)
}
}
|