Thanks to Chris Chapman (@cjchapman) for driving this!
Add bidiClass to Unicode.Scalar.Properties
- Proposal: SE-NNNN
- Authors: Chris Chapman, Michael Ilseman
- Review Manager: TBD
- Status: Awaiting review
- Implementation: swiftlang/swift#91404
- Review: (pitch)
Introduction
Unicode.Scalar.Properties exposes many of the scalar properties defined by the Unicode Standard, but not Bidi_Class, the classification that drives the Unicode Bidirectional Algorithm (UAX #9). This proposal adds a bidiClass property and a Unicode.BidiClass enum for its values.
Motivation
Code that lays out or transforms bidirectional text needs each scalar's Bidi_Class. An example task is deciding whether a piece of text must be wrapped in isolate controls (U+2068…U+2069) before being embedded in surrounding text of the opposite direction, so that it does not reorder its neighbors. That decision requires knowing whether the text contains strong-directional or numeric scalars, which is exactly what Bidi_Class reports. The closely related generalCategory is not a substitute: it does not distinguish a strong left-to-right letter from a strong right-to-left one.
Today this value must come from elsewhere, and the most direct source, the system's ICU library, is the same pain point described in SE-0211. Swift's Foundation ran into this when implementing list-item isolate wrapping in ListFormatStyle.
Proposed solution
We add a bidiClass computed property to Unicode.Scalar.Properties, and a Unicode.BidiClass enum, mirroring the existing generalCategory property and Unicode.GeneralCategory enum:
let scalar: Unicode.Scalar = "\u{05D0}" // HEBREW LETTER ALEF
scalar.properties.bidiClass // .rightToLeft
Every scalar has a defined Bidi_Class: scalars not explicitly assigned one, including unassigned code points, take a code-point-based default. So bidiClass returns a value for every Unicode.Scalar.
Detailed design
Unicode.BidiClass has one case per Bidi_Class value, with names derived from the property's long value names, each documented with its standard abbreviation (as Unicode.GeneralCategory does with the two-letter codes):
extension Unicode {
/// The classification of a scalar used by the Unicode Bidirectional
/// Algorithm.
@available(SwiftStdlib 6.5, *)
public enum BidiClass: Hashable, Sendable {
case leftToRight // L
case rightToLeft // R
case arabicLetter // AL
case europeanNumber // EN
case europeanSeparator // ES
case europeanTerminator // ET
case arabicNumber // AN
case commonSeparator // CS
case nonspacingMark // NSM
case boundaryNeutral // BN
case paragraphSeparator // B
case segmentSeparator // S
case whitespace // WS
case otherNeutral // ON
case leftToRightEmbedding // LRE
case leftToRightOverride // LRO
case rightToLeftEmbedding // RLE
case rightToLeftOverride // RLO
case popDirectionalFormat // PDF
case leftToRightIsolate // LRI
case rightToLeftIsolate // RLI
case firstStrongIsolate // FSI
case popDirectionalIsolate // PDI
}
}
extension Unicode.Scalar.Properties {
/// The bidirectional class of the scalar.
///
/// This property corresponds to the "Bidi_Class" property in the
/// [Unicode Standard](http://www.unicode.org/versions/latest/).
@available(SwiftStdlib 6.5, *)
public var bidiClass: Unicode.BidiClass { get }
}
The values follow the Unicode Character Database's DerivedBidiClass.txt, including its @missing default rules.
Source compatibility
This proposal is purely additive and has no source compatibility impact.
Effect on ABI stability
This proposal is purely an extension of the ABI of the standard library. Unicode.BidiClass is a non-frozen enum (like Unicode.GeneralCategory), so future values can be added without breaking ABI. No raw representation is exposed, leaving the storage free to change.