Better quote selection (#3422)

This commit is contained in:
Laurenz 2024-02-15 10:41:27 +01:00 committed by GitHub
parent 79e37ccbac
commit aabb4b5ecf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 75 additions and 28 deletions

View File

@ -745,3 +745,13 @@ impl<T, const N: usize> Fold for SmallVec<[T; N]> {
self
}
}
/// A type that accumulates depth when folded.
#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
pub struct Depth(pub usize);
impl Fold for Depth {
fn fold(self, outer: Self) -> Self {
Self(outer.0 + self.0)
}
}

View File

@ -464,15 +464,12 @@ fn collect<'a>(
Segment::Text(c.len_utf8())
} else if let Some(elem) = child.to_packed::<SmartQuoteElem>() {
let prev = full.len();
if SmartQuoteElem::enabled_in(styles) {
let quotes = SmartQuoteElem::quotes_in(styles);
let lang = TextElem::lang_in(styles);
let region = TextElem::region_in(styles);
if elem.enabled(styles) {
let quotes = SmartQuotes::new(
quotes,
lang,
region,
SmartQuoteElem::alternative_in(styles),
elem.quotes(styles),
TextElem::lang_in(styles),
TextElem::region_in(styles),
elem.alternative(styles),
);
let peeked = iter.peek().and_then(|&child| {
let child = if let Some(styled) = child.to_packed::<StyledElem>() {

View File

@ -1,7 +1,7 @@
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Array, Content, Fold, Func, Packed, Smart, StyleChain, Value,
cast, elem, scope, Array, Content, Depth, Func, Packed, Smart, StyleChain, Value,
};
use crate::layout::{
Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
@ -236,12 +236,3 @@ cast! {
},
v: Func => Self::Func(v),
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
struct Depth(usize);
impl Fold for Depth {
fn fold(self, outer: Self) -> Self {
Self(outer.0 + self.0)
}
}

View File

@ -1,12 +1,12 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Content, Label, NativeElement, Packed, Show, ShowSet, Smart, StyleChain,
Styles,
cast, elem, Content, Depth, Label, NativeElement, Packed, Show, ShowSet, Smart,
StyleChain, Styles,
};
use crate::layout::{Alignment, BlockElem, Em, HElem, PadElem, Spacing, VElem};
use crate::model::{CitationForm, CiteElem};
use crate::text::{SmartQuoteElem, SpaceElem, TextElem};
use crate::text::{SmartQuoteElem, SmartQuotes, SpaceElem, TextElem};
/// Displays a quote alongside an optional attribution.
///
@ -126,6 +126,12 @@ pub struct QuoteElem {
/// The quote.
#[required]
body: Content,
/// The nesting depth.
#[internal]
#[fold]
#[ghost]
depth: Depth,
}
/// Attribution for a [quote](QuoteElem).
@ -152,11 +158,27 @@ impl Show for Packed<QuoteElem> {
let block = self.block(styles);
if self.quotes(styles) == Smart::Custom(true) || !block {
let quotes = SmartQuotes::new(
SmartQuoteElem::quotes_in(styles),
TextElem::lang_in(styles),
TextElem::region_in(styles),
SmartQuoteElem::alternative_in(styles),
);
// Alternate between single and double quotes.
let Depth(depth) = QuoteElem::depth_in(styles);
let double = depth % 2 == 0;
// Add zero-width weak spacing to make the quotes "sticky".
let hole = HElem::hole().pack();
let quote = SmartQuoteElem::new().with_double(true).pack();
realized =
Content::sequence([quote.clone(), hole.clone(), realized, hole, quote]);
realized = Content::sequence([
TextElem::packed(quotes.open(double)),
hole.clone(),
realized,
hole,
TextElem::packed(quotes.close(double)),
])
.styled(QuoteElem::set_depth(Depth(1)));
}
if block {

View File

@ -251,7 +251,7 @@ impl<'s> SmartQuotes<'s> {
}
/// The opening quote.
fn open(&self, double: bool) -> &'s str {
pub fn open(&self, double: bool) -> &'s str {
if double {
self.double_open
} else {
@ -260,7 +260,7 @@ impl<'s> SmartQuotes<'s> {
}
/// The closing quote.
fn close(&self, double: bool) -> &'s str {
pub fn close(&self, double: bool) -> &'s str {
if double {
self.double_close
} else {
@ -269,7 +269,7 @@ impl<'s> SmartQuotes<'s> {
}
/// Which character should be used as a prime.
fn prime(&self, double: bool) -> &'static str {
pub fn prime(&self, double: bool) -> &'static str {
if double {
""
} else {
@ -278,7 +278,7 @@ impl<'s> SmartQuotes<'s> {
}
/// Which character should be used as a fallback quote.
fn fallback(&self, double: bool) -> &'static str {
pub fn fallback(&self, double: bool) -> &'static str {
if double {
"\""
} else {

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,27 @@
// Test quote nesting.
---
// Test quote selection.
#set page(width: auto)
#set text(lang: "en")
=== EN
#quote[An apostroph'] \
#quote[A #quote[nested] quote] \
#quote[A #quote[very #quote[nested]] quote]
#set text(lang: "de")
=== DE
#quote[Satz mit Apostroph'] \
#quote[Satz mit #quote[Zitat]] \
#quote[A #quote[very #quote[nested]] quote]
#set smartquote(alternative: true)
=== DE Alternative
#quote[Satz mit Apostroph'] \
#quote[Satz mit #quote[Zitat]] \
#quote[A #quote[very #quote[nested]] quote]
---
// With custom quotes.
#set smartquote(quotes: (single: ("<", ">"), double: ("(", ")")))
#quote[A #quote[nested] quote]