From bbb607106abcfd870e40874eb46d3ba698bfc0c5 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 04:44:47 -0400 Subject: [PATCH] documents: link addresses stay out of the log; tests for the link rules and the extended syntax Co-Authored-By: Claude Opus 5.5 (1M context) --- src/documents/document/parse.rs | 28 +++++++++++++ src/documents/document/tests/links.rs | 60 +++++++++++++++++++++++++++ src/documents/document/tests/mod.rs | 1 + src/documents/picture.rs | 2 +- src/documents/render.rs | 3 +- 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/documents/document/tests/links.rs diff --git a/src/documents/document/parse.rs b/src/documents/document/parse.rs index 0429fa0..6daf2e9 100644 --- a/src/documents/document/parse.rs +++ b/src/documents/document/parse.rs @@ -108,6 +108,7 @@ pub(crate) fn alike(old: &Parsed, new: &Parsed) -> (usize, usize) { #[cfg(test)] mod tests { use super::*; + use crate::documents::tree::Style; #[test] fn every_block_is_shown_raw_ones_as_written() { @@ -136,6 +137,33 @@ mod tests { assert_eq!(headings, [(1, "Lift", 18), (2, "Quoted", 67)]); } + #[test] + fn the_extended_syntax_draws_as_content() { + let text = "| a |\n| - |\n| 1 |\n\n- [x] done\n\n~~gone~~\n\n$$\nx^2\n$$\n\n```mermaid\ngraph TD\nA-->B\n```\n\nNote[^1]\n\n[^1]: foot\n"; + let found = parsed(text); + let drawn: Vec<&Node> = found + .blocks + .iter() + .map(|block| match &block.nodes { + Some(nodes) => &nodes[0], + None => panic!("{:?} shows raw", &text[block.range.clone()]), + }) + .collect(); + assert!(matches!(drawn[0], Node::Table { .. })); + assert!(matches!(drawn[1], Node::List { items, .. } if items[0].0 == Some(true))); + assert!( + matches!(drawn[2], Node::Paragraph(line) if line.styles.iter().any(|(_, style)| *style == Style::Strike)) + ); + assert!(matches!(drawn[3], Node::Math(formula) if formula == "x^2")); + assert!( + matches!(drawn[4], Node::Code { language: Some(language), .. } if language == "mermaid") + ); + assert!( + matches!(drawn[5], Node::Paragraph(line) if line.styles.iter().any(|(_, style)| *style == Style::Footnote)) + ); + assert!(matches!(drawn[6], Node::Footnote { label, .. } if label == "1")); + } + #[test] fn an_edit_keeps_the_blocks_around_it() { let old = parsed("# A\n\nb\n\nc\n\nd\n"); diff --git a/src/documents/document/tests/links.rs b/src/documents/document/tests/links.rs new file mode 100644 index 0000000..8f3787e --- /dev/null +++ b/src/documents/document/tests/links.rs @@ -0,0 +1,60 @@ +use std::{cell::RefCell, rc::Rc}; + +use gpui::{Entity, Modifiers, MouseButton, TestAppContext, VisualTestContext, point, px}; + +use super::{MarkdownDocument, MarkdownMode, open}; + +const LINKED: &str = "[a long linked line of text](https://ely.dev)\n"; + +/// Links pressed so far, as the owner hears them. +fn heard( + document: &Entity, + cx: &mut VisualTestContext, +) -> Rc>> { + let heard = Rc::new(RefCell::new(Vec::new())); + let kept = heard.clone(); + document.update(cx, |document, _| { + document.on_link(move |url, _, _| kept.borrow_mut().push(url.to_string())) + }); + heard +} + +/// Presses the text of block 0, past its padding. +fn press_text(modifiers: Modifiers, cx: &mut VisualTestContext) { + let bounds = cx + .debug_bounds("markdown-block-0") + .expect("the block is drawn"); + let at = point(bounds.left() + px(16.0), bounds.top() + px(10.0)); + cx.simulate_mouse_down(at, MouseButton::Left, modifiers); + cx.simulate_mouse_up(at, MouseButton::Left, modifiers); + cx.run_until_parked(); +} + +fn is_open(document: &Entity, cx: &mut VisualTestContext) -> bool { + document.read_with(cx, |document, _| document.open.is_some()) +} + +#[gpui::test] +fn a_click_in_preview_hands_the_link_over(cx: &mut TestAppContext) { + let (document, cx) = open(LINKED, MarkdownMode::Preview, cx); + let heard = heard(&document, cx); + press_text(Modifiers::none(), cx); + assert_eq!(*heard.borrow(), ["https://ely.dev"]); +} + +#[gpui::test] +fn a_plain_press_in_visual_edits_the_block(cx: &mut TestAppContext) { + let (document, cx) = open(LINKED, MarkdownMode::Visual, cx); + let heard = heard(&document, cx); + press_text(Modifiers::none(), cx); + assert!(heard.borrow().is_empty() && is_open(&document, cx)); +} + +#[gpui::test] +fn a_secondary_press_in_visual_hands_the_link_over(cx: &mut TestAppContext) { + let (document, cx) = open(LINKED, MarkdownMode::Visual, cx); + let heard = heard(&document, cx); + press_text(Modifiers::secondary_key(), cx); + assert_eq!(*heard.borrow(), ["https://ely.dev"]); + assert!(!is_open(&document, cx), "no block opens"); +} diff --git a/src/documents/document/tests/mod.rs b/src/documents/document/tests/mod.rs index be20f47..b1bd230 100644 --- a/src/documents/document/tests/mod.rs +++ b/src/documents/document/tests/mod.rs @@ -366,6 +366,7 @@ fn a_press_after_a_write_back_waits_for_the_long_read(cx: &mut TestAppContext) { } mod history; +mod links; mod place; #[gpui::test] diff --git a/src/documents/picture.rs b/src/documents/picture.rs index 1b677ce..f4094b2 100644 --- a/src/documents/picture.rs +++ b/src/documents/picture.rs @@ -200,7 +200,7 @@ fn picture(shown: Picture, window: &mut Window, cx: &mut App) -> AnyElement { .into_any_element() } Some(Err(why)) => { - log::warn!("markdown: a picture did not load: {why}"); + log::warn!("markdown: a picture did not load"); note(alt, why, colors.danger, cx) } None => note(alt, url.to_string().into(), colors.fg_subtle, cx), diff --git a/src/documents/render.rs b/src/documents/render.rs index 41307f1..1bf651f 100644 --- a/src/documents/render.rs +++ b/src/documents/render.rs @@ -395,7 +395,8 @@ impl Draw<'_> { /// Opens `url` through the owner, else the system. fn open_link(on_link: &Option, url: &str, window: &mut Window, cx: &mut App) { - log::info!("markdown: open {url}"); + // The address is the document's: never logged. + log::info!("markdown: a link opens"); match on_link { Some(on_link) => on_link(url, window, cx), None => cx.open_url(url),