@@ -125,11 +125,7 @@ extension String {
125125 ///
126126 /// This function handles loading a character from a string while respecting
127127 /// 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+
133129 /// - If `pos` is at or past `end`, this function returns `nil`.
134130 /// - If `end` is between `pos` and the next grapheme cluster boundary (i.e.,
135131 /// `end` is before `self.index(after: pos)`, then the returned character
@@ -139,11 +135,20 @@ extension String {
139135 /// is not on a Unicode scalar boundary, the partial scalar is dropped. This
140136 /// can result in a `nil` return or a character that includes only part of
141137 /// 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.
142145 func characterAndEnd( at pos: String . Index , limitedBy end: String . Index ) -> ( Character , String . Index ) ? {
143146 // FIXME: Sink into the stdlib to avoid multiple boundary calculations
144147 guard pos < end else { return nil }
145148 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) }
147152 }
148153
149154 func matchAnyNonNewline(
0 commit comments