Repository: typst/typst
File: crates/typst-library/src/math/accent.rs (Rust)

Code before:
    #[default(true)]
    pub dotless: bool,
}

/// An accent character.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Accent(pub char);

impl Accent {
    /// Normalizes a string into an accent.
    pub fn new(s: &str) -> StrResult<Self> {
        if let Some(accent) = Self::combine(s) {
            return Ok(Self(accent));
        }
        if let Ok(accent) = s.parse::<char>() {
            return Ok(Self(accent));
        }
        bail!("expected a single codepoint")
    }

    /// Normalize an accent to a combining one.
    pub fn combine(s: &str) -> Option<char> {
        ACCENTS
            .iter()
            .copied()
            .find(|(_, names)| names.contains(&s))
            .map(|(accent, _)| accent)
    }

    /// Whether this accent is a bottom accent or not.
    pub fn is_bottom(&self) -> bool {
        static COMBINING_CLASS_DATA: LazyLock<CodePointMapData<CanonicalCombiningClass>> =
            LazyLock::new(|| {
                icu_properties::maps::load_canonical_combining_class(
                    &BlobDataProvider::try_new_from_static_blob(typst_assets::icu::ICU)
                        .unwrap()
                        .as_deserializing(),
                )
                .unwrap()
            });

        matches!(
            COMBINING_CLASS_DATA.as_borrowed().get(self.0),
            CanonicalCombiningClass::Below
        )
    }
}

/// Gets the accent function corresponding to a name, if any.
pub fn get_accent_func(name: &str) -> Option<Func> {
    Some(

Review comment (bug):
The test failure seems unrelated to the arguments. The problem is that `names` is empty here for `dot.triple` and `dot.quad`. I think this should also allow for `s` itself to be in the accent list as the char.

Code after:
    /// ```
    #[default(true)]
    pub dotless: bool,
}

/// An accent character.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Accent(pub char);

impl Accent {
    /// Normalize a character into an accent.
    pub fn new(c: char) -> Self {
        Self(Self::combine(c).unwrap_or(c))
    }

    /// Normalize an accent to a combining one.
    pub fn combine(c: char) -> Option<char> {
        ACCENTS
            .iter()
            .copied()
            .find(|(accent, names)| *accent == c || names.contains(&c))
            .map(|(accent, _)| accent)
    }

    /// Whether this accent is a bottom accent or not.
    pub fn is_bottom(&self) -> bool {
        static COMBINING_CLASS_DATA: LazyLock<CodePointMapData<CanonicalCombiningClass>> =
            LazyLock::new(|| {
                icu_properties::maps::load_canonical_combining_class(
                    &BlobDataProvider::try_new_from_static_blob(typst_assets::icu::ICU)
                        .unwrap()
                        .as_deserializing(),
                )
                .unwrap()
            });

        matches!(
            COMBINING_CLASS_DATA.as_borrowed().get(self.0),
            CanonicalCombiningClass::Below
        )
    }
}

/// Gets the accent function corresponding to a name, if any.
pub fn get_accent_func(c: char) -> Option<Func> {
    Some(
        FUNCS
            .get(&Accent::combine(c)?)
            .expect("this accent should have a function")
            .into(),
    )