@@ -125,11 +125,7 @@ extension String {
125
125
///
126
126
/// This function handles loading a character from a string while respecting
127
127
/// an end boundary, even if that end boundary is sub-character or sub-scalar.
128
- ///
129
- /// - Parameters:
130
- /// - pos: The position to load a character from.
131
- /// - end: The limit for the character at `pos`.
132
- /// - Returns:
128
+
133
129
/// - If `pos` is at or past `end`, this function returns `nil`.
134
130
/// - If `end` is between `pos` and the next grapheme cluster boundary (i.e.,
135
131
/// `end` is before `self.index(after: pos)`, then the returned character
@@ -139,11 +135,20 @@ extension String {
139
135
/// is not on a Unicode scalar boundary, the partial scalar is dropped. This
140
136
/// can result in a `nil` return or a character that includes only part of
141
137
/// the `self[pos]` character.
138
+ ///
139
+ /// - Parameters:
140
+ /// - pos: The position to load a character from.
141
+ /// - end: The limit for the character at `pos`.
142
+ /// - Returns: The character at `pos`, bounded by `end`, if it exists, along
143
+ /// with the upper bound of that character. The upper bound is always
144
+ /// scalar-aligned.
142
145
func characterAndEnd( at pos: String . Index , limitedBy end: String . Index ) -> ( Character , String . Index ) ? {
143
146
// FIXME: Sink into the stdlib to avoid multiple boundary calculations
144
147
guard pos < end else { return nil }
145
148
let next = index ( pos, offsetBy: 1 , limitedBy: end) ?? end
146
- return self [ pos..< next] . first. map { ( $0, next) }
149
+ // Substring will round down non-scalar aligned indices
150
+ let substr = self [ pos..< next]
151
+ return substr. first. map { ( $0, substr. endIndex) }
147
152
}
148
153
149
154
func matchAnyNonNewline(
0 commit comments