mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-25 10:46:50 -06:00
Lists: an empty paragraph gets no bullet or number
A list paragraph with no characters before its return draws no label, and numbering carries on past it: in a story's own list, in named lists (also across stories) and in cross-reference paragraph numbers.
This commit is contained in:
@@ -492,9 +492,10 @@ fn compose_with_db(doc: &Document, story: &Story, frames: &[FrameSpec], opts: &C
|
||||
story
|
||||
.paras
|
||||
.iter()
|
||||
.map(|p| {
|
||||
.zip(¶_ranges)
|
||||
.map(|(p, r)| {
|
||||
let (pp, _) = doc.styles.resolve_para(p);
|
||||
if pp.list_type != designcraft_doc::ListType::Numbers || pp.list_name.is_empty() {
|
||||
if pp.list_type != designcraft_doc::ListType::Numbers || pp.list_name.is_empty() || r.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let c = counters.entry(pp.list_name.clone()).or_insert_with(|| doc.list_start(story.id, &pp.list_name));
|
||||
@@ -701,6 +702,8 @@ fn compose_with_db(doc: &Document, story: &Story, frames: &[FrameSpec], opts: &C
|
||||
// List labels take the default super/subscript settings.
|
||||
let label_env = shape::TypeEnv { adv: Default::default(), ..env };
|
||||
match pp.list_type {
|
||||
// An empty paragraph has no bullet or number, and the numbering carries on past it.
|
||||
designcraft_doc::ListType::Numbers | designcraft_doc::ListType::Bullets if prange.is_empty() => {}
|
||||
designcraft_doc::ListType::Numbers if !pp.list_name.is_empty() => {
|
||||
// A named list: carries on past other paragraphs (and from earlier stories).
|
||||
let n = named_numbers.get(pi).copied().flatten().unwrap_or(1);
|
||||
|
||||
@@ -2262,3 +2262,21 @@ fn numbering_from_the_largest_start_number_does_not_overflow() {
|
||||
let max = format!("{}.", u32::MAX);
|
||||
assert_eq!(list_labels(&cs, LABEL_CHARS), [max.clone(), max]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_paragraphs_get_no_bullet_or_number() {
|
||||
let bullets = ParaAttrs { list_type: Some(designcraft_doc::ListType::Bullets), ..Default::default() };
|
||||
let (d, sid, _) = doc_with("One\n\nTwo\n", Rect::new(0.0, 0.0, 300.0, 300.0), bullets);
|
||||
let cs = compose_story(&d, sid, &ComposeOptions::default());
|
||||
assert_eq!(list_labels(&cs, LABEL_CHARS), ["\u{2022}", "", "\u{2022}", ""]);
|
||||
// An empty paragraph doesn't use up a number, in a story's own list or a named one.
|
||||
for name in ["", "Steps"] {
|
||||
let attrs = ParaAttrs { list_name: Some(name.into()), ..numbered(designcraft_doc::NumberStyle::Arabic, "^#.^t") };
|
||||
let (mut d, sid, _) = doc_with("One\n\nTwo\n", Rect::new(0.0, 0.0, 300.0, 300.0), attrs);
|
||||
if !name.is_empty() {
|
||||
d.settings.lists.push(designcraft_doc::NumberedList { name: name.into(), continue_across_stories: false });
|
||||
}
|
||||
let cs = compose_story(&d, sid, &ComposeOptions::default());
|
||||
assert_eq!(list_labels(&cs, LABEL_CHARS), ["1.", "", "2.", ""], "list `{name}`");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,9 +235,9 @@ impl crate::Document {
|
||||
before.sort_by_key(|st| key(st));
|
||||
let mut n = 0u32;
|
||||
for st in before {
|
||||
for p in &st.paras {
|
||||
for (p, r) in st.paras.iter().zip(st.para_ranges()) {
|
||||
let (pp, _) = self.styles.resolve_para(p);
|
||||
if pp.list_type == crate::ListType::Numbers && pp.list_name == name {
|
||||
if pp.list_type == crate::ListType::Numbers && pp.list_name == name && !r.is_empty() {
|
||||
n = pp.start_at.map_or(n.saturating_add(1), |s| s.max(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,12 +347,17 @@ impl Document {
|
||||
let (sid, pos) = self.find_anchor(id)?;
|
||||
let st = self.story(sid)?;
|
||||
let pi = st.para_at(pos);
|
||||
let r = st.para_ranges()[pi].clone();
|
||||
let ranges = st.para_ranges();
|
||||
let r = ranges[pi].clone();
|
||||
let (pp, _) = self.styles.resolve_para(&st.paras[pi]);
|
||||
let para_num = match pp.list_type {
|
||||
crate::ListType::Numbers => {
|
||||
// Count the run of numbered paragraphs before this one.
|
||||
let n = (0..=pi).rev().take_while(|&k| self.styles.resolve_para(&st.paras[k]).0.list_type == crate::ListType::Numbers).count();
|
||||
crate::ListType::Numbers if !r.is_empty() => {
|
||||
// Count the run of numbered paragraphs before this one (empty ones have no number).
|
||||
let n = (0..=pi)
|
||||
.rev()
|
||||
.take_while(|&k| self.styles.resolve_para(&st.paras[k]).0.list_type == crate::ListType::Numbers)
|
||||
.filter(|&k| ranges.get(k).is_some_and(|r| !r.is_empty()))
|
||||
.count();
|
||||
pp.number_label(u32::try_from(n).unwrap_or(u32::MAX)).trim_end().to_string()
|
||||
}
|
||||
_ => String::new(),
|
||||
|
||||
Reference in New Issue
Block a user