Merge pull request #1376 from Silvertree2010/fix/h869-table-alternating-fill-counts-ar

Saturate table alternating-fill and index paragraph counts
This commit is contained in:
Brandon Thomas
2026-10-23 17:28:43 -04:00
committed by GitHub
3 changed files with 33 additions and 7 deletions
+6 -3
View File
@@ -72,7 +72,9 @@ fn add_reference(s: &mut Session, p: &Value) -> Result<Value> {
"currentPage" => IndexRange::CurrentPage,
"toEndOfStory" => IndexRange::ToEndOfStory,
"suppressPageRange" => IndexRange::SuppressPageRange,
"nextParagraphs" => IndexRange::NextParagraphs(p.get("paragraphs").and_then(Value::as_u64).unwrap_or(1) as u32),
"nextParagraphs" => {
IndexRange::NextParagraphs(p.get("paragraphs").and_then(Value::as_u64).map_or(1, |x| u32::try_from(x).unwrap_or(u32::MAX)))
}
"see" => IndexRange::See(target()),
"seeAlso" => IndexRange::SeeAlso(target()),
r => return Err(bad("index.addReference", format!("unknown range `{r}`"))),
@@ -103,8 +105,9 @@ fn resolve(s: &Session, d: &Document, skip: Option<StoryId>) -> Vec<(StoryId, us
IndexRange::ToEndOfStory => page(st.len().saturating_sub(1)),
IndexRange::NextParagraphs(n) => {
let ranges = st.para_ranges();
let pi = (st.para_at(pos) + *n as usize).min(ranges.len() - 1);
page(ranges[pi].end.saturating_sub(1).max(ranges[pi].start))
let n = usize::try_from(*n).unwrap_or(usize::MAX);
let pi = st.para_at(pos).saturating_add(n).min(ranges.len().saturating_sub(1));
ranges.get(pi).and_then(|r| page(r.end.saturating_sub(1).max(r.start)))
}
_ => start,
};
+4 -4
View File
@@ -1393,14 +1393,14 @@ fn parse_alt(v: &Value) -> Option<AltFills> {
}
let d = AltFills::default();
Some(AltFills {
first: v.get("first").and_then(Value::as_u64).map_or(d.first, |x| x as u32),
first: v.get("first").and_then(Value::as_u64).map_or(d.first, |x| u32::try_from(x).unwrap_or(u32::MAX)),
first_color: color_param(v, "firstColor").unwrap_or(d.first_color),
first_tint: v.get("firstTint").and_then(Value::as_f64).map_or(d.first_tint, |x| x as f32),
next: v.get("next").and_then(Value::as_u64).map_or(d.next, |x| x as u32),
next: v.get("next").and_then(Value::as_u64).map_or(d.next, |x| u32::try_from(x).unwrap_or(u32::MAX)),
next_color: color_param(v, "nextColor").unwrap_or(d.next_color),
next_tint: v.get("nextTint").and_then(Value::as_f64).map_or(d.next_tint, |x| x as f32),
skip_first: v.get("skipFirst").and_then(Value::as_u64).map_or(0, |x| x as u32),
skip_last: v.get("skipLast").and_then(Value::as_u64).map_or(0, |x| x as u32),
skip_first: v.get("skipFirst").and_then(Value::as_u64).map_or(0, |x| u32::try_from(x).unwrap_or(u32::MAX)),
skip_last: v.get("skipLast").and_then(Value::as_u64).map_or(0, |x| u32::try_from(x).unwrap_or(u32::MAX)),
})
}
+23
View File
@@ -1097,3 +1097,26 @@ fn convert_rows_keeps_merged_cells_on_one_side() {
assert!(!convert_enabled(&s, "Body"));
check(&s);
}
#[test]
fn alternating_fill_counts_saturate_instead_of_wrapping() {
let (mut s, _, _) = session_with_frame();
s.execute("table.insert", &json!({"rows": 3, "cols": 2})).unwrap();
s.execute("table.options", &json!({"altRows": {"first": 4294967297u64, "next": 1, "skipFirst": 4294967296u64, "skipLast": 9999999999u64}}))
.unwrap();
let t = s.execute("table.get", &json!({})).unwrap();
let a = &t["options"]["altRows"];
assert_eq!(a["first"], u32::MAX);
assert_eq!(a["skipFirst"], u32::MAX);
assert_eq!(a["skipLast"], u32::MAX);
assert_eq!(a["next"], 1);
}
#[test]
fn index_next_paragraphs_saturates_instead_of_wrapping() {
let (mut s, _, _) = session_with_frame();
s.execute("index.addReference", &json!({"topic": "Intro", "range": "nextParagraphs", "paragraphs": 4294967296u64})).unwrap();
let l = s.execute("index.list", &json!({})).unwrap();
assert_eq!(l[0]["range"]["type"], "nextParagraphs");
assert_eq!(l[0]["range"]["value"], u32::MAX);
}