Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion crates/unicode/src/case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
Expand Down
Loading