rumdl 0.0.100__tar.gz → 0.0.102__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 (364) hide show
  1. {rumdl-0.0.100 → rumdl-0.0.102}/CHANGELOG.md +25 -1
  2. {rumdl-0.0.100 → rumdl-0.0.102}/Cargo.lock +1 -1
  3. {rumdl-0.0.100 → rumdl-0.0.102}/Cargo.toml +1 -1
  4. {rumdl-0.0.100 → rumdl-0.0.102}/PKG-INFO +1 -1
  5. {rumdl-0.0.100 → rumdl-0.0.102}/src/main.rs +37 -1
  6. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md005_list_indent.rs +133 -26
  7. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md006_start_bullets.rs +102 -26
  8. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md007_ul_indent.rs +168 -44
  9. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md009_trailing_spaces.rs +7 -14
  10. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md010_no_hard_tabs.rs +2 -7
  11. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md011_no_reversed_links.rs +0 -1
  12. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md013_line_length.rs +3 -24
  13. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md020_no_missing_space_closed_atx.rs +3 -10
  14. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md025_single_title.rs +36 -18
  15. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md026_no_trailing_punctuation.rs +10 -0
  16. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md029_ordered_list_prefix.rs +4 -5
  17. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md031_blanks_around_fences.rs +0 -6
  18. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md032_blanks_around_lists.rs +1 -1
  19. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md034_no_bare_urls.rs +3 -9
  20. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md035_hr_style.rs +3 -11
  21. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md036_no_emphasis_only_first.rs +6 -0
  22. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md037_spaces_around_emphasis.rs +3 -9
  23. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md041_first_line_heading.rs +2 -9
  24. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md044_proper_names.rs +13 -0
  25. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md045_no_alt_text.rs +11 -9
  26. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md046_code_block_style.rs +0 -6
  27. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md048_code_fence_style.rs +6 -0
  28. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md051_link_fragments.rs +6 -0
  29. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md052_reference_links_images.rs +6 -0
  30. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md053_link_image_reference_definitions.rs +6 -0
  31. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md055_table_pipe_style.rs +5 -0
  32. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md056_table_column_count.rs +5 -0
  33. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md058_blanks_around_tables.rs +5 -0
  34. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/regex_cache.rs +35 -0
  35. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/list_rules_integration_test.rs +7 -7
  36. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md005_test.rs +21 -8
  37. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md005_unicode_test.rs +1 -1
  38. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md007_test.rs +45 -17
  39. {rumdl-0.0.100 → rumdl-0.0.102}/.config/nextest.toml +0 -0
  40. {rumdl-0.0.100 → rumdl-0.0.102}/.mise.toml +0 -0
  41. {rumdl-0.0.100 → rumdl-0.0.102}/.pre-commit-config.yaml +0 -0
  42. {rumdl-0.0.100 → rumdl-0.0.102}/.rumdl.toml +0 -0
  43. {rumdl-0.0.100 → rumdl-0.0.102}/.rustfmt.toml +0 -0
  44. {rumdl-0.0.100 → rumdl-0.0.102}/LICENSE +0 -0
  45. {rumdl-0.0.100 → rumdl-0.0.102}/MANIFEST.in +0 -0
  46. {rumdl-0.0.100 → rumdl-0.0.102}/Makefile +0 -0
  47. {rumdl-0.0.100 → rumdl-0.0.102}/README.md +0 -0
  48. {rumdl-0.0.100 → rumdl-0.0.102}/assets/logo.png +0 -0
  49. {rumdl-0.0.100 → rumdl-0.0.102}/benches/fix_performance.rs +0 -0
  50. {rumdl-0.0.100 → rumdl-0.0.102}/benches/range_performance.rs +0 -0
  51. {rumdl-0.0.100 → rumdl-0.0.102}/benches/range_utils_benchmark.rs +0 -0
  52. {rumdl-0.0.100 → rumdl-0.0.102}/benches/rule_performance.rs +0 -0
  53. {rumdl-0.0.100 → rumdl-0.0.102}/benches/simple_fix_bench.rs +0 -0
  54. {rumdl-0.0.100 → rumdl-0.0.102}/docs/RULES.md +0 -0
  55. {rumdl-0.0.100 → rumdl-0.0.102}/docs/global-settings.md +0 -0
  56. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md001.md +0 -0
  57. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md002.md +0 -0
  58. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md003.md +0 -0
  59. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md004.md +0 -0
  60. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md005.md +0 -0
  61. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md006.md +0 -0
  62. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md007.md +0 -0
  63. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md009.md +0 -0
  64. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md010.md +0 -0
  65. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md011.md +0 -0
  66. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md012.md +0 -0
  67. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md013.md +0 -0
  68. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md014.md +0 -0
  69. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md018.md +0 -0
  70. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md019.md +0 -0
  71. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md020.md +0 -0
  72. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md021.md +0 -0
  73. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md022.md +0 -0
  74. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md023.md +0 -0
  75. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md024.md +0 -0
  76. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md025.md +0 -0
  77. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md026.md +0 -0
  78. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md027.md +0 -0
  79. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md028.md +0 -0
  80. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md029.md +0 -0
  81. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md030.md +0 -0
  82. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md031.md +0 -0
  83. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md032.md +0 -0
  84. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md033.md +0 -0
  85. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md034.md +0 -0
  86. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md035.md +0 -0
  87. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md036.md +0 -0
  88. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md037.md +0 -0
  89. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md038.md +0 -0
  90. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md039.md +0 -0
  91. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md040.md +0 -0
  92. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md041.md +0 -0
  93. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md042.md +0 -0
  94. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md043.md +0 -0
  95. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md044.md +0 -0
  96. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md045.md +0 -0
  97. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md046.md +0 -0
  98. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md047.md +0 -0
  99. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md048.md +0 -0
  100. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md049.md +0 -0
  101. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md050.md +0 -0
  102. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md051.md +0 -0
  103. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md052.md +0 -0
  104. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md053.md +0 -0
  105. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md054.md +0 -0
  106. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md055.md +0 -0
  107. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md056.md +0 -0
  108. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md057.md +0 -0
  109. {rumdl-0.0.100 → rumdl-0.0.102}/docs/md058.md +0 -0
  110. {rumdl-0.0.100 → rumdl-0.0.102}/docs/vscode-extension.md +0 -0
  111. {rumdl-0.0.100 → rumdl-0.0.102}/parity_check.py +0 -0
  112. {rumdl-0.0.100 → rumdl-0.0.102}/pyproject.toml +0 -0
  113. {rumdl-0.0.100 → rumdl-0.0.102}/python/MANIFEST.in +0 -0
  114. {rumdl-0.0.100 → rumdl-0.0.102}/python/PYTHON-README.md +0 -0
  115. {rumdl-0.0.100 → rumdl-0.0.102}/python/rumdl/__init__.py +0 -0
  116. {rumdl-0.0.100 → rumdl-0.0.102}/python/rumdl/__main__.py +0 -0
  117. {rumdl-0.0.100 → rumdl-0.0.102}/python/rumdl/py.typed +0 -0
  118. {rumdl-0.0.100 → rumdl-0.0.102}/rumdl.toml.example +0 -0
  119. {rumdl-0.0.100 → rumdl-0.0.102}/rust-toolchain.toml +0 -0
  120. {rumdl-0.0.100 → rumdl-0.0.102}/scripts/extract-changelog.sh +0 -0
  121. {rumdl-0.0.100 → rumdl-0.0.102}/scripts/prepare-release.sh +0 -0
  122. {rumdl-0.0.100 → rumdl-0.0.102}/scripts/setup-pre-commit.sh +0 -0
  123. {rumdl-0.0.100 → rumdl-0.0.102}/scripts/update-pre-commit-docs.sh +0 -0
  124. {rumdl-0.0.100 → rumdl-0.0.102}/src/bin/benchmark.rs +0 -0
  125. {rumdl-0.0.100 → rumdl-0.0.102}/src/bin/benchmark_rule.rs +0 -0
  126. {rumdl-0.0.100 → rumdl-0.0.102}/src/bin/file_parallel_benchmark.rs +0 -0
  127. {rumdl-0.0.100 → rumdl-0.0.102}/src/bin/measure_code_span_performance.rs +0 -0
  128. {rumdl-0.0.100 → rumdl-0.0.102}/src/config.rs +0 -0
  129. {rumdl-0.0.100 → rumdl-0.0.102}/src/exit_codes.rs +0 -0
  130. {rumdl-0.0.100 → rumdl-0.0.102}/src/init.rs +0 -0
  131. {rumdl-0.0.100 → rumdl-0.0.102}/src/inline_config.rs +0 -0
  132. {rumdl-0.0.100 → rumdl-0.0.102}/src/lib.rs +0 -0
  133. {rumdl-0.0.100 → rumdl-0.0.102}/src/lint_context.rs +0 -0
  134. {rumdl-0.0.100 → rumdl-0.0.102}/src/lsp/mod.rs +0 -0
  135. {rumdl-0.0.100 → rumdl-0.0.102}/src/lsp/server.rs +0 -0
  136. {rumdl-0.0.100 → rumdl-0.0.102}/src/lsp/types.rs +0 -0
  137. {rumdl-0.0.100 → rumdl-0.0.102}/src/markdownlint_config.rs +0 -0
  138. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/azure.rs +0 -0
  139. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/concise.rs +0 -0
  140. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/github.rs +0 -0
  141. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/gitlab.rs +0 -0
  142. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/grouped.rs +0 -0
  143. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/json.rs +0 -0
  144. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/json_lines.rs +0 -0
  145. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/junit.rs +0 -0
  146. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/mod.rs +0 -0
  147. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/pylint.rs +0 -0
  148. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/sarif.rs +0 -0
  149. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/formatters/text.rs +0 -0
  150. {rumdl-0.0.100 → rumdl-0.0.102}/src/output/mod.rs +0 -0
  151. {rumdl-0.0.100 → rumdl-0.0.102}/src/parallel.rs +0 -0
  152. {rumdl-0.0.100 → rumdl-0.0.102}/src/performance.rs +0 -0
  153. {rumdl-0.0.100 → rumdl-0.0.102}/src/profiling.rs +0 -0
  154. {rumdl-0.0.100 → rumdl-0.0.102}/src/python.rs +0 -0
  155. {rumdl-0.0.100 → rumdl-0.0.102}/src/rule.rs +0 -0
  156. {rumdl-0.0.100 → rumdl-0.0.102}/src/rule_config.rs +0 -0
  157. {rumdl-0.0.100 → rumdl-0.0.102}/src/rule_config_serde.rs +0 -0
  158. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/blockquote_utils.rs +0 -0
  159. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/code_block_utils.rs +0 -0
  160. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/code_fence_utils.rs +0 -0
  161. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/emphasis_style.rs +0 -0
  162. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/front_matter_utils.rs +0 -0
  163. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/heading_utils.rs +0 -0
  164. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/list_utils.rs +0 -0
  165. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md001_heading_increment.rs +0 -0
  166. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md002_first_heading_h1/md002_config.rs +0 -0
  167. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md002_first_heading_h1.rs +0 -0
  168. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md003_heading_style/md003_config.rs +0 -0
  169. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md003_heading_style.rs +0 -0
  170. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md004_unordered_list_style/md004_config.rs +0 -0
  171. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md004_unordered_list_style.rs +0 -0
  172. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md007_ul_indent/md007_config.rs +0 -0
  173. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md009_trailing_spaces/md009_config.rs +0 -0
  174. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md010_no_hard_tabs/md010_config.rs +0 -0
  175. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md012_no_multiple_blanks/md012_config.rs +0 -0
  176. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md012_no_multiple_blanks.rs +0 -0
  177. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md013_line_length/md013_config.rs +0 -0
  178. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md014_commands_show_output/md014_config.rs +0 -0
  179. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md014_commands_show_output.rs +0 -0
  180. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md018_no_missing_space_atx.rs +0 -0
  181. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md019_no_multiple_space_atx.rs +0 -0
  182. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
  183. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md022_blanks_around_headings/md022_config.rs +0 -0
  184. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md022_blanks_around_headings.rs +0 -0
  185. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md023_heading_start_left.rs +0 -0
  186. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md024_no_duplicate_heading/md024_config.rs +0 -0
  187. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md024_no_duplicate_heading.rs +0 -0
  188. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md025_single_title/md025_config.rs +0 -0
  189. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md026_no_trailing_punctuation/md026_config.rs +0 -0
  190. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
  191. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md028_no_blanks_blockquote.rs +0 -0
  192. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md030_list_marker_space/md030_config.rs +0 -0
  193. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md030_list_marker_space.rs +0 -0
  194. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md033_no_inline_html/md033_config.rs +0 -0
  195. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md033_no_inline_html.rs +0 -0
  196. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md035_hr_style/md035_config.rs +0 -0
  197. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md036_no_emphasis_only_first/md036_config.rs +0 -0
  198. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md038_no_space_in_code.rs +0 -0
  199. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md039_no_space_in_links.rs +0 -0
  200. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md040_fenced_code_language.rs +0 -0
  201. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md042_no_empty_links.rs +0 -0
  202. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md043_required_headings.rs +0 -0
  203. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md044_proper_names/md044_config.rs +0 -0
  204. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md045_no_alt_text/md045_config.rs +0 -0
  205. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md046_code_block_style/md046_config.rs +0 -0
  206. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md047_single_trailing_newline.rs +0 -0
  207. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md048_code_fence_style/md048_config.rs +0 -0
  208. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md049_emphasis_style/md049_config.rs +0 -0
  209. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md049_emphasis_style.rs +0 -0
  210. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md050_strong_style/md050_config.rs +0 -0
  211. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md050_strong_style.rs +0 -0
  212. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md054_link_image_style/md054_config.rs +0 -0
  213. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md054_link_image_style.rs +0 -0
  214. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md055_table_pipe_style/md055_config.rs +0 -0
  215. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md057_existing_relative_links/md057_config.rs +0 -0
  216. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/md057_existing_relative_links.rs +0 -0
  217. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/mod.rs +0 -0
  218. {rumdl-0.0.100 → rumdl-0.0.102}/src/rules/strong_style.rs +0 -0
  219. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/ast_utils.rs +0 -0
  220. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/code_block_utils.rs +0 -0
  221. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/document_structure.rs +0 -0
  222. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/early_returns.rs +0 -0
  223. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/element_cache.rs +0 -0
  224. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/emphasis_utils.rs +0 -0
  225. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/fix_utils.rs +0 -0
  226. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/markdown_elements.rs +0 -0
  227. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/mod.rs +0 -0
  228. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/range_utils.rs +0 -0
  229. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/string_interner.rs +0 -0
  230. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/table_utils.rs +0 -0
  231. {rumdl-0.0.100 → rumdl-0.0.102}/src/utils/text_reflow.rs +0 -0
  232. {rumdl-0.0.100 → rumdl-0.0.102}/src/vscode.rs +0 -0
  233. {rumdl-0.0.100 → rumdl-0.0.102}/tests/advanced_integration_tests.rs +0 -0
  234. {rumdl-0.0.100 → rumdl-0.0.102}/tests/character_ranges/additional_tests.rs +0 -0
  235. {rumdl-0.0.100 → rumdl-0.0.102}/tests/character_ranges/basic_tests.rs +0 -0
  236. {rumdl-0.0.100 → rumdl-0.0.102}/tests/character_ranges/comprehensive_tests.rs +0 -0
  237. {rumdl-0.0.100 → rumdl-0.0.102}/tests/character_ranges/extended_tests.rs +0 -0
  238. {rumdl-0.0.100 → rumdl-0.0.102}/tests/character_ranges/mod.rs +0 -0
  239. {rumdl-0.0.100 → rumdl-0.0.102}/tests/character_ranges/unicode_utils.rs +0 -0
  240. {rumdl-0.0.100 → rumdl-0.0.102}/tests/cli_duplication_test.rs +0 -0
  241. {rumdl-0.0.100 → rumdl-0.0.102}/tests/cli_explain_test.rs +0 -0
  242. {rumdl-0.0.100 → rumdl-0.0.102}/tests/cli_flag_precedence_test.rs +0 -0
  243. {rumdl-0.0.100 → rumdl-0.0.102}/tests/cli_integration_tests.rs +0 -0
  244. {rumdl-0.0.100 → rumdl-0.0.102}/tests/cli_lsp_fix_consistency.rs +0 -0
  245. {rumdl-0.0.100 → rumdl-0.0.102}/tests/cli_statistics_test.rs +0 -0
  246. {rumdl-0.0.100 → rumdl-0.0.102}/tests/commonmark_compliance_tests.rs +0 -0
  247. {rumdl-0.0.100 → rumdl-0.0.102}/tests/comprehensive_integration_tests.rs +0 -0
  248. {rumdl-0.0.100 → rumdl-0.0.102}/tests/comprehensive_output_format_tests.rs +0 -0
  249. {rumdl-0.0.100 → rumdl-0.0.102}/tests/config_application_tests.rs +0 -0
  250. {rumdl-0.0.100 → rumdl-0.0.102}/tests/config_file_command_test.rs +0 -0
  251. {rumdl-0.0.100 → rumdl-0.0.102}/tests/config_tests.rs +0 -0
  252. {rumdl-0.0.100 → rumdl-0.0.102}/tests/configuration_inheritance_tests.rs +0 -0
  253. {rumdl-0.0.100 → rumdl-0.0.102}/tests/consistency_regression_tests.rs +0 -0
  254. {rumdl-0.0.100 → rumdl-0.0.102}/tests/cross_platform_compatibility_tests.rs +0 -0
  255. {rumdl-0.0.100 → rumdl-0.0.102}/tests/deeply_nested_lists_performance_test.rs +0 -0
  256. {rumdl-0.0.100 → rumdl-0.0.102}/tests/escaped_brackets_test.rs +0 -0
  257. {rumdl-0.0.100 → rumdl-0.0.102}/tests/final_confidence_assessment.rs +0 -0
  258. {rumdl-0.0.100 → rumdl-0.0.102}/tests/init_command_test.rs +0 -0
  259. {rumdl-0.0.100 → rumdl-0.0.102}/tests/init_tests.rs +0 -0
  260. {rumdl-0.0.100 → rumdl-0.0.102}/tests/inline_config_test.rs +0 -0
  261. {rumdl-0.0.100 → rumdl-0.0.102}/tests/integration_tests.rs +0 -0
  262. {rumdl-0.0.100 → rumdl-0.0.102}/tests/json_output_test.rs +0 -0
  263. {rumdl-0.0.100 → rumdl-0.0.102}/tests/lib.rs +0 -0
  264. {rumdl-0.0.100 → rumdl-0.0.102}/tests/lsp_editor_integration_tests.rs +0 -0
  265. {rumdl-0.0.100 → rumdl-0.0.102}/tests/lsp_integration_tests.rs +0 -0
  266. {rumdl-0.0.100 → rumdl-0.0.102}/tests/lsp_memory_leak_tests.rs +0 -0
  267. {rumdl-0.0.100 → rumdl-0.0.102}/tests/lsp_tests.rs +0 -0
  268. {rumdl-0.0.100 → rumdl-0.0.102}/tests/malformed_markdown_stress_tests.rs +0 -0
  269. {rumdl-0.0.100 → rumdl-0.0.102}/tests/markdownlint_cli_integration.rs +0 -0
  270. {rumdl-0.0.100 → rumdl-0.0.102}/tests/markdownlint_config_test.rs +0 -0
  271. {rumdl-0.0.100 → rumdl-0.0.102}/tests/markdownlintignore_test.rs +0 -0
  272. {rumdl-0.0.100 → rumdl-0.0.102}/tests/md013_reflow_integration_test.rs +0 -0
  273. {rumdl-0.0.100 → rumdl-0.0.102}/tests/nested_code_block_test.rs +0 -0
  274. {rumdl-0.0.100 → rumdl-0.0.102}/tests/output_format_integration_tests.rs +0 -0
  275. {rumdl-0.0.100 → rumdl-0.0.102}/tests/output_format_tests.rs +0 -0
  276. {rumdl-0.0.100 → rumdl-0.0.102}/tests/perf_check.rs +0 -0
  277. {rumdl-0.0.100 → rumdl-0.0.102}/tests/performance_validation_tests.rs +0 -0
  278. {rumdl-0.0.100 → rumdl-0.0.102}/tests/pyproject_config_tests.rs +0 -0
  279. {rumdl-0.0.100 → rumdl-0.0.102}/tests/python_bindings_test.rs +0 -0
  280. {rumdl-0.0.100 → rumdl-0.0.102}/tests/real_world_repository_tests.rs +0 -0
  281. {rumdl-0.0.100 → rumdl-0.0.102}/tests/regression_prevention_tests.rs +0 -0
  282. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/emphasis_edge_cases_test.rs +0 -0
  283. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/heading_edge_cases_test.rs +0 -0
  284. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/inline_content_edge_cases_test.rs +0 -0
  285. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/link_edge_cases_test.rs +0 -0
  286. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md001_test.rs +0 -0
  287. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md001_unicode_test.rs +0 -0
  288. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md002_test.rs +0 -0
  289. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md003_test.rs +0 -0
  290. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md004_test.rs +0 -0
  291. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md006_test.rs +0 -0
  292. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md006_unicode_test.rs +0 -0
  293. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md009_test.rs +0 -0
  294. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md010_test.rs +0 -0
  295. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md011_test.rs +0 -0
  296. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md012_test.rs +0 -0
  297. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md013_test.rs +0 -0
  298. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md014_test.rs +0 -0
  299. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md018_test.rs +0 -0
  300. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md019_test.rs +0 -0
  301. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md020_test.rs +0 -0
  302. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md021_test.rs +0 -0
  303. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md022_test.rs +0 -0
  304. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md023_extended_test.rs +0 -0
  305. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md023_test.rs +0 -0
  306. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md024_test.rs +0 -0
  307. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md025_test.rs +0 -0
  308. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md026_test.rs +0 -0
  309. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md027_test.rs +0 -0
  310. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md028_test.rs +0 -0
  311. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md029_test.rs +0 -0
  312. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md029_unicode_test.rs +0 -0
  313. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md030_test.rs +0 -0
  314. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md031_test.rs +0 -0
  315. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md032_test.rs +0 -0
  316. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md033_extended_test.rs +0 -0
  317. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md033_test.rs +0 -0
  318. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md034_ipv6_test.rs +0 -0
  319. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md034_test.rs +0 -0
  320. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md035_test.rs +0 -0
  321. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md036_test.rs +0 -0
  322. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md037_test.rs +0 -0
  323. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md038_nested_backticks_test.rs +0 -0
  324. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md038_test.rs +0 -0
  325. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md039_test.rs +0 -0
  326. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md040_test.rs +0 -0
  327. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md041_test.rs +0 -0
  328. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md042_test.rs +0 -0
  329. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md043_test.rs +0 -0
  330. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md044_test.rs +0 -0
  331. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md045_test.rs +0 -0
  332. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md046_test.rs +0 -0
  333. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md047_test.rs +0 -0
  334. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md048_test.rs +0 -0
  335. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md049_test.rs +0 -0
  336. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md050_test.rs +0 -0
  337. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md051_test.rs +0 -0
  338. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md051_unicode_test.rs +0 -0
  339. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md052_test.rs +0 -0
  340. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md053_additional_test.rs +0 -0
  341. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md053_proptest.rs +0 -0
  342. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md053_test.rs +0 -0
  343. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md054_test.rs +0 -0
  344. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md054_unicode_test.rs +0 -0
  345. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md055_test.rs +0 -0
  346. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md056_test.rs +0 -0
  347. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md057_test.rs +0 -0
  348. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/md058_test.rs +0 -0
  349. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules/mod.rs +0 -0
  350. {rumdl-0.0.100 → rumdl-0.0.102}/tests/rules_mod_test.rs +0 -0
  351. {rumdl-0.0.100 → rumdl-0.0.102}/tests/thread_safety_tests.rs +0 -0
  352. {rumdl-0.0.100 → rumdl-0.0.102}/tests/unicode_edge_case_tests.rs +0 -0
  353. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils/blockquote_utils_test.rs +0 -0
  354. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils/code_block_utils_extended_test.rs +0 -0
  355. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils/code_block_utils_test.rs +0 -0
  356. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils/core_utils_test.rs +0 -0
  357. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils/front_matter_utils_test.rs +0 -0
  358. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils/line_index_test.rs +0 -0
  359. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils/mod.rs +0 -0
  360. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils_markdown_edge_cases.rs +0 -0
  361. {rumdl-0.0.100 → rumdl-0.0.102}/tests/utils_tests.rs +0 -0
  362. {rumdl-0.0.100 → rumdl-0.0.102}/tests/vscode_extension_fixes.rs +0 -0
  363. {rumdl-0.0.100 → rumdl-0.0.102}/tests/vscode_test.rs +0 -0
  364. {rumdl-0.0.100 → rumdl-0.0.102}/tests/vscode_tests.rs +0 -0
@@ -7,8 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.102] - 2025-07-24
11
+
12
+ ## [0.0.101] - 2025-07-23
13
+
10
14
  ## [0.0.100] - 2025-07-22
11
15
 
16
+ ### Performance Improvements
17
+ - **MD032**: Eliminated redundant DocumentStructure creation through optimization interface delegation
18
+ - Refactored check() method to delegate to check_with_structure() for shared parsing
19
+ - Added fix_with_structure() helper method for optimized fixing operations
20
+ - **List Processing**: Major refactoring of complex list block merging logic for better maintainability
21
+ - Extracted merge_adjacent_list_blocks into clean ListBlockMerger struct
22
+ - Introduced BlockSpacing enum for clear categorization of list spacing types
23
+ - Separated compatibility checking, spacing analysis, and merging logic into focused methods
24
+ - **Memory Management**: Added comprehensive performance stress tests for deeply nested lists
25
+ - Created benchmarks for up to 20 levels of nesting with measurable performance baselines
26
+ - Established performance thresholds: <3ms parsing, <4ms rule checking for extreme nesting
27
+ - Added memory stress testing to prevent performance regressions
28
+
29
+ ### Code Quality
30
+ - Improved separation of concerns in list processing logic
31
+ - Enhanced code maintainability through better structured algorithms
32
+ - Added comprehensive test coverage for pathological markdown structures
33
+
12
34
  ## [0.0.99] - 2025-07-22
13
35
 
14
36
  ### Fixed
@@ -225,7 +247,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
225
247
 
226
248
  - Initial implementation of remaining rules for markdownlint parity
227
249
 
228
- [Unreleased]: https://github.com/rvben/rumdl/compare/v0.0.100...HEAD
250
+ [Unreleased]: https://github.com/rvben/rumdl/compare/v0.0.102...HEAD
251
+ [0.0.102]: https://github.com/rvben/rumdl/compare/v0.0.101...v0.0.102
252
+ [0.0.101]: https://github.com/rvben/rumdl/compare/v0.0.100...v0.0.101
229
253
  [0.0.100]: https://github.com/rvben/rumdl/compare/v0.0.99...v0.0.100
230
254
  [0.0.99]: https://github.com/rvben/rumdl/compare/v0.0.98...v0.0.99
231
255
  [0.0.98]: https://github.com/rvben/rumdl/compare/v0.0.97...v0.0.98
@@ -1493,7 +1493,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
1493
1493
 
1494
1494
  [[package]]
1495
1495
  name = "rumdl"
1496
- version = "0.0.100"
1496
+ version = "0.0.102"
1497
1497
  dependencies = [
1498
1498
  "anyhow",
1499
1499
  "assert_cmd",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rumdl"
3
- version = "0.0.100"
3
+ version = "0.0.102"
4
4
  edition = "2024"
5
5
  rust-version = "1.88.0"
6
6
  description = "A fast Markdown linter written in Rust (Ru(st) MarkDown Linter)"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rumdl
3
- Version: 0.0.100
3
+ Version: 0.0.102
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -609,6 +609,37 @@ fn find_markdown_files(
609
609
  // Add support for .markdownlintignore file
610
610
  walk_builder.add_custom_ignore_filename(".markdownlintignore");
611
611
 
612
+ // --- Pre-check for explicit file paths ---
613
+ // If not in discovery mode, validate that specified paths exist
614
+ if !is_discovery_mode {
615
+ for path_str in paths {
616
+ let path = Path::new(path_str);
617
+ if !path.exists() {
618
+ return Err(format!("File not found: {path_str}").into());
619
+ }
620
+ // If it's a file, check if it's a markdown file and add it directly
621
+ if path.is_file() {
622
+ if let Some(ext) = path.extension() {
623
+ if ext == "md" || ext == "markdown" {
624
+ let cleaned_path = if let Some(stripped) = path_str.strip_prefix("./") {
625
+ stripped.to_string()
626
+ } else {
627
+ path_str.clone()
628
+ };
629
+ file_paths.push(cleaned_path);
630
+ }
631
+ }
632
+ }
633
+ }
634
+
635
+ // If we found files directly, skip the walker
636
+ if !file_paths.is_empty() {
637
+ file_paths.sort();
638
+ file_paths.dedup();
639
+ return Ok(file_paths);
640
+ }
641
+ }
642
+
612
643
  // --- Execute Walk ---
613
644
 
614
645
  for result in walk_builder.build() {
@@ -628,7 +659,12 @@ fn find_markdown_files(
628
659
  file_paths.push(cleaned_path);
629
660
  }
630
661
  }
631
- Err(err) => eprintln!("Error walking directory: {err}"),
662
+ Err(err) => {
663
+ // Only show generic walking errors for directories, not for missing files
664
+ if is_discovery_mode {
665
+ eprintln!("Error walking directory: {err}");
666
+ }
667
+ }
632
668
  }
633
669
  }
634
670
 
@@ -7,29 +7,63 @@ use crate::utils::range_utils::{LineIndex, calculate_match_range};
7
7
 
8
8
  use crate::rule::{Fix, LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
9
9
  use crate::utils::document_structure::DocumentStructure;
10
- use lazy_static::lazy_static;
11
- use regex::Regex;
10
+ // No regex patterns needed for this rule
12
11
  use std::collections::HashMap;
13
12
  use toml;
14
13
 
15
- lazy_static! {
16
- // Regex to match blockquote prefixes (one or more '>' with optional spaces)
17
- static ref BLOCKQUOTE_PREFIX: Regex = Regex::new(r"^(\s*>\s*)+").unwrap();
18
- }
19
-
20
14
  /// Rule MD005: Inconsistent indentation for list items at the same level
21
15
  #[derive(Clone)]
22
16
  pub struct MD005ListIndent;
23
17
 
24
18
  impl MD005ListIndent {
25
- // Determine the expected indentation for a list item at a specific level
19
+ // Determine the expected indentation for a list item
20
+ // Each nested item should align with the text content of its parent
26
21
  #[inline]
27
- fn get_expected_indent(level: usize) -> usize {
22
+ fn get_expected_indent(level: usize, parent_text_position: Option<usize>) -> usize {
28
23
  if level == 1 {
29
24
  0 // Top level items should be at the start of the line
25
+ } else if let Some(pos) = parent_text_position {
26
+ // Align with parent's text content
27
+ pos
30
28
  } else {
31
- 2 * (level - 1) // Nested items should be indented by 2 spaces per level
29
+ // Fallback to standard nested indentation: 2 spaces per level
30
+ 2 * (level - 1)
31
+ }
32
+ }
33
+
34
+ /// Get parent info for any list item to determine proper text alignment
35
+ /// Returns parent_text_position where the child should align
36
+ fn get_parent_text_position(
37
+ &self,
38
+ ctx: &crate::lint_context::LintContext,
39
+ current_line: usize,
40
+ current_indent: usize,
41
+ ) -> Option<usize> {
42
+ // Look backward from current line to find parent item
43
+ for line_idx in (1..current_line).rev() {
44
+ if let Some(line_info) = ctx.line_info(line_idx) {
45
+ if let Some(list_item) = &line_info.list_item {
46
+ // Found a list item - check if it's at a lower indentation (parent level)
47
+ if list_item.marker_column < current_indent {
48
+ // This is a parent item - calculate where child should align
49
+ if list_item.is_ordered {
50
+ // For ordered lists, align with text start
51
+ let text_start_pos = list_item.marker_column + list_item.marker.len() + 1; // +1 for space after marker
52
+ return Some(text_start_pos);
53
+ } else {
54
+ // For unordered lists, align with text start
55
+ let text_start_pos = list_item.marker_column + 2; // "* " or "- " or "+ "
56
+ return Some(text_start_pos);
57
+ }
58
+ }
59
+ }
60
+ // If we encounter non-blank, non-list content at column 0, stop looking
61
+ else if !line_info.is_blank && line_info.indent == 0 {
62
+ break;
63
+ }
64
+ }
32
65
  }
66
+ None
33
67
  }
34
68
 
35
69
  /// Group related list blocks that should be treated as one logical list structure
@@ -144,7 +178,19 @@ impl MD005ListIndent {
144
178
  // Sort by line number to process in order
145
179
  group.sort_by_key(|(line_num, _, _)| *line_num);
146
180
 
147
- let expected_indent = Self::get_expected_indent(level);
181
+ // Get parent text position for proper alignment
182
+ let parent_text_position = if level > 1 {
183
+ // Get parent info from the first item in the group
184
+ if let Some((line_num, indent, _)) = group.first() {
185
+ self.get_parent_text_position(ctx, *line_num, *indent)
186
+ } else {
187
+ None
188
+ }
189
+ } else {
190
+ None
191
+ };
192
+
193
+ let expected_indent = Self::get_expected_indent(level, parent_text_position);
148
194
 
149
195
  // Check if items in this level have consistent indentation
150
196
  let indents: std::collections::HashSet<usize> = group.iter().map(|(_, indent, _)| *indent).collect();
@@ -401,11 +447,13 @@ mod tests {
401
447
  let content = "\
402
448
  1. Item 1
403
449
  2. Item 2
404
- 1. Nested 1
405
- 2. Nested 2
450
+ 1. Nested 1
451
+ 2. Nested 2
406
452
  3. Item 3";
407
453
  let ctx = LintContext::new(content);
408
454
  let result = rule.check(&ctx).unwrap();
455
+ // With dynamic alignment, nested items should align with parent's text content
456
+ // Ordered items starting with "1. " have text at column 3, so nested items need 3 spaces
409
457
  assert!(result.is_empty());
410
458
  }
411
459
 
@@ -418,9 +466,11 @@ mod tests {
418
466
  * Nested 1";
419
467
  let ctx = LintContext::new(content);
420
468
  let result = rule.check(&ctx).unwrap();
421
- assert_eq!(result.len(), 2);
469
+ // With dynamic alignment, line 3 correctly aligns with line 2's text position
470
+ // Only line 2 is incorrectly indented
471
+ assert_eq!(result.len(), 1);
422
472
  let fixed = rule.fix(&ctx).unwrap();
423
- assert_eq!(fixed, "* Item 1\n * Item 2\n * Nested 1");
473
+ assert_eq!(fixed, "* Item 1\n * Item 2\n * Nested 1");
424
474
  }
425
475
 
426
476
  #[test]
@@ -434,7 +484,10 @@ mod tests {
434
484
  let result = rule.check(&ctx).unwrap();
435
485
  assert_eq!(result.len(), 1);
436
486
  let fixed = rule.fix(&ctx).unwrap();
437
- assert_eq!(fixed, "1. Item 1\n 2. Item 2\n 1. Nested 1");
487
+ // With dynamic alignment, ordered items align with parent's text content
488
+ // Line 1 text starts at col 3, so line 2 should have 3 spaces
489
+ // Line 3 already correctly aligns with line 2's text position
490
+ assert_eq!(fixed, "1. Item 1\n 2. Item 2\n 1. Nested 1");
438
491
  }
439
492
 
440
493
  #[test]
@@ -461,12 +514,15 @@ mod tests {
461
514
  let result = rule.check(&ctx).unwrap();
462
515
  assert_eq!(result.len(), 2);
463
516
  let fixed = rule.fix(&ctx).unwrap();
517
+ // With dynamic alignment:
518
+ // Level 2 aligns with Level 1's text (2 spaces)
519
+ // Level 3 aligns with Level 2's text (5 spaces: 2 + "* " + 1)
464
520
  assert_eq!(
465
521
  fixed,
466
522
  "\
467
523
  * Level 1
468
524
  * Level 2
469
- * Level 3"
525
+ * Level 3"
470
526
  );
471
527
  }
472
528
 
@@ -525,11 +581,14 @@ Even more text";
525
581
  * Back to 1";
526
582
  let ctx = LintContext::new(content);
527
583
  let result = rule.check(&ctx).unwrap();
528
- assert_eq!(result.len(), 4);
584
+ // With dynamic alignment, fewer items need correction
585
+ // Lines 2,4: should align with Level 1's text (2 spaces)
586
+ // Line 5: should align with "Back to 2"'s text (5 spaces)
587
+ assert_eq!(result.len(), 3);
529
588
  let fixed = rule.fix(&ctx).unwrap();
530
589
  assert_eq!(
531
590
  fixed,
532
- "* Level 1\n * Level 2\n * Level 3\n * Back to 2\n 1. Ordered 3\n 2. Still 3\n* Back to 1"
591
+ "* Level 1\n * Level 2\n * Level 3\n * Back to 2\n 1. Ordered 3\n 2. Still 3\n* Back to 1"
533
592
  );
534
593
  }
535
594
 
@@ -692,16 +751,14 @@ Even more text";
692
751
  * Wrong 4";
693
752
  let ctx = LintContext::new(content);
694
753
  let fixed = rule.fix(&ctx).unwrap();
695
- // Verify all items are correctly indented
754
+ // With dynamic alignment, items align with their parent's text content
696
755
  let lines: Vec<&str> = fixed.lines().collect();
697
756
  assert_eq!(lines[0], "* Item 1");
698
757
  assert_eq!(lines[1], " * Wrong 1");
699
- assert_eq!(lines[2], " * Wrong 2");
700
- assert_eq!(lines[3], " * Wrong 3");
701
- // The "Correct" item with 2 spaces is treated as level 3 after the 4-space item
702
- // This is because MD005 tracks consistency within the current list context
703
- assert_eq!(lines[4], " * Correct");
704
- assert_eq!(lines[5], " * Wrong 4");
758
+ assert_eq!(lines[2], " * Wrong 2"); // Aligns with line 2's text
759
+ assert_eq!(lines[3], " * Wrong 3"); // Aligns with line 3's text
760
+ assert_eq!(lines[4], " * Correct"); // Back to level 2, aligns with line 1's text
761
+ assert_eq!(lines[5], " * Wrong 4"); // Same level as "Correct"
705
762
  }
706
763
 
707
764
  #[test]
@@ -791,6 +848,56 @@ Even more text";
791
848
  assert!(result.iter().any(|w| w.line == 2 && w.message.contains("found 3")));
792
849
  }
793
850
 
851
+ #[test]
852
+ fn test_nested_bullets_under_numbered_items() {
853
+ let rule = MD005ListIndent;
854
+ let content = "\
855
+ 1. **Active Directory/LDAP**
856
+ - User authentication and directory services
857
+ - LDAP for user information and validation
858
+
859
+ 2. **Oracle Unified Directory (OUD)**
860
+ - Extended user directory services
861
+ - Verification of project account presence and changes";
862
+ let ctx = LintContext::new(content);
863
+ let result = rule.check(&ctx).unwrap();
864
+ // Should have no warnings - 3 spaces is correct for bullets under numbered items
865
+ assert!(
866
+ result.is_empty(),
867
+ "Expected no warnings for bullets with 3 spaces under numbered items, got: {result:?}"
868
+ );
869
+ }
870
+
871
+ #[test]
872
+ fn test_nested_bullets_under_numbered_items_wrong_indent() {
873
+ let rule = MD005ListIndent;
874
+ let content = "\
875
+ 1. **Active Directory/LDAP**
876
+ - Wrong: only 2 spaces
877
+ - Correct: 3 spaces";
878
+ let ctx = LintContext::new(content);
879
+ let result = rule.check(&ctx).unwrap();
880
+ // Should flag the 2-space indentation as wrong
881
+ assert_eq!(result.len(), 2); // Both items flagged due to inconsistency
882
+ assert!(result.iter().any(|w| w.line == 2 && w.message.contains("found 2")));
883
+ }
884
+
885
+ #[test]
886
+ fn test_regular_nested_bullets_still_work() {
887
+ let rule = MD005ListIndent;
888
+ let content = "\
889
+ * Top level
890
+ * Second level (2 spaces is correct for bullets under bullets)
891
+ * Third level (4 spaces)";
892
+ let ctx = LintContext::new(content);
893
+ let result = rule.check(&ctx).unwrap();
894
+ // Should have no warnings - regular bullet nesting still uses 2-space increments
895
+ assert!(
896
+ result.is_empty(),
897
+ "Expected no warnings for regular bullet nesting, got: {result:?}"
898
+ );
899
+ }
900
+
794
901
  #[test]
795
902
  fn test_fix_range_accuracy() {
796
903
  let rule = MD005ListIndent;
@@ -2,8 +2,7 @@ use crate::utils::range_utils::LineIndex;
2
2
 
3
3
  use crate::rule::{Fix, LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
4
4
  use crate::utils::document_structure::{DocumentStructure, DocumentStructureExtensions};
5
- use lazy_static::lazy_static;
6
- use regex::Regex;
5
+ use crate::utils::regex_cache::UNORDERED_LIST_MARKER_REGEX;
7
6
 
8
7
  /// Rule MD006: Consider starting bulleted lists at the leftmost column
9
8
  ///
@@ -16,15 +15,33 @@ use regex::Regex;
16
15
  #[derive(Clone)]
17
16
  pub struct MD006StartBullets;
18
17
 
19
- lazy_static! {
20
- // Pattern to match bullet list items: captures indentation, marker, and space after marker
21
- static ref BULLET_PATTERN: Regex = Regex::new(r"^(\s*)([*+-])(\s+)").unwrap();
22
-
23
- // Pattern to match code fence markers
24
- static ref CODE_FENCE_PATTERN: Regex = Regex::new(r"^(\s*)(```|~~~)").unwrap();
25
- }
26
-
27
18
  impl MD006StartBullets {
19
+ /// Check if a bullet is nested under an ordered list item
20
+ fn is_nested_under_ordered_item(
21
+ &self,
22
+ ctx: &crate::lint_context::LintContext,
23
+ current_line: usize,
24
+ current_indent: usize,
25
+ ) -> bool {
26
+ // Look backward from current line to find parent item
27
+ for line_idx in (1..current_line).rev() {
28
+ if let Some(line_info) = ctx.line_info(line_idx) {
29
+ if let Some(list_item) = &line_info.list_item {
30
+ // Found a list item - check if it's at a lower indentation (parent level)
31
+ if list_item.marker_column < current_indent {
32
+ // This is a parent item - check if it's ordered
33
+ return list_item.is_ordered;
34
+ }
35
+ }
36
+ // If we encounter non-blank, non-list content at column 0, stop looking
37
+ else if !line_info.is_blank && line_info.indent == 0 {
38
+ break;
39
+ }
40
+ }
41
+ }
42
+ false
43
+ }
44
+
28
45
  /// Optimized check using centralized list blocks
29
46
  fn check_optimized(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
30
47
  let content = ctx.content;
@@ -56,21 +73,26 @@ impl MD006StartBullets {
56
73
  // Top-level items are always valid
57
74
  is_valid = true;
58
75
  } else {
59
- // Check if this is a valid nested item
60
- match Self::find_relevant_previous_bullet(&lines, line_idx) {
61
- Some((prev_idx, prev_indent)) => {
62
- match prev_indent.cmp(&indent) {
63
- std::cmp::Ordering::Less | std::cmp::Ordering::Equal => {
64
- // Valid nesting or sibling if previous item was valid
65
- is_valid = valid_bullet_lines[prev_idx];
66
- }
67
- std::cmp::Ordering::Greater => {
68
- // remains invalid
76
+ // Check if this is nested under an ordered item with correct indentation (3 spaces)
77
+ if indent == 3 && self.is_nested_under_ordered_item(ctx, item_line, indent) {
78
+ is_valid = true;
79
+ } else {
80
+ // Check if this is a valid nested item under another bullet
81
+ match Self::find_relevant_previous_bullet(&lines, line_idx) {
82
+ Some((prev_idx, prev_indent)) => {
83
+ match prev_indent.cmp(&indent) {
84
+ std::cmp::Ordering::Less | std::cmp::Ordering::Equal => {
85
+ // Valid nesting or sibling if previous item was valid
86
+ is_valid = valid_bullet_lines[prev_idx];
87
+ }
88
+ std::cmp::Ordering::Greater => {
89
+ // remains invalid
90
+ }
69
91
  }
70
92
  }
71
- }
72
- None => {
73
- // Indented item with no previous bullet remains invalid
93
+ None => {
94
+ // Indented item with no previous bullet remains invalid
95
+ }
74
96
  }
75
97
  }
76
98
  }
@@ -84,7 +106,7 @@ impl MD006StartBullets {
84
106
 
85
107
  // For the fix, we need to replace the highlighted part with just the bullet marker
86
108
  let trimmed = line.trim_start();
87
- let bullet_part = if let Some(captures) = BULLET_PATTERN.captures(trimmed) {
109
+ let bullet_part = if let Some(captures) = UNORDERED_LIST_MARKER_REGEX.captures(trimmed) {
88
110
  let marker = captures.get(2).map_or("*", |m| m.as_str());
89
111
  format!("{marker} ")
90
112
  } else {
@@ -123,7 +145,7 @@ impl MD006StartBullets {
123
145
  }
124
146
  /// Checks if a line is a bullet list item and returns its indentation level
125
147
  fn is_bullet_list_item(line: &str) -> Option<usize> {
126
- if let Some(captures) = BULLET_PATTERN.captures(line) {
148
+ if let Some(captures) = UNORDERED_LIST_MARKER_REGEX.captures(line) {
127
149
  if let Some(indent) = captures.get(1) {
128
150
  return Some(indent.as_str().len());
129
151
  }
@@ -338,7 +360,7 @@ impl Rule for MD006StartBullets {
338
360
  let line = lines[line_idx];
339
361
  let trimmed = line.trim_start();
340
362
  // Extract just the bullet marker and normalize to single space
341
- let bullet_part = if let Some(captures) = BULLET_PATTERN.captures(trimmed) {
363
+ let bullet_part = if let Some(captures) = UNORDERED_LIST_MARKER_REGEX.captures(trimmed) {
342
364
  format!("{} ", captures.get(2).unwrap().as_str()) // Always use single space
343
365
  } else {
344
366
  "* ".to_string() // fallback
@@ -453,4 +475,58 @@ mod tests {
453
475
  "Standard nesting (* Item -> * Item) should NOT generate warnings, found: {result:?}"
454
476
  );
455
477
  }
478
+
479
+ #[test]
480
+ fn test_bullets_nested_under_numbered_items() {
481
+ let rule = MD006StartBullets;
482
+ let content = "\
483
+ 1. **Active Directory/LDAP**
484
+ - User authentication and directory services
485
+ - LDAP for user information and validation
486
+
487
+ 2. **Oracle Unified Directory (OUD)**
488
+ - Extended user directory services";
489
+ let ctx = crate::lint_context::LintContext::new(content);
490
+ let result = rule.check(&ctx).unwrap();
491
+ // Should have no warnings - 3 spaces is valid for bullets under numbered items
492
+ assert!(
493
+ result.is_empty(),
494
+ "Expected no warnings for bullets with 3 spaces under numbered items, got: {result:?}"
495
+ );
496
+ }
497
+
498
+ #[test]
499
+ fn test_bullets_nested_under_numbered_items_wrong_indent() {
500
+ let rule = MD006StartBullets;
501
+ let content = "\
502
+ 1. **Active Directory/LDAP**
503
+ - Wrong: only 2 spaces
504
+ - Also wrong: 4 spaces";
505
+ let ctx = crate::lint_context::LintContext::new(content);
506
+ let result = rule.check(&ctx).unwrap();
507
+ // Should flag the incorrect indentations
508
+ assert_eq!(
509
+ result.len(),
510
+ 2,
511
+ "Expected warnings for bullets with incorrect spacing under numbered items"
512
+ );
513
+ assert!(result.iter().any(|w| w.line == 2));
514
+ assert!(result.iter().any(|w| w.line == 3));
515
+ }
516
+
517
+ #[test]
518
+ fn test_regular_bullet_nesting_still_works() {
519
+ let rule = MD006StartBullets;
520
+ let content = "\
521
+ * Top level
522
+ * Nested bullet (2 spaces is correct)
523
+ * Deeply nested (4 spaces)";
524
+ let ctx = crate::lint_context::LintContext::new(content);
525
+ let result = rule.check(&ctx).unwrap();
526
+ // Should have no warnings - standard bullet nesting still works
527
+ assert!(
528
+ result.is_empty(),
529
+ "Expected no warnings for standard bullet nesting, got: {result:?}"
530
+ );
531
+ }
456
532
  }