diff --git a/Sources/FoundationEssentials/AttributedString/AttributeContainer.swift b/Sources/FoundationEssentials/AttributedString/AttributeContainer.swift index 16b268496..10c2dc35c 100644 --- a/Sources/FoundationEssentials/AttributedString/AttributeContainer.swift +++ b/Sources/FoundationEssentials/AttributedString/AttributeContainer.swift @@ -108,7 +108,9 @@ extension AttributeContainer { @available(FoundationPreview 6.2, *) extension AttributeContainer { - /// Returns an attribute container storing only the attributes in `self` with the `inheritedByAddedText` property set to `true` + /// 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 { @@ -120,9 +122,9 @@ extension AttributeContainer { return AttributeContainer(storage) } - /// Returns an attribute container storing only the attributes in `self` with a matching run boundary property - /// - /// Note: if `nil` is provided then only attributes not bound to any particular boundary will be returned + /// 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 { diff --git a/Sources/FoundationEssentials/AttributedString/AttributeScope.swift b/Sources/FoundationEssentials/AttributedString/AttributeScope.swift index 656bf27cb..2fac17426 100644 --- a/Sources/FoundationEssentials/AttributedString/AttributeScope.swift +++ b/Sources/FoundationEssentials/AttributedString/AttributeScope.swift @@ -206,6 +206,7 @@ extension AttributeScope { Self.scopeDescription.markdownAttributes } + /// A list of all attribute keys contained within this scope and any sub-scopes. @available(FoundationPreview 6.2, *) public static var attributeKeys: some Sequence { Self.scopeDescription.attributes.values diff --git a/Sources/FoundationEssentials/AttributedString/AttributedString+IndexTracking.swift b/Sources/FoundationEssentials/AttributedString/AttributedString+IndexTracking.swift index c4ac53797..c4646fc6c 100644 --- a/Sources/FoundationEssentials/AttributedString/AttributedString+IndexTracking.swift +++ b/Sources/FoundationEssentials/AttributedString/AttributedString+IndexTracking.swift @@ -79,10 +79,12 @@ extension AttributedString.Guts { extension AttributedString { // MARK: inout API - /// Tracks the location of the provided range throughout the mutation closure, updating the provided range to one that represents the same effective locations after the mutation. If updating the provided range is not possible (tracking failed) then this function will fatal error. Use the Optional-returning variants to provide custom fallback behavior. + /// Tracks the location of the provided range throughout the mutation closure, updating the provided range to one that represents the same effective locations after the mutation. + /// + /// If updating the provided range is not possible (tracking failed) then this function will fatal error. Use the `Optional`-returning variants to provide custom fallback behavior. /// - Parameters: - /// - range: a range to track throughout the `body` closure - /// - body: a mutating operation, or set of operations, to perform on the value of `self`. The value of `self` is provided to the closure as an `inout AttributedString` that the closure should mutate directly. Do not capture the value of `self` in the provided closure - the closure should mutate the provided `inout` copy. + /// - range: A range to track throughout the `body` closure. + /// - body: A mutating operation, or set of operations, to perform on the value of `self`. The value of `self` is provided to the closure as an `inout AttributedString` that the closure should mutate directly. Do not capture the value of `self` in the provided closure - the closure should mutate the provided `inout` copy. public mutating func transform(updating range: inout Range, body: (inout AttributedString) throws(E) -> Void) throws(E) -> Void { guard let result = try self.transform(updating: range, body: body) else { fatalError("The provided mutation body did not allow for maintaining index tracking. Ensure that your mutation body mutates the provided AttributedString instead of replacing it with a different AttributedString or use the non-inout version of transform(updating:body:) which returns an Optional value to provide fallback behavior.") @@ -90,10 +92,12 @@ extension AttributedString { range = result } - /// Tracks the location of the provided ranges throughout the mutation closure, updating them to new ranges that represent the same effective locations after the mutation. If updating the provided ranges is not possible (tracking failed) then this function will fatal error. Use the Optional-returning variants to provide custom fallback behavior. + /// Tracks the location of the provided ranges throughout the mutation closure, updating them to new ranges that represent the same effective locations after the mutation. + /// + /// If updating the provided ranges is not possible (tracking failed) then this function will fatal error. Use the `Optional`-returning variants to provide custom fallback behavior. /// - Parameters: - /// - ranges: a list of ranges to track throughout the `body` closure. The updated array (after the function is called) is guaranteed to be the same size as the provided array. Updated ranges are located at the same indices as their respective original ranges in the input `ranges` array. - /// - body: a mutating operation, or set of operations, to perform on the value of `self`. The value of `self` is provided to the closure as an `inout AttributedString` that the closure should mutate directly. Do not capture the value of `self` in the provided closure - the closure should mutate the provided `inout` copy. + /// - ranges: A list of ranges to track throughout the `body` closure. The updated array (after the function is called) is guaranteed to be the same size as the provided array. Updated ranges are located at the same indices as their respective original ranges in the input `ranges` array. + /// - body: A mutating operation, or set of operations, to perform on the value of `self`. The value of `self` is provided to the closure as an `inout AttributedString` that the closure should mutate directly. Do not capture the value of `self` in the provided closure - the closure should mutate the provided `inout` copy. public mutating func transform(updating ranges: inout [Range], body: (inout AttributedString) throws(E) -> Void) throws(E) -> Void { guard let result = try self.transform(updating: ranges, body: body) else { fatalError("The provided mutation body did not allow for maintaining index tracking. Ensure that your mutation body mutates the provided AttributedString instead of replacing it with a different AttributedString or use the non-inout version of transform(updating:body:) which returns an Optional value to provide fallback behavior.") @@ -103,20 +107,20 @@ extension AttributedString { // MARK: Optional-returning API - /// Tracks the location of the provided range throughout the mutation closure, returning a new, updated range that represents the same effective locations after the mutation + /// Tracks the location of the provided range throughout the mutation closure, returning a new, updated range that represents the same effective locations after the mutation. /// - Parameters: - /// - range: a range to track throughout the `mutation` block - /// - mutation: a mutating operation, or set of operations, to perform on this `AttributedString` - /// - Returns: the updated `Range` that is valid after the mutation has been performed, or `nil` if the mutation performed does not allow for tracking to succeed (such as replacing the provided inout variable with an entirely different AttributedString) + /// - range: A range to track throughout the `body` block. + /// - body: A mutating operation, or set of operations, to perform on this `AttributedString`. + /// - Returns: the updated `Range` that is valid after the mutation has been performed, or `nil` if the mutation performed does not allow for tracking to succeed (such as replacing the provided inout variable with an entirely different `AttributedString`). public mutating func transform(updating range: Range, body: (inout AttributedString) throws(E) -> Void) throws(E) -> Range? { try self.transform(updating: [range], body: body)?.first } /// Tracks the location of the provided ranges throughout the mutation closure, returning a new, updated range that represents the same effective locations after the mutation /// - Parameters: - /// - index: an index to track throughout the `mutation` block - /// - mutation: a mutating operation, or set of operations, to perform on this `AttributedString` - /// - Returns: the updated `Range`s that is valid after the mutation has been performed, or `nil` if the mutation performed does not allow for tracking to succeed (such as replacing the provided inout variable with an entirely different AttributedString). When the return value is non-nil, the returned array is guaranteed to be the same size as the provided array with updated ranges at the same Array indices as their respective original ranges in the input array. + /// - ranges: Ranges to track throughout the `body` block. + /// - body: A mutating operation, or set of operations, to perform on this `AttributedString`. + /// - Returns: the updated `Range`s that are valid after the mutation has been performed or `nil` if the mutation performed does not allow for tracking to succeed (such as replacing the provided inout variable with an entirely different `AttributedString`). When the return value is non-`nil`, the returned array is guaranteed to be the same size as the provided array with updated ranges at the same indices as their respective original ranges in the input array. public mutating func transform(updating ranges: [Range], body: (inout AttributedString) throws(E) -> Void) throws(E) -> [Range]? { precondition(!ranges.isEmpty, "Cannot update an empty array of ranges") diff --git a/Sources/FoundationEssentials/AttributedString/AttributedString+IndexValidity.swift b/Sources/FoundationEssentials/AttributedString/AttributedString+IndexValidity.swift index ec35ca236..8b617aaab 100644 --- a/Sources/FoundationEssentials/AttributedString/AttributedString+IndexValidity.swift +++ b/Sources/FoundationEssentials/AttributedString/AttributedString+IndexValidity.swift @@ -45,12 +45,18 @@ extension AttributedString.Guts { @available(FoundationPreview 6.2, *) extension AttributedString.Index { + /// Indicates whether the index is valid for use with the provided attributed string. + /// - Parameter text: An attributed string used to validate the index. + /// - Returns: `true` when the index is valid for use with the provided attributed string; otherwise, false. An index is valid if it is both within the bounds of the attributed string and was produced from the provided string without any intermediate mutations. public func isValid(within text: some AttributedStringProtocol) -> Bool { self._version == text.__guts.version && self >= text.startIndex && self < text.endIndex } + /// Indicates whether the index is valid for use with the provided discontiguous attributed string. + /// - Parameter text: A discontiguous attributed string used to validate the index. + /// - Returns: `true` when the index is valid for use with the provided discontiguous attributed string; otherwise, false. An index is valid if it is both within the bounds of the discontigous attributed string and was produced from the provided string without any intermediate mutations. public func isValid(within text: DiscontiguousAttributedSubstring) -> Bool { self._version == text._guts.version && text._indices.contains(self._value) @@ -59,6 +65,9 @@ extension AttributedString.Index { @available(FoundationPreview 6.2, *) extension Range { + /// Indicates whether the range is valid for use with the provided attributed string. + /// - Parameter text: An attributed string used to validate the range. + /// - Returns: `true` when the range is valid for use with the provided attributed string; otherwise, false. A range is valid if its lower and upper bounds are each either valid in the attributed string or equivalent to the string's `endIndex`. public func isValid(within text: some AttributedStringProtocol) -> Bool { // Note: By nature of Range's lowerBound <= upperBound requirement, this is also sufficient to determine that lowerBound <= endIndex && upperBound >= startIndex self.lowerBound._version == text.__guts.version && @@ -67,6 +76,9 @@ extension Range { self.upperBound <= text.endIndex } + /// Indicates whether the range is valid for use with the provided discontiguous attributed string. + /// - Parameter text: A discontiguous attributed string used to validate the range. + /// - Returns: `true` when the range is valid for use with the provided discontiguous attributed string; otherwise, false. A range is valid if its lower and upper bounds are each either valid in the discontiguous attributed string or equivalent to the string's `endIndex`. public func isValid(within text: DiscontiguousAttributedSubstring) -> Bool { let endIndex = text._indices.ranges.last?.upperBound return self.lowerBound._version == text._guts.version && @@ -78,12 +90,18 @@ extension Range { @available(FoundationPreview 6.2, *) extension RangeSet { + /// Indicates whether the range set is valid for use with the provided attributed string. + /// - Parameter text: An attributed string used to validate the range set. + /// - Returns: `true` when the range set is valid for use with the provided attributed string; otherwise, false. A range set is valid if each of its ranges are valid in the attributed string. public func isValid(within text: some AttributedStringProtocol) -> Bool { self.ranges.allSatisfy { $0.isValid(within: text) } } + /// Indicates whether the range set is valid for use with the provided discontiguous attributed string. + /// - Parameter text: A discontigious attributed string used to validate the range set. + /// - Returns: `true` when the range set is valid for use with the provided discontiguous attributed string; otherwise, false. A range set is valid if each of its ranges are valid in the discontiguous attributed string. public func isValid(within text: DiscontiguousAttributedSubstring) -> Bool { self.ranges.allSatisfy { $0.isValid(within: text) diff --git a/Sources/FoundationEssentials/AttributedString/AttributedString+UTF16View.swift b/Sources/FoundationEssentials/AttributedString/AttributedString+UTF16View.swift index b732e4e3c..a9566bebd 100644 --- a/Sources/FoundationEssentials/AttributedString/AttributedString+UTF16View.swift +++ b/Sources/FoundationEssentials/AttributedString/AttributedString+UTF16View.swift @@ -20,6 +20,7 @@ internal import _FoundationCollections @available(FoundationPreview 6.2, *) extension AttributedString { + /// A view of an attributed string’s contents as a collection of UTF-16 code units. public struct UTF16View: Sendable { internal var _guts: Guts internal var _range: Range @@ -35,6 +36,7 @@ extension AttributedString { } } + /// A view of the attributed string’s contents as a collection of UTF-16 code units. public var utf16: UTF16View { UTF16View(_guts) } @@ -42,6 +44,7 @@ extension AttributedString { @available(FoundationPreview 6.2, *) extension AttributedSubstring { + /// A view of the attributed substring's contents as a collection of UTF-16 code units. public var utf16: AttributedString.UTF16View { AttributedString.UTF16View(_guts, in: _range) } diff --git a/Sources/FoundationEssentials/AttributedString/AttributedString+UTF8View.swift b/Sources/FoundationEssentials/AttributedString/AttributedString+UTF8View.swift index 1a993e7fc..7f68cf8b2 100644 --- a/Sources/FoundationEssentials/AttributedString/AttributedString+UTF8View.swift +++ b/Sources/FoundationEssentials/AttributedString/AttributedString+UTF8View.swift @@ -20,6 +20,7 @@ internal import _FoundationCollections @available(FoundationPreview 6.2, *) extension AttributedString { + /// A view of an attributed string’s contents as a collection of UTF-8 code units. public struct UTF8View: Sendable { internal var _guts: Guts internal var _range: Range @@ -34,7 +35,8 @@ extension AttributedString { _range = range } } - + + /// A view of the attributed string’s contents as a collection of UTF-8 code units. public var utf8: UTF8View { UTF8View(_guts) } @@ -42,6 +44,7 @@ extension AttributedString { @available(FoundationPreview 6.2, *) extension AttributedSubstring { + /// A view of the attributed substring's contents as a collection of UTF-8 code units. public var utf8: AttributedString.UTF8View { AttributedString.UTF8View(_guts, in: _range) } diff --git a/Sources/FoundationEssentials/AttributedString/DiscontiguousAttributedSubstring.swift b/Sources/FoundationEssentials/AttributedString/DiscontiguousAttributedSubstring.swift index e73fcb907..649ceb730 100644 --- a/Sources/FoundationEssentials/AttributedString/DiscontiguousAttributedSubstring.swift +++ b/Sources/FoundationEssentials/AttributedString/DiscontiguousAttributedSubstring.swift @@ -18,6 +18,7 @@ internal import _RopeModule internal import _FoundationCollections #endif +/// A discontiguous portion of an attributed string. @dynamicMemberLookup @available(FoundationPreview 6.2, *) public struct DiscontiguousAttributedSubstring: Sendable { @@ -42,6 +43,7 @@ public struct DiscontiguousAttributedSubstring: Sendable { @available(FoundationPreview 6.2, *) extension DiscontiguousAttributedSubstring { + /// The underlying attributed string that the discontiguous attributed substring derives from. public var base: AttributedString { return AttributedString(_guts) } @@ -124,6 +126,8 @@ extension DiscontiguousAttributedSubstring : AttributedStringAttributeMutation { } } + /// Returns a discontiguous substring of this discontiguous attributed string using a range to indicate the discontiguous substring bounds. + /// - Parameter bounds: A range that indicates the bounds of the discontiguous substring to return. public subscript(bounds: some RangeExpression) -> DiscontiguousAttributedSubstring { let characterView = AttributedString.CharacterView(_guts) let bounds = bounds.relative(to: characterView)._bstringRange @@ -133,6 +137,8 @@ extension DiscontiguousAttributedSubstring : AttributedStringAttributeMutation { preconditionFailure("Attributed string index range \(bounds) is out of bounds") } + /// Returns a discontiguous substring of this discontiguous attributed string using a set of ranges to indicate the discontiguous substring bounds. + /// - Parameter bounds: A set of ranges that indicate the bounds of the discontiguous substring to return. public subscript(bounds: RangeSet) -> DiscontiguousAttributedSubstring { let bounds = bounds._bstringIndices if bounds.ranges.isEmpty { @@ -151,14 +157,17 @@ extension DiscontiguousAttributedSubstring : AttributedStringAttributeMutation { @available(FoundationPreview 6.2, *) extension DiscontiguousAttributedSubstring { + /// The characters of the discontiguous attributed string, as a view into the underlying string. public var characters: DiscontiguousSlice { AttributedString.CharacterView(_guts)[_indices._attributedStringIndices(version: _guts.version)] } + /// The Unicode scalars of the discontiguous attributed string, as a view into the underlying string. public var unicodeScalars: DiscontiguousSlice { AttributedString.UnicodeScalarView(_guts)[_indices._attributedStringIndices(version: _guts.version)] } + /// The attributed runs of the discontiguous attributed string, as a view into the underlying string. public var runs: AttributedString.Runs { AttributedString.Runs(_guts, in: _indices) } @@ -166,6 +175,10 @@ extension DiscontiguousAttributedSubstring { @available(FoundationPreview 6.2, *) extension DiscontiguousAttributedSubstring { + /// Returns an attribute value that corresponds to an attributed string key. + /// + /// This subscript returns `nil` unless the specified attribute exists, and is present and identical for the entire discontiguous attributed substring. To find portions of an attributed string with consistent attributes, use the `runs` property. + /// Getting or setting stringwide attributes with this subscript has `O(n)` behavior in the worst case, where n is the number of runs. public subscript(_: K.Type) -> K.Value? where K.Value : Sendable { get { var result: AttributedString._AttributeValue? @@ -194,6 +207,10 @@ extension DiscontiguousAttributedSubstring { } } + /// Returns an attribute value that a key path indicates. + /// + /// This subscript returns `nil` unless the specified attribute exists, and is present and identical for the entire discontiguous attributed substring. To find portions of an attributed string with consistent attributes, use the `runs` property. + /// Getting or setting stringwide attributes with this subscript has `O(n)` behavior in the worst case, where n is the number of runs. @inlinable // Trivial implementation, allows callers to optimize away the keypath allocation public subscript( dynamicMember keyPath: KeyPath @@ -202,6 +219,7 @@ extension DiscontiguousAttributedSubstring { set { self[K.self] = newValue } } + /// Returns a scoped attribute container that a key path indicates. public subscript( dynamicMember keyPath: KeyPath ) -> ScopedAttributeContainer { @@ -247,6 +265,8 @@ extension DiscontiguousAttributedSubstring { @available(FoundationPreview 6.2, *) extension AttributedString { + /// Creates an attributed string from a discontiguous attributed substring. + /// - Parameter substring: A discontiguous attributed substring to create the new attributed string from. public init(_ substring: DiscontiguousAttributedSubstring) { let created = AttributedString.Guts() for range in substring._indices.ranges { @@ -261,6 +281,8 @@ extension AttributedString { @available(FoundationPreview 6.2, *) extension AttributedStringProtocol { + /// Returns a discontiguous substring of this attributed string using a set of ranges to indicate the discontiguous substring bounds. + /// - Parameter indices: A set of ranges that indicate the bounds of the discontiguous substring to return. public subscript(_ indices: RangeSet) -> DiscontiguousAttributedSubstring { let range = Range(uncheckedBounds: (startIndex, endIndex))._bstringRange let newIndices = indices._bstringIndices.intersection(RangeSet(range)) @@ -270,6 +292,8 @@ extension AttributedStringProtocol { @available(FoundationPreview 6.2, *) extension AttributedString { + /// Returns a discontiguous substring of this discontiguous attributed string using a set of ranges to indicate the discontiguous substring bounds. + /// - Parameter indices: A set of ranges that indicate the bounds of the discontiguous substring to return. public subscript(_ indices: RangeSet) -> DiscontiguousAttributedSubstring { get { let range = Range(uncheckedBounds: (startIndex, endIndex))._bstringRange @@ -312,6 +336,8 @@ extension AttributedString { } } + /// Removes the elements at the given indices. + /// - Parameter subranges: The indices of the elements to remove. public mutating func removeSubranges(_ subranges: RangeSet) { ensureUniqueReference() for range in subranges.ranges.lazy.reversed() {