From 2e196a10a7c3265eac6d5c52ebcec465ad0374c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 12 Oct 2026 05:23:55 -0400 Subject: [PATCH] documents: a picture shrinks within a narrow table cell; tree tests in their own file; test comments within ten words Co-Authored-By: Claude Opus 5.5 (1M context) --- src/documents/render.rs | 2 + src/documents/tests.rs | 23 ++- src/documents/{tree.rs => tree/mod.rs} | 201 +------------------------ src/documents/tree/tests.rs | 195 ++++++++++++++++++++++++ 4 files changed, 215 insertions(+), 206 deletions(-) rename src/documents/{tree.rs => tree/mod.rs} (60%) create mode 100644 src/documents/tree/tests.rs diff --git a/src/documents/render.rs b/src/documents/render.rs index 6f8932e..773ab74 100644 --- a/src/documents/render.rs +++ b/src/documents/render.rs @@ -300,6 +300,7 @@ impl Draw<'_> { let cell = |blocks: &[Node], column: usize, header: bool, cx: &App| { div() .flex_1() + .min_w_0() .flex() .px_3() .py_1p5() @@ -315,6 +316,7 @@ impl Draw<'_> { }) .child( div() + .max_w_full() .flex() .flex_col() .gap_1() diff --git a/src/documents/tests.rs b/src/documents/tests.rs index 6cf67cf..d53a6c5 100644 --- a/src/documents/tests.rs +++ b/src/documents/tests.rs @@ -23,7 +23,7 @@ fn hit(page: usize) -> PageHit { } } -/// Three tall pages and the hits found; a press while pinning records the note asked. +/// Three tall pages and hits; a pinning press records notes. struct Reader { query: Entity, width: f32, @@ -60,7 +60,7 @@ fn open(cx: &mut TestAppContext) -> (Entity, Noted, &mut VisualTestConte open_with(400.0, None, cx) } -/// Pages `width` points wide, with a hit on the second, current from the first frame when `current` says. +/// Pages `width` wide, a hit on page two, maybe current. fn open_with( width: f32, current: Option, @@ -86,7 +86,7 @@ fn settle(cx: &mut VisualTestContext) { cx.run_until_parked(); } -/// A point on the pages, below the header and the find bar, at the view's middle. +/// A point on the pages, below the bars, mid-view. fn on_pages(cx: &mut VisualTestContext) -> Point { cx.update(|window, _| point(window.viewport_size().width / 2.0, px(300.0))) } @@ -102,7 +102,7 @@ fn wheel(down: f32, cx: &mut VisualTestContext) { settle(cx); } -/// Turns pinning on with the header's last button, then presses the pages at their middle; returns the note asked. +/// Turns pinning on, presses mid-page; the note asked. fn pin(cx: &mut VisualTestContext, noted: &Noted) -> (usize, Point) { let at = on_pages(cx); pin_at(at, cx, noted) @@ -143,7 +143,7 @@ fn a_note_lands_where_the_press_is_on_a_scrolled_page(cx: &mut TestAppContext) { assert_eq!(scrolled.y, top.y + 150.0, "the press counts the scroll"); } -/// Scrolls `down`, makes the hit current, then pins a note at the same window point. +/// Scrolls `down`, makes the hit current, pins a note. fn revealed(down: f32, cx: &mut TestAppContext) -> (usize, Point) { let (reader, noted, cx) = open(cx); if down > 0.0 { @@ -201,7 +201,7 @@ fn a_hit_current_from_the_first_frame_comes_into_view(cx: &mut TestAppContext) { assert_eq!(at_once, later); } -/// A field outside a zen view, and how often Escape left zen. +/// A field outside zen, and the Escapes that left zen. struct Zen { outside: Entity, on: bool, @@ -375,6 +375,17 @@ fn a_picture_in_a_table_cell_draws(cx: &mut TestAppContext) { assert_eq!(drawn_picture(table, 400.0, cx), size(px(200.0), px(100.0))); } +#[gpui::test] +fn a_picture_in_a_narrow_cell_shrinks_within_it(cx: &mut TestAppContext) { + let table = "| A | B | +| - | - | +| ![shot](assets/wide%20shot.png) | b | +"; + let drawn = drawn_picture(table, 160.0, cx); + assert!(drawn.width < px(80.0), "within its half: {drawn:?}"); + assert_eq!(drawn.height, drawn.width / 2.0, "at its ratio"); +} + #[gpui::test] fn a_formula_that_fails_shows_why_and_its_source(cx: &mut TestAppContext) { cx.update(Theme::init); diff --git a/src/documents/tree.rs b/src/documents/tree/mod.rs similarity index 60% rename from src/documents/tree.rs rename to src/documents/tree/mod.rs index 69f173b..5d77008 100644 --- a/src/documents/tree.rs +++ b/src/documents/tree/mod.rs @@ -310,203 +310,4 @@ fn styled(inline: &mut Inline, text: &str, style: Style) { } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn blocks_nest_as_written() { - let nodes = parse( - "# Lift\n\nBlend **toward** [white](https://ely.dev).\n\n- [x] clamp\n- [ ] gamma\n\n> quoted\n\n```rust\nfn main() {}\n```\n\n---\n", - ); - assert!(matches!(&nodes[0], Node::Heading(1, text) if text.text == "Lift")); - let Node::Paragraph(line) = &nodes[1] else { - panic!("a paragraph, not {:?}", nodes[1]) - }; - assert_eq!(line.text, "Blend toward white."); - assert_eq!(line.styles, [(6..12, Style::Strong), (13..18, Style::Link)]); - assert_eq!(line.links, [(13..18, "https://ely.dev".to_string())]); - let Node::List { start: None, items } = &nodes[2] else { - panic!("a list, not {:?}", nodes[2]) - }; - assert_eq!( - items.iter().map(|(task, _)| *task).collect::>(), - [Some(true), Some(false)] - ); - assert!(matches!(&items[0].1[0], Node::Paragraph(text) if text.text == "clamp")); - assert!(matches!(&nodes[3], Node::Quote(inner) if inner.len() == 1)); - assert_eq!( - nodes[4], - Node::Code { - language: Some("rust".into()), - text: "fn main() {}".into() - } - ); - assert_eq!(nodes[5], Node::Rule); - } - - #[test] - fn an_image_inside_an_image_is_the_outer_one() { - // markdown-rs drops the inner image from the alt. - assert_eq!( - parse("![outer ![inner](inner.png)](outer.png)"), - [Node::Image { - alt: "outer ".into(), - url: "outer.png".into(), - link: None - }] - ); - } - - #[test] - fn an_image_splits_its_paragraph_and_references_resolve() { - let nodes = - parse("Before ![a][pic] after [site].\n\n[pic]: a.png\n[site]: https://ely.dev\n"); - assert!(matches!(&nodes[0], Node::Paragraph(text) if text.text == "Before ")); - assert_eq!( - nodes[1], - Node::Image { - alt: "a".into(), - url: "a.png".into(), - link: None - } - ); - let Node::Paragraph(after) = &nodes[2] else { - panic!("a paragraph, not {:?}", nodes[2]) - }; - assert_eq!(after.text, " after site."); - assert_eq!(after.links, [(7..11, "https://ely.dev".to_string())]); - assert_eq!(nodes.len(), 3, "definitions draw nothing: {nodes:?}"); - } - - #[test] - fn a_soft_break_reads_as_a_space_and_a_hard_one_as_a_line() { - for (source, shown) in [ - ("one\ntwo \nthree", "one two\nthree"), - ("one\r\ntwo \r\nthree", "one two\nthree"), - ("one\rtwo \rthree", "one two\nthree"), - ] { - let nodes = parse(source); - assert!( - matches!(&nodes[0], Node::Paragraph(text) if text.text == shown), - "{source:?} reads {nodes:?}" - ); - } - } - - #[test] - fn definitions_nested_in_quotes_and_lists_resolve() { - for source in [ - "[site] ![pic]\n\n> [site]: https://ely.dev\n> [pic]: a.png\n", - "[site] ![pic]\n\n- [site]: https://ely.dev\n- [pic]: a.png\n", - ] { - let nodes = parse(source); - let Node::Paragraph(text) = &nodes[0] else { - panic!("a paragraph, not {:?}", nodes[0]) - }; - assert_eq!(text.links, [(0..4, "https://ely.dev".to_string())]); - assert_eq!( - nodes[1], - Node::Image { - alt: "pic".into(), - url: "a.png".into(), - link: None - } - ); - } - } - - #[test] - fn an_image_inside_styles_and_links_stays_an_image() { - let nodes = parse("*a ![x](x.png) b* [![y](y.png)](https://ely.dev)"); - let Node::Paragraph(before) = &nodes[0] else { - panic!("a paragraph, not {:?}", nodes[0]) - }; - assert_eq!( - (before.text.as_str(), &before.styles[..]), - ("a ", &[(0..2, Style::Emphasis)][..]) - ); - assert_eq!( - nodes[1], - Node::Image { - alt: "x".into(), - url: "x.png".into(), - link: None - } - ); - let Node::Paragraph(after) = &nodes[2] else { - panic!("a paragraph, not {:?}", nodes[2]) - }; - assert_eq!( - (after.text.as_str(), &after.styles[..]), - (" b ", &[(0..2, Style::Emphasis)][..]) - ); - assert_eq!( - nodes[3], - Node::Image { - alt: "y".into(), - url: "y.png".into(), - link: Some("https://ely.dev".into()) - } - ); - assert_eq!(nodes.len(), 4, "{nodes:?}"); - } - - #[test] - fn a_heading_keeps_its_images_after_it() { - let nodes = parse("# Lift ![x](x.png)"); - assert!(matches!(&nodes[0], Node::Heading(1, text) if text.text == "Lift ")); - assert!(matches!(&nodes[1], Node::Image { url, .. } if url == "x.png")); - } - - #[test] - fn display_math_stands_alone() { - assert_eq!(parse("$$c = 1$$"), [Node::Math("c = 1".into())]); - assert_eq!(parse("$$\nc = 1\n$$"), [Node::Math("c = 1".into())]); - let Node::Paragraph(inline) = &parse("so $c$ holds")[0] else { - panic!("inline math stays in its paragraph") - }; - assert_eq!(inline.styles, [(3..4, Style::Math)]); - } - - #[test] - fn tables_keep_their_cells_and_alignment() { - let nodes = parse("| Name | Lift |\n|:--|--:|\n| accent | 0.5 |\n"); - let Node::Table { aligns, head, rows } = &nodes[0] else { - panic!("a table, not {:?}", nodes[0]) - }; - assert_eq!(aligns, &[AlignKind::Left, AlignKind::Right]); - let text = |cell: &[Node]| match cell { - [Node::Paragraph(line)] => line.text.clone(), - other => panic!("one line, not {other:?}"), - }; - assert_eq!( - head.iter().map(|cell| text(cell)).collect::>(), - ["Name", "Lift"] - ); - assert_eq!(text(&rows[0][1]), "0.5"); - } - - #[test] - fn a_picture_in_a_cell_stays_a_picture() { - let nodes = parse("| Picture |\n| --- |\n| ![shot](assets/shot.png) |\n"); - let Node::Table { rows, .. } = &nodes[0] else { - panic!("a table, not {:?}", nodes[0]) - }; - assert!( - matches!(&rows[0][0][..], [Node::Image { alt, url, .. }] if alt == "shot" && url == "assets/shot.png") - ); - } - - #[test] - fn footnotes_number_their_references_and_hold_their_body() { - let nodes = parse("Lift[^a].\n\n[^a]: Toward white.\n"); - let Node::Paragraph(line) = &nodes[0] else { - panic!("a paragraph, not {:?}", nodes[0]) - }; - assert_eq!(line.text, "Lift[a]."); - assert_eq!(line.styles, [(4..7, Style::Footnote)]); - assert!( - matches!(&nodes[1], Node::Footnote { label, body } if label == "a" && body.len() == 1) - ); - } -} +mod tests; diff --git a/src/documents/tree/tests.rs b/src/documents/tree/tests.rs new file mode 100644 index 0000000..2f2ba51 --- /dev/null +++ b/src/documents/tree/tests.rs @@ -0,0 +1,195 @@ +use super::*; + +#[test] +fn blocks_nest_as_written() { + let nodes = parse( + "# Lift\n\nBlend **toward** [white](https://ely.dev).\n\n- [x] clamp\n- [ ] gamma\n\n> quoted\n\n```rust\nfn main() {}\n```\n\n---\n", + ); + assert!(matches!(&nodes[0], Node::Heading(1, text) if text.text == "Lift")); + let Node::Paragraph(line) = &nodes[1] else { + panic!("a paragraph, not {:?}", nodes[1]) + }; + assert_eq!(line.text, "Blend toward white."); + assert_eq!(line.styles, [(6..12, Style::Strong), (13..18, Style::Link)]); + assert_eq!(line.links, [(13..18, "https://ely.dev".to_string())]); + let Node::List { start: None, items } = &nodes[2] else { + panic!("a list, not {:?}", nodes[2]) + }; + assert_eq!( + items.iter().map(|(task, _)| *task).collect::>(), + [Some(true), Some(false)] + ); + assert!(matches!(&items[0].1[0], Node::Paragraph(text) if text.text == "clamp")); + assert!(matches!(&nodes[3], Node::Quote(inner) if inner.len() == 1)); + assert_eq!( + nodes[4], + Node::Code { + language: Some("rust".into()), + text: "fn main() {}".into() + } + ); + assert_eq!(nodes[5], Node::Rule); +} + +#[test] +fn an_image_inside_an_image_is_the_outer_one() { + // markdown-rs drops the inner image from the alt. + assert_eq!( + parse("![outer ![inner](inner.png)](outer.png)"), + [Node::Image { + alt: "outer ".into(), + url: "outer.png".into(), + link: None + }] + ); +} + +#[test] +fn an_image_splits_its_paragraph_and_references_resolve() { + let nodes = parse("Before ![a][pic] after [site].\n\n[pic]: a.png\n[site]: https://ely.dev\n"); + assert!(matches!(&nodes[0], Node::Paragraph(text) if text.text == "Before ")); + assert_eq!( + nodes[1], + Node::Image { + alt: "a".into(), + url: "a.png".into(), + link: None + } + ); + let Node::Paragraph(after) = &nodes[2] else { + panic!("a paragraph, not {:?}", nodes[2]) + }; + assert_eq!(after.text, " after site."); + assert_eq!(after.links, [(7..11, "https://ely.dev".to_string())]); + assert_eq!(nodes.len(), 3, "definitions draw nothing: {nodes:?}"); +} + +#[test] +fn a_soft_break_reads_as_a_space_and_a_hard_one_as_a_line() { + for (source, shown) in [ + ("one\ntwo \nthree", "one two\nthree"), + ("one\r\ntwo \r\nthree", "one two\nthree"), + ("one\rtwo \rthree", "one two\nthree"), + ] { + let nodes = parse(source); + assert!( + matches!(&nodes[0], Node::Paragraph(text) if text.text == shown), + "{source:?} reads {nodes:?}" + ); + } +} + +#[test] +fn definitions_nested_in_quotes_and_lists_resolve() { + for source in [ + "[site] ![pic]\n\n> [site]: https://ely.dev\n> [pic]: a.png\n", + "[site] ![pic]\n\n- [site]: https://ely.dev\n- [pic]: a.png\n", + ] { + let nodes = parse(source); + let Node::Paragraph(text) = &nodes[0] else { + panic!("a paragraph, not {:?}", nodes[0]) + }; + assert_eq!(text.links, [(0..4, "https://ely.dev".to_string())]); + assert_eq!( + nodes[1], + Node::Image { + alt: "pic".into(), + url: "a.png".into(), + link: None + } + ); + } +} + +#[test] +fn an_image_inside_styles_and_links_stays_an_image() { + let nodes = parse("*a ![x](x.png) b* [![y](y.png)](https://ely.dev)"); + let Node::Paragraph(before) = &nodes[0] else { + panic!("a paragraph, not {:?}", nodes[0]) + }; + assert_eq!( + (before.text.as_str(), &before.styles[..]), + ("a ", &[(0..2, Style::Emphasis)][..]) + ); + assert_eq!( + nodes[1], + Node::Image { + alt: "x".into(), + url: "x.png".into(), + link: None + } + ); + let Node::Paragraph(after) = &nodes[2] else { + panic!("a paragraph, not {:?}", nodes[2]) + }; + assert_eq!( + (after.text.as_str(), &after.styles[..]), + (" b ", &[(0..2, Style::Emphasis)][..]) + ); + assert_eq!( + nodes[3], + Node::Image { + alt: "y".into(), + url: "y.png".into(), + link: Some("https://ely.dev".into()) + } + ); + assert_eq!(nodes.len(), 4, "{nodes:?}"); +} + +#[test] +fn a_heading_keeps_its_images_after_it() { + let nodes = parse("# Lift ![x](x.png)"); + assert!(matches!(&nodes[0], Node::Heading(1, text) if text.text == "Lift ")); + assert!(matches!(&nodes[1], Node::Image { url, .. } if url == "x.png")); +} + +#[test] +fn display_math_stands_alone() { + assert_eq!(parse("$$c = 1$$"), [Node::Math("c = 1".into())]); + assert_eq!(parse("$$\nc = 1\n$$"), [Node::Math("c = 1".into())]); + let Node::Paragraph(inline) = &parse("so $c$ holds")[0] else { + panic!("inline math stays in its paragraph") + }; + assert_eq!(inline.styles, [(3..4, Style::Math)]); +} + +#[test] +fn tables_keep_their_cells_and_alignment() { + let nodes = parse("| Name | Lift |\n|:--|--:|\n| accent | 0.5 |\n"); + let Node::Table { aligns, head, rows } = &nodes[0] else { + panic!("a table, not {:?}", nodes[0]) + }; + assert_eq!(aligns, &[AlignKind::Left, AlignKind::Right]); + let text = |cell: &[Node]| match cell { + [Node::Paragraph(line)] => line.text.clone(), + other => panic!("one line, not {other:?}"), + }; + assert_eq!( + head.iter().map(|cell| text(cell)).collect::>(), + ["Name", "Lift"] + ); + assert_eq!(text(&rows[0][1]), "0.5"); +} + +#[test] +fn a_picture_in_a_cell_stays_a_picture() { + let nodes = parse("| Picture |\n| --- |\n| ![shot](assets/shot.png) |\n"); + let Node::Table { rows, .. } = &nodes[0] else { + panic!("a table, not {:?}", nodes[0]) + }; + assert!( + matches!(&rows[0][0][..], [Node::Image { alt, url, .. }] if alt == "shot" && url == "assets/shot.png") + ); +} + +#[test] +fn footnotes_number_their_references_and_hold_their_body() { + let nodes = parse("Lift[^a].\n\n[^a]: Toward white.\n"); + let Node::Paragraph(line) = &nodes[0] else { + panic!("a paragraph, not {:?}", nodes[0]) + }; + assert_eq!(line.text, "Lift[a]."); + assert_eq!(line.styles, [(4..7, Style::Footnote)]); + assert!(matches!(&nodes[1], Node::Footnote { label, body } if label == "a" && body.len() == 1)); +}