rumdl 0.0.65__tar.gz → 0.0.66__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 (251) hide show
  1. {rumdl-0.0.65 → rumdl-0.0.66}/Cargo.lock +13 -5
  2. {rumdl-0.0.65 → rumdl-0.0.66}/Cargo.toml +2 -2
  3. {rumdl-0.0.65 → rumdl-0.0.66}/PKG-INFO +1 -1
  4. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md006_start_bullets.rs +48 -12
  5. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md007_ul_indent.rs +70 -90
  6. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md030_list_marker_space.rs +3 -2
  7. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md034_no_bare_urls.rs +20 -31
  8. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md046_code_block_style.rs +136 -3
  9. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md052_reference_links_images.rs +43 -13
  10. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/element_cache.rs +195 -21
  11. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md007_test.rs +2 -2
  12. {rumdl-0.0.65 → rumdl-0.0.66}/.rumdl.toml +0 -0
  13. {rumdl-0.0.65 → rumdl-0.0.66}/MANIFEST.in +0 -0
  14. {rumdl-0.0.65 → rumdl-0.0.66}/Makefile +0 -0
  15. {rumdl-0.0.65 → rumdl-0.0.66}/README.md +0 -0
  16. {rumdl-0.0.65 → rumdl-0.0.66}/assets/logo.png +0 -0
  17. {rumdl-0.0.65 → rumdl-0.0.66}/benches/range_performance.rs +0 -0
  18. {rumdl-0.0.65 → rumdl-0.0.66}/benches/range_utils_benchmark.rs +0 -0
  19. {rumdl-0.0.65 → rumdl-0.0.66}/benches/rule_performance.rs +0 -0
  20. {rumdl-0.0.65 → rumdl-0.0.66}/docs/RULES.md +0 -0
  21. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md001.md +0 -0
  22. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md002.md +0 -0
  23. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md003.md +0 -0
  24. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md004.md +0 -0
  25. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md005.md +0 -0
  26. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md006.md +0 -0
  27. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md007.md +0 -0
  28. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md009.md +0 -0
  29. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md010.md +0 -0
  30. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md011.md +0 -0
  31. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md012.md +0 -0
  32. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md013.md +0 -0
  33. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md014.md +0 -0
  34. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md018.md +0 -0
  35. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md019.md +0 -0
  36. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md020.md +0 -0
  37. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md021.md +0 -0
  38. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md022.md +0 -0
  39. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md023.md +0 -0
  40. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md024.md +0 -0
  41. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md025.md +0 -0
  42. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md026.md +0 -0
  43. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md027.md +0 -0
  44. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md028.md +0 -0
  45. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md029.md +0 -0
  46. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md030.md +0 -0
  47. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md031.md +0 -0
  48. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md032.md +0 -0
  49. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md033.md +0 -0
  50. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md034.md +0 -0
  51. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md035.md +0 -0
  52. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md036.md +0 -0
  53. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md037.md +0 -0
  54. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md038.md +0 -0
  55. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md039.md +0 -0
  56. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md040.md +0 -0
  57. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md041.md +0 -0
  58. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md042.md +0 -0
  59. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md043.md +0 -0
  60. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md044.md +0 -0
  61. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md045.md +0 -0
  62. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md046.md +0 -0
  63. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md047.md +0 -0
  64. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md048.md +0 -0
  65. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md049.md +0 -0
  66. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md050.md +0 -0
  67. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md051.md +0 -0
  68. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md052.md +0 -0
  69. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md053.md +0 -0
  70. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md054.md +0 -0
  71. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md055.md +0 -0
  72. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md056.md +0 -0
  73. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md057.md +0 -0
  74. {rumdl-0.0.65 → rumdl-0.0.66}/docs/md058.md +0 -0
  75. {rumdl-0.0.65 → rumdl-0.0.66}/issues/plan-rule-parity-with-markdownlint.md +0 -0
  76. {rumdl-0.0.65 → rumdl-0.0.66}/parity_check.py +0 -0
  77. {rumdl-0.0.65 → rumdl-0.0.66}/pyproject.toml +0 -0
  78. {rumdl-0.0.65 → rumdl-0.0.66}/python/MANIFEST.in +0 -0
  79. {rumdl-0.0.65 → rumdl-0.0.66}/python/PYTHON-README.md +0 -0
  80. {rumdl-0.0.65 → rumdl-0.0.66}/python/rumdl/__init__.py +0 -0
  81. {rumdl-0.0.65 → rumdl-0.0.66}/python/rumdl/__main__.py +0 -0
  82. {rumdl-0.0.65 → rumdl-0.0.66}/python/rumdl/py.typed +0 -0
  83. {rumdl-0.0.65 → rumdl-0.0.66}/rumdl.toml.example +0 -0
  84. {rumdl-0.0.65 → rumdl-0.0.66}/src/config.rs +0 -0
  85. {rumdl-0.0.65 → rumdl-0.0.66}/src/init.rs +0 -0
  86. {rumdl-0.0.65 → rumdl-0.0.66}/src/lib.rs +0 -0
  87. {rumdl-0.0.65 → rumdl-0.0.66}/src/lint_context.rs +0 -0
  88. {rumdl-0.0.65 → rumdl-0.0.66}/src/lsp/mod.rs +0 -0
  89. {rumdl-0.0.65 → rumdl-0.0.66}/src/lsp/server.rs +0 -0
  90. {rumdl-0.0.65 → rumdl-0.0.66}/src/lsp/types.rs +0 -0
  91. {rumdl-0.0.65 → rumdl-0.0.66}/src/main.rs +0 -0
  92. {rumdl-0.0.65 → rumdl-0.0.66}/src/markdownlint_config.rs +0 -0
  93. {rumdl-0.0.65 → rumdl-0.0.66}/src/profiling.rs +0 -0
  94. {rumdl-0.0.65 → rumdl-0.0.66}/src/python.rs +0 -0
  95. {rumdl-0.0.65 → rumdl-0.0.66}/src/rule.rs +0 -0
  96. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/blockquote_utils.rs +0 -0
  97. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/code_block_utils.rs +0 -0
  98. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/code_fence_utils.rs +0 -0
  99. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/emphasis_style.rs +0 -0
  100. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/front_matter_utils.rs +0 -0
  101. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/heading_utils.rs +0 -0
  102. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/list_utils.rs +0 -0
  103. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md001_heading_increment.rs +0 -0
  104. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md002_first_heading_h1.rs +0 -0
  105. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md003_heading_style.rs +0 -0
  106. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md004_unordered_list_style.rs +0 -0
  107. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md005_list_indent.rs +0 -0
  108. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md009_trailing_spaces.rs +0 -0
  109. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md010_no_hard_tabs.rs +0 -0
  110. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md011_no_reversed_links.rs +0 -0
  111. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md012_no_multiple_blanks.rs +0 -0
  112. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md013_line_length.rs +0 -0
  113. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md014_commands_show_output.rs +0 -0
  114. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md018_no_missing_space_atx.rs +0 -0
  115. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md019_no_multiple_space_atx.rs +0 -0
  116. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
  117. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
  118. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md022_blanks_around_headings.rs +0 -0
  119. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md023_heading_start_left.rs +0 -0
  120. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md024_no_duplicate_heading.rs +0 -0
  121. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md025_single_title.rs +0 -0
  122. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md026_no_trailing_punctuation.rs +0 -0
  123. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
  124. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md028_no_blanks_blockquote.rs +0 -0
  125. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md029_ordered_list_prefix.rs +0 -0
  126. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md031_blanks_around_fences.rs +0 -0
  127. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md032_blanks_around_lists.rs +0 -0
  128. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md033_no_inline_html.rs +0 -0
  129. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md035_hr_style.rs +0 -0
  130. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md036_no_emphasis_only_first.rs +0 -0
  131. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md037_spaces_around_emphasis.rs +0 -0
  132. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md038_no_space_in_code.rs +0 -0
  133. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md039_no_space_in_links.rs +0 -0
  134. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md040_fenced_code_language.rs +0 -0
  135. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md041_first_line_heading.rs +0 -0
  136. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md042_no_empty_links.rs +0 -0
  137. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md043_required_headings.rs +0 -0
  138. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md044_proper_names.rs +0 -0
  139. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md045_no_alt_text.rs +0 -0
  140. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md047_single_trailing_newline.rs +0 -0
  141. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md048_code_fence_style.rs +0 -0
  142. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md049_emphasis_style.rs +0 -0
  143. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md050_strong_style.rs +0 -0
  144. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md051_link_fragments.rs +0 -0
  145. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md053_link_image_reference_definitions.rs +0 -0
  146. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md054_link_image_style.rs +0 -0
  147. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md055_table_pipe_style.rs +0 -0
  148. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md056_table_column_count.rs +0 -0
  149. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md057_existing_relative_links.rs +0 -0
  150. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/md058_blanks_around_tables.rs +0 -0
  151. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/mod.rs +0 -0
  152. {rumdl-0.0.65 → rumdl-0.0.66}/src/rules/strong_style.rs +0 -0
  153. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/ast_utils.rs +0 -0
  154. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/code_block_utils.rs +0 -0
  155. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/document_structure.rs +0 -0
  156. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/early_returns.rs +0 -0
  157. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/markdown_elements.rs +0 -0
  158. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/mod.rs +0 -0
  159. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/range_utils.rs +0 -0
  160. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/regex_cache.rs +0 -0
  161. {rumdl-0.0.65 → rumdl-0.0.66}/src/utils/string_interner.rs +0 -0
  162. {rumdl-0.0.65 → rumdl-0.0.66}/tests/advanced_integration_tests.rs +0 -0
  163. {rumdl-0.0.65 → rumdl-0.0.66}/tests/cli_duplication_test.rs +0 -0
  164. {rumdl-0.0.65 → rumdl-0.0.66}/tests/cli_integration_tests.rs +0 -0
  165. {rumdl-0.0.65 → rumdl-0.0.66}/tests/commonmark_compliance_tests.rs +0 -0
  166. {rumdl-0.0.65 → rumdl-0.0.66}/tests/comprehensive_integration_tests.rs +0 -0
  167. {rumdl-0.0.65 → rumdl-0.0.66}/tests/config_application_tests.rs +0 -0
  168. {rumdl-0.0.65 → rumdl-0.0.66}/tests/config_tests.rs +0 -0
  169. {rumdl-0.0.65 → rumdl-0.0.66}/tests/init_command_test.rs +0 -0
  170. {rumdl-0.0.65 → rumdl-0.0.66}/tests/init_tests.rs +0 -0
  171. {rumdl-0.0.65 → rumdl-0.0.66}/tests/integration_tests.rs +0 -0
  172. {rumdl-0.0.65 → rumdl-0.0.66}/tests/json_output_test.rs +0 -0
  173. {rumdl-0.0.65 → rumdl-0.0.66}/tests/lib.rs +0 -0
  174. {rumdl-0.0.65 → rumdl-0.0.66}/tests/lsp_integration_tests.rs +0 -0
  175. {rumdl-0.0.65 → rumdl-0.0.66}/tests/lsp_tests.rs +0 -0
  176. {rumdl-0.0.65 → rumdl-0.0.66}/tests/markdownlint_cli_integration.rs +0 -0
  177. {rumdl-0.0.65 → rumdl-0.0.66}/tests/markdownlint_config_test.rs +0 -0
  178. {rumdl-0.0.65 → rumdl-0.0.66}/tests/md030_edge_cases.md +0 -0
  179. {rumdl-0.0.65 → rumdl-0.0.66}/tests/output_format_tests.rs +0 -0
  180. {rumdl-0.0.65 → rumdl-0.0.66}/tests/perf_check.rs +0 -0
  181. {rumdl-0.0.65 → rumdl-0.0.66}/tests/pyproject_config_tests.rs +0 -0
  182. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md001_test.rs +0 -0
  183. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md001_unicode_test.rs +0 -0
  184. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md002_test.rs +0 -0
  185. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md003_test.rs +0 -0
  186. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md004_test.rs +0 -0
  187. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md005_test.rs +0 -0
  188. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md006_test.rs +0 -0
  189. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md006_unicode_test.rs +0 -0
  190. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md009_test.rs +0 -0
  191. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md010_test.rs +0 -0
  192. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md011_test.rs +0 -0
  193. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md012_test.rs +0 -0
  194. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md013_test.rs +0 -0
  195. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md014_test.rs +0 -0
  196. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md018_test.rs +0 -0
  197. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md019_test.rs +0 -0
  198. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md020_test.rs +0 -0
  199. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md021_test.rs +0 -0
  200. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md022_test.rs +0 -0
  201. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md023_extended_test.rs +0 -0
  202. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md023_test.rs +0 -0
  203. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md024_test.rs +0 -0
  204. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md025_test.rs +0 -0
  205. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md026_test.rs +0 -0
  206. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md027_test.rs +0 -0
  207. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md028_test.rs +0 -0
  208. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md029_test.rs +0 -0
  209. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md030_test.rs +0 -0
  210. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md031_test.rs +0 -0
  211. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md032_test.rs +0 -0
  212. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md033_extended_test.rs +0 -0
  213. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md033_test.rs +0 -0
  214. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md034_test.rs +0 -0
  215. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md035_test.rs +0 -0
  216. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md036_test.rs +0 -0
  217. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md037_test.rs +0 -0
  218. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md038_test.rs +0 -0
  219. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md039_test.rs +0 -0
  220. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md040_test.rs +0 -0
  221. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md041_test.rs +0 -0
  222. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md042_test.rs +0 -0
  223. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md043_test.rs +0 -0
  224. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md044_test.rs +0 -0
  225. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md045_test.rs +0 -0
  226. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md046_test.rs +0 -0
  227. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md047_test.rs +0 -0
  228. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md048_test.rs +0 -0
  229. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md049_test.rs +0 -0
  230. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md050_test.rs +0 -0
  231. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md051_test.rs +0 -0
  232. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md052_test.rs +0 -0
  233. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md053_additional_test.rs +0 -0
  234. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md053_proptest.rs +0 -0
  235. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md053_test.rs +0 -0
  236. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md054_test.rs +0 -0
  237. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md054_unicode_test.rs +0 -0
  238. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md055_test.rs +0 -0
  239. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md056_test.rs +0 -0
  240. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md057_test.rs +0 -0
  241. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/md058_test.rs +0 -0
  242. {rumdl-0.0.65 → rumdl-0.0.66}/tests/rules/mod.rs +0 -0
  243. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils/blockquote_utils_test.rs +0 -0
  244. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils/code_block_utils_extended_test.rs +0 -0
  245. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils/code_block_utils_test.rs +0 -0
  246. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils/core_utils_test.rs +0 -0
  247. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils/front_matter_utils_test.rs +0 -0
  248. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils/line_index_test.rs +0 -0
  249. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils/mod.rs +0 -0
  250. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils_markdown_edge_cases.rs +0 -0
  251. {rumdl-0.0.65 → rumdl-0.0.66}/tests/utils_tests.rs +0 -0
@@ -1704,7 +1704,7 @@ dependencies = [
1704
1704
 
1705
1705
  [[package]]
1706
1706
  name = "rumdl"
1707
- version = "0.0.65"
1707
+ version = "0.0.66"
1708
1708
  dependencies = [
1709
1709
  "anyhow",
1710
1710
  "assert_cmd",
@@ -1745,7 +1745,7 @@ dependencies = [
1745
1745
  "tokio-util",
1746
1746
  "toml",
1747
1747
  "toml_edit",
1748
- "tower",
1748
+ "tower 0.5.2",
1749
1749
  "tower-lsp",
1750
1750
  "tower-service",
1751
1751
  "unicode-normalization",
@@ -2182,7 +2182,16 @@ dependencies = [
2182
2182
  "pin-project-lite",
2183
2183
  "tower-layer",
2184
2184
  "tower-service",
2185
- "tracing",
2185
+ ]
2186
+
2187
+ [[package]]
2188
+ name = "tower"
2189
+ version = "0.5.2"
2190
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2191
+ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
2192
+ dependencies = [
2193
+ "tower-layer",
2194
+ "tower-service",
2186
2195
  ]
2187
2196
 
2188
2197
  [[package]]
@@ -2209,7 +2218,7 @@ dependencies = [
2209
2218
  "serde_json",
2210
2219
  "tokio",
2211
2220
  "tokio-util",
2212
- "tower",
2221
+ "tower 0.4.13",
2213
2222
  "tower-lsp-macros",
2214
2223
  "tracing",
2215
2224
  ]
@@ -2237,7 +2246,6 @@ version = "0.1.41"
2237
2246
  source = "registry+https://github.com/rust-lang/crates.io-index"
2238
2247
  checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
2239
2248
  dependencies = [
2240
- "log",
2241
2249
  "pin-project-lite",
2242
2250
  "tracing-attributes",
2243
2251
  "tracing-core",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rumdl"
3
- version = "0.0.65"
3
+ version = "0.0.66"
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>"]
@@ -60,7 +60,7 @@ seahash = "4.1"
60
60
  tower-lsp = "0.20"
61
61
  tokio = { version = "1.0", features = ["full"] }
62
62
  tokio-util = "0.7"
63
- tower = "0.4"
63
+ tower = "0.5.2"
64
64
  tower-service = "0.3"
65
65
  env_logger = "0.11"
66
66
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rumdl
3
- Version: 0.0.65
3
+ Version: 0.0.66
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -67,6 +67,34 @@ impl MD006StartBullets {
67
67
  }
68
68
  None
69
69
  }
70
+
71
+ /// Check if a list item is nested under a parent list item
72
+ fn is_nested_list_item(lines: &[&str], line_idx: usize) -> bool {
73
+ if let Some(current_indent) = Self::is_bullet_list_item(lines[line_idx]) {
74
+ // Look backwards for a parent list item with less indentation
75
+ for i in (0..line_idx).rev() {
76
+ let line = lines[i];
77
+
78
+ // Skip blank lines
79
+ if Self::is_blank_line(line) {
80
+ continue;
81
+ }
82
+
83
+ // If we find a list item with less indentation, this is nested
84
+ if let Some(parent_indent) = Self::is_bullet_list_item(line) {
85
+ if parent_indent < current_indent {
86
+ return true;
87
+ }
88
+ }
89
+
90
+ // If we hit non-list content, stop looking
91
+ if Self::is_bullet_list_item(line).is_none() {
92
+ break;
93
+ }
94
+ }
95
+ }
96
+ false
97
+ }
70
98
  }
71
99
 
72
100
  impl Rule for MD006StartBullets {
@@ -116,6 +144,8 @@ impl Rule for MD006StartBullets {
116
144
  Node::ListItem(ListItem {
117
145
  position, children, ..
118
146
  }) => {
147
+ // Only flag top-level list items (depth == 1) that are indented
148
+ // but make sure they're actually top-level and not nested sub-lists
119
149
  if depth == 1 && !in_blockquote {
120
150
  if let Some(pos) = position {
121
151
  let line_idx = pos.start.line.saturating_sub(1);
@@ -123,17 +153,23 @@ impl Rule for MD006StartBullets {
123
153
  if let Some(cap) = BULLET_PATTERN.captures(line) {
124
154
  let indent = cap[1].len();
125
155
  if indent > 0 {
126
- result.push(LintWarning {
127
- rule_name: Some("MD006"),
128
- severity: Severity::Warning,
129
- line: line_idx + 1,
130
- column: 1,
131
- message: "Consider starting bulleted lists at the beginning of the line".to_string(),
132
- fix: Some(Fix {
133
- range: 0..indent,
134
- replacement: "".to_string(),
135
- }),
136
- });
156
+ // Check if this is actually a nested list item
157
+ // by looking for a parent list item above it
158
+ let is_nested = MD006StartBullets::is_nested_list_item(lines, line_idx);
159
+
160
+ if !is_nested {
161
+ result.push(LintWarning {
162
+ rule_name: Some("MD006"),
163
+ severity: Severity::Warning,
164
+ line: line_idx + 1,
165
+ column: 1,
166
+ message: "Consider starting bulleted lists at the beginning of the line".to_string(),
167
+ fix: Some(Fix {
168
+ range: 0..indent,
169
+ replacement: "".to_string(),
170
+ }),
171
+ });
172
+ }
137
173
  }
138
174
  }
139
175
  }
@@ -310,7 +346,7 @@ impl Rule for MD006StartBullets {
310
346
  }
311
347
 
312
348
  fn as_maybe_document_structure(&self) -> Option<&dyn crate::rule::MaybeDocumentStructure> {
313
- Some(self)
349
+ None
314
350
  }
315
351
 
316
352
  fn from_config(_config: &crate::config::Config) -> Box<dyn Rule>
@@ -99,104 +99,84 @@ impl Rule for MD007ULIndent {
99
99
  self.check(ctx)
100
100
  }
101
101
 
102
- fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
102
+ fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
103
103
  let content = ctx.content;
104
- let tab_str = " ".repeat(self.indent);
105
- let mut lines: Vec<String> = content
106
- .lines()
107
- .map(|l| {
108
- // Normalize leading tabs to spaces
109
- let mut norm = String::new();
110
- let mut chars = l.chars().peekable();
111
- while let Some(&c) = chars.peek() {
112
- if c == '\t' {
113
- norm.push_str(&tab_str);
114
- chars.next();
115
- } else if c == ' ' {
116
- norm.push(' ');
117
- chars.next();
118
- } else {
119
- break;
120
- }
121
- }
122
- norm.extend(chars);
123
- norm
124
- })
125
- .collect();
126
-
127
- // Recompute logical nesting for each unordered list item
128
- let mut prev_items: Vec<(usize, usize, usize)> = Vec::new(); // (blockquote_depth, indent, nesting_level)
129
- for (_, line) in lines.iter_mut().enumerate() {
130
- let orig_line = line.clone();
131
- // Inline blockquote prefix parsing (since parse_blockquote_prefix is private)
132
- let mut rest = orig_line.as_str();
133
- let mut blockquote_prefix = String::new();
134
- let mut blockquote_depth = 0;
135
- loop {
136
- let trimmed = rest.trim_start();
137
- if trimmed.starts_with('>') {
138
- // Find the '>' and a single optional space
139
- let after = &trimmed[1..];
140
- let mut chars = after.chars();
141
- let mut space_count = 0;
142
- if let Some(' ') = chars.next() {
143
- space_count = 1;
144
- }
145
- let (spaces, after_marker) = after.split_at(space_count);
146
- blockquote_prefix.push('>');
147
- blockquote_prefix.push_str(spaces);
148
- rest = after_marker;
149
- blockquote_depth += 1;
150
- } else {
151
- break;
152
- }
153
- }
154
- // Only process unordered list items outside code blocks
155
- if rest.trim().is_empty() || rest.starts_with("```") || rest.starts_with("~~~") {
156
- // Do NOT clear prev_items on blank lines; only skip processing
157
- continue;
104
+
105
+ // Get the warnings to know which lines need indentation fixing
106
+ let warnings = self.check(ctx)?;
107
+ let mut lines_to_fix: std::collections::HashSet<usize> = std::collections::HashSet::new();
108
+ for warning in &warnings {
109
+ lines_to_fix.insert(warning.line);
110
+ }
111
+
112
+ let element_cache = ElementCache::new(content);
113
+ let lines: Vec<&str> = content.lines().collect();
114
+ let mut result_lines: Vec<String> = Vec::new();
115
+
116
+ // Check if any list items have tabs that need normalization
117
+ let mut has_tabs = false;
118
+ for &line in &lines {
119
+ if line.contains('\t') && element_cache.get_list_items().iter().any(|item| item.line_number == lines.iter().position(|&l| l == line).unwrap_or(0) + 1) {
120
+ has_tabs = true;
121
+ break;
158
122
  }
159
- // Use the same regex as element_cache
160
- let re = regex::Regex::new(r"^(?P<indent>[ ]*)(?P<marker>[*+-])(?P<after>[ ]+)(?P<content>.*)$").unwrap();
161
- if let Some(caps) = re.captures(rest) {
162
- let indent_str = caps.name("indent").map_or("", |m| m.as_str());
163
- let marker = caps.name("marker").unwrap().as_str();
164
- let after = caps.name("after").map_or(" ", |m| m.as_str());
165
- let content = caps.name("content").map_or("", |m| m.as_str());
166
- let indent = indent_str.len();
167
- // Compute logical nesting level
168
- let mut nesting_level = 0;
169
- if let Some(&(_last_bq, last_indent, last_level)) = prev_items.iter().rev().find(|(bq, _, _)| *bq == blockquote_depth) {
170
- if indent > last_indent {
171
- nesting_level = last_level + 1;
172
- } else {
173
- for &(prev_bq, prev_indent, prev_level) in prev_items.iter().rev() {
174
- if prev_bq == blockquote_depth && prev_indent <= indent {
175
- nesting_level = prev_level;
176
- break;
177
- }
123
+ }
124
+
125
+ // If no warnings and no tabs to normalize, return original content
126
+ if warnings.is_empty() && !has_tabs {
127
+ return Ok(content.to_string());
128
+ }
129
+
130
+ for (line_idx, &line) in lines.iter().enumerate() {
131
+ let line_number = line_idx + 1;
132
+
133
+ // Check if this line is a list item
134
+ if let Some(item) = element_cache.get_list_items().iter().find(|item| item.line_number == line_number) {
135
+ if matches!(item.marker_type, ListMarkerType::Asterisk | ListMarkerType::Plus | ListMarkerType::Minus) {
136
+ // Determine if we need to fix this line
137
+ let needs_indentation_fix = lines_to_fix.contains(&line_number);
138
+ let needs_tab_normalization = line.contains('\t');
139
+
140
+ if needs_indentation_fix || needs_tab_normalization {
141
+ let expected_indent = item.nesting_level * self.indent;
142
+
143
+ // Reconstruct the line with correct indentation
144
+ let marker_char = match item.marker_type {
145
+ ListMarkerType::Asterisk => '*',
146
+ ListMarkerType::Plus => '+',
147
+ ListMarkerType::Minus => '-',
148
+ _ => unreachable!(),
149
+ };
150
+
151
+ // Extract the content after the marker and space
152
+ let re = regex::Regex::new(r"^(\s*)([*+-])(\s+)(.*)$").unwrap();
153
+ if let Some(caps) = re.captures(line) {
154
+ let content_part = caps.get(4).map_or("", |m| m.as_str());
155
+ let space_after_marker = caps.get(3).map_or(" ", |m| m.as_str());
156
+ let correct_indent = " ".repeat(expected_indent);
157
+ let fixed_line = format!("{}{}{}{}{}", item.blockquote_prefix, correct_indent, marker_char, space_after_marker, content_part);
158
+ result_lines.push(fixed_line);
159
+ } else {
160
+ // Fallback: just use the original line
161
+ result_lines.push(line.to_string());
178
162
  }
163
+ } else {
164
+ result_lines.push(line.to_string());
179
165
  }
166
+ } else {
167
+ result_lines.push(line.to_string());
180
168
  }
181
- // Remove stack entries with indent >= current indent and same blockquote depth
182
- while let Some(&(prev_bq, prev_indent, _)) = prev_items.last() {
183
- if prev_bq != blockquote_depth || prev_indent < indent {
184
- break;
185
- }
186
- prev_items.pop();
187
- }
188
- prev_items.push((blockquote_depth, indent, nesting_level));
189
- // Reconstruct line with correct indentation
190
- let correct_indent = " ".repeat(nesting_level * self.indent);
191
- *line = format!("{}{}{}{}{}", blockquote_prefix, correct_indent, marker, after, content);
192
169
  } else {
193
- // Only clear prev_items if the line is not blank and not a list item
194
- if !rest.trim().is_empty() {
195
- prev_items.clear();
196
- }
170
+ result_lines.push(line.to_string());
197
171
  }
198
172
  }
199
- Ok(lines.join("\n"))
173
+
174
+ let result = result_lines.join("\n");
175
+ if content.ends_with('\n') {
176
+ Ok(result + "\n")
177
+ } else {
178
+ Ok(result)
179
+ }
200
180
  }
201
181
 
202
182
  /// Get the category of this rule for selective processing
@@ -185,8 +185,9 @@ impl Rule for MD030ListMarkerSpace {
185
185
  ))
186
186
  }
187
187
 
188
- fn fix(&self, _ctx: &crate::lint_context::LintContext) -> Result<String, crate::rule::LintError> {
189
- Err(crate::rule::LintError::FixFailed("Automatic fixing is not supported for MD030. See todos/md030_fix_strategy.md for details.".to_string()))
188
+ fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, crate::rule::LintError> {
189
+ // MD030 is not fixable - return content unchanged
190
+ Ok(ctx.content.to_string())
190
191
  }
191
192
  }
192
193
 
@@ -284,13 +284,17 @@ impl MD034NoBareUrls {
284
284
  if let Some(pos) = &text.position {
285
285
  let offset = pos.start.offset + url_start;
286
286
  let (line, column) = ctx.offset_to_line_col(offset);
287
+ let url_text = &text_str[url_start..url_end];
287
288
  warnings.push(LintWarning {
288
289
  rule_name: Some(self.name()),
289
290
  line,
290
291
  column,
291
- message: format!("Bare URL found: {}", &text_str[url_start..url_end]),
292
+ message: format!("Bare URL found: {}", url_text),
292
293
  severity: Severity::Warning,
293
- fix: None, // Fix not implemented yet
294
+ fix: Some(Fix {
295
+ range: offset..(offset + url_text.len()),
296
+ replacement: format!("<{}>", url_text),
297
+ }),
294
298
  });
295
299
  }
296
300
  }
@@ -401,45 +405,30 @@ impl Rule for MD034NoBareUrls {
401
405
  }
402
406
 
403
407
  // Get all warnings first - only fix URLs that are actually flagged
404
- let warnings = self.check(ctx)?;
408
+ // Use AST-based detection to match the main linting path (since uses_ast() returns true)
409
+ let ast = crate::utils::ast_utils::get_cached_ast(content);
410
+ let warnings = self.check_with_ast(ctx, &ast)?;
405
411
  if warnings.is_empty() {
406
412
  return Ok(content.to_string());
407
413
  }
408
414
 
409
- // Group warnings by line number for easier processing
410
- let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
415
+ // Sort warnings by byte offset in reverse order (rightmost first) to avoid offset issues
416
+ let mut sorted_warnings = warnings.clone();
417
+ sorted_warnings.sort_by_key(|w| std::cmp::Reverse(w.fix.as_ref().map(|f| f.range.start).unwrap_or(0)));
411
418
 
412
- // Process warnings line by line (in reverse order to avoid offset issues)
413
- let mut warnings_by_line: std::collections::BTreeMap<usize, Vec<&crate::rule::LintWarning>> = std::collections::BTreeMap::new();
414
- for warning in &warnings {
415
- warnings_by_line.entry(warning.line).or_insert_with(Vec::new).push(warning);
416
- }
417
-
418
- // Process lines in reverse order to avoid affecting line indices
419
- for (line_num, line_warnings) in warnings_by_line.iter().rev() {
420
- let line_idx = line_num - 1;
421
- if line_idx >= lines.len() {
422
- continue;
423
- }
419
+ let mut result = content.to_string();
420
+ for warning in sorted_warnings {
421
+ if let Some(fix) = &warning.fix {
422
+ let start = fix.range.start;
423
+ let end = fix.range.end;
424
424
 
425
- // Sort warnings by column in reverse order (rightmost first)
426
- let mut sorted_warnings = line_warnings.clone();
427
- sorted_warnings.sort_by_key(|w| std::cmp::Reverse(w.column));
428
-
429
- for warning in sorted_warnings {
430
- if let Some(fix) = &warning.fix {
431
- let line = &mut lines[line_idx];
432
- let start = fix.range.start;
433
- let end = fix.range.end;
434
-
435
- if start <= line.len() && end <= line.len() && start < end {
436
- line.replace_range(start..end, &fix.replacement);
437
- }
425
+ if start <= result.len() && end <= result.len() && start < end {
426
+ result.replace_range(start..end, &fix.replacement);
438
427
  }
439
428
  }
440
429
  }
441
430
 
442
- Ok(lines.join("\n"))
431
+ Ok(result)
443
432
  }
444
433
 
445
434
  /// Get the category of this rule for selective processing
@@ -50,15 +50,148 @@ impl MD046CodeBlockStyle {
50
50
  return false;
51
51
  }
52
52
 
53
- // Not a list item
54
- let prev_line_is_list = i > 0 && self.is_list_item(lines[i - 1]);
55
- if prev_line_is_list {
53
+ // Check if this is part of a list structure
54
+ if self.is_part_of_list_structure(lines, i) {
55
+ return false;
56
+ }
57
+
58
+ // Check if this is part of a formatted text block (not a code block)
59
+ if self.is_part_of_formatted_text_block(lines, i) {
60
+ return false;
61
+ }
62
+
63
+ // Check if preceded by a blank line (typical for code blocks)
64
+ let has_blank_line_before = i == 0 || lines[i - 1].trim().is_empty();
65
+
66
+ // If no blank line before, it's likely list continuation, not a code block
67
+ if !has_blank_line_before {
56
68
  return false;
57
69
  }
58
70
 
59
71
  true
60
72
  }
61
73
 
74
+ /// Check if an indented line is part of a formatted text block (like license text)
75
+ /// rather than a code block
76
+ fn is_part_of_formatted_text_block(&self, lines: &[&str], i: usize) -> bool {
77
+ let line = lines[i];
78
+ let trimmed = line.trim();
79
+
80
+ // Look for patterns that suggest this is formatted text, not code:
81
+
82
+ // 1. License/legal text patterns
83
+ if trimmed.contains("Copyright") ||
84
+ trimmed.contains("License") ||
85
+ trimmed.contains("Foundation") ||
86
+ trimmed.contains("Certificate") ||
87
+ trimmed.contains("Origin") ||
88
+ trimmed.starts_with("Version ") ||
89
+ trimmed.contains("permitted") ||
90
+ trimmed.contains("contribution") ||
91
+ trimmed.contains("certify") {
92
+ return true;
93
+ }
94
+
95
+ // 2. Address/contact information patterns
96
+ if trimmed.contains("Drive") ||
97
+ trimmed.contains("Suite") ||
98
+ trimmed.contains("CA,") ||
99
+ trimmed.contains("San Francisco") {
100
+ return true;
101
+ }
102
+
103
+ // 3. Email signature patterns
104
+ if trimmed.contains("Signed-off-by:") ||
105
+ trimmed.contains("@") && trimmed.contains(".com") {
106
+ return true;
107
+ }
108
+
109
+ // 4. Check if this is part of a larger block of indented text
110
+ // that looks like formatted prose rather than code
111
+ let mut consecutive_indented_lines = 0;
112
+ let mut has_prose_content = false;
113
+
114
+ // Look at surrounding lines to see if this is part of a prose block
115
+ let start = if i >= 5 { i - 5 } else { 0 };
116
+ let end = if i + 5 < lines.len() { i + 5 } else { lines.len() };
117
+
118
+ for j in start..end {
119
+ let check_line = lines[j];
120
+ if check_line.starts_with(" ") || check_line.starts_with("\t") {
121
+ consecutive_indented_lines += 1;
122
+ let check_trimmed = check_line.trim();
123
+ // Look for prose indicators
124
+ if check_trimmed.len() > 20 &&
125
+ (check_trimmed.contains(" the ") ||
126
+ check_trimmed.contains(" and ") ||
127
+ check_trimmed.contains(" or ") ||
128
+ check_trimmed.contains(" to ") ||
129
+ check_trimmed.contains(" of ") ||
130
+ check_trimmed.contains(" in ") ||
131
+ check_trimmed.contains(" is ") ||
132
+ check_trimmed.contains(" that ")) {
133
+ has_prose_content = true;
134
+ }
135
+ }
136
+ }
137
+
138
+ // If we have many consecutive indented lines with prose content,
139
+ // it's likely formatted text, not code
140
+ if consecutive_indented_lines >= 5 && has_prose_content {
141
+ return true;
142
+ }
143
+
144
+ false
145
+ }
146
+
147
+ /// Check if an indented line is part of a list structure
148
+ fn is_part_of_list_structure(&self, lines: &[&str], i: usize) -> bool {
149
+ // Look backwards to find if we're in a list context
150
+ // We need to be more aggressive about detecting list contexts
151
+
152
+ for j in (0..i).rev() {
153
+ let line = lines[j];
154
+
155
+ // Skip empty lines - they don't break list context
156
+ if line.trim().is_empty() {
157
+ continue;
158
+ }
159
+
160
+ // If we find a list item, we're definitely in a list context
161
+ if self.is_list_item(line) {
162
+ return true;
163
+ }
164
+
165
+ // Check if this line looks like it's part of a list item
166
+ // (indented content that's not a code block)
167
+ let trimmed = line.trim_start();
168
+ let indent_len = line.len() - trimmed.len();
169
+
170
+ // If we find a line that starts at column 0 and is not a list item,
171
+ // check if it's a structural element that would end list context
172
+ if indent_len == 0 && !trimmed.is_empty() {
173
+ // Headings definitely end list context
174
+ if trimmed.starts_with('#') {
175
+ break;
176
+ }
177
+ // Horizontal rules end list context
178
+ if trimmed.starts_with("---") || trimmed.starts_with("***") {
179
+ break;
180
+ }
181
+ // If it's a paragraph that doesn't look like it's part of a list,
182
+ // we might not be in a list anymore, but let's be conservative
183
+ // and keep looking a bit more
184
+ if j > 0 && j < i - 5 { // Only break if we've looked back a reasonable distance
185
+ break;
186
+ }
187
+ }
188
+
189
+ // Continue looking backwards through indented content
190
+ }
191
+
192
+ false
193
+ }
194
+
62
195
  /// Helper function to check if a line is part of a list
63
196
  fn is_in_list(&self, lines: &[&str], i: usize) -> bool {
64
197
  // Check if current line is a list item