diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index edf650e3..06300fd2 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "adler" @@ -116,6 +116,31 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a636f83af97c6946f3f5cf5c268ec02375bf5efd371110292dfd57961f57a509" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7eaf1bfaa5b8d512abfd36d0c432591fef139d3de9ee54f1f839ea109d70d33" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -198,12 +223,12 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "comrak" -version = "0.29.0" +version = "0.30.0" dependencies = [ "arbitrary", + "bon", "caseless", "clap", - "derive_builder", "emojis", "entities", "memchr", @@ -237,9 +262,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -247,9 +272,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", @@ -261,9 +286,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", @@ -290,37 +315,6 @@ dependencies = [ "syn", ] -[[package]] -name = "derive_builder" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" -dependencies = [ - "derive_builder_core", - "syn", -] - [[package]] name = "deunicode" version = "1.4.4" @@ -561,11 +555,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -630,6 +634,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.17" @@ -706,9 +716,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", diff --git a/src/html.rs b/src/html.rs index b759711c..0d633c6b 100644 --- a/src/html.rs +++ b/src/html.rs @@ -663,6 +663,7 @@ impl<'o> HtmlFormatter<'o> { .map(|n| n.data.borrow().value.clone()) { Some(NodeValue::List(nl)) => nl.tight, + Some(NodeValue::DescriptionItem(nd)) => nd.tight, _ => false, }; diff --git a/src/nodes.rs b/src/nodes.rs index ec6e549c..85407a7a 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -313,6 +313,10 @@ pub struct NodeDescriptionItem { /// Number of characters between the start of the list marker and the item text (including the list marker(s)). pub padding: usize, + + /// Whether the list is [tight](https://github.github.com/gfm/#tight), i.e. whether the + /// paragraphs are wrapped in `

` tags when formatted as HTML. + pub tight: bool, } /// The type of list. diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 03b8d5de..2c233981 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1640,7 +1640,7 @@ impl<'a, 'o> Parser<'a, 'o> { scanners::description_item_start(&line[self.first_nonspace..]), &mut matched, ) - && self.parse_desc_list_details(container) + && self.parse_desc_list_details(container, matched) { let offset = self.first_nonspace + matched - self.offset; self.advance_offset(line, offset, false); @@ -1884,15 +1884,26 @@ impl<'a, 'o> Parser<'a, 'o> { } } - fn parse_desc_list_details(&mut self, node: &mut &'a AstNode<'a>) -> bool { - let container = node; - + fn parse_desc_list_details(&mut self, container: &mut &'a AstNode<'a>, matched: usize) -> bool { + let mut tight = false; let last_child = match container.last_child() { Some(lc) => lc, None => { // Happens when the detail line is directly after the term, // without a blank line between. - *container = container.parent().unwrap(); + if !node_matches!(container, NodeValue::Paragraph) { + // If the container is not a paragraph, then this can't + // be a description list item. + return false; + } + + let parent = container.parent(); + if parent.is_none() { + return false; + } + + tight = true; + *container = parent.unwrap(); container.last_child().unwrap() } }; @@ -1918,7 +1929,7 @@ impl<'a, 'o> Parser<'a, 'o> { // All are incorrect; they all give the start line/col of // the DescriptionDetails, and the end line/col is completely off. // - // descriptionDetails: + // DescriptionDetails: // Same as the DescriptionItem. All but last, the end line/col // is (l+1):0. // @@ -1941,7 +1952,8 @@ impl<'a, 'o> Parser<'a, 'o> { let metadata = NodeDescriptionItem { marker_offset: self.indent, - padding: 2, + padding: matched, + tight, }; let item = self.add_child( @@ -1961,11 +1973,15 @@ impl<'a, 'o> Parser<'a, 'o> { true } else if node_matches!(last_child, NodeValue::DescriptionItem(..)) { let parent = last_child.parent().unwrap(); - reopen_ast_nodes(parent); + let tight = match last_child.data.borrow().value { + NodeValue::DescriptionItem(ref ndi) => ndi.tight, + _ => false, + }; let metadata = NodeDescriptionItem { marker_offset: self.indent, - padding: 2, + padding: matched, + tight, }; let item = self.add_child( diff --git a/src/scanners.re b/src/scanners.re index ecdcaa8c..f665f854 100644 --- a/src/scanners.re +++ b/src/scanners.re @@ -433,10 +433,9 @@ pub fn tasklist(s: &[u8]) -> Option<(usize, u8)> { pub fn description_item_start(s: &[u8]) -> Option { let mut cursor = 0; - let _marker = 0; let len = s.len(); /*!re2c - [:~] ([ \t]+|[\r\n]) { return Some(cursor); } + [:~] ([ \t]+) { return Some(cursor); } * { return None; } */ } diff --git a/src/scanners.rs b/src/scanners.rs index 43e72ca6..29eca63b 100644 --- a/src/scanners.rs +++ b/src/scanners.rs @@ -1,4 +1,4 @@ -/* Generated by re2c 3.0 */ +/* Generated by re2rust 4.0 */ pub fn atx_heading_start(s: &[u8]) -> Option { let mut cursor = 0; @@ -246,9 +246,7 @@ pub fn atx_heading_start(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -2065,9 +2063,7 @@ pub fn html_block_end_1(s: &[u8]) -> bool { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -2747,9 +2743,7 @@ pub fn html_block_end_2(s: &[u8]) -> bool { 25 => { return true; } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -3369,9 +3363,7 @@ pub fn html_block_end_3(s: &[u8]) -> bool { 24 => { return true; } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -3900,9 +3892,7 @@ pub fn html_block_end_4(s: &[u8]) -> bool { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -4582,9 +4572,7 @@ pub fn html_block_end_5(s: &[u8]) -> bool { 25 => { return true; } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -5280,9 +5268,7 @@ pub fn open_code_fence(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -5544,9 +5530,7 @@ pub fn close_code_fence(s: &[u8]) -> Option { return Some(cursor); } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -9164,9 +9148,7 @@ pub fn html_block_start(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -10279,9 +10261,7 @@ pub fn html_block_start_7(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -10509,9 +10489,7 @@ pub fn setext_heading_line(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -10845,9 +10823,7 @@ pub fn footnote_definition(s: &[u8]) -> Option { 17 => { return Some(cursor); } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -11690,9 +11666,7 @@ pub fn scheme(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -12735,9 +12709,7 @@ pub fn autolink_uri(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -16586,9 +16558,7 @@ pub fn autolink_email(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -17635,9 +17605,7 @@ pub fn html_tag(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -18030,9 +17998,7 @@ pub fn html_comment(s: &[u8]) -> Option { 15 => { return Some(cursor); } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -18559,9 +18525,7 @@ pub fn html_processing_instruction(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -18868,9 +18832,7 @@ pub fn html_declaration(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -19347,9 +19309,7 @@ pub fn html_cdata(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -19411,9 +19371,7 @@ pub fn spacechars(s: &[u8]) -> Option { 3 => { return Some(cursor); } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -20588,9 +20546,7 @@ pub fn link_title(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -21372,9 +21328,7 @@ pub fn dangerous_url(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -21703,9 +21657,7 @@ pub fn table_start(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -22279,9 +22231,7 @@ pub fn table_cell(s: &[u8], spoiler: bool) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -22781,9 +22731,7 @@ pub fn table_cell(s: &[u8], spoiler: bool) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -22846,9 +22794,7 @@ pub fn table_cell_end(s: &[u8]) -> Option { 3 => { return Some(cursor); } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -23000,9 +22946,7 @@ pub fn table_row_end(s: &[u8]) -> Option { } } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -23102,9 +23046,7 @@ pub fn shortcode(s: &[u8]) -> Option { 7 => { return Some(cursor); } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -23258,9 +23200,7 @@ pub fn open_multiline_block_quote_fence(s: &[u8]) -> Option { return Some(cursor); } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -23414,9 +23354,7 @@ pub fn close_multiline_block_quote_fence(s: &[u8]) -> Option { return Some(cursor); } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -23840,9 +23778,7 @@ pub fn tasklist(s: &[u8]) -> Option<(usize, u8)> { return Some((cursor, s[t1])); } } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } @@ -23850,7 +23786,6 @@ pub fn tasklist(s: &[u8]) -> Option<(usize, u8)> { pub fn description_item_start(s: &[u8]) -> Option { let mut cursor = 0; - let _marker = 0; let len = s.len(); { @@ -23900,11 +23835,6 @@ pub fn description_item_start(s: &[u8]) -> Option { yystate = 4; continue 'yyl; } - 0x0A | 0x0D => { - cursor += 1; - yystate = 6; - continue 'yyl; - } _ => { yystate = 2; continue 'yyl; @@ -23934,13 +23864,7 @@ pub fn description_item_start(s: &[u8]) -> Option { 5 => { return Some(cursor); } - 6 => { - yystate = 5; - continue 'yyl; - } - _ => { - panic!("internal lexer error") - } + _ => panic!("internal lexer error"), } } } diff --git a/src/tests/description_lists.rs b/src/tests/description_lists.rs index 13f11e9f..e127efc4 100644 --- a/src/tests/description_lists.rs +++ b/src/tests/description_lists.rs @@ -1,7 +1,7 @@ use super::*; #[test] -fn description_lists() { +fn description_lists_loose() { html_opts!( [extension.description_lists], concat!( @@ -57,6 +57,93 @@ fn description_lists() { ); } +#[test] +fn description_lists_tight() { + html_opts!( + [extension.description_lists], + concat!( + "Term 1\n", + ": Definition 1\n", + "\n", + "Term 2 with *inline markup*\n", + ": Definition 2\n" + ), + concat!( + "

\n", + "
Term 1
\n", + "
Definition 1
\n", + "
Term 2 with inline markup
\n", + "
Definition 2
\n", + "
\n", + ), + no_roundtrip, + ); + + html_opts!( + [extension.description_lists], + concat!( + "* Nested\n", + "\n", + " Term 1\n", + " : Definition 1\n\n", + " Term 2 with *inline markup*\n", + " : Definition 2\n\n" + ), + concat!( + "\n", + ), + no_roundtrip, + ); +} +#[test] +fn description_lists_edge_cases() { + html_opts!( + [extension.description_lists], + concat!(":"), + concat!("

:

\n"), + ); + + html_opts!( + [extension.description_lists], + concat!(": foo"), + concat!("

: foo

\n"), + ); + + html_opts!( + [extension.description_lists], + concat!("a\n:"), + concat!("

a\n:

\n"), + ); + + html_opts!( + [extension.description_lists], + concat!( + "- foo\n", + "- : bar\n", + " - baz\n", + ), + concat!( + "\n", + ), + ); +} #[test] fn sourcepos() { // TODO There's plenty of work to do here still. The test currently represents diff --git a/src/tests/fixtures/description_lists.md b/src/tests/fixtures/description_lists.md index a9ca0fc4..5f689014 100644 --- a/src/tests/fixtures/description_lists.md +++ b/src/tests/fixtures/description_lists.md @@ -11,9 +11,8 @@ with `:` (after 0-2 spaces); subsequent lines must be indented unless they are lazy paragraph continuations. -There is no distinction between a "tight" list or a -"loose" list. Definitions are always wrapped in `

` -tags. +The list is tight if there is no blank line between +the term and the first definition, otherwise loose. ```````````````````````````````` example apple @@ -24,13 +23,9 @@ orange .

apple
-
-

red fruit

-
+
red fruit
orange
-
-

orange fruit

-
+
orange fruit
```````````````````````````````` @@ -68,13 +63,9 @@ orange .
apple
-
-

red fruit

-
+
red fruit
orange
-
-

orange fruit

-
+
orange fruit
```````````````````````````````` @@ -101,8 +92,6 @@ orange Multiple blocks in a definition: -Note that the column - ```````````````````````````````` example *apple* @@ -161,7 +150,6 @@ term ```````````````````````````````` Multiple definitions, tight: -(always rendered as loose) ```````````````````````````````` example apple @@ -174,19 +162,11 @@ orange .
apple
-
-

red fruit

-
-
-

computer company

-
+
red fruit
+
computer company
orange
-
-

orange fruit

-
-
-

telecom company

-
+
orange fruit
+
telecom company
```````````````````````````````` @@ -271,13 +251,9 @@ orange .
apple
-
-

red fruit

-
+
red fruit
orange
-
-

orange fruit

-
+
orange fruit
```````````````````````````````` @@ -315,12 +291,8 @@ bim

Foo

bar
-
-

baz

-
+
baz
bim
-
-

bor

-
+
bor
````````````````````````````````