rumdl 0.0.58__tar.gz → 0.0.59__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 (244) hide show
  1. {rumdl-0.0.58 → rumdl-0.0.59}/Cargo.lock +1 -1
  2. {rumdl-0.0.58 → rumdl-0.0.59}/Cargo.toml +1 -1
  3. {rumdl-0.0.58 → rumdl-0.0.59}/PKG-INFO +1 -1
  4. {rumdl-0.0.58 → rumdl-0.0.59}/src/main.rs +3 -3
  5. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/blockquote_utils.rs +22 -6
  6. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md001_heading_increment.rs +9 -68
  7. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md002_first_heading_h1.rs +4 -0
  8. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md003_heading_style.rs +17 -57
  9. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md004_unordered_list_style.rs +14 -0
  10. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md005_list_indent.rs +5 -1
  11. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md006_start_bullets.rs +4 -0
  12. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md007_ul_indent.rs +4 -0
  13. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md012_no_multiple_blanks.rs +32 -2
  14. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md013_line_length.rs +37 -3
  15. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md018_no_missing_space_atx.rs +11 -0
  16. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md019_no_multiple_space_atx.rs +11 -0
  17. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md020_no_missing_space_closed_atx.rs +4 -0
  18. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md021_no_multiple_space_closed_atx.rs +4 -0
  19. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md022_blanks_around_headings.rs +30 -28
  20. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md023_heading_start_left.rs +4 -0
  21. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md024_no_duplicate_heading.rs +40 -1
  22. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md025_single_title.rs +16 -0
  23. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md026_no_trailing_punctuation.rs +76 -2
  24. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md028_no_blanks_blockquote.rs +33 -54
  25. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md029_ordered_list_prefix.rs +45 -115
  26. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md030_list_marker_space.rs +4 -0
  27. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md032_blanks_around_lists.rs +72 -6
  28. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md033_no_inline_html.rs +29 -21
  29. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md034_no_bare_urls.rs +44 -3
  30. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md037_spaces_around_emphasis.rs +20 -209
  31. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md038_no_space_in_code.rs +10 -1
  32. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md039_no_space_in_links.rs +4 -0
  33. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md044_proper_names.rs +30 -4
  34. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md046_code_block_style.rs +65 -117
  35. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md051_link_fragments.rs +101 -111
  36. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/document_structure.rs +68 -26
  37. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/regex_cache.rs +40 -2
  38. {rumdl-0.0.58 → rumdl-0.0.59}/tests/advanced_integration_tests.rs +1 -1
  39. {rumdl-0.0.58 → rumdl-0.0.59}/.rumdl.toml +0 -0
  40. {rumdl-0.0.58 → rumdl-0.0.59}/MANIFEST.in +0 -0
  41. {rumdl-0.0.58 → rumdl-0.0.59}/Makefile +0 -0
  42. {rumdl-0.0.58 → rumdl-0.0.59}/README.md +0 -0
  43. {rumdl-0.0.58 → rumdl-0.0.59}/assets/logo.png +0 -0
  44. {rumdl-0.0.58 → rumdl-0.0.59}/benches/range_performance.rs +0 -0
  45. {rumdl-0.0.58 → rumdl-0.0.59}/benches/range_utils_benchmark.rs +0 -0
  46. {rumdl-0.0.58 → rumdl-0.0.59}/benches/rule_performance.rs +0 -0
  47. {rumdl-0.0.58 → rumdl-0.0.59}/docs/RULES.md +0 -0
  48. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md001.md +0 -0
  49. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md002.md +0 -0
  50. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md003.md +0 -0
  51. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md004.md +0 -0
  52. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md005.md +0 -0
  53. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md006.md +0 -0
  54. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md007.md +0 -0
  55. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md009.md +0 -0
  56. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md010.md +0 -0
  57. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md011.md +0 -0
  58. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md012.md +0 -0
  59. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md013.md +0 -0
  60. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md014.md +0 -0
  61. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md018.md +0 -0
  62. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md019.md +0 -0
  63. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md020.md +0 -0
  64. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md021.md +0 -0
  65. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md022.md +0 -0
  66. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md023.md +0 -0
  67. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md024.md +0 -0
  68. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md025.md +0 -0
  69. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md026.md +0 -0
  70. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md027.md +0 -0
  71. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md028.md +0 -0
  72. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md029.md +0 -0
  73. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md030.md +0 -0
  74. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md031.md +0 -0
  75. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md032.md +0 -0
  76. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md033.md +0 -0
  77. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md034.md +0 -0
  78. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md035.md +0 -0
  79. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md036.md +0 -0
  80. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md037.md +0 -0
  81. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md038.md +0 -0
  82. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md039.md +0 -0
  83. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md040.md +0 -0
  84. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md041.md +0 -0
  85. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md042.md +0 -0
  86. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md043.md +0 -0
  87. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md044.md +0 -0
  88. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md045.md +0 -0
  89. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md046.md +0 -0
  90. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md047.md +0 -0
  91. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md048.md +0 -0
  92. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md049.md +0 -0
  93. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md050.md +0 -0
  94. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md051.md +0 -0
  95. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md052.md +0 -0
  96. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md053.md +0 -0
  97. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md054.md +0 -0
  98. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md055.md +0 -0
  99. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md056.md +0 -0
  100. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md057.md +0 -0
  101. {rumdl-0.0.58 → rumdl-0.0.59}/docs/md058.md +0 -0
  102. {rumdl-0.0.58 → rumdl-0.0.59}/issues/plan-rule-parity-with-markdownlint.md +0 -0
  103. {rumdl-0.0.58 → rumdl-0.0.59}/parity_check.py +0 -0
  104. {rumdl-0.0.58 → rumdl-0.0.59}/pyproject.toml +0 -0
  105. {rumdl-0.0.58 → rumdl-0.0.59}/python/MANIFEST.in +0 -0
  106. {rumdl-0.0.58 → rumdl-0.0.59}/python/PYTHON-README.md +0 -0
  107. {rumdl-0.0.58 → rumdl-0.0.59}/python/rumdl/__init__.py +0 -0
  108. {rumdl-0.0.58 → rumdl-0.0.59}/python/rumdl/__main__.py +0 -0
  109. {rumdl-0.0.58 → rumdl-0.0.59}/python/rumdl/py.typed +0 -0
  110. {rumdl-0.0.58 → rumdl-0.0.59}/rumdl.toml.example +0 -0
  111. {rumdl-0.0.58 → rumdl-0.0.59}/src/config.rs +0 -0
  112. {rumdl-0.0.58 → rumdl-0.0.59}/src/init.rs +0 -0
  113. {rumdl-0.0.58 → rumdl-0.0.59}/src/lib.rs +0 -0
  114. {rumdl-0.0.58 → rumdl-0.0.59}/src/lint_context.rs +0 -0
  115. {rumdl-0.0.58 → rumdl-0.0.59}/src/markdownlint_config.rs +0 -0
  116. {rumdl-0.0.58 → rumdl-0.0.59}/src/profiling.rs +0 -0
  117. {rumdl-0.0.58 → rumdl-0.0.59}/src/python.rs +0 -0
  118. {rumdl-0.0.58 → rumdl-0.0.59}/src/rule.rs +0 -0
  119. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/code_block_utils.rs +0 -0
  120. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/code_fence_utils.rs +0 -0
  121. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/emphasis_style.rs +0 -0
  122. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/front_matter_utils.rs +0 -0
  123. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/heading_utils.rs +0 -0
  124. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/list_utils.rs +0 -0
  125. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md009_trailing_spaces.rs +0 -0
  126. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md010_no_hard_tabs.rs +0 -0
  127. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md011_no_reversed_links.rs +0 -0
  128. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md014_commands_show_output.rs +0 -0
  129. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
  130. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md031_blanks_around_fences.rs +0 -0
  131. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md035_hr_style.rs +0 -0
  132. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md036_no_emphasis_only_first.rs +0 -0
  133. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md040_fenced_code_language.rs +0 -0
  134. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md041_first_line_heading.rs +0 -0
  135. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md042_no_empty_links.rs +0 -0
  136. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md043_required_headings.rs +0 -0
  137. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md045_no_alt_text.rs +0 -0
  138. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md047_single_trailing_newline.rs +0 -0
  139. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md048_code_fence_style.rs +0 -0
  140. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md049_emphasis_style.rs +0 -0
  141. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md050_strong_style.rs +0 -0
  142. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md052_reference_links_images.rs +0 -0
  143. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md053_link_image_reference_definitions.rs +0 -0
  144. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md054_link_image_style.rs +0 -0
  145. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md055_table_pipe_style.rs +0 -0
  146. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md056_table_column_count.rs +0 -0
  147. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md057_existing_relative_links.rs +0 -0
  148. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/md058_blanks_around_tables.rs +0 -0
  149. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/mod.rs +0 -0
  150. {rumdl-0.0.58 → rumdl-0.0.59}/src/rules/strong_style.rs +0 -0
  151. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/code_block_utils.rs +0 -0
  152. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/early_returns.rs +0 -0
  153. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/element_cache.rs +0 -0
  154. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/markdown_elements.rs +0 -0
  155. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/mod.rs +0 -0
  156. {rumdl-0.0.58 → rumdl-0.0.59}/src/utils/range_utils.rs +0 -0
  157. {rumdl-0.0.58 → rumdl-0.0.59}/tests/cli_duplication_test.rs +0 -0
  158. {rumdl-0.0.58 → rumdl-0.0.59}/tests/cli_integration_tests.rs +0 -0
  159. {rumdl-0.0.58 → rumdl-0.0.59}/tests/commonmark_compliance_tests.rs +0 -0
  160. {rumdl-0.0.58 → rumdl-0.0.59}/tests/comprehensive_integration_tests.rs +0 -0
  161. {rumdl-0.0.58 → rumdl-0.0.59}/tests/config_application_tests.rs +0 -0
  162. {rumdl-0.0.58 → rumdl-0.0.59}/tests/config_tests.rs +0 -0
  163. {rumdl-0.0.58 → rumdl-0.0.59}/tests/init_command_test.rs +0 -0
  164. {rumdl-0.0.58 → rumdl-0.0.59}/tests/init_tests.rs +0 -0
  165. {rumdl-0.0.58 → rumdl-0.0.59}/tests/integration_tests.rs +0 -0
  166. {rumdl-0.0.58 → rumdl-0.0.59}/tests/json_output_test.rs +0 -0
  167. {rumdl-0.0.58 → rumdl-0.0.59}/tests/lib.rs +0 -0
  168. {rumdl-0.0.58 → rumdl-0.0.59}/tests/markdownlint_cli_integration.rs +0 -0
  169. {rumdl-0.0.58 → rumdl-0.0.59}/tests/markdownlint_config_test.rs +0 -0
  170. {rumdl-0.0.58 → rumdl-0.0.59}/tests/md030_edge_cases.md +0 -0
  171. {rumdl-0.0.58 → rumdl-0.0.59}/tests/output_format_tests.rs +0 -0
  172. {rumdl-0.0.58 → rumdl-0.0.59}/tests/perf_check.rs +0 -0
  173. {rumdl-0.0.58 → rumdl-0.0.59}/tests/pyproject_config_tests.rs +0 -0
  174. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md001_test.rs +0 -0
  175. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md001_unicode_test.rs +0 -0
  176. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md002_test.rs +0 -0
  177. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md003_test.rs +0 -0
  178. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md004_test.rs +0 -0
  179. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md005_test.rs +0 -0
  180. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md006_test.rs +0 -0
  181. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md006_unicode_test.rs +0 -0
  182. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md007_test.rs +0 -0
  183. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md009_test.rs +0 -0
  184. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md010_test.rs +0 -0
  185. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md011_test.rs +0 -0
  186. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md012_test.rs +0 -0
  187. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md013_test.rs +0 -0
  188. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md014_test.rs +0 -0
  189. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md018_test.rs +0 -0
  190. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md019_test.rs +0 -0
  191. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md020_test.rs +0 -0
  192. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md021_test.rs +0 -0
  193. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md022_test.rs +0 -0
  194. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md023_extended_test.rs +0 -0
  195. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md023_test.rs +0 -0
  196. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md024_test.rs +0 -0
  197. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md025_test.rs +0 -0
  198. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md026_test.rs +0 -0
  199. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md027_test.rs +0 -0
  200. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md028_test.rs +0 -0
  201. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md029_test.rs +0 -0
  202. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md030_test.rs +0 -0
  203. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md031_test.rs +0 -0
  204. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md032_test.rs +0 -0
  205. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md033_extended_test.rs +0 -0
  206. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md033_test.rs +0 -0
  207. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md034_test.rs +0 -0
  208. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md035_test.rs +0 -0
  209. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md036_test.rs +0 -0
  210. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md037_test.rs +0 -0
  211. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md038_test.rs +0 -0
  212. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md039_test.rs +0 -0
  213. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md040_test.rs +0 -0
  214. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md041_test.rs +0 -0
  215. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md042_test.rs +0 -0
  216. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md043_test.rs +0 -0
  217. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md044_test.rs +0 -0
  218. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md045_test.rs +0 -0
  219. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md046_test.rs +0 -0
  220. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md047_test.rs +0 -0
  221. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md048_test.rs +0 -0
  222. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md049_test.rs +0 -0
  223. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md050_test.rs +0 -0
  224. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md051_test.rs +0 -0
  225. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md052_test.rs +0 -0
  226. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md053_additional_test.rs +0 -0
  227. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md053_proptest.rs +0 -0
  228. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md053_test.rs +0 -0
  229. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md054_test.rs +0 -0
  230. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md054_unicode_test.rs +0 -0
  231. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md055_test.rs +0 -0
  232. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md056_test.rs +0 -0
  233. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md057_test.rs +0 -0
  234. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/md058_test.rs +0 -0
  235. {rumdl-0.0.58 → rumdl-0.0.59}/tests/rules/mod.rs +0 -0
  236. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils/blockquote_utils_test.rs +0 -0
  237. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils/code_block_utils_extended_test.rs +0 -0
  238. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils/code_block_utils_test.rs +0 -0
  239. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils/core_utils_test.rs +0 -0
  240. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils/front_matter_utils_test.rs +0 -0
  241. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils/line_index_test.rs +0 -0
  242. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils/mod.rs +0 -0
  243. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils_markdown_edge_cases.rs +0 -0
  244. {rumdl-0.0.58 → rumdl-0.0.59}/tests/utils_tests.rs +0 -0
@@ -1368,7 +1368,7 @@ dependencies = [
1368
1368
 
1369
1369
  [[package]]
1370
1370
  name = "rumdl"
1371
- version = "0.0.58"
1371
+ version = "0.0.59"
1372
1372
  dependencies = [
1373
1373
  "anyhow",
1374
1374
  "assert_cmd",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rumdl"
3
- version = "0.0.58"
3
+ version = "0.0.59"
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.58
3
+ Version: 0.0.59
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -1388,11 +1388,11 @@ fn process_file(
1388
1388
  // Fix issues if requested
1389
1389
  let mut warnings_fixed = 0;
1390
1390
  if _fix {
1391
- // Skip rules that don't have fixes
1391
+ // Apply fixes for rules that have warnings, regardless of whether individual warnings have fixes
1392
1392
  for rule in rules {
1393
1393
  if all_warnings
1394
1394
  .iter()
1395
- .any(|w| w.rule_name == Some(rule.name()) && w.fix.is_some())
1395
+ .any(|w| w.rule_name == Some(rule.name()))
1396
1396
  {
1397
1397
  let ctx = LintContext::new(&content);
1398
1398
  match rule.fix(&ctx) {
@@ -1402,7 +1402,7 @@ fn process_file(
1402
1402
  // Apply fixes for this rule - we consider all warnings for the rule fixed
1403
1403
  warnings_fixed += all_warnings
1404
1404
  .iter()
1405
- .filter(|w| w.rule_name == Some(rule.name()) && w.fix.is_some())
1405
+ .filter(|w| w.rule_name == Some(rule.name()))
1406
1406
  .count();
1407
1407
  }
1408
1408
  }
@@ -5,11 +5,11 @@ lazy_static! {
5
5
  // Pattern to match blockquote lines
6
6
  static ref BLOCKQUOTE_LINE: Regex = Regex::new(r"^(\s*)>\s?(.*)$").unwrap();
7
7
 
8
- // Pattern to match empty blockquote lines
9
- static ref EMPTY_BLOCKQUOTE_LINE: Regex = Regex::new(r"^(\s*)>\s*$").unwrap();
8
+ // Pattern to match empty blockquote lines (> with no space or content)
9
+ static ref EMPTY_BLOCKQUOTE_LINE: Regex = Regex::new(r"^(\s*)>$").unwrap();
10
10
 
11
- // Pattern to match nested empty blockquote lines
12
- static ref NESTED_EMPTY_BLOCKQUOTE_LINE: Regex = Regex::new(r"^(\s*)>+\s*$").unwrap();
11
+ // Pattern to match nested empty blockquote lines (>> with no space or content)
12
+ static ref NESTED_EMPTY_BLOCKQUOTE_LINE: Regex = Regex::new(r"^(\s*)>+$").unwrap();
13
13
 
14
14
  // Pattern to match blockquote lines with no space after >
15
15
  static ref BLOCKQUOTE_NO_SPACE: Regex = Regex::new(r"^(\s*)>([^\s].*)$").unwrap();
@@ -32,12 +32,12 @@ impl BlockquoteUtils {
32
32
 
33
33
  /// Check if a line is an empty blockquote (> with no content)
34
34
  pub fn is_empty_blockquote(line: &str) -> bool {
35
- // Check for simple empty blockquote
35
+ // Check for simple empty blockquote (> with no space)
36
36
  if EMPTY_BLOCKQUOTE_LINE.is_match(line) {
37
37
  return true;
38
38
  }
39
39
 
40
- // Check for nested empty blockquote
40
+ // Check for nested empty blockquote (>> with no space)
41
41
  if NESTED_EMPTY_BLOCKQUOTE_LINE.is_match(line) {
42
42
  return true;
43
43
  }
@@ -51,6 +51,22 @@ impl BlockquoteUtils {
51
51
  false
52
52
  }
53
53
 
54
+ /// Check if an empty blockquote line needs fixing for MD028
55
+ /// This is more restrictive than is_empty_blockquote - only flags lines that actually need fixing
56
+ pub fn needs_md028_fix(line: &str) -> bool {
57
+ // Only flag blockquotes that have NO space after the > marker
58
+ // Lines with a single space ("> ") are already correct and don't need fixing
59
+ if EMPTY_BLOCKQUOTE_LINE.is_match(line) {
60
+ return true;
61
+ }
62
+
63
+ if NESTED_EMPTY_BLOCKQUOTE_LINE.is_match(line) {
64
+ return true;
65
+ }
66
+
67
+ false
68
+ }
69
+
54
70
  /// Check if a blockquote line has no space after the > marker
55
71
  pub fn has_no_space_after_marker(line: &str) -> bool {
56
72
  BLOCKQUOTE_NO_SPACE.is_match(line)
@@ -76,77 +76,14 @@ impl Rule for MD001HeadingIncrement {
76
76
  return Ok(vec![]);
77
77
  }
78
78
 
79
- let structure = DocumentStructure::new(content);
80
- if structure.heading_lines.is_empty() {
79
+ // Quick check for headings
80
+ if !content.contains('#') && !content.contains("===") && !content.contains("---") {
81
81
  return Ok(vec![]);
82
82
  }
83
83
 
84
- let line_index = LineIndex::new(content.to_string());
85
- let mut warnings = Vec::new();
86
- let mut prev_level = 0;
87
- let lines: Vec<&str> = content.lines().collect();
88
-
89
- // Process headings using pre-computed heading information
90
- for i in 0..structure.heading_lines.len() {
91
- let line_num = structure.heading_lines[i];
92
- let level = structure.heading_levels[i];
93
-
94
- // Check if this heading level is more than one level deeper than the previous
95
- if prev_level > 0 && level > prev_level + 1 {
96
- let adjusted_line_num = line_num - 1; // Convert 1-indexed to 0-indexed
97
- let indentation = if adjusted_line_num < lines.len() {
98
- HeadingUtils::get_indentation(lines[adjusted_line_num])
99
- } else {
100
- 0
101
- };
102
-
103
- // Get the heading text
104
- let heading_text = if adjusted_line_num < lines.len() {
105
- lines[adjusted_line_num]
106
- .trim_start()
107
- .trim_start_matches('#')
108
- .trim()
109
- .to_string()
110
- } else {
111
- String::new()
112
- };
113
-
114
- // Determine heading style
115
- let style = if adjusted_line_num + 1 < lines.len()
116
- && (lines[adjusted_line_num + 1].trim().starts_with('=')
117
- || lines[adjusted_line_num + 1].trim().starts_with('-'))
118
- {
119
- if lines[adjusted_line_num + 1].trim().starts_with('=') {
120
- HeadingStyle::Setext1
121
- } else {
122
- HeadingStyle::Setext2
123
- }
124
- } else {
125
- HeadingStyle::Atx
126
- };
127
-
128
- // Create a fix with the correct heading level
129
- let fixed_level = prev_level + 1;
130
- let replacement =
131
- HeadingUtils::convert_heading_style(&heading_text, fixed_level as u32, style);
132
-
133
- warnings.push(LintWarning {
134
- rule_name: Some(self.name()),
135
- line: line_num,
136
- column: indentation + 1,
137
- message: format!("Heading level should be {} for this level", prev_level + 1),
138
- severity: Severity::Warning,
139
- fix: Some(Fix {
140
- range: line_index.line_col_to_byte_range(line_num, indentation + 1),
141
- replacement: format!("{}{}", " ".repeat(indentation), replacement),
142
- }),
143
- });
144
- }
145
-
146
- prev_level = level;
147
- }
148
-
149
- Ok(warnings)
84
+ // Fallback path: create structure manually (should rarely be used)
85
+ let structure = DocumentStructure::new(content);
86
+ self.check_with_structure(ctx, &structure)
150
87
  }
151
88
 
152
89
  /// Optimized check using document structure
@@ -312,6 +249,10 @@ impl Rule for MD001HeadingIncrement {
312
249
  self
313
250
  }
314
251
 
252
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
253
+ Some(self)
254
+ }
255
+
315
256
  fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
316
257
  where
317
258
  Self: Sized,
@@ -333,6 +333,10 @@ impl Rule for MD002FirstHeadingH1 {
333
333
  self
334
334
  }
335
335
 
336
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
337
+ Some(self)
338
+ }
339
+
336
340
  fn default_config_section(&self) -> Option<(String, toml::Value)> {
337
341
  let mut map = toml::map::Map::new();
338
342
  map.insert("level".to_string(), toml::Value::Integer(self.level as i64));
@@ -160,64 +160,9 @@ impl Rule for MD003HeadingStyle {
160
160
  return Ok(Vec::new());
161
161
  }
162
162
 
163
- let mut result = Vec::new();
164
-
165
- // Create DocumentStructure and use it for heading info
163
+ // Fallback path: create structure manually (should rarely be used)
166
164
  let structure = DocumentStructure::new(content);
167
- let target_style = self.get_target_style(content, Some(&structure));
168
-
169
- let lines: Vec<&str> = content.lines().collect();
170
- for (idx, &line_num) in structure.heading_lines.iter().enumerate() {
171
- let level = structure.heading_levels[idx];
172
- let region = structure.heading_regions[idx];
173
- // Determine the current style of the heading
174
- let style = if region.0 != region.1 {
175
- // Setext heading (has an underline)
176
- if level == 1 {
177
- HeadingStyle::Setext1
178
- } else {
179
- HeadingStyle::Setext2
180
- }
181
- } else {
182
- // ATX heading
183
- let line = lines.get(line_num - 1).map_or("", |v| *v);
184
- if line.trim().ends_with('#') {
185
- HeadingStyle::AtxClosed
186
- } else {
187
- HeadingStyle::Atx
188
- }
189
- };
190
-
191
- // For markdownlint parity: when target style is Setext, all headings are expected to be Setext
192
- // This will flag level 3+ as violations since they can't be represented as Setext
193
- let expected_style = if target_style == HeadingStyle::Setext1
194
- || target_style == HeadingStyle::Setext2
195
- {
196
- if level == 1 {
197
- HeadingStyle::Setext1
198
- } else {
199
- HeadingStyle::Setext2
200
- }
201
- } else {
202
- target_style
203
- };
204
-
205
- if style != expected_style {
206
- result.push(LintWarning {
207
- rule_name: Some(self.name()),
208
- line: line_num,
209
- column: 1,
210
- message: format!(
211
- "Heading style should be {:?} (found {:?})",
212
- expected_style, style
213
- ),
214
- severity: Severity::Warning,
215
- fix: None,
216
- });
217
- }
218
- }
219
-
220
- Ok(result)
165
+ self.check_with_structure(ctx, &structure)
221
166
  }
222
167
 
223
168
  fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
@@ -468,6 +413,10 @@ impl Rule for MD003HeadingStyle {
468
413
  self
469
414
  }
470
415
 
416
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
417
+ Some(self)
418
+ }
419
+
471
420
  fn default_config_section(&self) -> Option<(String, toml::Value)> {
472
421
  let mut map = toml::map::Map::new();
473
422
  map.insert(
@@ -488,6 +437,17 @@ impl Rule for MD003HeadingStyle {
488
437
  }
489
438
  }
490
439
 
440
+ impl crate::utils::document_structure::DocumentStructureExtensions for MD003HeadingStyle {
441
+ fn has_relevant_elements(
442
+ &self,
443
+ _ctx: &crate::lint_context::LintContext,
444
+ doc_structure: &crate::utils::document_structure::DocumentStructure,
445
+ ) -> bool {
446
+ // This rule is only relevant if there are headings
447
+ !doc_structure.heading_lines.is_empty()
448
+ }
449
+ }
450
+
491
451
  #[cfg(test)]
492
452
  mod tests {
493
453
  use super::*;
@@ -167,7 +167,21 @@ impl Rule for MD004UnorderedListStyle {
167
167
  "Use consistent style for unordered list markers"
168
168
  }
169
169
 
170
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
171
+ Some(self)
172
+ }
173
+
170
174
  fn check(&self, ctx: &LintContext) -> LintResult {
175
+ // Early returns for performance
176
+ if ctx.content.is_empty() {
177
+ return Ok(Vec::new());
178
+ }
179
+
180
+ // Quick check for any list markers before processing
181
+ if !ctx.content.contains(|c: char| c == '*' || c == '-' || c == '+') {
182
+ return Ok(Vec::new());
183
+ }
184
+
171
185
  let mut warnings = Vec::new();
172
186
  let content = &ctx.content;
173
187
  let mut in_code_block = false;
@@ -743,11 +743,15 @@ impl Rule for MD005ListIndent {
743
743
  self
744
744
  }
745
745
 
746
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
747
+ Some(self)
748
+ }
749
+
746
750
  fn default_config_section(&self) -> Option<(String, toml::Value)> {
747
751
  None
748
752
  }
749
753
 
750
- fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
754
+ fn from_config(config: &crate::config::Config) -> Box<dyn Rule>
751
755
  where
752
756
  Self: Sized,
753
757
  {
@@ -309,6 +309,10 @@ impl Rule for MD006StartBullets {
309
309
  self
310
310
  }
311
311
 
312
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
313
+ Some(self)
314
+ }
315
+
312
316
  fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
313
317
  where
314
318
  Self: Sized,
@@ -216,6 +216,10 @@ impl Rule for MD007ULIndent {
216
216
  self
217
217
  }
218
218
 
219
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
220
+ Some(self)
221
+ }
222
+
219
223
  fn default_config_section(&self) -> Option<(String, toml::Value)> {
220
224
  let mut map = toml::map::Map::new();
221
225
  map.insert(
@@ -63,8 +63,29 @@ impl Rule for MD012NoMultipleBlanks {
63
63
  "Multiple consecutive blank lines"
64
64
  }
65
65
 
66
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
67
+ Some(self)
68
+ }
69
+
66
70
  fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
67
71
  let content = ctx.content;
72
+
73
+ // Early return for empty content
74
+ if content.is_empty() {
75
+ return Ok(Vec::new());
76
+ }
77
+
78
+ // Quick check for consecutive newlines or potential whitespace-only lines before processing
79
+ // Look for multiple consecutive lines that could be blank (empty or whitespace-only)
80
+ let lines: Vec<&str> = content.lines().collect();
81
+ let has_potential_blanks = lines.windows(2).any(|pair| {
82
+ pair[0].trim().is_empty() && pair[1].trim().is_empty()
83
+ });
84
+
85
+ if !has_potential_blanks {
86
+ return Ok(Vec::new());
87
+ }
88
+
68
89
  let _line_index = LineIndex::new(content.to_string());
69
90
 
70
91
  let mut warnings = Vec::new();
@@ -72,8 +93,6 @@ impl Rule for MD012NoMultipleBlanks {
72
93
  let mut blank_count = 0;
73
94
  let mut blank_start = 0;
74
95
 
75
- let lines: Vec<&str> = content.lines().collect();
76
-
77
96
  for (line_num, &line) in lines.iter().enumerate() {
78
97
  // Skip code blocks and front matter
79
98
  if Self::is_in_code_block(&lines, line_num)
@@ -255,3 +274,14 @@ impl Rule for MD012NoMultipleBlanks {
255
274
  Box::new(MD012NoMultipleBlanks::new(maximum))
256
275
  }
257
276
  }
277
+
278
+ impl crate::utils::document_structure::DocumentStructureExtensions for MD012NoMultipleBlanks {
279
+ fn has_relevant_elements(
280
+ &self,
281
+ ctx: &crate::lint_context::LintContext,
282
+ _structure: &crate::utils::document_structure::DocumentStructure,
283
+ ) -> bool {
284
+ // MD012 checks for consecutive blank lines, so it's relevant for any non-empty content
285
+ !ctx.content.is_empty()
286
+ }
287
+ }
@@ -2,7 +2,7 @@
2
2
  ///
3
3
  /// See [docs/md013.md](../../docs/md013.md) for full documentation, configuration, and examples.
4
4
  use crate::rule::{LintError, LintResult, LintWarning, Rule, Severity};
5
- use crate::utils::document_structure::DocumentStructure;
5
+ use crate::utils::document_structure::{DocumentStructure, DocumentStructureExtensions};
6
6
  use lazy_static::lazy_static;
7
7
  use regex::Regex;
8
8
  use toml;
@@ -119,13 +119,31 @@ impl Rule for MD013LineLength {
119
119
 
120
120
  fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
121
121
  let content = ctx.content;
122
- let mut warnings = Vec::new();
122
+
123
+ // Early return for empty content
124
+ if content.is_empty() {
125
+ return Ok(Vec::new());
126
+ }
127
+
128
+ // Fallback path: create structure manually (should rarely be used)
123
129
  let structure = DocumentStructure::new(content);
130
+ self.check_with_structure(ctx, &structure)
131
+ }
132
+
133
+ /// Optimized check using pre-computed document structure
134
+ fn check_with_structure(
135
+ &self,
136
+ ctx: &crate::lint_context::LintContext,
137
+ structure: &DocumentStructure,
138
+ ) -> LintResult {
139
+ let content = ctx.content;
140
+ let mut warnings = Vec::new();
124
141
  let lines: Vec<&str> = content.lines().collect();
125
142
 
126
143
  // Create a quick lookup set for heading lines
127
144
  let heading_lines_set: std::collections::HashSet<usize> =
128
145
  structure.heading_lines.iter().cloned().collect();
146
+
129
147
  // Create a quick lookup for setext headings (where start_line != end_line in regions)
130
148
  let _setext_lines_set: std::collections::HashSet<usize> = structure
131
149
  .heading_regions
@@ -133,6 +151,7 @@ impl Rule for MD013LineLength {
133
151
  .filter(|(start, end)| start != end)
134
152
  .flat_map(|(start, end)| (*start..=*end).collect::<Vec<usize>>())
135
153
  .collect();
154
+
136
155
  // Create a quick lookup set for list item lines (including continuations)
137
156
  let _list_lines_set: std::collections::HashSet<usize> =
138
157
  structure.list_lines.iter().cloned().collect();
@@ -168,7 +187,7 @@ impl Rule for MD013LineLength {
168
187
  }
169
188
 
170
189
  // Skip lines that are only a URL, image ref, or link ref
171
- if self.should_ignore_line(line, &lines, line_num, &structure) {
190
+ if self.should_ignore_line(line, &lines, line_num, structure) {
172
191
  continue;
173
192
  }
174
193
  }
@@ -207,6 +226,10 @@ impl Rule for MD013LineLength {
207
226
  self
208
227
  }
209
228
 
229
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
230
+ Some(self)
231
+ }
232
+
210
233
  fn default_config_section(&self) -> Option<(String, toml::Value)> {
211
234
  let mut map = toml::map::Map::new();
212
235
  map.insert(
@@ -248,3 +271,14 @@ impl Rule for MD013LineLength {
248
271
  ))
249
272
  }
250
273
  }
274
+
275
+ impl DocumentStructureExtensions for MD013LineLength {
276
+ fn has_relevant_elements(
277
+ &self,
278
+ ctx: &crate::lint_context::LintContext,
279
+ _doc_structure: &DocumentStructure,
280
+ ) -> bool {
281
+ // This rule always applies unless content is empty
282
+ !ctx.content.is_empty()
283
+ }
284
+ }
@@ -74,6 +74,13 @@ impl Rule for MD018NoMissingSpaceAtx {
74
74
 
75
75
  fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
76
76
  let content = ctx.content;
77
+
78
+ // Early return for empty content or content without ATX headings
79
+ if content.is_empty() || !content.contains('#') {
80
+ return Ok(Vec::new());
81
+ }
82
+
83
+ // Fallback path: create structure manually (should rarely be used)
77
84
  let structure = DocumentStructure::new(content);
78
85
  self.check_with_structure(ctx, &structure)
79
86
  }
@@ -171,6 +178,10 @@ impl Rule for MD018NoMissingSpaceAtx {
171
178
  self
172
179
  }
173
180
 
181
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
182
+ Some(self)
183
+ }
184
+
174
185
  fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
175
186
  where
176
187
  Self: Sized,
@@ -60,6 +60,13 @@ impl Rule for MD019NoMultipleSpaceAtx {
60
60
 
61
61
  fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
62
62
  let content = ctx.content;
63
+
64
+ // Early return for empty content or content without ATX headings
65
+ if content.is_empty() || !content.contains('#') {
66
+ return Ok(Vec::new());
67
+ }
68
+
69
+ // Fallback path: create structure manually (should rarely be used)
63
70
  let structure = DocumentStructure::new(content);
64
71
  self.check_with_structure(ctx, &structure)
65
72
  }
@@ -160,6 +167,10 @@ impl Rule for MD019NoMultipleSpaceAtx {
160
167
  self
161
168
  }
162
169
 
170
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
171
+ Some(self)
172
+ }
173
+
163
174
  fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
164
175
  where
165
176
  Self: Sized,
@@ -221,6 +221,10 @@ impl Rule for MD020NoMissingSpaceClosedAtx {
221
221
  self
222
222
  }
223
223
 
224
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
225
+ Some(self)
226
+ }
227
+
224
228
  fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
225
229
  where
226
230
  Self: Sized,
@@ -224,6 +224,10 @@ impl Rule for MD021NoMultipleSpaceClosedAtx {
224
224
  self
225
225
  }
226
226
 
227
+ fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
228
+ Some(self)
229
+ }
230
+
227
231
  fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
228
232
  where
229
233
  Self: Sized,