[Pitch] AttributedString Discontiguous Operations

Hi everyone! In addition to other recent AttributedString APIs that I've pitched, I'd also like to pitch new APIs for AttributedString to support operations over discontiguous subranges. These APIs mirror those introduced by SE-0270 on Collection itself, and I'd love to hear any thoughts/feedback on introducing similar APIs to better support those operations on AttributedStrings. Check out the details below:


AttributedString Discontiguous Operations

Introduction

In SE-0270, we added a new RangeSet type to Swift (representing a sorted, noncontiguous set of ranges in a collection) along with collection APIs that perform operations over referenced noncontiguous elements. These APIs have proved beneficial for use in a variety of collections to easily locate, identify, and mutate multiple ranges of elements in single expressions. AttributedString has already benefitted from these generic collection APIs that are available via the character, unicode scalar, and runs view. However, as AttributedString does not conform to Collection itself, it lacks proper, fully integrated support for operations over discontiguous segments.

Motivation

Using RangeSet-based APIs provided on the AttributedString type directly instead of its individual collection views can be very beneficial. In particular, full support for discontiguous representations of an AttributedString are critical for modeling concepts such as an AttributedString's selection from a UI (either multiple, discontiguous visual collections or a singular visual selection that maps to discontiguous ranges in the logical text storage due to mixed RTL/LTR text). Discontiguous operations would allow for not only iterating over discontiguous contents but will also add the ability to mutate attributes over these discontiguous ranges (which is not possible today with the immutable runs view). We feel that providing these tools will improve the parity between AttributedString and other standard collection types (like String) and will set up AttributedString for success as the best model representation of rich text.

Proposed solution

Developers can use APIs provided by the standard library today to create RangeSets representing indices:

var text = AttributedString("Hello, world!")
let indicesOfL: RangeSet<AttributedString.Index> = text.characters.indices(of: "l")

These new APIs will allow developers to use those indices on AttributedString operations, such as the following:

// Make all "l"s blue
text[indicesOfL].foregroundColor = .blue

print(text[indicesOfL]) // "lll { SwiftUI.ForegroundColor = ... }"

// Iterate all foreground colors on the "l"s
for (range, color) in text[indicesOfL].runs[\.foregroundColor] {
    // ...
}

For the full detailed design along with alternatives considered and future directions, check out the full proposal on the swift-foundation repo PR.

3 Likes