rumdl 0.0.65__tar.gz → 0.0.67__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 (252) hide show
  1. {rumdl-0.0.65 → rumdl-0.0.67}/Cargo.lock +13 -5
  2. {rumdl-0.0.65 → rumdl-0.0.67}/Cargo.toml +2 -2
  3. {rumdl-0.0.65 → rumdl-0.0.67}/PKG-INFO +1 -1
  4. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md003_heading_style.rs +76 -134
  5. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md006_start_bullets.rs +48 -12
  6. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md007_ul_indent.rs +52 -94
  7. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md013_line_length.rs +204 -40
  8. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md030_list_marker_space.rs +168 -4
  9. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md034_no_bare_urls.rs +60 -33
  10. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md046_code_block_style.rs +142 -3
  11. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md049_emphasis_style.rs +58 -53
  12. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md052_reference_links_images.rs +43 -13
  13. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md053_link_image_reference_definitions.rs +52 -35
  14. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/element_cache.rs +195 -21
  15. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md007_test.rs +2 -2
  16. rumdl-0.0.67/tests/rules/md030_test.rs +468 -0
  17. rumdl-0.0.65/tests/rules/md030_test.rs +0 -194
  18. {rumdl-0.0.65 → rumdl-0.0.67}/.rumdl.toml +0 -0
  19. {rumdl-0.0.65 → rumdl-0.0.67}/MANIFEST.in +0 -0
  20. {rumdl-0.0.65 → rumdl-0.0.67}/Makefile +0 -0
  21. {rumdl-0.0.65 → rumdl-0.0.67}/README.md +0 -0
  22. {rumdl-0.0.65 → rumdl-0.0.67}/assets/logo.png +0 -0
  23. {rumdl-0.0.65 → rumdl-0.0.67}/benches/range_performance.rs +0 -0
  24. {rumdl-0.0.65 → rumdl-0.0.67}/benches/range_utils_benchmark.rs +0 -0
  25. {rumdl-0.0.65 → rumdl-0.0.67}/benches/rule_performance.rs +0 -0
  26. {rumdl-0.0.65 → rumdl-0.0.67}/docs/RULES.md +0 -0
  27. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md001.md +0 -0
  28. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md002.md +0 -0
  29. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md003.md +0 -0
  30. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md004.md +0 -0
  31. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md005.md +0 -0
  32. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md006.md +0 -0
  33. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md007.md +0 -0
  34. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md009.md +0 -0
  35. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md010.md +0 -0
  36. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md011.md +0 -0
  37. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md012.md +0 -0
  38. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md013.md +0 -0
  39. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md014.md +0 -0
  40. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md018.md +0 -0
  41. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md019.md +0 -0
  42. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md020.md +0 -0
  43. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md021.md +0 -0
  44. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md022.md +0 -0
  45. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md023.md +0 -0
  46. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md024.md +0 -0
  47. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md025.md +0 -0
  48. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md026.md +0 -0
  49. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md027.md +0 -0
  50. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md028.md +0 -0
  51. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md029.md +0 -0
  52. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md030.md +0 -0
  53. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md031.md +0 -0
  54. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md032.md +0 -0
  55. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md033.md +0 -0
  56. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md034.md +0 -0
  57. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md035.md +0 -0
  58. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md036.md +0 -0
  59. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md037.md +0 -0
  60. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md038.md +0 -0
  61. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md039.md +0 -0
  62. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md040.md +0 -0
  63. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md041.md +0 -0
  64. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md042.md +0 -0
  65. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md043.md +0 -0
  66. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md044.md +0 -0
  67. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md045.md +0 -0
  68. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md046.md +0 -0
  69. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md047.md +0 -0
  70. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md048.md +0 -0
  71. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md049.md +0 -0
  72. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md050.md +0 -0
  73. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md051.md +0 -0
  74. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md052.md +0 -0
  75. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md053.md +0 -0
  76. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md054.md +0 -0
  77. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md055.md +0 -0
  78. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md056.md +0 -0
  79. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md057.md +0 -0
  80. {rumdl-0.0.65 → rumdl-0.0.67}/docs/md058.md +0 -0
  81. {rumdl-0.0.65 → rumdl-0.0.67}/issues/plan-rule-parity-with-markdownlint.md +0 -0
  82. {rumdl-0.0.65 → rumdl-0.0.67}/parity_check.py +0 -0
  83. {rumdl-0.0.65 → rumdl-0.0.67}/pyproject.toml +0 -0
  84. {rumdl-0.0.65 → rumdl-0.0.67}/python/MANIFEST.in +0 -0
  85. {rumdl-0.0.65 → rumdl-0.0.67}/python/PYTHON-README.md +0 -0
  86. {rumdl-0.0.65 → rumdl-0.0.67}/python/rumdl/__init__.py +0 -0
  87. {rumdl-0.0.65 → rumdl-0.0.67}/python/rumdl/__main__.py +0 -0
  88. {rumdl-0.0.65 → rumdl-0.0.67}/python/rumdl/py.typed +0 -0
  89. {rumdl-0.0.65 → rumdl-0.0.67}/rumdl.toml.example +0 -0
  90. {rumdl-0.0.65 → rumdl-0.0.67}/src/config.rs +0 -0
  91. {rumdl-0.0.65 → rumdl-0.0.67}/src/init.rs +0 -0
  92. {rumdl-0.0.65 → rumdl-0.0.67}/src/lib.rs +0 -0
  93. {rumdl-0.0.65 → rumdl-0.0.67}/src/lint_context.rs +0 -0
  94. {rumdl-0.0.65 → rumdl-0.0.67}/src/lsp/mod.rs +0 -0
  95. {rumdl-0.0.65 → rumdl-0.0.67}/src/lsp/server.rs +0 -0
  96. {rumdl-0.0.65 → rumdl-0.0.67}/src/lsp/types.rs +0 -0
  97. {rumdl-0.0.65 → rumdl-0.0.67}/src/main.rs +0 -0
  98. {rumdl-0.0.65 → rumdl-0.0.67}/src/markdownlint_config.rs +0 -0
  99. {rumdl-0.0.65 → rumdl-0.0.67}/src/profiling.rs +0 -0
  100. {rumdl-0.0.65 → rumdl-0.0.67}/src/python.rs +0 -0
  101. {rumdl-0.0.65 → rumdl-0.0.67}/src/rule.rs +0 -0
  102. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/blockquote_utils.rs +0 -0
  103. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/code_block_utils.rs +0 -0
  104. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/code_fence_utils.rs +0 -0
  105. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/emphasis_style.rs +0 -0
  106. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/front_matter_utils.rs +0 -0
  107. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/heading_utils.rs +0 -0
  108. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/list_utils.rs +0 -0
  109. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md001_heading_increment.rs +0 -0
  110. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md002_first_heading_h1.rs +0 -0
  111. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md004_unordered_list_style.rs +0 -0
  112. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md005_list_indent.rs +0 -0
  113. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md009_trailing_spaces.rs +0 -0
  114. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md010_no_hard_tabs.rs +0 -0
  115. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md011_no_reversed_links.rs +0 -0
  116. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md012_no_multiple_blanks.rs +0 -0
  117. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md014_commands_show_output.rs +0 -0
  118. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md018_no_missing_space_atx.rs +0 -0
  119. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md019_no_multiple_space_atx.rs +0 -0
  120. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md020_no_missing_space_closed_atx.rs +0 -0
  121. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md021_no_multiple_space_closed_atx.rs +0 -0
  122. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md022_blanks_around_headings.rs +0 -0
  123. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md023_heading_start_left.rs +0 -0
  124. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md024_no_duplicate_heading.rs +0 -0
  125. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md025_single_title.rs +0 -0
  126. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md026_no_trailing_punctuation.rs +0 -0
  127. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md027_multiple_spaces_blockquote.rs +0 -0
  128. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md028_no_blanks_blockquote.rs +0 -0
  129. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md029_ordered_list_prefix.rs +0 -0
  130. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md031_blanks_around_fences.rs +0 -0
  131. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md032_blanks_around_lists.rs +0 -0
  132. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md033_no_inline_html.rs +0 -0
  133. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md035_hr_style.rs +0 -0
  134. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md036_no_emphasis_only_first.rs +0 -0
  135. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md037_spaces_around_emphasis.rs +0 -0
  136. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md038_no_space_in_code.rs +0 -0
  137. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md039_no_space_in_links.rs +0 -0
  138. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md040_fenced_code_language.rs +0 -0
  139. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md041_first_line_heading.rs +0 -0
  140. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md042_no_empty_links.rs +0 -0
  141. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md043_required_headings.rs +0 -0
  142. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md044_proper_names.rs +0 -0
  143. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md045_no_alt_text.rs +0 -0
  144. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md047_single_trailing_newline.rs +0 -0
  145. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md048_code_fence_style.rs +0 -0
  146. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md050_strong_style.rs +0 -0
  147. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md051_link_fragments.rs +0 -0
  148. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md054_link_image_style.rs +0 -0
  149. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md055_table_pipe_style.rs +0 -0
  150. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md056_table_column_count.rs +0 -0
  151. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md057_existing_relative_links.rs +0 -0
  152. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/md058_blanks_around_tables.rs +0 -0
  153. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/mod.rs +0 -0
  154. {rumdl-0.0.65 → rumdl-0.0.67}/src/rules/strong_style.rs +0 -0
  155. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/ast_utils.rs +0 -0
  156. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/code_block_utils.rs +0 -0
  157. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/document_structure.rs +0 -0
  158. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/early_returns.rs +0 -0
  159. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/markdown_elements.rs +0 -0
  160. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/mod.rs +0 -0
  161. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/range_utils.rs +0 -0
  162. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/regex_cache.rs +0 -0
  163. {rumdl-0.0.65 → rumdl-0.0.67}/src/utils/string_interner.rs +0 -0
  164. {rumdl-0.0.65 → rumdl-0.0.67}/tests/advanced_integration_tests.rs +0 -0
  165. {rumdl-0.0.65 → rumdl-0.0.67}/tests/cli_duplication_test.rs +0 -0
  166. {rumdl-0.0.65 → rumdl-0.0.67}/tests/cli_integration_tests.rs +0 -0
  167. {rumdl-0.0.65 → rumdl-0.0.67}/tests/commonmark_compliance_tests.rs +0 -0
  168. {rumdl-0.0.65 → rumdl-0.0.67}/tests/comprehensive_integration_tests.rs +0 -0
  169. {rumdl-0.0.65 → rumdl-0.0.67}/tests/config_application_tests.rs +0 -0
  170. {rumdl-0.0.65 → rumdl-0.0.67}/tests/config_tests.rs +0 -0
  171. {rumdl-0.0.65 → rumdl-0.0.67}/tests/init_command_test.rs +0 -0
  172. {rumdl-0.0.65 → rumdl-0.0.67}/tests/init_tests.rs +0 -0
  173. {rumdl-0.0.65 → rumdl-0.0.67}/tests/integration_tests.rs +0 -0
  174. {rumdl-0.0.65 → rumdl-0.0.67}/tests/json_output_test.rs +0 -0
  175. {rumdl-0.0.65 → rumdl-0.0.67}/tests/lib.rs +0 -0
  176. {rumdl-0.0.65 → rumdl-0.0.67}/tests/lsp_integration_tests.rs +0 -0
  177. {rumdl-0.0.65 → rumdl-0.0.67}/tests/lsp_tests.rs +0 -0
  178. {rumdl-0.0.65 → rumdl-0.0.67}/tests/markdownlint_cli_integration.rs +0 -0
  179. {rumdl-0.0.65 → rumdl-0.0.67}/tests/markdownlint_config_test.rs +0 -0
  180. {rumdl-0.0.65 → rumdl-0.0.67}/tests/md030_edge_cases.md +0 -0
  181. {rumdl-0.0.65 → rumdl-0.0.67}/tests/output_format_tests.rs +0 -0
  182. {rumdl-0.0.65 → rumdl-0.0.67}/tests/perf_check.rs +0 -0
  183. {rumdl-0.0.65 → rumdl-0.0.67}/tests/pyproject_config_tests.rs +0 -0
  184. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md001_test.rs +0 -0
  185. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md001_unicode_test.rs +0 -0
  186. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md002_test.rs +0 -0
  187. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md003_test.rs +0 -0
  188. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md004_test.rs +0 -0
  189. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md005_test.rs +0 -0
  190. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md006_test.rs +0 -0
  191. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md006_unicode_test.rs +0 -0
  192. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md009_test.rs +0 -0
  193. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md010_test.rs +0 -0
  194. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md011_test.rs +0 -0
  195. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md012_test.rs +0 -0
  196. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md013_test.rs +0 -0
  197. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md014_test.rs +0 -0
  198. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md018_test.rs +0 -0
  199. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md019_test.rs +0 -0
  200. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md020_test.rs +0 -0
  201. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md021_test.rs +0 -0
  202. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md022_test.rs +0 -0
  203. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md023_extended_test.rs +0 -0
  204. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md023_test.rs +0 -0
  205. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md024_test.rs +0 -0
  206. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md025_test.rs +0 -0
  207. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md026_test.rs +0 -0
  208. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md027_test.rs +0 -0
  209. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md028_test.rs +0 -0
  210. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md029_test.rs +0 -0
  211. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md031_test.rs +0 -0
  212. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md032_test.rs +0 -0
  213. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md033_extended_test.rs +0 -0
  214. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md033_test.rs +0 -0
  215. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md034_test.rs +0 -0
  216. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md035_test.rs +0 -0
  217. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md036_test.rs +0 -0
  218. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md037_test.rs +0 -0
  219. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md038_test.rs +0 -0
  220. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md039_test.rs +0 -0
  221. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md040_test.rs +0 -0
  222. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md041_test.rs +0 -0
  223. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md042_test.rs +0 -0
  224. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md043_test.rs +0 -0
  225. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md044_test.rs +0 -0
  226. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md045_test.rs +0 -0
  227. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md046_test.rs +0 -0
  228. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md047_test.rs +0 -0
  229. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md048_test.rs +0 -0
  230. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md049_test.rs +0 -0
  231. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md050_test.rs +0 -0
  232. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md051_test.rs +0 -0
  233. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md052_test.rs +0 -0
  234. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md053_additional_test.rs +0 -0
  235. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md053_proptest.rs +0 -0
  236. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md053_test.rs +0 -0
  237. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md054_test.rs +0 -0
  238. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md054_unicode_test.rs +0 -0
  239. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md055_test.rs +0 -0
  240. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md056_test.rs +0 -0
  241. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md057_test.rs +0 -0
  242. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/md058_test.rs +0 -0
  243. {rumdl-0.0.65 → rumdl-0.0.67}/tests/rules/mod.rs +0 -0
  244. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/blockquote_utils_test.rs +0 -0
  245. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/code_block_utils_extended_test.rs +0 -0
  246. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/code_block_utils_test.rs +0 -0
  247. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/core_utils_test.rs +0 -0
  248. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/front_matter_utils_test.rs +0 -0
  249. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/line_index_test.rs +0 -0
  250. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils/mod.rs +0 -0
  251. {rumdl-0.0.65 → rumdl-0.0.67}/tests/utils_markdown_edge_cases.rs +0 -0
  252. {rumdl-0.0.65 → rumdl-0.0.67}/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.67"
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.67"
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.67
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -4,9 +4,8 @@
4
4
  //! See [docs/md003.md](../../docs/md003.md) for full documentation, configuration, and examples.
5
5
 
6
6
  use crate::rule::{LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
7
- use crate::rules::heading_utils::{HeadingStyle, HeadingUtils};
7
+ use crate::rules::heading_utils::HeadingStyle;
8
8
  use crate::utils::document_structure::DocumentStructure;
9
- use crate::utils::markdown_elements::{ElementQuality, ElementType, MarkdownElements};
10
9
  use lazy_static::lazy_static;
11
10
  use regex::Regex;
12
11
  use std::str::FromStr;
@@ -166,143 +165,29 @@ impl Rule for MD003HeadingStyle {
166
165
  }
167
166
 
168
167
  fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
169
- let content = ctx.content;
170
- // Early return for empty content
171
- if content.is_empty() {
172
- return Ok(String::new());
173
- }
168
+ // Get all warnings with their fixes
169
+ let warnings = self.check(ctx)?;
174
170
 
175
- // Quick check if there are any headings at all
176
- if !QUICK_HEADING_CHECK.is_match(content) {
177
- return Ok(content.to_string());
171
+ // If no warnings, return original content
172
+ if warnings.is_empty() {
173
+ return Ok(ctx.content.to_string());
178
174
  }
179
175
 
180
- let mut fixed_content = String::new();
181
- let mut last_processed_line = 0;
182
- let lines: Vec<&str> = content.lines().collect();
183
-
184
- // Get the target style - use the fallback method since no structure is available
185
- let target_style = self.get_target_style(content, None);
186
-
187
- // Get all headings using the MarkdownElements utility
188
- let headings = MarkdownElements::detect_headings(content);
189
-
190
- for heading in headings {
191
- if heading.element_type != ElementType::Heading
192
- || heading.quality != ElementQuality::Valid
193
- {
194
- continue; // Skip non-headings or invalid headings
195
- }
196
-
197
- // Add any lines before this heading
198
- for i in last_processed_line..heading.start_line {
199
- if !fixed_content.is_empty() {
200
- fixed_content.push('\n');
201
- }
202
- fixed_content.push_str(lines.get(i).unwrap_or(&""));
176
+ // Collect all fixes and sort by range start (descending) to apply from end to beginning
177
+ let mut fixes: Vec<_> = warnings.iter()
178
+ .filter_map(|w| w.fix.as_ref().map(|f| (f.range.start, f.range.end, &f.replacement)))
179
+ .collect();
180
+ fixes.sort_by(|a, b| b.0.cmp(&a.0));
181
+
182
+ // Apply fixes from end to beginning to preserve byte offsets
183
+ let mut result = ctx.content.to_string();
184
+ for (start, end, replacement) in fixes {
185
+ if start < result.len() && end <= result.len() && start <= end {
186
+ result.replace_range(start..end, replacement);
203
187
  }
204
-
205
- // Get the heading level
206
- if let Some(level_str) = &heading.metadata {
207
- if let Ok(level) = level_str.parse::<u32>() {
208
- // Determine the current style of the heading
209
- let style = if heading.end_line > heading.start_line {
210
- // Setext heading (has an underline)
211
- if level == 1 {
212
- HeadingStyle::Setext1
213
- } else {
214
- HeadingStyle::Setext2
215
- }
216
- } else {
217
- // ATX heading
218
- let line = lines.get(heading.start_line).map_or("", |v| *v);
219
- if line.trim().ends_with('#') {
220
- HeadingStyle::AtxClosed
221
- } else {
222
- HeadingStyle::Atx
223
- }
224
- };
225
-
226
- // For markdownlint parity: when target style is Setext, all headings are expected to be Setext
227
- // For level 3+, we can't actually convert to Setext, so leave as ATX but flag as violation
228
- let expected_style = if target_style == HeadingStyle::Setext1
229
- || target_style == HeadingStyle::Setext2
230
- {
231
- if level > 2 {
232
- // Level 3+ can't be Setext, so keep as ATX but this will be flagged as violation
233
- HeadingStyle::Atx
234
- } else if level == 1 {
235
- HeadingStyle::Setext1
236
- } else {
237
- HeadingStyle::Setext2
238
- }
239
- } else {
240
- target_style
241
- };
242
-
243
- // If this heading's style doesn't match the target, convert it
244
- if style != expected_style {
245
- // Get the text content from the heading
246
- let text_content = if heading.end_line > heading.start_line {
247
- // Setext heading
248
- lines.get(heading.start_line).unwrap_or(&"").to_string()
249
- } else {
250
- // ATX heading
251
- let line = lines.get(heading.start_line).map_or("", |v| *v);
252
- HeadingUtils::get_heading_text(line).unwrap_or_default()
253
- };
254
-
255
- // Get indentation
256
- let indentation = if let Some(line) = lines.get(heading.start_line) {
257
- line.chars()
258
- .take_while(|c| c.is_whitespace())
259
- .collect::<String>()
260
- } else {
261
- String::new()
262
- };
263
-
264
- // Convert heading to target style
265
- let converted_heading = HeadingUtils::convert_heading_style(
266
- &format!("{}{}", indentation, text_content),
267
- level,
268
- expected_style,
269
- );
270
-
271
- // Add converted heading
272
- if !fixed_content.is_empty() {
273
- fixed_content.push('\n');
274
- }
275
- fixed_content.push_str(&converted_heading);
276
- } else {
277
- // Add original heading lines
278
- for i in heading.start_line..=heading.end_line {
279
- if !fixed_content.is_empty() {
280
- fixed_content.push('\n');
281
- }
282
- fixed_content.push_str(lines.get(i).unwrap_or(&""));
283
- }
284
- }
285
-
286
- // Update last processed line
287
- last_processed_line = heading.end_line + 1;
288
- }
289
- }
290
- }
291
-
292
- // Add any remaining lines
293
- for i in last_processed_line..lines.len() {
294
- if !fixed_content.is_empty() {
295
- fixed_content.push('\n');
296
- }
297
- fixed_content.push_str(lines.get(i).unwrap_or(&""));
298
188
  }
299
189
 
300
- // Preserve trailing newline
301
- if content.ends_with('\n') && !fixed_content.ends_with('\n') {
302
- fixed_content.push('\n');
303
- }
304
-
305
- Ok(fixed_content)
190
+ Ok(result)
306
191
  }
307
192
 
308
193
  fn check_with_structure(
@@ -383,6 +268,63 @@ impl Rule for MD003HeadingStyle {
383
268
  };
384
269
 
385
270
  if style != expected_style {
271
+ // Generate fix for this heading
272
+ let fix = {
273
+ use crate::rules::heading_utils::HeadingUtils;
274
+
275
+ // Get the text content from the heading
276
+ let text_content = if next_line_idx < lines.len() &&
277
+ (lines[next_line_idx].trim_start().starts_with('=') ||
278
+ lines[next_line_idx].trim_start().starts_with('-')) {
279
+ // Setext heading
280
+ current_line.to_string()
281
+ } else {
282
+ // ATX heading
283
+ HeadingUtils::get_heading_text(current_line).unwrap_or_default()
284
+ };
285
+
286
+ // Get indentation
287
+ let indentation = current_line.chars()
288
+ .take_while(|c| c.is_whitespace())
289
+ .collect::<String>();
290
+
291
+ // Convert heading to target style
292
+ let converted_heading = HeadingUtils::convert_heading_style(
293
+ &format!("{}{}", indentation, text_content.trim()),
294
+ level as u32,
295
+ expected_style,
296
+ );
297
+
298
+ // Calculate the correct range for the heading
299
+ let line_index = crate::utils::range_utils::LineIndex::new(ctx.content.to_string());
300
+ let range = if next_line_idx < lines.len() &&
301
+ (lines[next_line_idx].trim_start().starts_with('=') ||
302
+ lines[next_line_idx].trim_start().starts_with('-')) {
303
+ // Setext heading spans two lines
304
+ let start_byte = line_index.line_col_to_byte_range(line_num, 1).start;
305
+ let end_byte = if line_num + 1 < lines.len() {
306
+ line_index.line_col_to_byte_range(line_num + 2, 1).start - 1
307
+ } else {
308
+ ctx.content.len()
309
+ };
310
+ start_byte..end_byte
311
+ } else {
312
+ // ATX heading is single line
313
+ let start_byte = line_index.line_col_to_byte_range(line_num, 1).start;
314
+ let end_byte = if line_num < lines.len() {
315
+ line_index.line_col_to_byte_range(line_num + 1, 1).start - 1
316
+ } else {
317
+ ctx.content.len()
318
+ };
319
+ start_byte..end_byte
320
+ };
321
+
322
+ Some(crate::rule::Fix {
323
+ range,
324
+ replacement: converted_heading,
325
+ })
326
+ };
327
+
386
328
  result.push(LintWarning {
387
329
  rule_name: Some(self.name()),
388
330
  line: line_num, // Already 1-indexed
@@ -392,7 +334,7 @@ impl Rule for MD003HeadingStyle {
392
334
  expected_style, style
393
335
  ),
394
336
  severity: Severity::Warning,
395
- fix: None,
337
+ fix,
396
338
  });
397
339
  }
398
340
  }
@@ -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>
@@ -73,6 +73,38 @@ impl Rule for MD007ULIndent {
73
73
  if matches!(item.marker_type, ListMarkerType::Asterisk | ListMarkerType::Plus | ListMarkerType::Minus) {
74
74
  let expected_indent = item.nesting_level * self.indent;
75
75
  if item.indentation != expected_indent {
76
+ // Generate fix for this list item
77
+ let fix = {
78
+ let lines: Vec<&str> = content.lines().collect();
79
+ if let Some(line) = lines.get(item.line_number - 1) {
80
+ // Extract the marker and content
81
+ let re = regex::Regex::new(r"^(\s*)([*+-])(\s+)(.*)$").unwrap();
82
+ if let Some(caps) = re.captures(line) {
83
+ let content_part = caps.get(4).map_or("", |m| m.as_str());
84
+ let space_after_marker = caps.get(3).map_or(" ", |m| m.as_str());
85
+ let marker_char = caps.get(2).map_or("*", |m| m.as_str());
86
+ let correct_indent = " ".repeat(expected_indent);
87
+ let fixed_line = format!("{}{}{}{}{}", item.blockquote_prefix, correct_indent, marker_char, space_after_marker, content_part);
88
+
89
+ let line_index = crate::utils::range_utils::LineIndex::new(content.to_string());
90
+ let line_start = line_index.line_col_to_byte_range(item.line_number, 1).start;
91
+ let line_end = if item.line_number < lines.len() {
92
+ line_index.line_col_to_byte_range(item.line_number + 1, 1).start - 1
93
+ } else {
94
+ content.len()
95
+ };
96
+ Some(crate::rule::Fix {
97
+ range: line_start..line_end,
98
+ replacement: fixed_line,
99
+ })
100
+ } else {
101
+ None
102
+ }
103
+ } else {
104
+ None
105
+ }
106
+ };
107
+
76
108
  warnings.push(LintWarning {
77
109
  rule_name: Some(self.name()),
78
110
  message: format!(
@@ -82,7 +114,7 @@ impl Rule for MD007ULIndent {
82
114
  line: item.line_number,
83
115
  column: item.blockquote_prefix.len() + item.indentation + 1, // correct column for marker
84
116
  severity: Severity::Warning,
85
- fix: None,
117
+ fix,
86
118
  });
87
119
  }
88
120
  }
@@ -100,103 +132,29 @@ impl Rule for MD007ULIndent {
100
132
  }
101
133
 
102
134
  fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
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
- })
135
+ // Get all warnings with their fixes
136
+ let warnings = self.check(ctx)?;
137
+
138
+ // If no warnings, return original content
139
+ if warnings.is_empty() {
140
+ return Ok(ctx.content.to_string());
141
+ }
142
+
143
+ // Collect all fixes and sort by range start (descending) to apply from end to beginning
144
+ let mut fixes: Vec<_> = warnings.iter()
145
+ .filter_map(|w| w.fix.as_ref().map(|f| (f.range.start, f.range.end, &f.replacement)))
125
146
  .collect();
147
+ fixes.sort_by(|a, b| b.0.cmp(&a.0));
126
148
 
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;
158
- }
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
- }
178
- }
179
- }
180
- }
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
- } 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
- }
149
+ // Apply fixes from end to beginning to preserve byte offsets
150
+ let mut result = ctx.content.to_string();
151
+ for (start, end, replacement) in fixes {
152
+ if start < result.len() && end <= result.len() && start <= end {
153
+ result.replace_range(start..end, replacement);
197
154
  }
198
155
  }
199
- Ok(lines.join("\n"))
156
+
157
+ Ok(result)
200
158
  }
201
159
 
202
160
  /// Get the category of this rule for selective processing