Allow passing region to LocalName (#926)

This commit is contained in:
pan93412 2023-04-26 17:30:03 +08:00 committed by GitHub
parent 1d42d6674c
commit a6df909a8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 114 additions and 26 deletions

View File

@ -285,7 +285,7 @@ impl<T: Into<Value>> From<Celled<T>> for Value {
}
impl LocalName for TableElem {
fn local_name(&self, lang: Lang) -> &'static str {
fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str {
match lang {
Lang::ARABIC => "جدول",
Lang::BOKMÅL => "Tabell",

View File

@ -277,7 +277,7 @@ impl Count for EquationElem {
}
impl LocalName for EquationElem {
fn local_name(&self, lang: Lang) -> &'static str {
fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str {
match lang {
Lang::ARABIC => "معادلة",
Lang::BOKMÅL => "Ligning",
@ -305,10 +305,11 @@ impl Refable for EquationElem {
vt: &mut Vt,
supplement: Option<Content>,
lang: Lang,
region: Option<Region>,
) -> SourceResult<Content> {
// first we create the supplement of the heading
let mut supplement =
supplement.unwrap_or_else(|| TextElem::packed(self.local_name(lang)));
supplement.unwrap_or_else(|| TextElem::packed(self.local_name(lang, region)));
// we append a space if the supplement is not empty
if !supplement.is_empty() {

View File

@ -152,10 +152,14 @@ impl Show for BibliographyElem {
let mut seq = vec![];
if let Some(title) = self.title(styles) {
let title = title.unwrap_or_else(|| {
TextElem::packed(self.local_name(TextElem::lang_in(styles)))
let title =
title.unwrap_or_else(|| {
TextElem::packed(self.local_name(
TextElem::lang_in(styles),
TextElem::region_in(styles),
))
.spanned(self.span())
});
});
seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack());
}
@ -206,7 +210,7 @@ impl Finalize for BibliographyElem {
}
impl LocalName for BibliographyElem {
fn local_name(&self, lang: Lang) -> &'static str {
fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str {
match lang {
Lang::ARABIC => "المراجع",
Lang::BOKMÅL => "Bibliografi",

View File

@ -193,7 +193,10 @@ impl Synthesize for FigureElem {
Smart::Auto => match &kind {
FigureKind::Elem(func) => {
let elem = Content::new(*func).with::<dyn LocalName>().map(|c| {
TextElem::packed(c.local_name(TextElem::lang_in(styles)))
TextElem::packed(c.local_name(
TextElem::lang_in(styles),
TextElem::region_in(styles),
))
});
if numbering.is_some() {
@ -273,6 +276,7 @@ impl Refable for FigureElem {
vt: &mut Vt,
supplement: Option<Content>,
_: Lang,
_: Option<Region>,
) -> SourceResult<Content> {
// If the figure is not numbered, we cannot reference it.
// Otherwise we build the supplement and numbering scheme.
@ -283,7 +287,12 @@ impl Refable for FigureElem {
Ok(desc)
}
fn outline(&self, vt: &mut Vt, _: Lang) -> SourceResult<Option<Content>> {
fn outline(
&self,
vt: &mut Vt,
_: Lang,
_: Option<Region>,
) -> SourceResult<Option<Content>> {
// If the figure is not outlined, it is not referenced.
if !self.outlined(StyleChain::default()) {
return Ok(None);

View File

@ -164,13 +164,14 @@ impl Refable for HeadingElem {
vt: &mut Vt,
supplement: Option<Content>,
lang: Lang,
region: Option<Region>,
) -> SourceResult<Content> {
// Create the supplement of the heading.
let mut supplement = if let Some(supplement) = supplement {
supplement
} else {
match self.supplement(StyleChain::default()) {
Smart::Auto => TextElem::packed(self.local_name(lang)),
Smart::Auto => TextElem::packed(self.local_name(lang, region)),
Smart::Custom(None) => Content::empty(),
Smart::Custom(Some(supplement)) => {
supplement.resolve(vt, std::iter::once(Value::from(self.clone())))?
@ -208,7 +209,12 @@ impl Refable for HeadingElem {
Counter::of(Self::func())
}
fn outline(&self, vt: &mut Vt, _: Lang) -> SourceResult<Option<Content>> {
fn outline(
&self,
vt: &mut Vt,
_: Lang,
_: Option<Region>,
) -> SourceResult<Option<Content>> {
// Check whether the heading is outlined.
if !self.outlined(StyleChain::default()) {
return Ok(None);
@ -228,7 +234,7 @@ impl Refable for HeadingElem {
}
impl LocalName for HeadingElem {
fn local_name(&self, lang: Lang) -> &'static str {
fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str {
match lang {
Lang::ARABIC => "الفصل",
Lang::BOKMÅL => "Kapittel",

View File

@ -27,9 +27,10 @@ pub use self::reference::*;
pub use self::state::*;
use typst::doc::Lang;
use typst::doc::Region;
/// The named with which an element is referenced.
pub trait LocalName {
/// Get the name in the given language.
fn local_name(&self, lang: Lang) -> &'static str;
/// Get the name in the given language and (optionally) region.
fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str;
}

View File

@ -1,5 +1,7 @@
use std::str::FromStr;
use typst::util::option_eq;
use super::{
Counter, CounterKey, HeadingElem, LocalName, Numbering, NumberingPattern, Refable,
};
@ -141,10 +143,14 @@ impl Show for OutlineElem {
let mut seq = vec![ParbreakElem::new().pack()];
// Build the outline title.
if let Some(title) = self.title(styles) {
let title = title.unwrap_or_else(|| {
TextElem::packed(self.local_name(TextElem::lang_in(styles)))
let title =
title.unwrap_or_else(|| {
TextElem::packed(self.local_name(
TextElem::lang_in(styles),
TextElem::region_in(styles),
))
.spanned(self.span())
});
});
seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack());
}
@ -152,6 +158,7 @@ impl Show for OutlineElem {
let indent = self.indent(styles);
let depth = self.depth(styles).map_or(usize::MAX, NonZeroUsize::get);
let lang = TextElem::lang_in(styles);
let region = TextElem::region_in(styles);
let mut ancestors: Vec<&Content> = vec![];
let elems = vt.introspector.query(&self.target(styles));
@ -165,7 +172,7 @@ impl Show for OutlineElem {
continue;
}
let Some(outline) = refable.outline(vt, lang)? else {
let Some(outline) = refable.outline(vt, lang, region)? else {
continue;
};
@ -255,10 +262,11 @@ impl Finalize for OutlineElem {
}
impl LocalName for OutlineElem {
fn local_name(&self, lang: Lang) -> &'static str {
fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str {
match lang {
Lang::ARABIC => "المحتويات",
Lang::BOKMÅL => "Innhold",
Lang::CHINESE if option_eq(region, "TW") => "目錄",
Lang::CHINESE => "目录",
Lang::CZECH => "Obsah",
Lang::FRENCH => "Table des matières",

View File

@ -184,10 +184,11 @@ impl Show for RefElem {
};
let lang = TextElem::lang_in(styles);
let region = TextElem::region_in(styles);
let reference = elem
.with::<dyn Refable>()
.expect("element should be refable")
.reference(vt, supplement, lang)?;
.reference(vt, supplement, lang, region)?;
Ok(reference.linked(Destination::Location(elem.location().unwrap())))
}
@ -259,21 +260,27 @@ pub trait Refable {
///
/// # Arguments
/// - `vt` - The virtual typesetter.
/// - `styles` - The styles of the reference.
/// - `location` - The location where the reference is being created.
/// - `supplement` - The supplement of the reference.
/// - `lang`: The language of the reference.
/// - `region`: The region of the reference.
fn reference(
&self,
vt: &mut Vt,
supplement: Option<Content>,
lang: Lang,
region: Option<Region>,
) -> SourceResult<Content>;
/// Tries to build an outline element for this element.
/// If this returns `None`, the outline will not include this element.
/// By default this just calls [`Refable::reference`].
fn outline(&self, vt: &mut Vt, lang: Lang) -> SourceResult<Option<Content>> {
self.reference(vt, None, lang).map(Some)
fn outline(
&self,
vt: &mut Vt,
lang: Lang,
region: Option<Region>,
) -> SourceResult<Option<Content>> {
self.reference(vt, None, lang, region).map(Some)
}
/// Returns the level of this element.

View File

@ -201,7 +201,7 @@ impl Finalize for RawElem {
}
impl LocalName for RawElem {
fn local_name(&self, lang: Lang) -> &'static str {
fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str {
match lang {
Lang::ARABIC => "قائمة",
Lang::BOKMÅL => "Utskrift",

View File

@ -122,7 +122,7 @@ impl Layout for ImageElem {
}
impl LocalName for ImageElem {
fn local_name(&self, lang: Lang) -> &'static str {
fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str {
match lang {
Lang::ARABIC => "شكل",
Lang::BOKMÅL => "Figur",

View File

@ -591,6 +591,12 @@ impl Region {
}
}
impl PartialEq<&str> for Region {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl FromStr for Region {
type Err = &'static str;
@ -688,3 +694,23 @@ cast_to_value! {
"y" => Value::Length(v.point.y.into()),
})
}
#[cfg(test)]
mod tests {
use crate::{doc::Region, util::option_eq};
#[test]
fn test_partialeq_str() {
let region = Region([b'U', b'S']);
assert_eq!(region, "US");
assert_ne!(region, "AB");
}
#[test]
fn test_region_option_eq() {
let region = Some(Region([b'U', b'S']));
assert!(option_eq(region, "US"));
assert!(!option_eq(region, "AB"));
}
}

View File

@ -226,3 +226,13 @@ pub fn pretty_array_like(parts: &[impl AsRef<str>], trailing_comma: bool) -> Str
buf.push(')');
buf
}
/// Check if the [`Option`]-wrapped L is same to R.
///
/// This is the stable version of [`Option::contains`].
pub fn option_eq<L, R>(left: Option<L>, other: R) -> bool
where
L: PartialEq<R>,
{
left.map(|v| v == other).unwrap_or(false)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,16 @@
// Test if text with region works
---
// without any region
#set text(lang: "zh")
#outline()
---
// with unknown region configured
#set text(lang: "zh", region: "XX")
#outline()
---
// with region configured
#set text(lang: "zh", region: "TW")
#outline()