diff --git a/src/lib.rs b/src/lib.rs index 340067e..0fe55be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,14 +84,18 @@ pub struct State<'a> { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum LinkCategory<'a> { AngleBracketed, + Reference { uri: Cow<'a, str>, title: Cow<'a, str>, id: Cow<'a, str> }, + Collapsed { uri: Cow<'a, str>, title: Cow<'a, str> }, Shortcut { uri: Cow<'a, str>, title: Cow<'a, str> }, Other { uri: Cow<'a, str>, title: Cow<'a, str> }, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ImageLink<'a> { - uri: Cow<'a, str>, - title: Cow<'a, str>, +pub enum ImageLink<'a> { + Reference { uri: Cow<'a, str>, title: Cow<'a, str>, id: Cow<'a, str> }, + Collapsed { uri: Cow<'a, str>, title: Cow<'a, str> }, + Shortcut { uri: Cow<'a, str>, title: Cow<'a, str> }, + Other { uri: Cow<'a, str>, title: Cow<'a, str> }, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -301,13 +305,29 @@ where link_type, dest_url, title, - id: _, + id, } => { state.link_stack.push(match link_type { LinkType::Autolink | LinkType::Email => { formatter.write_char('<')?; LinkCategory::AngleBracketed } + LinkType::Reference => { + formatter.write_char('[')?; + LinkCategory::Reference { + uri: dest_url.clone().into(), + title: title.clone().into(), + id: id.clone().into(), + } + } + LinkType::Collapsed => { + state.current_shortcut_text = Some(String::new()); + formatter.write_char('[')?; + LinkCategory::Collapsed { + uri: dest_url.clone().into(), + title: title.clone().into(), + } + } LinkType::Shortcut => { state.current_shortcut_text = Some(String::new()); formatter.write_char('[')?; @@ -327,14 +347,35 @@ where Ok(()) } Image { - link_type: _, + link_type, dest_url, title, - id: _, + id, } => { - state.image_stack.push(ImageLink { - uri: dest_url.clone().into(), - title: title.clone().into(), + state.image_stack.push(match link_type { + LinkType::Reference => ImageLink::Reference { + uri: dest_url.clone().into(), + title: title.clone().into(), + id: id.clone().into(), + }, + LinkType::Collapsed => { + state.current_shortcut_text = Some(String::new()); + ImageLink::Collapsed { + uri: dest_url.clone().into(), + title: title.clone().into(), + } + }, + LinkType::Shortcut => { + state.current_shortcut_text = Some(String::new()); + ImageLink::Shortcut { + uri: dest_url.clone().into(), + title: title.clone().into(), + } + }, + _ => ImageLink::Other { + uri: dest_url.clone().into(), + title: title.clone().into(), + }, }); formatter.write_str("![") } @@ -434,6 +475,22 @@ where End(tag) => match tag { TagEnd::Link => match state.link_stack.pop().unwrap() { LinkCategory::AngleBracketed => formatter.write_char('>'), + LinkCategory::Reference { uri, title, id } => { + state + .shortcuts + .push((id.to_string(), uri.to_string(), title.to_string())); + formatter.write_str("][")?; + formatter.write_str(&id)?; + formatter.write_char(']') + } + LinkCategory::Collapsed { uri, title } => { + if let Some(shortcut_text) = state.current_shortcut_text.take() { + state + .shortcuts + .push((shortcut_text, uri.to_string(), title.to_string())); + } + formatter.write_str("][]") + } LinkCategory::Shortcut { uri, title } => { if let Some(shortcut_text) = state.current_shortcut_text.take() { state @@ -445,8 +502,35 @@ where LinkCategory::Other { uri, title } => close_link(&uri, &title, formatter, LinkType::Inline), }, TagEnd::Image => { - let ImageLink { uri, title } = state.image_stack.pop().unwrap(); - close_link(uri.as_ref(), title.as_ref(), formatter, LinkType::Inline) + match state.image_stack.pop().unwrap() { + ImageLink::Reference { uri, title, id } => { + state + .shortcuts + .push((id.to_string(), uri.to_string(), title.to_string())); + formatter.write_str("][")?; + formatter.write_str(&id)?; + formatter.write_char(']') + } + ImageLink::Collapsed { uri, title } => { + if let Some(shortcut_text) = state.current_shortcut_text.take() { + state + .shortcuts + .push((shortcut_text, uri.to_string(), title.to_string())); + } + formatter.write_str("][]") + } + ImageLink::Shortcut { uri, title } => { + if let Some(shortcut_text) = state.current_shortcut_text.take() { + state + .shortcuts + .push((shortcut_text, uri.to_string(), title.to_string())); + } + formatter.write_char(']') + } + ImageLink::Other { uri, title } => { + close_link(uri.as_ref(), title.as_ref(), formatter, LinkType::Inline) + } + } } TagEnd::Emphasis => formatter.write_char(options.emphasis_token), TagEnd::Strong => formatter.write_str(options.strong_token), diff --git a/tests/fixtures/snapshots/stupicat-event-by-event-output b/tests/fixtures/snapshots/stupicat-event-by-event-output index 4a6593f..2f99b9c 100644 --- a/tests/fixtures/snapshots/stupicat-event-by-event-output +++ b/tests/fixtures/snapshots/stupicat-event-by-event-output @@ -25,7 +25,7 @@ tempor invidunt ut labore et dolore magna aliquyam erat Lorem ipsum dolor sit amet, [consetetur sadipscing](http://www.example.com/inline) elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos -et accusam et [justo duo dolores](http://www.example.com/reference) et ea rebum. Stet clita kasd gubergren, no +et accusam et [justo duo dolores][1] et ea rebum. Stet clita kasd gubergren, no sea [takimata sanctus](./showcase.md) est Lorem ipsum dolor sit amet. Ask to . @@ -216,7 +216,7 @@ What about \*this\* or \*\*that\*\*? \[disabled named link\]: target -[named-link](bah) +[named-link][enabled] \[disabled named link\]\[disabled\] @@ -245,4 +245,6 @@ foo [Links]: http://www.example.com/shortcut [`diam`]: http://www.example.com/shortcut_code_diam [`voluptua`]: http://www.example.com/shortcut_code_voluptua -[Images]: http://www.example.com/another_shortcut \ No newline at end of file +[1]: http://www.example.com/reference +[Images]: http://www.example.com/another_shortcut +[enabled]: bah \ No newline at end of file diff --git a/tests/fixtures/snapshots/stupicat-output b/tests/fixtures/snapshots/stupicat-output index 4a6593f..2f99b9c 100644 --- a/tests/fixtures/snapshots/stupicat-output +++ b/tests/fixtures/snapshots/stupicat-output @@ -25,7 +25,7 @@ tempor invidunt ut labore et dolore magna aliquyam erat Lorem ipsum dolor sit amet, [consetetur sadipscing](http://www.example.com/inline) elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos -et accusam et [justo duo dolores](http://www.example.com/reference) et ea rebum. Stet clita kasd gubergren, no +et accusam et [justo duo dolores][1] et ea rebum. Stet clita kasd gubergren, no sea [takimata sanctus](./showcase.md) est Lorem ipsum dolor sit amet. Ask to . @@ -216,7 +216,7 @@ What about \*this\* or \*\*that\*\*? \[disabled named link\]: target -[named-link](bah) +[named-link][enabled] \[disabled named link\]\[disabled\] @@ -245,4 +245,6 @@ foo [Links]: http://www.example.com/shortcut [`diam`]: http://www.example.com/shortcut_code_diam [`voluptua`]: http://www.example.com/shortcut_code_voluptua -[Images]: http://www.example.com/another_shortcut \ No newline at end of file +[1]: http://www.example.com/reference +[Images]: http://www.example.com/another_shortcut +[enabled]: bah \ No newline at end of file diff --git a/tests/fmt.rs b/tests/fmt.rs index be1b9b3..b157f20 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -215,7 +215,21 @@ mod inline_elements { assert_eq!( fmts_both("![a](b)\n![c][d]\n\n[d]: e"), ( - "![a](b)\n![c](e)".into(), + "![a](b)\n![c][d]\n\n[d]: e".into(), + State { + newlines_before_start: 2, + ..Default::default() + } + ) + ); + } + + #[test] + fn image_collapsed() { + assert_eq!( + fmts_both("![c][d]\n\n![c][]![c][]\n\n[d]: e\n[c]: f"), + ( + "![c][d]\n\n![c][]![c][]\n\n[d]: e\n[c]: f".into(), State { newlines_before_start: 2, ..Default::default() @@ -256,7 +270,21 @@ mod inline_elements { assert_eq!( fmts_both("[a](b)\n[c][d]\n\n[d]: e"), ( - "[a](b)\n[c](e)".into(), + "[a](b)\n[c][d]\n\n[d]: e".into(), + State { + newlines_before_start: 2, + ..Default::default() + } + ) + ); + } + + #[test] + fn links_collapsed() { + assert_eq!( + fmts_both("[c][d]\n\n[c][][c][]\n\n[d]: e\n[c]: f"), + ( + "[c][d]\n\n[c][][c][]\n\n[d]: e\n[c]: f".into(), State { newlines_before_start: 2, ..Default::default()