swift-foundation/Sources/FoundationEssentials/AttributedString/AttributedString+Runs+AttributeSlices.swift
2023-04-03 10:41:22 -07:00

783 lines
24 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributedString.Runs {
@preconcurrency
public struct AttributesSlice1<T : AttributedStringKey> : BidirectionalCollection, Sendable where T.Value : Sendable {
public typealias Index = AttributedString.Index
// FIXME: Why no labels?
public typealias Element = (T.Value?, Range<AttributedString.Index>)
internal typealias Runs = AttributedString.Runs
let runs : Runs
let _names: [String]
let _constraints: [AttributeRunBoundaries]
init(runs: Runs) {
self.runs = runs
// FIXME: Get these from a proper cache in runs._guts.
_names = [T.name]
_constraints = T._constraintsInvolved
}
public struct Iterator: IteratorProtocol, Sendable {
// Note: This is basically equivalent to `IndexingIterator`.
public typealias Element = AttributesSlice1.Element
let _slice: AttributesSlice1
var _index: AttributesSlice1.Index
internal init(_ slice: AttributesSlice1) {
self._slice = slice
self._index = slice.startIndex
}
public mutating func next() -> Element? {
if _index == _slice.endIndex {
return nil
}
let run = _slice.runs[_index]
let next = _slice.index(after: _index)
let range = _index ..< next
_index = next
return (run._attributes[T.self], range)
}
}
public func makeIterator() -> Iterator {
Iterator(self)
}
public var startIndex: Index {
runs._range.lowerBound
}
public var endIndex: Index {
runs._range.upperBound
}
public func index(before i: Index) -> Index {
runs._slicedRunBoundary(
before: i,
attributeNames: _names,
constraints: _constraints)
}
public func index(after i: Index) -> Index {
runs._slicedRunBoundary(
after: i,
attributeNames: _names,
constraints: _constraints)
}
public subscript(position: AttributedString.Index) -> Element {
let (start, runIndex) = runs._slicedRunBoundary(
roundingDown: position,
attributeNames: _names,
constraints: _constraints)
let end = self.index(after: position)
let attributes = runs._guts.runs[runIndex.rangeIndex].attributes
return (attributes[T.self], start ..< end)
}
}
public subscript<T : AttributedStringKey>(_ keyPath: KeyPath<AttributeDynamicLookup, T>) -> AttributesSlice1<T> {
return AttributesSlice1<T>(runs: self)
}
public subscript<T : AttributedStringKey>(_ t: T.Type) -> AttributesSlice1<T> {
return AttributesSlice1<T>(runs: self)
}
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributedString.Runs {
@preconcurrency
public struct AttributesSlice2<
T : AttributedStringKey,
U : AttributedStringKey
> : BidirectionalCollection, Sendable
where
T.Value : Sendable,
U.Value : Sendable
{
public typealias Index = AttributedString.Index
// FIXME: Why no labels?
public typealias Element = (T.Value?, U.Value?, Range<AttributedString.Index>)
internal typealias Runs = AttributedString.Runs
let runs : Runs
let _names: [String]
let _constraints: [AttributeRunBoundaries]
init(runs: Runs) {
self.runs = runs
// FIXME: Get these from a proper cache in runs._guts.
_names = [T.name, U.name]
_constraints = Array(_contents: T.runBoundaries, U.runBoundaries)
}
public struct Iterator: IteratorProtocol, Sendable {
// Note: This is basically equivalent to `IndexingIterator`.
public typealias Element = AttributesSlice2.Element
let _slice: AttributesSlice2
var _index: AttributedString.Index
internal init(_ slice: AttributesSlice2) {
self._slice = slice
self._index = slice.startIndex
}
public mutating func next() -> Element? {
if _index == _slice.endIndex {
return nil
}
let run = _slice.runs[_index]
let next = _slice.index(after: _index)
let range = _index ..< next
_index = next
return (run._attributes[T.self], run._attributes[U.self], range)
}
}
public func makeIterator() -> Iterator {
Iterator(self)
}
public var startIndex: Index {
runs._range.lowerBound
}
public var endIndex: Index {
runs._range.upperBound
}
public func index(before i: Index) -> Index {
runs._slicedRunBoundary(
before: i,
attributeNames: _names,
constraints: _constraints)
}
public func index(after i: Index) -> Index {
runs._slicedRunBoundary(
after: i,
attributeNames: _names,
constraints: _constraints)
}
public subscript(position: AttributedString.Index) -> Element {
let (start, runIndex) = runs._slicedRunBoundary(
roundingDown: position,
attributeNames: _names,
constraints: _constraints)
let end = self.index(after: position)
let attributes = runs._guts.runs[runIndex.rangeIndex].attributes
return (attributes[T.self], attributes[U.self], start ..< end)
}
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey
> (
_ t: KeyPath<AttributeDynamicLookup, T>,
_ u: KeyPath<AttributeDynamicLookup, U>
) -> AttributesSlice2<T, U> {
return AttributesSlice2<T, U>(runs: self)
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey
> (
_ t: T.Type,
_ u: U.Type
) -> AttributesSlice2<T, U> {
return AttributesSlice2<T, U>(runs: self)
}
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributedString.Runs {
@preconcurrency
public struct AttributesSlice3<
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey
> : BidirectionalCollection, Sendable
where
T.Value : Sendable,
U.Value : Sendable,
V.Value : Sendable
{
public typealias Index = AttributedString.Index
// FIXME: Why no labels?
public typealias Element = (T.Value?, U.Value?, V.Value?, Range<AttributedString.Index>)
internal typealias Runs = AttributedString.Runs
let runs : Runs
let _names: [String]
let _constraints: [AttributeRunBoundaries]
init(runs: Runs) {
self.runs = runs
// FIXME: Get these from a proper cache in runs._guts.
_names = [T.name, U.name, V.name]
_constraints = Array(_contents: T.runBoundaries, U.runBoundaries, V.runBoundaries)
}
public struct Iterator: IteratorProtocol, Sendable {
// Note: This is basically equivalent to `IndexingIterator`.
public typealias Element = AttributesSlice3.Element
let _slice: AttributesSlice3
var _index: AttributedString.Index
internal init(_ slice: AttributesSlice3) {
self._slice = slice
self._index = slice.startIndex
}
public mutating func next() -> Element? {
if _index == _slice.endIndex {
return nil
}
let run = _slice.runs[_index]
let next = _slice.index(after: _index)
let range = _index ..< next
_index = next
return (
run._attributes[T.self],
run._attributes[U.self],
run._attributes[V.self],
range)
}
}
public func makeIterator() -> Iterator {
Iterator(self)
}
public var startIndex: Index {
runs._range.lowerBound
}
public var endIndex: Index {
runs._range.upperBound
}
public func index(before i: Index) -> Index {
runs._slicedRunBoundary(
before: i,
attributeNames: _names,
constraints: _constraints)
}
public func index(after i: Index) -> Index {
runs._slicedRunBoundary(
after: i,
attributeNames: _names,
constraints: _constraints)
}
public subscript(position: AttributedString.Index) -> Element {
let (start, runIndex) = runs._slicedRunBoundary(
roundingDown: position,
attributeNames: _names,
constraints: _constraints)
let end = self.index(after: position)
let attributes = runs._guts.runs[runIndex.rangeIndex].attributes
return (attributes[T.self], attributes[U.self], attributes[V.self], start ..< end)
}
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey
> (
_ t: KeyPath<AttributeDynamicLookup, T>,
_ u: KeyPath<AttributeDynamicLookup, U>,
_ v: KeyPath<AttributeDynamicLookup, V>
) -> AttributesSlice3<T, U, V> {
return AttributesSlice3<T, U, V>(runs: self)
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey
> (
_ t: T.Type,
_ u: U.Type,
_ v: V.Type
) -> AttributesSlice3<T, U, V> {
return AttributesSlice3<T, U, V>(runs: self)
}
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributedString.Runs {
@preconcurrency
public struct AttributesSlice4<
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey,
W : AttributedStringKey
> : BidirectionalCollection, Sendable
where
T.Value : Sendable,
U.Value : Sendable,
V.Value : Sendable,
W.Value : Sendable
{
public typealias Index = AttributedString.Index
// FIXME: Why no labels?
public typealias Element = (T.Value?, U.Value?, V.Value?, W.Value?, Range<AttributedString.Index>)
internal typealias Runs = AttributedString.Runs
let runs : Runs
let _names: [String]
let _constraints: [AttributeRunBoundaries]
init(runs: Runs) {
self.runs = runs
// FIXME: Get these from a proper cache in runs._guts.
_names = [T.name, U.name, V.name, W.name]
_constraints = Array(
_contents: T.runBoundaries, U.runBoundaries, V.runBoundaries, W.runBoundaries)
}
public struct Iterator: IteratorProtocol, Sendable {
// Note: This is basically equivalent to `IndexingIterator`.
public typealias Element = AttributesSlice4.Element
let _slice: AttributesSlice4
var _index: AttributedString.Index
internal init(_ slice: AttributesSlice4) {
self._slice = slice
self._index = slice.startIndex
}
public mutating func next() -> Element? {
if _index == _slice.endIndex {
return nil
}
let run = _slice.runs[_index]
let next = _slice.index(after: _index)
let range = _index ..< next
_index = next
return (
run._attributes[T.self],
run._attributes[U.self],
run._attributes[V.self],
run._attributes[W.self],
range)
}
}
public func makeIterator() -> Iterator {
Iterator(self)
}
public var startIndex: Index {
runs._range.lowerBound
}
public var endIndex: Index {
runs._range.upperBound
}
public func index(before i: Index) -> Index {
runs._slicedRunBoundary(
before: i,
attributeNames: _names,
constraints: _constraints)
}
public func index(after i: Index) -> Index {
runs._slicedRunBoundary(
after: i,
attributeNames: _names,
constraints: _constraints)
}
public subscript(position: AttributedString.Index) -> Element {
let (start, runIndex) = runs._slicedRunBoundary(
roundingDown: position,
attributeNames: _names,
constraints: _constraints)
let end = self.index(after: position)
let attributes = runs._guts.runs[runIndex.rangeIndex].attributes
return (
attributes[T.self],
attributes[U.self],
attributes[V.self],
attributes[W.self],
start ..< end)
}
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey,
W : AttributedStringKey
> (
_ t: KeyPath<AttributeDynamicLookup, T>,
_ u: KeyPath<AttributeDynamicLookup, U>,
_ v: KeyPath<AttributeDynamicLookup, V>,
_ w: KeyPath<AttributeDynamicLookup, W>
) -> AttributesSlice4<T, U, V, W> {
return AttributesSlice4<T, U, V, W>(runs: self)
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey,
W : AttributedStringKey
> (
_ t: T.Type,
_ u: U.Type,
_ v: V.Type,
_ w: W.Type
) -> AttributesSlice4<T, U, V, W> {
return AttributesSlice4<T, U, V, W>(runs: self)
}
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributedString.Runs {
@preconcurrency
public struct AttributesSlice5<
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey,
W : AttributedStringKey,
X : AttributedStringKey
> : BidirectionalCollection, Sendable
where
T.Value : Sendable,
U.Value : Sendable,
V.Value : Sendable,
W.Value : Sendable,
X.Value : Sendable
{
public typealias Index = AttributedString.Index
// FIXME: Why no labels?
public typealias Element = (T.Value?, U.Value?, V.Value?, W.Value?, X.Value?, Range<AttributedString.Index>)
internal typealias Runs = AttributedString.Runs
let runs : Runs
let _names: [String]
let _constraints: [AttributeRunBoundaries]
init(runs: Runs) {
self.runs = runs
// FIXME: Get these from a proper cache in runs._guts.
_names = [T.name, U.name, V.name, W.name]
_constraints = Array(
_contents: T.runBoundaries,
U.runBoundaries,
V.runBoundaries,
W.runBoundaries,
X.runBoundaries)
}
public struct Iterator: IteratorProtocol, Sendable {
public typealias Element = AttributesSlice5.Element
let _slice: AttributesSlice5
var _index: AttributedString.Index
internal init(_ slice: AttributesSlice5) {
self._slice = slice
self._index = slice.startIndex
}
public mutating func next() -> Element? {
if _index == _slice.endIndex {
return nil
}
let run = _slice.runs[_index]
let next = _slice.index(after: _index)
let range = _index ..< next
_index = next
return (
run._attributes[T.self],
run._attributes[U.self],
run._attributes[V.self],
run._attributes[W.self],
run._attributes[X.self],
range)
}
}
public func makeIterator() -> Iterator {
Iterator(self)
}
public var startIndex: Index {
runs._range.lowerBound
}
public var endIndex: Index {
runs._range.upperBound
}
public func index(before i: Index) -> Index {
runs._slicedRunBoundary(
before: i,
attributeNames: _names,
constraints: _constraints)
}
public func index(after i: Index) -> Index {
runs._slicedRunBoundary(
after: i,
attributeNames: _names,
constraints: _constraints)
}
public subscript(position: AttributedString.Index) -> Element {
let (start, runIndex) = runs._slicedRunBoundary(
roundingDown: position,
attributeNames: _names,
constraints: _constraints)
let end = self.index(after: position)
let attributes = runs._guts.runs[runIndex.rangeIndex].attributes
return (
attributes[T.self],
attributes[U.self],
attributes[V.self],
attributes[W.self],
attributes[X.self],
start ..< end)
}
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey,
W : AttributedStringKey,
X : AttributedStringKey
> (
_ t: KeyPath<AttributeDynamicLookup, T>,
_ u: KeyPath<AttributeDynamicLookup, U>,
_ v: KeyPath<AttributeDynamicLookup, V>,
_ w: KeyPath<AttributeDynamicLookup, W>,
_ x: KeyPath<AttributeDynamicLookup, X>
) -> AttributesSlice5<T, U, V, W, X> {
return AttributesSlice5<T, U, V, W, X>(runs: self)
}
public subscript <
T : AttributedStringKey,
U : AttributedStringKey,
V : AttributedStringKey,
W : AttributedStringKey,
X : AttributedStringKey
> (
_ t: T.Type,
_ u: U.Type,
_ v: V.Type,
_ w: W.Type,
_ x: X.Type
) -> AttributesSlice5<T, U, V, W, X> {
return AttributesSlice5<T, U, V, W, X>(runs: self)
}
}
#if FOUNDATION_FRAMEWORK
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
extension AttributedString.Runs {
@_spi(AttributedString)
public struct NSAttributesSlice : BidirectionalCollection, Sendable {
public typealias Index = AttributedString.Index
// FIXME: Why no labels?
public typealias Element = (AttributeContainer, Range<AttributedString.Index>)
internal typealias Runs = AttributedString.Runs
let runs : Runs
let keys : [NSAttributedString.Key]
private let _names: [String]
internal init(runs: Runs, keys: [NSAttributedString.Key]) {
self.runs = runs
self.keys = keys
self._names = keys.map { $0.rawValue }
}
public struct Iterator: IteratorProtocol, Sendable {
// Note: This is basically equivalent to `IndexingIterator`.
public typealias Element = NSAttributesSlice.Element
let _slice: NSAttributesSlice
var _index: AttributedString.Index
internal init(_ slice: NSAttributesSlice) {
self._slice = slice
self._index = slice.startIndex
}
public mutating func next() -> Element? {
if _index == _slice.endIndex {
return nil
}
let run = _slice.runs[_index]
let next = _slice.index(after: _index)
let range = _index ..< next
_index = next
return (_slice.buildContainer(from: run._attributes), range)
}
}
public func makeIterator() -> Iterator {
Iterator(self)
}
public var startIndex: Index {
runs._range.lowerBound
}
public var endIndex: Index {
runs._range.upperBound
}
public func index(before i: Index) -> Index {
runs._slicedRunBoundary(
before: i,
attributeNames: _names,
constraints: [])
}
public func index(after i: Index) -> Index {
runs._slicedRunBoundary(
after: i,
attributeNames: _names,
constraints: [])
}
public subscript(position: AttributedString.Index) -> Element {
let (start, runIndex) = runs._slicedRunBoundary(
roundingDown: position,
attributeNames: _names,
constraints: [])
let end = self.index(after: position)
let attributes = runs._guts.runs[runIndex.rangeIndex].attributes
return (buildContainer(from: attributes), start ..< end)
}
private func buildContainer(from storage: AttributedString._AttributeStorage) -> AttributeContainer {
AttributeContainer(storage.filter { _names.contains($0.key) })
}
}
@_spi(AttributedString)
public subscript(nsAttributedStringKeys keys: NSAttributedString.Key...) -> NSAttributesSlice {
return NSAttributesSlice(runs: self, keys: keys)
}
}
#endif // FOUNDATION_FRAMEWORK
extension RangeReplaceableCollection {
internal init(_contents item1: Element?) {
self.init()
if let item1 { self.append(item1) }
}
internal init(_contents item1: Element?, _ item2: Element?) {
self.init()
var c = 0
if item1 != nil { c &+= 1 }
if item2 != nil { c &+= 1 }
guard c > 0 else { return }
self.reserveCapacity(c)
if let item1 { self.append(item1) }
if let item2 { self.append(item2) }
}
internal init(_contents item1: Element?, _ item2: Element?, _ item3: Element?) {
self.init()
var c = 0
if item1 != nil { c &+= 1 }
if item2 != nil { c &+= 1 }
if item3 != nil { c &+= 1 }
guard c > 0 else { return }
self.reserveCapacity(c)
if let item1 { self.append(item1) }
if let item2 { self.append(item2) }
if let item3 { self.append(item3) }
}
internal init(
_contents item1: Element?, _ item2: Element?, _ item3: Element?, _ item4: Element?
) {
self.init()
var c = 0
if item1 != nil { c &+= 1 }
if item2 != nil { c &+= 1 }
if item3 != nil { c &+= 1 }
if item4 != nil { c &+= 1 }
guard c > 0 else { return }
self.reserveCapacity(c)
if let item1 { self.append(item1) }
if let item2 { self.append(item2) }
if let item3 { self.append(item3) }
if let item4 { self.append(item4) }
}
internal init(
_contents item1: Element?,
_ item2: Element?,
_ item3: Element?,
_ item4: Element?,
_ item5: Element?
) {
self.init()
var c = 0
if item1 != nil { c &+= 1 }
if item2 != nil { c &+= 1 }
if item3 != nil { c &+= 1 }
if item4 != nil { c &+= 1 }
if item5 != nil { c &+= 1 }
guard c > 0 else { return }
self.reserveCapacity(c)
if let item1 { self.append(item1) }
if let item2 { self.append(item2) }
if let item3 { self.append(item3) }
if let item4 { self.append(item4) }
if let item5 { self.append(item5) }
}
}