mirror of
https://github.com/typst/typst
synced 2025-05-15 01:25:28 +08:00
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
|
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())
|
Segment::Text(c.len_utf8())
|
||||||
} else if let Some(elem) = child.to_packed::<SmartQuoteElem>() {
|
} else if let Some(elem) = child.to_packed::<SmartQuoteElem>() {
|
||||||
let prev = full.len();
|
let prev = full.len();
|
||||||
if SmartQuoteElem::enabled_in(styles) {
|
if elem.enabled(styles) {
|
||||||
let quotes = SmartQuoteElem::quotes_in(styles);
|
|
||||||
let lang = TextElem::lang_in(styles);
|
|
||||||
let region = TextElem::region_in(styles);
|
|
||||||
let quotes = SmartQuotes::new(
|
let quotes = SmartQuotes::new(
|
||||||
quotes,
|
elem.quotes(styles),
|
||||||
lang,
|
TextElem::lang_in(styles),
|
||||||
region,
|
TextElem::region_in(styles),
|
||||||
SmartQuoteElem::alternative_in(styles),
|
elem.alternative(styles),
|
||||||
);
|
);
|
||||||
let peeked = iter.peek().and_then(|&child| {
|
let peeked = iter.peek().and_then(|&child| {
|
||||||
let child = if let Some(styled) = child.to_packed::<StyledElem>() {
|
let child = if let Some(styled) = child.to_packed::<StyledElem>() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
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::{
|
use crate::layout::{
|
||||||
Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
|
Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
|
||||||
@ -236,12 +236,3 @@ cast! {
|
|||||||
},
|
},
|
||||||
v: Func => Self::Func(v),
|
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::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, Content, Label, NativeElement, Packed, Show, ShowSet, Smart, StyleChain,
|
cast, elem, Content, Depth, Label, NativeElement, Packed, Show, ShowSet, Smart,
|
||||||
Styles,
|
StyleChain, Styles,
|
||||||
};
|
};
|
||||||
use crate::layout::{Alignment, BlockElem, Em, HElem, PadElem, Spacing, VElem};
|
use crate::layout::{Alignment, BlockElem, Em, HElem, PadElem, Spacing, VElem};
|
||||||
use crate::model::{CitationForm, CiteElem};
|
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.
|
/// Displays a quote alongside an optional attribution.
|
||||||
///
|
///
|
||||||
@ -126,6 +126,12 @@ pub struct QuoteElem {
|
|||||||
/// The quote.
|
/// The quote.
|
||||||
#[required]
|
#[required]
|
||||||
body: Content,
|
body: Content,
|
||||||
|
|
||||||
|
/// The nesting depth.
|
||||||
|
#[internal]
|
||||||
|
#[fold]
|
||||||
|
#[ghost]
|
||||||
|
depth: Depth,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attribution for a [quote](QuoteElem).
|
/// Attribution for a [quote](QuoteElem).
|
||||||
@ -152,11 +158,27 @@ impl Show for Packed<QuoteElem> {
|
|||||||
let block = self.block(styles);
|
let block = self.block(styles);
|
||||||
|
|
||||||
if self.quotes(styles) == Smart::Custom(true) || !block {
|
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".
|
// Add zero-width weak spacing to make the quotes "sticky".
|
||||||
let hole = HElem::hole().pack();
|
let hole = HElem::hole().pack();
|
||||||
let quote = SmartQuoteElem::new().with_double(true).pack();
|
realized = Content::sequence([
|
||||||
realized =
|
TextElem::packed(quotes.open(double)),
|
||||||
Content::sequence([quote.clone(), hole.clone(), realized, hole, quote]);
|
hole.clone(),
|
||||||
|
realized,
|
||||||
|
hole,
|
||||||
|
TextElem::packed(quotes.close(double)),
|
||||||
|
])
|
||||||
|
.styled(QuoteElem::set_depth(Depth(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if block {
|
if block {
|
||||||
|
@ -251,7 +251,7 @@ impl<'s> SmartQuotes<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The opening quote.
|
/// The opening quote.
|
||||||
fn open(&self, double: bool) -> &'s str {
|
pub fn open(&self, double: bool) -> &'s str {
|
||||||
if double {
|
if double {
|
||||||
self.double_open
|
self.double_open
|
||||||
} else {
|
} else {
|
||||||
@ -260,7 +260,7 @@ impl<'s> SmartQuotes<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The closing quote.
|
/// The closing quote.
|
||||||
fn close(&self, double: bool) -> &'s str {
|
pub fn close(&self, double: bool) -> &'s str {
|
||||||
if double {
|
if double {
|
||||||
self.double_close
|
self.double_close
|
||||||
} else {
|
} else {
|
||||||
@ -269,7 +269,7 @@ impl<'s> SmartQuotes<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Which character should be used as a prime.
|
/// 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 {
|
if double {
|
||||||
"″"
|
"″"
|
||||||
} else {
|
} else {
|
||||||
@ -278,7 +278,7 @@ impl<'s> SmartQuotes<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Which character should be used as a fallback quote.
|
/// 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 {
|
if double {
|
||||||
"\""
|
"\""
|
||||||
} else {
|
} 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