@@ -4737,6 +4737,221 @@ fn test_reflow_admonition_body_indent_preserved() {
47374737 }
47384738}
47394739
4740+ #[ test]
4741+ fn test_reflow_admonition_with_code_block_in_list_item ( ) {
4742+ // Code blocks inside admonitions within list items must be preserved
4743+ // verbatim. The closing fence must not be merged with subsequent text.
4744+ // Regression test for https://github.com/rvben/rumdl/issues/485
4745+ let config = MD013Config {
4746+ line_length : crate :: types:: LineLength :: from_const ( 88 ) ,
4747+ paragraphs : true ,
4748+ code_blocks : true ,
4749+ tables : true ,
4750+ headings : true ,
4751+ strict : false ,
4752+ reflow : true ,
4753+ reflow_mode : ReflowMode :: Default ,
4754+ length_mode : LengthMode :: default ( ) ,
4755+ abbreviations : Vec :: new ( ) ,
4756+ } ;
4757+ let rule = MD013LineLength :: from_config_struct ( config) ;
4758+
4759+ let content = concat ! (
4760+ "# Test\n " ,
4761+ "\n " ,
4762+ "- Lorem ipsum dolor sit amet.\n " ,
4763+ "\n " ,
4764+ " !!! example\n " ,
4765+ "\n " ,
4766+ " ```yaml\n " ,
4767+ " hello: world\n " ,
4768+ " ```\n " ,
4769+ "\n " ,
4770+ " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n " ,
4771+ ) ;
4772+
4773+ let ctx = LintContext :: new ( content, MarkdownFlavor :: MkDocs , None ) ;
4774+ let result = rule. check ( & ctx) . unwrap ( ) ;
4775+ let fixes: Vec < _ > = result. iter ( ) . filter ( |w| w. fix . is_some ( ) ) . collect ( ) ;
4776+ assert ! ( !fixes. is_empty( ) , "Should have a reflow fix" ) ;
4777+
4778+ let fix = fixes[ 0 ] . fix . as_ref ( ) . unwrap ( ) ;
4779+ let replacement = & fix. replacement ;
4780+
4781+ // The code block must be preserved intact
4782+ assert ! (
4783+ replacement. contains( " ```yaml\n hello: world\n ```" ) ,
4784+ "Code block inside admonition must be preserved verbatim; got:\n {replacement}"
4785+ ) ;
4786+
4787+ // The closing fence must NOT be merged with paragraph text
4788+ assert ! (
4789+ !replacement. contains( "``` Lorem" ) ,
4790+ "Closing fence must not be merged with paragraph text; got:\n {replacement}"
4791+ ) ;
4792+
4793+ // The trailing paragraph must be reflowed
4794+ assert ! (
4795+ replacement. contains( " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor\n incididunt ut labore et dolore magna aliqua." ) ,
4796+ "Trailing paragraph should be reflowed; got:\n {replacement}"
4797+ ) ;
4798+ }
4799+
4800+ #[ test]
4801+ fn test_reflow_admonition_with_tilde_fence_in_list_item ( ) {
4802+ // Tilde fences (~~~) inside admonitions must be handled the same as backtick fences.
4803+ let config = MD013Config {
4804+ line_length : crate :: types:: LineLength :: from_const ( 88 ) ,
4805+ paragraphs : true ,
4806+ code_blocks : true ,
4807+ tables : true ,
4808+ headings : true ,
4809+ strict : false ,
4810+ reflow : true ,
4811+ reflow_mode : ReflowMode :: Default ,
4812+ length_mode : LengthMode :: default ( ) ,
4813+ abbreviations : Vec :: new ( ) ,
4814+ } ;
4815+ let rule = MD013LineLength :: from_config_struct ( config) ;
4816+
4817+ let content = concat ! (
4818+ "# Test\n " ,
4819+ "\n " ,
4820+ "- Lorem ipsum dolor sit amet.\n " ,
4821+ "\n " ,
4822+ " !!! example\n " ,
4823+ "\n " ,
4824+ " ~~~python\n " ,
4825+ " def hello():\n " ,
4826+ " pass\n " ,
4827+ " ~~~\n " ,
4828+ "\n " ,
4829+ " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n " ,
4830+ ) ;
4831+
4832+ let ctx = LintContext :: new ( content, MarkdownFlavor :: MkDocs , None ) ;
4833+ let result = rule. check ( & ctx) . unwrap ( ) ;
4834+ let fixes: Vec < _ > = result. iter ( ) . filter ( |w| w. fix . is_some ( ) ) . collect ( ) ;
4835+ assert ! ( !fixes. is_empty( ) , "Should have a reflow fix" ) ;
4836+
4837+ let fix = fixes[ 0 ] . fix . as_ref ( ) . unwrap ( ) ;
4838+ let replacement = & fix. replacement ;
4839+
4840+ // Tilde-fenced code block must be preserved intact
4841+ assert ! (
4842+ replacement. contains( " ~~~python\n def hello():\n pass\n ~~~" ) ,
4843+ "Tilde-fenced code block must be preserved; got:\n {replacement}"
4844+ ) ;
4845+
4846+ assert ! (
4847+ !replacement. contains( "~~~ Lorem" ) && !replacement. contains( "~~~Lorem" ) ,
4848+ "Closing tilde fence must not be merged with text; got:\n {replacement}"
4849+ ) ;
4850+ }
4851+
4852+ #[ test]
4853+ fn test_reflow_admonition_with_multiple_code_blocks_in_list_item ( ) {
4854+ // Multiple code blocks inside an admonition must all be preserved.
4855+ let config = MD013Config {
4856+ line_length : crate :: types:: LineLength :: from_const ( 88 ) ,
4857+ paragraphs : true ,
4858+ code_blocks : true ,
4859+ tables : true ,
4860+ headings : true ,
4861+ strict : false ,
4862+ reflow : true ,
4863+ reflow_mode : ReflowMode :: Default ,
4864+ length_mode : LengthMode :: default ( ) ,
4865+ abbreviations : Vec :: new ( ) ,
4866+ } ;
4867+ let rule = MD013LineLength :: from_config_struct ( config) ;
4868+
4869+ let content = concat ! (
4870+ "# Test\n " ,
4871+ "\n " ,
4872+ "- Lorem ipsum dolor sit amet.\n " ,
4873+ "\n " ,
4874+ " !!! example\n " ,
4875+ "\n " ,
4876+ " ```yaml\n " ,
4877+ " hello: world\n " ,
4878+ " ```\n " ,
4879+ "\n " ,
4880+ " ```python\n " ,
4881+ " print(\" hello\" )\n " ,
4882+ " ```\n " ,
4883+ "\n " ,
4884+ " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n " ,
4885+ ) ;
4886+
4887+ let ctx = LintContext :: new ( content, MarkdownFlavor :: MkDocs , None ) ;
4888+ let result = rule. check ( & ctx) . unwrap ( ) ;
4889+ let fixes: Vec < _ > = result. iter ( ) . filter ( |w| w. fix . is_some ( ) ) . collect ( ) ;
4890+ assert ! ( !fixes. is_empty( ) , "Should have a reflow fix" ) ;
4891+
4892+ let fix = fixes[ 0 ] . fix . as_ref ( ) . unwrap ( ) ;
4893+ let replacement = & fix. replacement ;
4894+
4895+ // Both code blocks must be preserved
4896+ assert ! (
4897+ replacement. contains( "```yaml" ) ,
4898+ "First code block opening fence must be preserved; got:\n {replacement}"
4899+ ) ;
4900+ assert ! (
4901+ replacement. contains( "```python" ) ,
4902+ "Second code block opening fence must be preserved; got:\n {replacement}"
4903+ ) ;
4904+
4905+ // No fence markers merged with text
4906+ assert ! (
4907+ !replacement. contains( "``` Lorem" ) && !replacement. contains( "``` print" ) ,
4908+ "Fence markers must not be merged with other content; got:\n {replacement}"
4909+ ) ;
4910+ }
4911+
4912+ #[ test]
4913+ fn test_reflow_admonition_code_block_idempotent ( ) {
4914+ // After fixing, running again should produce no changes.
4915+ let config = MD013Config {
4916+ line_length : crate :: types:: LineLength :: from_const ( 88 ) ,
4917+ paragraphs : true ,
4918+ code_blocks : true ,
4919+ tables : true ,
4920+ headings : true ,
4921+ strict : false ,
4922+ reflow : true ,
4923+ reflow_mode : ReflowMode :: Default ,
4924+ length_mode : LengthMode :: default ( ) ,
4925+ abbreviations : Vec :: new ( ) ,
4926+ } ;
4927+ let rule = MD013LineLength :: from_config_struct ( config) ;
4928+
4929+ // This is already the correctly-formatted output
4930+ let content = concat ! (
4931+ "# Test\n " ,
4932+ "\n " ,
4933+ "- Lorem ipsum dolor sit amet.\n " ,
4934+ "\n " ,
4935+ " !!! example\n " ,
4936+ "\n " ,
4937+ " ```yaml\n " ,
4938+ " hello: world\n " ,
4939+ " ```\n " ,
4940+ "\n " ,
4941+ " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor\n " ,
4942+ " incididunt ut labore et dolore magna aliqua.\n " ,
4943+ ) ;
4944+
4945+ let ctx = LintContext :: new ( content, MarkdownFlavor :: MkDocs , None ) ;
4946+ let result = rule. check ( & ctx) . unwrap ( ) ;
4947+ let fixes: Vec < _ > = result. iter ( ) . filter ( |w| w. fix . is_some ( ) ) . collect ( ) ;
4948+ assert ! (
4949+ fixes. is_empty( ) ,
4950+ "Already correctly formatted content should not produce fixes; got {} fix(es)" ,
4951+ fixes. len( )
4952+ ) ;
4953+ }
4954+
47404955#[ test]
47414956fn test_reflow_tab_container_in_list_item ( ) {
47424957 // MkDocs tab containers (=== "Tab Title") inside list items should not
0 commit comments