rumdl 0.0.53__tar.gz → 0.0.54__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rumdl might be problematic. Click here for more details.

Files changed (252) hide show
  1. {rumdl-0.0.53 → rumdl-0.0.54}/Cargo.lock +1 -1
  2. {rumdl-0.0.53 → rumdl-0.0.54}/Cargo.toml +1 -1
  3. {rumdl-0.0.53 → rumdl-0.0.54}/PKG-INFO +1 -1
  4. rumdl-0.0.54/src/rules/md007_ul_indent.rs +222 -0
  5. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md035_hr_style.rs +28 -39
  6. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/element_cache.rs +84 -50
  7. rumdl-0.0.54/tests/rules/md007_test.rs +127 -0
  8. rumdl-0.0.54/tests/rules/md035_test.rs +153 -0
  9. rumdl-0.0.53/src/rules/md007_ul_indent.rs +0 -276
  10. rumdl-0.0.53/tests/rules/md007_test.rs +0 -69
  11. rumdl-0.0.53/tests/rules/md035_test.rs +0 -61
  12. {rumdl-0.0.53 → rumdl-0.0.54}/.rumdl.toml +0 -0
  13. {rumdl-0.0.53 → rumdl-0.0.54}/MANIFEST.in +0 -0
  14. {rumdl-0.0.53 → rumdl-0.0.54}/Makefile +0 -0
  15. {rumdl-0.0.53 → rumdl-0.0.54}/README.md +0 -0
  16. {rumdl-0.0.53 → rumdl-0.0.54}/assets/logo.png +0 -0
  17. {rumdl-0.0.53 → rumdl-0.0.54}/benches/range_performance.rs +0 -0
  18. {rumdl-0.0.53 → rumdl-0.0.54}/benches/range_utils_benchmark.rs +0 -0
  19. {rumdl-0.0.53 → rumdl-0.0.54}/benches/rule_performance.rs +0 -0
  20. {rumdl-0.0.53 → rumdl-0.0.54}/docs/RULES.md +0 -0
  21. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md001.md +0 -0
  22. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md002.md +0 -0
  23. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md003.md +0 -0
  24. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md004.md +0 -0
  25. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md005.md +0 -0
  26. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md006.md +0 -0
  27. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md007.md +0 -0
  28. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md008.md +0 -0
  29. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md009.md +0 -0
  30. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md010.md +0 -0
  31. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md011.md +0 -0
  32. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md012.md +0 -0
  33. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md013.md +0 -0
  34. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md014.md +0 -0
  35. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md015.md +0 -0
  36. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md018.md +0 -0
  37. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md019.md +0 -0
  38. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md020.md +0 -0
  39. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md021.md +0 -0
  40. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md022.md +0 -0
  41. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md023.md +0 -0
  42. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md024.md +0 -0
  43. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md025.md +0 -0
  44. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md026.md +0 -0
  45. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md027.md +0 -0
  46. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md028.md +0 -0
  47. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md029.md +0 -0
  48. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md030.md +0 -0
  49. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md031.md +0 -0
  50. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md032.md +0 -0
  51. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md033.md +0 -0
  52. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md034.md +0 -0
  53. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md035.md +0 -0
  54. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md036.md +0 -0
  55. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md037.md +0 -0
  56. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md038.md +0 -0
  57. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md039.md +0 -0
  58. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md040.md +0 -0
  59. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md041.md +0 -0
  60. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md042.md +0 -0
  61. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md043.md +0 -0
  62. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md044.md +0 -0
  63. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md045.md +0 -0
  64. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md046.md +0 -0
  65. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md047.md +0 -0
  66. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md048.md +0 -0
  67. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md049.md +0 -0
  68. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md050.md +0 -0
  69. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md051.md +0 -0
  70. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md052.md +0 -0
  71. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md053.md +0 -0
  72. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md054.md +0 -0
  73. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md055.md +0 -0
  74. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md056.md +0 -0
  75. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md057.md +0 -0
  76. {rumdl-0.0.53 → rumdl-0.0.54}/docs/md058.md +0 -0
  77. {rumdl-0.0.53 → rumdl-0.0.54}/issues/plan-rule-parity-with-markdownlint.md +0 -0
  78. {rumdl-0.0.53 → rumdl-0.0.54}/pyproject.toml +0 -0
  79. {rumdl-0.0.53 → rumdl-0.0.54}/python/MANIFEST.in +0 -0
  80. {rumdl-0.0.53 → rumdl-0.0.54}/python/PYTHON-README.md +0 -0
  81. {rumdl-0.0.53 → rumdl-0.0.54}/python/rumdl/__init__.py +0 -0
  82. {rumdl-0.0.53 → rumdl-0.0.54}/python/rumdl/__main__.py +0 -0
  83. {rumdl-0.0.53 → rumdl-0.0.54}/python/rumdl/py.typed +0 -0
  84. {rumdl-0.0.53 → rumdl-0.0.54}/rumdl.toml.example +0 -0
  85. {rumdl-0.0.53 → rumdl-0.0.54}/src/config.rs +0 -0
  86. {rumdl-0.0.53 → rumdl-0.0.54}/src/init.rs +0 -0
  87. {rumdl-0.0.53 → rumdl-0.0.54}/src/lib.rs +0 -0
  88. {rumdl-0.0.53 → rumdl-0.0.54}/src/lint_context.rs +0 -0
  89. {rumdl-0.0.53 → rumdl-0.0.54}/src/main.rs +0 -0
  90. {rumdl-0.0.53 → rumdl-0.0.54}/src/markdownlint_config.rs +0 -0
  91. {rumdl-0.0.53 → rumdl-0.0.54}/src/profiling.rs +0 -0
  92. {rumdl-0.0.53 → rumdl-0.0.54}/src/python.rs +0 -0
  93. {rumdl-0.0.53 → rumdl-0.0.54}/src/rule.rs +0 -0
  94. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/blockquote_utils.rs +0 -0
  95. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/code_block_utils.rs +0 -0
  96. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/code_fence_utils.rs +0 -0
  97. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/emphasis_style.rs +0 -0
  98. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/front_matter_utils.rs +0 -0
  99. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/heading_utils.rs +0 -0
  100. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/list_utils.rs +0 -0
  101. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md001_heading_increment.rs +0 -0
  102. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md002_first_heading_h1.rs +0 -0
  103. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md003_heading_style.rs +0 -0
  104. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md004_unordered_list_style.rs +0 -0
  105. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md005_list_indent.rs +0 -0
  106. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md006_start_bullets.rs +0 -0
  107. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md008_ul_style.rs +0 -0
  108. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md009_trailing_spaces.rs +0 -0
  109. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md010_no_hard_tabs.rs +0 -0
  110. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md011_no_reversed_links.rs +0 -0
  111. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md012_no_multiple_blanks.rs +0 -0
  112. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md013_line_length.rs +0 -0
  113. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md014_commands_show_output.rs +0 -0
  114. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md015_no_missing_space_after_list_marker.rs +0 -0
  115. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md018_no_missing_space_atx.rs +0 -0
  116. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md019_no_multiple_space_atx.rs +0 -0
  117. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
  118. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
  119. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md022_blanks_around_headings.rs +0 -0
  120. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md023_heading_start_left.rs +0 -0
  121. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md024_no_duplicate_heading.rs +0 -0
  122. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md025_single_title.rs +0 -0
  123. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md026_no_trailing_punctuation.rs +0 -0
  124. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
  125. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md028_no_blanks_blockquote.rs +0 -0
  126. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md029_ordered_list_prefix.rs +0 -0
  127. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md030_list_marker_space.rs +0 -0
  128. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md031_blanks_around_fences.rs +0 -0
  129. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md032_blanks_around_lists.rs +0 -0
  130. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md033_no_inline_html.rs +0 -0
  131. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md034_no_bare_urls.rs +0 -0
  132. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md036_no_emphasis_only_first.rs +0 -0
  133. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md037_spaces_around_emphasis.rs +0 -0
  134. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md038_no_space_in_code.rs +0 -0
  135. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md039_no_space_in_links.rs +0 -0
  136. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md040_fenced_code_language.rs +0 -0
  137. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md041_first_line_heading.rs +0 -0
  138. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md042_no_empty_links.rs +0 -0
  139. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md043_required_headings.rs +0 -0
  140. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md044_proper_names.rs +0 -0
  141. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md045_no_alt_text.rs +0 -0
  142. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md046_code_block_style.rs +0 -0
  143. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md047_single_trailing_newline.rs +0 -0
  144. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md048_code_fence_style.rs +0 -0
  145. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md049_emphasis_style.rs +0 -0
  146. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md050_strong_style.rs +0 -0
  147. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md051_link_fragments.rs +0 -0
  148. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md052_reference_links_images.rs +0 -0
  149. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md053_link_image_reference_definitions.rs +0 -0
  150. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md054_link_image_style.rs +0 -0
  151. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md055_table_pipe_style.rs +0 -0
  152. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md056_table_column_count.rs +0 -0
  153. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md057_existing_relative_links.rs +0 -0
  154. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/md058_blanks_around_tables.rs +0 -0
  155. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/mod.rs +0 -0
  156. {rumdl-0.0.53 → rumdl-0.0.54}/src/rules/strong_style.rs +0 -0
  157. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/code_block_utils.rs +0 -0
  158. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/document_structure.rs +0 -0
  159. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/early_returns.rs +0 -0
  160. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/markdown_elements.rs +0 -0
  161. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/mod.rs +0 -0
  162. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/range_utils.rs +0 -0
  163. {rumdl-0.0.53 → rumdl-0.0.54}/src/utils/regex_cache.rs +0 -0
  164. {rumdl-0.0.53 → rumdl-0.0.54}/tests/advanced_integration_tests.rs +0 -0
  165. {rumdl-0.0.53 → rumdl-0.0.54}/tests/cli_duplication_test.rs +0 -0
  166. {rumdl-0.0.53 → rumdl-0.0.54}/tests/cli_integration_tests.rs +0 -0
  167. {rumdl-0.0.53 → rumdl-0.0.54}/tests/commonmark_compliance_tests.rs +0 -0
  168. {rumdl-0.0.53 → rumdl-0.0.54}/tests/comprehensive_integration_tests.rs +0 -0
  169. {rumdl-0.0.53 → rumdl-0.0.54}/tests/config_application_tests.rs +0 -0
  170. {rumdl-0.0.53 → rumdl-0.0.54}/tests/config_tests.rs +0 -0
  171. {rumdl-0.0.53 → rumdl-0.0.54}/tests/init_command_test.rs +0 -0
  172. {rumdl-0.0.53 → rumdl-0.0.54}/tests/init_tests.rs +0 -0
  173. {rumdl-0.0.53 → rumdl-0.0.54}/tests/integration_tests.rs +0 -0
  174. {rumdl-0.0.53 → rumdl-0.0.54}/tests/lib.rs +0 -0
  175. {rumdl-0.0.53 → rumdl-0.0.54}/tests/markdownlint_cli_integration.rs +0 -0
  176. {rumdl-0.0.53 → rumdl-0.0.54}/tests/markdownlint_config_test.rs +0 -0
  177. {rumdl-0.0.53 → rumdl-0.0.54}/tests/md030_edge_cases.md +0 -0
  178. {rumdl-0.0.53 → rumdl-0.0.54}/tests/output_format_tests.rs +0 -0
  179. {rumdl-0.0.53 → rumdl-0.0.54}/tests/perf_check.rs +0 -0
  180. {rumdl-0.0.53 → rumdl-0.0.54}/tests/pyproject_config_tests.rs +0 -0
  181. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md001_test.rs +0 -0
  182. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md001_unicode_test.rs +0 -0
  183. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md002_test.rs +0 -0
  184. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md003_test.rs +0 -0
  185. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md004_test.rs +0 -0
  186. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md005_test.rs +0 -0
  187. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md006_test.rs +0 -0
  188. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md006_unicode_test.rs +0 -0
  189. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md008_test.rs +0 -0
  190. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md009_test.rs +0 -0
  191. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md010_test.rs +0 -0
  192. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md011_test.rs +0 -0
  193. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md012_test.rs +0 -0
  194. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md013_test.rs +0 -0
  195. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md014_test.rs +0 -0
  196. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md015_proptest.rs +0 -0
  197. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md015_test.rs +0 -0
  198. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md018_test.rs +0 -0
  199. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md019_test.rs +0 -0
  200. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md020_test.rs +0 -0
  201. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md021_test.rs +0 -0
  202. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md022_test.rs +0 -0
  203. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md023_extended_test.rs +0 -0
  204. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md023_test.rs +0 -0
  205. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md024_test.rs +0 -0
  206. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md025_test.rs +0 -0
  207. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md026_test.rs +0 -0
  208. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md027_test.rs +0 -0
  209. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md028_test.rs +0 -0
  210. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md029_test.rs +0 -0
  211. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md030_test.rs +0 -0
  212. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md031_test.rs +0 -0
  213. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md032_test.rs +0 -0
  214. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md033_extended_test.rs +0 -0
  215. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md033_test.rs +0 -0
  216. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md034_test.rs +0 -0
  217. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md036_test.rs +0 -0
  218. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md037_test.rs +0 -0
  219. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md038_test.rs +0 -0
  220. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md039_test.rs +0 -0
  221. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md040_test.rs +0 -0
  222. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md041_test.rs +0 -0
  223. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md042_test.rs +0 -0
  224. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md043_test.rs +0 -0
  225. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md044_test.rs +0 -0
  226. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md045_test.rs +0 -0
  227. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md046_test.rs +0 -0
  228. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md047_test.rs +0 -0
  229. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md048_test.rs +0 -0
  230. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md049_test.rs +0 -0
  231. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md050_test.rs +0 -0
  232. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md051_test.rs +0 -0
  233. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md052_test.rs +0 -0
  234. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md053_additional_test.rs +0 -0
  235. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md053_proptest.rs +0 -0
  236. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md053_test.rs +0 -0
  237. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md054_test.rs +0 -0
  238. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md054_unicode_test.rs +0 -0
  239. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md055_test.rs +0 -0
  240. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md056_test.rs +0 -0
  241. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md057_test.rs +0 -0
  242. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/md058_test.rs +0 -0
  243. {rumdl-0.0.53 → rumdl-0.0.54}/tests/rules/mod.rs +0 -0
  244. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/blockquote_utils_test.rs +0 -0
  245. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/code_block_utils_extended_test.rs +0 -0
  246. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/code_block_utils_test.rs +0 -0
  247. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/core_utils_test.rs +0 -0
  248. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/front_matter_utils_test.rs +0 -0
  249. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/line_index_test.rs +0 -0
  250. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils/mod.rs +0 -0
  251. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils_markdown_edge_cases.rs +0 -0
  252. {rumdl-0.0.53 → rumdl-0.0.54}/tests/utils_tests.rs +0 -0
@@ -1385,7 +1385,7 @@ dependencies = [
1385
1385
 
1386
1386
  [[package]]
1387
1387
  name = "rumdl"
1388
- version = "0.0.53"
1388
+ version = "0.0.54"
1389
1389
  dependencies = [
1390
1390
  "anyhow",
1391
1391
  "assert_cmd",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rumdl"
3
- version = "0.0.53"
3
+ version = "0.0.54"
4
4
  edition = "2021"
5
5
  description = "A fast Markdown linter written in Rust (Ru(st) MarkDown Linter)"
6
6
  authors = ["Ruben J. Jongejan <ruben.jongejan@gmail.com>"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rumdl
3
- Version: 0.0.53
3
+ Version: 0.0.54
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -0,0 +1,222 @@
1
+ /// Rule MD007: Unordered list indentation
2
+ ///
3
+ /// See [docs/md007.md](../../docs/md007.md) for full documentation, configuration, and examples.
4
+ use crate::rule::{Fix, LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
5
+ use crate::utils::document_structure::{DocumentStructure, DocumentStructureExtensions};
6
+ use crate::utils::element_cache::{ElementCache, ListMarkerType};
7
+ use crate::utils::range_utils::LineIndex;
8
+ use lazy_static::lazy_static;
9
+ use regex::Regex;
10
+ use toml;
11
+
12
+ #[derive(Debug, Clone)]
13
+ pub struct MD007ULIndent {
14
+ pub indent: usize,
15
+ }
16
+
17
+ impl Default for MD007ULIndent {
18
+ fn default() -> Self {
19
+ Self { indent: 2 }
20
+ }
21
+ }
22
+
23
+ impl MD007ULIndent {
24
+ pub fn new(indent: usize) -> Self {
25
+ Self { indent }
26
+ }
27
+
28
+ /// Detect code blocks, including those inside blockquotes
29
+ fn compute_code_block_lines(content: &str) -> std::collections::HashSet<usize> {
30
+ let mut code_block_lines = std::collections::HashSet::new();
31
+ let mut in_code_block = false;
32
+ let mut fence: Option<String> = None;
33
+ for (i, line) in content.lines().enumerate() {
34
+ let trimmed = line.trim_start();
35
+ if !in_code_block {
36
+ if trimmed.starts_with("```") || trimmed.starts_with("~~~") {
37
+ in_code_block = true;
38
+ fence = Some(trimmed[..3].to_string());
39
+ code_block_lines.insert(i + 1);
40
+ continue;
41
+ }
42
+ } else {
43
+ code_block_lines.insert(i + 1);
44
+ if let Some(ref f) = fence {
45
+ if trimmed.starts_with(f) {
46
+ in_code_block = false;
47
+ fence = None;
48
+ }
49
+ }
50
+ continue;
51
+ }
52
+ }
53
+ code_block_lines
54
+ }
55
+
56
+ #[allow(dead_code)]
57
+ fn is_in_code_block(content: &str, line_idx: usize) -> bool {
58
+ lazy_static! {
59
+ static ref CODE_BLOCK_MARKER: Regex = Regex::new(r"^(```|~~~)").unwrap();
60
+ }
61
+
62
+ let lines: Vec<&str> = content.lines().collect();
63
+ let mut in_code_block = false;
64
+
65
+ for (i, line) in lines.iter().enumerate() {
66
+ if i > line_idx {
67
+ break;
68
+ }
69
+
70
+ if CODE_BLOCK_MARKER.is_match(line.trim_start()) {
71
+ in_code_block = !in_code_block;
72
+ }
73
+
74
+ if i == line_idx {
75
+ return in_code_block;
76
+ }
77
+ }
78
+
79
+ false
80
+ }
81
+ }
82
+
83
+ impl Rule for MD007ULIndent {
84
+ fn name(&self) -> &'static str {
85
+ "MD007"
86
+ }
87
+
88
+ fn description(&self) -> &'static str {
89
+ "Unordered list indentation"
90
+ }
91
+
92
+ fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
93
+ let content = ctx.content;
94
+ let element_cache = ElementCache::new(content);
95
+ let mut warnings = Vec::new();
96
+ for item in element_cache.get_list_items() {
97
+ // Only unordered list items
98
+ // Skip list items inside code blocks (including YAML/front matter)
99
+ if element_cache.is_in_code_block(item.line_number) {
100
+ continue;
101
+ }
102
+ if matches!(item.marker_type, ListMarkerType::Asterisk | ListMarkerType::Plus | ListMarkerType::Minus) {
103
+ let expected_indent = item.nesting_level * self.indent;
104
+ println!(
105
+ "MD007 DEBUG: line {} | indent={} | nesting={} | expected={} | emit_warning={}",
106
+ item.line_number,
107
+ item.indentation,
108
+ item.nesting_level,
109
+ expected_indent,
110
+ item.indentation != expected_indent
111
+ );
112
+ if item.indentation != expected_indent {
113
+ warnings.push(LintWarning {
114
+ rule_name: Some(self.name()),
115
+ message: format!(
116
+ "Incorrect indentation: expected {} spaces for nesting level {}, found {}",
117
+ expected_indent, item.nesting_level, item.indentation
118
+ ),
119
+ line: item.line_number,
120
+ column: item.blockquote_prefix.len() + item.indentation + 1, // correct column for marker
121
+ severity: Severity::Warning,
122
+ fix: None,
123
+ });
124
+ }
125
+ }
126
+ }
127
+ Ok(warnings)
128
+ }
129
+
130
+ /// Optimized check using document structure
131
+ fn check_with_structure(
132
+ &self,
133
+ ctx: &crate::lint_context::LintContext,
134
+ _doc_structure: &DocumentStructure,
135
+ ) -> LintResult {
136
+ self.check(ctx)
137
+ }
138
+
139
+ fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
140
+ let content = ctx.content;
141
+ let element_cache = ElementCache::new(content);
142
+ let mut lines: Vec<&str> = content.lines().collect();
143
+ for item in element_cache.get_list_items() {
144
+ if element_cache.is_in_code_block(item.line_number) {
145
+ continue;
146
+ }
147
+ if matches!(item.marker_type, ListMarkerType::Asterisk | ListMarkerType::Plus | ListMarkerType::Minus) {
148
+ let expected_indent = item.nesting_level * self.indent;
149
+ if item.indentation != expected_indent {
150
+ // Reconstruct the line: blockquote_prefix + correct_indent + marker + spaces_after_marker + content
151
+ let correct_indent = " ".repeat(expected_indent);
152
+ let marker = &item.marker;
153
+ let after_marker = " ".repeat(item.spaces_after_marker.max(1));
154
+ let new_line = format!(
155
+ "{}{}{}{}{}",
156
+ item.blockquote_prefix,
157
+ correct_indent,
158
+ marker,
159
+ after_marker,
160
+ item.content
161
+ );
162
+ lines[item.line_number - 1] = Box::leak(new_line.into_boxed_str());
163
+ }
164
+ }
165
+ }
166
+ Ok(lines.join("\n"))
167
+ }
168
+
169
+ /// Get the category of this rule for selective processing
170
+ fn category(&self) -> RuleCategory {
171
+ RuleCategory::List
172
+ }
173
+
174
+ /// Check if this rule should be skipped
175
+ fn should_skip(&self, ctx: &crate::lint_context::LintContext) -> bool {
176
+ ctx.content.is_empty()
177
+ || (!ctx.content.contains('*')
178
+ && !ctx.content.contains('-')
179
+ && !ctx.content.contains('+'))
180
+ }
181
+
182
+ fn as_any(&self) -> &dyn std::any::Any {
183
+ self
184
+ }
185
+
186
+ fn default_config_section(&self) -> Option<(String, toml::Value)> {
187
+ let mut map = toml::map::Map::new();
188
+ map.insert(
189
+ "indent".to_string(),
190
+ toml::Value::Integer(self.indent as i64),
191
+ );
192
+ Some((self.name().to_string(), toml::Value::Table(map)))
193
+ }
194
+
195
+ fn from_config(config: &crate::config::Config) -> Box<dyn Rule>
196
+ where
197
+ Self: Sized,
198
+ {
199
+ let indent =
200
+ crate::config::get_rule_config_value::<usize>(config, "MD007", "indent").unwrap_or(2);
201
+ Box::new(MD007ULIndent::new(indent))
202
+ }
203
+ }
204
+
205
+ impl DocumentStructureExtensions for MD007ULIndent {
206
+ fn has_relevant_elements(
207
+ &self,
208
+ _ctx: &crate::lint_context::LintContext,
209
+ doc_structure: &DocumentStructure,
210
+ ) -> bool {
211
+ // Use the document structure to check if there are any unordered list elements
212
+ !doc_structure.list_lines.is_empty()
213
+ }
214
+ }
215
+
216
+ #[cfg(test)]
217
+ mod tests {
218
+ use super::*;
219
+ use crate::lint_context::LintContext;
220
+
221
+ // Remove test_with_document_structure, as the rule no longer uses DocumentStructure for its logic
222
+ }
@@ -57,23 +57,35 @@ impl MD035HRStyle {
57
57
  }
58
58
 
59
59
  let line = lines[i].trim();
60
-
61
60
  let prev_line = lines[i - 1].trim();
62
61
 
63
- // Check if the current line is all dashes or equals signs
64
-
65
62
  let is_dash_line = !line.is_empty() && line.chars().all(|c| c == '-');
66
-
67
63
  let is_equals_line = !line.is_empty() && line.chars().all(|c| c == '=');
68
-
69
- // Check if the previous line is not empty and not a horizontal rule
70
-
71
64
  let prev_line_has_content = !prev_line.is_empty() && !Self::is_horizontal_rule(prev_line);
72
-
73
- // If the current line is all dashes or equals signs and the previous line has content,
74
- // it's likely a Setext heading
75
65
  (is_dash_line || is_equals_line) && prev_line_has_content
76
66
  }
67
+
68
+ /// Find the most prevalent HR style in the document (excluding setext headings)
69
+ fn most_prevalent_hr_style(lines: &[&str]) -> Option<String> {
70
+ use std::collections::HashMap;
71
+ let mut counts: HashMap<&str, usize> = HashMap::new();
72
+ let mut order: Vec<&str> = Vec::new();
73
+ for (i, line) in lines.iter().enumerate() {
74
+ if Self::is_horizontal_rule(line) && !Self::is_potential_setext_heading(lines, i) {
75
+ let style = line.trim();
76
+ let counter = counts.entry(style).or_insert(0);
77
+ *counter += 1;
78
+ if *counter == 1 {
79
+ order.push(style);
80
+ }
81
+ }
82
+ }
83
+ // Find the style with the highest count, breaking ties by first encountered
84
+ counts
85
+ .iter()
86
+ .max_by_key(|&(style, count)| (*count, -(order.iter().position(|&s| s == *style).unwrap_or(usize::MAX) as isize)))
87
+ .map(|(style, _)| style.to_string())
88
+ }
77
89
  }
78
90
 
79
91
  impl Rule for MD035HRStyle {
@@ -90,21 +102,11 @@ impl Rule for MD035HRStyle {
90
102
  let _line_index = LineIndex::new(content.to_string());
91
103
 
92
104
  let mut warnings = Vec::new();
93
-
94
105
  let lines: Vec<&str> = content.lines().collect();
95
106
 
96
- // Use the configured style or find the first HR style
97
-
98
- let expected_style = if self.style.is_empty() {
99
- // Find the first HR in the document
100
- let mut first_style = "---".to_string(); // Default if none found
101
- for (i, line) in lines.iter().enumerate() {
102
- if Self::is_horizontal_rule(line) && !Self::is_potential_setext_heading(&lines, i) {
103
- first_style = line.trim().to_string();
104
- break;
105
- }
106
- }
107
- first_style
107
+ // Use the configured style or find the most prevalent HR style
108
+ let expected_style = if self.style.is_empty() || self.style == "consistent" {
109
+ Self::most_prevalent_hr_style(&lines).unwrap_or_else(|| "---".to_string())
108
110
  } else {
109
111
  self.style.clone()
110
112
  };
@@ -148,21 +150,11 @@ impl Rule for MD035HRStyle {
148
150
  let _line_index = LineIndex::new(content.to_string());
149
151
 
150
152
  let mut result = Vec::new();
151
-
152
153
  let lines: Vec<&str> = content.lines().collect();
153
154
 
154
- // Use the configured style or find the first HR style
155
-
156
- let expected_style = if self.style.is_empty() {
157
- // Find the first HR in the document
158
- let mut first_style = "---".to_string(); // Default if none found
159
- for (i, line) in lines.iter().enumerate() {
160
- if Self::is_horizontal_rule(line) && !Self::is_potential_setext_heading(&lines, i) {
161
- first_style = line.trim().to_string();
162
- break;
163
- }
164
- }
165
- first_style
155
+ // Use the configured style or find the most prevalent HR style
156
+ let expected_style = if self.style.is_empty() || self.style == "consistent" {
157
+ Self::most_prevalent_hr_style(&lines).unwrap_or_else(|| "---".to_string())
166
158
  } else {
167
159
  self.style.clone()
168
160
  };
@@ -174,9 +166,6 @@ impl Rule for MD035HRStyle {
174
166
  continue;
175
167
  }
176
168
 
177
- let _trimmed = line.trim();
178
-
179
- // Simplify the horizontal rule detection and replacement
180
169
  if Self::is_horizontal_rule(line) {
181
170
  // Here we have a proper horizontal rule - replace it with the expected style
182
171
  result.push(expected_style.clone());
@@ -10,8 +10,8 @@ lazy_static! {
10
10
  static ref INDENTED_CODE_BLOCK_REGEX: Regex = Regex::new(r"^(\s{4,})(.+)$").unwrap();
11
11
 
12
12
  // List detection patterns
13
- static ref UNORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>[*+-])(?P<after>[ \t]+)(?P<content>.*)$").unwrap();
14
- static ref ORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>\d+\.)(?P<after>[ \t]+)(?P<content>.*)$").unwrap();
13
+ static ref UNORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>[*+-])(?P<after>[ \t]*)(?P<content>.*)$").unwrap();
14
+ static ref ORDERED_LIST_REGEX: FancyRegex = FancyRegex::new(r"^(?P<indent>[ \t]*)(?P<marker>\d+\.)(?P<after>[ \t]*)(?P<content>.*)$").unwrap();
15
15
 
16
16
  // Inline code span pattern
17
17
  static ref CODE_SPAN_REGEX: Regex = Regex::new(r"`+").unwrap();
@@ -62,6 +62,8 @@ pub struct ListItem {
62
62
  pub spaces_after_marker: usize,
63
63
  pub nesting_level: usize,
64
64
  pub parent_line_number: Option<usize>,
65
+ pub blockquote_depth: usize, // Number of leading blockquote markers
66
+ pub blockquote_prefix: String, // The actual prefix (e.g., "> > ")
65
67
  }
66
68
 
67
69
  /// Cache for Markdown document structural elements
@@ -279,31 +281,92 @@ impl ElementCache {
279
281
  fn populate_list_items(&mut self) {
280
282
  if let Some(content) = &self.content {
281
283
  let lines: Vec<&str> = content.lines().collect();
282
- let mut prev_items: Vec<(usize, usize)> = Vec::new(); // (nesting_level, line_number)
284
+ let mut prev_items: Vec<(usize, usize, usize)> = Vec::new(); // (blockquote_depth, nesting_level, line_number)
283
285
  for (i, line) in lines.iter().enumerate() {
284
286
  // Reset stack on blank lines
285
287
  if line.trim().is_empty() {
286
288
  prev_items.clear(); // Reset nesting after blank line
287
289
  continue;
288
290
  }
289
- // Always try to match a list item, regardless of indentation
290
- if let Some(item) = self.parse_list_item(line, i + 1, &mut prev_items) {
291
+ // Parse and strip blockquote prefix
292
+ let (blockquote_depth, blockquote_prefix, rest) = Self::parse_blockquote_prefix(line);
293
+ // Always call parse_list_item and always push if Some
294
+ if let Some(item) = self.parse_list_item(rest, i + 1, &mut prev_items, blockquote_depth, blockquote_prefix.clone()) {
291
295
  self.list_items.push(item);
292
296
  self.list_line_map[i] = true;
293
- continue; // If it's a list item, do not treat as code block
294
297
  }
295
- // If not a list item, mark as code block if indented (4+ spaces)
296
- // (This is for completeness, but code block map is handled elsewhere)
297
298
  }
298
299
  }
299
300
  }
300
301
 
302
+ /// Parse and strip all leading blockquote markers, returning (depth, prefix, rest_of_line)
303
+ fn parse_blockquote_prefix(line: &str) -> (usize, String, &str) {
304
+ let mut rest = line;
305
+ let mut prefix = String::new();
306
+ let mut depth = 0;
307
+ loop {
308
+ let trimmed = rest.trim_start();
309
+ if trimmed.starts_with('>') {
310
+ // Find the '>' and a single optional space
311
+ let after = &trimmed[1..];
312
+ let mut chars = after.chars();
313
+ let mut space_count = 0;
314
+ if let Some(' ') = chars.next() {
315
+ space_count = 1;
316
+ }
317
+ let (spaces, after_marker) = after.split_at(space_count);
318
+ prefix.push('>');
319
+ prefix.push_str(spaces);
320
+ rest = after_marker;
321
+ depth += 1;
322
+ } else {
323
+ break;
324
+ }
325
+ }
326
+ (depth, prefix, rest)
327
+ }
328
+
329
+ /// Calculate the nesting level for a list item, considering blockquote depth
330
+ fn calculate_nesting_level(
331
+ &self,
332
+ indent: usize,
333
+ blockquote_depth: usize,
334
+ prev_items: &mut Vec<(usize, usize, usize)>,
335
+ ) -> usize {
336
+ // Only consider previous items with the same blockquote depth
337
+ let mut nesting_level = 0;
338
+ if let Some(&(last_bq, last_indent, last_level)) = prev_items.iter().rev().find(|(bq, _, _)| *bq == blockquote_depth) {
339
+ if indent >= last_indent + 2 {
340
+ nesting_level = last_level + 1;
341
+ } else {
342
+ // Walk back to find the most recent indent <= current indent with same blockquote depth
343
+ for &(prev_bq, prev_indent, prev_level) in prev_items.iter().rev() {
344
+ if prev_bq == blockquote_depth && prev_indent <= indent {
345
+ nesting_level = prev_level;
346
+ break;
347
+ }
348
+ }
349
+ }
350
+ }
351
+ // Remove stack entries with indent >= current indent and same blockquote depth
352
+ while let Some(&(prev_bq, prev_indent, _)) = prev_items.last() {
353
+ if prev_bq != blockquote_depth || prev_indent < indent {
354
+ break;
355
+ }
356
+ prev_items.pop();
357
+ }
358
+ prev_items.push((blockquote_depth, indent, nesting_level));
359
+ nesting_level
360
+ }
361
+
301
362
  /// Parse a line as a list item and determine its nesting level
302
363
  fn parse_list_item(
303
364
  &self,
304
365
  line: &str,
305
366
  line_num: usize,
306
- prev_items: &mut Vec<(usize, usize)>,
367
+ prev_items: &mut Vec<(usize, usize, usize)>,
368
+ blockquote_depth: usize,
369
+ blockquote_prefix: String,
307
370
  ) -> Option<ListItem> {
308
371
  match UNORDERED_LIST_REGEX.captures(line) {
309
372
  Ok(Some(captures)) => {
@@ -323,13 +386,13 @@ impl ElementCache {
323
386
  "-" => ListMarkerType::Minus,
324
387
  _ => unreachable!(),
325
388
  };
326
- let nesting_level = self.calculate_nesting_level(indentation, prev_items);
327
- // Find parent: most recent previous item with lower nesting_level
389
+ let nesting_level = self.calculate_nesting_level(indentation, blockquote_depth, prev_items);
390
+ // Find parent: most recent previous item with lower nesting_level and same blockquote depth
328
391
  let parent_line_number = prev_items
329
392
  .iter()
330
393
  .rev()
331
- .find(|(level, _)| *level < nesting_level)
332
- .map(|(_, line_num)| *line_num);
394
+ .find(|(bq, _, level)| *bq == blockquote_depth && *level < nesting_level)
395
+ .map(|(_, _, line_num)| *line_num);
333
396
  return Some(ListItem {
334
397
  line_number: line_num,
335
398
  indentation,
@@ -340,6 +403,8 @@ impl ElementCache {
340
403
  spaces_after_marker: spaces,
341
404
  nesting_level,
342
405
  parent_line_number,
406
+ blockquote_depth,
407
+ blockquote_prefix,
343
408
  });
344
409
  }
345
410
  Ok(None) => {
@@ -361,13 +426,13 @@ impl ElementCache {
361
426
  .map_or("", |m| m.as_str())
362
427
  .trim_start()
363
428
  .to_string();
364
- let nesting_level = self.calculate_nesting_level(indentation, prev_items);
365
- // Find parent: most recent previous item with lower nesting_level
429
+ let nesting_level = self.calculate_nesting_level(indentation, blockquote_depth, prev_items);
430
+ // Find parent: most recent previous item with lower nesting_level and same blockquote depth
366
431
  let parent_line_number = prev_items
367
432
  .iter()
368
433
  .rev()
369
- .find(|(level, _)| *level < nesting_level)
370
- .map(|(_, line_num)| *line_num);
434
+ .find(|(bq, _, level)| *bq == blockquote_depth && *level < nesting_level)
435
+ .map(|(_, _, line_num)| *line_num);
371
436
  return Some(ListItem {
372
437
  line_number: line_num,
373
438
  indentation,
@@ -378,6 +443,8 @@ impl ElementCache {
378
443
  spaces_after_marker: spaces,
379
444
  nesting_level,
380
445
  parent_line_number,
446
+ blockquote_depth,
447
+ blockquote_prefix,
381
448
  });
382
449
  }
383
450
  Ok(None) => {}
@@ -385,39 +452,6 @@ impl ElementCache {
385
452
  }
386
453
  None
387
454
  }
388
-
389
- /// Calculate the nesting level for a list item
390
- fn calculate_nesting_level(
391
- &self,
392
- indent: usize,
393
- prev_items: &mut Vec<(usize, usize)>,
394
- ) -> usize {
395
- // CommonMark: a list item is nested if it is indented at least 2 spaces more than the previous list item
396
- let mut nesting_level = 0;
397
- if let Some(&(last_indent, last_level)) = prev_items.last() {
398
- if indent >= last_indent + 2 {
399
- // Nested under previous
400
- nesting_level = last_level + 1;
401
- } else {
402
- // Walk back to find the most recent indent <= current indent
403
- for &(prev_indent, prev_level) in prev_items.iter().rev() {
404
- if prev_indent <= indent {
405
- nesting_level = prev_level;
406
- break;
407
- }
408
- }
409
- }
410
- }
411
- // Remove stack entries with indent >= current indent
412
- while let Some(&(prev_indent, _)) = prev_items.last() {
413
- if prev_indent < indent {
414
- break;
415
- }
416
- prev_items.pop();
417
- }
418
- prev_items.push((indent, nesting_level));
419
- nesting_level
420
- }
421
455
  }
422
456
 
423
457
  // Thread-local cache for sharing across rules