rumdl 0.0.58__tar.gz → 0.0.60__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 (248) hide show
  1. {rumdl-0.0.58 → rumdl-0.0.60}/Cargo.lock +43 -1
  2. {rumdl-0.0.58 → rumdl-0.0.60}/Cargo.toml +6 -5
  3. {rumdl-0.0.58 → rumdl-0.0.60}/PKG-INFO +1 -1
  4. {rumdl-0.0.58 → rumdl-0.0.60}/src/config.rs +65 -15
  5. rumdl-0.0.60/src/lib.rs +273 -0
  6. {rumdl-0.0.58 → rumdl-0.0.60}/src/main.rs +115 -34
  7. {rumdl-0.0.58 → rumdl-0.0.60}/src/rule.rs +81 -1
  8. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/blockquote_utils.rs +22 -6
  9. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md001_heading_increment.rs +9 -68
  10. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md002_first_heading_h1.rs +4 -0
  11. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md003_heading_style.rs +17 -57
  12. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md004_unordered_list_style.rs +14 -0
  13. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md005_list_indent.rs +4 -0
  14. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md006_start_bullets.rs +4 -0
  15. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md007_ul_indent.rs +4 -0
  16. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md009_trailing_spaces.rs +21 -8
  17. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md010_no_hard_tabs.rs +10 -1
  18. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md012_no_multiple_blanks.rs +32 -2
  19. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md013_line_length.rs +54 -11
  20. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md018_no_missing_space_atx.rs +11 -0
  21. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md019_no_multiple_space_atx.rs +11 -0
  22. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md020_no_missing_space_closed_atx.rs +4 -0
  23. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md021_no_multiple_space_closed_atx.rs +4 -0
  24. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md022_blanks_around_headings.rs +30 -28
  25. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md023_heading_start_left.rs +4 -0
  26. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md024_no_duplicate_heading.rs +40 -1
  27. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md025_single_title.rs +16 -0
  28. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md026_no_trailing_punctuation.rs +76 -2
  29. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md028_no_blanks_blockquote.rs +33 -54
  30. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md029_ordered_list_prefix.rs +45 -115
  31. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md030_list_marker_space.rs +4 -0
  32. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md032_blanks_around_lists.rs +72 -6
  33. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md033_no_inline_html.rs +29 -21
  34. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md034_no_bare_urls.rs +72 -4
  35. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md037_spaces_around_emphasis.rs +20 -209
  36. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md038_no_space_in_code.rs +10 -1
  37. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md039_no_space_in_links.rs +4 -0
  38. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md044_proper_names.rs +51 -21
  39. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md046_code_block_style.rs +65 -117
  40. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md051_link_fragments.rs +101 -111
  41. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md057_existing_relative_links.rs +18 -20
  42. rumdl-0.0.60/src/utils/ast_utils.rs +235 -0
  43. {rumdl-0.0.58 → rumdl-0.0.60}/src/utils/document_structure.rs +68 -26
  44. rumdl-0.0.60/src/utils/early_returns.rs +288 -0
  45. {rumdl-0.0.58 → rumdl-0.0.60}/src/utils/element_cache.rs +18 -31
  46. {rumdl-0.0.58 → rumdl-0.0.60}/src/utils/mod.rs +3 -0
  47. {rumdl-0.0.58 → rumdl-0.0.60}/src/utils/regex_cache.rs +137 -2
  48. rumdl-0.0.60/src/utils/string_interner.rs +114 -0
  49. {rumdl-0.0.58 → rumdl-0.0.60}/tests/advanced_integration_tests.rs +1 -1
  50. rumdl-0.0.58/src/lib.rs +0 -85
  51. rumdl-0.0.58/src/utils/early_returns.rs +0 -125
  52. {rumdl-0.0.58 → rumdl-0.0.60}/.rumdl.toml +0 -0
  53. {rumdl-0.0.58 → rumdl-0.0.60}/MANIFEST.in +0 -0
  54. {rumdl-0.0.58 → rumdl-0.0.60}/Makefile +0 -0
  55. {rumdl-0.0.58 → rumdl-0.0.60}/README.md +0 -0
  56. {rumdl-0.0.58 → rumdl-0.0.60}/assets/logo.png +0 -0
  57. {rumdl-0.0.58 → rumdl-0.0.60}/benches/range_performance.rs +0 -0
  58. {rumdl-0.0.58 → rumdl-0.0.60}/benches/range_utils_benchmark.rs +0 -0
  59. {rumdl-0.0.58 → rumdl-0.0.60}/benches/rule_performance.rs +0 -0
  60. {rumdl-0.0.58 → rumdl-0.0.60}/docs/RULES.md +0 -0
  61. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md001.md +0 -0
  62. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md002.md +0 -0
  63. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md003.md +0 -0
  64. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md004.md +0 -0
  65. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md005.md +0 -0
  66. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md006.md +0 -0
  67. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md007.md +0 -0
  68. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md009.md +0 -0
  69. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md010.md +0 -0
  70. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md011.md +0 -0
  71. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md012.md +0 -0
  72. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md013.md +0 -0
  73. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md014.md +0 -0
  74. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md018.md +0 -0
  75. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md019.md +0 -0
  76. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md020.md +0 -0
  77. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md021.md +0 -0
  78. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md022.md +0 -0
  79. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md023.md +0 -0
  80. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md024.md +0 -0
  81. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md025.md +0 -0
  82. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md026.md +0 -0
  83. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md027.md +0 -0
  84. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md028.md +0 -0
  85. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md029.md +0 -0
  86. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md030.md +0 -0
  87. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md031.md +0 -0
  88. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md032.md +0 -0
  89. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md033.md +0 -0
  90. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md034.md +0 -0
  91. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md035.md +0 -0
  92. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md036.md +0 -0
  93. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md037.md +0 -0
  94. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md038.md +0 -0
  95. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md039.md +0 -0
  96. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md040.md +0 -0
  97. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md041.md +0 -0
  98. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md042.md +0 -0
  99. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md043.md +0 -0
  100. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md044.md +0 -0
  101. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md045.md +0 -0
  102. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md046.md +0 -0
  103. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md047.md +0 -0
  104. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md048.md +0 -0
  105. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md049.md +0 -0
  106. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md050.md +0 -0
  107. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md051.md +0 -0
  108. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md052.md +0 -0
  109. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md053.md +0 -0
  110. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md054.md +0 -0
  111. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md055.md +0 -0
  112. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md056.md +0 -0
  113. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md057.md +0 -0
  114. {rumdl-0.0.58 → rumdl-0.0.60}/docs/md058.md +0 -0
  115. {rumdl-0.0.58 → rumdl-0.0.60}/issues/plan-rule-parity-with-markdownlint.md +0 -0
  116. {rumdl-0.0.58 → rumdl-0.0.60}/parity_check.py +0 -0
  117. {rumdl-0.0.58 → rumdl-0.0.60}/pyproject.toml +0 -0
  118. {rumdl-0.0.58 → rumdl-0.0.60}/python/MANIFEST.in +0 -0
  119. {rumdl-0.0.58 → rumdl-0.0.60}/python/PYTHON-README.md +0 -0
  120. {rumdl-0.0.58 → rumdl-0.0.60}/python/rumdl/__init__.py +0 -0
  121. {rumdl-0.0.58 → rumdl-0.0.60}/python/rumdl/__main__.py +0 -0
  122. {rumdl-0.0.58 → rumdl-0.0.60}/python/rumdl/py.typed +0 -0
  123. {rumdl-0.0.58 → rumdl-0.0.60}/rumdl.toml.example +0 -0
  124. {rumdl-0.0.58 → rumdl-0.0.60}/src/init.rs +0 -0
  125. {rumdl-0.0.58 → rumdl-0.0.60}/src/lint_context.rs +0 -0
  126. {rumdl-0.0.58 → rumdl-0.0.60}/src/markdownlint_config.rs +0 -0
  127. {rumdl-0.0.58 → rumdl-0.0.60}/src/profiling.rs +0 -0
  128. {rumdl-0.0.58 → rumdl-0.0.60}/src/python.rs +0 -0
  129. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/code_block_utils.rs +0 -0
  130. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/code_fence_utils.rs +0 -0
  131. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/emphasis_style.rs +0 -0
  132. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/front_matter_utils.rs +0 -0
  133. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/heading_utils.rs +0 -0
  134. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/list_utils.rs +0 -0
  135. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md011_no_reversed_links.rs +0 -0
  136. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md014_commands_show_output.rs +0 -0
  137. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
  138. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md031_blanks_around_fences.rs +0 -0
  139. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md035_hr_style.rs +0 -0
  140. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md036_no_emphasis_only_first.rs +0 -0
  141. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md040_fenced_code_language.rs +0 -0
  142. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md041_first_line_heading.rs +0 -0
  143. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md042_no_empty_links.rs +0 -0
  144. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md043_required_headings.rs +0 -0
  145. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md045_no_alt_text.rs +0 -0
  146. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md047_single_trailing_newline.rs +0 -0
  147. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md048_code_fence_style.rs +0 -0
  148. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md049_emphasis_style.rs +0 -0
  149. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md050_strong_style.rs +0 -0
  150. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md052_reference_links_images.rs +0 -0
  151. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md053_link_image_reference_definitions.rs +0 -0
  152. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md054_link_image_style.rs +0 -0
  153. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md055_table_pipe_style.rs +0 -0
  154. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md056_table_column_count.rs +0 -0
  155. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/md058_blanks_around_tables.rs +0 -0
  156. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/mod.rs +0 -0
  157. {rumdl-0.0.58 → rumdl-0.0.60}/src/rules/strong_style.rs +0 -0
  158. {rumdl-0.0.58 → rumdl-0.0.60}/src/utils/code_block_utils.rs +0 -0
  159. {rumdl-0.0.58 → rumdl-0.0.60}/src/utils/markdown_elements.rs +0 -0
  160. {rumdl-0.0.58 → rumdl-0.0.60}/src/utils/range_utils.rs +0 -0
  161. {rumdl-0.0.58 → rumdl-0.0.60}/tests/cli_duplication_test.rs +0 -0
  162. {rumdl-0.0.58 → rumdl-0.0.60}/tests/cli_integration_tests.rs +0 -0
  163. {rumdl-0.0.58 → rumdl-0.0.60}/tests/commonmark_compliance_tests.rs +0 -0
  164. {rumdl-0.0.58 → rumdl-0.0.60}/tests/comprehensive_integration_tests.rs +0 -0
  165. {rumdl-0.0.58 → rumdl-0.0.60}/tests/config_application_tests.rs +0 -0
  166. {rumdl-0.0.58 → rumdl-0.0.60}/tests/config_tests.rs +0 -0
  167. {rumdl-0.0.58 → rumdl-0.0.60}/tests/init_command_test.rs +0 -0
  168. {rumdl-0.0.58 → rumdl-0.0.60}/tests/init_tests.rs +0 -0
  169. {rumdl-0.0.58 → rumdl-0.0.60}/tests/integration_tests.rs +0 -0
  170. {rumdl-0.0.58 → rumdl-0.0.60}/tests/json_output_test.rs +0 -0
  171. {rumdl-0.0.58 → rumdl-0.0.60}/tests/lib.rs +0 -0
  172. {rumdl-0.0.58 → rumdl-0.0.60}/tests/markdownlint_cli_integration.rs +0 -0
  173. {rumdl-0.0.58 → rumdl-0.0.60}/tests/markdownlint_config_test.rs +0 -0
  174. {rumdl-0.0.58 → rumdl-0.0.60}/tests/md030_edge_cases.md +0 -0
  175. {rumdl-0.0.58 → rumdl-0.0.60}/tests/output_format_tests.rs +0 -0
  176. {rumdl-0.0.58 → rumdl-0.0.60}/tests/perf_check.rs +0 -0
  177. {rumdl-0.0.58 → rumdl-0.0.60}/tests/pyproject_config_tests.rs +0 -0
  178. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md001_test.rs +0 -0
  179. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md001_unicode_test.rs +0 -0
  180. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md002_test.rs +0 -0
  181. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md003_test.rs +0 -0
  182. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md004_test.rs +0 -0
  183. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md005_test.rs +0 -0
  184. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md006_test.rs +0 -0
  185. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md006_unicode_test.rs +0 -0
  186. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md007_test.rs +0 -0
  187. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md009_test.rs +0 -0
  188. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md010_test.rs +0 -0
  189. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md011_test.rs +0 -0
  190. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md012_test.rs +0 -0
  191. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md013_test.rs +0 -0
  192. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md014_test.rs +0 -0
  193. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md018_test.rs +0 -0
  194. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md019_test.rs +0 -0
  195. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md020_test.rs +0 -0
  196. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md021_test.rs +0 -0
  197. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md022_test.rs +0 -0
  198. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md023_extended_test.rs +0 -0
  199. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md023_test.rs +0 -0
  200. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md024_test.rs +0 -0
  201. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md025_test.rs +0 -0
  202. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md026_test.rs +0 -0
  203. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md027_test.rs +0 -0
  204. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md028_test.rs +0 -0
  205. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md029_test.rs +0 -0
  206. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md030_test.rs +0 -0
  207. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md031_test.rs +0 -0
  208. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md032_test.rs +0 -0
  209. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md033_extended_test.rs +0 -0
  210. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md033_test.rs +0 -0
  211. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md034_test.rs +0 -0
  212. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md035_test.rs +0 -0
  213. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md036_test.rs +0 -0
  214. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md037_test.rs +0 -0
  215. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md038_test.rs +0 -0
  216. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md039_test.rs +0 -0
  217. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md040_test.rs +0 -0
  218. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md041_test.rs +0 -0
  219. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md042_test.rs +0 -0
  220. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md043_test.rs +0 -0
  221. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md044_test.rs +0 -0
  222. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md045_test.rs +0 -0
  223. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md046_test.rs +0 -0
  224. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md047_test.rs +0 -0
  225. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md048_test.rs +0 -0
  226. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md049_test.rs +0 -0
  227. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md050_test.rs +0 -0
  228. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md051_test.rs +0 -0
  229. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md052_test.rs +0 -0
  230. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md053_additional_test.rs +0 -0
  231. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md053_proptest.rs +0 -0
  232. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md053_test.rs +0 -0
  233. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md054_test.rs +0 -0
  234. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md054_unicode_test.rs +0 -0
  235. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md055_test.rs +0 -0
  236. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md056_test.rs +0 -0
  237. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md057_test.rs +0 -0
  238. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/md058_test.rs +0 -0
  239. {rumdl-0.0.58 → rumdl-0.0.60}/tests/rules/mod.rs +0 -0
  240. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils/blockquote_utils_test.rs +0 -0
  241. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils/code_block_utils_extended_test.rs +0 -0
  242. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils/code_block_utils_test.rs +0 -0
  243. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils/core_utils_test.rs +0 -0
  244. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils/front_matter_utils_test.rs +0 -0
  245. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils/line_index_test.rs +0 -0
  246. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils/mod.rs +0 -0
  247. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils_markdown_edge_cases.rs +0 -0
  248. {rumdl-0.0.58 → rumdl-0.0.60}/tests/utils_tests.rs +0 -0
@@ -917,6 +917,15 @@ version = "2.7.4"
917
917
  source = "registry+https://github.com/rust-lang/crates.io-index"
918
918
  checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
919
919
 
920
+ [[package]]
921
+ name = "memmap2"
922
+ version = "0.9.5"
923
+ source = "registry+https://github.com/rust-lang/crates.io-index"
924
+ checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
925
+ dependencies = [
926
+ "libc",
927
+ ]
928
+
920
929
  [[package]]
921
930
  name = "memoffset"
922
931
  version = "0.9.1"
@@ -1368,7 +1377,7 @@ dependencies = [
1368
1377
 
1369
1378
  [[package]]
1370
1379
  name = "rumdl"
1371
- version = "0.0.58"
1380
+ version = "0.0.60"
1372
1381
  dependencies = [
1373
1382
  "anyhow",
1374
1383
  "assert_cmd",
@@ -1388,6 +1397,7 @@ dependencies = [
1388
1397
  "lazy_static",
1389
1398
  "log",
1390
1399
  "markdown",
1400
+ "memmap2",
1391
1401
  "once_cell",
1392
1402
  "predicates",
1393
1403
  "pretty_assertions",
@@ -1396,6 +1406,7 @@ dependencies = [
1396
1406
  "rand 0.9.1",
1397
1407
  "rayon",
1398
1408
  "regex",
1409
+ "seahash",
1399
1410
  "serde",
1400
1411
  "serde_json",
1401
1412
  "serde_yaml",
@@ -1404,6 +1415,7 @@ dependencies = [
1404
1415
  "thiserror 2.0.12",
1405
1416
  "toml",
1406
1417
  "toml_edit",
1418
+ "unicode-normalization",
1407
1419
  "url",
1408
1420
  "walkdir",
1409
1421
  ]
@@ -1465,6 +1477,12 @@ dependencies = [
1465
1477
  "winapi-util",
1466
1478
  ]
1467
1479
 
1480
+ [[package]]
1481
+ name = "seahash"
1482
+ version = "4.1.0"
1483
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1484
+ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
1485
+
1468
1486
  [[package]]
1469
1487
  name = "serde"
1470
1488
  version = "1.0.219"
@@ -1670,6 +1688,21 @@ dependencies = [
1670
1688
  "serde_json",
1671
1689
  ]
1672
1690
 
1691
+ [[package]]
1692
+ name = "tinyvec"
1693
+ version = "1.9.0"
1694
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1695
+ checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
1696
+ dependencies = [
1697
+ "tinyvec_macros",
1698
+ ]
1699
+
1700
+ [[package]]
1701
+ name = "tinyvec_macros"
1702
+ version = "0.1.1"
1703
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1704
+ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1705
+
1673
1706
  [[package]]
1674
1707
  name = "toml"
1675
1708
  version = "0.8.22"
@@ -1747,6 +1780,15 @@ version = "1.0.18"
1747
1780
  source = "registry+https://github.com/rust-lang/crates.io-index"
1748
1781
  checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
1749
1782
 
1783
+ [[package]]
1784
+ name = "unicode-normalization"
1785
+ version = "0.1.24"
1786
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1787
+ checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
1788
+ dependencies = [
1789
+ "tinyvec",
1790
+ ]
1791
+
1750
1792
  [[package]]
1751
1793
  name = "unicode-segmentation"
1752
1794
  version = "1.12.0"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rumdl"
3
- version = "0.0.58"
3
+ version = "0.0.60"
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>"]
@@ -44,15 +44,17 @@ anyhow = "1.0"
44
44
  console = "0.15.11"
45
45
  dialoguer = "0.11.0"
46
46
  indicatif = "0.17.11"
47
-
48
47
  log = "0.4.27"
49
48
  pretty_assertions = "1.4"
50
- markdown = "1.0.0"
49
+ markdown = "1.0"
51
50
  config = "0.15"
52
- regex = "1.11.1"
51
+ regex = "1.11"
53
52
  toml_edit = "0.22"
54
53
  dyn-clone = "1"
55
54
  url = { version = "2", features = ["serde"] }
55
+ unicode-normalization = "0.1"
56
+ memmap2 = "0.9"
57
+ seahash = "4.1"
56
58
 
57
59
  [features]
58
60
  default = ["parallel", "profiling"]
@@ -70,7 +72,6 @@ glob = "0.3.2"
70
72
  proptest = "1.6.0"
71
73
  criterion = { version = "0.5", features = ["html_reports"] }
72
74
  rand = "0.9.1"
73
-
74
75
  pretty_assertions = "1.4"
75
76
 
76
77
  [[bench]]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rumdl
3
- Version: 0.0.58
3
+ Version: 0.0.60
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -209,21 +209,35 @@ pub enum ConfigError {
209
209
  }
210
210
 
211
211
  /// Get a rule-specific configuration value
212
+ /// Automatically tries both the original key and normalized variants (kebab-case ↔ snake_case)
213
+ /// for better markdownlint compatibility
212
214
  pub fn get_rule_config_value<T: serde::de::DeserializeOwned>(
213
215
  config: &Config,
214
216
  rule_name: &str,
215
217
  key: &str,
216
218
  ) -> Option<T> {
217
- let norm_key = normalize_key(key);
218
219
  let norm_rule_name = rule_name.to_ascii_uppercase(); // Use uppercase for lookup
219
- config
220
- .rules
221
- .get(&norm_rule_name)
222
- .and_then(|rule_config| rule_config.values.get(&norm_key))
223
- .and_then(|value| {
224
- let result = T::deserialize(value.clone()).ok();
225
- result
226
- })
220
+
221
+ let rule_config = config.rules.get(&norm_rule_name)?;
222
+
223
+ // Try multiple key variants to support both underscore and kebab-case formats
224
+ let key_variants = [
225
+ key.to_string(), // Original key as provided
226
+ normalize_key(key), // Normalized key (lowercase, kebab-case)
227
+ key.replace('-', "_"), // Convert kebab-case to snake_case
228
+ key.replace('_', "-"), // Convert snake_case to kebab-case
229
+ ];
230
+
231
+ // Try each variant until we find a match
232
+ for variant in &key_variants {
233
+ if let Some(value) = rule_config.values.get(variant) {
234
+ if let Ok(result) = T::deserialize(value.clone()) {
235
+ return Some(result);
236
+ }
237
+ }
238
+ }
239
+
240
+ None
227
241
  }
228
242
 
229
243
  /// Generate default rumdl configuration for pyproject.toml
@@ -904,16 +918,52 @@ impl RuleRegistry {
904
918
  self.rule_schemas.keys().cloned().collect()
905
919
  }
906
920
 
907
- /// Get valid config keys for a rule
921
+ /// Get the valid configuration keys for a rule, including both original and normalized variants
908
922
  pub fn config_keys_for(&self, rule: &str) -> Option<std::collections::BTreeSet<String>> {
909
- self.rule_schemas
910
- .get(rule)
911
- .map(|m| m.keys().cloned().collect())
923
+ self.rule_schemas.get(rule).map(|schema| {
924
+ let mut all_keys = std::collections::BTreeSet::new();
925
+
926
+ // Add original keys from schema
927
+ for key in schema.keys() {
928
+ all_keys.insert(key.clone());
929
+ }
930
+
931
+ // Add normalized variants for markdownlint compatibility
932
+ for key in schema.keys() {
933
+ // Add kebab-case variant
934
+ all_keys.insert(key.replace('_', "-"));
935
+ // Add snake_case variant
936
+ all_keys.insert(key.replace('-', "_"));
937
+ // Add normalized variant
938
+ all_keys.insert(normalize_key(key));
939
+ }
940
+
941
+ all_keys
942
+ })
912
943
  }
913
944
 
914
- /// Get the expected TOML value for a rule's config key (for type checking)
945
+ /// Get the expected value type for a rule's configuration key, trying variants
915
946
  pub fn expected_value_for(&self, rule: &str, key: &str) -> Option<&toml::Value> {
916
- self.rule_schemas.get(rule).and_then(|m| m.get(key))
947
+ if let Some(schema) = self.rule_schemas.get(rule) {
948
+ // Try the original key first
949
+ if let Some(value) = schema.get(key) {
950
+ return Some(value);
951
+ }
952
+
953
+ // Try key variants
954
+ let key_variants = [
955
+ key.replace('-', "_"), // Convert kebab-case to snake_case
956
+ key.replace('_', "-"), // Convert snake_case to kebab-case
957
+ normalize_key(key), // Normalized key (lowercase, kebab-case)
958
+ ];
959
+
960
+ for variant in &key_variants {
961
+ if let Some(value) = schema.get(variant) {
962
+ return Some(value);
963
+ }
964
+ }
965
+ }
966
+ None
917
967
  }
918
968
  }
919
969
 
@@ -0,0 +1,273 @@
1
+ pub mod config;
2
+ pub mod init;
3
+ pub mod lint_context;
4
+ pub mod markdownlint_config;
5
+ pub mod profiling;
6
+ pub mod rule;
7
+ pub mod rules;
8
+ pub mod utils;
9
+
10
+ #[cfg(feature = "python")]
11
+ pub mod python;
12
+
13
+ pub use rules::heading_utils::{Heading, HeadingStyle};
14
+ pub use rules::*;
15
+
16
+ pub use crate::lint_context::LintContext;
17
+ use crate::rule::{LintResult, Rule, RuleCategory};
18
+ use crate::utils::document_structure::DocumentStructure;
19
+ use std::time::Instant;
20
+
21
+ /// Content characteristics for efficient rule filtering
22
+ #[derive(Debug, Default)]
23
+ struct ContentCharacteristics {
24
+ has_headings: bool, // # or setext headings
25
+ has_lists: bool, // *, -, +, 1. etc
26
+ has_links: bool, // [text](url) or [text][ref]
27
+ has_code: bool, // ``` or ~~~ or indented code
28
+ has_emphasis: bool, // * or _ for emphasis
29
+ has_html: bool, // < > tags
30
+ has_tables: bool, // | pipes
31
+ has_blockquotes: bool, // > markers
32
+ has_images: bool, // ![alt](url)
33
+ line_count: usize,
34
+ }
35
+
36
+ impl ContentCharacteristics {
37
+ fn analyze(content: &str) -> Self {
38
+ let mut chars = Self::default();
39
+ chars.line_count = content.lines().count();
40
+
41
+ // Quick single-pass analysis
42
+ let mut has_atx_heading = false;
43
+ let mut has_setext_heading = false;
44
+
45
+ for line in content.lines() {
46
+ let trimmed = line.trim();
47
+
48
+ // Headings: ATX (#) or Setext (underlines)
49
+ if !has_atx_heading && trimmed.starts_with('#') {
50
+ has_atx_heading = true;
51
+ }
52
+ if !has_setext_heading && (trimmed.chars().all(|c| c == '=' || c == '-') && trimmed.len() > 1) {
53
+ has_setext_heading = true;
54
+ }
55
+
56
+ // Quick character-based detection (more efficient than regex)
57
+ if !chars.has_lists && (line.contains("* ") || line.contains("- ") || line.contains("+ ")) {
58
+ chars.has_lists = true;
59
+ }
60
+ if !chars.has_lists && line.chars().next().map_or(false, |c| c.is_ascii_digit()) && line.contains(". ") {
61
+ chars.has_lists = true;
62
+ }
63
+ if !chars.has_links && line.contains('[') {
64
+ chars.has_links = true;
65
+ }
66
+ if !chars.has_images && line.contains("![") {
67
+ chars.has_images = true;
68
+ }
69
+ if !chars.has_code && (line.contains('`') || line.contains("~~~")) {
70
+ chars.has_code = true;
71
+ }
72
+ if !chars.has_emphasis && (line.contains('*') || line.contains('_')) {
73
+ chars.has_emphasis = true;
74
+ }
75
+ if !chars.has_html && line.contains('<') {
76
+ chars.has_html = true;
77
+ }
78
+ if !chars.has_tables && line.contains('|') {
79
+ chars.has_tables = true;
80
+ }
81
+ if !chars.has_blockquotes && line.starts_with('>') {
82
+ chars.has_blockquotes = true;
83
+ }
84
+ }
85
+
86
+ chars.has_headings = has_atx_heading || has_setext_heading;
87
+ chars
88
+ }
89
+
90
+ /// Check if a rule should be skipped based on content characteristics
91
+ fn should_skip_rule(&self, rule: &dyn Rule) -> bool {
92
+ match rule.category() {
93
+ RuleCategory::Heading => !self.has_headings,
94
+ RuleCategory::List => !self.has_lists,
95
+ RuleCategory::Link => !self.has_links && !self.has_images,
96
+ RuleCategory::Image => !self.has_images,
97
+ RuleCategory::CodeBlock => !self.has_code,
98
+ RuleCategory::Html => !self.has_html,
99
+ RuleCategory::Emphasis => !self.has_emphasis,
100
+ RuleCategory::Blockquote => !self.has_blockquotes,
101
+ RuleCategory::Table => !self.has_tables,
102
+ // Always check these categories as they apply to all content
103
+ RuleCategory::Whitespace | RuleCategory::FrontMatter | RuleCategory::Other => false,
104
+ }
105
+ }
106
+ }
107
+
108
+ /// Lint a file against the given rules with intelligent rule filtering
109
+ /// Assumes the provided `rules` vector contains the final,
110
+ /// configured, and filtered set of rules to be executed.
111
+ pub fn lint(content: &str, rules: &[Box<dyn Rule>], _verbose: bool) -> LintResult {
112
+ let mut warnings = Vec::new();
113
+ let _overall_start = Instant::now();
114
+
115
+ // Early return for empty content
116
+ if content.is_empty() {
117
+ return Ok(warnings);
118
+ }
119
+
120
+ // Analyze content characteristics for rule filtering
121
+ let characteristics = ContentCharacteristics::analyze(content);
122
+
123
+ // Filter rules based on content characteristics
124
+ let applicable_rules: Vec<_> = rules
125
+ .iter()
126
+ .filter(|rule| !characteristics.should_skip_rule(rule.as_ref()))
127
+ .collect();
128
+
129
+ // Calculate skipped rules count before consuming applicable_rules
130
+ let _total_rules = rules.len();
131
+ let _applicable_count = applicable_rules.len();
132
+
133
+ // Parse DocumentStructure once
134
+ let structure = DocumentStructure::new(content);
135
+
136
+ // Parse AST once for rules that can benefit from it
137
+ let ast_rules_count = applicable_rules.iter().filter(|rule| rule.uses_ast()).count();
138
+ let ast = if ast_rules_count > 0 {
139
+ Some(crate::utils::ast_utils::get_cached_ast(content))
140
+ } else {
141
+ None
142
+ };
143
+
144
+ // Parse LintContext once (migration step)
145
+ let lint_ctx = crate::lint_context::LintContext::new(content);
146
+
147
+ for rule in applicable_rules {
148
+ let _rule_start = Instant::now();
149
+
150
+ // Try optimized paths in order of preference
151
+ let result = if rule.uses_ast() && ast.is_some() {
152
+ // 1. AST-based path
153
+ rule.as_maybe_ast()
154
+ .and_then(|ext| ext.check_with_ast_opt(&lint_ctx, ast.as_ref().unwrap()))
155
+ .unwrap_or_else(|| rule.check_with_ast(&lint_ctx, ast.as_ref().unwrap()))
156
+ } else {
157
+ // 2. Document structure path
158
+ rule.as_maybe_document_structure()
159
+ .and_then(|ext| ext.check_with_structure_opt(&lint_ctx, &structure))
160
+ .unwrap_or_else(|| rule.check(&lint_ctx))
161
+ };
162
+
163
+ match result {
164
+ Ok(rule_warnings) => {
165
+ warnings.extend(rule_warnings);
166
+ }
167
+ Err(e) => {
168
+ log::error!("Error checking rule {}: {}", rule.name(), e);
169
+ return Err(e);
170
+ }
171
+ }
172
+
173
+ #[cfg(not(test))]
174
+ if _verbose {
175
+ let rule_duration = _rule_start.elapsed();
176
+ if rule_duration.as_millis() > 500 {
177
+ log::debug!("Rule {} took {:?}", rule.name(), rule_duration);
178
+ }
179
+ }
180
+ }
181
+
182
+ #[cfg(not(test))]
183
+ if _verbose {
184
+ let skipped_rules = _total_rules - _applicable_count;
185
+ if skipped_rules > 0 {
186
+ log::debug!("Skipped {} of {} rules based on content analysis", skipped_rules, _total_rules);
187
+ }
188
+ if ast.is_some() {
189
+ log::debug!("Used shared AST for {} rules", ast_rules_count);
190
+ }
191
+ }
192
+
193
+ Ok(warnings)
194
+ }
195
+
196
+ /// Get the profiling report
197
+ pub fn get_profiling_report() -> String {
198
+ profiling::get_report()
199
+ }
200
+
201
+ /// Reset the profiling data
202
+ pub fn reset_profiling() {
203
+ profiling::reset()
204
+ }
205
+
206
+ /// Get regex cache statistics for performance monitoring
207
+ pub fn get_regex_cache_stats() -> std::collections::HashMap<String, u64> {
208
+ crate::utils::regex_cache::get_cache_stats()
209
+ }
210
+
211
+ /// Get AST cache statistics for performance monitoring
212
+ pub fn get_ast_cache_stats() -> std::collections::HashMap<u64, u64> {
213
+ crate::utils::ast_utils::get_ast_cache_stats()
214
+ }
215
+
216
+ /// Clear all caches (useful for testing and memory management)
217
+ pub fn clear_all_caches() {
218
+ crate::utils::ast_utils::clear_ast_cache();
219
+ // Note: Regex cache is intentionally not cleared as it's global and shared
220
+ }
221
+
222
+ /// Get comprehensive cache performance report
223
+ pub fn get_cache_performance_report() -> String {
224
+ let regex_stats = get_regex_cache_stats();
225
+ let ast_stats = get_ast_cache_stats();
226
+
227
+ let mut report = String::new();
228
+
229
+ report.push_str("=== Cache Performance Report ===\n\n");
230
+
231
+ // Regex cache statistics
232
+ report.push_str("Regex Cache:\n");
233
+ if regex_stats.is_empty() {
234
+ report.push_str(" No regex patterns cached\n");
235
+ } else {
236
+ let total_usage: u64 = regex_stats.values().sum();
237
+ report.push_str(&format!(" Total patterns: {}\n", regex_stats.len()));
238
+ report.push_str(&format!(" Total usage: {}\n", total_usage));
239
+
240
+ // Show top 5 most used patterns
241
+ let mut sorted_patterns: Vec<_> = regex_stats.iter().collect();
242
+ sorted_patterns.sort_by(|a, b| b.1.cmp(a.1));
243
+
244
+ report.push_str(" Top patterns by usage:\n");
245
+ for (pattern, count) in sorted_patterns.iter().take(5) {
246
+ let truncated_pattern = if pattern.len() > 50 {
247
+ format!("{}...", &pattern[..47])
248
+ } else {
249
+ pattern.to_string()
250
+ };
251
+ report.push_str(&format!(" {} ({}x): {}\n", count, pattern.len().min(50), truncated_pattern));
252
+ }
253
+ }
254
+
255
+ report.push_str("\n");
256
+
257
+ // AST cache statistics
258
+ report.push_str("AST Cache:\n");
259
+ if ast_stats.is_empty() {
260
+ report.push_str(" No AST nodes cached\n");
261
+ } else {
262
+ let total_usage: u64 = ast_stats.values().sum();
263
+ report.push_str(&format!(" Total ASTs: {}\n", ast_stats.len()));
264
+ report.push_str(&format!(" Total usage: {}\n", total_usage));
265
+
266
+ if total_usage > ast_stats.len() as u64 {
267
+ let cache_hit_rate = ((total_usage - ast_stats.len() as u64) as f64 / total_usage as f64) * 100.0;
268
+ report.push_str(&format!(" Cache hit rate: {:.1}%\n", cache_hit_rate));
269
+ }
270
+ }
271
+
272
+ report
273
+ }