Better quote selection (#3422)
This commit is contained in:
parent
79e37ccbac
commit
aabb4b5ecf
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>() {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
BIN
tests/ref/text/quote-nesting.png
Normal file
BIN
tests/ref/text/quote-nesting.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
27
tests/typ/text/quote-nesting.typ
Normal file
27
tests/typ/text/quote-nesting.typ
Normal 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]
|
Loading…
x
Reference in New Issue
Block a user