mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Titlecase the first-of-word code point without leading adjustment (#8241)
case::{title,capitalize}_wtf8 pass a single first-of-word code point to
TitlecaseMapper::titlecase_segment. Under the default Auto leading
adjustment the mapper looks for a Letter/Number/Symbol/Private_Use head
and skips anything else, dropping the titlecase mapping of cased marks
such as U+0345 (COMBINING GREEK YPOGEGRAMMENI) -> U+0399: 'ͅ'.title()
returned 'ͅ' instead of 'Ι'. Use LeadingAdjustment::None so the
given code point is titlecased directly, matching CPython str.title and
str.capitalize.
Assisted-by: Claude
This commit is contained in:
1 parent
797d931f7a
commit
c03e266c9b
1 file changed
+27
-1
@@ -19,6 +19,7 @@ use alloc::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use icu_casemap::options::{LeadingAdjustment, TitlecaseOptions};
|
||||
use icu_casemap::{CaseMapper, TitlecaseMapper};
|
||||
use icu_locale::LanguageIdentifier;
|
||||
use icu_properties::props::{
|
||||
@@ -236,8 +237,16 @@ fn titlecase_string(s: &str, out: &mut FmtWriter<'_>) {
|
||||
}
|
||||
|
||||
fn titlecase_segment(s: &str, out: &mut FmtWriter<'_>) {
|
||||
// Callers pass a single first-of-word code point, which Python titlecases
|
||||
// unconditionally (applying its titlecase mapping). The default `Auto`
|
||||
// leading adjustment looks for a head in Letter/Number/Symbol/Private_Use
|
||||
// and skips anything else, dropping the titlecase mapping of cased marks
|
||||
// such as U+0345 (`ͅ`, general category Mn) -> U+0399 (`Ι`). `None`
|
||||
// titlecases the code point as given.
|
||||
let mut options = TitlecaseOptions::default();
|
||||
options.leading_adjustment = Some(LeadingAdjustment::None);
|
||||
TitlecaseMapper::new()
|
||||
.titlecase_segment(s, &LanguageIdentifier::UNKNOWN, Default::default())
|
||||
.titlecase_segment(s, &LanguageIdentifier::UNKNOWN, options)
|
||||
.write_to(out)
|
||||
.expect("writing to an in-memory buffer cannot fail");
|
||||
}
|
||||
@@ -353,6 +362,23 @@ mod tests {
|
||||
assert_eq!(swapcase_str("Hello"), "hELLO");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn titlecase_first_of_word_takes_titlecase_mapping() {
|
||||
// A leading cased combining mark still takes its titlecase mapping:
|
||||
// U+0345 (ͅ, general category Mn) titlecases to U+0399 (Ι), even though
|
||||
// it is not a Letter/Number/Symbol head.
|
||||
assert_eq!(title_str("\u{0345}"), "\u{0399}");
|
||||
assert_eq!(capitalize_str("\u{0345}"), "\u{0399}");
|
||||
assert_eq!(title_str("\u{0345}a"), "\u{0399}a");
|
||||
// Full (one-to-many) titlecase mappings still apply to the first
|
||||
// character of each word.
|
||||
// cspell:ignore finnish NNISH dzungla Dzungla ßhello Sshello
|
||||
assert_eq!(capitalize_str("finnish"), "Finnish");
|
||||
assert_eq!(title_str("fiNNISH"), "Finnish");
|
||||
assert_eq!(capitalize_str("dzungla"), "Dzungla");
|
||||
assert_eq!(capitalize_str("ßhello"), "Sshello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wtf8_passes_surrogates_through() {
|
||||
let mut buf = Wtf8Buf::from("ab cd");
|
||||
|
||||
Reference in new issue
Block a user