List items are only paragraphs in wide lists
@ -6,7 +6,7 @@ use typst_library::foundations::{Content, Context, Depth, Packed, StyleChain};
|
||||
use typst_library::introspection::Locator;
|
||||
use typst_library::layout::grid::resolve::{Cell, CellGrid};
|
||||
use typst_library::layout::{Axes, Fragment, HAlignment, Regions, Sizing, VAlignment};
|
||||
use typst_library::model::{EnumElem, ListElem, Numbering, ParElem};
|
||||
use typst_library::model::{EnumElem, ListElem, Numbering, ParElem, ParbreakElem};
|
||||
use typst_library::text::TextElem;
|
||||
|
||||
use crate::grid::GridLayouter;
|
||||
@ -22,8 +22,9 @@ pub fn layout_list(
|
||||
) -> SourceResult<Fragment> {
|
||||
let indent = elem.indent(styles);
|
||||
let body_indent = elem.body_indent(styles);
|
||||
let tight = elem.tight(styles);
|
||||
let gutter = elem.spacing(styles).unwrap_or_else(|| {
|
||||
if elem.tight(styles) {
|
||||
if tight {
|
||||
ParElem::leading_in(styles).into()
|
||||
} else {
|
||||
ParElem::spacing_in(styles).into()
|
||||
@ -41,11 +42,17 @@ pub fn layout_list(
|
||||
let mut locator = locator.split();
|
||||
|
||||
for item in &elem.children {
|
||||
// Text in wide lists shall always turn into paragraphs.
|
||||
let mut body = item.body.clone();
|
||||
if !tight {
|
||||
body += ParbreakElem::shared();
|
||||
}
|
||||
|
||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||
cells.push(Cell::new(marker.clone(), locator.next(&marker.span())));
|
||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||
cells.push(Cell::new(
|
||||
item.body.clone().styled(ListElem::set_depth(Depth(1))),
|
||||
body.styled(ListElem::set_depth(Depth(1))),
|
||||
locator.next(&item.body.span()),
|
||||
));
|
||||
}
|
||||
@ -78,8 +85,9 @@ pub fn layout_enum(
|
||||
let reversed = elem.reversed(styles);
|
||||
let indent = elem.indent(styles);
|
||||
let body_indent = elem.body_indent(styles);
|
||||
let tight = elem.tight(styles);
|
||||
let gutter = elem.spacing(styles).unwrap_or_else(|| {
|
||||
if elem.tight(styles) {
|
||||
if tight {
|
||||
ParElem::leading_in(styles).into()
|
||||
} else {
|
||||
ParElem::spacing_in(styles).into()
|
||||
@ -124,11 +132,17 @@ pub fn layout_enum(
|
||||
let resolved =
|
||||
resolved.aligned(number_align).styled(TextElem::set_overhang(false));
|
||||
|
||||
// Text in wide enums shall always turn into paragraphs.
|
||||
let mut body = item.body.clone();
|
||||
if !tight {
|
||||
body += ParbreakElem::shared();
|
||||
}
|
||||
|
||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||
cells.push(Cell::new(resolved, locator.next(&())));
|
||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||
cells.push(Cell::new(
|
||||
item.body.clone().styled(EnumElem::set_parents(smallvec![number])),
|
||||
body.styled(EnumElem::set_parents(smallvec![number])),
|
||||
locator.next(&item.body.span()),
|
||||
));
|
||||
number =
|
||||
|
@ -11,7 +11,9 @@ use crate::foundations::{
|
||||
};
|
||||
use crate::html::{attr, tag, HtmlElem};
|
||||
use crate::layout::{Alignment, BlockElem, Em, HAlignment, Length, VAlignment, VElem};
|
||||
use crate::model::{ListItemLike, ListLike, Numbering, NumberingPattern, ParElem};
|
||||
use crate::model::{
|
||||
ListItemLike, ListLike, Numbering, NumberingPattern, ParElem, ParbreakElem,
|
||||
};
|
||||
|
||||
/// A numbered list.
|
||||
///
|
||||
@ -226,6 +228,8 @@ impl EnumElem {
|
||||
|
||||
impl Show for Packed<EnumElem> {
|
||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||
let tight = self.tight(styles);
|
||||
|
||||
if TargetElem::target_in(styles).is_html() {
|
||||
let mut elem = HtmlElem::new(tag::ol);
|
||||
if self.reversed(styles) {
|
||||
@ -239,7 +243,12 @@ impl Show for Packed<EnumElem> {
|
||||
if let Some(nr) = item.number(styles) {
|
||||
li = li.with_attr(attr::value, eco_format!("{nr}"));
|
||||
}
|
||||
li.with_body(Some(item.body.clone())).pack().spanned(item.span())
|
||||
// Text in wide enums shall always turn into paragraphs.
|
||||
let mut body = item.body.clone();
|
||||
if !tight {
|
||||
body += ParbreakElem::shared();
|
||||
}
|
||||
li.with_body(Some(body)).pack().spanned(item.span())
|
||||
}));
|
||||
return Ok(elem.with_body(Some(body)).pack().spanned(self.span()));
|
||||
}
|
||||
@ -249,7 +258,7 @@ impl Show for Packed<EnumElem> {
|
||||
.pack()
|
||||
.spanned(self.span());
|
||||
|
||||
if self.tight(styles) {
|
||||
if tight {
|
||||
let leading = ParElem::leading_in(styles);
|
||||
let spacing =
|
||||
VElem::new(leading.into()).with_weak(true).with_attach(true).pack();
|
||||
|
@ -8,7 +8,7 @@ use crate::foundations::{
|
||||
};
|
||||
use crate::html::{tag, HtmlElem};
|
||||
use crate::layout::{BlockElem, Em, Length, VElem};
|
||||
use crate::model::ParElem;
|
||||
use crate::model::{ParElem, ParbreakElem};
|
||||
use crate::text::TextElem;
|
||||
|
||||
/// A bullet list.
|
||||
@ -141,11 +141,18 @@ impl ListElem {
|
||||
|
||||
impl Show for Packed<ListElem> {
|
||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||
let tight = self.tight(styles);
|
||||
|
||||
if TargetElem::target_in(styles).is_html() {
|
||||
return Ok(HtmlElem::new(tag::ul)
|
||||
.with_body(Some(Content::sequence(self.children.iter().map(|item| {
|
||||
// Text in wide lists shall always turn into paragraphs.
|
||||
let mut body = item.body.clone();
|
||||
if !tight {
|
||||
body += ParbreakElem::shared();
|
||||
}
|
||||
HtmlElem::new(tag::li)
|
||||
.with_body(Some(item.body.clone()))
|
||||
.with_body(Some(body))
|
||||
.pack()
|
||||
.spanned(item.span())
|
||||
}))))
|
||||
@ -158,7 +165,7 @@ impl Show for Packed<ListElem> {
|
||||
.pack()
|
||||
.spanned(self.span());
|
||||
|
||||
if self.tight(styles) {
|
||||
if tight {
|
||||
let leading = ParElem::leading_in(styles);
|
||||
let spacing =
|
||||
VElem::new(leading.into()).with_weak(true).with_attach(true).pack();
|
||||
|
@ -8,7 +8,7 @@ use crate::foundations::{
|
||||
};
|
||||
use crate::html::{tag, HtmlElem};
|
||||
use crate::layout::{Em, HElem, Length, Sides, StackChild, StackElem, VElem};
|
||||
use crate::model::{ListItemLike, ListLike, ParElem};
|
||||
use crate::model::{ListItemLike, ListLike, ParElem, ParbreakElem};
|
||||
use crate::text::TextElem;
|
||||
|
||||
/// A list of terms and their descriptions.
|
||||
@ -116,17 +116,25 @@ impl TermsElem {
|
||||
impl Show for Packed<TermsElem> {
|
||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||
let span = self.span();
|
||||
let tight = self.tight(styles);
|
||||
|
||||
if TargetElem::target_in(styles).is_html() {
|
||||
return Ok(HtmlElem::new(tag::dl)
|
||||
.with_body(Some(Content::sequence(self.children.iter().flat_map(
|
||||
|item| {
|
||||
// Text in wide term lists shall always turn into paragraphs.
|
||||
let mut description = item.description.clone();
|
||||
if !tight {
|
||||
description += ParbreakElem::shared();
|
||||
}
|
||||
|
||||
[
|
||||
HtmlElem::new(tag::dt)
|
||||
.with_body(Some(item.term.clone()))
|
||||
.pack()
|
||||
.spanned(item.term.span()),
|
||||
HtmlElem::new(tag::dd)
|
||||
.with_body(Some(item.description.clone()))
|
||||
.with_body(Some(description))
|
||||
.pack()
|
||||
.spanned(item.description.span()),
|
||||
]
|
||||
@ -139,7 +147,7 @@ impl Show for Packed<TermsElem> {
|
||||
let indent = self.indent(styles);
|
||||
let hanging_indent = self.hanging_indent(styles);
|
||||
let gutter = self.spacing(styles).unwrap_or_else(|| {
|
||||
if self.tight(styles) {
|
||||
if tight {
|
||||
ParElem::leading_in(styles).into()
|
||||
} else {
|
||||
ParElem::spacing_in(styles).into()
|
||||
@ -157,6 +165,12 @@ impl Show for Packed<TermsElem> {
|
||||
seq.push(child.term.clone().strong());
|
||||
seq.push((*separator).clone());
|
||||
seq.push(child.description.clone());
|
||||
|
||||
// Text in wide term lists shall always turn into paragraphs.
|
||||
if !tight {
|
||||
seq.push(ParbreakElem::shared().clone());
|
||||
}
|
||||
|
||||
children.push(StackChild::Block(Content::sequence(seq)));
|
||||
}
|
||||
|
||||
@ -168,7 +182,7 @@ impl Show for Packed<TermsElem> {
|
||||
.spanned(span)
|
||||
.padded(padding);
|
||||
|
||||
if self.tight(styles) {
|
||||
if tight {
|
||||
let leading = ParElem::leading_in(styles);
|
||||
let spacing = VElem::new(leading.into())
|
||||
.with_weak(true)
|
||||
|
BIN
tests/ref/enum-par.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
36
tests/ref/html/enum-par.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<ol>
|
||||
<li>Hello</li>
|
||||
<li>World</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Hello</p>
|
||||
<p>From</p>
|
||||
</li>
|
||||
<li>World</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Hello</p>
|
||||
<p>From</p>
|
||||
<p>The</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>World</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
36
tests/ref/html/list-par.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<ul>
|
||||
<li>Hello</li>
|
||||
<li>World</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Hello</p>
|
||||
<p>From</p>
|
||||
</li>
|
||||
<li>World</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Hello</p>
|
||||
<p>From</p>
|
||||
<p>The</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>World</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
42
tests/ref/html/terms-par.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<dl>
|
||||
<dt>Hello</dt>
|
||||
<dd>A</dd>
|
||||
<dt>World</dt>
|
||||
<dd>B</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
<dl>
|
||||
<dt>Hello</dt>
|
||||
<dd>
|
||||
<p>A</p>
|
||||
<p>From</p>
|
||||
</dd>
|
||||
<dt>World</dt>
|
||||
<dd>B</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
<dl>
|
||||
<dt>Hello</dt>
|
||||
<dd>
|
||||
<p>A</p>
|
||||
<p>From</p>
|
||||
<p>The</p>
|
||||
</dd>
|
||||
<dt>World</dt>
|
||||
<dd>
|
||||
<p>B</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
tests/ref/issue-5503-enum-in-align.png
Normal file
After Width: | Height: | Size: 421 B |
Before Width: | Height: | Size: 1004 B |
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 415 B |
Before Width: | Height: | Size: 569 B After Width: | Height: | Size: 569 B |
BIN
tests/ref/list-par.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
tests/ref/terms-par.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
@ -183,22 +183,44 @@ a + 0.
|
||||
#set enum(number-align: horizon)
|
||||
#set enum(number-align: bottom)
|
||||
|
||||
--- enum-par render html ---
|
||||
// Check whether the contents of enum items become paragraphs.
|
||||
#show par: it => if target() != "html" { highlight(it) } else { it }
|
||||
|
||||
// No paragraphs.
|
||||
#block[
|
||||
+ Hello
|
||||
+ World
|
||||
]
|
||||
|
||||
#block[
|
||||
+ Hello // Paragraphs
|
||||
|
||||
From
|
||||
+ World // No paragraph because it's a tight enum
|
||||
]
|
||||
|
||||
#block[
|
||||
+ Hello // Paragraphs
|
||||
|
||||
From
|
||||
|
||||
The
|
||||
|
||||
+ World // Paragraph because it's a wide enum
|
||||
]
|
||||
|
||||
--- issue-2530-enum-item-panic ---
|
||||
// Enum item (pre-emptive)
|
||||
#enum.item(none)[Hello]
|
||||
#enum.item(17)[Hello]
|
||||
|
||||
--- issue-5503-enum-interrupted-by-par-align ---
|
||||
// `align` is block-level and should interrupt an enum
|
||||
// but not a `par`
|
||||
--- issue-5503-enum-in-align ---
|
||||
// `align` is block-level and should interrupt an enum.
|
||||
+ a
|
||||
+ b
|
||||
#par(leading: 5em)[+ par]
|
||||
#align(right)[+ c]
|
||||
+ d
|
||||
#par[+ par]
|
||||
+ f
|
||||
#align(right)[+ align]
|
||||
+ h
|
||||
|
||||
--- issue-5719-enum-nested ---
|
||||
// Enums can be immediately nested.
|
||||
|
@ -238,6 +238,33 @@ World
|
||||
#text(red)[- World]
|
||||
#text(green)[- What up?]
|
||||
|
||||
--- list-par render html ---
|
||||
// Check whether the contents of list items become paragraphs.
|
||||
#show par: it => if target() != "html" { highlight(it) } else { it }
|
||||
|
||||
#block[
|
||||
// No paragraphs.
|
||||
- Hello
|
||||
- World
|
||||
]
|
||||
|
||||
#block[
|
||||
- Hello // Paragraphs
|
||||
|
||||
From
|
||||
- World // No paragraph because it's a tight list.
|
||||
]
|
||||
|
||||
#block[
|
||||
- Hello // Paragraphs either way
|
||||
|
||||
From
|
||||
|
||||
The
|
||||
|
||||
- World // Paragraph because it's a wide list.
|
||||
]
|
||||
|
||||
--- issue-2530-list-item-panic ---
|
||||
// List item (pre-emptive)
|
||||
#list.item[Hello]
|
||||
@ -262,18 +289,11 @@ World
|
||||
part($ x $ + parbreak() + parbreak() + list[A])
|
||||
}
|
||||
|
||||
--- issue-5503-list-interrupted-by-par-align ---
|
||||
// `align` is block-level and should interrupt a list
|
||||
// but not a `par`
|
||||
--- issue-5503-list-in-align ---
|
||||
// `align` is block-level and should interrupt a list.
|
||||
#show list: [List]
|
||||
- a
|
||||
- b
|
||||
#par(leading: 5em)[- c]
|
||||
- d
|
||||
- e
|
||||
#par[- f]
|
||||
- g
|
||||
- h
|
||||
#align(right)[- i]
|
||||
- j
|
||||
|
||||
|
@ -59,6 +59,34 @@ Not in list
|
||||
// Error: 8 expected colon
|
||||
/ Hello
|
||||
|
||||
--- terms-par render html ---
|
||||
// Check whether the contents of term list items become paragraphs.
|
||||
#show par: it => if target() != "html" { highlight(it) } else { it }
|
||||
|
||||
// No paragraphs.
|
||||
#block[
|
||||
/ Hello: A
|
||||
/ World: B
|
||||
]
|
||||
|
||||
#block[
|
||||
/ Hello: A // Paragraphs
|
||||
|
||||
From
|
||||
/ World: B // No paragraphs because it's a tight term list.
|
||||
]
|
||||
|
||||
#block[
|
||||
/ Hello: A // Paragraphs
|
||||
|
||||
From
|
||||
|
||||
The
|
||||
|
||||
/ World: B // Paragraph because it's a wide term list.
|
||||
]
|
||||
|
||||
|
||||
--- issue-1050-terms-indent ---
|
||||
#set page(width: 110pt)
|
||||
#set par(first-line-indent: 0.5cm)
|
||||
@ -76,18 +104,10 @@ Not in list
|
||||
// Term item (pre-emptive)
|
||||
#terms.item[Hello][World!]
|
||||
|
||||
--- issue-5503-terms-interrupted-by-par-align ---
|
||||
// `align` is block-level and should interrupt a `terms`
|
||||
// but not a `par`
|
||||
--- issue-5503-terms-in-align ---
|
||||
// `align` is block-level and should interrupt a `terms`.
|
||||
#show terms: [Terms]
|
||||
/ a: a
|
||||
/ b: b
|
||||
#par(leading: 5em)[/ c: c]
|
||||
/ d: d
|
||||
/ e: e
|
||||
#par[/ f: f]
|
||||
/ g: g
|
||||
/ h: h
|
||||
#align(right)[/ i: i]
|
||||
/ j: j
|
||||
|
||||
|