rumdl 0.0.72__tar.gz → 0.0.73__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 (263) hide show
  1. {rumdl-0.0.72 → rumdl-0.0.73}/Cargo.lock +1 -1
  2. {rumdl-0.0.72 → rumdl-0.0.73}/Cargo.toml +1 -1
  3. {rumdl-0.0.72 → rumdl-0.0.73}/PKG-INFO +1 -1
  4. {rumdl-0.0.72 → rumdl-0.0.73}/src/main.rs +7 -10
  5. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md006_start_bullets.rs +19 -8
  6. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md034_no_bare_urls.rs +81 -92
  7. rumdl-0.0.73/tests/rules/md006_test.rs +243 -0
  8. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md034_test.rs +69 -2
  9. rumdl-0.0.72/tests/rules/md006_test.rs +0 -143
  10. {rumdl-0.0.72 → rumdl-0.0.73}/.rumdl.toml +0 -0
  11. {rumdl-0.0.72 → rumdl-0.0.73}/MANIFEST.in +0 -0
  12. {rumdl-0.0.72 → rumdl-0.0.73}/Makefile +0 -0
  13. {rumdl-0.0.72 → rumdl-0.0.73}/README.md +0 -0
  14. {rumdl-0.0.72 → rumdl-0.0.73}/assets/logo.png +0 -0
  15. {rumdl-0.0.72 → rumdl-0.0.73}/benches/fix_performance.rs +0 -0
  16. {rumdl-0.0.72 → rumdl-0.0.73}/benches/range_performance.rs +0 -0
  17. {rumdl-0.0.72 → rumdl-0.0.73}/benches/range_utils_benchmark.rs +0 -0
  18. {rumdl-0.0.72 → rumdl-0.0.73}/benches/rule_performance.rs +0 -0
  19. {rumdl-0.0.72 → rumdl-0.0.73}/benches/simple_fix_bench.rs +0 -0
  20. {rumdl-0.0.72 → rumdl-0.0.73}/docs/RULES.md +0 -0
  21. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md001.md +0 -0
  22. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md002.md +0 -0
  23. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md003.md +0 -0
  24. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md004.md +0 -0
  25. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md005.md +0 -0
  26. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md006.md +0 -0
  27. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md007.md +0 -0
  28. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md009.md +0 -0
  29. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md010.md +0 -0
  30. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md011.md +0 -0
  31. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md012.md +0 -0
  32. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md013.md +0 -0
  33. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md014.md +0 -0
  34. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md018.md +0 -0
  35. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md019.md +0 -0
  36. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md020.md +0 -0
  37. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md021.md +0 -0
  38. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md022.md +0 -0
  39. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md023.md +0 -0
  40. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md024.md +0 -0
  41. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md025.md +0 -0
  42. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md026.md +0 -0
  43. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md027.md +0 -0
  44. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md028.md +0 -0
  45. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md029.md +0 -0
  46. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md030.md +0 -0
  47. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md031.md +0 -0
  48. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md032.md +0 -0
  49. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md033.md +0 -0
  50. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md034.md +0 -0
  51. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md035.md +0 -0
  52. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md036.md +0 -0
  53. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md037.md +0 -0
  54. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md038.md +0 -0
  55. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md039.md +0 -0
  56. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md040.md +0 -0
  57. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md041.md +0 -0
  58. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md042.md +0 -0
  59. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md043.md +0 -0
  60. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md044.md +0 -0
  61. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md045.md +0 -0
  62. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md046.md +0 -0
  63. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md047.md +0 -0
  64. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md048.md +0 -0
  65. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md049.md +0 -0
  66. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md050.md +0 -0
  67. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md051.md +0 -0
  68. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md052.md +0 -0
  69. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md053.md +0 -0
  70. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md054.md +0 -0
  71. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md055.md +0 -0
  72. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md056.md +0 -0
  73. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md057.md +0 -0
  74. {rumdl-0.0.72 → rumdl-0.0.73}/docs/md058.md +0 -0
  75. {rumdl-0.0.72 → rumdl-0.0.73}/issues/plan-rule-parity-with-markdownlint.md +0 -0
  76. {rumdl-0.0.72 → rumdl-0.0.73}/parity_check.py +0 -0
  77. {rumdl-0.0.72 → rumdl-0.0.73}/pyproject.toml +0 -0
  78. {rumdl-0.0.72 → rumdl-0.0.73}/python/MANIFEST.in +0 -0
  79. {rumdl-0.0.72 → rumdl-0.0.73}/python/PYTHON-README.md +0 -0
  80. {rumdl-0.0.72 → rumdl-0.0.73}/python/rumdl/__init__.py +0 -0
  81. {rumdl-0.0.72 → rumdl-0.0.73}/python/rumdl/__main__.py +0 -0
  82. {rumdl-0.0.72 → rumdl-0.0.73}/python/rumdl/py.typed +0 -0
  83. {rumdl-0.0.72 → rumdl-0.0.73}/rumdl.toml.example +0 -0
  84. {rumdl-0.0.72 → rumdl-0.0.73}/src/config.rs +0 -0
  85. {rumdl-0.0.72 → rumdl-0.0.73}/src/init.rs +0 -0
  86. {rumdl-0.0.72 → rumdl-0.0.73}/src/lib.rs +0 -0
  87. {rumdl-0.0.72 → rumdl-0.0.73}/src/lint_context.rs +0 -0
  88. {rumdl-0.0.72 → rumdl-0.0.73}/src/lsp/mod.rs +0 -0
  89. {rumdl-0.0.72 → rumdl-0.0.73}/src/lsp/server.rs +0 -0
  90. {rumdl-0.0.72 → rumdl-0.0.73}/src/lsp/types.rs +0 -0
  91. {rumdl-0.0.72 → rumdl-0.0.73}/src/markdownlint_config.rs +0 -0
  92. {rumdl-0.0.72 → rumdl-0.0.73}/src/parallel.rs +0 -0
  93. {rumdl-0.0.72 → rumdl-0.0.73}/src/performance.rs +0 -0
  94. {rumdl-0.0.72 → rumdl-0.0.73}/src/profiling.rs +0 -0
  95. {rumdl-0.0.72 → rumdl-0.0.73}/src/python.rs +0 -0
  96. {rumdl-0.0.72 → rumdl-0.0.73}/src/rule.rs +0 -0
  97. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/blockquote_utils.rs +0 -0
  98. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/code_block_utils.rs +0 -0
  99. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/code_fence_utils.rs +0 -0
  100. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/emphasis_style.rs +0 -0
  101. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/front_matter_utils.rs +0 -0
  102. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/heading_utils.rs +0 -0
  103. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/list_utils.rs +0 -0
  104. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md001_heading_increment.rs +0 -0
  105. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md002_first_heading_h1.rs +0 -0
  106. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md003_heading_style.rs +0 -0
  107. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md004_unordered_list_style.rs +0 -0
  108. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md005_list_indent.rs +0 -0
  109. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md007_ul_indent.rs +0 -0
  110. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md009_trailing_spaces.rs +0 -0
  111. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md010_no_hard_tabs.rs +0 -0
  112. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md011_no_reversed_links.rs +0 -0
  113. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md012_no_multiple_blanks.rs +0 -0
  114. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md013_line_length.rs +0 -0
  115. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md014_commands_show_output.rs +0 -0
  116. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md018_no_missing_space_atx.rs +0 -0
  117. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md019_no_multiple_space_atx.rs +0 -0
  118. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
  119. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
  120. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md022_blanks_around_headings.rs +0 -0
  121. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md023_heading_start_left.rs +0 -0
  122. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md024_no_duplicate_heading.rs +0 -0
  123. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md025_single_title.rs +0 -0
  124. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md026_no_trailing_punctuation.rs +0 -0
  125. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
  126. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md028_no_blanks_blockquote.rs +0 -0
  127. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md029_ordered_list_prefix.rs +0 -0
  128. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md030_list_marker_space.rs +0 -0
  129. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md031_blanks_around_fences.rs +0 -0
  130. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md032_blanks_around_lists.rs +0 -0
  131. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md033_no_inline_html.rs +0 -0
  132. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md035_hr_style.rs +0 -0
  133. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md036_no_emphasis_only_first.rs +0 -0
  134. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md037_spaces_around_emphasis.rs +0 -0
  135. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md038_no_space_in_code.rs +0 -0
  136. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md039_no_space_in_links.rs +0 -0
  137. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md040_fenced_code_language.rs +0 -0
  138. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md041_first_line_heading.rs +0 -0
  139. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md042_no_empty_links.rs +0 -0
  140. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md043_required_headings.rs +0 -0
  141. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md044_proper_names.rs +0 -0
  142. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md045_no_alt_text.rs +0 -0
  143. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md046_code_block_style.rs +0 -0
  144. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md047_single_trailing_newline.rs +0 -0
  145. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md048_code_fence_style.rs +0 -0
  146. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md049_emphasis_style.rs +0 -0
  147. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md050_strong_style.rs +0 -0
  148. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md051_link_fragments.rs +0 -0
  149. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md052_reference_links_images.rs +0 -0
  150. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md053_link_image_reference_definitions.rs +0 -0
  151. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md054_link_image_style.rs +0 -0
  152. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md055_table_pipe_style.rs +0 -0
  153. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md056_table_column_count.rs +0 -0
  154. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md057_existing_relative_links.rs +0 -0
  155. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/md058_blanks_around_tables.rs +0 -0
  156. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/mod.rs +0 -0
  157. {rumdl-0.0.72 → rumdl-0.0.73}/src/rules/strong_style.rs +0 -0
  158. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/ast_utils.rs +0 -0
  159. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/code_block_utils.rs +0 -0
  160. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/document_structure.rs +0 -0
  161. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/early_returns.rs +0 -0
  162. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/element_cache.rs +0 -0
  163. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/markdown_elements.rs +0 -0
  164. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/mod.rs +0 -0
  165. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/range_utils.rs +0 -0
  166. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/regex_cache.rs +0 -0
  167. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/string_interner.rs +0 -0
  168. {rumdl-0.0.72 → rumdl-0.0.73}/src/utils/table_utils.rs +0 -0
  169. {rumdl-0.0.72 → rumdl-0.0.73}/tests/advanced_integration_tests.rs +0 -0
  170. {rumdl-0.0.72 → rumdl-0.0.73}/tests/character_ranges/additional_tests.rs +0 -0
  171. {rumdl-0.0.72 → rumdl-0.0.73}/tests/character_ranges/basic_tests.rs +0 -0
  172. {rumdl-0.0.72 → rumdl-0.0.73}/tests/character_ranges/comprehensive_tests.rs +0 -0
  173. {rumdl-0.0.72 → rumdl-0.0.73}/tests/character_ranges/extended_tests.rs +0 -0
  174. {rumdl-0.0.72 → rumdl-0.0.73}/tests/character_ranges/mod.rs +0 -0
  175. {rumdl-0.0.72 → rumdl-0.0.73}/tests/character_ranges/unicode_utils.rs +0 -0
  176. {rumdl-0.0.72 → rumdl-0.0.73}/tests/cli_duplication_test.rs +0 -0
  177. {rumdl-0.0.72 → rumdl-0.0.73}/tests/cli_integration_tests.rs +0 -0
  178. {rumdl-0.0.72 → rumdl-0.0.73}/tests/commonmark_compliance_tests.rs +0 -0
  179. {rumdl-0.0.72 → rumdl-0.0.73}/tests/comprehensive_integration_tests.rs +0 -0
  180. {rumdl-0.0.72 → rumdl-0.0.73}/tests/config_application_tests.rs +0 -0
  181. {rumdl-0.0.72 → rumdl-0.0.73}/tests/config_tests.rs +0 -0
  182. {rumdl-0.0.72 → rumdl-0.0.73}/tests/init_command_test.rs +0 -0
  183. {rumdl-0.0.72 → rumdl-0.0.73}/tests/init_tests.rs +0 -0
  184. {rumdl-0.0.72 → rumdl-0.0.73}/tests/integration_tests.rs +0 -0
  185. {rumdl-0.0.72 → rumdl-0.0.73}/tests/json_output_test.rs +0 -0
  186. {rumdl-0.0.72 → rumdl-0.0.73}/tests/lib.rs +0 -0
  187. {rumdl-0.0.72 → rumdl-0.0.73}/tests/lsp_integration_tests.rs +0 -0
  188. {rumdl-0.0.72 → rumdl-0.0.73}/tests/lsp_tests.rs +0 -0
  189. {rumdl-0.0.72 → rumdl-0.0.73}/tests/markdownlint_cli_integration.rs +0 -0
  190. {rumdl-0.0.72 → rumdl-0.0.73}/tests/markdownlint_config_test.rs +0 -0
  191. {rumdl-0.0.72 → rumdl-0.0.73}/tests/md030_edge_cases.md +0 -0
  192. {rumdl-0.0.72 → rumdl-0.0.73}/tests/output_format_tests.rs +0 -0
  193. {rumdl-0.0.72 → rumdl-0.0.73}/tests/perf_check.rs +0 -0
  194. {rumdl-0.0.72 → rumdl-0.0.73}/tests/pyproject_config_tests.rs +0 -0
  195. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md001_test.rs +0 -0
  196. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md001_unicode_test.rs +0 -0
  197. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md002_test.rs +0 -0
  198. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md003_test.rs +0 -0
  199. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md004_test.rs +0 -0
  200. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md005_test.rs +0 -0
  201. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md006_unicode_test.rs +0 -0
  202. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md007_test.rs +0 -0
  203. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md009_test.rs +0 -0
  204. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md010_test.rs +0 -0
  205. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md011_test.rs +0 -0
  206. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md012_test.rs +0 -0
  207. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md013_test.rs +0 -0
  208. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md014_test.rs +0 -0
  209. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md018_test.rs +0 -0
  210. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md019_test.rs +0 -0
  211. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md020_test.rs +0 -0
  212. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md021_test.rs +0 -0
  213. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md022_test.rs +0 -0
  214. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md023_extended_test.rs +0 -0
  215. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md023_test.rs +0 -0
  216. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md024_test.rs +0 -0
  217. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md025_test.rs +0 -0
  218. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md026_test.rs +0 -0
  219. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md027_test.rs +0 -0
  220. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md028_test.rs +0 -0
  221. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md029_test.rs +0 -0
  222. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md030_test.rs +0 -0
  223. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md031_test.rs +0 -0
  224. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md032_test.rs +0 -0
  225. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md033_extended_test.rs +0 -0
  226. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md033_test.rs +0 -0
  227. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md035_test.rs +0 -0
  228. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md036_test.rs +0 -0
  229. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md037_test.rs +0 -0
  230. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md038_test.rs +0 -0
  231. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md039_test.rs +0 -0
  232. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md040_test.rs +0 -0
  233. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md041_test.rs +0 -0
  234. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md042_test.rs +0 -0
  235. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md043_test.rs +0 -0
  236. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md044_test.rs +0 -0
  237. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md045_test.rs +0 -0
  238. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md046_test.rs +0 -0
  239. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md047_test.rs +0 -0
  240. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md048_test.rs +0 -0
  241. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md049_test.rs +0 -0
  242. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md050_test.rs +0 -0
  243. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md051_test.rs +0 -0
  244. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md052_test.rs +0 -0
  245. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md053_additional_test.rs +0 -0
  246. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md053_proptest.rs +0 -0
  247. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md053_test.rs +0 -0
  248. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md054_test.rs +0 -0
  249. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md054_unicode_test.rs +0 -0
  250. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md055_test.rs +0 -0
  251. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md056_test.rs +0 -0
  252. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md057_test.rs +0 -0
  253. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/md058_test.rs +0 -0
  254. {rumdl-0.0.72 → rumdl-0.0.73}/tests/rules/mod.rs +0 -0
  255. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils/blockquote_utils_test.rs +0 -0
  256. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils/code_block_utils_extended_test.rs +0 -0
  257. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils/code_block_utils_test.rs +0 -0
  258. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils/core_utils_test.rs +0 -0
  259. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils/front_matter_utils_test.rs +0 -0
  260. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils/line_index_test.rs +0 -0
  261. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils/mod.rs +0 -0
  262. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils_markdown_edge_cases.rs +0 -0
  263. {rumdl-0.0.72 → rumdl-0.0.73}/tests/utils_tests.rs +0 -0
@@ -1789,7 +1789,7 @@ dependencies = [
1789
1789
 
1790
1790
  [[package]]
1791
1791
  name = "rumdl"
1792
- version = "0.0.72"
1792
+ version = "0.0.73"
1793
1793
  dependencies = [
1794
1794
  "anyhow",
1795
1795
  "assert_cmd",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rumdl"
3
- version = "0.0.72"
3
+ version = "0.0.73"
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.72
3
+ Version: 0.0.73
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -1297,16 +1297,13 @@ fn process_stdin(rules: &[Box<dyn Rule>], args: &CheckArgs) {
1297
1297
  let has_issues = !all_warnings.is_empty();
1298
1298
  if has_issues {
1299
1299
  for warning in &all_warnings {
1300
+ let rule_name = warning.rule_name.unwrap_or("unknown");
1300
1301
  println!(
1301
- "<stdin>:{}:{}: {}: {} [{}]",
1302
- warning.line,
1303
- warning.column,
1304
- match warning.severity {
1305
- rumdl::rule::Severity::Error => "error".red(),
1306
- rumdl::rule::Severity::Warning => "warning".yellow(),
1307
- },
1308
- warning.message,
1309
- warning.rule_name.unwrap_or("unknown")
1302
+ "<stdin>:{}:{}: {} {}",
1303
+ warning.line.to_string().cyan(),
1304
+ warning.column.to_string().cyan(),
1305
+ format!("[{:5}]", rule_name).yellow(), // Align rule names consistently
1306
+ warning.message
1310
1307
  );
1311
1308
  }
1312
1309
  }
@@ -1611,7 +1608,7 @@ fn process_file(
1611
1608
  file_path.blue().underline(),
1612
1609
  warning.line.to_string().cyan(),
1613
1610
  warning.column.to_string().cyan(),
1614
- format!("[{}]", rule_name).yellow(),
1611
+ format!("[{:5}]", rule_name).yellow(), // Pad rule name to 5 characters for alignment
1615
1612
  warning.message,
1616
1613
  fix_indicator.green()
1617
1614
  );
@@ -66,9 +66,15 @@ impl MD006StartBullets {
66
66
  if Self::is_bullet_list_item(lines[check_idx]).is_none() {
67
67
  // Found non-list content - check if it breaks the list structure
68
68
  let content_indent = lines[check_idx].len() - lines[check_idx].trim_start().len();
69
- // Content is only acceptable if it's indented at least as much as current item
70
- // AND we have a true parent relationship (prev_indent < current_indent)
71
- if content_indent < current_indent || prev_indent >= current_indent {
69
+
70
+ // Content is acceptable if:
71
+ // 1. It's indented at least as much as the current item (continuation of parent)
72
+ // 2. OR it's indented more than the previous bullet (continuation of previous item)
73
+ // 3. AND we have a true parent relationship (prev_indent < current_indent)
74
+ let is_continuation = content_indent >= prev_indent.max(2); // At least 2 spaces for continuation
75
+ let is_valid_nesting = prev_indent < current_indent;
76
+
77
+ if !is_continuation || !is_valid_nesting {
72
78
  has_breaking_content = true;
73
79
  break;
74
80
  }
@@ -78,13 +84,19 @@ impl MD006StartBullets {
78
84
  if !has_breaking_content {
79
85
  return Some((i, prev_indent));
80
86
  } else {
81
- // Content breaks the list structure
82
- return None;
87
+ // Content breaks the list structure, but continue searching for an earlier valid parent
88
+ continue;
83
89
  }
84
90
  }
85
91
  // If prev_indent > current_indent, it's a child of a sibling, ignore it and keep searching.
86
92
  } else {
87
- // Found non-list content - this breaks the search
93
+ // Found non-list content - check if it's a continuation line
94
+ let content_indent = lines[i].len() - lines[i].trim_start().len();
95
+ // If it's indented enough to be a continuation, don't break the search
96
+ if content_indent >= 2 {
97
+ continue;
98
+ }
99
+ // Otherwise, this breaks the search
88
100
  return None;
89
101
  }
90
102
  }
@@ -247,8 +259,7 @@ impl Rule for MD006StartBullets {
247
259
  column: start_col,
248
260
  end_line: line_num,
249
261
  end_column: end_col,
250
- message: "List item should start at the beginning of the line (remove indentation)"
251
- .to_string(),
262
+ message: "List item indentation".to_string(),
252
263
  fix: Some(Fix {
253
264
  range: line_index.line_col_to_byte_range(line_num, 1),
254
265
  replacement,
@@ -96,61 +96,53 @@ impl MD034NoBareUrls {
96
96
  &url[..end]
97
97
  }
98
98
 
99
- // Find all bare URLs in a line, using DocumentStructure for code span detection
100
- fn find_bare_urls_with_structure(
99
+ // Uses DocumentStructure for code block and code span detection in check_with_structure.
100
+ pub fn check_with_structure(
101
101
  &self,
102
- line: &str,
103
- line_idx: usize,
102
+ ctx: &crate::lint_context::LintContext,
104
103
  structure: &crate::utils::document_structure::DocumentStructure,
105
- ) -> Vec<LintWarning> {
106
- let mut warnings = Vec::new();
107
-
108
- // Early return: empty lines
109
- if line.trim().is_empty() {
110
- return warnings;
111
- }
112
-
113
- // Fast path - check if line potentially contains a URL
114
- if !URL_QUICK_CHECK.is_match(line) {
115
- return warnings;
116
- }
104
+ ) -> LintResult {
105
+ let content = ctx.content;
117
106
 
118
- // Skip lines that consist only of a badge link
119
- if BADGE_LINK_LINE.is_match(line) {
120
- return warnings;
107
+ // Early return: skip if no URLs or emails
108
+ if self.should_skip(content) {
109
+ return Ok(vec![]);
121
110
  }
122
111
 
123
- // Early return: skip reference definitions
124
- if REFERENCE_DEF_RE.is_match(line) {
125
- return warnings;
126
- }
112
+ // Process the entire content to handle multi-line markdown links
113
+ let mut warnings = Vec::new();
127
114
 
128
- // --- NEW: Collect all link/image destination ranges using regex ---
115
+ // First, find all markdown link ranges across the entire content
129
116
  let mut excluded_ranges: Vec<(usize, usize)> = Vec::new();
130
- // Markdown links: [text](url)
131
- for cap in MARKDOWN_LINK_PATTERN.captures_iter(line) {
117
+
118
+ // Markdown links: [text](url) - handle multi-line
119
+ for cap in MARKDOWN_LINK_PATTERN.captures_iter(content) {
132
120
  if let Some(dest) = cap.get(1) {
133
121
  excluded_ranges.push((dest.start(), dest.end()));
134
122
  }
135
123
  }
136
- // Markdown images: ![alt](url)
137
- for cap in MARKDOWN_IMAGE_PATTERN.captures_iter(line) {
124
+
125
+ // Markdown images: ![alt](url) - handle multi-line
126
+ for cap in MARKDOWN_IMAGE_PATTERN.captures_iter(content) {
138
127
  if let Some(dest) = cap.get(2) {
139
128
  excluded_ranges.push((dest.start(), dest.end()));
140
129
  }
141
130
  }
131
+
142
132
  // Angle-bracket links: <url>
143
- for cap in ANGLE_LINK_PATTERN.captures_iter(line) {
133
+ for cap in ANGLE_LINK_PATTERN.captures_iter(content) {
144
134
  if let Some(m) = cap.get(1) {
145
135
  excluded_ranges.push((m.start(), m.end()));
146
136
  }
147
137
  }
138
+
148
139
  // HTML attribute URLs: src="url", href="url", etc.
149
- for cap in HTML_ATTRIBUTE_URL.captures_iter(line) {
140
+ for cap in HTML_ATTRIBUTE_URL.captures_iter(content) {
150
141
  if let Some(url_attr) = cap.get(1) {
151
142
  excluded_ranges.push((url_attr.start(), url_attr.end()));
152
143
  }
153
144
  }
145
+
154
146
  // Sort and merge overlapping ranges
155
147
  excluded_ranges.sort_by_key(|r| r.0);
156
148
  let mut merged: Vec<(usize, usize)> = Vec::new();
@@ -164,12 +156,13 @@ impl MD034NoBareUrls {
164
156
  merged.push((start, end));
165
157
  }
166
158
 
167
- for url_match in SIMPLE_URL_REGEX.find_iter(line) {
159
+ // Now find all URLs in the content and check if they're excluded
160
+ for url_match in SIMPLE_URL_REGEX.find_iter(content) {
168
161
  let url_start = url_match.start();
169
162
  let mut url_end = url_match.end();
170
163
 
171
164
  // Trim trailing punctuation that's likely sentence punctuation
172
- let raw_url = &line[url_start..url_end];
165
+ let raw_url = &content[url_start..url_end];
173
166
  let trimmed_url = self.trim_trailing_punctuation(raw_url);
174
167
  url_end = url_start + trimmed_url.len();
175
168
 
@@ -182,9 +175,9 @@ impl MD034NoBareUrls {
182
175
  let before = if url_start == 0 {
183
176
  None
184
177
  } else {
185
- line.get(url_start - 1..url_start)
178
+ content.get(url_start - 1..url_start)
186
179
  };
187
- let after = line.get(url_end..url_end + 1);
180
+ let after = content.get(url_end..url_end + 1);
188
181
  let is_valid_boundary = before.map_or(true, |c| {
189
182
  !c.chars().next().unwrap().is_alphanumeric() && c != "_"
190
183
  }) && after.map_or(true, |c| {
@@ -193,45 +186,67 @@ impl MD034NoBareUrls {
193
186
  if !is_valid_boundary {
194
187
  continue;
195
188
  }
196
- // Skip if this URL is within a code span (using DocumentStructure)
197
- if structure.is_in_code_span(line_idx + 1, url_start + 1) {
189
+
190
+ // Convert byte offset to line/column
191
+ let (line_num, col_num) = ctx.offset_to_line_col(url_start);
192
+
193
+ // Skip if this URL is within a code span
194
+ if structure.is_in_code_span(line_num, col_num) {
198
195
  continue;
199
196
  }
200
- // --- NEW: Skip if URL is within any excluded range (link/image dest) ---
197
+
198
+ // Skip if this URL is within a code block
199
+ if structure.is_in_code_block(line_num) {
200
+ continue;
201
+ }
202
+
203
+ // Skip if URL is within any excluded range (link/image dest)
201
204
  let in_any_range = merged
202
205
  .iter()
203
206
  .any(|(start, end)| url_start >= *start && url_end <= *end);
204
207
  if in_any_range {
205
208
  continue;
206
209
  }
210
+
211
+ // Skip reference definitions
212
+ let line_start = content[..url_start].rfind('\n').map(|i| i + 1).unwrap_or(0);
213
+ let line_end = content[url_start..].find('\n').map(|i| url_start + i).unwrap_or(content.len());
214
+ let line = &content[line_start..line_end];
215
+ if REFERENCE_DEF_RE.is_match(line) {
216
+ continue;
217
+ }
218
+
219
+ let url_text = &content[url_start..url_end];
207
220
  let (start_line, start_col, end_line, end_col) =
208
- calculate_url_range(line_idx + 1, line, url_start, url_end - url_start);
221
+ calculate_url_range(line_num, line, col_num - 1, url_text.len());
222
+
209
223
  warnings.push(LintWarning {
210
224
  rule_name: Some(self.name()),
211
225
  line: start_line,
212
226
  column: start_col,
213
227
  end_line,
214
228
  end_column: end_col,
215
- message: format!("Bare URL found (wrap in angle brackets: <URL> or use link syntax: [text](URL))"),
229
+ message: format!("Bare URL found"),
216
230
  severity: Severity::Warning,
217
231
  fix: Some(Fix {
218
232
  range: url_start..url_end,
219
- replacement: format!("<{}>", &line[url_start..url_end]),
233
+ replacement: format!("<{}>", url_text),
220
234
  }),
221
235
  });
222
236
  }
223
237
 
224
238
  // Check for email addresses - similar logic to URLs
225
- for email_match in EMAIL_REGEX.find_iter(line) {
239
+ for email_match in EMAIL_REGEX.find_iter(content) {
226
240
  let email_start = email_match.start();
227
241
  let email_end = email_match.end();
242
+
228
243
  // Manual boundary check: not part of a larger word
229
244
  let before = if email_start == 0 {
230
245
  None
231
246
  } else {
232
- line.get(email_start - 1..email_start)
247
+ content.get(email_start - 1..email_start)
233
248
  };
234
- let after = line.get(email_end..email_end + 1);
249
+ let after = content.get(email_end..email_end + 1);
235
250
  let is_valid_boundary = before.map_or(true, |c| {
236
251
  !c.chars().next().unwrap().is_alphanumeric() && c != "_" && c != "."
237
252
  }) && after.map_or(true, |c| {
@@ -240,10 +255,20 @@ impl MD034NoBareUrls {
240
255
  if !is_valid_boundary {
241
256
  continue;
242
257
  }
243
- // Skip if this email is within a code span (using DocumentStructure)
244
- if structure.is_in_code_span(line_idx + 1, email_start + 1) {
258
+
259
+ // Convert byte offset to line/column
260
+ let (line_num, col_num) = ctx.offset_to_line_col(email_start);
261
+
262
+ // Skip if this email is within a code span
263
+ if structure.is_in_code_span(line_num, col_num) {
264
+ continue;
265
+ }
266
+
267
+ // Skip if this email is within a code block
268
+ if structure.is_in_code_block(line_num) {
245
269
  continue;
246
270
  }
271
+
247
272
  // Skip if email is within any excluded range (link/image dest)
248
273
  let in_any_range = merged
249
274
  .iter()
@@ -251,65 +276,29 @@ impl MD034NoBareUrls {
251
276
  if in_any_range {
252
277
  continue;
253
278
  }
279
+
280
+ let email_text = &content[email_start..email_end];
281
+ let line_start = content[..email_start].rfind('\n').map(|i| i + 1).unwrap_or(0);
282
+ let line_end = content[email_start..].find('\n').map(|i| email_start + i).unwrap_or(content.len());
283
+ let line = &content[line_start..line_end];
254
284
  let (start_line, start_col, end_line, end_col) =
255
- calculate_url_range(line_idx + 1, line, email_start, email_end - email_start);
285
+ calculate_url_range(line_num, line, col_num - 1, email_text.len());
286
+
256
287
  warnings.push(LintWarning {
257
288
  rule_name: Some(self.name()),
258
289
  line: start_line,
259
290
  column: start_col,
260
291
  end_line,
261
292
  end_column: end_col,
262
- message: format!(
263
- "Bare email address found: {}",
264
- &line[email_start..email_end]
265
- ),
293
+ message: format!("Bare email address found"),
266
294
  severity: Severity::Warning,
267
295
  fix: Some(Fix {
268
296
  range: email_start..email_end,
269
- replacement: format!("<{}>", &line[email_start..email_end]),
297
+ replacement: format!("<{}>", email_text),
270
298
  }),
271
299
  });
272
300
  }
273
- warnings
274
- }
275
-
276
- // Uses DocumentStructure for code block and code span detection in check_with_structure.
277
- pub fn check_with_structure(
278
- &self,
279
- ctx: &crate::lint_context::LintContext,
280
- structure: &crate::utils::document_structure::DocumentStructure,
281
- ) -> LintResult {
282
- let content = ctx.content;
283
301
 
284
- // Early return: skip if no URLs or emails
285
- if self.should_skip(content) {
286
- return Ok(vec![]);
287
- }
288
-
289
- let mut warnings = Vec::new();
290
- for (i, line) in content.lines().enumerate() {
291
- // Fast path: Skip empty lines
292
- if line.trim().is_empty() {
293
- continue;
294
- }
295
-
296
- // Fast path: Skip lines without potential URLs or emails
297
- if !line.contains("http") && !line.contains("ftp") && !line.contains('@') {
298
- continue;
299
- }
300
-
301
- // Skip lines in code blocks
302
- if structure.is_in_code_block(i + 1) {
303
- continue;
304
- }
305
-
306
- // Fast path: Skip reference link definitions
307
- if REFERENCE_DEF_RE.is_match(line) {
308
- continue;
309
- }
310
-
311
- warnings.extend(self.find_bare_urls_with_structure(line, i, structure));
312
- }
313
302
  Ok(warnings)
314
303
  }
315
304
 
@@ -368,7 +357,7 @@ impl MD034NoBareUrls {
368
357
  column: start_col,
369
358
  end_line,
370
359
  end_column: end_col,
371
- message: format!("Bare URL found (wrap in angle brackets: <URL> or use link syntax: [text](URL))"),
360
+ message: format!("Bare URL found"),
372
361
  severity: Severity::Warning,
373
362
  fix: Some(Fix {
374
363
  range: offset..(offset + url_text.len()),
@@ -466,7 +455,7 @@ impl MD034NoBareUrls {
466
455
  column: start_col,
467
456
  end_line,
468
457
  end_column: end_col,
469
- message: format!("Bare URL found (wrap in angle brackets: <URL> or use link syntax: [text](URL))"),
458
+ message: format!("Bare URL found"),
470
459
  severity: Severity::Warning,
471
460
  fix: Some(Fix {
472
461
  range: offset..(offset + url_text.len()),
@@ -0,0 +1,243 @@
1
+ use rumdl::lint_context::LintContext;
2
+ use rumdl::rule::Rule;
3
+ use rumdl::rules::MD006StartBullets;
4
+
5
+ #[test]
6
+ fn test_valid_unordered_list() {
7
+ let rule = MD006StartBullets;
8
+ let content = "\
9
+ * Item 1
10
+ * Item 2
11
+ * Nested item
12
+ * Another nested item
13
+ * Item 3";
14
+ let ctx = LintContext::new(content);
15
+ let result = rule.check(&ctx).unwrap();
16
+ assert!(result.is_empty());
17
+ }
18
+
19
+ #[test]
20
+ fn test_valid_nested_list() {
21
+ let rule = MD006StartBullets;
22
+ let content = "\
23
+ * Item 1
24
+ * Item 2
25
+ * Deeply nested item
26
+ * Item 3";
27
+ let ctx = LintContext::new(content);
28
+ let result = rule.check(&ctx).unwrap();
29
+ assert!(
30
+ result.is_empty(),
31
+ "Valid nested lists should not generate warnings, found: {:?}",
32
+ result
33
+ );
34
+ }
35
+
36
+ #[test]
37
+ fn test_invalid_indented_list() {
38
+ let rule = MD006StartBullets;
39
+ let content = "\
40
+ Some text here.
41
+
42
+ * First item should not be indented
43
+ * Second item should not be indented
44
+ * Third item should not be indented";
45
+ let ctx = LintContext::new(content);
46
+ let result = rule.check(&ctx).unwrap();
47
+ assert_eq!(result.len(), 3);
48
+ let fixed = rule.fix(&ctx).unwrap();
49
+ assert_eq!(
50
+ fixed,
51
+ "\
52
+ Some text here.
53
+
54
+ * First item should not be indented
55
+ * Second item should not be indented
56
+ * Third item should not be indented"
57
+ );
58
+ }
59
+
60
+ #[test]
61
+ fn test_mixed_list_styles() {
62
+ let rule = MD006StartBullets;
63
+ let content = "\
64
+ * Item 1
65
+ * Nested item
66
+ * Item 2
67
+
68
+ - Another item
69
+ - Nested item
70
+ - Final item";
71
+ let ctx = LintContext::new(content);
72
+ let result = rule.check(&ctx).unwrap();
73
+ assert!(result.is_empty());
74
+ }
75
+
76
+ #[test]
77
+ fn test_multiple_lists() {
78
+ let rule = MD006StartBullets;
79
+ let content = "\
80
+ * First list item
81
+ * Second list item
82
+
83
+ Some text here
84
+
85
+ * Indented list 1
86
+ * Indented list 2";
87
+ let ctx = LintContext::new(content);
88
+ let result = rule.check(&ctx).unwrap();
89
+ assert_eq!(result.len(), 2);
90
+ let fixed = rule.fix(&ctx).unwrap();
91
+ assert_eq!(
92
+ fixed,
93
+ "\
94
+ * First list item
95
+ * Second list item
96
+
97
+ Some text here
98
+
99
+ * Indented list 1
100
+ * Indented list 2"
101
+ );
102
+ }
103
+
104
+ #[test]
105
+ fn test_empty_lines() {
106
+ let rule = MD006StartBullets;
107
+ let content = "\
108
+ * Item 1
109
+
110
+ * Nested item
111
+
112
+ * Item 2";
113
+ let ctx = LintContext::new(content);
114
+ let result = rule.check(&ctx).unwrap();
115
+ assert!(result.is_empty());
116
+ }
117
+
118
+ #[test]
119
+ fn test_no_lists() {
120
+ let rule = MD006StartBullets;
121
+ let content = "\
122
+ Just some text
123
+ More text
124
+ Even more text";
125
+ let ctx = LintContext::new(content);
126
+ let result = rule.check(&ctx).unwrap();
127
+ assert!(result.is_empty());
128
+ }
129
+
130
+ #[test]
131
+ fn test_code_blocks_ignored() {
132
+ let rule = MD006StartBullets;
133
+ let content = "\
134
+ ```markdown
135
+ * This indented item is inside a code block
136
+ * These should be ignored
137
+ ```
138
+
139
+ * Regular item outside code block";
140
+ let ctx = LintContext::new(content);
141
+ let result = rule.check(&ctx).unwrap();
142
+ assert!(result.is_empty());
143
+ }
144
+
145
+ // REGRESSION TESTS: Prevent false positives that were previously fixed
146
+
147
+ #[test]
148
+ fn test_nested_list_with_multiline_content_not_flagged() {
149
+ let rule = MD006StartBullets;
150
+ // This is the exact pattern that was causing false positives before the fix
151
+ let content = "\
152
+ - Introduces changes or additions to the [MLflow REST
153
+ API](https://mlflow.org/docs/latest/rest-api.html)
154
+ - The MLflow REST API is implemented by a variety of open source
155
+ and proprietary platforms. Changes to the REST API impact all of
156
+ these platforms. Accordingly, we encourage developers to
157
+ thoroughly explore alternatives before attempting to introduce
158
+ REST API changes.
159
+ - Introduces new user-facing MLflow APIs
160
+ - MLflow's API surface is carefully designed to generalize across
161
+ a variety of common ML operations. It is important to ensure
162
+ that new APIs are broadly useful to ML developers, easy to work
163
+ with, and simple yet powerful.";
164
+
165
+ let ctx = LintContext::new(content);
166
+ let result = rule.check(&ctx).unwrap();
167
+
168
+ // Should not flag any list items since they are properly nested
169
+ assert!(
170
+ result.is_empty(),
171
+ "Properly nested list items with multi-line content should not be flagged. Found {} warnings: {:#?}",
172
+ result.len(),
173
+ result
174
+ );
175
+
176
+ // Fix should not change anything since the list structure is correct
177
+ let fixed = rule.fix(&ctx).unwrap();
178
+ assert_eq!(
179
+ fixed, content,
180
+ "Fix should not change content with properly nested list items"
181
+ );
182
+ }
183
+
184
+ #[test]
185
+ fn test_nested_list_with_markdown_link_continuation() {
186
+ let rule = MD006StartBullets;
187
+ // Test that markdown link continuations don't break list nesting validation
188
+ let content = "\
189
+ * First item with [a link that spans
190
+ multiple lines](https://example.com/very/long/url)
191
+ * This nested item should be valid
192
+ * Another nested item
193
+ * Second top-level item";
194
+
195
+ let ctx = LintContext::new(content);
196
+ let result = rule.check(&ctx).unwrap();
197
+
198
+ // Should not flag any list items since the nesting is correct
199
+ assert!(
200
+ result.is_empty(),
201
+ "List items with markdown link continuations should not break nesting validation. Found {} warnings: {:#?}",
202
+ result.len(),
203
+ result
204
+ );
205
+ }
206
+
207
+ #[test]
208
+ fn test_mixed_valid_and_invalid_nesting() {
209
+ let rule = MD006StartBullets;
210
+ // Test that we correctly identify invalid nesting while preserving valid nesting
211
+ let content = "\
212
+ * Valid top-level item
213
+ * Valid nested item
214
+ * Valid deeply nested item
215
+
216
+ Some breaking content
217
+
218
+ * This should be flagged as invalid (indented without parent)
219
+ * This should also be flagged
220
+
221
+ * Valid top-level item after break
222
+ * Valid nested item after break";
223
+
224
+ let ctx = LintContext::new(content);
225
+ let result = rule.check(&ctx).unwrap();
226
+
227
+ // Should only flag the 2 items that are improperly indented after the break
228
+ assert_eq!(
229
+ result.len(),
230
+ 2,
231
+ "Should only flag improperly indented items, not valid nested items. Found {} warnings: {:#?}",
232
+ result.len(),
233
+ result
234
+ );
235
+
236
+ // Verify the correct lines are flagged
237
+ let flagged_lines: Vec<usize> = result.iter().map(|w| w.line).collect();
238
+ assert!(
239
+ flagged_lines.contains(&7) && flagged_lines.contains(&8),
240
+ "Should flag lines 7 and 8 (the improperly indented items), but flagged: {:?}",
241
+ flagged_lines
242
+ );
243
+ }